xref: /original-bsd/lib/libtelnet/kerberos5.c (revision f91c1509)
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.1 (Berkeley) 02/28/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 
62 #define	KRB_AUTH	0		/* Authentication data follows */
63 #define	KRB_REJECT	1		/* Rejected (reason might follow) */
64 #define	KRB_ACCEPT	2		/* Accepted (name might follow) */
65 #define	KRB_NEWKEY	3		/* New key to use */
66 #define KRB_NAME        4               /* Name to authenticate for */
67 
68 static	krb5_data auth;
69 	/* telnetd gets session key from here */
70 static	krb5_tkt_authent *authdat = NULL;
71 
72 #if	defined(ENCRYPT)
73 Block	session_key;
74 #endif
75 
76 	static int
77 Data(type, d, c)
78 	int type;
79 	void *d;
80 	int c;
81 {
82         unsigned char *p = str_data + 6;
83 	unsigned char *cd = (unsigned char *)d;
84 
85 	if (c == -1)
86 		c = strlen(d);
87 
88         if (auth_debug_mode) {
89                 printf("%s:%d: [%d] (%d)",
90                         str_data[3] == TELQUAL_IS ? ">>>IS" : ">>>REPLY",
91                         str_data[3],
92                         type, c);
93                 printd(d, c);
94                 printf("\r\n");
95         }
96 	*p++ = type;
97         while (c-- > 0) {
98                 if ((*p++ = *cd++) == IAC)
99                         *p++ = IAC;
100         }
101         *p++ = IAC;
102         *p++ = SE;
103 	if (str_data[3] == TELQUAL_IS)
104 		printsub('>', &str_data[2], p - &str_data[2]);
105         return(net_write(str_data, p - str_data));
106 }
107 
108 	int
109 kerberos5_init(ap, server)
110 	Authenticator *ap;
111 	int server;
112 {
113 	if (server)
114 		str_data[3] = TELQUAL_REPLY;
115 	else
116 		str_data[3] = TELQUAL_IS;
117 	str_data[4] = ap->type;
118 	str_data[5] = ap->way;
119         krb5_init_ets();
120 	return(1);
121 }
122 
123 	int
124 kerberos5_send(ap)
125 	Authenticator *ap;
126 {
127 	char **realms;
128 	char *name;
129 	char *p1, *p2;
130 	krb5_checksum ksum;
131 	krb5_octet sum[CRC32_CKSUM_LENGTH];
132 	krb5_data *server[4];
133 	krb5_data srvdata[3];
134 	krb5_error_code r;
135 	krb5_ccache ccache;
136 	krb5_creds creds;		/* telnet gets session key from here */
137 	extern krb5_flags krb5_kdc_default_options;
138 
139 	ksum.checksum_type = CKSUMTYPE_CRC32;
140 	ksum.contents = sum;
141 	ksum.length = sizeof(sum);
142 	bzero((void *)sum, sizeof(sum));
143 
144         if (!UserNameRequested) {
145                 if (auth_debug_mode) {
146                         printf("Kerberos V5: no user name supplied\r\n");
147                 }
148                 return(0);
149         }
150 
151 	if (r = krb5_cc_default(&ccache)) {
152 		if (auth_debug_mode) {
153 			printf("Kerberos V5: could not get default ccache\r\n");
154 		}
155 		return(0);
156 	}
157 
158 	if ((name = malloc(strlen(RemoteHostName)+1)) == NULL) {
159 		if (auth_debug_mode)
160 			printf("Out of memory for hostname in Kerberos V5\r\n");
161 		return(0);
162 	}
163 
164 	if (r = krb5_get_host_realm(RemoteHostName, &realms)) {
165 		if (auth_debug_mode)
166 			printf("Kerberos V5: no realm for %s\r\n", RemoteHostName);
167 		free(name);
168 		return(0);
169 	}
170 
171 	p1 = RemoteHostName;
172 	p2 = name;
173 
174 	while (*p2 = *p1++) {
175 		if (isupper(*p2))
176 			*p2 |= 040;
177 		++p2;
178 	}
179 
180 	srvdata[0].data = realms[0];
181 	srvdata[0].length = strlen(realms[0]);
182 	srvdata[1].data = "rcmd";
183 	srvdata[1].length = 4;
184 	srvdata[2].data = name;
185 	srvdata[2].length = p2 - name;
186 
187 	server[0] = &srvdata[0];
188 	server[1] = &srvdata[1];
189 	server[2] = &srvdata[2];
190 	server[3] = 0;
191 
192 	bzero((char *)&creds, sizeof(creds));
193 	creds.server = (krb5_principal)server;
194 
195 	if (r = krb5_cc_get_principal(ccache, &creds.client)) {
196 		if (auth_debug_mode) {
197 			printf("Keberos V5: failure on principal (%d)\r\n",
198 				error_message(r));
199 		}
200 		free(name);
201 		krb5_free_host_realm(realms);
202 		return(0);
203 	}
204 
205 	if (r = krb5_get_credentials(krb5_kdc_default_options, ccache, &creds)) {
206 		if (auth_debug_mode) {
207 			printf("Keberos V5: failure on credentials(%d)\r\n",r);
208 		}
209 		free(name);
210 		krb5_free_host_realm(realms);
211 		return(0);
212 	}
213 
214 	r = krb5_mk_req_extended(0, &ksum, &creds.times,
215 				 krb5_kdc_default_options,
216 				 ccache, &creds, 0, &auth);
217 
218 	free(name);
219 	krb5_free_host_realm(realms);
220 	if (r) {
221 		if (auth_debug_mode) {
222 			printf("Keberos V5: mk_req failed\r\n");
223 		}
224 		return(0);
225 	}
226 
227         if (!Data(KRB_NAME, (void *)UserNameRequested, -1)) {
228                 if (auth_debug_mode)
229                         printf("Not enough room for user name\r\n");
230                 return(0);
231         }
232 	if (!Data(KRB_AUTH, auth.data, auth.length)) {
233 		if (auth_debug_mode)
234 			printf("Not enough room for authentication data\r\n");
235 		return(0);
236 	}
237 #if     defined(ENCRYPT)
238 	if (creds.keyblock.keytype == KEYTYPE_DES) {
239 		Schedule krb_sched;
240 		Block enckey;
241 
242 		des_key_sched(creds.keyblock.contents, krb_sched);
243 		des_set_random_generator_seed(creds.keyblock.contents);
244 		des_new_random_key(session_key);
245 		des_ecb_encrypt(session_key, enckey, krb_sched, 1);
246 		Data(KRB_NEWKEY, (void *)enckey, sizeof(enckey));
247 	}
248 #endif
249 	if (auth_debug_mode) {
250 		printf("Sent Kerberos V5 credentials to server\r\n");
251 	}
252 	return(1);
253 }
254 
255 	void
256 kerberos5_is(ap, data, cnt)
257 	Authenticator *ap;
258 	unsigned char *data;
259 	int cnt;
260 {
261 	int r;
262 	struct hostent *hp;
263 	char *p1, *p2;
264 	static char *realm = NULL;
265 	krb5_data *server[4];
266 	krb5_data srvdata[3];
267         Schedule sched;
268         Session_Key skey;
269 	char *name;
270 	char *getenv();
271 
272 	if (cnt-- < 1)
273 		return;
274 	switch (*data++) {
275         case KRB_NAME: {
276                 char user[256];
277 
278                 if (cnt > 255)
279                         cnt = 255;
280                 strncpy(user, data, cnt);
281                 user[cnt] = 0;
282                 auth_encrypt_user(user);
283                 return;
284             }
285 	case KRB_AUTH:
286 		auth.data = (char *)data;
287 		auth.length = cnt;
288 
289 		if (!(hp = gethostbyname(LocalHostName))) {
290 			if (auth_debug_mode)
291 				printf("Cannot resolve local host name\r\n");
292 			Data(KRB_REJECT, "Unknown local hostname.", -1);
293 			auth_finished(ap, AUTH_REJECT);
294 			return;
295 		}
296 
297 		if (!realm && (krb5_get_default_realm(&realm))) {
298 			if (auth_debug_mode)
299 				printf("Could not get defualt realm\r\n");
300 			Data(KRB_REJECT, "Could not get default realm.", -1);
301 			auth_finished(ap, AUTH_REJECT);
302 			return;
303 		}
304 
305 		if ((name = malloc(strlen(hp->h_name)+1)) == NULL) {
306 			if (auth_debug_mode)
307 				printf("Out of memory for hostname in Kerberos V5\r\n");
308 			Data(KRB_REJECT, "Out of memory.", -1);
309 			auth_finished(ap, AUTH_REJECT);
310 			return;
311 		}
312 
313 		p1 = hp->h_name;
314 		p2 = name;
315 
316 		while (*p2 = *p1++) {
317 			if (isupper(*p2))
318 				*p2 |= 040;
319 			++p2;
320 		}
321 
322 		srvdata[0].data = realm;
323 		srvdata[0].length = strlen(realm);
324 		srvdata[1].data = "rcmd";
325 		srvdata[1].length = 4;
326 		srvdata[2].data = name;
327 		srvdata[2].length = p2 - name;
328 
329 		server[0] = &srvdata[0];
330 		server[1] = &srvdata[1];
331 		server[2] = &srvdata[2];
332 		server[3] = 0;
333 
334 		if (authdat)
335 			krb5_free_tkt_authent(authdat);
336 		if (r = krb5_rd_req_simple(&auth, server, 0, &authdat)) {
337 			char errbuf[128];
338 
339 			authdat = 0;
340 			(void) strcpy(errbuf, "Read req failed: ");
341 			(void) strcat(errbuf, error_message(r));
342 			Data(KRB_REJECT, errbuf, -1);
343 			if (auth_debug_mode)
344 				printf("%s\r\n", errbuf);
345 			return;
346 		}
347 		free(name);
348 		if (krb5_unparse_name(authdat->ticket->enc_part2 ->client,
349 				      					&name))
350 			name = 0;
351 		Data(KRB_ACCEPT, name, name ? -1 : 0);
352 		if (auth_debug_mode) {
353 			printf("Kerberos5 accepting him as ``%s''\r\n",
354 							name ? name : "");
355 		}
356                 auth_finished(ap, AUTH_USER);
357                 return;
358         case KRB_NEWKEY:
359 #if     defined(ENCRYPT)
360 		if (authdat && authdat->ticket->enc_part2->session->keytype
361 				== KEYTYPE_DES)
362 		{
363 			des_key_sched(authdat->ticket->enc_part2->session
364 							->contents, sched);
365 			des_ecb_encrypt(data, session_key, sched, 0);
366 			skey.type = SK_DES;
367 			skey.length = 8;
368 			skey.data = session_key;
369 			encrypt_session_key(&skey, 1);
370 		}
371 #endif
372                 return;
373 	default:
374 		if (auth_debug_mode)
375 			printf("Unknown Kerberos option %d\r\n", data[-1]);
376 		Data(KRB_REJECT, 0, 0);
377 		return;
378 	}
379 }
380 
381 	void
382 kerberos5_reply(ap, data, cnt)
383 	Authenticator *ap;
384 	unsigned char *data;
385 	int cnt;
386 {
387         Session_Key skey;
388 
389 	if (cnt-- < 1)
390 		return;
391 	switch (*data++) {
392 	case KRB_REJECT:
393 		if (cnt > 0) {
394 			printf("[ Kerberos V5 refuses authentication because %.*s ]\r\n",
395 				cnt, data);
396 		} else
397 			printf("[ Kerberos V5 refuses authentication ]\r\n");
398 		auth_send_retry();
399 		return;
400 	case KRB_ACCEPT:
401 		if (cnt > 0) {
402 			printf("[ Kerberos V5 accepts you as %.*s ]\n", cnt, data);
403 		} else
404 			printf("[ Kerberos V5 accepts you ]\n", cnt, data);
405 #if	defined(ENCRYPT)
406                 skey.type = SK_DES;
407                 skey.length = 8;
408                 skey.data = session_key;
409                 encrypt_session_key(&skey, 0);
410 #endif
411                 auth_finished(ap, AUTH_USER);
412 		return;
413 	default:
414 		if (auth_debug_mode)
415 			printf("Unknown Kerberos option %d\r\n", data[-1]);
416 		return;
417 	}
418 }
419 
420 	int
421 kerberos5_status(ap, name, level)
422 	Authenticator *ap;
423 	char *name;
424 	int level;
425 {
426 	if (level < AUTH_USER)
427 		return(level);
428 
429 	if (UserNameRequested &&
430 	    krb5_kuserok(authdat->ticket->enc_part2->client, UserNameRequested))
431 	{
432 		strcpy(name, UserNameRequested);
433 		return(AUTH_VALID);
434 	} else
435 		return(AUTH_USER);
436 }
437 
438 #define	BUMP(buf, len)		while (*(buf)) {++(buf), --(len);}
439 #define	ADDC(buf, len, c)	if ((len) > 0) {*(buf)++ = (c); --(len));}
440 
441 	void
442 kerberos5_printsub(data, cnt, buf, buflen)
443 	unsigned char *data, *buf;
444 	int cnt, buflen;
445 {
446 	char lbuf[32];
447 	register int i;
448 
449 	buf[buflen-1] = '\0';		/* make sure its NULL terminated */
450 	buflen -= 1;
451 
452 	switch(data[3]) {
453 	case KRB_NAME:			/* Name to authenticate for */
454 		strncpy(buf, " NAME ", buflen);
455 		goto common;
456 
457 	case KRB_REJECT:		/* Rejected (reason might follow) */
458 		strncpy(buf, " REJECT ", buflen);
459 		goto common;
460 
461 	case KRB_ACCEPT:		/* Accepted (name might follow) */
462 		strncpy(buf, " ACCEPT ", buflen);
463 	common:
464 		BUMP(buf, buflen);
465 		if (cnt <= 4)
466 			break;
467 		ADDC(buf, buflen, '"');
468 		for (i = 4; i < cnt; i++)
469 			ADDC(buf, buflen, data[i]);
470 		ADDC(buf, buflen, '"');
471 		ADDC(buf, buflen, '\0');
472 		break;
473 
474 	case KRB_AUTH:			/* Authentication data follows */
475 		strncpy(buf, " AUTH", buflen);
476 		goto common2;
477 
478 	case KRB_NEWKEY:		/* A new session key follows */
479 		strncpy(buf, " NEWKEY", buflen);
480 		goto common2;
481 
482 	default:
483 		sprintf(lbuf, " %d (unknown)", data[3]);
484 		strncpy(buf, lbuf, buflen);
485 	common2:
486 		BUMP(buf, buflen);
487 		for (i = 4; i < cnt; i++) {
488 			sprintf(lbuf, " %d", data[i]);
489 			strncpy(buf, lbuf, buflen);
490 			BUMP(buf, buflen);
491 		}
492 		break;
493 	}
494 }
495 #endif
496