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