Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/kdc/main.c
34869 views
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
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 *
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 *
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
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
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
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
430
on_monitor_signal(int signo)
431
{
432
signal_received = signo;
433
}
434
435
static void
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
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
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
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
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
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
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
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
1049