xref: /freebsd/contrib/telnet/libtelnet/kerberos5.c (revision 1e413cf93298b5b97441a21d9a50fdcd0ee9945e)
1 /*-
2  * Copyright (c) 1991, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 /*
35  * Copyright (C) 1990 by the Massachusetts Institute of Technology
36  *
37  * Export of this software from the United States of America may
38  * require a specific license from the United States Government.
39  * It is the responsibility of any person or organization contemplating
40  * export to obtain such a license before exporting.
41  *
42  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
43  * distribute this software and its documentation for any purpose and
44  * without fee is hereby granted, provided that the above copyright
45  * notice appear in all copies and that both that copyright notice and
46  * this permission notice appear in supporting documentation, and that
47  * the name of M.I.T. not be used in advertising or publicity pertaining
48  * to distribution of the software without specific, written prior
49  * permission.  M.I.T. makes no representations about the suitability of
50  * this software for any purpose.  It is provided "as is" without express
51  * or implied warranty.
52  */
53 
54 #include <sys/cdefs.h>
55 
56 __FBSDID("$FreeBSD$");
57 
58 #ifdef	KRB5
59 
60 #include <arpa/telnet.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <unistd.h>
65 #include <netdb.h>
66 #include <ctype.h>
67 #include <pwd.h>
68 #define Authenticator k5_Authenticator
69 #include <krb5.h>
70 #undef Authenticator
71 
72 #include "encrypt.h"
73 #include "auth.h"
74 #include "misc.h"
75 
76 int forward_flags = 0;  /* Flags get set in telnet/main.c on -f and -F */
77 
78 /* These values need to be the same as those defined in telnet/main.c. */
79 /* Either define them in both places, or put in some common header file. */
80 #define OPTS_FORWARD_CREDS	0x00000002
81 #define OPTS_FORWARDABLE_CREDS	0x00000001
82 
83 void kerberos5_forward (Authenticator *);
84 
85 static unsigned char str_data[1024] = { IAC, SB, TELOPT_AUTHENTICATION, 0,
86 			  		AUTHTYPE_KERBEROS_V5, };
87 
88 #define	KRB_AUTH		0	/* Authentication data follows */
89 #define	KRB_REJECT		1	/* Rejected (reason might follow) */
90 #define	KRB_ACCEPT		2	/* Accepted */
91 #define	KRB_RESPONSE		3	/* Response for mutual auth. */
92 
93 #define KRB_FORWARD     	4       /* Forwarded credentials follow */
94 #define KRB_FORWARD_ACCEPT     	5       /* Forwarded credentials accepted */
95 #define KRB_FORWARD_REJECT     	6       /* Forwarded credentials rejected */
96 
97 static	krb5_data auth;
98 static  krb5_ticket *ticket;
99 
100 static krb5_context context;
101 static krb5_auth_context auth_context;
102 
103 static int
104 Data(Authenticator *ap, int type, const char *d, int c)
105 {
106     unsigned char *p = str_data + 4;
107     const unsigned char *cd = d;
108 
109     if (c == -1)
110 	c = strlen(cd);
111 
112     if (auth_debug_mode) {
113 	printf("%s:%d: [%d] (%d)",
114 	       str_data[3] == TELQUAL_IS ? ">>>IS" : ">>>REPLY",
115 	       str_data[3],
116 	       type, c);
117 	printd(d, c);
118 	printf("\r\n");
119     }
120     *p++ = ap->type;
121     *p++ = ap->way;
122     *p++ = type;
123     while (c-- > 0) {
124 	if ((*p++ = *cd++) == IAC)
125 	    *p++ = IAC;
126     }
127     *p++ = IAC;
128     *p++ = SE;
129     if (str_data[3] == TELQUAL_IS)
130 	printsub('>', &str_data[2], p - &str_data[2]);
131     return(net_write(str_data, p - str_data));
132 }
133 
134 int
135 kerberos5_init(Authenticator *ap __unused, int server)
136 {
137     krb5_error_code ret;
138 
139     ret = krb5_init_context(&context);
140     if (ret)
141 	return 0;
142     if (server) {
143 	krb5_keytab kt;
144 	krb5_kt_cursor cursor;
145 
146 	ret = krb5_kt_default(context, &kt);
147 	if (ret)
148 	    return 0;
149 
150 	ret = krb5_kt_start_seq_get (context, kt, &cursor);
151 	if (ret) {
152 	    krb5_kt_close (context, kt);
153 	    return 0;
154 	}
155 	krb5_kt_end_seq_get (context, kt, &cursor);
156 	krb5_kt_close (context, kt);
157 
158 	str_data[3] = TELQUAL_REPLY;
159     } else
160 	str_data[3] = TELQUAL_IS;
161     return(1);
162 }
163 
164 extern int net;
165 
166 static int
167 kerberos5_send(const char *name, Authenticator *ap)
168 {
169     krb5_error_code ret;
170     krb5_ccache ccache;
171     int ap_opts;
172     krb5_data cksum_data;
173     char foo[2];
174 
175     if (!UserNameRequested) {
176 	if (auth_debug_mode) {
177 	    printf("Kerberos V5: no user name supplied\r\n");
178 	}
179 	return(0);
180     }
181 
182     ret = krb5_cc_default(context, &ccache);
183     if (ret) {
184 	if (auth_debug_mode) {
185 	    printf("Kerberos V5: could not get default ccache: %s\r\n",
186 		   krb5_get_err_text (context, ret));
187 	}
188 	return 0;
189     }
190 
191     if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL)
192 	ap_opts = AP_OPTS_MUTUAL_REQUIRED;
193     else
194 	ap_opts = 0;
195     ap_opts |= AP_OPTS_USE_SUBKEY;
196 
197     ret = krb5_auth_con_init (context, &auth_context);
198     if (ret) {
199 	if (auth_debug_mode) {
200 	    printf("Kerberos V5: krb5_auth_con_init failed (%s)\r\n",
201 		   krb5_get_err_text(context, ret));
202 	}
203 	return(0);
204     }
205 
206     ret = krb5_auth_con_setaddrs_from_fd (context,
207 					  auth_context,
208 					  &net);
209     if (ret) {
210 	if (auth_debug_mode) {
211 	    printf ("Kerberos V5:"
212 		    " krb5_auth_con_setaddrs_from_fd failed (%s)\r\n",
213 		    krb5_get_err_text(context, ret));
214 	}
215 	return(0);
216     }
217 
218     krb5_auth_con_setkeytype (context, auth_context, KEYTYPE_DES);
219 
220     foo[0] = ap->type;
221     foo[1] = ap->way;
222 
223     cksum_data.length = sizeof(foo);
224     cksum_data.data   = foo;
225 
226 
227     {
228 	krb5_principal service;
229 	char sname[128];
230 
231 
232 	ret = krb5_sname_to_principal (context,
233 				       RemoteHostName,
234 				       NULL,
235 				       KRB5_NT_SRV_HST,
236 				       &service);
237 	if(ret) {
238 	    if (auth_debug_mode) {
239 		printf ("Kerberos V5:"
240 			" krb5_sname_to_principal(%s) failed (%s)\r\n",
241 			RemoteHostName, krb5_get_err_text(context, ret));
242 	    }
243 	    return 0;
244 	}
245 	ret = krb5_unparse_name_fixed(context, service, sname, sizeof(sname));
246 	if(ret) {
247 	    if (auth_debug_mode) {
248 		printf ("Kerberos V5:"
249 			" krb5_unparse_name_fixed failed (%s)\r\n",
250 			krb5_get_err_text(context, ret));
251 	    }
252 	    return 0;
253 	}
254 	printf("[ Trying %s (%s)... ]\r\n", name, sname);
255 	ret = krb5_mk_req_exact(context, &auth_context, ap_opts,
256 				service,
257 				&cksum_data, ccache, &auth);
258 	krb5_free_principal (context, service);
259 
260     }
261     if (ret) {
262 	if (1 || auth_debug_mode) {
263 	    printf("Kerberos V5: mk_req failed (%s)\r\n",
264 		   krb5_get_err_text(context, ret));
265 	}
266 	return(0);
267     }
268 
269     if (!auth_sendname((unsigned char *)UserNameRequested,
270 		       strlen(UserNameRequested))) {
271 	if (auth_debug_mode)
272 	    printf("Not enough room for user name\r\n");
273 	return(0);
274     }
275     if (!Data(ap, KRB_AUTH, auth.data, auth.length)) {
276 	if (auth_debug_mode)
277 	    printf("Not enough room for authentication data\r\n");
278 	return(0);
279     }
280     if (auth_debug_mode) {
281 	printf("Sent Kerberos V5 credentials to server\r\n");
282     }
283     return(1);
284 }
285 
286 int
287 kerberos5_send_mutual(Authenticator *ap)
288 {
289     return kerberos5_send("mutual KERBEROS5", ap);
290 }
291 
292 int
293 kerberos5_send_oneway(Authenticator *ap)
294 {
295     return kerberos5_send("KERBEROS5", ap);
296 }
297 
298 void
299 kerberos5_is(Authenticator *ap, unsigned char *data, int cnt)
300 {
301     krb5_error_code ret;
302     krb5_data outbuf;
303     krb5_keyblock *key_block;
304     char *name;
305     krb5_principal server;
306     int zero = 0;
307 
308     if (cnt-- < 1)
309 	return;
310     switch (*data++) {
311     case KRB_AUTH:
312 	auth.data = (char *)data;
313 	auth.length = cnt;
314 
315 	auth_context = NULL;
316 
317 	ret = krb5_auth_con_init (context, &auth_context);
318 	if (ret) {
319 	    Data(ap, KRB_REJECT, "krb5_auth_con_init failed", -1);
320 	    auth_finished(ap, AUTH_REJECT);
321 	    if (auth_debug_mode)
322 		printf("Kerberos V5: krb5_auth_con_init failed (%s)\r\n",
323 		       krb5_get_err_text(context, ret));
324 	    return;
325 	}
326 
327 	ret = krb5_auth_con_setaddrs_from_fd (context,
328 					      auth_context,
329 					      &zero);
330 	if (ret) {
331 	    Data(ap, KRB_REJECT, "krb5_auth_con_setaddrs_from_fd failed", -1);
332 	    auth_finished(ap, AUTH_REJECT);
333 	    if (auth_debug_mode)
334 		printf("Kerberos V5: "
335 		       "krb5_auth_con_setaddrs_from_fd failed (%s)\r\n",
336 		       krb5_get_err_text(context, ret));
337 	    return;
338 	}
339 
340 	ret = krb5_sock_to_principal (context,
341 				      0,
342 				      "host",
343 				      KRB5_NT_SRV_HST,
344 				      &server);
345 	if (ret) {
346 	    Data(ap, KRB_REJECT, "krb5_sock_to_principal failed", -1);
347 	    auth_finished(ap, AUTH_REJECT);
348 	    if (auth_debug_mode)
349 		printf("Kerberos V5: "
350 		       "krb5_sock_to_principal failed (%s)\r\n",
351 		       krb5_get_err_text(context, ret));
352 	    return;
353 	}
354 
355 	ret = krb5_rd_req(context,
356 			  &auth_context,
357 			  &auth,
358 			  server,
359 			  NULL,
360 			  NULL,
361 			  &ticket);
362 
363 	krb5_free_principal (context, server);
364 	if (ret) {
365 	    char *errbuf;
366 
367 	    asprintf(&errbuf,
368 		     "Read req failed: %s",
369 		     krb5_get_err_text(context, ret));
370 	    Data(ap, KRB_REJECT, errbuf, -1);
371 	    if (auth_debug_mode)
372 		printf("%s\r\n", errbuf);
373 	    free (errbuf);
374 	    return;
375 	}
376 
377 	{
378 	    char foo[2];
379 
380 	    foo[0] = ap->type;
381 	    foo[1] = ap->way;
382 
383 	    ret = krb5_verify_authenticator_checksum(context,
384 						     auth_context,
385 						     foo,
386 						     sizeof(foo));
387 
388 	    if (ret) {
389 		char *errbuf;
390 		asprintf(&errbuf, "Bad checksum: %s",
391 			 krb5_get_err_text(context, ret));
392 		Data(ap, KRB_REJECT, errbuf, -1);
393 		if (auth_debug_mode)
394 		    printf ("%s\r\n", errbuf);
395 		free(errbuf);
396 		return;
397 	    }
398 	}
399 	ret = krb5_auth_con_getremotesubkey (context,
400 					     auth_context,
401 					     &key_block);
402 
403 	if (ret) {
404 	    Data(ap, KRB_REJECT, "krb5_auth_con_getremotesubkey failed", -1);
405 	    auth_finished(ap, AUTH_REJECT);
406 	    if (auth_debug_mode)
407 		printf("Kerberos V5: "
408 		       "krb5_auth_con_getremotesubkey failed (%s)\r\n",
409 		       krb5_get_err_text(context, ret));
410 	    return;
411 	}
412 
413 	if (key_block == NULL) {
414 	    ret = krb5_auth_con_getkey(context,
415 				       auth_context,
416 				       &key_block);
417 	}
418 	if (ret) {
419 	    Data(ap, KRB_REJECT, "krb5_auth_con_getkey failed", -1);
420 	    auth_finished(ap, AUTH_REJECT);
421 	    if (auth_debug_mode)
422 		printf("Kerberos V5: "
423 		       "krb5_auth_con_getkey failed (%s)\r\n",
424 		       krb5_get_err_text(context, ret));
425 	    return;
426 	}
427 	if (key_block == NULL) {
428 	    Data(ap, KRB_REJECT, "no subkey received", -1);
429 	    auth_finished(ap, AUTH_REJECT);
430 	    if (auth_debug_mode)
431 		printf("Kerberos V5: "
432 		       "krb5_auth_con_getremotesubkey returned NULL key\r\n");
433 	    return;
434 	}
435 
436 	if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
437 	    ret = krb5_mk_rep(context, auth_context, &outbuf);
438 	    if (ret) {
439 		Data(ap, KRB_REJECT,
440 		     "krb5_mk_rep failed", -1);
441 		auth_finished(ap, AUTH_REJECT);
442 		if (auth_debug_mode)
443 		    printf("Kerberos V5: "
444 			   "krb5_mk_rep failed (%s)\r\n",
445 			   krb5_get_err_text(context, ret));
446 		return;
447 	    }
448 	    Data(ap, KRB_RESPONSE, outbuf.data, outbuf.length);
449 	}
450 	if (krb5_unparse_name(context, ticket->client, &name))
451 	    name = 0;
452 
453 	if(UserNameRequested && krb5_kuserok(context,
454 					     ticket->client,
455 					     UserNameRequested)) {
456 	    Data(ap, KRB_ACCEPT, name, name ? -1 : 0);
457 	    if (auth_debug_mode) {
458 		printf("Kerberos5 identifies him as ``%s''\r\n",
459 		       name ? name : "");
460 	    }
461 
462 	    if(key_block->keytype == ETYPE_DES_CBC_MD5 ||
463 	       key_block->keytype == ETYPE_DES_CBC_MD4 ||
464 	       key_block->keytype == ETYPE_DES_CBC_CRC) {
465 		Session_Key skey;
466 
467 		skey.type = SK_DES;
468 		skey.length = 8;
469 		skey.data = key_block->keyvalue.data;
470 		encrypt_session_key(&skey, 0);
471 	    }
472 
473 	} else {
474 	    char *msg;
475 
476 	    asprintf (&msg, "user `%s' is not authorized to "
477 		      "login as `%s'",
478 		      name ? name : "<unknown>",
479 		      UserNameRequested ? UserNameRequested : "<nobody>");
480 	    if (msg == NULL)
481 		Data(ap, KRB_REJECT, NULL, 0);
482 	    else {
483 		Data(ap, KRB_REJECT, (void *)msg, -1);
484 		free(msg);
485 	    }
486 	    auth_finished (ap, AUTH_REJECT);
487 	    krb5_free_keyblock_contents(context, key_block);
488 	    break;
489 	}
490 	auth_finished(ap, AUTH_USER);
491 	krb5_free_keyblock_contents(context, key_block);
492 
493 	break;
494     case KRB_FORWARD: {
495 	struct passwd *pwd;
496 	char ccname[1024];	/* XXX */
497 	krb5_data inbuf;
498 	krb5_ccache ccache;
499 	inbuf.data = (char *)data;
500 	inbuf.length = cnt;
501 
502 	pwd = getpwnam (UserNameRequested);
503 	if (pwd == NULL)
504 	    break;
505 
506 	snprintf (ccname, sizeof(ccname),
507 		  "FILE:/tmp/krb5cc_%u", pwd->pw_uid);
508 
509 	ret = krb5_cc_resolve (context, ccname, &ccache);
510 	if (ret) {
511 	    if (auth_debug_mode)
512 		printf ("Kerberos V5: could not get ccache: %s\r\n",
513 			krb5_get_err_text(context, ret));
514 	    break;
515 	}
516 
517 	ret = krb5_cc_initialize (context,
518 				  ccache,
519 				  ticket->client);
520 	if (ret) {
521 	    if (auth_debug_mode)
522 		printf ("Kerberos V5: could not init ccache: %s\r\n",
523 			krb5_get_err_text(context, ret));
524 	    break;
525 	}
526 
527 #if defined(DCE)
528 	esetenv("KRB5CCNAME", ccname, 1);
529 #endif
530 	ret = krb5_rd_cred2 (context,
531 			     auth_context,
532 			     ccache,
533 			     &inbuf);
534 	if(ret) {
535 	    char *errbuf;
536 
537 	    asprintf (&errbuf,
538 		      "Read forwarded creds failed: %s",
539 		      krb5_get_err_text (context, ret));
540 	    if(errbuf == NULL)
541 		Data(ap, KRB_FORWARD_REJECT, NULL, 0);
542 	    else
543 		Data(ap, KRB_FORWARD_REJECT, errbuf, -1);
544 	    if (auth_debug_mode)
545 		printf("Could not read forwarded credentials: %s\r\n",
546 		       errbuf);
547 	    free (errbuf);
548 	} else {
549 	    Data(ap, KRB_FORWARD_ACCEPT, 0, 0);
550 #if defined(DCE)
551 	    dfsfwd = 1;
552 #endif
553 	}
554 	chown (ccname + 5, pwd->pw_uid, -1);
555 	if (auth_debug_mode)
556 	    printf("Forwarded credentials obtained\r\n");
557 	break;
558     }
559     default:
560 	if (auth_debug_mode)
561 	    printf("Unknown Kerberos option %d\r\n", data[-1]);
562 	Data(ap, KRB_REJECT, 0, 0);
563 	break;
564     }
565 }
566 
567 void
568 kerberos5_reply(Authenticator *ap, unsigned char *data, int cnt)
569 {
570     static int mutual_complete = 0;
571 
572     if (cnt-- < 1)
573 	return;
574     switch (*data++) {
575     case KRB_REJECT:
576 	if (cnt > 0) {
577 	    printf("[ Kerberos V5 refuses authentication because %.*s ]\r\n",
578 		   cnt, data);
579 	} else
580 	    printf("[ Kerberos V5 refuses authentication ]\r\n");
581 	auth_send_retry();
582 	return;
583     case KRB_ACCEPT: {
584 	krb5_error_code ret;
585 	Session_Key skey;
586 	krb5_keyblock *keyblock;
587 
588 	if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL &&
589 	    !mutual_complete) {
590 	    printf("[ Kerberos V5 accepted you, but didn't provide mutual authentication! ]\r\n");
591 	    auth_send_retry();
592 	    return;
593 	}
594 	if (cnt)
595 	    printf("[ Kerberos V5 accepts you as ``%.*s'' ]\r\n", cnt, data);
596 	else
597 	    printf("[ Kerberos V5 accepts you ]\r\n");
598 
599 	ret = krb5_auth_con_getlocalsubkey (context,
600 					    auth_context,
601 					    &keyblock);
602 	if (ret)
603 	    ret = krb5_auth_con_getkey (context,
604 					auth_context,
605 					&keyblock);
606 	if(ret) {
607 	    printf("[ krb5_auth_con_getkey: %s ]\r\n",
608 		   krb5_get_err_text(context, ret));
609 	    auth_send_retry();
610 	    return;
611 	}
612 
613 	skey.type = SK_DES;
614 	skey.length = 8;
615 	skey.data = keyblock->keyvalue.data;
616 	encrypt_session_key(&skey, 0);
617 	krb5_free_keyblock_contents (context, keyblock);
618 	auth_finished(ap, AUTH_USER);
619 	if (forward_flags & OPTS_FORWARD_CREDS)
620 	    kerberos5_forward(ap);
621 	break;
622     }
623     case KRB_RESPONSE:
624 	if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
625 	    /* the rest of the reply should contain a krb_ap_rep */
626 	  krb5_ap_rep_enc_part *reply;
627 	  krb5_data inbuf;
628 	  krb5_error_code ret;
629 
630 	  inbuf.length = cnt;
631 	  inbuf.data = (char *)data;
632 
633 	  ret = krb5_rd_rep(context, auth_context, &inbuf, &reply);
634 	  if (ret) {
635 	      printf("[ Mutual authentication failed: %s ]\r\n",
636 		     krb5_get_err_text (context, ret));
637 	      auth_send_retry();
638 	      return;
639 	  }
640 	  krb5_free_ap_rep_enc_part(context, reply);
641 	  mutual_complete = 1;
642 	}
643 	return;
644     case KRB_FORWARD_ACCEPT:
645 	printf("[ Kerberos V5 accepted forwarded credentials ]\r\n");
646 	return;
647     case KRB_FORWARD_REJECT:
648 	printf("[ Kerberos V5 refuses forwarded credentials because %.*s ]\r\n",
649 	       cnt, data);
650 	return;
651     default:
652 	if (auth_debug_mode)
653 	    printf("Unknown Kerberos option %d\r\n", data[-1]);
654 	return;
655     }
656 }
657 
658 int
659 kerberos5_status(Authenticator *ap __unused, char *name, int level)
660 {
661     if (level < AUTH_USER)
662 	return(level);
663 
664     if (UserNameRequested &&
665 	krb5_kuserok(context,
666 		     ticket->client,
667 		     UserNameRequested))
668 	{
669 	    strcpy(name, UserNameRequested);
670 	    return(AUTH_VALID);
671 	} else
672 	    return(AUTH_USER);
673 }
674 
675 #define	BUMP(buf, len)		while (*(buf)) {++(buf), --(len);}
676 #define	ADDC(buf, len, c)	if ((len) > 0) {*(buf)++ = (c); --(len);}
677 
678 void
679 kerberos5_printsub(unsigned char *data, int cnt, unsigned char *buf, int buflen)
680 {
681     int i;
682 
683     buf[buflen-1] = '\0';		/* make sure its NULL terminated */
684     buflen -= 1;
685 
686     switch(data[3]) {
687     case KRB_REJECT:		/* Rejected (reason might follow) */
688 	strlcpy((char *)buf, " REJECT ", buflen);
689 	goto common;
690 
691     case KRB_ACCEPT:		/* Accepted (name might follow) */
692 	strlcpy((char *)buf, " ACCEPT ", buflen);
693     common:
694 	BUMP(buf, buflen);
695 	if (cnt <= 4)
696 	    break;
697 	ADDC(buf, buflen, '"');
698 	for (i = 4; i < cnt; i++)
699 	    ADDC(buf, buflen, data[i]);
700 	ADDC(buf, buflen, '"');
701 	ADDC(buf, buflen, '\0');
702 	break;
703 
704 
705     case KRB_AUTH:			/* Authentication data follows */
706 	strlcpy((char *)buf, " AUTH", buflen);
707 	goto common2;
708 
709     case KRB_RESPONSE:
710 	strlcpy((char *)buf, " RESPONSE", buflen);
711 	goto common2;
712 
713     case KRB_FORWARD:		/* Forwarded credentials follow */
714 	strlcpy((char *)buf, " FORWARD", buflen);
715 	goto common2;
716 
717     case KRB_FORWARD_ACCEPT:	/* Forwarded credentials accepted */
718 	strlcpy((char *)buf, " FORWARD_ACCEPT", buflen);
719 	goto common2;
720 
721     case KRB_FORWARD_REJECT:	/* Forwarded credentials rejected */
722 	/* (reason might follow) */
723 	strlcpy((char *)buf, " FORWARD_REJECT", buflen);
724 	goto common2;
725 
726     default:
727 	snprintf(buf, buflen, " %d (unknown)", data[3]);
728     common2:
729 	BUMP(buf, buflen);
730 	for (i = 4; i < cnt; i++) {
731 	    snprintf(buf, buflen, " %d", data[i]);
732 	    BUMP(buf, buflen);
733 	}
734 	break;
735     }
736 }
737 
738 void
739 kerberos5_forward(Authenticator *ap)
740 {
741     krb5_error_code ret;
742     krb5_ccache     ccache;
743     krb5_creds      creds;
744     krb5_kdc_flags  flags;
745     krb5_data       out_data;
746     krb5_principal  principal;
747 
748     ret = krb5_cc_default (context, &ccache);
749     if (ret) {
750 	if (auth_debug_mode)
751 	    printf ("KerberosV5: could not get default ccache: %s\r\n",
752 		    krb5_get_err_text (context, ret));
753 	return;
754     }
755 
756     ret = krb5_cc_get_principal (context, ccache, &principal);
757     if (ret) {
758 	if (auth_debug_mode)
759 	    printf ("KerberosV5: could not get principal: %s\r\n",
760 		    krb5_get_err_text (context, ret));
761 	return;
762     }
763 
764     memset (&creds, 0, sizeof(creds));
765 
766     creds.client = principal;
767 
768     ret = krb5_build_principal (context,
769 				&creds.server,
770 				strlen(principal->realm),
771 				principal->realm,
772 				"krbtgt",
773 				principal->realm,
774 				NULL);
775 
776     if (ret) {
777 	if (auth_debug_mode)
778 	    printf ("KerberosV5: could not get principal: %s\r\n",
779 		    krb5_get_err_text (context, ret));
780 	return;
781     }
782 
783     creds.times.endtime = 0;
784 
785     flags.i = 0;
786     flags.b.forwarded = 1;
787     if (forward_flags & OPTS_FORWARDABLE_CREDS)
788 	flags.b.forwardable = 1;
789 
790     ret = krb5_get_forwarded_creds (context,
791 				    auth_context,
792 				    ccache,
793 				    flags.i,
794 				    RemoteHostName,
795 				    &creds,
796 				    &out_data);
797     if (ret) {
798 	if (auth_debug_mode)
799 	    printf ("Kerberos V5: error getting forwarded creds: %s\r\n",
800 		    krb5_get_err_text (context, ret));
801 	return;
802     }
803 
804     if(!Data(ap, KRB_FORWARD, out_data.data, out_data.length)) {
805 	if (auth_debug_mode)
806 	    printf("Not enough room for authentication data\r\n");
807     } else {
808 	if (auth_debug_mode)
809 	    printf("Forwarded local Kerberos V5 credentials to server\r\n");
810     }
811 }
812 
813 #if defined(DCE)
814 /* if this was a K5 authentication try and join a PAG for the user. */
815 void
816 kerberos5_dfspag(void)
817 {
818     if (dfsk5ok) {
819 	dfspag = krb5_dfs_pag(context, dfsfwd, ticket->client,
820 			      UserNameRequested);
821     }
822 }
823 #endif
824 
825 #endif /* KRB5 */
826