xref: /freebsd/crypto/krb5/src/kdc/main.c (revision 4b15965daa99044daf184221b7c283bf7f2d7e66)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* kdc/main.c - Main procedure body for the KDC server process */
3 /*
4  * Copyright 1990,2001,2008,2009,2016 by the Massachusetts Institute of
5  * Technology.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26 
27 #include "k5-int.h"
28 #include "com_err.h"
29 #include <kadm5/admin.h>
30 #include "adm_proto.h"
31 #include "kdc_util.h"
32 #include "kdc_audit.h"
33 #include "extern.h"
34 #include "policy.h"
35 #include "kdc5_err.h"
36 #include "kdb_kt.h"
37 #include "net-server.h"
38 #ifdef HAVE_NETINET_IN_H
39 #include <netinet/in.h>
40 #endif
41 
42 #include <locale.h>
43 #include <syslog.h>
44 #include <signal.h>
45 #include <netdb.h>
46 #include <unistd.h>
47 #include <ctype.h>
48 #include <sys/wait.h>
49 
50 #if defined(NEED_DAEMON_PROTO)
51 extern int daemon(int, int);
52 #endif
53 
54 static void usage (char *);
55 
56 static void initialize_realms(krb5_context kcontext, int argc, char **argv,
57                               int *tcp_listen_backlog_out);
58 
59 static void finish_realms (void);
60 
61 static int nofork = 0;
62 static int workers = 0;
63 static int time_offset = 0;
64 static const char *pid_file = NULL;
65 static volatile int signal_received = 0;
66 static volatile int sighup_received = 0;
67 
68 #define KRB5_KDC_MAX_REALMS     32
69 
70 static const char *kdc_progname;
71 
72 /*
73  * Static server_handle for this file.  Other code will get access to
74  * it through the application handle that net-server.c uses.
75  */
76 static struct server_handle shandle;
77 
78 /*
79  * We use krb5_klog_init to set up a com_err callback to log error
80  * messages.  The callback also pulls the error message out of the
81  * context we pass to krb5_klog_init; however, we use realm-specific
82  * contexts for most of our krb5 library calls, so the error message
83  * isn't present in the global context.  This wrapper ensures that the
84  * error message state from the call context is copied into the
85  * context known by krb5_klog.  call_context can be NULL if the error
86  * code did not come from a krb5 library function.
87  */
88 void
89 kdc_err(krb5_context call_context, errcode_t code, const char *fmt, ...)
90 {
91     va_list ap;
92 
93     if (call_context)
94         krb5_copy_error_message(shandle.kdc_err_context, call_context);
95     va_start(ap, fmt);
96     com_err_va(kdc_progname, code, fmt, ap);
97     va_end(ap);
98 }
99 
100 /*
101  * Find the realm entry for a given realm.
102  */
103 kdc_realm_t *
104 find_realm_data(struct server_handle *handle, char *rname, krb5_ui_4 rsize)
105 {
106     int i;
107     kdc_realm_t **kdc_realmlist = handle->kdc_realmlist;
108     int kdc_numrealms = handle->kdc_numrealms;
109 
110     for (i=0; i<kdc_numrealms; i++) {
111         if ((rsize == strlen(kdc_realmlist[i]->realm_name)) &&
112             !strncmp(rname, kdc_realmlist[i]->realm_name, rsize))
113             return(kdc_realmlist[i]);
114     }
115     return((kdc_realm_t *) NULL);
116 }
117 
118 kdc_realm_t *
119 setup_server_realm(struct server_handle *handle, krb5_principal sprinc)
120 {
121     kdc_realm_t         *newrealm;
122     kdc_realm_t **kdc_realmlist = handle->kdc_realmlist;
123     int kdc_numrealms = handle->kdc_numrealms;
124 
125     if (sprinc == NULL)
126         return NULL;
127 
128     if (kdc_numrealms > 1) {
129         newrealm = find_realm_data(handle, sprinc->realm.data,
130                                    sprinc->realm.length);
131     } else {
132         newrealm = kdc_realmlist[0];
133     }
134     if (newrealm != NULL) {
135         krb5_klog_set_context(newrealm->realm_context);
136         shandle.kdc_err_context = newrealm->realm_context;
137     }
138     return newrealm;
139 }
140 
141 static void
142 finish_realm(kdc_realm_t *rdp)
143 {
144     if (rdp->realm_name)
145         free(rdp->realm_name);
146     if (rdp->realm_mpname)
147         free(rdp->realm_mpname);
148     if (rdp->realm_stash)
149         free(rdp->realm_stash);
150     if (rdp->realm_listen)
151         free(rdp->realm_listen);
152     if (rdp->realm_tcp_listen)
153         free(rdp->realm_tcp_listen);
154     if (rdp->realm_keytab)
155         krb5_kt_close(rdp->realm_context, rdp->realm_keytab);
156     if (rdp->realm_hostbased)
157         free(rdp->realm_hostbased);
158     if (rdp->realm_no_referral)
159         free(rdp->realm_no_referral);
160     if (rdp->realm_context) {
161         if (rdp->realm_mprinc)
162             krb5_free_principal(rdp->realm_context, rdp->realm_mprinc);
163         zapfree(rdp->realm_mkey.contents, rdp->realm_mkey.length);
164         krb5_db_fini(rdp->realm_context);
165         if (rdp->realm_tgsprinc)
166             krb5_free_principal(rdp->realm_context, rdp->realm_tgsprinc);
167         krb5_free_context(rdp->realm_context);
168     }
169     zapfree(rdp, sizeof(*rdp));
170 }
171 
172 /* Set *val_out to an allocated string containing val1 and/or val2, separated
173  * by a space if both are set, or NULL if neither is set. */
174 static krb5_error_code
175 combine(const char *val1, const char *val2, char **val_out)
176 {
177     if (val1 == NULL && val2 == NULL) {
178         *val_out = NULL;
179     } else if (val1 != NULL && val2 != NULL) {
180         if (asprintf(val_out, "%s %s", val1, val2) < 0) {
181             *val_out = NULL;
182             return ENOMEM;
183         }
184     } else {
185         *val_out = strdup((val1 != NULL) ? val1 : val2);
186         if (*val_out == NULL)
187             return ENOMEM;
188     }
189     return 0;
190 }
191 
192 /*
193  * Initialize a realm control structure from the alternate profile or from
194  * the specified defaults.
195  *
196  * After we're complete here, the essence of the realm is embodied in the
197  * realm data and we should be all set to begin operation for that realm.
198  */
199 static  krb5_error_code
200 init_realm(kdc_realm_t * rdp, krb5_pointer aprof, char *realm,
201            char *def_mpname, krb5_enctype def_enctype, char *def_udp_listen,
202            char *def_tcp_listen, krb5_boolean def_manual,
203            krb5_boolean def_restrict_anon, char **db_args, char *no_referral,
204            char *hostbased)
205 {
206     krb5_error_code     kret;
207     krb5_boolean        manual;
208     int                 kdb_open_flags;
209     char                *svalue = NULL;
210     const char          *hierarchy[4];
211     krb5_kvno       mkvno = IGNORE_VNO;
212     char ename[32];
213 
214     memset(rdp, 0, sizeof(kdc_realm_t));
215     if (!realm) {
216         kret = EINVAL;
217         goto whoops;
218     }
219 
220     if (def_enctype != ENCTYPE_UNKNOWN &&
221         krb5int_c_deprecated_enctype(def_enctype)) {
222         if (krb5_enctype_to_name(def_enctype, FALSE, ename, sizeof(ename)))
223             ename[0] = '\0';
224         fprintf(stderr,
225                 _("Requested master password enctype %s in %s is "
226                   "DEPRECATED!\n"),
227                 ename, realm);
228     }
229 
230     hierarchy[0] = KRB5_CONF_REALMS;
231     hierarchy[1] = realm;
232     hierarchy[3] = NULL;
233 
234     rdp->realm_name = strdup(realm);
235     if (rdp->realm_name == NULL) {
236         kret = ENOMEM;
237         goto whoops;
238     }
239     kret = krb5int_init_context_kdc(&rdp->realm_context);
240     if (kret) {
241         kdc_err(NULL, kret, _("while getting context for realm %s"), realm);
242         goto whoops;
243     }
244     if (time_offset != 0)
245         (void)krb5_set_time_offsets(rdp->realm_context, time_offset, 0);
246 
247     /* Handle master key name */
248     hierarchy[2] = KRB5_CONF_MASTER_KEY_NAME;
249     if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &rdp->realm_mpname)) {
250         rdp->realm_mpname = (def_mpname) ? strdup(def_mpname) :
251             strdup(KRB5_KDB_M_NAME);
252     }
253     if (!rdp->realm_mpname) {
254         kret = ENOMEM;
255         goto whoops;
256     }
257 
258     /* Handle KDC addresses/ports */
259     hierarchy[2] = KRB5_CONF_KDC_LISTEN;
260     if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &rdp->realm_listen)) {
261         /* Try the old kdc_ports configuration option. */
262         hierarchy[2] = KRB5_CONF_KDC_PORTS;
263         if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &rdp->realm_listen))
264             rdp->realm_listen = strdup(def_udp_listen);
265     }
266     if (!rdp->realm_listen) {
267         kret = ENOMEM;
268         goto whoops;
269     }
270     hierarchy[2] = KRB5_CONF_KDC_TCP_LISTEN;
271     if (krb5_aprof_get_string(aprof, hierarchy, TRUE,
272                               &rdp->realm_tcp_listen)) {
273         /* Try the old kdc_tcp_ports configuration option. */
274         hierarchy[2] = KRB5_CONF_KDC_TCP_PORTS;
275         if (krb5_aprof_get_string(aprof, hierarchy, TRUE,
276                                   &rdp->realm_tcp_listen))
277             rdp->realm_tcp_listen = strdup(def_tcp_listen);
278     }
279     if (!rdp->realm_tcp_listen) {
280         kret = ENOMEM;
281         goto whoops;
282     }
283     /* Handle stash file */
284     hierarchy[2] = KRB5_CONF_KEY_STASH_FILE;
285     if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &rdp->realm_stash))
286         manual = def_manual;
287     else
288         manual = FALSE;
289 
290     hierarchy[2] = KRB5_CONF_RESTRICT_ANONYMOUS_TO_TGT;
291     if (krb5_aprof_get_boolean(aprof, hierarchy, TRUE,
292                                &rdp->realm_restrict_anon))
293         rdp->realm_restrict_anon = def_restrict_anon;
294 
295     /* Handle master key type */
296     hierarchy[2] = KRB5_CONF_MASTER_KEY_TYPE;
297     if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &svalue) ||
298         krb5_string_to_enctype(svalue, &rdp->realm_mkey.enctype))
299         rdp->realm_mkey.enctype = manual ? def_enctype : ENCTYPE_UNKNOWN;
300     free(svalue);
301     svalue = NULL;
302 
303     /* Handle reject-bad-transit flag */
304     hierarchy[2] = KRB5_CONF_REJECT_BAD_TRANSIT;
305     if (krb5_aprof_get_boolean(aprof, hierarchy, TRUE,
306                                 &rdp->realm_reject_bad_transit))
307         rdp->realm_reject_bad_transit = TRUE;
308 
309     /* Handle ticket maximum life */
310     hierarchy[2] = KRB5_CONF_MAX_LIFE;
311     if (krb5_aprof_get_deltat(aprof, hierarchy, TRUE, &rdp->realm_maxlife))
312         rdp->realm_maxlife = KRB5_KDB_MAX_LIFE;
313 
314     /* Handle ticket renewable maximum life */
315     hierarchy[2] = KRB5_CONF_MAX_RENEWABLE_LIFE;
316     if (krb5_aprof_get_deltat(aprof, hierarchy, TRUE, &rdp->realm_maxrlife))
317         rdp->realm_maxrlife = KRB5_KDB_MAX_RLIFE;
318 
319     /* Handle KDC referrals */
320     hierarchy[2] = KRB5_CONF_NO_HOST_REFERRAL;
321     (void)krb5_aprof_get_string_all(aprof, hierarchy, &svalue);
322     kret = combine(no_referral, svalue, &rdp->realm_no_referral);
323     if (kret)
324         goto whoops;
325     free(svalue);
326     svalue = NULL;
327 
328     hierarchy[2] = KRB5_CONF_HOST_BASED_SERVICES;
329     (void)krb5_aprof_get_string_all(aprof, hierarchy, &svalue);
330     kret = combine(hostbased, svalue, &rdp->realm_hostbased);
331     if (kret)
332         goto whoops;
333     free(svalue);
334     svalue = NULL;
335 
336     hierarchy[2] = KRB5_CONF_DISABLE_PAC;
337     if (krb5_aprof_get_boolean(aprof, hierarchy, TRUE,
338                                &rdp->realm_disable_pac))
339         rdp->realm_disable_pac = FALSE;
340 
341     /*
342      * We've got our parameters, now go and setup our realm context.
343      */
344 
345     /* Set the default realm of this context */
346     if ((kret = krb5_set_default_realm(rdp->realm_context, realm))) {
347         kdc_err(rdp->realm_context, kret,
348                 _("while setting default realm to %s"), realm);
349         goto whoops;
350     }
351 
352     /* first open the database  before doing anything */
353     kdb_open_flags = KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_KDC;
354     if ((kret = krb5_db_open(rdp->realm_context, db_args, kdb_open_flags))) {
355         kdc_err(rdp->realm_context, kret,
356                 _("while initializing database for realm %s"), realm);
357         goto whoops;
358     }
359 
360     /* Assemble and parse the master key name */
361     if ((kret = krb5_db_setup_mkey_name(rdp->realm_context, rdp->realm_mpname,
362                                         rdp->realm_name, (char **) NULL,
363                                         &rdp->realm_mprinc))) {
364         kdc_err(rdp->realm_context, kret,
365                 _("while setting up master key name %s for realm %s"),
366                 rdp->realm_mpname, realm);
367         goto whoops;
368     }
369 
370     /*
371      * Get the master key (note, may not be the most current mkey).
372      */
373     if ((kret = krb5_db_fetch_mkey(rdp->realm_context, rdp->realm_mprinc,
374                                    rdp->realm_mkey.enctype, manual,
375                                    FALSE, rdp->realm_stash,
376                                    &mkvno, NULL, &rdp->realm_mkey))) {
377         kdc_err(rdp->realm_context, kret,
378                 _("while fetching master key %s for realm %s"),
379                 rdp->realm_mpname, realm);
380         goto whoops;
381     }
382 
383     if (krb5int_c_deprecated_enctype(rdp->realm_mkey.enctype)) {
384         if (krb5_enctype_to_name(rdp->realm_mkey.enctype, FALSE, ename,
385                                  sizeof(ename)))
386             ename[0] = '\0';
387         fprintf(stderr, _("Stash file %s uses DEPRECATED enctype %s!\n"),
388                 rdp->realm_stash, ename);
389     }
390 
391     if ((kret = krb5_db_fetch_mkey_list(rdp->realm_context, rdp->realm_mprinc,
392                                         &rdp->realm_mkey))) {
393         kdc_err(rdp->realm_context, kret,
394                 _("while fetching master keys list for realm %s"), realm);
395         goto whoops;
396     }
397 
398 
399     /* Set up the keytab */
400     if ((kret = krb5_ktkdb_resolve(rdp->realm_context, NULL,
401                                    &rdp->realm_keytab))) {
402         kdc_err(rdp->realm_context, kret,
403                 _("while resolving kdb keytab for realm %s"), realm);
404         goto whoops;
405     }
406 
407     /* Preformat the TGS name */
408     if ((kret = krb5_build_principal(rdp->realm_context, &rdp->realm_tgsprinc,
409                                      strlen(realm), realm, KRB5_TGS_NAME,
410                                      realm, (char *) NULL))) {
411         kdc_err(rdp->realm_context, kret,
412                 _("while building TGS name for realm %s"), realm);
413         goto whoops;
414     }
415 
416 whoops:
417     /*
418      * If we choked, then clean up any dirt we may have dropped on the floor.
419      */
420     if (kret) {
421 
422         finish_realm(rdp);
423     }
424     return(kret);
425 }
426 
427 static void
428 on_monitor_signal(int signo)
429 {
430     signal_received = signo;
431 }
432 
433 static void
434 on_monitor_sighup(int signo)
435 {
436     sighup_received = 1;
437 }
438 
439 /*
440  * Kill the worker subprocesses given by pids[0..bound-1], skipping any which
441  * are set to -1, and wait for them to exit (so that we know the ports are no
442  * longer in use).
443  */
444 static void
445 terminate_workers(pid_t *pids, int bound)
446 {
447     int i, status, num_active = 0;
448     pid_t pid;
449 
450     /* Kill the active worker pids. */
451     for (i = 0; i < bound; i++) {
452         if (pids[i] == -1)
453             continue;
454         kill(pids[i], SIGTERM);
455         num_active++;
456     }
457 
458     /* Wait for them to exit. */
459     while (num_active > 0) {
460         pid = wait(&status);
461         if (pid >= 0)
462             num_active--;
463     }
464 }
465 
466 /*
467  * Create num worker processes and return successfully in each child.  The
468  * parent process will act as a supervisor and will only return from this
469  * function in error cases.
470  */
471 static krb5_error_code
472 create_workers(verto_ctx *ctx, int num)
473 {
474     krb5_error_code retval;
475     int i, status;
476     pid_t pid, *pids;
477 #ifdef POSIX_SIGNALS
478     struct sigaction s_action;
479 #endif /* POSIX_SIGNALS */
480 
481     /*
482      * Setup our signal handlers which will forward to the children.
483      * These handlers will be overridden in the child processes.
484      */
485 #ifdef POSIX_SIGNALS
486     (void) sigemptyset(&s_action.sa_mask);
487     s_action.sa_flags = 0;
488     s_action.sa_handler = on_monitor_signal;
489     (void) sigaction(SIGINT, &s_action, (struct sigaction *) NULL);
490     (void) sigaction(SIGTERM, &s_action, (struct sigaction *) NULL);
491     (void) sigaction(SIGQUIT, &s_action, (struct sigaction *) NULL);
492     s_action.sa_handler = on_monitor_sighup;
493     (void) sigaction(SIGHUP, &s_action, (struct sigaction *) NULL);
494 #else  /* POSIX_SIGNALS */
495     signal(SIGINT, on_monitor_signal);
496     signal(SIGTERM, on_monitor_signal);
497     signal(SIGQUIT, on_monitor_signal);
498     signal(SIGHUP, on_monitor_sighup);
499 #endif /* POSIX_SIGNALS */
500 
501     /* Create child worker processes; return in each child. */
502     krb5_klog_syslog(LOG_INFO, _("creating %d worker processes"), num);
503     pids = calloc(num, sizeof(pid_t));
504     if (pids == NULL)
505         return ENOMEM;
506     for (i = 0; i < num; i++) {
507         pid = fork();
508         if (pid == 0) {
509             free(pids);
510             if (!verto_reinitialize(ctx)) {
511                 krb5_klog_syslog(LOG_ERR,
512                                  _("Unable to reinitialize main loop"));
513                 return ENOMEM;
514             }
515             retval = loop_setup_signals(ctx, &shandle, reset_for_hangup);
516             if (retval) {
517                 krb5_klog_syslog(LOG_ERR, _("Unable to initialize signal "
518                                             "handlers in pid %d"), pid);
519                 return retval;
520             }
521 
522             /* Avoid race condition */
523             if (signal_received)
524                 exit(0);
525 
526             /* Return control to main() in the new worker process. */
527             return 0;
528         }
529         if (pid == -1) {
530             /* Couldn't fork enough times. */
531             status = errno;
532             terminate_workers(pids, i);
533             free(pids);
534             return status;
535         }
536         pids[i] = pid;
537     }
538 
539     /* We're going to use our own main loop here. */
540     loop_free(ctx);
541 
542     /* Supervise the worker processes. */
543     while (!signal_received) {
544         /* Wait until a worker process exits or we get a signal. */
545         pid = wait(&status);
546         if (pid >= 0) {
547             krb5_klog_syslog(LOG_ERR, _("worker %ld exited with status %d"),
548                              (long) pid, status);
549 
550             /* Remove the pid from the table. */
551             for (i = 0; i < num; i++) {
552                 if (pids[i] == pid)
553                     pids[i] = -1;
554             }
555 
556             /* When one worker process exits, terminate them all, so that KDC
557              * crashes behave similarly with or without worker processes. */
558             break;
559         }
560 
561         /* Propagate HUP signal to worker processes if we received one. */
562         if (sighup_received) {
563             sighup_received = 0;
564             for (i = 0; i < num; i++) {
565                 if (pids[i] != -1)
566                     kill(pids[i], SIGHUP);
567             }
568         }
569     }
570     if (signal_received)
571         krb5_klog_syslog(LOG_INFO, _("signal %d received in supervisor"),
572                          signal_received);
573 
574     terminate_workers(pids, num);
575     free(pids);
576     exit(0);
577 }
578 
579 static void
580 usage(char *name)
581 {
582     fprintf(stderr,
583             _("usage: %s [-x db_args]* [-d dbpathname] [-r dbrealmname]\n"
584               "\t\t[-T time_offset] [-m] [-k masterenctype]\n"
585               "\t\t[-M masterkeyname] [-p port] [-P pid_file]\n"
586               "\t\t[-n] [-w numworkers] [/]\n\n"
587               "where,\n"
588               "\t[-x db_args]* - Any number of database specific arguments.\n"
589               "\t\t\tLook at each database module documentation for "
590               "\t\t\tsupported arguments\n"),
591             name);
592     exit(1);
593 }
594 
595 
596 static void
597 initialize_realms(krb5_context kcontext, int argc, char **argv,
598                   int *tcp_listen_backlog_out)
599 {
600     int                 c;
601     char                *db_name = (char *) NULL;
602     char                *lrealm = (char *) NULL;
603     char                *mkey_name = (char *) NULL;
604     krb5_error_code     retval;
605     krb5_enctype        menctype = ENCTYPE_UNKNOWN;
606     kdc_realm_t         *rdatap = NULL;
607     krb5_boolean        manual = FALSE;
608     krb5_boolean        def_restrict_anon;
609     char                *def_udp_listen = NULL;
610     char                *def_tcp_listen = NULL;
611     krb5_pointer        aprof = kcontext->profile;
612     const char          *hierarchy[3];
613     char                *no_referral = NULL;
614     char                *hostbased = NULL;
615     int                  db_args_size = 0;
616     char                **db_args = NULL;
617 
618     extern char *optarg;
619 
620     hierarchy[0] = KRB5_CONF_KDCDEFAULTS;
621     hierarchy[1] = KRB5_CONF_KDC_LISTEN;
622     hierarchy[2] = NULL;
623     if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &def_udp_listen)) {
624         hierarchy[1] = KRB5_CONF_KDC_PORTS;
625         if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &def_udp_listen))
626             def_udp_listen = NULL;
627     }
628     hierarchy[1] = KRB5_CONF_KDC_TCP_LISTEN;
629     if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &def_tcp_listen)) {
630         hierarchy[1] = KRB5_CONF_KDC_TCP_PORTS;
631         if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &def_tcp_listen))
632             def_tcp_listen = NULL;
633     }
634     hierarchy[1] = KRB5_CONF_KDC_MAX_DGRAM_REPLY_SIZE;
635     if (krb5_aprof_get_int32(aprof, hierarchy, TRUE, &max_dgram_reply_size))
636         max_dgram_reply_size = MAX_DGRAM_SIZE;
637     if (tcp_listen_backlog_out != NULL) {
638         hierarchy[1] = KRB5_CONF_KDC_TCP_LISTEN_BACKLOG;
639         if (krb5_aprof_get_int32(aprof, hierarchy, TRUE,
640                                  tcp_listen_backlog_out))
641             *tcp_listen_backlog_out = DEFAULT_TCP_LISTEN_BACKLOG;
642     }
643     hierarchy[1] = KRB5_CONF_RESTRICT_ANONYMOUS_TO_TGT;
644     if (krb5_aprof_get_boolean(aprof, hierarchy, TRUE, &def_restrict_anon))
645         def_restrict_anon = FALSE;
646     hierarchy[1] = KRB5_CONF_NO_HOST_REFERRAL;
647     if (krb5_aprof_get_string_all(aprof, hierarchy, &no_referral))
648         no_referral = 0;
649     hierarchy[1] = KRB5_CONF_HOST_BASED_SERVICES;
650     if (krb5_aprof_get_string_all(aprof, hierarchy, &hostbased))
651         hostbased = 0;
652 
653     if (def_udp_listen == NULL) {
654         def_udp_listen = strdup(DEFAULT_KDC_UDP_PORTLIST);
655         if (def_udp_listen == NULL) {
656             fprintf(stderr, _(" KDC cannot initialize. Not enough memory\n"));
657             exit(1);
658         }
659     }
660     if (def_tcp_listen == NULL) {
661         def_tcp_listen = strdup(DEFAULT_KDC_TCP_PORTLIST);
662         if (def_tcp_listen == NULL) {
663             fprintf(stderr, _(" KDC cannot initialize. Not enough memory\n"));
664             exit(1);
665         }
666     }
667 
668     /*
669      * Loop through the option list.  Each time we encounter a realm name, use
670      * the previously scanned options to fill in for defaults.  We do this
671      * twice if worker processes are used, so we must initialize optind.
672      */
673     optind = 1;
674     while ((c = getopt(argc, argv, "x:r:d:mM:k:R:P:p:nw:4:T:X3")) != -1) {
675         switch(c) {
676         case 'x':
677             db_args_size++;
678             {
679                 char **temp = realloc( db_args, sizeof(char*) * (db_args_size+1)); /* one for NULL */
680                 if( temp == NULL )
681                 {
682                     fprintf(stderr, _("%s: KDC cannot initialize. Not enough "
683                                       "memory\n"), argv[0]);
684                     exit(1);
685                 }
686 
687                 db_args = temp;
688             }
689             db_args[db_args_size-1] = optarg;
690             db_args[db_args_size]   = NULL;
691             break;
692 
693         case 'r':                       /* realm name for db */
694             if (!find_realm_data(&shandle, optarg, (krb5_ui_4) strlen(optarg))) {
695                 if ((rdatap = (kdc_realm_t *) malloc(sizeof(kdc_realm_t)))) {
696                     retval = init_realm(rdatap, aprof, optarg, mkey_name,
697                                         menctype, def_udp_listen,
698                                         def_tcp_listen, manual,
699                                         def_restrict_anon, db_args,
700                                         no_referral, hostbased);
701                     if (retval) {
702                         fprintf(stderr, _("%s: cannot initialize realm %s - "
703                                           "see log file for details\n"),
704                                 argv[0], optarg);
705                         exit(1);
706                     }
707                     shandle.kdc_realmlist[shandle.kdc_numrealms] = rdatap;
708                     shandle.kdc_numrealms++;
709                     free(db_args), db_args=NULL, db_args_size = 0;
710                 }
711                 else
712                 {
713                     fprintf(stderr, _("%s: cannot initialize realm %s. Not "
714                                       "enough memory\n"), argv[0], optarg);
715                     exit(1);
716                 }
717             }
718             break;
719         case 'd':                       /* pathname for db */
720             /* now db_name is not a separate argument.
721              * It has to be passed as part of the db_args
722              */
723             if( db_name == NULL ) {
724                 if (asprintf(&db_name, "dbname=%s", optarg) < 0) {
725                     fprintf(stderr, _("%s: KDC cannot initialize. Not enough "
726                                       "memory\n"), argv[0]);
727                     exit(1);
728                 }
729             }
730 
731             db_args_size++;
732             {
733                 char **temp = realloc( db_args, sizeof(char*) * (db_args_size+1)); /* one for NULL */
734                 if( temp == NULL )
735                 {
736                     fprintf(stderr, _("%s: KDC cannot initialize. Not enough "
737                                       "memory\n"), argv[0]);
738                     exit(1);
739                 }
740 
741                 db_args = temp;
742             }
743             db_args[db_args_size-1] = db_name;
744             db_args[db_args_size]   = NULL;
745             break;
746         case 'm':                       /* manual type-in of master key */
747             manual = TRUE;
748             if (menctype == ENCTYPE_UNKNOWN)
749                 menctype = DEFAULT_KDC_ENCTYPE;
750             break;
751         case 'M':                       /* master key name in DB */
752             mkey_name = optarg;
753             break;
754         case 'n':
755             nofork++;                   /* don't detach from terminal */
756             break;
757         case 'w':                       /* create multiple worker processes */
758             workers = atoi(optarg);
759             if (workers <= 0)
760                 usage(argv[0]);
761             break;
762         case 'k':                       /* enctype for master key */
763             if (krb5_string_to_enctype(optarg, &menctype))
764                 com_err(argv[0], 0, _("invalid enctype %s"), optarg);
765             break;
766         case 'R':
767             /* Replay cache name; defunct since we don't use a replay cache. */
768             break;
769         case 'P':
770             pid_file = optarg;
771             break;
772         case 'p':
773             free(def_udp_listen);
774             free(def_tcp_listen);
775             def_udp_listen = strdup(optarg);
776             def_tcp_listen = strdup(optarg);
777             if (def_udp_listen == NULL || def_tcp_listen == NULL) {
778                 fprintf(stderr, _(" KDC cannot initialize. Not enough "
779                                   "memory\n"));
780                 exit(1);
781             }
782             break;
783         case 'T':
784             time_offset = atoi(optarg);
785             break;
786         case '4':
787             break;
788         case 'X':
789             break;
790         case '?':
791         default:
792             usage(argv[0]);
793         }
794     }
795 
796     /*
797      * Check to see if we processed any realms.
798      */
799     if (shandle.kdc_numrealms == 0) {
800         /* no realm specified, use default realm */
801         if ((retval = krb5_get_default_realm(kcontext, &lrealm))) {
802             com_err(argv[0], retval,
803                     _("while attempting to retrieve default realm"));
804             fprintf (stderr,
805                      _("%s: %s, attempting to retrieve default realm\n"),
806                      argv[0], krb5_get_error_message(kcontext, retval));
807             exit(1);
808         }
809         if ((rdatap = (kdc_realm_t *) malloc(sizeof(kdc_realm_t)))) {
810             retval = init_realm(rdatap, aprof, lrealm, mkey_name, menctype,
811                                 def_udp_listen, def_tcp_listen, manual,
812                                 def_restrict_anon, db_args, no_referral,
813                                 hostbased);
814             if (retval) {
815                 fprintf(stderr, _("%s: cannot initialize realm %s - see log "
816                                   "file for details\n"), argv[0], lrealm);
817                 exit(1);
818             }
819             shandle.kdc_realmlist[0] = rdatap;
820             shandle.kdc_numrealms++;
821         }
822         krb5_free_default_realm(kcontext, lrealm);
823     }
824 
825     if (def_udp_listen)
826         free(def_udp_listen);
827     if (def_tcp_listen)
828         free(def_tcp_listen);
829     if (db_args)
830         free(db_args);
831     if (db_name)
832         free(db_name);
833     if (hostbased)
834         free(hostbased);
835     if (no_referral)
836         free(no_referral);
837 
838     return;
839 }
840 
841 static krb5_error_code
842 write_pid_file(const char *path)
843 {
844     FILE *file;
845     unsigned long pid;
846 
847     file = fopen(path, "w");
848     if (file == NULL)
849         return errno;
850     pid = (unsigned long) getpid();
851     if (fprintf(file, "%ld\n", pid) < 0 || fclose(file) == EOF)
852         return errno;
853     return 0;
854 }
855 
856 static void
857 finish_realms()
858 {
859     int i;
860 
861     for (i = 0; i < shandle.kdc_numrealms; i++) {
862         finish_realm(shandle.kdc_realmlist[i]);
863         shandle.kdc_realmlist[i] = 0;
864     }
865     shandle.kdc_numrealms = 0;
866 }
867 
868 /*
869   outline:
870 
871   process args & setup
872 
873   initialize database access (fetch master key, open DB)
874 
875   initialize network
876 
877   loop:
878   listen for packet
879 
880   determine packet type, dispatch to handling routine
881   (AS or TGS (or V4?))
882 
883   reflect response
884 
885   exit on signal
886 
887   clean up secrets, close db
888 
889   shut down network
890 
891   exit
892 */
893 
894 int main(int argc, char **argv)
895 {
896     krb5_error_code     retval;
897     krb5_context        kcontext;
898     kdc_realm_t *realm;
899     verto_ctx *ctx;
900     int tcp_listen_backlog;
901     int errout = 0;
902     int i;
903 
904     setlocale(LC_ALL, "");
905     if (strrchr(argv[0], '/'))
906         argv[0] = strrchr(argv[0], '/')+1;
907 
908     shandle.kdc_realmlist = malloc(sizeof(kdc_realm_t *) *
909                                    KRB5_KDC_MAX_REALMS);
910     if (shandle.kdc_realmlist == NULL) {
911         fprintf(stderr, _("%s: cannot get memory for realm list\n"), argv[0]);
912         exit(1);
913     }
914     memset(shandle.kdc_realmlist, 0,
915            (size_t) (sizeof(kdc_realm_t *) * KRB5_KDC_MAX_REALMS));
916 
917     /*
918      * A note about Kerberos contexts: This context, "kcontext", is used
919      * for the KDC operations, i.e. setup, network connection and error
920      * reporting.  The per-realm operations use the "realm_context"
921      * associated with each realm.
922      */
923     retval = krb5int_init_context_kdc(&kcontext);
924     if (retval) {
925         com_err(argv[0], retval, _("while initializing krb5"));
926         exit(1);
927     }
928     krb5_klog_init(kcontext, "kdc", argv[0], 1);
929     shandle.kdc_err_context = kcontext;
930     kdc_progname = argv[0];
931     /* N.B.: After this point, com_err sends output to the KDC log
932        file, and not to stderr.  We use the kdc_err wrapper around
933        com_err to ensure that the error state exists in the context
934        known to the krb5_klog callback. */
935 
936     initialize_kdc5_error_table();
937 
938     /*
939      * Scan through the argument list
940      */
941     initialize_realms(kcontext, argc, argv, &tcp_listen_backlog);
942 
943 #ifndef NOCACHE
944     retval = kdc_init_lookaside(kcontext);
945     if (retval) {
946         kdc_err(kcontext, retval, _("while initializing lookaside cache"));
947         finish_realms();
948         return 1;
949     }
950 #endif
951 
952     ctx = loop_init(VERTO_EV_TYPE_NONE);
953     if (!ctx) {
954         kdc_err(kcontext, ENOMEM, _("while creating main loop"));
955         finish_realms();
956         return 1;
957     }
958 
959     load_preauth_plugins(&shandle, kcontext, ctx);
960     load_authdata_plugins(kcontext);
961     retval = load_kdcpolicy_plugins(kcontext);
962     if (retval) {
963         kdc_err(kcontext, retval, _("while loading KDC policy plugin"));
964         finish_realms();
965         return 1;
966     }
967 
968     /* Add each realm's listener addresses to the loop. */
969     for (i = 0; i < shandle.kdc_numrealms; i++) {
970         realm = shandle.kdc_realmlist[i];
971         if (*realm->realm_listen != '\0') {
972             retval = loop_add_udp_address(KRB5_DEFAULT_PORT,
973                                           realm->realm_listen);
974             if (retval)
975                 goto net_init_error;
976         }
977         if (*realm->realm_tcp_listen != '\0') {
978             retval = loop_add_tcp_address(KRB5_DEFAULT_PORT,
979                                           realm->realm_tcp_listen);
980             if (retval)
981                 goto net_init_error;
982         }
983     }
984 
985     if (workers == 0) {
986         retval = loop_setup_signals(ctx, &shandle, reset_for_hangup);
987         if (retval) {
988             kdc_err(kcontext, retval, _("while initializing signal handlers"));
989             finish_realms();
990             return 1;
991         }
992     }
993     if ((retval = loop_setup_network(ctx, &shandle, kdc_progname,
994                                      tcp_listen_backlog))) {
995     net_init_error:
996         kdc_err(kcontext, retval, _("while initializing network"));
997         finish_realms();
998         return 1;
999     }
1000 
1001     /* Clean up realms for now and reinitialize them after daemonizing, since
1002      * some KDB modules are not fork-safe. */
1003     finish_realms();
1004 
1005     if (!nofork && daemon(0, 0)) {
1006         kdc_err(kcontext, errno, _("while detaching from tty"));
1007         return 1;
1008     }
1009     if (pid_file != NULL) {
1010         retval = write_pid_file(pid_file);
1011         if (retval) {
1012             kdc_err(kcontext, retval, _("while creating PID file"));
1013             finish_realms();
1014             return 1;
1015         }
1016     }
1017     if (workers > 0) {
1018         retval = create_workers(ctx, workers);
1019         if (retval) {
1020             kdc_err(kcontext, errno, _("creating worker processes"));
1021             return 1;
1022         }
1023     }
1024 
1025     initialize_realms(kcontext, argc, argv, NULL);
1026 
1027     /* Initialize audit system and audit KDC startup. */
1028     retval = load_audit_modules(kcontext);
1029     if (retval) {
1030         kdc_err(kcontext, retval, _("while loading audit plugin module(s)"));
1031         finish_realms();
1032         return 1;
1033     }
1034     krb5_klog_syslog(LOG_INFO, _("commencing operation"));
1035     if (nofork)
1036         fprintf(stderr, _("%s: starting...\n"), kdc_progname);
1037     kau_kdc_start(kcontext, TRUE);
1038 
1039     verto_run(ctx);
1040     kau_kdc_stop(kcontext, TRUE);
1041     krb5_klog_syslog(LOG_INFO, _("shutting down"));
1042     unload_preauth_plugins(kcontext);
1043     unload_authdata_plugins(kcontext);
1044     unload_kdcpolicy_plugins(kcontext);
1045     unload_audit_modules(kcontext);
1046     krb5_klog_close(kcontext);
1047     finish_realms();
1048     if (shandle.kdc_realmlist)
1049         free(shandle.kdc_realmlist);
1050 #ifndef NOCACHE
1051     kdc_free_lookaside(kcontext);
1052 #endif
1053     loop_free(ctx);
1054     krb5_free_context(kcontext);
1055     return errout;
1056 }
1057