xref: /original-bsd/lib/libtelnet/kerberos5.c (revision b4971bb3)
1 /*
2  *	$Source: /afs/athena.mit.edu/astaff/project/krb5/src/appl/telnet/libtelnet/RCS/kerberos5.c,v $
3  *	$Author: jtkohl $
4  *	$Id: kerberos5.c,v 1.3 91/07/19 16:37:57 jtkohl Exp Locker: tytso $
5  */
6 
7 #if !defined(lint) && !defined(SABER)
8 static
9 #ifdef __STDC__
10 const
11 #endif
12 char rcsid_kerberos5_c[] = "$Id: kerberos5.c,v 1.3 91/07/19 16:37:57 jtkohl Exp Locker: tytso $";
13 #endif /* lint */
14 
15 /*-
16  * Copyright (c) 1991, 1993
17  *	The Regents of the University of California.  All rights reserved.
18  *
19  * %sccs.include.redist.c%
20  */
21 
22 #ifndef lint
23 static char sccsid[] = "@(#)kerberos5.c	8.1 (Berkeley) 06/04/93";
24 #endif /* not lint */
25 
26 /*
27  * Copyright (C) 1990 by the Massachusetts Institute of Technology
28  *
29  * Export of this software from the United States of America is assumed
30  * to require a specific license from the United States Government.
31  * It is the responsibility of any person or organization contemplating
32  * export to obtain such a license before exporting.
33  *
34  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
35  * distribute this software and its documentation for any purpose and
36  * without fee is hereby granted, provided that the above copyright
37  * notice appear in all copies and that both that copyright notice and
38  * this permission notice appear in supporting documentation, and that
39  * the name of M.I.T. not be used in advertising or publicity pertaining
40  * to distribution of the software without specific, written prior
41  * permission.  M.I.T. makes no representations about the suitability of
42  * this software for any purpose.  It is provided "as is" without express
43  * or implied warranty.
44  */
45 
46 
47 #ifdef	KRB5
48 #include <arpa/telnet.h>
49 #include <stdio.h>
50 #include <krb5/krb5.h>
51 #include <krb5/crc-32.h>
52 #include <krb5/los-proto.h>
53 #include <krb5/ext-proto.h>
54 #include <com_err.h>
55 #include <netdb.h>
56 #include <ctype.h>
57 
58 
59 /* kerberos 5 include files (ext-proto.h) will get an appropriate stdlib.h
60    and string.h/strings.h */
61 
62 #include "encrypt.h"
63 #include "auth.h"
64 #include "misc.h"
65 
66 extern auth_debug_mode;
67 
68 
69 static unsigned char str_data[1024] = { IAC, SB, TELOPT_AUTHENTICATION, 0,
70 			  		AUTHTYPE_KERBEROS_V5, };
71 /*static unsigned char str_name[1024] = { IAC, SB, TELOPT_AUTHENTICATION,
72 					TELQUAL_NAME, };*/
73 
74 #define	KRB_AUTH	0		/* Authentication data follows */
75 #define	KRB_REJECT	1		/* Rejected (reason might follow) */
76 #define	KRB_ACCEPT	2		/* Accepted */
77 #define	KRB_RESPONSE	3		/* Response for mutual auth. */
78 
79 static	krb5_data auth;
80 	/* telnetd gets session key from here */
81 static	krb5_tkt_authent *authdat = NULL;
82 /* telnet matches the AP_REQ and AP_REP with this */
83 static	krb5_authenticator authenticator;
84 
85 /* some compilers can't hack void *, so we use the Kerberos krb5_pointer,
86    which is either void * or char *, depending on the compiler. */
87 
88 #define Voidptr krb5_pointer
89 
90 #ifdef	ENCRYPTION
91 Block	session_key;
92 #endif	/* ENCRYPTION */
93 	static int
94 Data(ap, type, d, c)
95 	Authenticator *ap;
96 	int type;
97 	Voidptr d;
98 	int c;
99 {
100         unsigned char *p = str_data + 4;
101 	unsigned char *cd = (unsigned char *)d;
102 
103 	if (c == -1)
104 		c = strlen((char *)cd);
105 
106         if (auth_debug_mode) {
107                 printf("%s:%d: [%d] (%d)",
108                         str_data[3] == TELQUAL_IS ? ">>>IS" : ">>>REPLY",
109                         str_data[3],
110                         type, c);
111                 printd(d, c);
112                 printf("\r\n");
113         }
114 	*p++ = ap->type;
115 	*p++ = ap->way;
116 	*p++ = type;
117         while (c-- > 0) {
118                 if ((*p++ = *cd++) == IAC)
119                         *p++ = IAC;
120         }
121         *p++ = IAC;
122         *p++ = SE;
123 	if (str_data[3] == TELQUAL_IS)
124 		printsub('>', &str_data[2], p - &str_data[2]);
125         return(net_write(str_data, p - str_data));
126 }
127 
128 	int
129 kerberos5_init(ap, server)
130 	Authenticator *ap;
131 	int server;
132 {
133 	if (server)
134 		str_data[3] = TELQUAL_REPLY;
135 	else
136 		str_data[3] = TELQUAL_IS;
137         krb5_init_ets();
138 	return(1);
139 }
140 
141 	int
142 kerberos5_send(ap)
143 	Authenticator *ap;
144 {
145 	char **realms;
146 	char *name;
147 	char *p1, *p2;
148 	krb5_checksum ksum;
149 	krb5_octet sum[CRC32_CKSUM_LENGTH];
150  	krb5_principal server;
151 	krb5_error_code r;
152 	krb5_ccache ccache;
153 	krb5_creds creds;		/* telnet gets session key from here */
154 	extern krb5_flags krb5_kdc_default_options;
155 	int ap_opts;
156 
157 #ifdef	ENCRYPTION
158 	krb5_keyblock *newkey = 0;
159 #endif	/* ENCRYPTION */
160 
161 	ksum.checksum_type = CKSUMTYPE_CRC32;
162 	ksum.contents = sum;
163 	ksum.length = sizeof(sum);
164 	bzero((Voidptr )sum, sizeof(sum));
165 
166         if (!UserNameRequested) {
167                 if (auth_debug_mode) {
168                         printf("Kerberos V5: no user name supplied\r\n");
169                 }
170                 return(0);
171         }
172 
173 	if (r = krb5_cc_default(&ccache)) {
174 		if (auth_debug_mode) {
175 			printf("Kerberos V5: could not get default ccache\r\n");
176 		}
177 		return(0);
178 	}
179 
180 	if ((name = malloc(strlen(RemoteHostName)+1)) == NULL) {
181 		if (auth_debug_mode)
182 			printf("Out of memory for hostname in Kerberos V5\r\n");
183 		return(0);
184 	}
185 
186 	if (r = krb5_get_host_realm(RemoteHostName, &realms)) {
187 		if (auth_debug_mode)
188 			printf("Kerberos V5: no realm for %s\r\n", RemoteHostName);
189 		free(name);
190 		return(0);
191 	}
192 
193 	p1 = RemoteHostName;
194 	p2 = name;
195 
196 	while (*p2 = *p1++) {
197 		if (isupper(*p2))
198 			*p2 |= 040;
199 		++p2;
200 	}
201 
202 	if (r = krb5_build_principal_ext(&server,
203 					 strlen(realms[0]), realms[0],
204 					 4, "rcmd",
205 					 p2 - name, name,
206 					 0)) {
207 		if (auth_debug_mode) {
208 			printf("Kerberos V5: failure setting up principal (%s)\r\n",
209 			       error_message(r));
210 		}
211 		free(name);
212 		krb5_free_host_realm(realms);
213 		return(0);
214 	}
215 
216 
217 	bzero((char *)&creds, sizeof(creds));
218 	creds.server = server;
219 
220 	if (r = krb5_cc_get_principal(ccache, &creds.client)) {
221 		if (auth_debug_mode) {
222 			printf("Kerberos V5: failure on principal (%s)\r\n",
223 				error_message(r));
224 		}
225 		free(name);
226 		krb5_free_principal(server);
227 		krb5_free_host_realm(realms);
228 		return(0);
229 	}
230 
231 	if (r = krb5_get_credentials(krb5_kdc_default_options, ccache, &creds)) {
232 		if (auth_debug_mode) {
233 			printf("Kerberos V5: failure on credentials(%d)\r\n",r);
234 		}
235 		free(name);
236 		krb5_free_host_realm(realms);
237 		krb5_free_principal(server);
238 		return(0);
239 	}
240 
241 	if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL)
242 	    ap_opts = AP_OPTS_MUTUAL_REQUIRED;
243 	else
244 	    ap_opts = 0;
245 
246 	r = krb5_mk_req_extended(ap_opts, &ksum, krb5_kdc_default_options, 0,
247 #ifdef	ENCRYPTION
248 				 &newkey,
249 #else	/* ENCRYPTION */
250 				 0,
251 #endif	/* ENCRYPTION */
252 				 ccache, &creds, &authenticator, &auth);
253 	/* don't let the key get freed if we clean up the authenticator */
254 	authenticator.subkey = 0;
255 
256 	free(name);
257 	krb5_free_host_realm(realms);
258 	krb5_free_principal(server);
259 #ifdef	ENCRYPTION
260 	if (newkey) {
261 	    /* keep the key in our private storage, but don't use it
262 	       yet---see kerberos5_reply() below */
263 	    if (newkey->keytype != KEYTYPE_DES) {
264 		if (creds.keyblock.keytype == KEYTYPE_DES)
265 		    /* use the session key in credentials instead */
266 		    memcpy((char *)session_key,
267 			   (char *)creds.keyblock.contents, sizeof(Block));
268 		else
269 		    /* XXX ? */;
270 	    } else {
271 		memcpy((char *)session_key, (char *)newkey->contents,
272 		       sizeof(Block));
273 	    }
274 	    krb5_free_keyblock(newkey);
275 	}
276 #endif	/* ENCRYPTION */
277 	if (r) {
278 		if (auth_debug_mode) {
279 			printf("Kerberos V5: mk_req failed (%s)\r\n",
280 			       error_message(r));
281 		}
282 		return(0);
283 	}
284 
285         if (!auth_sendname(UserNameRequested, strlen(UserNameRequested))) {
286                 if (auth_debug_mode)
287                         printf("Not enough room for user name\r\n");
288                 return(0);
289         }
290 	if (!Data(ap, KRB_AUTH, auth.data, auth.length)) {
291 		if (auth_debug_mode)
292 			printf("Not enough room for authentication data\r\n");
293 		return(0);
294 	}
295 	if (auth_debug_mode) {
296 		printf("Sent Kerberos V5 credentials to server\r\n");
297 	}
298 	return(1);
299 }
300 
301 	void
302 kerberos5_is(ap, data, cnt)
303 	Authenticator *ap;
304 	unsigned char *data;
305 	int cnt;
306 {
307 	int r;
308 	struct hostent *hp;
309 	char *p1, *p2;
310 	static char *realm = NULL;
311 	krb5_principal server;
312 	krb5_ap_rep_enc_part reply;
313 	krb5_data outbuf;
314 	Session_Key skey;
315 	char *name;
316 	char *getenv();
317 
318 	if (cnt-- < 1)
319 		return;
320 	switch (*data++) {
321 	case KRB_AUTH:
322 		auth.data = (char *)data;
323 		auth.length = cnt;
324 
325 		if (!(hp = gethostbyname(LocalHostName))) {
326 			if (auth_debug_mode)
327 				printf("Cannot resolve local host name\r\n");
328 			Data(ap, KRB_REJECT, "Unknown local hostname.", -1);
329 			auth_finished(ap, AUTH_REJECT);
330 			return;
331 		}
332 
333 		if (!realm && (krb5_get_default_realm(&realm))) {
334 			if (auth_debug_mode)
335 				printf("Could not get default realm\r\n");
336 			Data(ap, KRB_REJECT, "Could not get default realm.", -1);
337 			auth_finished(ap, AUTH_REJECT);
338 			return;
339 		}
340 
341 		if ((name = malloc(strlen(hp->h_name)+1)) == NULL) {
342 			if (auth_debug_mode)
343 				printf("Out of memory for hostname in Kerberos V5\r\n");
344 			Data(ap, KRB_REJECT, "Out of memory.", -1);
345 			auth_finished(ap, AUTH_REJECT);
346 			return;
347 		}
348 
349 		p1 = hp->h_name;
350 		p2 = name;
351 
352 		while (*p2 = *p1++) {
353 			if (isupper(*p2))
354 				*p2 |= 040;
355 			++p2;
356 		}
357 
358 		if (authdat)
359 			krb5_free_tkt_authent(authdat);
360 
361 	        r = krb5_build_principal_ext(&server,
362 					     strlen(realm), realm,
363 					     4, "rcmd",
364 					     p2 - name, name,
365 					     0);
366 		if (!r) {
367 		    r = krb5_rd_req_simple(&auth, server, 0, &authdat);
368 		    krb5_free_principal(server);
369 		}
370 		if (r) {
371 			char errbuf[128];
372 
373 		    errout:
374 			authdat = 0;
375 			(void) strcpy(errbuf, "Read req failed: ");
376 			(void) strcat(errbuf, error_message(r));
377 			Data(ap, KRB_REJECT, errbuf, -1);
378 			if (auth_debug_mode)
379 				printf("%s\r\n", errbuf);
380 			return;
381 		}
382 		free(name);
383 		if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
384 		    /* do ap_rep stuff here */
385 		    reply.ctime = authdat->authenticator->ctime;
386 		    reply.cusec = authdat->authenticator->cusec;
387 		    reply.subkey = 0;	/* use the one he gave us, so don't
388 					   need to return one here */
389 		    reply.seq_number = 0; /* we don't do seq #'s. */
390 
391 		    if (r = krb5_mk_rep(&reply,
392 					authdat->authenticator->subkey ?
393 					authdat->authenticator->subkey :
394 					authdat->ticket->enc_part2->session,
395 					&outbuf)) {
396 			goto errout;
397 		    }
398 		    Data(ap, KRB_RESPONSE, outbuf.data, outbuf.length);
399 		}
400 		if (krb5_unparse_name(authdat->ticket->enc_part2 ->client,
401 				      					&name))
402 			name = 0;
403 		Data(ap, KRB_ACCEPT, name, name ? -1 : 0);
404 		if (auth_debug_mode) {
405 			printf("Kerberos5 identifies him as ``%s''\r\n",
406 							name ? name : "");
407 		}
408                 auth_finished(ap, AUTH_USER);
409 
410 		free(name);
411 	    	if (authdat->authenticator->subkey &&
412 		    authdat->authenticator->subkey->keytype == KEYTYPE_DES) {
413 		    bcopy((Voidptr )authdat->authenticator->subkey->contents,
414 			  (Voidptr )session_key, sizeof(Block));
415 		} else if (authdat->ticket->enc_part2->session->keytype ==
416 			   KEYTYPE_DES) {
417 		    bcopy((Voidptr )authdat->ticket->enc_part2->session->contents,
418 			  (Voidptr )session_key, sizeof(Block));
419 		} else
420 		    break;
421 
422 		skey.type = SK_DES;
423 		skey.length = 8;
424 		skey.data = session_key;
425 		encrypt_session_key(&skey, 1);
426 		break;
427 	default:
428 		if (auth_debug_mode)
429 			printf("Unknown Kerberos option %d\r\n", data[-1]);
430 		Data(ap, KRB_REJECT, 0, 0);
431 		break;
432 	}
433 }
434 
435 	void
436 kerberos5_reply(ap, data, cnt)
437 	Authenticator *ap;
438 	unsigned char *data;
439 	int cnt;
440 {
441         Session_Key skey;
442 	static int mutual_complete = 0;
443 
444 	if (cnt-- < 1)
445 		return;
446 	switch (*data++) {
447 	case KRB_REJECT:
448 		if (cnt > 0) {
449 			printf("[ Kerberos V5 refuses authentication because %.*s ]\r\n",
450 				cnt, data);
451 		} else
452 			printf("[ Kerberos V5 refuses authentication ]\r\n");
453 		auth_send_retry();
454 		return;
455 	case KRB_ACCEPT:
456 		if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL &&
457 		    !mutual_complete) {
458 		    printf("[ Kerberos V5 accepted you, but didn't provide mutual authentication! ]\n");
459 		    auth_send_retry();
460 		    return;
461 		}
462 		if (cnt)
463 		    printf("[ Kerberos V5 accepts you as ``%.*s'' ]\n", cnt, data);
464 		else
465 		    printf("[ Kerberos V5 accepts you ]\n");
466 		auth_finished(ap, AUTH_USER);
467 		break;
468 	case KRB_RESPONSE:
469 		if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
470 		    /* the rest of the reply should contain a krb_ap_rep */
471 		    krb5_ap_rep_enc_part *reply;
472 		    krb5_data inbuf;
473 		    krb5_error_code r;
474 		    krb5_keyblock tmpkey;
475 
476 		    inbuf.length = cnt;
477 		    inbuf.data = (char *)data;
478 
479 		    tmpkey.keytype = KEYTYPE_DES;
480 		    tmpkey.contents = session_key;
481 		    tmpkey.length = sizeof(Block);
482 
483 		    if (r = krb5_rd_rep(&inbuf, &tmpkey, &reply)) {
484 			printf("[ Mutual authentication failed: %s ]\n",
485 			       error_message(r));
486 			auth_send_retry();
487 			return;
488 		    }
489 		    if (reply->ctime != authenticator.ctime ||
490 			reply->cusec != authenticator.cusec) {
491 			printf("[ Mutual authentication failed (mismatched KRB_AP_REP) ]\n");
492 			auth_send_retry();
493 			return;
494 		    }
495 		    krb5_free_ap_rep_enc_part(reply);
496 #ifdef	ENCRYPTION
497 			skey.type = SK_DES;
498 			skey.length = 8;
499 			skey.data = session_key;
500 			encrypt_session_key(&skey, 0);
501 #endif	/* ENCRYPTION */
502 		    mutual_complete = 1;
503 		}
504 		return;
505 	default:
506 		if (auth_debug_mode)
507 			printf("Unknown Kerberos option %d\r\n", data[-1]);
508 		return;
509 	}
510 }
511 
512 	int
513 kerberos5_status(ap, name, level)
514 	Authenticator *ap;
515 	char *name;
516 	int level;
517 {
518 	if (level < AUTH_USER)
519 		return(level);
520 
521 	if (UserNameRequested &&
522 	    krb5_kuserok(authdat->ticket->enc_part2->client, UserNameRequested))
523 	{
524 		strcpy(name, UserNameRequested);
525 		return(AUTH_VALID);
526 	} else
527 		return(AUTH_USER);
528 }
529 
530 #define	BUMP(buf, len)		while (*(buf)) {++(buf), --(len);}
531 #define	ADDC(buf, len, c)	if ((len) > 0) {*(buf)++ = (c); --(len);}
532 
533 	void
534 kerberos5_printsub(data, cnt, buf, buflen)
535 	unsigned char *data, *buf;
536 	int cnt, buflen;
537 {
538 	char lbuf[32];
539 	register int i;
540 
541 	buf[buflen-1] = '\0';		/* make sure its NULL terminated */
542 	buflen -= 1;
543 
544 	switch(data[3]) {
545 	case KRB_REJECT:		/* Rejected (reason might follow) */
546 		strncpy((char *)buf, " REJECT ", buflen);
547 		goto common;
548 
549 	case KRB_ACCEPT:		/* Accepted (name might follow) */
550 		strncpy((char *)buf, " ACCEPT ", buflen);
551 	common:
552 		BUMP(buf, buflen);
553 		if (cnt <= 4)
554 			break;
555 		ADDC(buf, buflen, '"');
556 		for (i = 4; i < cnt; i++)
557 			ADDC(buf, buflen, data[i]);
558 		ADDC(buf, buflen, '"');
559 		ADDC(buf, buflen, '\0');
560 		break;
561 
562 
563 	case KRB_AUTH:			/* Authentication data follows */
564 		strncpy((char *)buf, " AUTH", buflen);
565 		goto common2;
566 
567 	case KRB_RESPONSE:
568 		strncpy((char *)buf, " RESPONSE", buflen);
569 		goto common2;
570 
571 	default:
572 		sprintf(lbuf, " %d (unknown)", data[3]);
573 		strncpy((char *)buf, lbuf, buflen);
574 	common2:
575 		BUMP(buf, buflen);
576 		for (i = 4; i < cnt; i++) {
577 			sprintf(lbuf, " %d", data[i]);
578 			strncpy((char *)buf, lbuf, buflen);
579 			BUMP(buf, buflen);
580 		}
581 		break;
582 	}
583 }
584 #endif
585