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