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