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