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