2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
10 * Copyright 1990,2001 by the Massachusetts Institute of Technology.
12 * Export of this software from the United States of America may
13 * require a specific license from the United States Government.
14 * It is the responsibility of any person or organization contemplating
15 * export to obtain such a license before exporting.
17 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
18 * distribute this software and its documentation for any purpose and
19 * without fee is hereby granted, provided that the above copyright
20 * notice appear in all copies and that both that copyright notice and
21 * this permission notice appear in supporting documentation, and that
22 * the name of M.I.T. not be used in advertising or publicity pertaining
23 * to distribution of the software without specific, written prior
24 * permission. Furthermore if you modify this software you must label
25 * your software as modified software and not distribute it in such a
26 * fashion that it might be confused with the original M.I.T. software.
27 * M.I.T. makes no representations about the suitability of
28 * this software for any purpose. It is provided "as is" without express
29 * or implied warranty.
32 * Main procedure body for the KDC server process.
44 #include "adm_proto.h"
51 #ifdef HAVE_NETINET_IN_H
52 #include <netinet/in.h>
55 #ifdef KRB5_KRB4_COMPAT
59 #if defined(NEED_DAEMON_PROTO)
60 extern int daemon(int, int);
65 krb5_sigtype
request_exit (int);
66 krb5_sigtype
request_hup (int);
68 void setup_signal_handlers (void);
70 krb5_error_code
setup_sam (void);
72 void initialize_realms (krb5_context
, int, char **);
74 void finish_realms (char *);
76 static int nofork
= 0;
77 static int rkey_init_done
= 0;
79 /* Solaris Kerberos: global here that other functions access */
80 int max_tcp_data_connections
;
83 static struct sigaction s_action
;
84 #endif /* POSIX_SIGNALS */
86 #define KRB5_KDC_MAX_REALMS 32
89 * Find the realm entry for a given realm.
92 find_realm_data(char *rname
, krb5_ui_4 rsize
)
95 for (i
=0; i
<kdc_numrealms
; i
++) {
96 if ((rsize
== strlen(kdc_realmlist
[i
]->realm_name
)) &&
97 !strncmp(rname
, kdc_realmlist
[i
]->realm_name
, rsize
))
98 return(kdc_realmlist
[i
]);
100 return((kdc_realm_t
*) NULL
);
104 setup_server_realm(krb5_principal sprinc
)
106 krb5_error_code kret
;
107 kdc_realm_t
*newrealm
;
110 if (kdc_numrealms
> 1) {
111 if (!(newrealm
= find_realm_data(sprinc
->realm
.data
,
112 (krb5_ui_4
) sprinc
->realm
.length
)))
115 kdc_active_realm
= newrealm
;
118 kdc_active_realm
= kdc_realmlist
[0];
123 finish_realm(kdc_realm_t
*rdp
)
125 free(rdp
->realm_dbname
);
126 free(rdp
->realm_mpname
);
127 free(rdp
->realm_stash
);
128 free(rdp
->realm_ports
);
129 free(rdp
->realm_tcp_ports
);
130 if (rdp
->realm_keytab
)
131 krb5_kt_close(rdp
->realm_context
, rdp
->realm_keytab
);
132 if (rdp
->realm_context
) {
133 if (rdp
->realm_mprinc
)
134 krb5_free_principal(rdp
->realm_context
, rdp
->realm_mprinc
);
135 if (rdp
->realm_mkey
.length
&& rdp
->realm_mkey
.contents
) {
136 memset(rdp
->realm_mkey
.contents
, 0, rdp
->realm_mkey
.length
);
137 free(rdp
->realm_mkey
.contents
);
139 krb5_db_fini(rdp
->realm_context
);
140 if (rdp
->realm_tgsprinc
)
141 krb5_free_principal(rdp
->realm_context
, rdp
->realm_tgsprinc
);
142 krb5_free_context(rdp
->realm_context
);
144 memset((char *) rdp
, 0, sizeof(*rdp
));
149 * Initialize a realm control structure from the alternate profile or from
150 * the specified defaults.
152 * After we're complete here, the essence of the realm is embodied in the
153 * realm data and we should be all set to begin operation for that realm.
155 static krb5_error_code
156 init_realm(krb5_context kcontext
, char *progname
, kdc_realm_t
*rdp
, char *realm
,
157 char *def_mpname
, krb5_enctype def_enctype
, char *def_udp_ports
,
158 char *def_tcp_ports
, krb5_boolean def_manual
, char **db_args
)
160 krb5_error_code kret
;
162 krb5_realm_params
*rparams
;
164 memset((char *) rdp
, 0, sizeof(kdc_realm_t
));
170 rdp
->realm_name
= realm
;
171 kret
= krb5int_init_context_kdc(&rdp
->realm_context
);
173 com_err(progname
, kret
, gettext("while getting context for realm %s"),
180 * Set the current context to that of the realm being init'ed
182 krb5_klog_set_context(rdp
->realm_context
);
184 kret
= krb5_read_realm_params(rdp
->realm_context
, rdp
->realm_name
,
187 com_err(progname
, kret
, gettext("while reading realm parameters"));
191 /* Handle profile file name */
192 if (rparams
&& rparams
->realm_profile
)
193 rdp
->realm_profile
= strdup(rparams
->realm_profile
);
195 /* Handle master key name */
196 if (rparams
&& rparams
->realm_mkey_name
)
197 rdp
->realm_mpname
= strdup(rparams
->realm_mkey_name
);
199 rdp
->realm_mpname
= (def_mpname
) ? strdup(def_mpname
) :
200 strdup(KRB5_KDB_M_NAME
);
202 /* Handle KDC ports */
203 if (rparams
&& rparams
->realm_kdc_ports
)
204 rdp
->realm_ports
= strdup(rparams
->realm_kdc_ports
);
206 rdp
->realm_ports
= strdup(def_udp_ports
);
207 if (rparams
&& rparams
->realm_kdc_tcp_ports
)
208 rdp
->realm_tcp_ports
= strdup(rparams
->realm_kdc_tcp_ports
);
210 rdp
->realm_tcp_ports
= strdup(def_tcp_ports
);
212 /* Handle stash file */
213 if (rparams
&& rparams
->realm_stash_file
) {
214 rdp
->realm_stash
= strdup(rparams
->realm_stash_file
);
219 /* Handle master key type */
220 if (rparams
&& rparams
->realm_enctype_valid
)
221 rdp
->realm_mkey
.enctype
= (krb5_enctype
) rparams
->realm_enctype
;
223 rdp
->realm_mkey
.enctype
= manual
? def_enctype
: ENCTYPE_UNKNOWN
;
225 /* Handle reject-bad-transit flag */
226 if (rparams
&& rparams
->realm_reject_bad_transit_valid
)
227 rdp
->realm_reject_bad_transit
= rparams
->realm_reject_bad_transit
;
229 rdp
->realm_reject_bad_transit
= 1;
231 /* Handle ticket maximum life */
232 rdp
->realm_maxlife
= (rparams
&& rparams
->realm_max_life_valid
) ?
233 rparams
->realm_max_life
: KRB5_KDB_MAX_LIFE
;
235 /* Handle ticket renewable maximum life */
236 rdp
->realm_maxrlife
= (rparams
&& rparams
->realm_max_rlife_valid
) ?
237 rparams
->realm_max_rlife
: KRB5_KDB_MAX_RLIFE
;
240 krb5_free_realm_params(rdp
->realm_context
, rparams
);
243 * We've got our parameters, now go and setup our realm context.
246 /* Set the default realm of this context */
247 if ((kret
= krb5_set_default_realm(rdp
->realm_context
, realm
))) {
248 com_err(progname
, kret
, gettext("while setting default realm to %s"),
253 /* first open the database before doing anything */
254 #ifdef KRBCONF_KDC_MODIFIES_KDB
255 if ((kret
= krb5_db_open(rdp
->realm_context
, db_args
,
256 KRB5_KDB_OPEN_RW
| KRB5_KDB_SRV_TYPE_KDC
))) {
258 if ((kret
= krb5_db_open(rdp
->realm_context
, db_args
,
259 KRB5_KDB_OPEN_RO
| KRB5_KDB_SRV_TYPE_KDC
))) {
263 * Make sure that error messages are printed using gettext
265 com_err(progname
, kret
,
266 gettext("while initializing database for realm %s"), realm
);
270 /* Assemble and parse the master key name */
271 if ((kret
= krb5_db_setup_mkey_name(rdp
->realm_context
, rdp
->realm_mpname
,
272 rdp
->realm_name
, (char **) NULL
,
273 &rdp
->realm_mprinc
))) {
274 com_err(progname
, kret
,
275 gettext("while setting up master key name %s for realm %s"),
276 rdp
->realm_mpname
, realm
);
281 * Get the master key.
283 if ((kret
= krb5_db_fetch_mkey(rdp
->realm_context
, rdp
->realm_mprinc
,
284 rdp
->realm_mkey
.enctype
, manual
,
285 FALSE
, rdp
->realm_stash
,
286 0, &rdp
->realm_mkey
))) {
287 com_err(progname
, kret
,
288 gettext("while fetching master key %s for realm %s"),
289 rdp
->realm_mpname
, realm
);
293 /* Verify the master key */
294 if ((kret
= krb5_db_verify_master_key(rdp
->realm_context
,
296 &rdp
->realm_mkey
))) {
297 com_err(progname
, kret
,
298 gettext("while verifying master key for realm %s"),
303 if ((kret
= krb5_db_set_mkey(rdp
->realm_context
, &rdp
->realm_mkey
))) {
304 com_err(progname
, kret
,
305 gettext("while processing master key for realm %s"),
310 /* Set up the keytab */
311 if ((kret
= krb5_ktkdb_resolve(rdp
->realm_context
, NULL
,
312 &rdp
->realm_keytab
))) {
313 com_err(progname
, kret
,
314 gettext("while resolving kdb keytab for realm %s"),
319 /* Preformat the TGS name */
320 if ((kret
= krb5_build_principal(rdp
->realm_context
, &rdp
->realm_tgsprinc
,
321 strlen(realm
), realm
, KRB5_TGS_NAME
,
322 realm
, (char *) NULL
))) {
323 com_err(progname
, kret
,
324 gettext("while building TGS name for realm %s"),
329 if (!rkey_init_done
) {
331 #ifdef KRB5_KRB4_COMPAT
332 krb5_keyblock temp_key
;
335 * If all that worked, then initialize the random key
339 seed
.length
= rdp
->realm_mkey
.length
;
340 seed
.data
= (char *)rdp
->realm_mkey
.contents
;
341 /* SUNW14resync - XXX */
343 if ((kret
= krb5_c_random_add_entropy(rdp
->realm_context
,
344 KRB5_C_RANDSOURCE_TRUSTEDPARTY
, &seed
)))
348 #ifdef KRB5_KRB4_COMPAT
349 if ((kret
= krb5_c_make_random_key(rdp
->realm_context
,
350 ENCTYPE_DES_CBC_CRC
, &temp_key
))) {
351 com_err(progname
, kret
,
352 "while initializing V4 random key generator");
356 (void) des_init_random_number_generator(temp_key
.contents
);
357 krb5_free_keyblock_contents(rdp
->realm_context
, &temp_key
);
363 * If we choked, then clean up any dirt we may have dropped on the floor.
372 * Set the current context back to the general context
374 krb5_klog_set_context(kcontext
);
380 request_exit(int signo
)
382 signal_requests_exit
= 1;
392 request_hup(int signo
)
394 signal_requests_hup
= 1;
404 setup_signal_handlers(void)
407 (void) sigemptyset(&s_action
.sa_mask
);
408 s_action
.sa_flags
= 0;
409 s_action
.sa_handler
= request_exit
;
410 (void) sigaction(SIGINT
, &s_action
, NULL
);
411 (void) sigaction(SIGTERM
, &s_action
, NULL
);
412 s_action
.sa_handler
= request_hup
;
413 (void) sigaction(SIGHUP
, &s_action
, NULL
);
414 s_action
.sa_handler
= SIG_IGN
;
415 (void) sigaction(SIGPIPE
, &s_action
, NULL
);
416 #else /* POSIX_SIGNALS */
417 signal(SIGINT
, request_exit
);
418 signal(SIGTERM
, request_exit
);
419 signal(SIGHUP
, request_hup
);
420 signal(SIGPIPE
, SIG_IGN
);
421 #endif /* POSIX_SIGNALS */
429 return krb5_c_make_random_key(kdc_context
, ENCTYPE_DES_CBC_MD5
, &psr_key
);
435 fprintf(stderr
, gettext("usage: %s [-d dbpathname] [-r dbrealmname] [-R replaycachename ]\n\t[-m] [-k masterenctype] [-M masterkeyname] [-p port] [-n]\n"), name
);
436 fprintf(stderr
, "usage: %s [-x db_args]* [-d dbpathname] [-r dbrealmname] [-R replaycachename ]\n\t[-m] [-k masterenctype] [-M masterkeyname] [-p port] [-X] [-n]\n"
437 "\nwhere,\n\t[-x db_args]* - any number of database specific arguments.\n"
438 "\t\t\tLook at each database documentation for supported arguments\n",
444 initialize_realms(krb5_context kcontext
, int argc
, char **argv
)
447 char *db_name
= (char *) NULL
;
448 char *mkey_name
= (char *) NULL
;
449 char *rcname
= KDCRCACHE
;
451 krb5_error_code retval
;
452 krb5_enctype menctype
= ENCTYPE_UNKNOWN
;
454 krb5_boolean manual
= FALSE
;
455 char *default_udp_ports
= 0;
456 char *default_tcp_ports
= 0;
458 const char *hierarchy
[3];
459 char **db_args
= NULL
;
460 int db_args_size
= 0;
462 #ifdef KRB5_KRB4_COMPAT
467 if (!krb5_aprof_init(DEFAULT_KDC_PROFILE
, KDC_PROFILE_ENV
, &aprof
)) {
468 hierarchy
[0] = "kdcdefaults";
469 hierarchy
[1] = "kdc_ports";
470 hierarchy
[2] = (char *) NULL
;
471 if (krb5_aprof_get_string(aprof
, hierarchy
, TRUE
, &default_udp_ports
))
472 default_udp_ports
= 0;
473 hierarchy
[1] = "kdc_tcp_ports";
474 if (krb5_aprof_get_string(aprof
, hierarchy
, TRUE
, &default_tcp_ports
))
475 default_tcp_ports
= 0;
476 hierarchy
[1] = "kdc_max_tcp_connections";
477 if (krb5_aprof_get_int32(aprof
, hierarchy
, TRUE
,
478 &max_tcp_data_connections
)) {
479 max_tcp_data_connections
= DEFAULT_KDC_TCP_CONNECTIONS
;
480 } else if (max_tcp_data_connections
< MIN_KDC_TCP_CONNECTIONS
) {
481 max_tcp_data_connections
= DEFAULT_KDC_TCP_CONNECTIONS
;
483 #ifdef KRB5_KRB4_COMPAT
484 hierarchy
[1] = "v4_mode";
485 if (krb5_aprof_get_string(aprof
, hierarchy
, TRUE
, &v4mode
))
488 /* aprof_init can return 0 with aprof == NULL */
490 krb5_aprof_finish(aprof
);
492 if (default_udp_ports
== 0)
493 default_udp_ports
= strdup(DEFAULT_KDC_UDP_PORTLIST
);
494 if (default_tcp_ports
== 0)
495 default_tcp_ports
= strdup(DEFAULT_KDC_TCP_PORTLIST
);
497 * Loop through the option list. Each time we encounter a realm name,
498 * use the previously scanned options to fill in for defaults.
500 while ((c
= getopt(argc
, argv
, "x:r:d:mM:k:R:e:p:s:n4:X3")) != -1) {
505 char **temp
= reallocarray(db_args
, db_args_size
+ 1,
506 sizeof (char *)); /* one for NULL */
509 /* Solaris Kerberos: Keep error messages consistent */
510 com_err(argv
[0], errno
, gettext("while initializing KDC"));
516 db_args
[db_args_size
-1] = optarg
;
517 db_args
[db_args_size
] = NULL
;
520 case 'r': /* realm name for db */
521 if (!find_realm_data(optarg
, (krb5_ui_4
) strlen(optarg
))) {
522 if ((rdatap
= (kdc_realm_t
*) malloc(sizeof(kdc_realm_t
)))) {
523 if ((retval
= init_realm(kcontext
, argv
[0], rdatap
, optarg
,
526 default_tcp_ports
, manual
, db_args
))) {
527 /* Solaris Kerberos: Keep error messages consistent */
528 com_err(argv
[0], retval
, gettext("while initializing realm %s"), optarg
);
531 kdc_realmlist
[kdc_numrealms
] = rdatap
;
533 free(db_args
), db_args
=NULL
, db_args_size
= 0;
537 /* Solaris Kerberos: Keep error messages consistent */
538 com_err(argv
[0], errno
, gettext("while initializing realm %s"), optarg
);
543 case 'd': /* pathname for db */
544 /* now db_name is not a seperate argument. It has to be passed as part of the db_args */
545 if( db_name
== NULL
)
547 db_name
= malloc(sizeof("dbname=") + strlen(optarg
));
548 if( db_name
== NULL
)
550 /* Solaris Kerberos: Keep error messages consistent */
551 com_err(argv
[0], errno
, gettext("while initializing KDC"));
555 sprintf( db_name
, "dbname=%s", optarg
);
560 char **temp
= reallocarray(db_args
, db_args_size
+ 1,
561 sizeof (char *)); /* one for NULL */
564 /* Solaris Kerberos: Keep error messages consistent */
565 com_err(argv
[0], errno
, gettext("while initializing KDC"));
571 db_args
[db_args_size
-1] = db_name
;
572 db_args
[db_args_size
] = NULL
;
574 case 'm': /* manual type-in of master key */
576 if (menctype
== ENCTYPE_UNKNOWN
)
577 menctype
= ENCTYPE_DES_CBC_CRC
;
579 case 'M': /* master key name in DB */
583 nofork
++; /* don't detach from terminal */
585 case 'k': /* enctype for master key */
586 /* Solaris Kerberos: Keep error messages consistent */
587 if (retval
= krb5_string_to_enctype(optarg
, &menctype
))
588 com_err(argv
[0], retval
,
589 gettext("while converting %s to an enctype"), optarg
);
595 free(default_udp_ports
);
596 default_udp_ports
= strdup(optarg
);
598 free(default_tcp_ports
);
599 default_tcp_ports
= strdup(optarg
);
603 #ifdef KRB5_KRB4_COMPAT
605 v4mode
= strdup(optarg
);
609 #ifdef KRB5_KRB4_COMPAT
610 enable_v4_crossrealm(argv
[0]);
620 #ifdef KRB5_KRB4_COMPAT
624 process_v4_mode(argv
[0], v4mode
);
629 * Check to see if we processed any realms.
631 if (kdc_numrealms
== 0) {
632 /* no realm specified, use default realm */
633 if ((retval
= krb5_get_default_realm(kcontext
, &lrealm
))) {
634 com_err(argv
[0], retval
,
635 gettext("while attempting to retrieve default realm"));
636 /* Solaris Kerberos: avoid double logging */
638 fprintf (stderr
, "%s: %s, %s", argv
[0], error_message (retval
),
639 gettext("attempting to retrieve default realm\n"));
643 if ((rdatap
= (kdc_realm_t
*) malloc(sizeof(kdc_realm_t
)))) {
644 if ((retval
= init_realm(kcontext
, argv
[0], rdatap
, lrealm
,
645 mkey_name
, menctype
, default_udp_ports
,
646 default_tcp_ports
, manual
, db_args
))) {
647 /* Solaris Kerberos: Keep error messages consistent */
648 com_err(argv
[0], retval
, gettext("while initializing realm %s"), lrealm
);
651 kdc_realmlist
[0] = rdatap
;
660 * Now handle the replay cache.
662 if ((retval
= kdc_initialize_rcache(kcontext
, rcname
))) {
663 com_err(argv
[0], retval
, gettext("while initializing KDC replay cache '%s'"),
669 /* Ensure that this is set for our first request. */
670 kdc_active_realm
= kdc_realmlist
[0];
672 free(default_udp_ports
);
673 free(default_tcp_ports
);
681 finish_realms(char *prog
)
685 for (i
= 0; i
< kdc_numrealms
; i
++) {
686 finish_realm(kdc_realmlist
[i
]);
687 kdc_realmlist
[i
] = 0;
696 initialize database access (fetch master key, open DB)
703 determine packet type, dispatch to handling routine
710 clean up secrets, close db
717 int main(int argc
, char **argv
)
719 krb5_error_code retval
;
720 krb5_context kcontext
;
723 krb5_boolean log_stderr_set
;
725 (void) setlocale(LC_ALL
, "");
727 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
728 #define TEXT_DOMAIN "KRB5KDC_TEST" /* Use this only if it weren't */
731 (void) textdomain(TEXT_DOMAIN
);
733 if (strrchr(argv
[0], '/'))
734 argv
[0] = strrchr(argv
[0], '/')+1;
736 if (!(kdc_realmlist
= (kdc_realm_t
**) malloc(sizeof(kdc_realm_t
*) *
737 KRB5_KDC_MAX_REALMS
))) {
738 fprintf(stderr
, gettext("%s: cannot get memory for realm list\n"), argv
[0]);
741 memset((char *) kdc_realmlist
, 0,
742 (size_t) (sizeof(kdc_realm_t
*) * KRB5_KDC_MAX_REALMS
));
745 * A note about Kerberos contexts: This context, "kcontext", is used
746 * for the KDC operations, i.e. setup, network connection and error
747 * reporting. The per-realm operations use the "realm_context"
748 * associated with each realm.
750 retval
= krb5int_init_context_kdc(&kcontext
);
752 com_err(argv
[0], retval
, gettext("while initializing krb5"));
755 krb5_klog_init(kcontext
, "kdc", argv
[0], 1);
759 * In the early stages of krb5kdc it is desirable to log error messages
760 * to stderr as well as any other logging locations specified in config
763 log_stderr_set
= krb5_klog_logging_to_stderr();
764 if (log_stderr_set
!= TRUE
) {
765 krb5_klog_add_stderr();
768 /* initialize_kdc5_error_table(); SUNWresync121 XXX */
771 * Scan through the argument list
773 initialize_realms(kcontext
, argc
, argv
);
775 setup_signal_handlers();
777 load_preauth_plugins(kcontext
);
779 retval
= setup_sam();
781 com_err(argv
[0], retval
, gettext("while initializing SAM"));
782 finish_realms(argv
[0]);
786 if ((retval
= setup_network(argv
[0]))) {
787 com_err(argv
[0], retval
, gettext("while initializing network"));
788 finish_realms(argv
[0]);
792 /* Solaris Kerberos: Remove the extra stderr logging */
793 if (log_stderr_set
!= TRUE
)
794 krb5_klog_remove_stderr();
798 * List the logs (FILE, STDERR, etc) which are currently being
799 * logged to and print that to stderr. Useful when trying to
800 * track down a failure via SMF.
802 if (retval
= krb5_klog_list_logs(argv
[0])) {
803 com_err(argv
[0], retval
, gettext("while listing logs"));
804 if (log_stderr_set
!= TRUE
) {
805 fprintf(stderr
, gettext("%s: %s while listing logs\n"),
806 argv
[0], error_message(retval
));
810 if (!nofork
&& daemon(0, 0)) {
811 com_err(argv
[0], errno
, gettext("while detaching from tty"));
812 if (log_stderr_set
!= TRUE
) {
813 fprintf(stderr
, gettext("%s: %s while detaching from tty\n"),
814 argv
[0], strerror(errno
));
816 finish_realms(argv
[0]);
819 if (retval
= krb5_klog_syslog(LOG_INFO
, "commencing operation")) {
820 com_err(argv
[0], retval
, gettext("while logging message"));
824 if ((retval
= listen_and_process(argv
[0]))) {
825 com_err(argv
[0], retval
, gettext("while processing network requests"));
828 if ((retval
= closedown_network(argv
[0]))) {
829 com_err(argv
[0], retval
, gettext("while shutting down network"));
832 krb5_klog_syslog(LOG_INFO
, "shutting down");
833 unload_preauth_plugins(kcontext
);
834 krb5_klog_close(kdc_context
);
835 finish_realms(argv
[0]);
838 (void) krb5_rc_close(kcontext
, kdc_rcache
);
841 kdc_free_lookaside(kcontext
);
843 krb5_free_context(kcontext
);