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