xref: /illumos-gate/usr/src/cmd/krb5/krb5kdc/main.c (revision 3aedfe0b5d40c671717b8bec3135984b90d27349)
1 /*
2  * Copyright 2007 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 #ifdef KRB5_KRB4_COMPAT
57 #include <des.h>
58 #endif
59 
60 #if defined(NEED_DAEMON_PROTO)
61 extern int daemon(int, int);
62 #endif
63 
64 void usage (char *);
65 
66 krb5_sigtype request_exit (int);
67 krb5_sigtype request_hup  (int);
68 
69 void setup_signal_handlers (void);
70 
71 krb5_error_code setup_sam (void);
72 
73 void initialize_realms (krb5_context, int, char **);
74 
75 void finish_realms (char *);
76 
77 static int nofork = 0;
78 static int rkey_init_done = 0;
79 
80 /* Solaris Kerberos: global here that other functions access */
81 int max_tcp_data_connections;
82 
83 #ifdef POSIX_SIGNALS
84 static struct sigaction s_action;
85 #endif /* POSIX_SIGNALS */
86 
87 #define	KRB5_KDC_MAX_REALMS	32
88 
89 /*
90  * Find the realm entry for a given realm.
91  */
92 kdc_realm_t *
93 find_realm_data(char *rname, krb5_ui_4 rsize)
94 {
95     int i;
96     for (i=0; i<kdc_numrealms; i++) {
97 	if ((rsize == strlen(kdc_realmlist[i]->realm_name)) &&
98 	    !strncmp(rname, kdc_realmlist[i]->realm_name, rsize))
99 	    return(kdc_realmlist[i]);
100     }
101     return((kdc_realm_t *) NULL);
102 }
103 
104 krb5_error_code
105 setup_server_realm(krb5_principal sprinc)
106 {
107     krb5_error_code	kret;
108     kdc_realm_t		*newrealm;
109 
110     kret = 0;
111     if (kdc_numrealms > 1) {
112 	if (!(newrealm = find_realm_data(sprinc->realm.data,
113 					 (krb5_ui_4) sprinc->realm.length)))
114 	    kret = ENOENT;
115 	else
116 	    kdc_active_realm = newrealm;
117     }
118     else
119 	kdc_active_realm = kdc_realmlist[0];
120     return(kret);
121 }
122 
123 static void
124 finish_realm(kdc_realm_t *rdp)
125 {
126     if (rdp->realm_dbname)
127 	free(rdp->realm_dbname);
128     if (rdp->realm_mpname)
129 	free(rdp->realm_mpname);
130     if (rdp->realm_stash)
131 	free(rdp->realm_stash);
132     if (rdp->realm_ports)
133 	free(rdp->realm_ports);
134     if (rdp->realm_tcp_ports)
135 	free(rdp->realm_tcp_ports);
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 	krb5_db_fini(rdp->realm_context);
146 	if (rdp->realm_tgsprinc)
147 	    krb5_free_principal(rdp->realm_context, rdp->realm_tgsprinc);
148 	krb5_free_context(rdp->realm_context);
149     }
150     memset((char *) rdp, 0, sizeof(*rdp));
151     free(rdp);
152 }
153 
154 /*
155  * Initialize a realm control structure from the alternate profile or from
156  * the specified defaults.
157  *
158  * After we're complete here, the essence of the realm is embodied in the
159  * realm data and we should be all set to begin operation for that realm.
160  */
161 static krb5_error_code
162 init_realm(char *progname, kdc_realm_t *rdp, char *realm,
163 	   char *def_mpname, krb5_enctype def_enctype, char *def_udp_ports,
164 	   char *def_tcp_ports, krb5_boolean def_manual, char **db_args)
165 {
166     krb5_error_code	kret;
167     krb5_boolean	manual;
168     krb5_realm_params	*rparams;
169 
170     memset((char *) rdp, 0, sizeof(kdc_realm_t));
171     if (!realm) {
172 	kret = EINVAL;
173 	goto whoops;
174     }
175 
176     rdp->realm_name = realm;
177     kret = krb5int_init_context_kdc(&rdp->realm_context);
178     if (kret) {
179 	com_err(progname, kret, gettext("while getting context for realm %s"),
180 		realm);
181 	goto whoops;
182     }
183 
184     kret = krb5_read_realm_params(rdp->realm_context, rdp->realm_name,
185 				  (char *) NULL, (char *) NULL, &rparams);
186     if (kret) {
187 	com_err(progname, kret, gettext("while reading realm parameters"));
188 	goto whoops;
189     }
190 
191     /* Handle profile file name */
192     if (rparams && rparams->realm_profile)
193 	rdp->realm_profile = strdup(rparams->realm_profile);
194 
195     /* Handle master key name */
196     if (rparams && rparams->realm_mkey_name)
197 	rdp->realm_mpname = strdup(rparams->realm_mkey_name);
198     else
199 	rdp->realm_mpname = (def_mpname) ? strdup(def_mpname) :
200 	    strdup(KRB5_KDB_M_NAME);
201 
202     /* Handle KDC ports */
203     if (rparams && rparams->realm_kdc_ports)
204 	rdp->realm_ports = strdup(rparams->realm_kdc_ports);
205     else
206 	rdp->realm_ports = strdup(def_udp_ports);
207     if (rparams && rparams->realm_kdc_tcp_ports)
208 	rdp->realm_tcp_ports = strdup(rparams->realm_kdc_tcp_ports);
209     else
210 	rdp->realm_tcp_ports = strdup(def_tcp_ports);
211 
212     /* Handle stash file */
213     if (rparams && rparams->realm_stash_file) {
214 	rdp->realm_stash = strdup(rparams->realm_stash_file);
215 	manual = FALSE;
216     } else
217 	manual = def_manual;
218 
219     /* Handle master key type */
220     if (rparams && rparams->realm_enctype_valid)
221 	rdp->realm_mkey.enctype = (krb5_enctype) rparams->realm_enctype;
222     else
223 	rdp->realm_mkey.enctype = manual ? def_enctype : ENCTYPE_UNKNOWN;
224 
225     /* Handle reject-bad-transit flag */
226     if (rparams && rparams->realm_reject_bad_transit_valid)
227 	rdp->realm_reject_bad_transit = rparams->realm_reject_bad_transit;
228     else
229 	rdp->realm_reject_bad_transit = 1;
230 
231     /* Handle ticket maximum life */
232     rdp->realm_maxlife = (rparams && rparams->realm_max_life_valid) ?
233 	rparams->realm_max_life : KRB5_KDB_MAX_LIFE;
234 
235     /* Handle ticket renewable maximum life */
236     rdp->realm_maxrlife = (rparams && rparams->realm_max_rlife_valid) ?
237 	rparams->realm_max_rlife : KRB5_KDB_MAX_RLIFE;
238 
239     if (rparams)
240 	krb5_free_realm_params(rdp->realm_context, rparams);
241 
242     /*
243      * We've got our parameters, now go and setup our realm context.
244      */
245 
246     /* Set the default realm of this context */
247     if ((kret = krb5_set_default_realm(rdp->realm_context, realm))) {
248 	com_err(progname, kret, gettext("while setting default realm to %s"),
249 		realm);
250 	goto whoops;
251     }
252 
253     /* first open the database  before doing anything */
254 #ifdef KRBCONF_KDC_MODIFIES_KDB
255     if ((kret = krb5_db_open(rdp->realm_context, db_args,
256 			     KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_KDC))) {
257 #else
258     if ((kret = krb5_db_open(rdp->realm_context, db_args,
259 			     KRB5_KDB_OPEN_RO | KRB5_KDB_SRV_TYPE_KDC))) {
260 #endif
261 	com_err(progname, kret,
262 		"while initializing database for realm %s", realm);
263 	goto whoops;
264     }
265 
266     /* Assemble and parse the master key name */
267     if ((kret = krb5_db_setup_mkey_name(rdp->realm_context, rdp->realm_mpname,
268 					rdp->realm_name, (char **) NULL,
269 					&rdp->realm_mprinc))) {
270 	com_err(progname, kret,
271 		gettext("while setting up master key name %s for realm %s"),
272 		rdp->realm_mpname, realm);
273 	goto whoops;
274     }
275 
276     /*
277      * Get the master key.
278      */
279     if ((kret = krb5_db_fetch_mkey(rdp->realm_context, rdp->realm_mprinc,
280 				   rdp->realm_mkey.enctype, manual,
281 				   FALSE, rdp->realm_stash,
282 				   0, &rdp->realm_mkey))) {
283 	com_err(progname, kret,
284 		gettext("while fetching master key %s for realm %s"),
285 		rdp->realm_mpname, realm);
286 	goto whoops;
287     }
288 
289     /* Verify the master key */
290     if ((kret = krb5_db_verify_master_key(rdp->realm_context,
291 					  rdp->realm_mprinc,
292 					  &rdp->realm_mkey))) {
293 	com_err(progname, kret,
294 		gettext("while verifying master key for realm %s"),
295 		realm);
296 	goto whoops;
297     }
298 
299     if ((kret = krb5_db_set_mkey(rdp->realm_context, &rdp->realm_mkey))) {
300 	com_err(progname, kret,
301 		gettext("while processing master key for realm %s"),
302 		realm);
303 	goto whoops;
304     }
305 
306     /* Set up the keytab */
307     if ((kret = krb5_ktkdb_resolve(rdp->realm_context, NULL,
308 				   &rdp->realm_keytab))) {
309 	com_err(progname, kret,
310 		gettext("while resolving kdb keytab for realm %s"),
311 		realm);
312 	goto whoops;
313     }
314 
315     /* Preformat the TGS name */
316     if ((kret = krb5_build_principal(rdp->realm_context, &rdp->realm_tgsprinc,
317 				     strlen(realm), realm, KRB5_TGS_NAME,
318 				     realm, (char *) NULL))) {
319 	com_err(progname, kret,
320 		gettext("while building TGS name for realm %s"),
321 		realm);
322 	goto whoops;
323     }
324 
325     if (!rkey_init_done) {
326 	krb5_data seed;
327 #ifdef KRB5_KRB4_COMPAT
328 	krb5_keyblock temp_key;
329 #endif
330 	/*
331 	 * If all that worked, then initialize the random key
332 	 * generators.
333 	 */
334 
335 	seed.length = rdp->realm_mkey.length;
336 	seed.data = (char *)rdp->realm_mkey.contents;
337 /* SUNW14resync - XXX */
338 #if 0
339 	if ((kret = krb5_c_random_add_entropy(rdp->realm_context,
340 					     KRB5_C_RANDSOURCE_TRUSTEDPARTY, &seed)))
341 	    goto whoops;
342 #endif
343 
344 #ifdef KRB5_KRB4_COMPAT
345 	if ((kret = krb5_c_make_random_key(rdp->realm_context,
346 					   ENCTYPE_DES_CBC_CRC, &temp_key))) {
347 	    com_err(progname, kret,
348 		    "while initializing V4 random key generator");
349 	    goto whoops;
350 	}
351 
352 	(void) des_init_random_number_generator(temp_key.contents);
353 	krb5_free_keyblock_contents(rdp->realm_context, &temp_key);
354 #endif
355 	rkey_init_done = 1;
356     }
357  whoops:
358     /*
359      * If we choked, then clean up any dirt we may have dropped on the floor.
360      */
361     if (kret) {
362 
363 	finish_realm(rdp);
364     }
365     return(kret);
366 }
367 
368 krb5_sigtype
369 request_exit(int signo)
370 {
371     signal_requests_exit = 1;
372 
373 #ifdef POSIX_SIGTYPE
374     return;
375 #else
376     return(0);
377 #endif
378 }
379 
380 krb5_sigtype
381 request_hup(int signo)
382 {
383     signal_requests_hup = 1;
384 
385 #ifdef POSIX_SIGTYPE
386     return;
387 #else
388     return(0);
389 #endif
390 }
391 
392 void
393 setup_signal_handlers(void)
394 {
395 #ifdef POSIX_SIGNALS
396     (void) sigemptyset(&s_action.sa_mask);
397     s_action.sa_flags = 0;
398     s_action.sa_handler = request_exit;
399     (void) sigaction(SIGINT, &s_action, (struct sigaction *) NULL);
400     (void) sigaction(SIGTERM, &s_action, (struct sigaction *) NULL);
401     s_action.sa_handler = request_hup;
402     (void) sigaction(SIGHUP, &s_action, (struct sigaction *) NULL);
403     s_action.sa_handler = SIG_IGN;
404     (void) sigaction(SIGPIPE, &s_action, (struct sigaction *) NULL);
405 #else  /* POSIX_SIGNALS */
406     signal(SIGINT, request_exit);
407     signal(SIGTERM, request_exit);
408     signal(SIGHUP, request_hup);
409     signal(SIGPIPE, SIG_IGN);
410 #endif /* POSIX_SIGNALS */
411 
412     return;
413 }
414 
415 krb5_error_code
416 setup_sam(void)
417 {
418     return krb5_c_make_random_key(kdc_context, ENCTYPE_DES_CBC_MD5, &psr_key);
419 }
420 
421 void
422 usage(char *name)
423 {
424     fprintf(stderr, gettext("usage: %s [-d dbpathname] [-r dbrealmname] [-R replaycachename ]\n\t[-m] [-k masterenctype] [-M masterkeyname] [-p port] [-n]\n"), name);
425     fprintf(stderr, "usage: %s [-x db_args]* [-d dbpathname] [-r dbrealmname] [-R replaycachename ]\n\t[-m] [-k masterenctype] [-M masterkeyname] [-p port] [-X] [-n]\n"
426 	    "\nwhere,\n\t[-x db_args]* - any number of database specific arguments.\n"
427 	    "\t\t\tLook at each database documentation for supported arguments\n",
428 	    name);
429     return;
430 }
431 
432 void
433 initialize_realms(krb5_context kcontext, int argc, char **argv)
434 {
435     int 		c;
436     char		*db_name = (char *) NULL;
437     char		*mkey_name = (char *) NULL;
438     char		*rcname = KDCRCACHE;
439     char		*lrealm;
440     krb5_error_code	retval;
441     krb5_enctype	menctype = ENCTYPE_UNKNOWN;
442     kdc_realm_t		*rdatap;
443     krb5_boolean	manual = FALSE;
444     char		*default_udp_ports = 0;
445     char		*default_tcp_ports = 0;
446     krb5_pointer	aprof;
447     const char		*hierarchy[3];
448     char               **db_args      = NULL;
449     int                  db_args_size = 0;
450 
451 #ifdef KRB5_KRB4_COMPAT
452     char                *v4mode = 0;
453 #endif
454     extern char *optarg;
455 
456     if (!krb5_aprof_init(DEFAULT_KDC_PROFILE, KDC_PROFILE_ENV, &aprof)) {
457 	hierarchy[0] = "kdcdefaults";
458 	hierarchy[1] = "kdc_ports";
459 	hierarchy[2] = (char *) NULL;
460 	if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &default_udp_ports))
461 	    default_udp_ports = 0;
462 	hierarchy[1] = "kdc_tcp_ports";
463 	if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &default_tcp_ports))
464 	    default_tcp_ports = 0;
465 	hierarchy[1] = "kdc_max_tcp_connections";
466 	if (krb5_aprof_get_int32(aprof, hierarchy, TRUE,
467 		&max_tcp_data_connections)) {
468 	    max_tcp_data_connections = DEFAULT_KDC_TCP_CONNECTIONS;
469 	} else if (max_tcp_data_connections < MIN_KDC_TCP_CONNECTIONS) {
470 	    max_tcp_data_connections = DEFAULT_KDC_TCP_CONNECTIONS;
471 	}
472 #ifdef KRB5_KRB4_COMPAT
473 	hierarchy[1] = "v4_mode";
474 	if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &v4mode))
475 	    v4mode = 0;
476 #endif
477 	/* aprof_init can return 0 with aprof == NULL */
478 	if (aprof)
479 	     krb5_aprof_finish(aprof);
480     }
481     if (default_udp_ports == 0)
482 	default_udp_ports = strdup(DEFAULT_KDC_UDP_PORTLIST);
483     if (default_tcp_ports == 0)
484 	default_tcp_ports = strdup(DEFAULT_KDC_TCP_PORTLIST);
485     /*
486      * Loop through the option list.  Each time we encounter a realm name,
487      * use the previously scanned options to fill in for defaults.
488      */
489     while ((c = getopt(argc, argv, "x:r:d:mM:k:R:e:p:s:n4:X3")) != -1) {
490 	switch(c) {
491 	case 'x':
492 	    db_args_size++;
493 	    {
494 		char **temp = realloc( db_args, sizeof(char*) * (db_args_size+1)); /* one for NULL */
495 		if( temp == NULL )
496 		{
497 		    fprintf(stderr, gettext("%s: KDC cannot initialize. Not enough memory\n"),
498 			    argv[0]);
499 		    exit(1);
500 		}
501 
502 		db_args = temp;
503 	    }
504 	    db_args[db_args_size-1] = optarg;
505 	    db_args[db_args_size]   = NULL;
506 	  break;
507 
508 	case 'r':			/* realm name for db */
509 	    if (!find_realm_data(optarg, (krb5_ui_4) strlen(optarg))) {
510 		if ((rdatap = (kdc_realm_t *) malloc(sizeof(kdc_realm_t)))) {
511 		    if ((retval = init_realm(argv[0], rdatap, optarg,
512 					     mkey_name, menctype,
513 					     default_udp_ports,
514 					     default_tcp_ports, manual, db_args))) {
515 			fprintf(stderr,gettext("%s: cannot initialize realm %s\n"),
516 				argv[0], optarg);
517 			exit(1);
518 		    }
519 		    kdc_realmlist[kdc_numrealms] = rdatap;
520 		    kdc_numrealms++;
521 		    free(db_args), db_args=NULL, db_args_size = 0;
522 		}
523 		else
524 		{
525 			fprintf(stderr, gettext("%s: cannot initialize realm %s. Not enough memory\n"),
526 				argv[0], optarg);
527 			exit(1);
528 		}
529 	    }
530 	    break;
531 	case 'd':			/* pathname for db */
532 	    /* now db_name is not a seperate argument. It has to be passed as part of the db_args */
533 	    if( db_name == NULL )
534 	    {
535 		db_name = malloc(sizeof("dbname=") + strlen(optarg));
536 		if( db_name == NULL )
537 		{
538 			fprintf(stderr, gettext("%s: KDC cannot initialize. Not enough memory\n"),
539 				argv[0] );
540 			exit(1);
541 		}
542 
543 		sprintf( db_name, "dbname=%s", optarg);
544 	    }
545 
546 	    db_args_size++;
547 	    {
548 		char **temp = realloc( db_args, sizeof(char*) * (db_args_size+1)); /* one for NULL */
549 		if( temp == NULL )
550 		{
551 		    fprintf(stderr, gettext("%s: KDC cannot initialize. Not enough memory\n"),
552 			    argv[0]);
553 		    exit(1);
554 		}
555 
556 		db_args = temp;
557 	    }
558 	    db_args[db_args_size-1] = db_name;
559 	    db_args[db_args_size]   = NULL;
560 	    break;
561 	case 'm':			/* manual type-in of master key */
562 	    manual = TRUE;
563 	    if (menctype == ENCTYPE_UNKNOWN)
564 		menctype = ENCTYPE_DES_CBC_CRC;
565 	    break;
566 	case 'M':			/* master key name in DB */
567 	    mkey_name = optarg;
568 	    break;
569 	case 'n':
570 	    nofork++;			/* don't detach from terminal */
571 	    break;
572 	case 'k':			/* enctype for master key */
573 	    if (krb5_string_to_enctype(optarg, &menctype))
574 		com_err(argv[0], 0,
575 		gettext("invalid enctype %s"), optarg);
576 	    break;
577 	case 'R':
578 	    rcname = optarg;
579 	    break;
580 	case 'p':
581 	    if (default_udp_ports)
582 		free(default_udp_ports);
583 	    default_udp_ports = strdup(optarg);
584 
585 	    if (default_tcp_ports)
586 		free(default_tcp_ports);
587 	    default_tcp_ports = strdup(optarg);
588 
589 	    break;
590 	case '4':
591 #ifdef KRB5_KRB4_COMPAT
592 	    if (v4mode)
593 		free(v4mode);
594 	    v4mode = strdup(optarg);
595 #endif
596 	    break;
597 	case 'X':
598 #ifdef KRB5_KRB4_COMPAT
599 		enable_v4_crossrealm(argv[0]);
600 #endif
601 		break;
602 	case '?':
603 	default:
604 	    usage(argv[0]);
605 	    exit(1);
606 	}
607     }
608 
609 #ifdef KRB5_KRB4_COMPAT
610     /*
611      * Setup the v4 mode
612      */
613     process_v4_mode(argv[0], v4mode);
614     free(v4mode);
615 #endif
616 
617     /*
618      * Check to see if we processed any realms.
619      */
620     if (kdc_numrealms == 0) {
621 	/* no realm specified, use default realm */
622 	if ((retval = krb5_get_default_realm(kcontext, &lrealm))) {
623 	    com_err(argv[0], retval,
624 		gettext("while attempting to retrieve default realm"));
625 	    fprintf (stderr, "%s: %s, %s", argv[0], error_message (retval),
626 	        gettext("attempting to retrieve default realm\n"));
627 	    exit(1);
628 	}
629 	if ((rdatap = (kdc_realm_t *) malloc(sizeof(kdc_realm_t)))) {
630 	    if ((retval = init_realm(argv[0], rdatap, lrealm,
631 				     mkey_name, menctype, default_udp_ports,
632 				     default_tcp_ports, manual, db_args))) {
633 		fprintf(stderr,
634 			gettext("%s: cannot initialize realm %s\n"),
635 			argv[0], lrealm);
636 		exit(1);
637 	    }
638 	    kdc_realmlist[0] = rdatap;
639 	    kdc_numrealms++;
640 	}
641     }
642 
643 #ifdef USE_RCACHE
644     /*
645      * Now handle the replay cache.
646      */
647     if ((retval = kdc_initialize_rcache(kcontext, rcname))) {
648 	com_err(argv[0], retval, gettext("while initializing KDC replay cache '%s'"),
649 		rcname);
650 	exit(1);
651     }
652 #endif
653 
654     /* Ensure that this is set for our first request. */
655     kdc_active_realm = kdc_realmlist[0];
656     if (default_udp_ports)
657 	free(default_udp_ports);
658     if (default_tcp_ports)
659 	free(default_tcp_ports);
660     if (db_args)
661 	free(db_args);
662     if (db_name)
663 	free(db_name);
664 
665     return;
666 }
667 
668 void
669 finish_realms(char *prog)
670 {
671     int i;
672 
673     for (i = 0; i < kdc_numrealms; i++) {
674 	finish_realm(kdc_realmlist[i]);
675 	kdc_realmlist[i] = 0;
676     }
677 }
678 
679 /*
680  outline:
681 
682  process args & setup
683 
684  initialize database access (fetch master key, open DB)
685 
686  initialize network
687 
688  loop:
689  	listen for packet
690 
691 	determine packet type, dispatch to handling routine
692 		(AS or TGS (or V4?))
693 
694 	reflect response
695 
696 	exit on signal
697 
698  clean up secrets, close db
699 
700  shut down network
701 
702  exit
703  */
704 
705 int main(int argc, char **argv)
706 {
707     krb5_error_code	retval;
708     krb5_context	kcontext;
709     int errout = 0;
710 
711     (void) setlocale(LC_ALL, "");
712 
713 #if !defined(TEXT_DOMAIN)		/* Should be defined by cc -D */
714 #define	TEXT_DOMAIN	"KRB5KDC_TEST"	/* Use this only if it weren't */
715 #endif
716 
717     (void) textdomain(TEXT_DOMAIN);
718 
719     if (strrchr(argv[0], '/'))
720 	argv[0] = strrchr(argv[0], '/')+1;
721 
722     if (!(kdc_realmlist = (kdc_realm_t **) malloc(sizeof(kdc_realm_t *) *
723 						  KRB5_KDC_MAX_REALMS))) {
724 	fprintf(stderr, gettext("%s: cannot get memory for realm list\n"), argv[0]);
725 	exit(1);
726     }
727     memset((char *) kdc_realmlist, 0,
728 	   (size_t) (sizeof(kdc_realm_t *) * KRB5_KDC_MAX_REALMS));
729 
730     /*
731      * A note about Kerberos contexts: This context, "kcontext", is used
732      * for the KDC operations, i.e. setup, network connection and error
733      * reporting.  The per-realm operations use the "realm_context"
734      * associated with each realm.
735      */
736     retval = krb5int_init_context_kdc(&kcontext);
737     if (retval) {
738 	    com_err(argv[0], retval, gettext("while initializing krb5"));
739 	    exit(1);
740     }
741     krb5_klog_init(kcontext, "kdc", argv[0], 1);
742     /* initialize_kdc5_error_table();  SUNWresync121 XXX */
743 
744     /*
745      * Scan through the argument list
746      */
747     initialize_realms(kcontext, argc, argv);
748 
749     setup_signal_handlers();
750 
751     retval = setup_sam();
752     if (retval) {
753 	com_err(argv[0], retval, gettext("while initializing SAM"));
754 	finish_realms(argv[0]);
755 	return 1;
756     }
757 
758     if ((retval = setup_network(argv[0]))) {
759 	com_err(argv[0], retval, gettext("while initializing network"));
760 	finish_realms(argv[0]);
761 	return 1;
762     }
763     if (!nofork && daemon(0, 0)) {
764 	com_err(argv[0], errno, gettext("while detaching from tty"));
765 	finish_realms(argv[0]);
766 	return 1;
767     }
768     if (retval = krb5_klog_syslog(LOG_INFO, "commencing operation")) {
769 	com_err(argv[0], retval, gettext("while logging message"));
770 	errout++;
771 	};
772 
773     if ((retval = listen_and_process(argv[0]))) {
774 	com_err(argv[0], retval, gettext("while processing network requests"));
775 	errout++;
776     }
777     if ((retval = closedown_network(argv[0]))) {
778 	com_err(argv[0], retval, gettext("while shutting down network"));
779 	errout++;
780     }
781     krb5_klog_syslog(LOG_INFO, "shutting down");
782     krb5_klog_close(kdc_context);
783     finish_realms(argv[0]);
784     if (kdc_realmlist)
785       free(kdc_realmlist);
786 #ifdef USE_RCACHE
787     (void) krb5_rc_close(kcontext, kdc_rcache);
788 #endif
789 #ifndef NOCACHE
790     kdc_free_lookaside(kcontext);
791 #endif
792     krb5_free_context(kcontext);
793     return errout;
794 }
795 
796 
797 
798 
799