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