2 * Copyright (C) 2002 by the Massachusetts Institute of Technology.
5 * Export of this software from the United States of America may
6 * require a specific license from the United States Government.
7 * It is the responsibility of any person or organization contemplating
8 * export to obtain such a license before exporting.
10 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
11 * distribute this software and its documentation for any purpose and
12 * without fee is hereby granted, provided that the above copyright
13 * notice appear in all copies and that both that copyright notice and
14 * this permission notice appear in supporting documentation, and that
15 * the name of M.I.T. not be used in advertising or publicity pertaining
16 * to distribution of the software without specific, written prior
17 * permission. Furthermore if you modify this software you must label
18 * your software as modified software and not distribute it in such a
19 * fashion that it might be confused with the original M.I.T. software.
20 * M.I.T. makes no representations about the suitability of
21 * this software for any purpose. It is provided "as is" without express
22 * or implied warranty.
23 * Copyright 1994 by OpenVision Technologies, Inc.
25 * Permission to use, copy, modify, distribute, and sell this software
26 * and its documentation for any purpose is hereby granted without fee,
27 * provided that the above copyright notice appears in all copies and
28 * that both that copyright notice and this permission notice appear in
29 * supporting documentation, and that the name of OpenVision not be used
30 * in advertising or publicity pertaining to distribution of the software
31 * without specific, written prior permission. OpenVision makes no
32 * representations about the suitability of this software for any
33 * purpose. It is provided "as is" without express or implied warranty.
35 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
36 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
37 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
38 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
39 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
40 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
41 * PERFORMANCE OF THIS SOFTWARE.
45 #include <kadm5/admin.h>
46 #include <krb5/adm_proto.h>
52 #ifdef HAVE_SYS_SELECT_H
53 #include <sys/select.h>
57 #include <sys/types.h>
59 #include <sys/signal.h>
60 #include <netinet/in.h>
65 #if defined(NEED_DAEMON_PROTO)
66 extern int daemon(int, int);
70 #define TKT_BUFSIZ 2048
78 int use_keytab, use_master;
79 int allow_v4_crossrealm = 0;
83 void init_keytab(krb5_context),
84 init_master(krb5_context, kadm5_config_params *),
85 cleanup_and_exit(int, krb5_context);
86 krb5_error_code do_connection(int, krb5_context);
87 krb5_error_code lookup_service_key(krb5_context, krb5_principal,
88 krb5_enctype, krb5_kvno,
89 krb5_keyblock *, krb5_kvno *);
90 krb5_error_code kdc_get_server_key(krb5_context, krb5_principal,
91 krb5_keyblock *, krb5_kvno *,
92 krb5_enctype, krb5_kvno);
94 static krb5_error_code
95 handle_classic_v4 (krb5_context context, krb5_ticket *v5tkt,
96 struct sockaddr_in *saddr,
97 krb5_data *tktdata, krb5_kvno *v4kvno);
98 static krb5_error_code
99 afs_return_v4(krb5_context, const krb5_principal , int *use_v5);
101 static void usage(context)
102 krb5_context context;
104 fprintf(stderr, "Usage: %s [-k[eytab]] [-m[aster] [-r realm]] [-nofork] [-p portnum]\n", whoami);
105 cleanup_and_exit(1, context);
108 static RETSIGTYPE request_exit(signo)
114 int (*encode_v4tkt)(KTEXT, char *, unsigned int *) = 0;
120 struct servent *serv;
121 struct sockaddr_in saddr;
122 struct timeval timeout;
125 krb5_context context;
126 krb5_error_code retval;
127 kadm5_config_params config_params;
128 unsigned long port = 0;
130 whoami = ((whoami = strrchr(argv[0], '/')) ? whoami + 1 : argv[0]);
132 retval = krb5_init_context(&context);
134 com_err(whoami, retval, "while initializing krb5");
139 krb5int_access k5int;
140 retval = krb5int_accessor(&k5int, KRB5INT_ACCESS_VERSION);
142 com_err(whoami, retval,
143 "while accessing krb5 library internal support");
146 encode_v4tkt = k5int.krb524_encode_v4tkt;
147 if (encode_v4tkt == NULL) {
149 "krb4 support disabled in krb5 support library");
155 use_master = use_keytab = nofork = 0;
156 config_params.mask = 0;
159 if (strncmp(*argv, "-X", 2) == 0) {
160 allow_v4_crossrealm = 1;
162 else if (strncmp(*argv, "-k", 2) == 0)
164 else if (strncmp(*argv, "-m", 2) == 0)
166 else if (strcmp(*argv, "-nofork") == 0)
168 else if (strcmp(*argv, "-r") == 0) {
170 if (argc == 0 || !use_master)
172 config_params.mask |= KADM5_CONFIG_REALM;
173 config_params.realm = *argv;
175 else if (strcmp(*argv, "-p") == 0) {
182 "port number may only be specified once");
185 port = strtoul (*argv, &endptr, 0);
186 if (*endptr != '\0' || port > 65535 || port == 0) {
188 "invalid port number %s, must be 1..65535\n",
197 if (argc || use_keytab + use_master > 1 ||
198 use_keytab + use_master == 0) {
199 use_keytab = use_master = 0;
203 signal(SIGINT, request_exit);
204 signal(SIGHUP, SIG_IGN);
205 signal(SIGTERM, request_exit);
207 krb5_klog_init(context, "krb524d", whoami, !nofork);
210 init_keytab(context);
212 init_master(context, &config_params);
214 memset((char *) &saddr, 0, sizeof(struct sockaddr_in));
215 saddr.sin_family = AF_INET;
216 saddr.sin_addr.s_addr = INADDR_ANY;
218 serv = getservbyname(KRB524_SERVICE, "udp");
220 com_err(whoami, 0, "service entry `%s' not found, using %d",
221 KRB524_SERVICE, KRB524_PORT);
222 saddr.sin_port = htons(KRB524_PORT);
224 saddr.sin_port = serv->s_port;
226 saddr.sin_port = htons(port);
228 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
229 com_err(whoami, errno, "creating main socket");
230 cleanup_and_exit(1, context);
232 if ((ret = bind(s, (struct sockaddr *) &saddr,
233 sizeof(struct sockaddr_in))) < 0) {
234 com_err(whoami, errno, "binding main socket");
235 cleanup_and_exit(1, context);
237 if (!nofork && daemon(0, 0)) {
238 com_err(whoami, errno, "while detaching from tty");
239 cleanup_and_exit(1, context);
245 timeout.tv_sec = TIMEOUT;
248 ret = select(s+1, &rfds, NULL, NULL, &timeout);
250 cleanup_and_exit(0, context);
253 ret = kadm5_flush(handle);
254 if (ret && ret != KRB5_KDB_DBNOTINITED) {
255 com_err(whoami, ret, "closing kerberos database");
256 cleanup_and_exit(1, context);
259 } else if (ret < 0 && errno != EINTR) {
260 com_err(whoami, errno, "in select");
261 cleanup_and_exit(1, context);
262 } else if (FD_ISSET(s, &rfds)) {
264 printf("received packet\n");
265 if ((ret = do_connection(s, context))) {
266 com_err(whoami, ret, "handling packet");
269 com_err(whoami, 0, "impossible situation occurred!");
272 cleanup_and_exit(0, context);
275 void cleanup_and_exit(ret, context)
277 krb5_context context;
279 if (use_master && handle) {
280 (void) kadm5_destroy(handle);
282 if (use_keytab && kt) krb5_kt_close(context, kt);
283 krb5_klog_close(context);
284 krb5_free_context(context);
288 void init_keytab(context)
289 krb5_context context;
293 if (keytab == NULL) {
294 if ((ret = krb5_kt_default(context, &kt))) {
295 com_err(whoami, ret, "while opening default keytab");
296 cleanup_and_exit(1, context);
299 if ((ret = krb5_kt_resolve(context, keytab, &kt))) {
300 com_err(whoami, ret, "while resolving keytab %s",
302 cleanup_and_exit(1, context);
305 use_keytab = 1; /* now safe to close keytab */
308 void init_master(context, params)
309 krb5_context context;
310 kadm5_config_params *params;
315 if ((ret = kadm5_init(whoami, NULL, KADM5_ADMIN_SERVICE, params,
316 KADM5_STRUCT_VERSION, KADM5_API_VERSION_2, NULL,
318 com_err(whoami, ret, "initializing kadm5 library");
319 cleanup_and_exit(1, context);
321 use_master = 1; /* now safe to close kadm5 */
324 krb5_error_code do_connection(s, context)
326 krb5_context context;
328 struct sockaddr saddr;
329 krb5_ticket *v5tkt = 0;
330 krb5_data msgdata, tktdata;
331 char msgbuf[MSGSIZE], tktbuf[TKT_BUFSIZ], *p;
334 krb5_int32 n; /* Must be 4 bytes */
337 msgdata.data = msgbuf;
338 msgdata.length = MSGSIZE;
339 tktdata.data = tktbuf;
340 tktdata.length = TKT_BUFSIZ;
341 saddrlen = sizeof(struct sockaddr);
342 ret = recvfrom(s, msgdata.data, (int) msgdata.length, 0, &saddr, &saddrlen);
344 /* if recvfrom fails, we probably don't have a valid saddr to
345 use for the reply, so don't even try to respond. */
349 printf("message received\n");
351 if ((ret = decode_krb5_ticket(&msgdata, &v5tkt))) {
353 case KRB5KDC_ERR_BAD_PVNO:
354 case ASN1_MISPLACED_FIELD:
355 case ASN1_MISSING_FIELD:
357 case KRB5_BADMSGTYPE:
358 /* don't even answer parse errors */
362 /* try and recognize our own error packet */
363 if (msgdata.length == sizeof(krb5_int32))
364 return KRB5_BADMSGTYPE;
370 printf("V5 ticket decoded\n");
372 if( krb5_princ_size(context, v5tkt->server) >= 1
373 &&krb5_princ_component(context, v5tkt->server, 0)->length == 3
374 &&strncmp(krb5_princ_component(context, v5tkt->server, 0)->data,
378 if ((ret = afs_return_v4(context, v5tkt->server,
381 if ((ret = encode_krb5_enc_data( &v5tkt->enc_part, &enc_part)) != 0)
383 if (!(use_v5 )|| enc_part->length >= 344) {
384 krb5_free_data(context, enc_part);
385 if ((ret = handle_classic_v4(context, v5tkt,
386 (struct sockaddr_in *) &saddr, &tktdata,
392 fake_v4tkt.length = enc_part->length;
393 memcpy(fake_v4tkt.dat, enc_part->data, enc_part->length);
394 v4kvno = (0x100-0x2b); /*protocol constant indicating v5
396 krb5_free_data(context, enc_part);
397 ret = encode_v4tkt(&fake_v4tkt, tktdata.data, &tktdata.length);
400 if ((ret = handle_classic_v4(context, v5tkt,
401 (struct sockaddr_in *) &saddr, &tktdata,
407 /* create the reply */
412 memcpy(p, (char *) &n, sizeof(krb5_int32));
413 p += sizeof(krb5_int32);
414 msgdata.length += sizeof(krb5_int32);
420 memcpy(p, (char *) &n, sizeof(krb5_int32));
421 p += sizeof(krb5_int32);
422 msgdata.length += sizeof(krb5_int32);
424 memcpy(p, tktdata.data, tktdata.length);
426 msgdata.length += tktdata.length;
430 (void) sendto(s, msgdata.data, (int) msgdata.length, 0, &saddr, saddrlen);
432 if (sendto(s, msgdata.data, msgdata.length, 0, &saddr, saddrlen)<0)
435 printf("reply written\n");
437 krb5_free_ticket(context, v5tkt);
443 krb5_error_code lookup_service_key(context, p, ktype, kvno, key, kvnop)
444 krb5_context context;
452 krb5_keytab_entry entry;
455 if ((ret = krb5_kt_get_entry(context, kt, p, kvno, ktype, &entry)))
458 key->contents = malloc(key->length);
460 memcpy(key->contents, entry.key.contents, key->length);
461 else if (key->length) {
464 memset (key, 0, sizeof (*key));
468 krb5_kt_free_entry(context, &entry);
470 } else if (use_master) {
471 return kdc_get_server_key(context, p, key, kvnop, ktype, kvno);
476 krb5_error_code kdc_get_server_key(context, service, key, kvnop, ktype, kvno)
477 krb5_context context;
478 krb5_principal service;
485 kadm5_principal_ent_rec server;
487 if ((ret = kadm5_get_principal(handle, service, &server,
488 KADM5_KEY_DATA|KADM5_ATTRIBUTES)))
491 if (server.attributes & KRB5_KDB_DISALLOW_ALL_TIX
492 || server.attributes & KRB5_KDB_DISALLOW_SVR) {
493 kadm5_free_principal_ent(handle, &server);
494 return KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
498 * We try kadm5_decrypt_key twice because in the case of a
499 * ENCTYPE_DES_CBC_CRC key, we prefer to find a krb4 salt type
500 * over a normal key. Note this may create a problem if the
501 * server key is passworded and has both a normal and v4 salt.
502 * There is no good solution to this.
504 if ((ret = kadm5_decrypt_key(handle,
507 (ktype == ENCTYPE_DES_CBC_CRC) ?
508 KRB5_KDB_SALTTYPE_V4 : -1,
510 key, NULL, kvnop)) &&
511 (ret = kadm5_decrypt_key(handle,
516 key, NULL, kvnop))) {
517 kadm5_free_principal_ent(handle, &server);
518 return (KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN);
521 kadm5_free_principal_ent(handle, &server);
526 * We support two kinds of v4 credentials. There are real v4
527 * credentials, and a Kerberos v5 enc part masquerading as a krb4
528 * credential to be used by modern AFS implementations; this function
529 * handles the classic v4 case.
532 static krb5_error_code
533 handle_classic_v4 (krb5_context context, krb5_ticket *v5tkt,
534 struct sockaddr_in *saddr,
535 krb5_data *tktdata, krb5_kvno *v4kvno)
538 krb5_keyblock v5_service_key, v4_service_key;
541 v5_service_key.contents = NULL;
542 v4_service_key.contents = NULL;
544 if ((ret = lookup_service_key(context, v5tkt->server,
545 v5tkt->enc_part.enctype,
546 v5tkt->enc_part.kvno,
547 &v5_service_key, NULL)))
550 if ( (ret = lookup_service_key(context, v5tkt->server,
553 &v4_service_key, v4kvno)))
557 printf("service key retrieved\n");
558 if ((ret = krb5_decrypt_tkt_part(context, &v5_service_key, v5tkt))) {
562 if (!(allow_v4_crossrealm || krb5_realm_compare(context, v5tkt->server,
563 v5tkt->enc_part2->client))) {
564 ret = KRB5KDC_ERR_POLICY ;
567 krb5_free_enc_tkt_part(context, v5tkt->enc_part2);
568 v5tkt->enc_part2= NULL;
570 ret = krb524_convert_tkt_skey(context, v5tkt, &v4tkt, &v5_service_key,
572 (struct sockaddr_in *)saddr);
577 printf("credentials converted\n");
579 ret = encode_v4tkt(&v4tkt, tktdata->data, &tktdata->length);
583 printf("v4 credentials encoded\n");
586 if (v5tkt->enc_part2) {
587 krb5_free_enc_tkt_part(context, v5tkt->enc_part2);
588 v5tkt->enc_part2 = NULL;
591 if(v5_service_key.contents)
592 krb5_free_keyblock_contents(context, &v5_service_key);
593 if (v4_service_key.contents)
594 krb5_free_keyblock_contents(context, &v4_service_key);
599 * afs_return_v4: a predicate to determine whether we want to try
600 * using the afs krb5 encrypted part encoding or whether we just
601 * return krb4. Takes a principal, and checks the configuration file.
603 static krb5_error_code
604 afs_return_v4 (krb5_context context, const krb5_principal princ,
611 assert(use_v5 != NULL);
612 ret = krb5_unparse_name(context, princ, &unparsed_name);
615 /* Trim out trailing realm component into separate string.*/
616 for (cp = unparsed_name; *cp != '\0'; cp++) {
618 cp++; /* We trust unparse_name not to leave a singleton
625 realm.length = strlen((char *) realm.data);
629 krb5_appdefault_boolean(context, "afs_krb5",
630 &realm, unparsed_name, 1,
632 krb5_free_unparsed_name(context, unparsed_name);