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