From 36b0618997fd316a4f8fff8878c12e30839606a6 Mon Sep 17 00:00:00 2001 From: Paul Park Date: Fri, 23 Jun 1995 14:01:45 +0000 Subject: [PATCH] Multiple realm support git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@6137 dc483132-0cff-0310-8789-dd5450dbe970 --- src/kdc/dispatch.c | 8 +- src/kdc/do_tgs_req.c | 10 +- src/kdc/extern.c | 23 +- src/kdc/extern.h | 75 +++- src/kdc/kerberos_v4.c | 1 - src/kdc/main.c | 828 ++++++++++++++++++++++++++++++------------ src/kdc/network.c | 102 ++++-- 7 files changed, 727 insertions(+), 320 deletions(-) diff --git a/src/kdc/dispatch.c b/src/kdc/dispatch.c index b16e55fac..7f10061e9 100644 --- a/src/kdc/dispatch.c +++ b/src/kdc/dispatch.c @@ -54,7 +54,13 @@ dispatch(pkt, from, is_secondary, response) retval = process_tgs_req(pkt, from, is_secondary, response); } else if (krb5_is_as_req(pkt)) { if (!(retval = decode_krb5_as_req(pkt, &as_req))) { - retval = process_as_req(as_req, from, is_secondary, response); + /* + * setup_server_realm() sets up the global realm-specific data + * pointer. + */ + if (!(retval = setup_server_realm(as_req->server))) { + retval = process_as_req(as_req, from, is_secondary, response); + } krb5_free_kdc_req(kdc_context, as_req); } } diff --git a/src/kdc/do_tgs_req.c b/src/kdc/do_tgs_req.c index fbf7ccc5d..f25e709dd 100644 --- a/src/kdc/do_tgs_req.c +++ b/src/kdc/do_tgs_req.c @@ -99,6 +99,12 @@ krb5_data **response; /* filled in with a response packet */ if (retval) return retval; + /* + * setup_server_realm() sets up the global realm-specific data pointer. + */ + if (retval = setup_server_realm(request->server)) + return retval; + #ifdef KRB5_USE_INET if (from->address->addrtype == ADDRTYPE_INET) fromstring = @@ -544,8 +550,8 @@ tgt_again: } else { /* convert server.key into a real key (it may be encrypted in the database) */ - if ((retval = KDB_CONVERT_KEY_OUTOF_DB(kdc_context, &server.key, - &encrypting_key))) { + if ((retval = krb5_kdb_decrypt_key(kdc_context, &master_encblock, + &server.key, &encrypting_key))) { status = "CONV_KEY"; goto cleanup; } diff --git a/src/kdc/extern.c b/src/kdc/extern.c index 9c070f260..757df67b8 100644 --- a/src/kdc/extern.c +++ b/src/kdc/extern.c @@ -27,26 +27,11 @@ #include "extern.h" /* real declarations of KDC's externs */ -krb5_rcache kdc_rcache; - +kdc_realm_t **kdc_realmlist = (kdc_realm_t **) NULL; +int kdc_numrealms = 0; +kdc_realm_t *kdc_active_realm = (kdc_realm_t *) NULL; krb5_data empty_string = {0, 0, ""}; krb5_timestamp kdc_infinity = KRB5_INT32_MAX; /* XXX */ - -krb5_deltat max_life_for_realm = KRB5_KDB_MAX_LIFE; /* XXX parameter per-realm? */ -krb5_deltat max_renewable_life_for_realm = KRB5_KDB_MAX_RLIFE; /* XXX param per-realm? */ -krb5_encrypt_block master_encblock; - -krb5_keyblock master_keyblock; -krb5_principal master_princ; +krb5_rcache kdc_rcache = (krb5_rcache) NULL; volatile int signal_requests_exit = 0; /* gets set when signal hits */ - -char *dbm_db_name = DEFAULT_KDB_FILE; - -krb5_keyblock tgs_key; -krb5_kvno tgs_kvno; - -static krb5_data tgs_data[3] = { {0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME}, {0, 0, 0}}; -krb5_principal_data tgs_server_struct = { 0, { 0, 0, 0}, tgs_data, 2, 0}; - -short primary_port = 0; diff --git a/src/kdc/extern.h b/src/kdc/extern.h index 6bb909296..fc9e57521 100644 --- a/src/kdc/extern.h +++ b/src/kdc/extern.h @@ -26,28 +26,65 @@ #ifndef __KRB5_KDC_EXTERN__ #define __KRB5_KDC_EXTERN__ -/* various externs for KDC */ -extern krb5_context kdc_context; /* New context for API changes */ -extern krb5_rcache kdc_rcache; /* KDC's replay cache */ +typedef struct __kdc_realm_data { + /* + * General Kerberos per-realm data. + */ + char * realm_name; /* Realm name */ + krb5_context realm_context; /* Context to be used for realm */ + /* + * Database per-realm data. + */ + char * realm_dbname; /* Database name for realm */ + char * realm_stash; /* Stash file name for realm */ + char * realm_mpname; /* Master principal name for realm */ + krb5_principal realm_mprinc; /* Master principal for realm */ + krb5_keyblock realm_mkey; /* Master key for this realm */ + krb5_kvno realm_mkvno; /* Master key vno for this realm */ + /* + * TGS per-realm data. + */ + krb5_principal realm_tgsprinc; /* TGS principal for this realm */ + krb5_keyblock realm_tgskey; /* TGS' key for this realm */ + krb5_kvno realm_tgskvno; /* TGS' key vno for this realm */ + /* + * Other per-realm data. + */ + krb5_encrypt_block realm_encblock; /* Per-realm master encryption block*/ + krb5_int32 realm_pport; /* Per-realm primary KDC port. */ + /* + * Per-realm parameters. + */ + krb5_deltat realm_maxlife; /* Maximum ticket life for realm */ + krb5_deltat realm_maxrlife; /* Maximum renewable life for realm */ +} kdc_realm_t; -extern krb5_data empty_string; /* an empty string */ -extern krb5_timestamp kdc_infinity; /* greater than all other timestamps */ +extern kdc_realm_t **kdc_realmlist; +extern int kdc_numrealms; +extern kdc_realm_t *kdc_active_realm; -extern krb5_deltat max_life_for_realm; /* XXX should be a parameter? */ -extern krb5_deltat max_renewable_life_for_realm; /* XXX should be a parameter? */ -extern krb5_encrypt_block master_encblock; +/* + * Replace previously used global variables with the active (e.g. request's) + * realm data. This allows us to support multiple realms with minimal logic + * changes. + */ +#define kdc_context kdc_active_realm->realm_context +#define max_life_for_realm kdc_active_realm->realm_maxlife +#define max_renewable_life_for_realm kdc_active_realm->realm_maxrlife +#define master_encblock kdc_active_realm->realm_encblock +#define master_keyblock kdc_active_realm->realm_mkey +#define master_princ kdc_active_realm->realm_mprinc +#define tgs_key kdc_active_realm->realm_tgskey +#define tgs_kvno kdc_active_realm->realm_tgskvno +#define tgs_server_struct *(kdc_active_realm->realm_tgsprinc) +#define tgs_server kdc_active_realm->realm_tgsprinc +#define dbm_db_name kdc_active_realm->realm_dbname +#define primary_port kdc_active_realm->realm_pport -extern krb5_keyblock master_keyblock; -extern krb5_principal master_princ; +/* various externs for KDC */ +extern krb5_data empty_string; /* an empty string */ +extern krb5_timestamp kdc_infinity; /* greater than all other timestamps */ +extern krb5_rcache kdc_rcache; /* replay cache */ extern volatile int signal_requests_exit; -extern char *dbm_db_name; - -extern krb5_keyblock tgs_key; -extern krb5_kvno tgs_kvno; -extern krb5_principal_data tgs_server_struct; -#define tgs_server (&tgs_server_struct) - -extern short primary_port; - #endif /* __KRB5_KDC_EXTERN__ */ diff --git a/src/kdc/kerberos_v4.c b/src/kdc/kerberos_v4.c index 36951a178..1f25cddd5 100644 --- a/src/kdc/kerberos_v4.c +++ b/src/kdc/kerberos_v4.c @@ -533,7 +533,6 @@ hang() } #define kdb_encrypt_key( in, out, mk, mks, e_d_flag) #define LONGLEN 4 -extern krb5_encrypt_block master_encblock; /* take a v5 keyblock, masquerading as a v4 key, * decrypt it, and convert the resulting v5 keyblock diff --git a/src/kdc/main.c b/src/kdc/main.c index b7c460be7..475bf652b 100644 --- a/src/kdc/main.c +++ b/src/kdc/main.c @@ -36,6 +36,477 @@ #include "adm_proto.h" static int nofork = 0; +static char *kdc_current_rcname = (char *) NULL; +static int rkey_init_done = 0; + +#define KRB5_KDC_MAX_REALMS 32 + +/* + * initialize the replay cache. + */ +krb5_error_code +kdc_initialize_rcache(kcontext, rcache_name) + krb5_context kcontext; + char *rcache_name; +{ + krb5_error_code retval; + extern krb5_deltat krb5_clockskew; + char *rcname; + char *sname; + + rcname = (rcache_name) ? rcache_name : kdc_current_rcname; + if (!rcname) + rcname = KDCRCACHE; + if (!(retval = krb5_rc_resolve_full(kcontext, &kdc_rcache, rcname))) { + /* Recover or initialize the replay cache */ + if (!(retval = krb5_rc_recover(kcontext, kdc_rcache)) || + !(retval = krb5_rc_initialize(kcontext, + kdc_rcache, + krb5_clockskew)) + ) { + /* Expunge the replay cache */ + if (!(retval = krb5_rc_expunge(kcontext, kdc_rcache))) { + sname = kdc_current_rcname; + kdc_current_rcname = strdup(rcname); + if (sname) + free(sname); + } + } + if (retval) + krb5_rc_close(kcontext, kdc_rcache); + } + return(retval); +} + +/* + * Find the realm entry for a given realm. + */ +kdc_realm_t * +find_realm_data(rname, rsize) + char *rname; + krb5_ui_4 rsize; +{ + int i; + for (i=0; irealm_name)) && + !strncmp(rname, kdc_realmlist[i]->realm_name, rsize)) + return(kdc_realmlist[i]); + } + return((kdc_realm_t *) NULL); +} + +krb5_error_code +setup_server_realm(sprinc) + krb5_principal sprinc; +{ + krb5_error_code kret; + kdc_realm_t *newrealm; + + kret = 0; + if (kdc_numrealms > 1) { + if (!(newrealm = find_realm_data(sprinc->realm.data, + (krb5_ui_4) sprinc->realm.length))) + kret = ENOENT; + else + kdc_active_realm = newrealm; + } + else + kdc_active_realm = kdc_realmlist[0]; + return(kret); +} + +static void +finish_realm(rdp) + kdc_realm_t *rdp; +{ + if (rdp->realm_dbname) + free(rdp->realm_dbname); + if (rdp->realm_mpname) + free(rdp->realm_mpname); + if (rdp->realm_stash) + free(rdp->realm_stash); + if (rdp->realm_context) { + if (rdp->realm_mprinc) + krb5_free_principal(rdp->realm_context, rdp->realm_mprinc); + if (rdp->realm_mkey.length && rdp->realm_mkey.contents) + krb5_free_keyblock(rdp->realm_context, &rdp->realm_mkey); + krb5_db_fini(rdp->realm_context); + if (rdp->realm_tgsprinc) + krb5_free_principal(rdp->realm_context, rdp->realm_tgsprinc); + krb5_free_context(rdp->realm_context); + } + memset((char *) rdp, 0, sizeof(*rdp)); +} + +/* + * Initialize a realm control structure from the alternate profile or from + * the specified defaults. + * + * After we're complete here, the essence of the realm is embodied in the + * realm data and we should be all set to begin operation for that realm. + */ +static krb5_error_code +init_realm(progname, rdp, altp, realm, def_dbname, def_mpname, + def_keytype, def_port, def_enctype, def_manual) + char *progname; + kdc_realm_t *rdp; + krb5_pointer altp; + char *realm; + char *def_dbname; + char *def_mpname; + krb5_keytype def_keytype; + krb5_int32 def_port; + krb5_enctype def_enctype; + krb5_boolean def_manual; +{ + krb5_error_code kret; + char *hierarchy[4]; + krb5_boolean manual; + krb5_db_entry db_entry; + int num2get; + krb5_boolean more; + krb5_boolean db_inited; + krb5_int32 ibuf; + krb5_enctype etype; + + kret = EINVAL; + db_inited = 0; + memset((char *) rdp, 0, sizeof(kdc_realm_t)); + if (realm) { + rdp->realm_name = realm; + if (!(kret = krb5_init_context(&rdp->realm_context))) { + hierarchy[0] = realm; + hierarchy[1] = "database_name"; + hierarchy[2] = (char *) NULL; + /* + * Attempt to get the real value for the database file. + */ + if (!altp || (kret = krb5_aprof_get_string(altp, + hierarchy, + TRUE, + &rdp->realm_dbname))) + rdp->realm_dbname = (def_dbname) ? strdup(def_dbname) : + strdup(DEFAULT_KDB_FILE); + + /* + * Attempt to get the real value for the master key name. + */ + hierarchy[1] = "master_key_name"; + if (!altp || (kret = krb5_aprof_get_string(altp, + hierarchy, + TRUE, + &rdp->realm_mpname))) + rdp->realm_mpname = (def_mpname) ? strdup(def_mpname) : + KRB5_KDB_M_NAME; + + /* + * Attempt to get the real value for the master key type. + */ + hierarchy[1] = "master_key_type"; + if (!altp || (kret = krb5_aprof_get_int32(altp, + hierarchy, + TRUE, + &ibuf))) + rdp->realm_mkey.keytype = (def_keytype) ? def_keytype : + KEYTYPE_DES; + else + rdp->realm_mkey.keytype = (krb5_keytype) ibuf; + + /* + * Attempt to get the real value for the primary port. + */ + hierarchy[1] = "port"; + if (!altp || (kret = krb5_aprof_get_int32(altp, + hierarchy, + TRUE, + &rdp->realm_pport))) + rdp->realm_pport = (def_port) ? def_port : KRB5_DEFAULT_PORT; + + /* + * Attempt to get the real value for the encryption type. + */ + hierarchy[1] = "encryption_type"; + if (!altp || (kret = krb5_aprof_get_int32(altp, + hierarchy, + TRUE, + &ibuf))) + etype = (def_enctype) ? def_enctype : DEFAULT_KDC_ETYPE; + else + etype = (krb5_enctype) ibuf; + + if (!valid_etype(etype)) { + com_err(progname, KRB5_PROG_ETYPE_NOSUPP, + "while setting up etype %d", etype); + exit(1); + } + /* + * Attempt to get the real value for the stash file. + */ + hierarchy[1] = "key_stash_file"; + if (!altp || (kret = krb5_aprof_get_string(altp, + hierarchy, + TRUE, + &rdp->realm_stash))) + manual = def_manual; + else + manual = FALSE; + + /* + * Attempt to get the real value for the maximum ticket life. + */ + hierarchy[1] = "max_life"; + if (!altp || (kret = krb5_aprof_get_deltat(altp, + hierarchy, + TRUE, + &rdp->realm_maxlife))) + rdp->realm_maxlife = KRB5_KDB_MAX_LIFE; + + /* + * Attempt to get the real value for the maximum renewable ticket + * life. + */ + hierarchy[1] = "max_renewable_life"; + if (!altp || (kret = krb5_aprof_get_deltat(altp, + hierarchy, + TRUE, + &rdp->realm_maxrlife))) + rdp->realm_maxrlife = KRB5_KDB_MAX_RLIFE; + + /* + * We've got our parameters, now go and setup our realm context. + */ + + /* Set the default realm of this context */ + if (kret = krb5_set_default_realm(rdp->realm_context, realm)) { + com_err(progname, kret, "while setting default realm to %s", + realm); + goto whoops; + } + + /* Assemble and parse the master key name */ + if (kret = krb5_db_setup_mkey_name(rdp->realm_context, + rdp->realm_mpname, + rdp->realm_name, + (char **) NULL, + &rdp->realm_mprinc)) { + com_err(progname, kret, + "while setting up master key name %s for realm %s", + rdp->realm_mpname, realm); + goto whoops; + } + + /* Select the specified encryption type */ + krb5_use_cstype(rdp->realm_context, &rdp->realm_encblock, etype); + + /* + * If there's a stash file, then we have to go get the key + * manually because krb5_db_fetch_mkey() doesn't let us supply + * where we've stashed the master key. + */ + if (rdp->realm_stash) { + FILE *sfile; + krb5_ui_2 keytype; + + if (sfile = fopen(rdp->realm_stash, "r")) { + if ((fread((krb5_pointer) &keytype, 2, 1, sfile) != 1) || + (fread((krb5_pointer) &rdp->realm_mkey.length, + sizeof(rdp->realm_mkey.length), + 1, + sfile) != 1) || + (!(rdp->realm_mkey.contents = (krb5_octet *) + malloc(rdp->realm_mkey.length))) || + (fread((krb5_pointer) rdp->realm_mkey.contents, + sizeof(krb5_octet), + rdp->realm_mkey.length, sfile) != + rdp->realm_mkey.length)) { + com_err(progname, KRB5_KDB_CANTREAD_STORED, + "while reading stash file %s for realm %s", + rdp->realm_stash, realm); + fclose(sfile); + goto whoops; + } + rdp->realm_mkey.keytype = keytype; + fclose(sfile); + } + else { + com_err(progname, errno, + "while opening stash file %s for realm %s", + rdp->realm_stash, realm); + goto whoops; + } + } + else { + /* + * No stash, fetch it. + */ + if (kret = krb5_db_fetch_mkey(rdp->realm_context, + rdp->realm_mprinc, + &rdp->realm_encblock, + manual, + FALSE, + 0, + &rdp->realm_mkey)) { + com_err(progname, kret, + "while fetching master key %s for realm %s", + rdp->realm_mpname, realm); + goto whoops; + } + } + + /* Set and open the database. */ + if (rdp->realm_dbname && + (kret = krb5_db_set_name(rdp->realm_context, + rdp->realm_dbname))) { + com_err(progname, kret, + "while setting database name to %s for realm %s", + rdp->realm_dbname, realm); + goto whoops; + } + if (kret = krb5_db_init(rdp->realm_context)) { + com_err(progname, kret, + "while initializing database for realm %s", realm); + goto whoops; + } + else + db_inited = 1; + + /* Verify the master key */ + if (kret = krb5_db_verify_master_key(rdp->realm_context, + rdp->realm_mprinc, + &rdp->realm_mkey, + &rdp->realm_encblock)) { + com_err(progname, kret, + "while verifying master key for realm %s", realm); + goto whoops; + } + + /* Fetch the master key and get its version number */ + num2get = 1; + if (!(kret = krb5_db_get_principal(rdp->realm_context, + rdp->realm_mprinc, + &db_entry, + &num2get, + &more))) { + if (num2get != 1) + kret = KRB5_KDB_NOMASTERKEY; + else { + if (more) { + krb5_db_free_principal(rdp->realm_context, + &db_entry, + num2get); + kret = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE; + } + } + } + if (kret) { + com_err(progname, kret, + "while fetching master entry for realm %s", realm); + goto whoops; + } + else { + rdp->realm_mkvno = db_entry.kvno; + krb5_db_free_principal(rdp->realm_context, + &db_entry, + num2get); + } + + /* Now preprocess the master key */ + if (kret = krb5_process_key(rdp->realm_context, + &rdp->realm_encblock, + &rdp->realm_mkey)) { + com_err(progname, kret, + "while processing master key for realm %s", realm); + goto whoops; + } + + /* Preformat the TGS name */ + if (kret = krb5_build_principal(rdp->realm_context, + &rdp->realm_tgsprinc, + strlen(realm), + realm, + KRB5_TGS_NAME, + realm, + (char *) NULL)) { + com_err(progname, kret, + "while building TGS name for realm %s", realm); + goto whoops; + } + + /* Get the TGS database entry */ + num2get = 1; + if (!(kret = krb5_db_get_principal(rdp->realm_context, + rdp->realm_tgsprinc, + &db_entry, + &num2get, + &more))) { + if (num2get != 1) + kret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; + else { + if (more) { + krb5_db_free_principal(rdp->realm_context, + &db_entry, + num2get); + kret = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE; + } + } + } + if (kret) { + com_err(progname, kret, + "while fetching TGS entry for realm %s", realm); + goto whoops; + } + else { + if (!(kret = krb5_kdb_decrypt_key(rdp->realm_context, + &rdp->realm_encblock, + &db_entry.key, + &rdp->realm_tgskey))) { + rdp->realm_tgskvno = db_entry.kvno; + } + krb5_db_free_principal(rdp->realm_context, + &db_entry, + num2get); + if (kret) { + com_err(progname, kret, + "while decrypting TGS key for realm %s", realm); + goto whoops; + } + } + if (!rkey_init_done) { + /* + * If all that worked, then initialize the random key + * generators. + */ + for (etype = 0; etype <= krb5_max_cryptosystem; etype++) { + if (krb5_csarray[etype]) { + if ((kret = (*krb5_csarray[etype]->system-> + init_random_key) + (&rdp->realm_mkey, + &krb5_csarray[etype]->random_sequence))) { + com_err(progname, kret, + "while setting up random key generator for etype %d--etype disabled", + etype); + krb5_csarray[etype] = 0; + } + } + } + rkey_init_done = 1; + } + } + else { + com_err(progname, kret, "while getting context for realm %s", + realm); + goto whoops; + } + } + whoops: + /* + * If we choked, then clean up any dirt we may have dropped on the floor. + */ + if (kret) { + finish_realm(rdp); + } + return(kret); +} krb5_sigtype request_exit() @@ -68,31 +539,56 @@ char *name; } void -process_args(argc, argv) -int argc; -char **argv; +initialize_realms(kcontext, altp, argc, argv) + krb5_context kcontext; + krb5_pointer altp; + int argc; + char **argv; { - int c; - krb5_boolean manual = FALSE; - int keytypedone = 0; - char *db_realm = 0; - char *mkey_name = 0; - char *rcname = 0; - char *lrealm; - krb5_error_code retval, retval2; - krb5_enctype kdc_etype = DEFAULT_KDC_ETYPE; - krb5_enctype etype; - extern krb5_deltat krb5_clockskew; + int c; + char *db_name = (char *) NULL; + char *mkey_name = (char *) NULL; + char *rcname = KDCRCACHE; + char *lrealm; + krb5_error_code retval, retval2; + krb5_keytype mkeytype = KEYTYPE_DES; + krb5_enctype kdc_etype = DEFAULT_KDC_ETYPE; + kdc_realm_t *rdatap; + krb5_boolean manual = FALSE; + krb5_int32 pport; extern char *optarg; + /* + * Loop through the option list. Each time we encounter a realm name, + * use the previously scanned options to fill in for defaults. + */ while ((c = getopt(argc, argv, "r:d:mM:k:R:e:p:n")) != EOF) { switch(c) { case 'r': /* realm name for db */ - db_realm = optarg; + if (!find_realm_data(optarg, (krb5_ui_4) strlen(optarg))) { + if (rdatap = (kdc_realm_t *) malloc(sizeof(kdc_realm_t))) { + if (retval = init_realm(argv[0], + rdatap, + altp, + optarg, + db_name, + mkey_name, + mkeytype, + pport, + kdc_etype, + manual)) { + fprintf(stderr,"%s: cannot initialize realm %s\n", + argv[0], optarg); + exit(1); + } + kdc_realmlist[kdc_numrealms] = rdatap; + kdc_numrealms++; + } + } break; case 'd': /* pathname for db */ - dbm_db_name = optarg; + db_name = optarg; break; case 'm': /* manual type-in of master key */ manual = TRUE; @@ -104,14 +600,13 @@ char **argv; nofork++; /* don't detach from terminal */ break; case 'k': /* keytype for master key */ - master_keyblock.keytype = atoi(optarg); - keytypedone++; + mkeytype = atoi(optarg); break; case 'R': rcname = optarg; break; case 'p': - primary_port = atoi(optarg); + pport = atoi(optarg); break; case 'e': kdc_etype = atoi(optarg); @@ -122,226 +617,59 @@ char **argv; exit(1); } } - if (!db_realm) { + + /* + * Check to see if we processed any realms. + */ + if (kdc_numrealms == 0) { /* no realm specified, use default realm */ - if ((retval = krb5_get_default_realm(kdc_context, &lrealm))) { + if ((retval = krb5_get_default_realm(kcontext, &lrealm))) { com_err(argv[0], retval, "while attempting to retrieve default realm"); exit(1); } - db_realm = lrealm; - } - - if (!mkey_name) - mkey_name = KRB5_KDB_M_NAME; - - if (!keytypedone) - master_keyblock.keytype = KEYTYPE_DES; - - if (!rcname) - rcname = KDCRCACHE; - if ((retval = krb5_rc_resolve_full(kdc_context, &kdc_rcache, rcname))) { - com_err(argv[0], retval, "while resolving replay cache '%s'", rcname); - exit(1); - } - if ((retval = krb5_rc_recover(kdc_context, kdc_rcache)) && - (retval2 = krb5_rc_initialize(kdc_context, kdc_rcache, krb5_clockskew))) { - com_err(argv[0], retval, "while recovering replay cache '%s:%s'", - kdc_rcache->ops->type, - krb5_rc_get_name(kdc_context, kdc_rcache)); - com_err(argv[0], retval2, "while initializing replay cache '%s:%s'", - kdc_rcache->ops->type, - krb5_rc_get_name(kdc_context, kdc_rcache)); - exit(1); - } - if ((retval = krb5_rc_expunge(kdc_context, kdc_rcache))) { - com_err(argv[0], retval, "while expunging replay cache '%s:%s'", - kdc_rcache->ops->type, - krb5_rc_get_name(kdc_context, kdc_rcache)); - exit(1); - } - /* assemble & parse the master key name */ - - if ((retval = krb5_db_setup_mkey_name(kdc_context, mkey_name, - db_realm, (char **) 0, - &master_princ))) { - com_err(argv[0], retval, "while setting up master key name"); - (void) krb5_rc_close(kdc_context, kdc_rcache); - exit(1); + if (rdatap = (kdc_realm_t *) malloc(sizeof(kdc_realm_t))) { + if (retval = init_realm(argv[0], + rdatap, + altp, + lrealm, + db_name, + mkey_name, + mkeytype, + pport, + kdc_etype, + manual)) { + fprintf(stderr,"%s: cannot initialize realm %s\n", + argv[0], lrealm); + exit(1); + } + kdc_realmlist[0] = rdatap; + kdc_numrealms++; + } } - if (!valid_etype(kdc_etype)) { - com_err(argv[0], KRB5_PROG_ETYPE_NOSUPP, - "while setting up etype %d", kdc_etype); + /* + * Now handle the replay cache. + */ + if (retval = kdc_initialize_rcache(kcontext, rcname)) { + com_err(argv[0], retval, + "while initializing KDC replay cache"); exit(1); } - krb5_use_cstype(kdc_context, &master_encblock, kdc_etype); - - if ((retval = krb5_db_fetch_mkey(kdc_context, master_princ, - &master_encblock, manual, - FALSE, /* only read it once, if at all */ - 0, &master_keyblock))) { - com_err(argv[0], retval, "while fetching master key"); - (void) krb5_rc_close(kdc_context, kdc_rcache); - exit(1); - } - /* initialize random key generators */ - for (etype = 0; etype <= krb5_max_cryptosystem; etype++) { - if (krb5_csarray[etype]) { - if ((retval = (*krb5_csarray[etype]->system-> - init_random_key)(&master_keyblock, - &krb5_csarray[etype]->random_sequence))) { - com_err(argv[0], retval, "while setting up random key generator for etype %d--etype disabled", etype); - krb5_csarray[etype] = 0; - } - } - } + /* Ensure that this is set for our first request. */ + kdc_active_realm = kdc_realmlist[0]; return; } void -finish_args(prog) -char *prog; +finish_realms(prog) + char *prog; { - char *rtype, *rname; - krb5_error_code retval; - - if (kdc_rcache) { - if (kdc_rcache->ops && kdc_rcache->ops->type) - rtype = strdup(kdc_rcache->ops->type); - else - rtype = strdup("Unknown_rcache_type"); - rname = strdup(krb5_rc_get_name(kdc_context, kdc_rcache)); - if ((retval = krb5_rc_close(kdc_context, kdc_rcache))) { - com_err(prog, retval, "while closing replay cache '%s:%s'", - rtype, rname); - } - free(rtype); - free(rname); - } - return; -} - + int i; -krb5_error_code -init_db(dbname, masterkeyname, masterkeyblock) -char *dbname; -krb5_principal masterkeyname; -krb5_keyblock *masterkeyblock; -{ - krb5_error_code retval; - int nprincs; - krb5_boolean more; - krb5_db_entry server; -#ifdef KRB5_KRB4_COMPAT - extern unsigned char master_key_version; -#endif - - /* set db name if appropriate */ - if (dbname && (retval = krb5_db_set_name(kdc_context, dbname))) - return(retval); - - /* initialize database */ - if ((retval = krb5_db_init(kdc_context))) - return(retval); - - if ((retval = krb5_db_verify_master_key(kdc_context, masterkeyname, - masterkeyblock, - &master_encblock))) { - master_encblock.crypto_entry = 0; - return(retval); - } - -#ifdef KRB5_KRB4_COMPAT - /* get the master key, to extract the master key version number */ - nprincs = 1; - if ((retval = krb5_db_get_principal(kdc_context, masterkeyname, - &server, &nprincs, &more))) { - return(retval); - } - if (nprincs != 1) { - if (nprincs) - krb5_db_free_principal(kdc_context, &server, nprincs); - return(KRB5_KDB_NOMASTERKEY); - } else if (more) { - krb5_db_free_principal(kdc_context, &server, nprincs); - return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE); - } - master_key_version = server.kvno; - krb5_db_free_principal(kdc_context, &server, nprincs); -#endif - - /* do any necessary key pre-processing */ - if ((retval = krb5_process_key(kdc_context, &master_encblock, - masterkeyblock))) { - master_encblock.crypto_entry = 0; - (void) krb5_db_fini(kdc_context); - return(retval); - } - - /* fetch the TGS key, and hold onto it; this is an efficiency hack */ - - /* the master key name here is from the master_princ global, - so we can safely share its substructure */ - - krb5_princ_set_realm(kdc_context, tgs_server, - krb5_princ_realm(kdc_context, masterkeyname)); - /* tgs_server[0] is init data */ - *krb5_princ_component(kdc_context, tgs_server, 1) = - *krb5_princ_realm(kdc_context, masterkeyname); - - nprincs = 1; - if ((retval = krb5_db_get_principal(kdc_context, tgs_server, - &server, &nprincs, &more))) { - return(retval); - } - if (more) { - krb5_db_free_principal(kdc_context, &server, nprincs); - (void) krb5_finish_key(kdc_context, &master_encblock); - memset((char *)&master_encblock, 0, sizeof(master_encblock)); - (void) krb5_db_fini(kdc_context ); - return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE); - } else if (nprincs != 1) { - krb5_db_free_principal(kdc_context, &server, nprincs); - (void) krb5_finish_key(kdc_context, &master_encblock); - memset((char *)&master_encblock, 0, sizeof(master_encblock)); - (void) krb5_db_fini(kdc_context ); - return(KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN); - } - /* convert server.key into a real key (it may be encrypted - in the database) */ - if ((retval = KDB_CONVERT_KEY_OUTOF_DB(kdc_context, &server.key, - &tgs_key))) { - krb5_db_free_principal(kdc_context, &server, nprincs); - (void) krb5_finish_key(kdc_context, &master_encblock); - memset((char *)&master_encblock, 0, sizeof(master_encblock)); - (void) krb5_db_fini(kdc_context ); - return retval; - } - tgs_kvno = server.kvno; - krb5_db_free_principal(kdc_context, &server, nprincs); - return 0; -} - -krb5_error_code -closedown_db() -{ - krb5_error_code retval; - - /* clean up master key stuff */ - retval = krb5_finish_key(kdc_context, &master_encblock); - - memset((char *)&master_encblock, 0, sizeof(master_encblock)); - - memset((char *)tgs_key.contents, 0, tgs_key.length); - - /* close database */ - if (retval) { - (void) krb5_db_fini(kdc_context ); - return retval; - } else - return (krb5_db_fini(kdc_context)); + for (i=0; i extern int errno; -extern short primary_port; -static int udp_port_fd = -1; +static int *udp_port_fds = (int *) NULL; +static u_short *udp_port_nums = (u_short *) NULL; static int sec_udp_port_fd = -1; static fd_set select_fds; -static int select_nfsd; +static int select_nfds; krb5_error_code setup_network(prog) @@ -55,34 +55,57 @@ const char *prog; struct servent *sp; struct sockaddr_in sin; krb5_error_code retval; + u_short default_port; + int i, j, found; FD_ZERO(&select_fds); - select_nfsd = 0; + select_nfds = 0; memset((char *)&sin, 0, sizeof(sin)); - if (primary_port) { - sin.sin_port = htons(primary_port); - } else { - sp = getservbyname(KDC_PORTNAME, "udp"); - if (!sp) - sin.sin_port = htons(KRB5_DEFAULT_PORT); - else - sin.sin_port = sp->s_port; - } - - if ((udp_port_fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) { - retval = errno; - com_err(prog, 0, "Cannot create server socket"); - return retval; - } - - if (bind(udp_port_fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) { - retval = errno; - com_err(prog, 0, "Cannot bind server socket to fd %d", udp_port_fd); - return retval; + sp = getservbyname(KDC_PORTNAME, "udp"); + default_port = (sp) ? ntohs(sp->s_port) : KRB5_DEFAULT_PORT; + if ((udp_port_fds = (int *) malloc(kdc_numrealms * sizeof(int))) && + (udp_port_nums = (u_short *) malloc(kdc_numrealms * sizeof(u_short))) + ) { + for (i=0; irealm_pport) { + found = 1; + break; + } + } + if (!found) { + if ((udp_port_fds[i] = socket(PF_INET, SOCK_DGRAM, 0)) == -1) { + retval = errno; + com_err(prog, 0, "Cannot create server socket on port %d", + kdc_realmlist[i]->realm_pport); + return(retval); + } + udp_port_nums[i] = kdc_realmlist[i]->realm_pport; + sin.sin_port = htons(kdc_realmlist[i]->realm_pport); + if (bind(udp_port_fds[i], + (struct sockaddr *) &sin, + sizeof(sin)) == -1) { + retval = errno; + com_err(prog, 0, "Cannot bind server socket on port %d", + kdc_realmlist[i]->realm_pport); + return(retval); + } + FD_SET(udp_port_fds[i], &select_fds); + if (udp_port_fds[i]+1 > select_nfds) + select_nfds = udp_port_fds[i]+1; + } + else { + udp_port_fds[i] = udp_port_fds[j]; + udp_port_nums[i] = udp_port_nums[j]; + } + } } - FD_SET(udp_port_fd, &select_fds); - if (udp_port_fd+1 > select_nfsd) - select_nfsd = udp_port_fd+1; /* * Now we set up the secondary listening port @@ -106,8 +129,8 @@ const char *prog; return 0; /* Don't give an error if we can't do this */ } FD_SET(sec_udp_port_fd, &select_fds); - if (sec_udp_port_fd+1 > select_nfsd) - select_nfsd = sec_udp_port_fd+1; + if (sec_udp_port_fd+1 > select_nfds) + select_nfds = sec_udp_port_fd+1; return 0; } @@ -176,21 +199,24 @@ const char *prog; { int nfound; fd_set readfds; + int i; - if (udp_port_fd == -1) + if (udp_port_fds == (int *) NULL) return KDC5_NONET; while (!signal_requests_exit) { readfds = select_fds; - nfound = select(select_nfsd, &readfds, 0, 0, 0); + nfound = select(select_nfds, &readfds, 0, 0, 0); if (nfound == -1) { if (errno == EINTR) continue; com_err(prog, errno, "while selecting for network input"); continue; } - if (FD_ISSET(udp_port_fd, &readfds)) - process_packet(udp_port_fd, prog, 0); + for (i=0; i 0) && FD_ISSET(sec_udp_port_fd, &readfds)) process_packet(sec_udp_port_fd, prog, 1); @@ -202,11 +228,15 @@ krb5_error_code closedown_network(prog) const char *prog; { - if (udp_port_fd == -1) + int i; + + if (udp_port_fds == (int *) NULL) return KDC5_NONET; - (void) close(udp_port_fd); - udp_port_fd = -1; + for (i=0; i