Multiple realm support
authorPaul Park <pjpark@mit.edu>
Fri, 23 Jun 1995 14:01:45 +0000 (14:01 +0000)
committerPaul Park <pjpark@mit.edu>
Fri, 23 Jun 1995 14:01:45 +0000 (14:01 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@6137 dc483132-0cff-0310-8789-dd5450dbe970

src/kdc/dispatch.c
src/kdc/do_tgs_req.c
src/kdc/extern.c
src/kdc/extern.h
src/kdc/kerberos_v4.c
src/kdc/main.c
src/kdc/network.c

index b16e55fac1f21ff76f31894df059dbe055762f90..7f10061e9737d0ea40eaa1ba96ebc0f6aa2cf18d 100644 (file)
@@ -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);
        }
     }
index fbf7ccc5d708c4dcb78d95cc9a451a56eea4721b..f25e709dd36a6ce0c041fb395ff189be2dc2e411 100644 (file)
@@ -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;
        }
index 9c070f26027c662cd485b9aec5d68910640b2646..757df67b80ebe66200ccf5e978dfe90bdf9754c6 100644 (file)
 #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;
index 6bb9092967fe1d30c5787582a990fb09dacabb8e..fc9e57521906f993a9f05a4ed38ea9f9d9f421f2 100644 (file)
 #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__ */
index 36951a178e8a4d061192d5d53448d9b3b65b3781..1f25cddd5441d4c7551257d2bd632f69a371a79e 100644 (file)
@@ -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
index b7c460be7055358f68f28ab7b4d884fbea3ac400..475bf652b56ce4a7bed1f7270d5fe2a4467d75cd 100644 (file)
 #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;
 }
 
index 5b8b8b3e0f3170c034675dd841b910ae607829fd..848a6b92b7328cf04779b8996a6e4e26906abad0 100644 (file)
 #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);