1/*
2 *	appl/telnet/libtelnet/kerberos5.c
3 */
4
5/*-
6 * Copyright (c) 1991, 1993
7 *	The Regents of the University of California.  All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *	This product includes software developed by the University of
20 *	California, Berkeley and its contributors.
21 * 4. Neither the name of the University nor the names of its contributors
22 *    may be used to endorse or promote products derived from this software
23 *    without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37
38/* based on @(#)kerberos5.c	8.1 (Berkeley) 6/4/93 */
39
40/*
41 * Copyright (C) 1990 by the Massachusetts Institute of Technology
42 *
43 * Export of this software from the United States of America may
44 * require a specific license from the United States Government.
45 * It is the responsibility of any person or organization contemplating
46 * export to obtain such a license before exporting.
47 *
48 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
49 * distribute this software and its documentation for any purpose and
50 * without fee is hereby granted, provided that the above copyright
51 * notice appear in all copies and that both that copyright notice and
52 * this permission notice appear in supporting documentation, and that
53 * the name of M.I.T. not be used in advertising or publicity pertaining
54 * to distribution of the software without specific, written prior
55 * permission.  Furthermore if you modify this software you must label
56 * your software as modified software and not distribute it in such a
57 * fashion that it might be confused with the original M.I.T. software.
58 * M.I.T. makes no representations about the suitability of
59 * this software for any purpose.  It is provided "as is" without express
60 * or implied warranty.
61 */
62
63
64#ifdef	KRB5
65#include <arpa/telnet.h>
66#include <stdio.h>
67#define KRB5_DEPRECATED 1 /* krb5_auth_con_getremotesubkey */
68#include <krb5.h>
69#include "com_err.h"
70#include <netdb.h>
71#include <ctype.h>
72#include <syslog.h>
73#include <sys/errno.h>
74
75#ifdef HAVE_STDLIB_H
76#include <stdlib.h>
77#else
78extern char *malloc();
79#endif
80#ifdef	HAVE_STRING_H
81#include <string.h>
82#else
83#include <strings.h>
84#endif
85
86#include "encrypt.h"
87#include "auth.h"
88#include "misc.h"
89
90extern int auth_debug_mode;
91extern int net;
92
93#ifdef	FORWARD
94int forward_flags = 0;  /* Flags get set in telnet/main.c on -f and -F */
95
96void kerberos5_forward();
97krb5_error_code
98rd_and_store_for_creds(krb5_context context, krb5_auth_context auth_context, krb5_data *inbuf, krb5_ticket *ticket);
99
100#endif	/* FORWARD */
101
102static unsigned char str_data[8192] = {IAC, SB, TELOPT_AUTHENTICATION, 0,
103			  		AUTHTYPE_KERBEROS_V5, };
104/*static unsigned char str_name[1024] = { IAC, SB, TELOPT_AUTHENTICATION,
105					TELQUAL_NAME, };*/
106
107#define AUTH_ENCRYPT_OFF        0
108#define AUTH_ENCRYPT_ON         4
109#define AUTH_ENCRYPT_MASK       4
110
111#define	KRB_AUTH		0	/* Authentication data follows */
112#define	KRB_REJECT		1	/* Rejected (reason might follow) */
113#define	KRB_ACCEPT		2	/* Accepted */
114#define	KRB_RESPONSE		3	/* Response for mutual auth. */
115
116#ifdef	FORWARD
117#define KRB_FORWARD     	4       /* Forwarded credentials follow */
118#define KRB_FORWARD_ACCEPT     	5       /* Forwarded credentials accepted */
119#define KRB_FORWARD_REJECT     	6       /* Forwarded credentials rejected */
120#endif	/* FORWARD */
121
122krb5_auth_context auth_context = 0;
123
124static	krb5_data auth;
125	/* telnetd gets session key from here */
126static	krb5_ticket * ticket = NULL;
127/* telnet matches the AP_REQ and AP_REP with this */
128
129/* some compilers can't hack void *, so we use the Kerberos krb5_pointer,
130   which is either void * or char *, depending on the compiler. */
131
132#define Voidptr krb5_pointer
133
134krb5_keyblock	*session_key = 0;
135char *		telnet_srvtab = NULL;
136char *		telnet_krb5_realm = NULL;
137
138	static int
139Data(ap, type, d, c)
140	Authenticator *ap;
141	int type;
142	Voidptr d;
143	int c;
144{
145        unsigned char *p = str_data + 4;
146	unsigned char *cd = (unsigned char *)d;
147	size_t spaceleft = sizeof(str_data) - 4;
148
149	if (c == -1)
150		c = strlen((char *)cd);
151
152        if (auth_debug_mode) {
153                printf("%s:%d: [%d] (%d)",
154                        str_data[3] == TELQUAL_IS ? ">>>IS" : ">>>REPLY",
155                        str_data[3],
156                        type, c);
157                printd(d, c);
158                printf("\r\n");
159        }
160	*p++ = ap->type;
161	*p++ = ap->way;
162	*p++ = type;
163	spaceleft -= 3;
164        while (c-- > 0) {
165		if ((*p++ = *cd++) == IAC) {
166			*p++ = IAC;
167			spaceleft--;
168		}
169		if ((--spaceleft < 4) && c) {
170			errno = ENOMEM;
171			return -1;
172		}
173        }
174        *p++ = IAC;
175        *p++ = SE;
176	if (str_data[3] == TELQUAL_IS)
177		printsub('>', &str_data[2], p - &str_data[2]);
178        return(net_write(str_data, p - str_data));
179}
180
181krb5_context telnet_context = 0;
182int
183kerberos5_init(ap, server)
184	Authenticator *ap;
185	int server;
186{
187	krb5_error_code retval;
188
189	if (server)
190		str_data[3] = TELQUAL_REPLY;
191	else
192		str_data[3] = TELQUAL_IS;
193	if (telnet_context == 0) {
194		retval = krb5_init_context(&telnet_context);
195		if (retval)
196			return 0;
197	}
198	return(1);
199}
200
201void
202kerberos5_cleanup()
203{
204    krb5_error_code retval;
205    krb5_ccache ccache;
206    char *ccname;
207
208    if (telnet_context == 0)
209	return;
210
211    ccname = getenv("KRB5CCNAME");
212    if (ccname) {
213	retval = krb5_cc_resolve(telnet_context, ccname, &ccache);
214	if (!retval)
215	    retval = krb5_cc_destroy(telnet_context, ccache);
216    }
217
218    krb5_free_context(telnet_context);
219    telnet_context = 0;
220}
221
222
223	int
224kerberos5_send(ap)
225	Authenticator *ap;
226{
227	krb5_error_code r;
228	krb5_ccache ccache;
229	krb5_creds creds;		/* telnet gets session key from here */
230	krb5_creds * new_creds = 0;
231	int ap_opts;
232	char type_check[2];
233	krb5_data check_data;
234
235#ifdef	ENCRYPTION
236	krb5_keyblock *newkey = 0;
237#endif	/* ENCRYPTION */
238
239        if (!UserNameRequested) {
240                if (auth_debug_mode) {
241                        printf(
242			"telnet: Kerberos V5: no user name supplied\r\n");
243                }
244                return(0);
245        }
246
247	if ((r = krb5_cc_default(telnet_context, &ccache))) {
248		if (auth_debug_mode) {
249		    printf(
250		    "telnet: Kerberos V5: could not get default ccache\r\n");
251		}
252		return(0);
253	}
254
255	memset((char *)&creds, 0, sizeof(creds));
256	if ((r = krb5_sname_to_principal(telnet_context, RemoteHostName,
257					 "host", KRB5_NT_SRV_HST,
258					 &creds.server))) {
259	    if (auth_debug_mode)
260		printf("telnet: Kerberos V5: error while constructing service name: %s\r\n", error_message(r));
261	    return(0);
262	}
263
264	if (telnet_krb5_realm != NULL) {
265	    krb5_data rdata;
266
267	    rdata.length = strlen(telnet_krb5_realm);
268	    rdata.data = (char *) malloc(rdata.length + 1);
269	    if (rdata.data == NULL) {
270	        fprintf(stderr, "malloc failed\n");
271		return(0);
272	    }
273	    strcpy(rdata.data, telnet_krb5_realm);
274	    krb5_princ_set_realm(telnet_context, creds.server, &rdata);
275	}
276
277	if ((r = krb5_cc_get_principal(telnet_context, ccache,
278				       &creds.client))) {
279		if (auth_debug_mode) {
280			printf(
281			"telnet: Kerberos V5: failure on principal (%s)\r\n",
282				error_message(r));
283		}
284		krb5_free_cred_contents(telnet_context, &creds);
285		return(0);
286	}
287
288	creds.keyblock.enctype=ENCTYPE_DES_CBC_CRC;
289	if ((r = krb5_get_credentials(telnet_context, 0,
290				      ccache, &creds, &new_creds))) {
291		if (auth_debug_mode) {
292			printf(
293			"telnet: Kerberos V5: failure on credentials(%s)\r\n",
294			       error_message(r));
295		}
296		krb5_free_cred_contents(telnet_context, &creds);
297		return(0);
298	}
299
300	if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL)
301	    ap_opts = AP_OPTS_MUTUAL_REQUIRED;
302	else
303	    ap_opts = 0;
304
305#ifdef ENCRYPTION
306	ap_opts |= AP_OPTS_USE_SUBKEY;
307#endif	/* ENCRYPTION */
308
309	if (auth_context) {
310	    krb5_auth_con_free(telnet_context, auth_context);
311	    auth_context = 0;
312	}
313	if ((r = krb5_auth_con_init(telnet_context, &auth_context))) {
314	    if (auth_debug_mode) {
315		printf("Kerberos V5: failed to init auth_context (%s)\r\n",
316		       error_message(r));
317	    }
318	    return(0);
319	}
320
321	krb5_auth_con_setflags(telnet_context, auth_context,
322			       KRB5_AUTH_CONTEXT_RET_TIME);
323
324	type_check[0] = ap->type;
325	type_check[1] = ap->way;
326	check_data.magic = KV5M_DATA;
327	check_data.length = 2;
328	check_data.data = (char *) &type_check;
329
330	r = krb5_mk_req_extended(telnet_context, &auth_context, ap_opts,
331				 &check_data, new_creds, &auth);
332
333#ifdef	ENCRYPTION
334	krb5_auth_con_getlocalsubkey(telnet_context, auth_context, &newkey);
335	if (session_key) {
336		krb5_free_keyblock(telnet_context, session_key);
337		session_key = 0;
338	}
339
340	if (newkey) {
341	    /* keep the key in our private storage, but don't use it
342	       yet---see kerberos5_reply() below */
343	    if ((newkey->enctype != ENCTYPE_DES_CBC_CRC) &&
344		(newkey-> enctype != ENCTYPE_DES_CBC_MD5)) {
345		if ((new_creds->keyblock.enctype == ENCTYPE_DES_CBC_CRC) ||
346		    (new_creds->keyblock.enctype == ENCTYPE_DES_CBC_MD5))
347		    /* use the session key in credentials instead */
348		    krb5_copy_keyblock(telnet_context,&new_creds->keyblock,
349				       &session_key);
350		else
351		    /* XXX ? */;
352	    } else {
353		krb5_copy_keyblock(telnet_context, newkey, &session_key);
354	    }
355	    krb5_free_keyblock(telnet_context, newkey);
356	}
357#endif	/* ENCRYPTION */
358	krb5_free_cred_contents(telnet_context, &creds);
359	krb5_free_creds(telnet_context, new_creds);
360	if (r) {
361		if (auth_debug_mode) {
362			printf("telnet: Kerberos V5: mk_req failed (%s)\r\n",
363			       error_message(r));
364		}
365		return(0);
366	}
367
368        if (!auth_sendname(UserNameRequested, strlen(UserNameRequested))) {
369                if (auth_debug_mode)
370                        printf("telnet: Not enough room for user name\r\n");
371                return(0);
372        }
373	if (!Data(ap, KRB_AUTH, auth.data, auth.length)) {
374		if (auth_debug_mode)
375		    printf(
376		    "telnet: Not enough room for authentication data\r\n");
377		return(0);
378	}
379	if (auth_debug_mode) {
380		printf("telnet: Sent Kerberos V5 credentials to server\r\n");
381	}
382	return(1);
383}
384
385	void
386kerberos5_is(ap, data, cnt)
387	Authenticator *ap;
388	unsigned char *data;
389	int cnt;
390{
391	int r = 0;
392	krb5_principal server;
393	krb5_keyblock *newkey = NULL;
394	krb5_keytab keytabid = 0;
395	krb5_data outbuf;
396#ifdef ENCRYPTION
397	Session_Key skey;
398#endif
399	char errbuf[320];
400	char *name;
401	char *getenv();
402	krb5_data inbuf;
403	krb5_authenticator *authenticator;
404
405	if (cnt-- < 1)
406		return;
407	switch (*data++) {
408	case KRB_AUTH:
409		auth.data = (char *)data;
410		auth.length = cnt;
411
412		if (!r && !auth_context)
413		    r = krb5_auth_con_init(telnet_context, &auth_context);
414		if (!r) {
415		    krb5_rcache rcache;
416
417		    r = krb5_auth_con_getrcache(telnet_context, auth_context,
418						&rcache);
419		    if (!r && !rcache) {
420			r = krb5_sname_to_principal(telnet_context, 0, 0,
421						    KRB5_NT_SRV_HST, &server);
422			if (!r) {
423			    r = krb5_get_server_rcache(telnet_context,
424					krb5_princ_component(telnet_context,
425							     server, 0),
426						       &rcache);
427			    krb5_free_principal(telnet_context, server);
428			}
429		    }
430		    if (!r)
431			r = krb5_auth_con_setrcache(telnet_context,
432						    auth_context, rcache);
433		}
434		if (!r && telnet_srvtab)
435		    r = krb5_kt_resolve(telnet_context,
436					telnet_srvtab, &keytabid);
437		if (!r)
438		    r = krb5_rd_req(telnet_context, &auth_context, &auth,
439				    NULL, keytabid, NULL, &ticket);
440		if (r) {
441			(void) strcpy(errbuf, "krb5_rd_req failed: ");
442			errbuf[sizeof(errbuf) - 1] = '\0';
443			(void) strncat(errbuf, error_message(r), sizeof(errbuf) - 1 - strlen(errbuf));
444			goto errout;
445		}
446
447		/*
448		 * 256 bytes should be much larger than any reasonable
449		 * first component of a service name especially since
450		 * the default is of length 4.
451		 */
452		if (krb5_princ_component(telnet_context,ticket->server,0)->length < 256) {
453		    char princ[256];
454		    strncpy(princ,
455			    krb5_princ_component(telnet_context, ticket->server,0)->data,
456			    krb5_princ_component(telnet_context, ticket->server,0)->length);
457		    princ[krb5_princ_component(telnet_context,
458					       ticket->server,0)->length] = '\0';
459		    if (strcmp("host", princ)) {
460                        if(strlen(princ) < sizeof(errbuf) - 39) {
461                            (void) sprintf(errbuf, "incorrect service name: \"%s\" != \"host\"",
462                                           princ);
463                        } else {
464                            (void) sprintf(errbuf, "incorrect service name: principal != \"host\"");
465                        }
466			goto errout;
467		    }
468		} else {
469		    (void) strcpy(errbuf, "service name too long");
470		    goto errout;
471		}
472
473		r = krb5_auth_con_getauthenticator(telnet_context,
474						   auth_context,
475						   &authenticator);
476		if (r) {
477		    (void) strcpy(errbuf,
478				  "krb5_auth_con_getauthenticator failed: ");
479		    errbuf[sizeof(errbuf) - 1] = '\0';
480		    (void) strncat(errbuf, error_message(r), sizeof(errbuf) - 1 - strlen(errbuf));
481		    goto errout;
482		}
483		if ((ap->way & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_ON &&
484		    !authenticator->checksum) {
485			(void) strcpy(errbuf,
486				"authenticator is missing required checksum");
487			goto errout;
488		}
489		if (authenticator->checksum) {
490		    char type_check[2];
491		    krb5_checksum *cksum = authenticator->checksum;
492		    krb5_keyblock *key;
493
494		    type_check[0] = ap->type;
495		    type_check[1] = ap->way;
496
497		    r = krb5_auth_con_getkey(telnet_context, auth_context,
498					     &key);
499		    if (r) {
500			(void) strcpy(errbuf, "krb5_auth_con_getkey failed: ");
501			errbuf[sizeof(errbuf) - 1] = '\0';
502			(void) strncat(errbuf, error_message(r), sizeof(errbuf) - 1 - strlen(errbuf));
503			goto errout;
504		    }
505		    r = krb5_verify_checksum(telnet_context,
506					     cksum->checksum_type, cksum,
507					     &type_check, 2, key->contents,
508					     key->length);
509		/*
510		 * Note that krb5_verify_checksum() will fail if a pre-
511		 * MIT Kerberos Beta 5 client is attempting to connect
512		 * to this server (Beta 6 or later). There is not way to
513		 * fix this without compromising encryption. It would be
514		 * reasonable to add a -i option to telnetd to ignore
515		 * checksums (like in klogind). Such an option is not
516		 * present at this time.
517		 */
518		    if (r) {
519			(void) strcpy(errbuf,
520				      "checksum verification failed: ");
521		        errbuf[sizeof(errbuf) - 1] = '\0';
522			(void) strncat(errbuf, error_message(r), sizeof(errbuf) - 1 - strlen(errbuf));
523			goto errout;
524		    }
525		    krb5_free_keyblock(telnet_context, key);
526		}
527		krb5_free_authenticator(telnet_context, authenticator);
528		if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
529		    /* do ap_rep stuff here */
530		    if ((r = krb5_mk_rep(telnet_context, auth_context,
531					 &outbuf))) {
532			(void) strcpy(errbuf, "Make reply failed: ");
533		        errbuf[sizeof(errbuf) - 1] = '\0';
534			(void) strncat(errbuf, error_message(r), sizeof(errbuf) - 1 - strlen(errbuf));
535			goto errout;
536		    }
537
538		    Data(ap, KRB_RESPONSE, outbuf.data, outbuf.length);
539		}
540		if (krb5_unparse_name(telnet_context,
541				      ticket->enc_part2 ->client,
542				      &name))
543			name = 0;
544		Data(ap, KRB_ACCEPT, name, name ? -1 : 0);
545		if (auth_debug_mode) {
546			printf(
547			"telnetd: Kerberos5 identifies him as ``%s''\r\n",
548							name ? name : "");
549		}
550                auth_finished(ap, AUTH_USER);
551
552		if (name)
553		    free(name);
554		krb5_auth_con_getremotesubkey(telnet_context, auth_context,
555					      &newkey);
556		if (session_key) {
557		    krb5_free_keyblock(telnet_context, session_key);
558		    session_key = 0;
559		}
560	    	if (newkey) {
561		    krb5_copy_keyblock(telnet_context, newkey, &session_key);
562		    krb5_free_keyblock(telnet_context, newkey);
563		} else {
564		    krb5_copy_keyblock(telnet_context,
565				       ticket->enc_part2->session,
566				       &session_key);
567		}
568
569#ifdef ENCRYPTION
570		skey.type = SK_DES;
571		skey.length = 8;
572		skey.data = session_key->contents;
573		encrypt_session_key(&skey, 1);
574#endif
575		break;
576#ifdef	FORWARD
577	case KRB_FORWARD:
578		inbuf.length = cnt;
579		inbuf.data = (char *)data;
580		if ((r = krb5_auth_con_genaddrs(telnet_context, auth_context,
581			net, KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR)) ||
582		    (r = rd_and_store_for_creds(telnet_context, auth_context,
583			   &inbuf, ticket))) {
584
585		    char errbuf[128];
586
587		    (void) strcpy(errbuf, "Read forwarded creds failed: ");
588		    errbuf[sizeof(errbuf) - 1] = '\0';
589		    (void) strncat(errbuf, error_message(r), sizeof(errbuf) - 1 - strlen(errbuf));
590		    Data(ap, KRB_FORWARD_REJECT, errbuf, -1);
591		    if (auth_debug_mode)
592		      printf(
593			"telnetd: Could not read forwarded credentials\r\n");
594		}
595		else
596		  Data(ap, KRB_FORWARD_ACCEPT, 0, 0);
597		  if (auth_debug_mode)
598		    printf("telnetd: Forwarded credentials obtained\r\n");
599		break;
600#endif	/* FORWARD */
601	default:
602		if (auth_debug_mode)
603			printf("telnetd: Unknown Kerberos option %d\r\n",
604			data[-1]);
605		Data(ap, KRB_REJECT, 0, 0);
606		break;
607	}
608	return;
609
610    errout:
611	{
612	    char eerrbuf[329];
613
614	    strcpy(eerrbuf, "telnetd: ");
615	    eerrbuf[sizeof(eerrbuf) - 1] = '\0';
616	    strncat(eerrbuf, errbuf, sizeof(eerrbuf) - 1 - strlen(eerrbuf));
617	    Data(ap, KRB_REJECT, eerrbuf, -1);
618	}
619	if (auth_debug_mode)
620	    printf("telnetd: %s\r\n", errbuf);
621	syslog(LOG_ERR, "%s", errbuf);
622	if (auth_context) {
623	    krb5_auth_con_free(telnet_context, auth_context);
624	    auth_context = 0;
625	}
626	return;
627}
628
629	void
630kerberos5_reply(ap, data, cnt)
631	Authenticator *ap;
632	unsigned char *data;
633	int cnt;
634{
635#ifdef ENCRYPTION
636        Session_Key skey;
637#endif
638	static int mutual_complete = 0;
639
640	if (cnt-- < 1)
641		return;
642	switch (*data++) {
643	case KRB_REJECT:
644		if (cnt > 0) {
645			printf("[ Kerberos V5 refuses authentication because %.*s ]\r\n",
646				cnt, data);
647		} else
648			printf("[ Kerberos V5 refuses authentication ]\r\n");
649		auth_send_retry();
650		return;
651	case KRB_ACCEPT:
652		if (!mutual_complete) {
653		    if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
654			printf("[ Kerberos V5 accepted you, but didn't provide mutual authentication! ]\r\n");
655			auth_send_retry();
656			return;
657		    }
658#ifdef	ENCRYPTION
659		    if (session_key) {
660			skey.type = SK_DES;
661			skey.length = 8;
662			skey.data = session_key->contents;
663			encrypt_session_key(&skey, 0);
664		    }
665#endif	/* ENCRYPTION */
666		}
667		if (cnt)
668		    printf("[ Kerberos V5 accepts you as ``%.*s'' ]\r\n", cnt, data);
669		else
670		    printf("[ Kerberos V5 accepts you ]\r\n");
671		auth_finished(ap, AUTH_USER);
672#ifdef	FORWARD
673		if (forward_flags & OPTS_FORWARD_CREDS)
674		  kerberos5_forward(ap);
675#endif	/* FORWARD */
676		break;
677	case KRB_RESPONSE:
678		if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
679		    /* the rest of the reply should contain a krb_ap_rep */
680		    krb5_ap_rep_enc_part *reply;
681		    krb5_data inbuf;
682		    krb5_error_code r;
683
684		    inbuf.length = cnt;
685		    inbuf.data = (char *)data;
686
687		    if ((r = krb5_rd_rep(telnet_context, auth_context, &inbuf,
688					 &reply))) {
689			printf("[ Mutual authentication failed: %s ]\r\n",
690			       error_message(r));
691			auth_send_retry();
692			return;
693		    }
694		    krb5_free_ap_rep_enc_part(telnet_context, reply);
695#ifdef	ENCRYPTION
696		    if (session_key) {
697			skey.type = SK_DES;
698			skey.length = 8;
699			skey.data = session_key->contents;
700			encrypt_session_key(&skey, 0);
701		      }
702#endif	/* ENCRYPTION */
703		    mutual_complete = 1;
704		}
705		return;
706#ifdef	FORWARD
707	case KRB_FORWARD_ACCEPT:
708		printf("[ Kerberos V5 accepted forwarded credentials ]\r\n");
709		return;
710	case KRB_FORWARD_REJECT:
711		printf("[ Kerberos V5 refuses forwarded credentials because %.*s ]\r\n",
712				cnt, data);
713		return;
714#endif	/* FORWARD */
715	default:
716		if (auth_debug_mode)
717			printf("Unknown Kerberos option %d\r\n", data[-1]);
718		return;
719	}
720	return;
721}
722
723	int
724kerberos5_status(ap, name, level)
725	Authenticator *ap;
726	char *name;
727	int level;
728{
729	if (level < AUTH_USER)
730		return(level);
731
732	if (UserNameRequested &&
733	    krb5_kuserok(telnet_context, ticket->enc_part2->client,
734			 UserNameRequested))
735	{
736		/* the name buffer comes from telnetd/telnetd{-ktd}.c */
737		strncpy(name, UserNameRequested, 255);
738		name[255] = '\0';
739		return(AUTH_VALID);
740	} else
741		return(AUTH_USER);
742}
743
744#define	BUMP(buf, len)		while (*(buf)) {++(buf), --(len);}
745#define	ADDC(buf, len, c)	if ((len) > 0) {*(buf)++ = (c); --(len);}
746
747	void
748kerberos5_printsub(data, cnt, buf, buflen)
749	unsigned char *data, *buf;
750	int cnt, buflen;
751{
752	char lbuf[32];
753	register int i;
754
755	buf[buflen-1] = '\0';		/* make sure its NULL terminated */
756	buflen -= 1;
757
758	switch(data[3]) {
759	case KRB_REJECT:		/* Rejected (reason might follow) */
760		strncpy((char *)buf, " REJECT ", buflen);
761		goto common;
762
763	case KRB_ACCEPT:		/* Accepted (name might follow) */
764		strncpy((char *)buf, " ACCEPT ", buflen);
765	common:
766		BUMP(buf, buflen);
767		if (cnt <= 4)
768			break;
769		ADDC(buf, buflen, '"');
770		for (i = 4; i < cnt; i++)
771			ADDC(buf, buflen, data[i]);
772		ADDC(buf, buflen, '"');
773		ADDC(buf, buflen, '\0');
774		break;
775
776
777	case KRB_AUTH:			/* Authentication data follows */
778		strncpy((char *)buf, " AUTH", buflen);
779		goto common2;
780
781	case KRB_RESPONSE:
782		strncpy((char *)buf, " RESPONSE", buflen);
783		goto common2;
784
785#ifdef	FORWARD
786	case KRB_FORWARD:               /* Forwarded credentials follow */
787		strncpy((char *)buf, " FORWARD", buflen);
788		goto common2;
789
790	case KRB_FORWARD_ACCEPT:               /* Forwarded credentials accepted */
791		strncpy((char *)buf, " FORWARD_ACCEPT", buflen);
792		goto common2;
793
794	case KRB_FORWARD_REJECT:               /* Forwarded credentials rejected */
795					       /* (reason might follow) */
796		strncpy((char *)buf, " FORWARD_REJECT", buflen);
797		goto common2;
798#endif	/* FORWARD */
799
800	default:
801		sprintf(lbuf, " %d (unknown)", data[3]);
802		strncpy((char *)buf, lbuf, buflen);
803	common2:
804		BUMP(buf, buflen);
805		for (i = 4; i < cnt; i++) {
806			sprintf(lbuf, " %d", data[i]);
807			strncpy((char *)buf, lbuf, buflen);
808			BUMP(buf, buflen);
809		}
810		break;
811	}
812}
813
814#ifdef	FORWARD
815
816void
817kerberos5_forward(ap)
818     Authenticator *ap;
819{
820    krb5_error_code r;
821    krb5_ccache ccache;
822    krb5_principal client = 0;
823    krb5_principal server = 0;
824    krb5_data forw_creds;
825
826    forw_creds.data = 0;
827
828    if ((r = krb5_cc_default(telnet_context, &ccache))) {
829	if (auth_debug_mode)
830	    printf("Kerberos V5: could not get default ccache - %s\r\n",
831		   error_message(r));
832	return;
833    }
834
835    if ((r = krb5_cc_get_principal(telnet_context, ccache, &client))) {
836	if (auth_debug_mode)
837	    printf("Kerberos V5: could not get default principal - %s\r\n",
838		   error_message(r));
839	goto cleanup;
840    }
841
842    if ((r = krb5_sname_to_principal(telnet_context, RemoteHostName, "host",
843				     KRB5_NT_SRV_HST, &server))) {
844	if (auth_debug_mode)
845	    printf("Kerberos V5: could not make server principal - %s\r\n",
846		   error_message(r));
847	goto cleanup;
848    }
849
850    if ((r = krb5_auth_con_genaddrs(telnet_context, auth_context, net,
851			    KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR))) {
852	if (auth_debug_mode)
853	    printf("Kerberos V5: could not gen local full address - %s\r\n",
854		    error_message(r));
855	goto cleanup;
856    }
857
858    if ((r = krb5_fwd_tgt_creds(telnet_context, auth_context, 0, client,
859				server, ccache,
860				forward_flags & OPTS_FORWARDABLE_CREDS,
861				&forw_creds))) {
862	if (auth_debug_mode)
863	    printf("Kerberos V5: error getting forwarded creds - %s\r\n",
864	  	   error_message(r));
865	goto cleanup;
866    }
867
868    /* Send forwarded credentials */
869    if (!Data(ap, KRB_FORWARD, forw_creds.data, forw_creds.length)) {
870	if (auth_debug_mode)
871	    printf("Not enough room for authentication data\r\n");
872    } else {
873	if (auth_debug_mode)
874	    printf("Forwarded local Kerberos V5 credentials to server\r\n");
875    }
876
877cleanup:
878    if (client)
879	krb5_free_principal(telnet_context, client);
880    if (server)
881	krb5_free_principal(telnet_context, server);
882    if (forw_creds.data)
883	free(forw_creds.data);
884    krb5_cc_close(telnet_context, ccache);
885}
886#endif	/* FORWARD */
887
888#endif /* KRB5 */
889