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