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