Reorganize KDC profile and network port handling
authorPaul Park <pjpark@mit.edu>
Wed, 12 Jul 1995 18:57:20 +0000 (18:57 +0000)
committerPaul Park <pjpark@mit.edu>
Wed, 12 Jul 1995 18:57:20 +0000 (18:57 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@6287 dc483132-0cff-0310-8789-dd5450dbe970

src/kdc/ChangeLog
src/kdc/extern.h
src/kdc/kdc_util.h
src/kdc/krb5kdc.M
src/kdc/main.c
src/kdc/network.c

index 554cc61404ccf2cf6626f79e95c6cf5704fdfbc0..4f708b140e8c1d1d68cf58539f7834f5ca098035 100644 (file)
@@ -1,3 +1,18 @@
+
+Wed Jul 12 12:19:44 EDT 1995   Paul Park       (pjpark@mit.edu)
+       * main.c - Reorganize KDC profile handling so that the hierarchy for
+               locating per-realm data is [realms]->realm->tag.  Add
+               [kdcdefaults] section with primary_ports and secondary_ports
+               to list ports to listen on.  Consolidate all port location here
+               from network.c.  Add -s flag and change meaning of -p flag to
+               be the default if none specified in KDC or Kerberos profile.
+       * network.c - Open list of primary ports and then per-realm ports.
+               Handle secondary ports just like primary ports except that
+               bind failures are only warnings.  Support more than one
+               secondary port.
+       * extern,kdc_util.h - Add supporting definitions.
+       * krb5kdc.M - update description of -p and add description of -s.
+
 Tue Jul 11 07:35:12 1995  Ezra Peisach  <epeisach@kangaroo.mit.edu>
 
        * kerberos_v4.c: Add prototype for set_tgtkey
index fc9e57521906f993a9f05a4ed38ea9f9d9f421f2..8b3bd8db4199b1661020cc295b7dd27648a12781 100644 (file)
@@ -32,6 +32,7 @@ typedef struct __kdc_realm_data {
      */
     char *             realm_name;     /* Realm name                       */
     krb5_context       realm_context;  /* Context to be used for realm     */
+    char *             realm_profile;  /* Profile file for this realm      */
     /*
      * Database per-realm data.
      */
@@ -52,6 +53,7 @@ typedef struct __kdc_realm_data {
      */
     krb5_encrypt_block realm_encblock; /* Per-realm master encryption block*/
     krb5_int32         realm_pport;    /* Per-realm primary KDC port.      */
+    krb5_int32         realm_sport;    /* Per-realm secondary KDC port.    */
     /*
      * Per-realm parameters.
      */
index 605380f4ca29a082489c1a1ff93c81544f686cff..5e3960c2f9d3c48246b4d3cf2a3db6e8a68730fc 100644 (file)
@@ -95,7 +95,9 @@ krb5_error_code kdc_initialize_rcache PROTOTYPE((krb5_context, char *));
 
 /* network.c */
 krb5_error_code listen_and_process PROTOTYPE((const char *));
-krb5_error_code setup_network PROTOTYPE((const char *));
+krb5_error_code setup_network PROTOTYPE((const char *,
+                                        int *,
+                                        int *));
 krb5_error_code closedown_network PROTOTYPE((const char *));
 void process_packet PROTOTYPE((int, const char *, int));
 
index 89532585c6251f993e06e990386ef430c991a7c7..45af02434bbc5a88a1a256de1dbefa0f1554df2c 100644 (file)
@@ -38,6 +38,9 @@ krb5kdc \- Kerberos V5 KDC
 .B \-p
 .I portnum
 ] [
+.B \-s
+.I portnum
+] [
 .B \-m
 ] [
 .B \-r
@@ -78,9 +81,22 @@ the default is KRB5_KDB_M_NAME (usually "K/M" in the KDC's realm).
 The
 .B \-p
 .I portnum
-option specifies the UDP port number which the KDC should listen on for
-requests.  If none is specified, then the value in /etc/services is used,
-with an ultimate default value of KRB5_DEFAULT_PORT, or 88.
+option specifies the default UDP port number which the KDC should listen on for
+Kerberos version 5 requests.  This value is used when no port is specified in
+the KDC profile and when no port is specified in the Kerberos configuration
+file.
+If no value is available, then the value in /etc/services for service
+"kerberos" is used.
+.PP
+The
+.B \-s
+.I portnum
+option specifies the default UDP port number which the KDC should listen on for
+Kerberos version 4 requests.  This value is used when no port is specified in
+the kdc profile and when no port is specified in the Kerberos configuration
+file.
+If no value is available, then the value in /etc/services for service
+"kerberos-sec" is used.
 .PP
 The
 .B \-m
index 676d73852543ae6bc0521ebb02f9c77d65694e29..6c046ba4f89a6be2f36563e95f8dc2771da5e982 100644 (file)
@@ -27,6 +27,7 @@
 #include <syslog.h>
 #include <signal.h>
 #include <errno.h>
+#include <netdb.h>
 
 #include "com_err.h"
 #include "k5-int.h"
@@ -55,6 +56,180 @@ static int rkey_init_done = 0;
 
 #define        KRB5_KDC_MAX_REALMS     32
 
+/*
+ * Get port information for a realm.  The precedence is:
+ *     [realms]-><realm>-><name> in profile (if our hostname and has a port)
+ *     defport
+ *     /etc/services entry matching <service>
+ */
+static krb5_int32
+get_realm_port(ctx, realm, name, defport, service)
+    krb5_context       ctx;
+    char               *realm;
+    char               *name;
+    krb5_int32         defport;
+    char               *service;
+{
+    krb5_error_code    kret;
+    char               our_host_name[MAXHOSTNAMELEN];
+    struct hostent     *our_hostent;
+    struct servent     *our_servent;
+    krb5_int32         retval;
+    krb5_boolean       found;
+
+    /*
+     * Some preliminaries here.  Get our hostname and our host entry.
+     */
+    found = 0;
+    if (!gethostname(our_host_name, sizeof(our_host_name)) &&
+       (our_hostent = gethostbyname(our_host_name))) {
+       const char      *hierarchy[4];
+       char            **hostlist;
+
+       hostlist = (char **) NULL;
+       hierarchy[0] = "realms";
+       hierarchy[1] = realm;
+       hierarchy[2] = name;
+       hierarchy[3] = (char *) NULL;
+       if (!(kret = profile_get_values(ctx->profile, hierarchy, &hostlist))) {
+           int         hi;
+           char        *cport;
+           char        *cp;
+           int         ai;
+           krb5_int32  pport;
+
+           cport = (char *) NULL;
+           for (hi=0; hostlist[hi]; hi++) {
+               /*
+                * This knows a little too much about the format of profile
+                * entries.  Shouldn't it just be some sort of tuple?
+                *
+                * The form is assumed to be:
+                *      <name> = <hostname>[:<portname>[<whitespace>]]
+                */
+               pport = -1;
+               cp = strchr(hostlist[hi], ' ');
+               if (cp)
+                   *cp = '\0';
+               cp = strchr(hostlist[hi], '\t');
+               if (cp)
+                   *cp = '\0';
+               cport = strchr(hostlist[hi], ':');
+               if (cport) {
+                   *cport = '\0';
+                   cport++;
+                   if (sscanf(cport, "%d", &pport) == 1) {
+                       pport = -1;
+                   }
+               }
+               /*
+                * We've stripped away the crud.  Now check to see if the
+                * profile entry matches our hostname.  If so, then this
+                * is the one to use.  Additionally, check the host alias
+                * list.
+                */
+               if (!strcmp(hostlist[hi], our_hostent->h_name)) {
+                   if (pport != -1) {
+                       retval = pport;
+                       found = 1;
+                   }
+               }
+               else {
+                   for (ai=0; our_hostent->h_aliases[ai]; ai++) {
+                       if (!strcmp(hostlist[hi],
+                                   our_hostent->h_aliases[ai])) {
+                           if (pport != -1) {
+                               retval = pport;
+                               found = 1;
+                           }
+                           break;
+                       }
+                   }
+               }
+           }
+           krb5_xfree(hostlist);
+       }
+    }
+    /*
+     * If we didn't find an entry in the profile, then use the default.
+     * If it's no good, then attempt to find it in /etc/services.
+     */
+    if (!found) {
+       retval = defport;
+       /* Get the service entry out of /etc/services */
+       if (retval <= 0) {
+           if (our_servent = getservbyname(service, "udp"))
+               retval = ntohs(our_servent->s_port);
+       }
+    }
+    return(retval);
+}
+
+/*
+ * Convert a string of the form <int>[,<int>]* to a list of ints.
+ */
+static int *
+string2intlist(string)
+    char       *string;
+{
+    int                nints, i;
+    char       *cp;
+    int                *intlist;
+
+    for ((nints=1, cp=string); *cp; cp++)
+       if (*cp == ',')
+           nints++;
+    if (intlist = (int *) malloc((nints+1) * sizeof(int))) {
+       cp = string;
+       for (i=0; i<nints; i++) {
+           if (sscanf(cp, "%d", &intlist[i]) != 1) {
+               free(intlist);
+               intlist = (int *) NULL;
+               break;
+           }
+           while ((*cp != ',') && (*cp != '\0'))
+               cp++;
+           cp++;
+       }
+       if (intlist)
+           intlist[nints] = -1;
+    }
+    return(intlist);
+}
+
+/*
+ * Get default portlists.
+ */
+static void
+get_default_portlists(aprof, plistp, slistp)
+    krb5_pointer       aprof;
+    int                        **plistp;
+    int                        **slistp;
+{
+    int                *plist;
+    int                *slist;
+    const char *hierarchy[3];
+    char       *liststring;
+
+    plist = slist = (int *) NULL;
+    if (aprof) {
+       hierarchy[0] = "kdcdefaults";
+       hierarchy[1] = "primary_ports";
+       hierarchy[2] = (char *) NULL;
+       if (!krb5_aprof_get_string(aprof, hierarchy, TRUE, &liststring)) {
+           plist = string2intlist(liststring);
+           krb5_xfree(liststring);
+       }
+       hierarchy[1] = "secondary_ports";
+       if (!krb5_aprof_get_string(aprof, hierarchy, TRUE, &liststring)) {
+           slist = string2intlist(liststring);
+           krb5_xfree(liststring);
+       }
+    }
+    *plistp = plist;
+    *slistp = slist;
+}
+
 /*
  * initialize the replay cache.
  */
@@ -161,7 +336,7 @@ finish_realm(rdp)
  */
 static krb5_error_code
 init_realm(progname, rdp, altp, realm, def_dbname, def_mpname,
-                def_keytype, def_port, def_enctype, def_manual)
+                def_keytype, def_port, def_sport, def_enctype, def_manual)
     char               *progname;
     kdc_realm_t                *rdp;
     krb5_pointer       altp;
@@ -170,6 +345,7 @@ init_realm(progname, rdp, altp, realm, def_dbname, def_mpname,
     char               *def_mpname;
     krb5_keytype       def_keytype;
     krb5_int32         def_port;
+    krb5_int32         def_sport;
     krb5_enctype       def_enctype;
     krb5_boolean       def_manual;
 {
@@ -189,12 +365,40 @@ init_realm(progname, rdp, altp, realm, def_dbname, def_mpname,
     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;
+           hierarchy[0] = "realms";
+           hierarchy[1] = realm;
+           hierarchy[2] = "profile";
+           hierarchy[3] = (char *) NULL;
+           /*
+            * Before any more per-realm initialization goes on, get the 
+            * per-realm profile, if any.
+            */
+           if (altp && !(kret = krb5_aprof_get_string(altp,
+                                                      hierarchy,
+                                                      TRUE,
+                                                      &rdp->realm_profile))) {
+               const char *filenames[2];
+
+               /*
+                * XXX - this knows too much about contexts.
+                */
+               filenames[0] = rdp->realm_profile;
+               filenames[1] = (char *) NULL;
+               if (rdp->realm_context->profile)
+                   profile_release(rdp->realm_context->profile);
+               if (kret = profile_init(filenames,
+                                       &rdp->realm_context->profile)) {
+                   com_err(progname, kret,
+                           "while loading profile %s for realm %s",
+                           rdp->realm_profile, realm);
+                   goto whoops;
+               }
+           }
+
            /*
             * Attempt to get the real value for the database file.
             */
+           hierarchy[2] = "database_name";
            if (!altp || (kret = krb5_aprof_get_string(altp,
                                                       hierarchy,
                                                       TRUE,
@@ -205,7 +409,7 @@ init_realm(progname, rdp, altp, realm, def_dbname, def_mpname,
            /*
             * Attempt to get the real value for the master key name.
             */
-           hierarchy[1] = "master_key_name";
+           hierarchy[2] = "master_key_name";
            if (!altp || (kret = krb5_aprof_get_string(altp,
                                                       hierarchy,
                                                       TRUE,
@@ -216,7 +420,7 @@ init_realm(progname, rdp, altp, realm, def_dbname, def_mpname,
            /*
             * Attempt to get the real value for the master key type.
             */
-           hierarchy[1] = "master_key_type";
+           hierarchy[2] = "master_key_type";
            if (!altp || (kret = krb5_aprof_get_int32(altp,
                                                      hierarchy,
                                                      TRUE,
@@ -229,17 +433,37 @@ init_realm(progname, rdp, altp, realm, def_dbname, def_mpname,
            /*
             * Attempt to get the real value for the primary port.
             */
-           hierarchy[1] = "port";
+           hierarchy[2] = "port";
            if (!altp || (kret = krb5_aprof_get_int32(altp,
                                                      hierarchy,
                                                      TRUE,
-                                                     &rdp->realm_pport)))
-               rdp->realm_pport = (def_port) ? def_port : KRB5_DEFAULT_PORT;
+                                                     &rdp->realm_pport))) {
+               rdp->realm_pport = get_realm_port(rdp->realm_context,
+                                                 realm,
+                                                 "kdc",
+                                                 def_port,
+                                                 KDC_PORTNAME);
+           }
+
+           /*
+            * Attempt to get the real value for the secondary port.
+            */
+           hierarchy[2] = "secondary_port";
+           if (!altp || (kret = krb5_aprof_get_int32(altp,
+                                                     hierarchy,
+                                                     TRUE,
+                                                     &rdp->realm_sport))) {
+               rdp->realm_sport = get_realm_port(rdp->realm_context,
+                                                 realm,
+                                                 "v4kdc",
+                                                 def_sport,
+                                                 KDC_SECONDARY_PORTNAME);
+           }
 
            /*
             * Attempt to get the real value for the encryption type.
             */
-           hierarchy[1] = "encryption_type";
+           hierarchy[2] = "encryption_type";
            if (!altp || (kret = krb5_aprof_get_int32(altp,
                                                      hierarchy,
                                                      TRUE,
@@ -256,7 +480,7 @@ init_realm(progname, rdp, altp, realm, def_dbname, def_mpname,
            /*
             * Attempt to get the real value for the stash file.
             */
-           hierarchy[1] = "key_stash_file";
+           hierarchy[2] = "key_stash_file";
            if (!altp || (kret = krb5_aprof_get_string(altp,
                                                       hierarchy,
                                                       TRUE,
@@ -268,7 +492,7 @@ init_realm(progname, rdp, altp, realm, def_dbname, def_mpname,
            /*
             * Attempt to get the real value for the maximum ticket life.
             */
-           hierarchy[1] = "max_life";
+           hierarchy[2] = "max_life";
            if (!altp || (kret = krb5_aprof_get_deltat(altp,
                                                       hierarchy,
                                                       TRUE,
@@ -279,7 +503,7 @@ init_realm(progname, rdp, altp, realm, def_dbname, def_mpname,
             * Attempt to get the real value for the maximum renewable ticket
             * life.
             */
-           hierarchy[1] = "max_renewable_life";
+           hierarchy[2] = "max_renewable_life";
            if (!altp || (kret = krb5_aprof_get_deltat(altp,
                                                       hierarchy,
                                                       TRUE,
@@ -570,15 +794,16 @@ initialize_realms(kcontext, altp, argc, argv)
     krb5_enctype       kdc_etype = DEFAULT_KDC_ETYPE;
     kdc_realm_t                *rdatap;
     krb5_boolean       manual = FALSE;
-    krb5_int32         pport;
+    krb5_int32         pport, sport;
 
     extern char *optarg;
 
+    pport = sport = -1;
     /*
      * 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) {
+    while ((c = getopt(argc, argv, "r:d:mM:k:R:e:p:s:n")) != EOF) {
        switch(c) {
        case 'r':                       /* realm name for db */
            if (!find_realm_data(optarg, (krb5_ui_4) strlen(optarg))) {
@@ -591,6 +816,7 @@ initialize_realms(kcontext, altp, argc, argv)
                                            mkey_name,
                                            mkeytype,
                                            pport,
+                                           sport,
                                            kdc_etype,
                                            manual)) {
                        fprintf(stderr,"%s: cannot initialize realm %s\n",
@@ -623,6 +849,9 @@ initialize_realms(kcontext, altp, argc, argv)
        case 'p':
            pport = atoi(optarg);
            break;
+       case 's':
+           sport = atoi(optarg);
+           break;
        case 'e':
            kdc_etype = atoi(optarg);
            break;
@@ -652,6 +881,7 @@ initialize_realms(kcontext, altp, argc, argv)
                                    mkey_name,
                                    mkeytype,
                                    pport,
+                                   sport,
                                    kdc_etype,
                                    manual)) {
                fprintf(stderr,"%s: cannot initialize realm %s\n",
@@ -720,6 +950,7 @@ char *argv[];
     krb5_error_code    retval;
     krb5_context       kcontext;
     krb5_pointer       alt_profile;
+    int                        *primaries, *secondaries;
     int errout = 0;
 
     if (strrchr(argv[0], '/'))
@@ -732,6 +963,7 @@ char *argv[];
     }
     memset((char *) kdc_realmlist, 0,
           (size_t) (sizeof(kdc_realm_t *) * KRB5_KDC_MAX_REALMS));
+    primaries = secondaries = (int *) NULL;
 
     /*
      * A note about Kerberos contexts: This context, "kcontext", is used
@@ -754,11 +986,16 @@ char *argv[];
      */
     initialize_realms(kcontext, alt_profile, argc, argv);
 
+    /*
+     * Get the default port lists.
+     */
+    get_default_portlists(alt_profile, &primaries, &secondaries);
+
     if (alt_profile)
        krb5_aprof_finish(alt_profile);
     setup_signal_handlers();
 
-    if ((retval = setup_network(argv[0]))) {
+    if ((retval = setup_network(argv[0], primaries, secondaries))) {
        com_err(argv[0], retval, "while initializing network");
        finish_realms(argv[0]);
        return 1;
@@ -780,6 +1017,10 @@ char *argv[];
     krb5_klog_syslog(LOG_INFO, "shutting down");
     krb5_klog_close(kdc_context);
     finish_realms(argv[0]);
+    if (primaries)
+       free(primaries);
+    if (secondaries)
+       free(secondaries);
     return errout;
 }
 
index b7eb556e6ed5d2101eba3828d57f5e699c8ff470..60688e6b1a55e3689f30f9b7532418fba4c0e64a 100644 (file)
 #include <sys/select.h>
 #endif
 #include <arpa/inet.h>
-#include <netdb.h>
 
 extern int errno;
 
 static int *udp_port_fds = (int *) NULL;
 static u_short *udp_port_nums = (u_short *) NULL;
-static int sec_udp_port_fd = -1;
+static int n_udp_ports = 0;
+static int *sec_udp_port_fds = (int *) NULL;
+static u_short *sec_udp_port_nums = (u_short *) NULL;
+static int n_sec_udp_ports = 0;
 static fd_set select_fds;
 static int select_nfds;
 
 krb5_error_code
-setup_network(prog)
+setup_network(prog, p_ports, s_ports)
 const char *prog;
+int *p_ports;
+int *s_ports;
 {
     struct servent *sp;
     struct sockaddr_in sin;
     krb5_error_code retval;
-    u_short    default_port;
     int i, j, found;
+    int npports, nsports;
 
     FD_ZERO(&select_fds);
     select_nfds = 0;
     memset((char *)&sin, 0, sizeof(sin));
-    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++) {
+
+    /*
+     * Count the number of primary and secondary ports supplied to us.
+     */
+    npports = 0;
+    if (p_ports)
+       for (npports=0; p_ports[npports] > 0; npports++);
+    nsports = 0;
+    if (s_ports)
+       for (nsports=0; s_ports[nsports] > 0; nsports++);
+
+    /*
+     * Now handle the primary ports.
+     */
+    if ((udp_port_fds = (int *) malloc((kdc_numrealms+npports)
+                                      * sizeof(int))) &&
+       (udp_port_nums = (u_short *) malloc((kdc_numrealms+npports)
+                                           * sizeof(u_short)))) {
+       /* Zero it out */
+       for (i=0; i<(kdc_numrealms+npports); i++) {
            udp_port_fds[i] = -1;
            udp_port_nums[i] = 0;
        }
 
-       for (i=0; i<kdc_numrealms; i++) {
+       /*
+        * First handle any explicitly named primary ports.
+        */
+       for (i=0; i<npports; i++) {
            found = 0;
            for (j=0; j<i; j++) {
-               if (udp_port_nums[j] == kdc_realmlist[i]->realm_pport) {
+               if (udp_port_nums[j] == p_ports[i]) {
                    found = 1;
                    break;
                }
@@ -83,17 +104,17 @@ const char *prog;
                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);
+                           p_ports[i]);
                    return(retval);
                }
-               udp_port_nums[i] = kdc_realmlist[i]->realm_pport;
-               sin.sin_port = htons(kdc_realmlist[i]->realm_pport);
+               udp_port_nums[i] = p_ports[i];
+               sin.sin_port = htons(p_ports[i]);
                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);
+                           p_ports[i]);
                    return(retval);
                }
                FD_SET(udp_port_fds[i], &select_fds);
@@ -105,33 +126,157 @@ const char *prog;
                udp_port_nums[i] = udp_port_nums[j];
            }
        }
+
+       /* Now handle each realm */
+       for (i=0; i<kdc_numrealms; i++) {
+           found = 0;
+           for (j=0; j<(npports+i); j++) {
+               if (udp_port_nums[j] == kdc_realmlist[i]->realm_pport) {
+                   found = 1;
+                   break;
+               }
+           }
+           if (!found) {
+               if ((udp_port_fds[npports+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[npports+i] = kdc_realmlist[i]->realm_pport;
+               sin.sin_port = htons(kdc_realmlist[i]->realm_pport);
+               if (bind(udp_port_fds[npports+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[npports+i], &select_fds);
+               if (udp_port_fds[npports+i]+1 > select_nfds)
+                   select_nfds = udp_port_fds[npports+i]+1;
+           }
+           else {
+               udp_port_fds[npports+i] = udp_port_fds[j];
+               udp_port_nums[npports+i] = udp_port_nums[j];
+           }
+       }
+       n_udp_ports = kdc_numrealms + npports;
     }
 
     /*
-     * Now we set up the secondary listening port
+     * Now we set up the secondary listening ports.  Special case here.
+     * If the first secondary port is -1, then we don't listen on secondary
+     * ports.
      */
+    if ((!s_ports || (s_ports[0] != -1)) &&
+       (sec_udp_port_fds = (int *) malloc((kdc_numrealms+nsports)
+                                      * sizeof(int))) &&
+       (sec_udp_port_nums = (u_short *) malloc((kdc_numrealms+nsports)
+                                           * sizeof(u_short)))) {
+       /* Zero it out */
+       for (i=0; i<(kdc_numrealms+nsports); i++) {
+           sec_udp_port_fds[i] = -1;
+           sec_udp_port_nums[i] = 0;
+       }
 
-    sp = getservbyname(KDC_SECONDARY_PORTNAME, "udp");
-    if (!sp && sin.sin_port == htons(KRB5_DEFAULT_SEC_PORT)) {
-       com_err(prog, 0, "%s/udp service unknown\n",
-               KDC_SECONDARY_PORTNAME);
-       return 0;               /* Don't give an error if we can't */
-                               /* find it */
-    }
-    if ((sec_udp_port_fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
-       com_err(prog, errno, "while trying to create secondary server socket");
-       return 0;               /* Don't give an error we we can't do this */
-    }
-    memset((char *)&sin, 0, sizeof(sin));
-    sin.sin_port = sp ? sp->s_port : htons(KRB5_DEFAULT_SEC_PORT);
-    if (bind(sec_udp_port_fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
-       com_err(prog, errno, "while trying to bind secondary server socket");
-       return 0;               /* Don't give an error if we can't do this */
+       /*
+        * First handle any explicitly named secondary ports.
+        */
+       for (i=0; i<nsports; i++) {
+           found = 0;
+           for (j=0; j<i; j++) {
+               if (sec_udp_port_nums[j] == s_ports[i]) {
+                   found = 1;
+                   break;
+               }
+           }
+           if (!found) {
+               if ((sec_udp_port_fds[i] =
+                    socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
+                   retval = errno;
+                   com_err(prog, 0,
+                           "Cannot create secondary server socket on port %d",
+                           s_ports[i]);
+                   return(retval);
+               }
+               sec_udp_port_nums[i] = s_ports[i];
+               sin.sin_port = htons(s_ports[i]);
+               if (bind(sec_udp_port_fds[i],
+                        (struct sockaddr *) &sin,
+                        sizeof(sin)) == -1) {
+                   retval = errno;
+                   com_err(prog, 0,
+                           "Cannot bind secondary server socket on port %d",
+                           s_ports[i]);
+                   close(sec_udp_port_fds[i]);
+                   sec_udp_port_fds[i] = -1;
+                   continue;
+               }
+               FD_SET(sec_udp_port_fds[i], &select_fds);
+               if (sec_udp_port_fds[i]+1 > select_nfds)
+                   select_nfds = sec_udp_port_fds[i]+1;
+           }
+           else {
+               sec_udp_port_fds[i] = sec_udp_port_fds[j];
+               sec_udp_port_nums[i] = sec_udp_port_nums[j];
+           }
+       }
+
+       /* Now handle each realm */
+       for (i=0; i<kdc_numrealms; i++) {
+           if (kdc_realmlist[i]->realm_sport > 0) {
+               found = 0;
+               for (j=0; j<(nsports+i); j++) {
+                   if (sec_udp_port_nums[j] ==
+                       kdc_realmlist[i]->realm_sport) {
+                       found = 1;
+                       break;
+                   }
+               }
+               if (!found && (kdc_realmlist[i]->realm_sport > 0)) {
+                   if ((sec_udp_port_fds[nsports+i] =
+                        socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
+                       retval = errno;
+                       com_err(prog, 0,
+                               "Cannot create secondary server socket on port %d",
+                               kdc_realmlist[i]->realm_sport);
+                       return(retval);
+                   }
+                   sec_udp_port_nums[nsports+i] =
+                       kdc_realmlist[i]->realm_sport;
+                   sin.sin_port = htons(kdc_realmlist[i]->realm_sport);
+                   if (bind(sec_udp_port_fds[nsports+i],
+                            (struct sockaddr *) &sin,
+                            sizeof(sin)) == -1) {
+                       retval = errno;
+                       com_err(prog, 0,
+                               "Cannot bind secondary server socket on port %d",
+                               kdc_realmlist[i]->realm_sport);
+                       close(sec_udp_port_fds[nsports+i]);
+                       sec_udp_port_fds[nsports+i] = -1;
+                       continue;
+                   }
+                   FD_SET(sec_udp_port_fds[nsports+i], &select_fds);
+                   if (sec_udp_port_fds[nsports+i]+1 > select_nfds)
+                       select_nfds = sec_udp_port_fds[nsports+i]+1;
+               }
+               else {
+                   if (kdc_realmlist[i]->realm_sport > 0) {
+                       sec_udp_port_fds[nsports+i] = sec_udp_port_fds[j];
+                       sec_udp_port_nums[nsports+i] = sec_udp_port_nums[j];
+                   }
+                   else {
+                       sec_udp_port_fds[nsports+i] = -1;
+                       sec_udp_port_nums[nsports+i] = -1;
+                   }
+               }
+           }
+       }
+       n_sec_udp_ports = kdc_numrealms + nsports;
     }
-    FD_SET(sec_udp_port_fd, &select_fds);
-    if (sec_udp_port_fd+1 > select_nfds)
-           select_nfds = sec_udp_port_fd+1;
-    
     return 0;
 }
 
@@ -200,6 +345,7 @@ const char *prog;
     int                        nfound;
     fd_set             readfds;
     int                        i;
+    int                        fdfound;
 
     if (udp_port_fds == (int *) NULL)
        return KDC5_NONET;
@@ -213,13 +359,24 @@ const char *prog;
            com_err(prog, errno, "while selecting for network input");
            continue;
        }
-       for (i=0; i<kdc_numrealms; i++) {
-           if (FD_ISSET(udp_port_fds[i], &readfds))
+       fdfound = 0;
+       for (i=0; i<n_udp_ports; i++) {
+           if (FD_ISSET(udp_port_fds[i], &readfds)) {
                process_packet(udp_port_fds[i], prog, 0);
+               fdfound = 1;
+               break;
+           }
        }
 
-       if ((sec_udp_port_fd > 0) && FD_ISSET(sec_udp_port_fd, &readfds))
-           process_packet(sec_udp_port_fd, prog, 1);
+       if (!fdfound) {
+           for (i=0; i<n_sec_udp_ports; i++) {
+               if (FD_ISSET(sec_udp_port_fds[i], &readfds)) {
+                   process_packet(sec_udp_port_fds[i], prog, 1);
+                   fdfound = 1;
+                   break;
+               }
+           }
+       }
     }
     return 0;
 }
@@ -233,14 +390,20 @@ const char *prog;
     if (udp_port_fds == (int *) NULL)
        return KDC5_NONET;
 
-    for (i=0; i<kdc_numrealms; i++)
-       (void) close(udp_port_fds[i]);
+    for (i=0; i<n_udp_ports; i++) {
+       if (udp_port_fds[i] >= 0)
+           (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);
-    sec_udp_port_fd = -1;
+    for (i=0; i<n_sec_udp_ports; i++) {
+       if (sec_udp_port_fds[i] >= 0)
+           (void) close(sec_udp_port_fds[i]);
+    }
+    free(sec_udp_port_fds);
+    free(sec_udp_port_nums);
+
     return 0;
 }