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