xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.bin/telnet/kerberos5.c (revision 7ae7577ce925f555e5e3410f14a3ccc896c842f2)
1 /*
2  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * usr/src/cmd/cmd-inet/usr.bin/telnet/kerberos5.c
8  *
9  * Copyright (c) 1991, 1993
10  *	The Regents of the University of California.  All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *	notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *	notice, this list of conditions and the following disclaimer in the
19  *	documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *	must display the following acknowledgement:
22  *	This product includes software developed by the University of
23  *	California, Berkeley and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *	may be used to endorse or promote products derived from this software
26  *	without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  */
40 
41 /* based on @(#)kerberos5.c	8.1 (Berkeley) 6/4/93 */
42 
43 /*
44  * Copyright (C) 1990 by the Massachusetts Institute of Technology
45  *
46  * Export of this software from the United States of America may
47  * require a specific license from the United States Government.
48  * It is the responsibility of any person or organization contemplating
49  * export to obtain such a license before exporting.
50  *
51  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
52  * distribute this software and its documentation for any purpose and
53  * without fee is hereby granted, provided that the above copyright
54  * notice appear in all copies and that both that copyright notice and
55  * this permission notice appear in supporting documentation, and that
56  * the name of M.I.T. not be used in advertising or publicity pertaining
57  * to distribution of the software without specific, written prior
58  * permission.  Furthermore if you modify this software you must label
59  * your software as modified software and not distribute it in such a
60  * fashion that it might be confused with the original M.I.T. software.
61  * M.I.T. makes no representations about the suitability of
62  * this software for any purpose.  It is provided "as is" without express
63  * or implied warranty.
64  */
65 
66 
67 #include <arpa/telnet.h>
68 #include <stdio.h>
69 #include <ctype.h>
70 #include <syslog.h>
71 #include <stdlib.h>
72 
73 /* the following are from the kerberos tree */
74 #include <k5-int.h>
75 #include <com_err.h>
76 #include <netdb.h>
77 #include <profile/prof_int.h>
78 #include <sys/param.h>
79 #include "externs.h"
80 
81 extern	char *RemoteHostName;
82 extern	boolean_t auth_debug_mode;
83 extern	int net;
84 
85 #define	ACCEPTED_ENCTYPE(a) \
86 	(a == ENCTYPE_DES_CBC_CRC || a == ENCTYPE_DES_CBC_MD5)
87 /* for comapatibility with non-Solaris KDC's, this has to be big enough */
88 #define	KERBEROS_BUFSIZ	8192
89 
90 int	forward_flags = 0;  /* Flags get set in telnet/main.c on -f and -F */
91 static	void kerberos5_forward(Authenticator *);
92 
93 static	unsigned char str_data[KERBEROS_BUFSIZ] = { IAC, SB,
94 		TELOPT_AUTHENTICATION, 0, AUTHTYPE_KERBEROS_V5, };
95 static	char *appdef[] = { "appdefaults", "telnet", NULL };
96 static	char *realmdef[] = { "realms", NULL, "telnet", NULL };
97 
98 static	krb5_auth_context auth_context = 0;
99 
100 static	krb5_data auth;	/* telnetd gets session key from here */
101 static	krb5_ticket *ticket = NULL;
102 	/* telnet matches the AP_REQ and AP_REP with this */
103 
104 static	krb5_keyblock	*session_key = 0;
105 char	*telnet_krb5_realm = NULL;
106 
107 /*
108  * Change the kerberos realm
109  */
110 void
111 set_krb5_realm(char *name)
112 {
113 	if (name == NULL) {
114 		(void) fprintf(stderr, gettext("Could not set Kerberos realm, "
115 			"no realm provided.\n"));
116 		return;
117 	}
118 
119 	if (telnet_krb5_realm)
120 		free(telnet_krb5_realm);
121 
122 	telnet_krb5_realm = (char *)strdup(name);
123 
124 	if (telnet_krb5_realm == NULL)
125 		(void) fprintf(stderr, gettext(
126 			"Could not set Kerberos realm, malloc failed\n"));
127 }
128 
129 #define	RETURN_NOMEM	{ errno = ENOMEM; return (-1); }
130 
131 static int
132 krb5_send_data(Authenticator *ap, int type, krb5_pointer d, int c)
133 {
134 	/* the first 3 bytes are control chars */
135 	unsigned char *p = str_data + 4;
136 	unsigned char *cd = (unsigned char *)d;
137 	/* spaceleft is incremented whenever p is decremented */
138 	size_t spaceleft = sizeof (str_data) - 4;
139 
140 	if (c == -1)
141 		c = strlen((char *)cd);
142 
143 	if (auth_debug_mode) {
144 		(void) printf("%s:%d: [%d] (%d)",
145 			str_data[3] == TELQUAL_IS ? ">>>IS" : ">>>REPLY",
146 			str_data[3], type, c);
147 		printd(d, c);
148 		(void) printf("\r\n");
149 	}
150 
151 	if (spaceleft < 3)
152 		RETURN_NOMEM;
153 	*p++ = ap->type;
154 	*p++ = ap->way;
155 	*p++ = type;
156 	spaceleft -= 3;
157 
158 	while (c-- > 0) {
159 		if (spaceleft < 2)
160 			RETURN_NOMEM;
161 		if ((*p++ = *cd++) == IAC) {
162 			*p++ = IAC;
163 			spaceleft -= 2;
164 		}
165 	}
166 
167 	if (spaceleft < 2)
168 		RETURN_NOMEM;
169 	*p++ = IAC;
170 	*p++ = SE;
171 	if (str_data[3] == TELQUAL_IS)
172 		printsub('>', &str_data[2], p - &str_data[2]);
173 	return (net_write(str_data, p - str_data));
174 }
175 
176 krb5_context telnet_context = 0;
177 
178 /* ARGSUSED */
179 int
180 kerberos5_init(Authenticator *ap)
181 {
182 	krb5_error_code retval;
183 
184 	str_data[3] = TELQUAL_IS;
185 	if (krb5auth_flag && (telnet_context == 0)) {
186 		retval = krb5_init_context(&telnet_context);
187 		if (retval)
188 			return (0);
189 	}
190 	return (1);
191 }
192 
193 int
194 kerberos5_send(Authenticator *ap)
195 {
196 	krb5_error_code retval;
197 	krb5_ccache ccache;
198 	krb5_creds creds;		/* telnet gets session key from here */
199 	krb5_creds *new_creds = 0;
200 	int ap_opts;
201 	char type_check[2];
202 	krb5_data check_data;
203 
204 	krb5_keyblock *newkey = 0;
205 
206 	int i;
207 	krb5_enctype *ktypes;
208 
209 	if (!UserNameRequested) {
210 		if (auth_debug_mode)
211 			(void) printf(gettext("telnet: Kerberos V5: "
212 				"no user name supplied\r\n"));
213 		return (0);
214 	}
215 
216 	if ((retval = krb5_cc_default(telnet_context, &ccache))) {
217 		if (auth_debug_mode)
218 		    (void) printf(gettext("telnet: Kerberos V5: "
219 			"could not get default ccache\r\n"));
220 		return (0);
221 	}
222 
223 	(void) memset((char *)&creds, 0, sizeof (creds));
224 	if (auth_debug_mode)
225 		printf("telnet: calling krb5_sname_to_principal\n");
226 	if ((retval = krb5_sname_to_principal(telnet_context, RemoteHostName,
227 		"host", KRB5_NT_SRV_HST, &creds.server))) {
228 		if (auth_debug_mode)
229 		    (void) printf(gettext("telnet: Kerberos V5: error "
230 			"while constructing service name: %s\r\n"),
231 			error_message(retval));
232 		return (0);
233 	}
234 	if (auth_debug_mode)
235 		printf("telnet: done calling krb5_sname_to_principal\n");
236 
237 	if (telnet_krb5_realm != NULL) {
238 		krb5_data rdata;
239 
240 		rdata.magic = 0;
241 		rdata.length = strlen(telnet_krb5_realm);
242 		rdata.data = (char *)malloc(rdata.length + 1);
243 		if (rdata.data == NULL) {
244 			(void) fprintf(stderr, gettext("malloc failed\n"));
245 			return (0);
246 		}
247 		(void) strcpy(rdata.data, telnet_krb5_realm);
248 		krb5_princ_set_realm(telnet_context, creds.server, &rdata);
249 		if (auth_debug_mode)
250 		    (void) printf(gettext(
251 			"telnet: Kerberos V5: set kerberos realm to %s\r\n"),
252 			telnet_krb5_realm);
253 	}
254 
255 	if ((retval = krb5_cc_get_principal(telnet_context, ccache,
256 		&creds.client)) != 0) {
257 		if (auth_debug_mode) {
258 			(void) printf(gettext(
259 			    "telnet: Kerberos V5: failure on principal "
260 			    "(%s)\r\n"), error_message(retval));
261 		}
262 		krb5_free_cred_contents(telnet_context, &creds);
263 		return (0);
264 	}
265 /*
266  * Check to to confirm that at least one of the supported
267  * encryption types (des-cbc-md5, des-cbc-crc is available. If
268  * one is available then use it to obtain credentials.
269  */
270 
271 	if ((retval = krb5_get_tgs_ktypes(telnet_context, creds.server,
272 		&ktypes))) {
273 		if (auth_debug_mode) {
274 			(void) printf(gettext(
275 			    "telnet: Kerberos V5: could not determine "
276 				"TGS encryption types "
277 				"(see default_tgs_enctypes in krb5.conf) "
278 			    "(%s)\r\n"), error_message(retval));
279 		}
280 		krb5_free_cred_contents(telnet_context, &creds);
281 		return (0);
282 	}
283 
284 	for (i = 0; ktypes[i]; i++) {
285 		if (ACCEPTED_ENCTYPE(ktypes[i]))
286 			break;
287 	}
288 
289 	if (ktypes[i] == 0) {
290 		if (auth_debug_mode) {
291 			(void) printf(gettext(
292 				"telnet: Kerberos V5: "
293 				"failure on encryption types. "
294 				"Cannot find des-cbc-md5 or des-cbc-crc "
295 				"in list of TGS encryption types "
296 				"(see default_tgs_enctypes in krb5.conf)\n"));
297 		}
298 		krb5_free_cred_contents(telnet_context, &creds);
299 		return (0);
300 	}
301 
302 	creds.keyblock.enctype = ktypes[i];
303 	if ((retval = krb5_get_credentials(telnet_context, 0,
304 		ccache, &creds, &new_creds))) {
305 		if (auth_debug_mode) {
306 			(void) printf(gettext(
307 			    "telnet: Kerberos V5: failure on credentials "
308 			    "(%s)\r\n"), error_message(retval));
309 		}
310 		krb5_free_cred_contents(telnet_context, &creds);
311 		return (0);
312 	}
313 
314 	ap_opts = ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
315 			AP_OPTS_MUTUAL_REQUIRED : 0;
316 
317 	ap_opts |= AP_OPTS_USE_SUBKEY;
318 
319 	if (auth_context) {
320 		krb5_auth_con_free(telnet_context, auth_context);
321 		auth_context = 0;
322 	}
323 	if ((retval = krb5_auth_con_init(telnet_context, &auth_context))) {
324 		if (auth_debug_mode) {
325 			(void) printf(gettext(
326 				"Kerberos V5: failed to init auth_context "
327 				"(%s)\r\n"), error_message(retval));
328 		}
329 		return (0);
330 	}
331 
332 	krb5_auth_con_setflags(telnet_context, auth_context,
333 		KRB5_AUTH_CONTEXT_RET_TIME);
334 
335 	type_check[0] = ap->type;
336 	type_check[1] = ap->way;
337 	check_data.magic = KV5M_DATA;
338 	check_data.length = 2;
339 	check_data.data = (char *)&type_check;
340 
341 	retval = krb5_mk_req_extended(telnet_context, &auth_context, ap_opts,
342 		&check_data, new_creds, &auth);
343 
344 	krb5_auth_con_getlocalsubkey(telnet_context, auth_context, &newkey);
345 	if (session_key) {
346 		krb5_free_keyblock(telnet_context, session_key);
347 		session_key = 0;
348 	}
349 
350 	if (newkey) {
351 		/*
352 		 * keep the key in our private storage, but don't use it
353 		 * yet---see kerberos5_reply() below
354 		 */
355 		if (!(ACCEPTED_ENCTYPE(newkey->enctype))) {
356 		    if (!(ACCEPTED_ENCTYPE(new_creds->keyblock.enctype)))
357 			/* use the session key in credentials instead */
358 			krb5_copy_keyblock(telnet_context,
359 				&new_creds->keyblock, &session_key);
360 		} else
361 			krb5_copy_keyblock(telnet_context,
362 				newkey, &session_key);
363 
364 		krb5_free_keyblock(telnet_context, newkey);
365 	}
366 
367 	krb5_free_cred_contents(telnet_context, &creds);
368 	krb5_free_creds(telnet_context, new_creds);
369 
370 	if (retval) {
371 		if (auth_debug_mode)
372 			(void) printf(gettext(
373 			    "telnet: Kerberos V5: mk_req failed (%s)\r\n"),
374 			    error_message(retval));
375 		return (0);
376 	}
377 
378 	if ((auth_sendname((uchar_t *)UserNameRequested,
379 		strlen(UserNameRequested))) == 0) {
380 		if (auth_debug_mode)
381 			(void) printf(gettext(
382 				"telnet: Not enough room for user name\r\n"));
383 		return (0);
384 	}
385 	retval = krb5_send_data(ap, KRB_AUTH, auth.data, auth.length);
386 	if (auth_debug_mode && retval) {
387 		(void) printf(gettext(
388 		    "telnet: Sent Kerberos V5 credentials to server\r\n"));
389 	} else if (auth_debug_mode) {
390 		(void) printf(gettext(
391 		    "telnet: Not enough room for authentication data\r\n"));
392 		return (0);
393 	}
394 	return (1);
395 }
396 
397 void
398 kerberos5_reply(Authenticator *ap, unsigned char *data, int cnt)
399 {
400 	Session_Key skey;
401 	static boolean_t mutual_complete = B_FALSE;
402 
403 	if (cnt-- < 1)
404 		return;
405 	switch (*data++) {
406 	case KRB_REJECT:
407 		if (cnt > 0)
408 		    (void) printf(gettext(
409 			"[ Kerberos V5 refuses authentication because "
410 			"%.*s ]\r\n"), cnt, data);
411 		else
412 		    (void) printf(gettext(
413 			"[ Kerberos V5 refuses authentication ]\r\n"));
414 		auth_send_retry();
415 		return;
416 	case KRB_ACCEPT:
417 		if (!mutual_complete) {
418 			if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
419 			    (void) printf(gettext(
420 				"[ Kerberos V5 accepted you, but didn't "
421 				"provide mutual authentication! ]\r\n"));
422 			    auth_send_retry();
423 			    return;
424 			}
425 
426 			if (session_key) {
427 			    skey.type = SK_DES;
428 			    skey.length = 8;
429 			    skey.data = session_key->contents;
430 			    encrypt_session_key(&skey);
431 			}
432 		}
433 		if (cnt)
434 			(void) printf(gettext(
435 			    "[ Kerberos V5 accepts you as ``%.*s'' ]\r\n"),
436 			    cnt, data);
437 		else
438 			(void) printf(gettext(
439 			    "[ Kerberos V5 accepts you ]\r\n"));
440 		auth_finished(ap, AUTH_USER);
441 
442 		if (forward_flags & OPTS_FORWARD_CREDS)
443 			kerberos5_forward(ap);
444 
445 		break;
446 	case KRB_RESPONSE:
447 		if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
448 			/* the rest of the reply should contain a krb_ap_rep */
449 			krb5_ap_rep_enc_part *reply;
450 			krb5_data inbuf;
451 			krb5_error_code retval;
452 
453 			inbuf.length = cnt;
454 			inbuf.data = (char *)data;
455 
456 			retval = krb5_rd_rep(telnet_context, auth_context,
457 				&inbuf, &reply);
458 			if (retval) {
459 				(void) printf(gettext(
460 					"[ Mutual authentication failed: "
461 					"%s ]\r\n"), error_message(retval));
462 				auth_send_retry();
463 				return;
464 			}
465 			krb5_free_ap_rep_enc_part(telnet_context, reply);
466 
467 			if (session_key) {
468 				skey.type = SK_DES;
469 				skey.length = 8;
470 				skey.data = session_key->contents;
471 				encrypt_session_key(&skey);
472 			}
473 			mutual_complete = B_TRUE;
474 		}
475 		return;
476 	case KRB_FORWARD_ACCEPT:
477 		(void) printf(gettext(
478 			"[ Kerberos V5 accepted forwarded credentials ]\r\n"));
479 		return;
480 	case KRB_FORWARD_REJECT:
481 		(void) printf(gettext(
482 			"[ Kerberos V5 refuses forwarded credentials because "
483 			"%.*s ]\r\n"), cnt, data);
484 		return;
485 	default:
486 		if (auth_debug_mode)
487 			(void) printf(gettext(
488 				"Unknown Kerberos option %d\r\n"), data[-1]);
489 		return;
490 	}
491 }
492 
493 /* ARGSUSED */
494 int
495 kerberos5_status(Authenticator *ap, char *name, int level)
496 {
497 	if (level < AUTH_USER)
498 		return (level);
499 
500 	if (UserNameRequested && krb5_kuserok(telnet_context,
501 		ticket->enc_part2->client, UserNameRequested)) {
502 
503 		/* the name buffer comes from telnetd/telnetd{-ktd}.c */
504 		(void) strncpy(name, UserNameRequested, MAXNAMELEN);
505 		name[MAXNAMELEN-1] = '\0';
506 		return (AUTH_VALID);
507 	} else
508 		return (AUTH_USER);
509 }
510 
511 #define	BUMP(buf, len)		while (*(buf)) {++(buf), --(len); }
512 #define	ADDC(buf, len, c)	if ((len) > 0) {*(buf)++ = (c); --(len); }
513 
514 /*
515  * Used with the set opt command to print suboptions
516  */
517 void
518 kerberos5_printsub(unsigned char *data, int cnt, unsigned char *buf, int buflen)
519 {
520 	char lbuf[AUTH_LBUF_BUFSIZ];
521 	register int i;
522 
523 	buf[buflen-1] = '\0';		/* make sure its NULL terminated */
524 	buflen -= 1;
525 
526 	switch (data[3]) {
527 	case KRB_REJECT:		/* Rejected (reason might follow) */
528 		(void) strncpy((char *)buf, " REJECT ", buflen);
529 		goto common;
530 
531 	case KRB_ACCEPT:		/* Accepted (name might follow) */
532 		(void) strncpy((char *)buf, " ACCEPT ", buflen);
533 	common:
534 		BUMP(buf, buflen);
535 		if (cnt <= 4)
536 			break;
537 		ADDC(buf, buflen, '"');
538 		for (i = 4; i < cnt; i++)
539 			ADDC(buf, buflen, data[i]);
540 		ADDC(buf, buflen, '"');
541 		ADDC(buf, buflen, '\0');
542 		break;
543 
544 	case KRB_AUTH:			/* Authentication data follows */
545 		(void) strncpy((char *)buf, " AUTH", buflen);
546 		goto common2;
547 
548 	case KRB_RESPONSE:
549 		(void) strncpy((char *)buf, " RESPONSE", buflen);
550 		goto common2;
551 
552 	case KRB_FORWARD:		/* Forwarded credentials follow */
553 		(void) strncpy((char *)buf, " FORWARD", buflen);
554 		goto common2;
555 
556 	case KRB_FORWARD_ACCEPT:	/* Forwarded credentials accepted */
557 		(void) strncpy((char *)buf, " FORWARD_ACCEPT", buflen);
558 		goto common2;
559 
560 	case KRB_FORWARD_REJECT:	/* Forwarded credentials rejected */
561 					/* (reason might follow) */
562 		(void) strncpy((char *)buf, " FORWARD_REJECT", buflen);
563 		goto common2;
564 
565 	default:
566 		(void) snprintf(lbuf, AUTH_LBUF_BUFSIZ,
567 			gettext(" %d (unknown)"),
568 			data[3]);
569 		(void) strncpy((char *)buf, lbuf, buflen);
570 	common2:
571 		BUMP(buf, buflen);
572 		for (i = 4; i < cnt; i++) {
573 			(void) snprintf(lbuf, AUTH_LBUF_BUFSIZ, " %d", data[i]);
574 			(void) strncpy((char *)buf, lbuf, buflen);
575 			BUMP(buf, buflen);
576 		}
577 		break;
578 	}
579 }
580 
581 void
582 krb5_profile_get_options(char *host, char *realm,
583 	profile_options_boolean *optionsp)
584 {
585 	char	**realms = NULL;
586 	krb5_error_code err = 0;
587 
588 	if (!telnet_context) {
589 	    err = krb5_init_context(&telnet_context);
590 	    if (err) {
591 		(void) fprintf(stderr, gettext(
592 			"Error initializing Kerberos 5 library: %s\n"),
593 			error_message(err));
594 		return;
595 	    }
596 	}
597 
598 	if ((realmdef[1] = realm) == NULL) {
599 		err = krb5_get_host_realm(telnet_context, host, &realms);
600 		if (err) {
601 		    (void) fprintf(stderr, gettext(
602 			"Error getting Kerberos 5 realms for: %s (%s)\n"),
603 			host, error_message(err));
604 		    return;
605 		}
606 		realmdef[1] = realms[0];
607 	}
608 
609 	profile_get_options_boolean(telnet_context->profile,
610 		realmdef, optionsp);
611 	profile_get_options_boolean(telnet_context->profile,
612 		appdef, optionsp);
613 }
614 
615 static void
616 kerberos5_forward(Authenticator *ap)
617 {
618 	krb5_error_code retval;
619 	krb5_ccache ccache;
620 	krb5_principal client = 0;
621 	krb5_principal server = 0;
622 	krb5_data forw_creds;
623 
624 	forw_creds.data = 0;
625 
626 	if ((retval = krb5_cc_default(telnet_context, &ccache))) {
627 	    if (auth_debug_mode)
628 		(void) printf(gettext(
629 			"Kerberos V5: could not get default ccache - %s\r\n"),
630 			error_message(retval));
631 	    return;
632 	}
633 
634 	retval = krb5_cc_get_principal(telnet_context, ccache, &client);
635 	if (retval) {
636 		if (auth_debug_mode)
637 			(void) printf(gettext(
638 				"Kerberos V5: could not get default "
639 				"principal - %s\r\n"), error_message(retval));
640 		goto cleanup;
641 	}
642 
643 	retval = krb5_sname_to_principal(telnet_context, RemoteHostName,
644 		"host", KRB5_NT_SRV_HST, &server);
645 	if (retval) {
646 		if (auth_debug_mode)
647 			(void) printf(gettext(
648 				"Kerberos V5: could not make server "
649 				"principal - %s\r\n"), error_message(retval));
650 		goto cleanup;
651 	}
652 
653 	retval = krb5_auth_con_genaddrs(telnet_context, auth_context, net,
654 				KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR);
655 	if (retval) {
656 		if (auth_debug_mode)
657 			(void) printf(gettext(
658 				"Kerberos V5: could not gen local full "
659 				"address - %s\r\n"), error_message(retval));
660 		goto cleanup;
661 	}
662 
663 	retval = krb5_fwd_tgt_creds(telnet_context, auth_context, 0, client,
664 		server, ccache, forward_flags & OPTS_FORWARDABLE_CREDS,
665 		&forw_creds);
666 	if (retval) {
667 		if (auth_debug_mode)
668 			(void) printf(gettext(
669 				"Kerberos V5: error getting forwarded "
670 				"creds - %s\r\n"), error_message(retval));
671 		goto cleanup;
672 	}
673 
674 	/* Send forwarded credentials */
675 	if (!krb5_send_data(ap, KRB_FORWARD, forw_creds.data,
676 		forw_creds.length)) {
677 		    if (auth_debug_mode)
678 			(void) printf(gettext(
679 			    "Not enough room for authentication data\r\n"));
680 	} else if (auth_debug_mode)
681 		(void) printf(gettext(
682 		    "Forwarded local Kerberos V5 credentials to server\r\n"));
683 cleanup:
684 	if (client)
685 		krb5_free_principal(telnet_context, client);
686 	if (server)
687 		krb5_free_principal(telnet_context, server);
688 	if (forw_creds.data)
689 		free(forw_creds.data);
690 	/* LINTED */
691 	krb5_cc_close(telnet_context, ccache);
692 }
693