Commit | Line | Data |
---|---|---|
058bd129 WJ |
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[] = "@(#)krb_des.c 5.1 (Berkeley) 2/28/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 | #if defined(AUTHENTICATE) && defined(ENCRYPT) && defined(KRBDES_ENCRYPT) | |
59 | #include <arpa/telnet.h> | |
60 | #include <stdio.h> | |
61 | #ifdef __STDC__ | |
62 | #include <stdlib.h> | |
63 | #endif | |
64 | ||
65 | #include "encrypt.h" | |
66 | #include "key-proto.h" | |
67 | #include "misc-proto.h" | |
68 | ||
69 | extern encrypt_debug_mode; | |
70 | ||
71 | static Block krbdes_key = { 0 }; | |
72 | static Schedule krbdes_sched = { 0 }; | |
73 | static Block input_feed = { 0 }; | |
74 | static Block output_feed = { 0 }; | |
75 | static Block temp_feed = { 0 }; | |
76 | ||
77 | static unsigned char str_feed[64] = { IAC, SB, TELOPT_ENCRYPT, | |
78 | ENCRYPT_IS, | |
79 | ENCTYPE_KRBDES, }; | |
80 | ||
81 | void | |
82 | krbdes_init(server) | |
83 | int server; | |
84 | { | |
85 | bzero((void *)krbdes_key, sizeof(Block)); | |
86 | bzero((void *)krbdes_sched, sizeof(Schedule)); | |
87 | bzero((void *)input_feed, sizeof(Block)); | |
88 | bzero((void *)output_feed, sizeof(Block)); | |
89 | } | |
90 | ||
91 | #define KRBDES_FEED_INIT 1 | |
92 | #define KRBDES_FEED_VRFY 2 | |
93 | #define KRBDES_FEED_FAIL 3 | |
94 | #define KRBDES_FEED_OK 4 | |
95 | #define KRBDES_FEED_VRFY_FAIL 5 | |
96 | ||
97 | /* | |
98 | * Returns: | |
99 | * -1: some error. Negotiation is done, encryption not ready. | |
100 | * 0: Successful, initial negotiation all done. | |
101 | * 1: successful, negotiation not done yet. | |
102 | * 2: Not yet. Other things (like getting the key from | |
103 | * Kerberos) have to happen before we can continue. | |
104 | */ | |
105 | ||
106 | ||
107 | #define NOT_YET 2 | |
108 | #define IN_PROGRESS 1 | |
109 | #define SUCCESS 0 | |
110 | #define FAILED -1 | |
111 | ||
112 | int state[2] = { NOT_YET, NOT_YET }; | |
113 | #define ENCRYPT_STATE 0 | |
114 | #define DECRYPT_STATE 1 | |
115 | ||
116 | /* | |
117 | * This is where we keep track of the fact that there has been a | |
118 | * call to krbdes_start(), but we haven't gotten a valid key from | |
119 | * kerberos, so that when (if) it does come in, we can continue with | |
120 | * the initial encryption startup. | |
121 | */ | |
122 | static int need_start; | |
123 | ||
124 | int | |
125 | krbdes_start(dir, server) | |
126 | int dir; | |
127 | int server; | |
128 | { | |
129 | Block b; | |
130 | int x; | |
131 | unsigned char *p; | |
132 | ||
133 | switch (dir) { | |
134 | case DIR_DECRYPT: | |
135 | /* | |
136 | * This is simply a request to have the other side | |
137 | * start output (our input). He will negotiate a | |
138 | * feed so we need not look for it. | |
139 | */ | |
140 | if (!VALIDKEY(krbdes_key)) | |
141 | return(state[DECRYPT_STATE]); | |
142 | if (state[DECRYPT_STATE] == NOT_YET) | |
143 | state[DECRYPT_STATE] = IN_PROGRESS; | |
144 | return(state[DECRYPT_STATE]); | |
145 | ||
146 | case DIR_ENCRYPT: | |
147 | if (!VALIDKEY(krbdes_key)) { | |
148 | need_start = 1; | |
149 | return(state[ENCRYPT_STATE]); | |
150 | } | |
151 | if (state[ENCRYPT_STATE] == IN_PROGRESS) | |
152 | return(IN_PROGRESS); | |
153 | if (VALIDKEY(output_feed)) | |
154 | return(SUCCESS); | |
155 | if (encrypt_debug_mode) | |
156 | printf("Creating new feed\r\n"); | |
157 | /* | |
158 | * Create a random feed and send it over encrypted. | |
159 | * The other side will decrypt it, xor in 'KRBDES' | |
160 | * and then recrypt it and send it back. | |
161 | */ | |
162 | des_new_random_key(temp_feed); | |
163 | des_ecb_encrypt(temp_feed, b, krbdes_sched, 1); | |
164 | str_feed[3] = ENCRYPT_IS; | |
165 | p = str_feed + 5; | |
166 | *p++ = KRBDES_FEED_INIT; | |
167 | for (x = 0; x < sizeof(Block); ++x) { | |
168 | if ((*p++ = b[x]) == IAC) | |
169 | *p++ = IAC; | |
170 | } | |
171 | *p++ = IAC; | |
172 | *p++ = SE; | |
173 | printsub('>', &str_feed[2], p - &str_feed[2]); | |
174 | net_write(str_feed, p - str_feed); | |
175 | temp_feed[0] ^= 'K'; | |
176 | temp_feed[1] ^= 'R'; | |
177 | temp_feed[2] ^= 'B'; | |
178 | temp_feed[3] ^= 'D'; | |
179 | temp_feed[4] ^= 'E'; | |
180 | temp_feed[5] ^= 'S'; | |
181 | state[ENCRYPT_STATE] = IN_PROGRESS; | |
182 | return(IN_PROGRESS); | |
183 | default: | |
184 | return(FAILED); | |
185 | } | |
186 | } | |
187 | ||
188 | /* | |
189 | * Returns: | |
190 | * -1: some error. Negotiation is done, encryption not ready. | |
191 | * 0: Successful, initial negotiation all done. | |
192 | * 1: successful, negotiation not done yet. | |
193 | */ | |
194 | ||
195 | int | |
196 | krbdes_is(data, cnt) | |
197 | unsigned char *data; | |
198 | int cnt; | |
199 | { | |
200 | int x; | |
201 | unsigned char *p; | |
202 | unsigned char *why = 0; | |
203 | Block b; | |
204 | ||
205 | if (cnt-- < 1) { | |
206 | why = (unsigned char *)"Bad IS suboption"; | |
207 | goto failure; | |
208 | } | |
209 | ||
210 | switch (*data++) { | |
211 | case KRBDES_FEED_INIT: | |
212 | if (cnt != sizeof(Block)) { | |
213 | if (encrypt_debug_mode) | |
214 | printf("Use Feed failed on size\r\n"); | |
215 | why = (unsigned char *)"Bad block size"; | |
216 | goto failure; | |
217 | } | |
218 | if (!VALIDKEY(krbdes_key)) { | |
219 | if (encrypt_debug_mode) | |
220 | printf("Use Feed failed on key\r\n"); | |
221 | why = (unsigned char *)"Invalid key"; | |
222 | goto failure; | |
223 | } | |
224 | if (encrypt_debug_mode) | |
225 | printf("Have a Use Feed\r\n"); | |
226 | ||
227 | bcopy((void *)data, (void *)b, sizeof(Block)); | |
228 | des_ecb_encrypt(b, input_feed, krbdes_sched, 0); | |
229 | ||
230 | input_feed[0] ^= 'K'; | |
231 | input_feed[1] ^= 'R'; | |
232 | input_feed[2] ^= 'B'; | |
233 | input_feed[3] ^= 'D'; | |
234 | input_feed[4] ^= 'E'; | |
235 | input_feed[5] ^= 'S'; | |
236 | ||
237 | des_ecb_encrypt(input_feed, b, krbdes_sched, 1); | |
238 | str_feed[3] = ENCRYPT_REPLY; | |
239 | p = str_feed + 5; | |
240 | *p++ = KRBDES_FEED_VRFY; | |
241 | for (x = 0; x < sizeof(Block); ++x) { | |
242 | if ((*p++ = b[x]) == IAC) | |
243 | *p++ = IAC; | |
244 | } | |
245 | *p++ = IAC; | |
246 | *p++ = SE; | |
247 | printsub('>', &str_feed[2], p - &str_feed[2]); | |
248 | net_write(str_feed, p - str_feed); | |
249 | if (encrypt_debug_mode) | |
250 | printf("Initializing Decrypt stream\r\n"); | |
251 | key_stream_init(krbdes_key, input_feed, DIR_DECRYPT); | |
252 | state[DECRYPT_STATE] = IN_PROGRESS; | |
253 | return(IN_PROGRESS); | |
254 | ||
255 | case KRBDES_FEED_OK: | |
256 | state[DECRYPT_STATE] = SUCCESS; | |
257 | return(SUCCESS); | |
258 | ||
259 | case KRBDES_FEED_FAIL: | |
260 | state[DECRYPT_STATE] = FAILED; | |
261 | return(FAILED); | |
262 | ||
263 | default: | |
264 | if (encrypt_debug_mode) { | |
265 | printf("Unknown option type: %d\r\n", data[-1]); | |
266 | printd(data, cnt); | |
267 | printf("\r\n"); | |
268 | } | |
269 | why = (unsigned char *)"Unknown sub-suboption"; | |
270 | failure: | |
271 | /* | |
272 | * We failed. Send an empty KRBDES_FEED_VRFY option | |
273 | * to the other side so it will know that things | |
274 | * failed. | |
275 | */ | |
276 | str_feed[3] = ENCRYPT_REPLY; | |
277 | p = str_feed + 5; | |
278 | *p++ = KRBDES_FEED_VRFY_FAIL; | |
279 | if (why) { | |
280 | while (*why) { | |
281 | if ((*p++ = *why++) == IAC) | |
282 | *p++ = IAC; | |
283 | } | |
284 | } | |
285 | *p++ = IAC; | |
286 | *p++ = SE; | |
287 | printsub('>', &str_feed[2], p - &str_feed[2]); | |
288 | net_write(str_feed, p - str_feed); | |
289 | state[DECRYPT_STATE] = FAILED; | |
290 | return(FAILED); | |
291 | } | |
292 | } | |
293 | ||
294 | /* | |
295 | * Returns: | |
296 | * -1: some error. Negotiation is done, encryption not ready. | |
297 | * 0: Successful, initial negotiation all done. | |
298 | * 1: successful, negotiation not done yet. | |
299 | */ | |
300 | ||
301 | int | |
302 | krbdes_reply(data, cnt) | |
303 | unsigned char *data; | |
304 | int cnt; | |
305 | { | |
306 | int x; | |
307 | unsigned char *p; | |
308 | unsigned char *why = 0; | |
309 | Block b; | |
310 | ||
311 | if (cnt-- < 1) { | |
312 | why = (unsigned char *)"Bad REPLY suboption"; | |
313 | goto failure; | |
314 | } | |
315 | ||
316 | switch (*data++) { | |
317 | case KRBDES_FEED_VRFY: | |
318 | if (cnt != sizeof(Block)) { | |
319 | if (encrypt_debug_mode) | |
320 | printf("Have Feed failed on size\r\n"); | |
321 | why = (unsigned char *)"Bad block size"; | |
322 | goto failure; | |
323 | } | |
324 | if (!VALIDKEY(krbdes_key)) { | |
325 | if (encrypt_debug_mode) | |
326 | printf("Have Feed failed on key\r\n"); | |
327 | why = (unsigned char *)"Invalid key"; | |
328 | goto failure; | |
329 | } | |
330 | if (encrypt_debug_mode) | |
331 | printf("Have a Test Feed\r\n"); | |
332 | ||
333 | bcopy((void *)data, (void *)b, sizeof(Block)); | |
334 | des_ecb_encrypt(b, output_feed, krbdes_sched, 0); | |
335 | if (!SAMEKEY(output_feed, temp_feed)) { | |
336 | if (encrypt_debug_mode) | |
337 | printf("Have Feed failed on challange\r\n"); | |
338 | bzero((void *)output_feed, sizeof(Block)); | |
339 | why = (unsigned char*)"Challange decrypt failed"; | |
340 | goto failure; | |
341 | } | |
342 | if (encrypt_debug_mode) | |
343 | printf("Initializing Encrypt stream\r\n"); | |
344 | key_stream_init(krbdes_key, output_feed, DIR_ENCRYPT); | |
345 | ||
346 | str_feed[3] = ENCRYPT_IS; | |
347 | p = str_feed + 5; | |
348 | *p++ = KRBDES_FEED_OK; | |
349 | *p++ = IAC; | |
350 | *p++ = SE; | |
351 | printsub('>', &str_feed[2], p - &str_feed[2]); | |
352 | net_write(str_feed, p - str_feed); | |
353 | state[ENCRYPT_STATE] = SUCCESS; | |
354 | return(SUCCESS); | |
355 | ||
356 | default: | |
357 | if (encrypt_debug_mode) { | |
358 | printf("Unknown option type: %d\r\n", data[-1]); | |
359 | printd(data, cnt); | |
360 | printf("\r\n"); | |
361 | } | |
362 | why = (unsigned char *)"Unknown sub-suboption"; | |
363 | failure: | |
364 | str_feed[3] = ENCRYPT_IS; | |
365 | p = str_feed + 5; | |
366 | *p++ = KRBDES_FEED_FAIL; | |
367 | if (why) { | |
368 | while (*why) { | |
369 | if ((*p++ = *why++) == IAC) | |
370 | *p++ = IAC; | |
371 | } | |
372 | } | |
373 | *p++ = IAC; | |
374 | *p++ = SE; | |
375 | printsub('>', &str_feed[2], p - &str_feed[2]); | |
376 | net_write(str_feed, p - str_feed); | |
377 | state[ENCRYPT_STATE] = FAILED; | |
378 | return(FAILED); | |
379 | } | |
380 | } | |
381 | ||
382 | void | |
383 | krbdes_encrypt(s, c) | |
384 | unsigned char *s; | |
385 | int c; | |
386 | { | |
387 | while (c-- > 0) { | |
388 | *s = key_stream(DIR_ENCRYPT, *s); | |
389 | ++s; | |
390 | } | |
391 | } | |
392 | ||
393 | int | |
394 | krbdes_decrypt(c) | |
395 | int c; | |
396 | { | |
397 | return(key_stream(DIR_DECRYPT, c)); | |
398 | #ifdef ndef | |
399 | ||
400 | if (c == -1) { | |
401 | ++use; | |
402 | return(0); | |
403 | } | |
404 | if (use) { | |
405 | use = 0; | |
406 | } else { | |
407 | last = key_stream(DIR_DECRYPT, c); | |
408 | } | |
409 | ||
410 | return(last ^ c); | |
411 | #endif | |
412 | } | |
413 | ||
414 | void | |
415 | krbdes_session(key, server) | |
416 | Session_Key *key; | |
417 | int server; | |
418 | { | |
419 | static once = 1; | |
420 | ||
421 | if (!key || key->type != SK_DES) { | |
422 | if (encrypt_debug_mode) | |
423 | printf("Can't set krbdes's session key (%d != %d)\r\n", | |
424 | key ? key->type : -1, SK_DES); | |
425 | return; | |
426 | } | |
427 | bcopy((void *)key->data, (void *)krbdes_key, sizeof(Block)); | |
428 | if (once) { | |
429 | des_set_random_generator_seed(krbdes_key); | |
430 | once = 0; | |
431 | } | |
432 | des_key_sched(krbdes_key, krbdes_sched); | |
433 | /* | |
434 | * Now look to see if krbdes_start() was was waiting for | |
435 | * the key to show up. If so, go ahead an call it now | |
436 | * that we have the key. | |
437 | */ | |
438 | if (need_start) { | |
439 | krbdes_start(DIR_ENCRYPT, server); | |
440 | need_start = 0; | |
441 | } | |
442 | } | |
443 | ||
444 | void | |
445 | krbdes_printsub(data, cnt, buf, buflen) | |
446 | unsigned char *data, *buf; | |
447 | int cnt, buflen; | |
448 | { | |
449 | char lbuf[32]; | |
450 | register int i; | |
451 | char *cp; | |
452 | ||
453 | buf[buflen-1] = '\0'; /* make sure it's NULL terminated */ | |
454 | buflen -= 1; | |
455 | ||
456 | switch(data[2]) { | |
457 | case KRBDES_FEED_OK: | |
458 | cp = "FEED_OK"; | |
459 | goto common1; | |
460 | case KRBDES_FEED_FAIL: | |
461 | cp = "FEED_FAIL"; | |
462 | common1: | |
463 | for (; (buflen > 0) && (*buf = *cp++); buf++) | |
464 | buflen--; | |
465 | if (cnt > 3) { | |
466 | if (buflen <= 0) | |
467 | break; | |
468 | *buf++ = ' '; | |
469 | if (--buflen <= 0) | |
470 | break; | |
471 | *buf++ = '"'; | |
472 | for (i = 3; i < cnt && buflen > 0; i++, buflen--) | |
473 | *buf++ = data[i]; | |
474 | if (buflen <= 0) | |
475 | break; | |
476 | *buf++ = '"'; | |
477 | --buflen; | |
478 | } | |
479 | if (buflen <= 0) | |
480 | break; | |
481 | *buf++ = '\0'; | |
482 | break; | |
483 | ||
484 | case KRBDES_FEED_INIT: | |
485 | cp = "FEED_INIT "; | |
486 | goto common2; | |
487 | ||
488 | case KRBDES_FEED_VRFY: | |
489 | cp = "FEED_VRFY "; | |
490 | goto common2; | |
491 | ||
492 | default: | |
493 | sprintf(lbuf, " %d (unknown)", data[2]); | |
494 | cp = lbuf; | |
495 | common2: | |
496 | for (; (buflen > 0) && (*buf = *cp++); buf++) | |
497 | buflen--; | |
498 | for (i = 3; i < cnt; i++) { | |
499 | sprintf(lbuf, " %d", data[i]); | |
500 | for (cp = lbuf; (buflen > 0) && (*buf = *cp++); buf++) | |
501 | buflen--; | |
502 | } | |
503 | break; | |
504 | } | |
505 | } | |
506 | ||
507 | static Block stream_output[2]; | |
508 | static Block stream_feed[2]; | |
509 | static Schedule stream_sched[2]; | |
510 | static int stream_index[2]; | |
511 | ||
512 | void | |
513 | key_stream_init(key, seed, dir) | |
514 | Block key; | |
515 | Block seed; | |
516 | int dir; | |
517 | { | |
518 | des_key_sched(key, stream_sched[--dir]); | |
519 | bcopy((void *)seed, (void *)stream_output[dir], sizeof(Block)); | |
520 | stream_index[dir] = sizeof(Block); | |
521 | } | |
522 | ||
523 | unsigned char | |
524 | key_stream(dir, data) | |
525 | int dir; | |
526 | int data; /* data de/encrypting */ | |
527 | { | |
528 | int index; | |
529 | ||
530 | if (data == -1) { | |
531 | /* Backpeddle */ | |
532 | if (stream_index[--dir]) | |
533 | --stream_index[dir]; | |
534 | return(0); | |
535 | } | |
536 | if ((index = stream_index[--dir]++) == sizeof(Block)) { | |
537 | Block b; | |
538 | des_ecb_encrypt(stream_output[dir], | |
539 | b, | |
540 | stream_sched[dir], 1); | |
541 | bcopy((void *)b, (void *)stream_feed[dir], sizeof(Block)); | |
542 | stream_index[dir] = 1; /* Next time will be 1 */ | |
543 | index = 0; /* But now use 0 */ | |
544 | } | |
545 | ||
546 | /* | |
547 | * On encryption, we store (feed ^ data) which is cypher | |
548 | * On decryption we store (data) which is cypher | |
549 | */ | |
550 | if (dir == DIR_DECRYPT) | |
551 | return(stream_output[dir][index] = | |
552 | (stream_feed[dir][index] ^ data)); | |
553 | else | |
554 | return((stream_output[dir][index] = data) ^ | |
555 | stream_feed[dir][index]); | |
556 | } | |
557 | #endif |