2175566497d09321ed23f7d9514768339ad835e4
[krb5.git] / src / kdc / main.c
1 /*
2  * kdc/main.c
3  *
4  * Copyright 1990 by the Massachusetts Institute of Technology.
5  *
6  * Export of this software from the United States of America may
7  *   require a specific license from the United States Government.
8  *   It is the responsibility of any person or organization contemplating
9  *   export to obtain such a license before exporting.
10  * 
11  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
12  * distribute this software and its documentation for any purpose and
13  * without fee is hereby granted, provided that the above copyright
14  * notice appear in all copies and that both that copyright notice and
15  * this permission notice appear in supporting documentation, and that
16  * the name of M.I.T. not be used in advertising or publicity pertaining
17  * to distribution of the software without specific, written prior
18  * permission.  M.I.T. makes no representations about the suitability of
19  * this software for any purpose.  It is provided "as is" without express
20  * or implied warranty.
21  * 
22  *
23  * Main procedure body for the KDC server process.
24  */
25
26 #include <stdio.h>
27 #include <syslog.h>
28 #include <signal.h>
29 #include <errno.h>
30 #include <netdb.h>
31
32 #include "k5-int.h"
33 #include "com_err.h"
34 #include "adm.h"
35 #include "adm_proto.h"
36 #include "kdc_util.h"
37 #include "extern.h"
38 #include "kdc5_err.h"
39 #include "kdb_kt.h"
40 #ifdef KRB5_USE_INET
41 #include <netinet/in.h>
42 #endif
43
44 kdc_realm_t *find_realm_data PROTOTYPE((char *, krb5_ui_4));
45
46 krb5_error_code setup_server_realm PROTOTYPE((krb5_principal));
47
48 void usage PROTOTYPE((char *));
49
50 krb5_sigtype request_exit PROTOTYPE((int));
51
52 void setup_signal_handlers PROTOTYPE((void));
53
54 void initialize_realms PROTOTYPE((krb5_context, int, char **));
55
56 void finish_realms PROTOTYPE((char *));
57
58 static int nofork = 0;
59 static char *kdc_current_rcname = (char *) NULL;
60 static int rkey_init_done = 0;
61
62 #define KRB5_KDC_MAX_REALMS     32
63
64 /*
65  * Get port information for a realm.  The precedence is:
66  *      [realms]-><realm>-><name> in profile (if our hostname and has a port)
67  *      defport
68  *      /etc/services entry matching <service>
69  */
70 static krb5_int32
71 get_realm_port(ctx, realm, name, defport, service)
72     krb5_context        ctx;
73     char                *realm;
74     char                *name;
75     krb5_int32          defport;
76     char                *service;
77 {
78     krb5_error_code     kret;
79     char                our_host_name[MAXHOSTNAMELEN];
80     struct hostent      *our_hostent;
81     struct servent      *our_servent;
82     krb5_int32          retval;
83     krb5_boolean        found;
84
85     /*
86      * Some preliminaries here.  Get our hostname and our host entry.
87      */
88     found = 0;
89     retval = -1;
90     if (!gethostname(our_host_name, sizeof(our_host_name)) &&
91         (our_hostent = gethostbyname(our_host_name))) {
92         const char      *hierarchy[4];
93         char            **hostlist;
94
95         hostlist = (char **) NULL;
96         hierarchy[0] = "realms";
97         hierarchy[1] = realm;
98         hierarchy[2] = name;
99         hierarchy[3] = (char *) NULL;
100         if (!(kret = profile_get_values(ctx->profile, hierarchy, &hostlist))) {
101             int         hi;
102             char        *cport;
103             char        *cp;
104             int         ai;
105             krb5_int32  pport;
106
107             cport = (char *) NULL;
108             for (hi=0; hostlist[hi]; hi++) {
109                 /*
110                  * This knows a little too much about the format of profile
111                  * entries.  Shouldn't it just be some sort of tuple?
112                  *
113                  * The form is assumed to be:
114                  *      <name> = <hostname>[:<portname>[<whitespace>]]
115                  */
116                 pport = -1;
117                 cp = strchr(hostlist[hi], ' ');
118                 if (cp)
119                     *cp = '\0';
120                 cp = strchr(hostlist[hi], '\t');
121                 if (cp)
122                     *cp = '\0';
123                 cport = strchr(hostlist[hi], ':');
124                 if (cport) {
125                     *cport = '\0';
126                     cport++;
127                     if (sscanf(cport, "%d", &pport) == 1) {
128                         pport = -1;
129                     }
130                 }
131                 /*
132                  * We've stripped away the crud.  Now check to see if the
133                  * profile entry matches our hostname.  If so, then this
134                  * is the one to use.  Additionally, check the host alias
135                  * list.
136                  */
137                 if (!strcmp(hostlist[hi], our_hostent->h_name)) {
138                     if (pport != -1) {
139                         retval = pport;
140                         found = 1;
141                     }
142                 }
143                 else {
144                     for (ai=0; our_hostent->h_aliases[ai]; ai++) {
145                         if (!strcmp(hostlist[hi],
146                                     our_hostent->h_aliases[ai])) {
147                             if (pport != -1) {
148                                 retval = pport;
149                                 found = 1;
150                             }
151                             break;
152                         }
153                     }
154                 }
155             }
156             free(hostlist);
157         }
158     }
159     /*
160      * If we didn't find an entry in the profile, then use the default.
161      * If it's no good, then attempt to find it in /etc/services.
162      */
163     if (!found) {
164         retval = defport;
165         /* Get the service entry out of /etc/services */
166         if (retval <= 0) {
167             if ((our_servent = getservbyname(service, "udp")))
168                 retval = ntohs(our_servent->s_port);
169         }
170     }
171     return(retval);
172 }
173
174 /*
175  * initialize the replay cache.
176  */
177 krb5_error_code
178 kdc_initialize_rcache(kcontext, rcache_name)
179     krb5_context        kcontext;
180     char                *rcache_name;
181 {
182     krb5_error_code     retval;
183     char                *rcname;
184     char                *sname;
185
186     rcname = (rcache_name) ? rcache_name : kdc_current_rcname;
187     if (!rcname)
188         rcname = KDCRCACHE;
189     if (!(retval = krb5_rc_resolve_full(kcontext, &kdc_rcache, rcname))) {
190         /* Recover or initialize the replay cache */
191         if (!(retval = krb5_rc_recover(kcontext, kdc_rcache)) ||
192             !(retval = krb5_rc_initialize(kcontext,
193                                           kdc_rcache,
194                                           kcontext->clockskew))
195             ) {
196             /* Expunge the replay cache */
197             if (!(retval = krb5_rc_expunge(kcontext, kdc_rcache))) {
198                 sname = kdc_current_rcname;
199                 kdc_current_rcname = strdup(rcname);
200                 if (sname)
201                     free(sname);
202             }
203         }
204         if (retval)
205             krb5_rc_close(kcontext, kdc_rcache);
206     }
207     return(retval);
208 }
209
210 /*
211  * Find the realm entry for a given realm.
212  */
213 kdc_realm_t *
214 find_realm_data(rname, rsize)
215     char        *rname;
216     krb5_ui_4   rsize;
217 {
218     int i;
219     for (i=0; i<kdc_numrealms; i++) {
220         if ((rsize == strlen(kdc_realmlist[i]->realm_name)) &&
221             !strncmp(rname, kdc_realmlist[i]->realm_name, rsize))
222             return(kdc_realmlist[i]);
223     }
224     return((kdc_realm_t *) NULL);
225 }
226
227 krb5_error_code
228 setup_server_realm(sprinc)
229     krb5_principal      sprinc;
230 {
231     krb5_error_code     kret;
232     kdc_realm_t         *newrealm;
233
234     kret = 0;
235     if (kdc_numrealms > 1) {
236         if (!(newrealm = find_realm_data(sprinc->realm.data,
237                                          (krb5_ui_4) sprinc->realm.length)))
238             kret = ENOENT;
239         else
240             kdc_active_realm = newrealm;
241     }
242     else
243         kdc_active_realm = kdc_realmlist[0];
244     return(kret);
245 }
246
247 static void
248 finish_realm(rdp)
249     kdc_realm_t *rdp;
250 {
251     if (rdp->realm_dbname)
252         free(rdp->realm_dbname);
253     if (rdp->realm_mpname)
254         free(rdp->realm_mpname);
255     if (rdp->realm_stash)
256         free(rdp->realm_stash);
257     if (rdp->realm_ports)
258         free(rdp->realm_ports);
259     if (rdp->realm_kstypes)
260         free(rdp->realm_kstypes);
261     if (rdp->realm_keytab)
262         krb5_kt_close(rdp->realm_context, rdp->realm_keytab);
263     if (rdp->realm_context) {
264         if (rdp->realm_mprinc)
265             krb5_free_principal(rdp->realm_context, rdp->realm_mprinc);
266         if (rdp->realm_mkey.length && rdp->realm_mkey.contents) {
267             memset(rdp->realm_mkey.contents, 0, rdp->realm_mkey.length);
268             free(rdp->realm_mkey.contents);
269         }
270         if (rdp->realm_tgskey.length && rdp->realm_tgskey.contents) {
271             memset(rdp->realm_tgskey.contents, 0, rdp->realm_tgskey.length);
272             free(rdp->realm_tgskey.contents);
273         }
274         if (rdp->realm_encblock.crypto_entry)
275                 krb5_finish_key(rdp->realm_context, &rdp->realm_encblock);
276         krb5_db_fini(rdp->realm_context);
277         if (rdp->realm_tgsprinc)
278             krb5_free_principal(rdp->realm_context, rdp->realm_tgsprinc);
279         krb5_free_context(rdp->realm_context);
280     }
281     memset((char *) rdp, 0, sizeof(*rdp));
282 }
283
284 /*
285  * Initialize a realm control structure from the alternate profile or from
286  * the specified defaults.
287  *
288  * After we're complete here, the essence of the realm is embodied in the
289  * realm data and we should be all set to begin operation for that realm.
290  */
291 static krb5_error_code
292 init_realm(progname, rdp, realm, def_dbname, def_mpname,
293                  def_enctype, def_ports, def_manual)
294     char                *progname;
295     kdc_realm_t         *rdp;
296     char                *realm;
297     char                *def_dbname;
298     char                *def_mpname;
299     krb5_enctype        def_enctype;
300     char                *def_ports;
301     krb5_boolean        def_manual;
302 {
303     krb5_error_code     kret;
304     krb5_boolean        manual;
305     krb5_db_entry       db_entry;
306     int                 num2get;
307     krb5_boolean        more;
308     krb5_boolean        db_inited;
309     krb5_realm_params   *rparams;
310     krb5_key_data       *kdata;
311     krb5_key_salt_tuple *kslist;
312     krb5_int32          nkslist;
313     int                 i;
314
315     db_inited = 0;
316     memset((char *) rdp, 0, sizeof(kdc_realm_t));
317     if (!realm) {
318         kret = EINVAL;
319         goto whoops;
320     }
321         
322     rdp->realm_name = realm;
323     kret = krb5_init_context(&rdp->realm_context);
324     if (kret) {
325         com_err(progname, kret, "while getting context for realm %s",
326                 realm);
327         goto whoops;
328     }
329
330     kret = krb5_read_realm_params(rdp->realm_context, rdp->realm_name,
331                                   (char *) NULL, (char *) NULL, &rparams);
332     if (kret) {
333         com_err(progname, kret, "while reading realm parameters");
334         goto whoops;
335     }
336     
337     /* Handle profile file name */
338     if (rparams && rparams->realm_profile)
339         rdp->realm_profile = strdup(rparams->realm_profile);
340
341     /* Handle database name */
342     if (rparams && rparams->realm_dbname)
343         rdp->realm_dbname = strdup(rparams->realm_dbname);
344     else
345         rdp->realm_dbname = (def_dbname) ? strdup(def_dbname) :
346             strdup(DEFAULT_KDB_FILE);
347
348     /* Handle master key name */
349     if (rparams && rparams->realm_mkey_name)
350         rdp->realm_mpname = strdup(rparams->realm_mkey_name);
351     else
352         rdp->realm_mpname = (def_mpname) ? strdup(def_mpname) :
353             strdup(KRB5_KDB_M_NAME);
354
355     /* Handle KDC port */
356     if (rparams && rparams->realm_kdc_ports)
357         rdp->realm_ports = strdup(rparams->realm_kdc_ports);
358     else
359         rdp->realm_ports = strdup(def_ports);
360             
361     /* Handle stash file */
362     if (rparams && rparams->realm_stash_file) {
363         rdp->realm_stash = strdup(rparams->realm_stash_file);
364         manual = FALSE;
365     } else
366         manual = def_manual;
367
368     /* Handle master key type */
369     if (rparams && rparams->realm_enctype_valid)
370         rdp->realm_mkey.enctype = (krb5_enctype) rparams->realm_enctype;
371     else
372         rdp->realm_mkey.enctype = manual ? def_enctype : ENCTYPE_UNKNOWN;
373
374     /* Handle ticket maximum life */
375     rdp->realm_maxlife = (rparams && rparams->realm_max_life_valid) ?
376         rparams->realm_max_life : KRB5_KDB_MAX_LIFE;
377
378     /* Handle ticket renewable maximum life */
379     rdp->realm_maxrlife = (rparams && rparams->realm_max_rlife_valid) ?
380         rparams->realm_max_rlife : KRB5_KDB_MAX_LIFE;
381
382     /* Handle key/salt list */
383     if (rparams && rparams->realm_num_keysalts) {
384         rdp->realm_kstypes = rparams->realm_keysalts;
385         rdp->realm_nkstypes = rparams->realm_num_keysalts;
386         rparams->realm_keysalts = NULL;
387         rparams->realm_num_keysalts = 0;
388         kslist = (krb5_key_salt_tuple *) rdp->realm_kstypes;
389         nkslist = rdp->realm_nkstypes;
390     } else {
391         /*
392          * XXX  Initialize default key/salt list.
393          */
394         if ((kslist = (krb5_key_salt_tuple *)
395              malloc(sizeof(krb5_key_salt_tuple)))) {
396             kslist->ks_enctype = ENCTYPE_DES_CBC_CRC;
397             kslist->ks_salttype = KRB5_KDB_SALTTYPE_NORMAL;
398             rdp->realm_kstypes = kslist;
399             rdp->realm_nkstypes = 1;
400             nkslist = 1;
401         }
402         else {
403             com_err(progname, ENOMEM,
404                     "while setting up key/salt list for realm %s",
405                     realm);
406             exit(1);
407         }
408     }
409
410     if (rparams)
411         krb5_free_realm_params(rdp->realm_context, rparams);
412
413     /*
414      * We've got our parameters, now go and setup our realm context.
415      */
416
417     /* Set the default realm of this context */
418     if ((kret = krb5_set_default_realm(rdp->realm_context, realm))) {
419         com_err(progname, kret, "while setting default realm to %s",
420                 realm);
421         goto whoops;
422     }
423
424     /* Assemble and parse the master key name */
425     if ((kret = krb5_db_setup_mkey_name(rdp->realm_context, rdp->realm_mpname,
426                                         rdp->realm_name, (char **) NULL,
427                                         &rdp->realm_mprinc))) {
428         com_err(progname, kret,
429                 "while setting up master key name %s for realm %s",
430                 rdp->realm_mpname, realm);
431         goto whoops;
432     }
433
434     /* Select the specified encryption type */
435     /* krb5_db_fetch_mkey will setup the encblock for stashed keys */
436     if (manual)
437         krb5_use_enctype(rdp->realm_context, &rdp->realm_encblock, 
438                          rdp->realm_mkey.enctype);
439     
440     /*
441      * Get the master key.
442      */
443     if ((kret = krb5_db_fetch_mkey(rdp->realm_context, rdp->realm_mprinc,
444                                    &rdp->realm_encblock, manual,
445                                    FALSE, rdp->realm_stash,
446                                    0, &rdp->realm_mkey))) {
447         com_err(progname, kret,
448                 "while fetching master key %s for realm %s",
449                 rdp->realm_mpname, realm);
450         goto whoops;
451     }
452
453     /* Set and open the database. */
454     if (rdp->realm_dbname &&
455         (kret = krb5_db_set_name(rdp->realm_context, rdp->realm_dbname))) {
456         com_err(progname, kret,
457                 "while setting database name to %s for realm %s",
458                 rdp->realm_dbname, realm);
459         goto whoops;
460     }
461     if ((kret = krb5_db_init(rdp->realm_context))) {
462         com_err(progname, kret,
463                 "while initializing database for realm %s", realm);
464         goto whoops;
465     } else
466         db_inited = 1;
467
468     /* Verify the master key */
469     if ((kret = krb5_db_verify_master_key(rdp->realm_context,
470                                           rdp->realm_mprinc,
471                                           &rdp->realm_mkey,
472                                           &rdp->realm_encblock))) {
473         com_err(progname, kret,
474                 "while verifying master key for realm %s", realm);
475         goto whoops;
476     }
477
478     /* Fetch the master key and get its version number */
479     num2get = 1;
480     kret = krb5_db_get_principal(rdp->realm_context, rdp->realm_mprinc,
481                                  &db_entry, &num2get, &more);
482     if (!kret) {
483         if (num2get != 1)
484             kret = KRB5_KDB_NOMASTERKEY;
485         else {
486             if (more) {
487                 krb5_db_free_principal(rdp->realm_context,
488                                        &db_entry,
489                                        num2get);
490                 kret = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
491             }
492         }
493     }
494     if (kret) {
495         com_err(progname, kret,
496                 "while fetching master entry for realm %s", realm);
497         goto whoops;
498     }
499         
500     /*
501      * Get the most recent master key.  Search the key list in
502      * the order specified by the key/salt list.
503      */
504     kdata = (krb5_key_data *) NULL;
505     for (i=0; i<nkslist; i++) {
506         if (!(kret = krb5_dbe_find_enctype(rdp->realm_context,
507                                            &db_entry,
508                                            kslist[i].ks_enctype,
509                                            -1,
510                                            -1,
511                                            &kdata)))
512             break;
513     }
514     if (!kdata) {
515         com_err(progname, kret,
516                 "while finding master key for realm %s",
517                 realm);
518         goto whoops;
519     }
520     rdp->realm_mkvno = kdata->key_data_kvno;
521     krb5_db_free_principal(rdp->realm_context, &db_entry, num2get);
522
523     /* Now preprocess the master key */
524     if ((kret = krb5_process_key(rdp->realm_context,
525                                  &rdp->realm_encblock,
526                                  &rdp->realm_mkey))) {
527         com_err(progname, kret,
528                 "while processing master key for realm %s", realm);
529         goto whoops;
530     }
531
532     if ((kret = krb5_db_set_mkey(rdp->realm_context, 
533                                  &rdp->realm_encblock))) {
534         com_err(progname, kret,
535                 "while setting master key for realm %s", realm);
536         goto whoops;
537     }
538
539     /* Set up the keytab */
540     if ((kret = krb5_ktkdb_resolve(rdp->realm_context, 
541                                    &rdp->realm_keytab))) {
542         com_err(progname, kret,
543                 "while resolving kdb keytab for realm %s", realm);
544         goto whoops;
545     }
546
547     /* Preformat the TGS name */
548     if ((kret = krb5_build_principal(rdp->realm_context, &rdp->realm_tgsprinc,
549                                      strlen(realm), realm, KRB5_TGS_NAME,
550                                      realm, (char *) NULL))) {
551         com_err(progname, kret,
552                 "while building TGS name for realm %s", realm);
553         goto whoops;
554     }
555
556     /* Get the TGS database entry */
557     num2get = 1;
558     if (!(kret = krb5_db_get_principal(rdp->realm_context,
559                                        rdp->realm_tgsprinc,
560                                        &db_entry,
561                                        &num2get,
562                                        &more))) {
563         if (num2get != 1)
564             kret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
565         else {
566             if (more) {
567                 krb5_db_free_principal(rdp->realm_context,
568                                        &db_entry,
569                                        num2get);
570                 kret = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
571             }
572         }
573     }
574     if (kret) {
575         com_err(progname, kret,
576                 "while fetching TGS entry for realm %s", realm);
577         goto whoops;
578     } 
579     /*
580      * Get the most recent TGS key.  Search the key list in
581      * the order specified by the key/salt list.
582      */
583     kdata = (krb5_key_data *) NULL;
584     for (i=0; i<nkslist; i++) {
585         if (!(kret = krb5_dbe_find_enctype(rdp->realm_context,
586                                            &db_entry,
587                                            kslist[i].ks_enctype,
588                                            -1,
589                                            -1,
590                                            &kdata)))
591             break;
592     }
593     if (!kdata) {
594         com_err(progname, kret, "while finding TGS key for realm %s",
595                 realm);
596         goto whoops;
597     }
598     if (!(kret = krb5_dbekd_decrypt_key_data(rdp->realm_context,
599                                              &rdp->realm_encblock,
600                                              kdata,
601                                              &rdp->realm_tgskey, NULL))){
602         rdp->realm_tgskvno = kdata->key_data_kvno;
603     }
604     krb5_db_free_principal(rdp->realm_context,
605                            &db_entry,
606                            num2get);
607     if (kret) {
608         com_err(progname, kret,
609                 "while decrypting TGS key for realm %s", realm);
610         goto whoops;
611     }
612
613     if (!rkey_init_done) {
614         krb5_enctype enctype;
615         krb5_encrypt_block temp_eblock;
616 #ifdef KRB5_KRB4_COMPAT
617         krb5_keyblock *temp_key;
618 #endif
619         /*
620          * If all that worked, then initialize the random key
621          * generators.
622          */
623         for (enctype = 0; enctype <= krb5_max_enctype; enctype++) {
624             if (krb5_enctype_array[enctype] &&
625                 !krb5_enctype_array[enctype]->random_sequence) {
626                 krb5_use_enctype(rdp->realm_context, &temp_eblock, enctype);
627                 if ((kret = krb5_init_random_key(
628                          rdp->realm_context, &temp_eblock,
629                          &rdp->realm_mkey,
630                         &krb5_enctype_array[enctype]->random_sequence))) {
631                     com_err(progname, kret, 
632                             "while setting up random key generator for enctype %d--enctype disabled",
633                             enctype);
634                     krb5_enctype_array[enctype] = 0;
635                 } else {
636 #ifdef KRB5_KRB4_COMPAT
637                     if (enctype == ENCTYPE_DES_CBC_CRC) {
638                         if ((kret = krb5_random_key(
639                             rdp->realm_context, &temp_eblock,
640                                 krb5_enctype_array[enctype]->random_sequence,
641                                 &temp_key)))
642                             com_err(progname, kret,
643                                     "while initializing V4 random key generator");
644                         else {
645                             (void) des_init_random_number_generator(temp_key->contents);
646                             krb5_free_keyblock(rdp->realm_context, temp_key);
647                         }
648                     }
649 #endif
650                 }
651             }
652         }
653         rkey_init_done = 1;
654     }
655  whoops:
656     /*
657      * If we choked, then clean up any dirt we may have dropped on the floor.
658      */
659     if (kret) {
660         finish_realm(rdp);
661     }
662     return(kret);
663 }
664
665 krb5_sigtype
666 request_exit(signo)
667     int signo;
668 {
669     signal_requests_exit = 1;
670
671 #ifdef POSIX_SIGTYPE
672     return;
673 #else
674     return(0);
675 #endif
676 }
677
678 void
679 setup_signal_handlers()
680 {
681     signal(SIGINT, request_exit);
682     signal(SIGHUP, request_exit);
683     signal(SIGTERM, request_exit);
684
685     return;
686 }
687
688 void
689 usage(name)
690 char *name;
691 {
692     fprintf(stderr, "usage: %s [-d dbpathname] [-r dbrealmname] [-R replaycachename ]\n\t[-m] [-k masterenctype] [-M masterkeyname] [-p port] [-n]\n", name);
693     return;
694 }
695
696 void
697 initialize_realms(kcontext, argc, argv)
698     krb5_context        kcontext;
699     int                 argc;
700     char                **argv;
701 {
702     int                 c;
703     char                *db_name = (char *) NULL;
704     char                *mkey_name = (char *) NULL;
705     char                *rcname = KDCRCACHE;
706     char                *lrealm;
707     krb5_error_code     retval;
708     krb5_enctype        menctype = ENCTYPE_UNKNOWN;
709     kdc_realm_t         *rdatap;
710     krb5_boolean        manual = FALSE;
711     char                *default_ports = 0;
712     krb5_pointer        aprof;
713     const char          *hierarchy[3];
714     extern char *optarg;
715
716     if (!krb5_aprof_init(DEFAULT_KDC_PROFILE, KDC_PROFILE_ENV, &aprof)) {
717         hierarchy[0] = "kdcdefaults";
718         hierarchy[1] = "kdc_ports";
719         hierarchy[2] = (char *) NULL;
720         if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &default_ports))
721             default_ports = 0;
722         /* aprof_init can return 0 with aprof == NULL */
723         if (aprof)
724              krb5_aprof_finish(aprof);
725     }
726     if (default_ports == 0)
727         default_ports = strdup(DEFAULT_KDC_PORTLIST);
728     
729     /*
730      * Loop through the option list.  Each time we encounter a realm name,
731      * use the previously scanned options to fill in for defaults.
732      */
733     while ((c = getopt(argc, argv, "r:d:mM:k:R:e:p:s:n")) != EOF) {
734         switch(c) {
735         case 'r':                       /* realm name for db */
736             if (!find_realm_data(optarg, (krb5_ui_4) strlen(optarg))) {
737                 if ((rdatap = (kdc_realm_t *) malloc(sizeof(kdc_realm_t)))) {
738                     if ((retval = init_realm(argv[0], rdatap, optarg, db_name,
739                                              mkey_name, menctype,
740                                              default_ports, manual))) {
741                         fprintf(stderr,"%s: cannot initialize realm %s\n",
742                                 argv[0], optarg);
743                         exit(1);
744                     }
745                     kdc_realmlist[kdc_numrealms] = rdatap;
746                     kdc_numrealms++;
747                 }
748             }
749             break;
750         case 'd':                       /* pathname for db */
751             db_name = optarg;
752             break;
753         case 'm':                       /* manual type-in of master key */
754             manual = TRUE;
755             if (menctype == ENCTYPE_UNKNOWN)
756                 menctype = ENCTYPE_DES_CBC_CRC;
757             break;
758         case 'M':                       /* master key name in DB */
759             mkey_name = optarg;
760             break;
761         case 'n':
762             nofork++;                   /* don't detach from terminal */
763             break;
764         case 'k':                       /* enctype for master key */
765             if (krb5_string_to_enctype(optarg, &menctype))
766                 com_err(argv[0], 0, "invalid enctype %s", optarg);
767             break;
768         case 'R':
769             rcname = optarg;
770             break;
771         case 'p':
772             if (default_ports)
773                 free(default_ports);
774             default_ports = strdup(optarg);
775             break;
776         case '?':
777         default:
778             usage(argv[0]);
779             exit(1);
780         }
781     }
782
783     /*
784      * Check to see if we processed any realms.
785      */
786     if (kdc_numrealms == 0) {
787         /* no realm specified, use default realm */
788         if ((retval = krb5_get_default_realm(kcontext, &lrealm))) {
789             com_err(argv[0], retval,
790                     "while attempting to retrieve default realm");
791             exit(1);
792         }
793         if ((rdatap = (kdc_realm_t *) malloc(sizeof(kdc_realm_t)))) {
794             if ((retval = init_realm(argv[0], rdatap, lrealm, db_name,
795                                      mkey_name, menctype, default_ports,
796                                      manual))) {
797                 fprintf(stderr,"%s: cannot initialize realm %s\n",
798                         argv[0], lrealm);
799                 exit(1);
800             }
801             kdc_realmlist[0] = rdatap;
802             kdc_numrealms++;
803         }
804     }
805
806 #ifdef USE_RCACHE
807     /*
808      * Now handle the replay cache.
809      */
810     if ((retval = kdc_initialize_rcache(kcontext, rcname))) {
811         com_err(argv[0], retval, "while initializing KDC replay cache");
812         exit(1);
813     }
814 #endif
815
816     /* Ensure that this is set for our first request. */
817     kdc_active_realm = kdc_realmlist[0];
818     if (default_ports)
819         free(default_ports);
820
821     return;
822 }
823
824 void
825 finish_realms(prog)
826     char *prog;
827 {
828     int i;
829
830     for (i = 0; i < kdc_numrealms; i++)
831         finish_realm(kdc_realmlist[i]);
832 }
833
834 /*
835  outline:
836
837  process args & setup
838
839  initialize database access (fetch master key, open DB)
840
841  initialize network
842
843  loop:
844         listen for packet
845
846         determine packet type, dispatch to handling routine
847                 (AS or TGS (or V4?))
848
849         reflect response
850
851         exit on signal
852
853  clean up secrets, close db
854
855  shut down network
856
857  exit
858  */
859
860 int main(argc, argv)
861 int argc;
862 char *argv[];
863 {
864     krb5_error_code     retval;
865     krb5_context        kcontext;
866     int                 *port_list;
867     int errout = 0;
868
869     if (strrchr(argv[0], '/'))
870         argv[0] = strrchr(argv[0], '/')+1;
871
872     if (!(kdc_realmlist = (kdc_realm_t **) malloc(sizeof(kdc_realm_t *) * 
873                                                   KRB5_KDC_MAX_REALMS))) {
874         fprintf(stderr, "%s: cannot get memory for realm list\n", argv[0]);
875         exit(1);
876     }
877     memset((char *) kdc_realmlist, 0,
878            (size_t) (sizeof(kdc_realm_t *) * KRB5_KDC_MAX_REALMS));
879     port_list = NULL;
880
881     /*
882      * A note about Kerberos contexts: This context, "kcontext", is used
883      * for the KDC operations, i.e. setup, network connection and error
884      * reporting.  The per-realm operations use the "realm_context"
885      * associated with each realm.
886      */
887     retval = krb5_init_context(&kcontext);
888     if (retval) {
889             com_err(argv[0], retval, "while initializing krb5");
890             exit(1);
891     }
892     krb5_klog_init(kcontext, "kdc", argv[0], 1);
893
894     /*
895      * Scan through the argument list
896      */
897     initialize_realms(kcontext, argc, argv);
898
899     setup_signal_handlers();
900
901     if ((retval = setup_network(argv[0]))) {
902         com_err(argv[0], retval, "while initializing network");
903         finish_realms(argv[0]);
904         return 1;
905     }
906     if (!nofork && daemon(0, 0)) {
907         com_err(argv[0], errno, "while detaching from tty");
908         finish_realms(argv[0]);
909         return 1;
910     }
911     krb5_klog_syslog(LOG_INFO, "commencing operation");
912     if ((retval = listen_and_process(argv[0]))) {
913         com_err(argv[0], retval, "while processing network requests");
914         errout++;
915     }
916     if ((retval = closedown_network(argv[0]))) {
917         com_err(argv[0], retval, "while shutting down network");
918         errout++;
919     }
920     krb5_klog_syslog(LOG_INFO, "shutting down");
921     krb5_klog_close(kdc_context);
922     finish_realms(argv[0]);
923     krb5_free_context(kcontext);
924     return errout;
925 }
926