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