summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Park <pjpark@mit.edu>1995-06-23 14:01:45 +0000
committerPaul Park <pjpark@mit.edu>1995-06-23 14:01:45 +0000
commit36b0618997fd316a4f8fff8878c12e30839606a6 (patch)
treefb1aa5208cd78268ad447d65029eeba3327499c2 /src
parent9a108a9cafe6a619a4ba3a7f3f717ff93bd687ab (diff)
downloadkrb5-36b0618997fd316a4f8fff8878c12e30839606a6.tar.gz
krb5-36b0618997fd316a4f8fff8878c12e30839606a6.tar.xz
krb5-36b0618997fd316a4f8fff8878c12e30839606a6.zip
Multiple realm support
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@6137 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src')
-rw-r--r--src/kdc/dispatch.c8
-rw-r--r--src/kdc/do_tgs_req.c10
-rw-r--r--src/kdc/extern.c23
-rw-r--r--src/kdc/extern.h75
-rw-r--r--src/kdc/kerberos_v4.c1
-rw-r--r--src/kdc/main.c828
-rw-r--r--src/kdc/network.c102
7 files changed, 727 insertions, 320 deletions
diff --git a/src/kdc/dispatch.c b/src/kdc/dispatch.c
index b16e55fac1..7f10061e97 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 fbf7ccc5d7..f25e709dd3 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 9c070f2602..757df67b80 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 6bb9092967..fc9e575219 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 36951a178e..1f25cddd54 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 b7c460be70..475bf652b5 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; i<kdc_numrealms; i++) {
+ if ((rsize == strlen(kdc_realmlist[i]->realm_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<kdc_numrealms; i++)
+ finish_realm(kdc_realmlist[i]);
}
/*
@@ -370,39 +698,59 @@ closedown_db()
exit
*/
-krb5_context kdc_context;
-
int main(argc, argv)
int argc;
char *argv[];
{
- krb5_error_code retval;
+ krb5_error_code retval;
+ krb5_context kcontext;
+ krb5_pointer alt_profile;
int errout = 0;
if (strrchr(argv[0], '/'))
argv[0] = strrchr(argv[0], '/')+1;
- krb5_init_context(&kdc_context);
- krb5_init_ets(kdc_context);
- krb5_klog_init(kdc_context, "kdc", argv[0], 1);
+ if (!(kdc_realmlist = (kdc_realm_t **) malloc(sizeof(kdc_realm_t *) *
+ KRB5_KDC_MAX_REALMS))) {
+ fprintf(stderr, "%s: cannot get memory for realm list\n", argv[0]);
+ exit(1);
+ }
+ memset((char *) kdc_realmlist, 0,
+ (size_t) (sizeof(kdc_realm_t *) * KRB5_KDC_MAX_REALMS));
+
+ /*
+ * A note about Kerberos contexts: This context, "kcontext", is used
+ * for the KDC operations, i.e. setup, network connection and error
+ * reporting. The per-realm operations use the "realm_context"
+ * associated with each realm.
+ */
+ krb5_init_context(&kcontext);
+ krb5_init_ets(kcontext);
+ krb5_klog_init(kcontext, "kdc", argv[0], 1);
+ if (retval = krb5_aprof_init(DEFAULT_KDC_PROFILE,
+ KDC_PROFILE_ENV,
+ &alt_profile)) {
+ fprintf(stderr, "%s: warning - cannot find kdc profile\n", argv[0]);
+ alt_profile = (krb5_pointer) NULL;
+ }
- process_args(argc, argv); /* includes reading master key */
+ /*
+ * Scan through the argument list
+ */
+ initialize_realms(kcontext, alt_profile, argc, argv);
+ if (alt_profile)
+ krb5_aprof_finish(alt_profile);
setup_signal_handlers();
- if ((retval = init_db(dbm_db_name, master_princ, &master_keyblock))) {
- com_err(argv[0], retval, "while initializing database");
- finish_args(argv[0]);
- return 1;
- }
if ((retval = setup_network(argv[0]))) {
com_err(argv[0], retval, "while initializing network");
- finish_args(argv[0]);
+ finish_realms(argv[0]);
return 1;
}
if (!nofork && daemon(0, 0)) {
com_err(argv[0], errno, "while detaching from tty");
- finish_args(argv[0]);
+ finish_realms(argv[0]);
return 1;
}
krb5_klog_syslog(LOG_INFO, "commencing operation");
@@ -414,13 +762,9 @@ char *argv[];
com_err(argv[0], retval, "while shutting down network");
errout++;
}
- if ((retval = closedown_db())) {
- com_err(argv[0], retval, "while closing database");
- errout++;
- }
krb5_klog_syslog(LOG_INFO, "shutting down");
krb5_klog_close(kdc_context);
- finish_args(argv[0]);
+ finish_realms(argv[0]);
return errout;
}
diff --git a/src/kdc/network.c b/src/kdc/network.c
index 5b8b8b3e0f..848a6b92b7 100644
--- a/src/kdc/network.c
+++ b/src/kdc/network.c
@@ -41,12 +41,12 @@
#include <netdb.h>
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; i<kdc_numrealms; i++) {
+ udp_port_fds[i] = -1;
+ udp_port_nums[i] = 0;
+ }
+
+ for (i=0; i<kdc_numrealms; i++) {
+ found = 0;
+ for (j=0; j<i; j++) {
+ if (udp_port_nums[j] == kdc_realmlist[i]->realm_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<kdc_numrealms; i++) {
+ if (FD_ISSET(udp_port_fds[i], &readfds))
+ process_packet(udp_port_fds[i], prog, 0);
+ }
if ((sec_udp_port_fd > 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<kdc_numrealms; i++)
+ (void) close(udp_port_fds[i]);
+ free(udp_port_fds);
+ free(udp_port_nums);
if (sec_udp_port_fd != -1)
(void) close(sec_udp_port_fd);