1 /*
2 * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 #pragma ident "%Z%%M% %I% %E% SMI"
7
8 /*
9 * usr/src/cmd/cmd-inet/usr.bin/telnet/enc_des.c
10 */
11
12 /*
13 * Copyright (c) 1991, 1993
14 * The Regents of the University of California. All rights reserved.
15 *
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
18 * are met:
19 * 1. Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
24 * 3. All advertising materials mentioning features or use of this software
25 * must display the following acknowledgement:
26 * This product includes software developed by the University of
27 * California, Berkeley and its contributors.
28 * 4. Neither the name of the University nor the names of its contributors
29 * may be used to endorse or promote products derived from this software
30 * without specific prior written permission.
31 *
32 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 * SUCH DAMAGE.
43 */
44
45 /*
46 * Copyright (C) 1998 by the FundsXpress, INC.
47 *
48 * All rights reserved.
49 *
50 * Export of this software from the United States of America may require
51 * a specific license from the United States Government. It is the
52 * responsibility of any person or organization contemplating export to
53 * obtain such a license before exporting.
54 *
55 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
56 * distribute this software and its documentation for any purpose and
57 * without fee is hereby granted, provided that the above copyright
58 * notice appear in all copies and that both that copyright notice and
59 * this permission notice appear in supporting documentation, and that
60 * the name of FundsXpress. not be used in advertising or publicity pertaining
61 * to distribution of the software without specific, written prior
62 * permission. FundsXpress makes no representations about the suitability of
63 * this software for any purpose. It is provided "as is" without express
64 * or implied warranty.
65 *
66 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
67 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
68 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
69 */
70
71 /* based on @(#)enc_des.c 8.1 (Berkeley) 6/4/93 */
72
73 #include <krb5.h>
74 #include <stdio.h>
75 #include <arpa/telnet.h>
76
77 #ifdef __STDC__
78 #include <stdlib.h>
79 #endif
80
81 #include "externs.h"
82
83 extern boolean_t encrypt_debug_mode;
84 extern krb5_context telnet_context;
85
86 #define KEYFLAG_SHIFT 2
87 #define SHIFT_VAL(a, b) (KEYFLAG_SHIFT*((a)+((b)*2)))
88
89 static struct _fb {
90 Block temp_feed;
91 int state[2]; /* state for each direction */
92 int keyid[2]; /* keyid for each direction */
93 int once;
94 unsigned char fb_feed[64];
95 boolean_t need_start;
96 boolean_t validkey;
97 struct stinfo {
98 Block str_output;
99 Block str_feed;
100 Block str_iv;
101 unsigned char str_keybytes[DES_BLOCKSIZE];
102 krb5_keyblock str_key;
103 int str_index;
104 int str_flagshift;
105 } streams[2]; /* one for encrypt, one for decrypt */
106 } des_cfb;
107
108 static void cfb64_stream_iv(Block, struct stinfo *);
109 static void cfb64_stream_key(Block, struct stinfo *);
110
111 static void
ecb_encrypt(struct stinfo * stp,Block in,Block out)112 ecb_encrypt(struct stinfo *stp, Block in, Block out)
113 {
114 krb5_error_code code;
115 krb5_data din;
116 krb5_enc_data dout;
117
118 din.length = DES_BLOCKSIZE;
119 din.data = (char *)in;
120
121 dout.ciphertext.length = DES_BLOCKSIZE;
122 dout.ciphertext.data = (char *)out;
123 /* this is a kerberos enctype, not a telopt enctype */
124 dout.enctype = ENCTYPE_UNKNOWN;
125
126 code = krb5_c_encrypt(telnet_context, &stp->str_key, NULL, NULL,
127 &din, &dout);
128 if (code)
129 (void) fprintf(stderr, gettext(
130 "Error encrypting stream data (%s)\r\n"), code);
131 }
132
133 void
cfb64_init(void)134 cfb64_init(void)
135 {
136 register struct _fb *fbp = &des_cfb;
137
138 (void) memset((void *)fbp, 0, sizeof (*fbp));
139 fbp->state[0] = des_cfb.state[1] = ENCR_STATE_FAILED;
140 fbp->fb_feed[0] = IAC;
141 fbp->fb_feed[1] = SB;
142 fbp->fb_feed[2] = TELOPT_ENCRYPT;
143 fbp->fb_feed[3] = ENCRYPT_IS;
144
145 fbp->fb_feed[4] = TELOPT_ENCTYPE_DES_CFB64;
146 fbp->streams[TELNET_DIR_DECRYPT].str_flagshift =
147 SHIFT_VAL(0, CFB);
148 fbp->streams[TELNET_DIR_ENCRYPT].str_flagshift =
149 SHIFT_VAL(1, CFB);
150 }
151
152
153 /*
154 * Returns:
155 * -1: some error. Negotiation is done, encryption not ready.
156 * 0: Successful, initial negotiation all done.
157 * 1: successful, negotiation not done yet.
158 * 2: Not yet. Other things (like getting the key from
159 * Kerberos) have to happen before we can continue.
160 */
161 int
cfb64_start(int dir)162 cfb64_start(int dir)
163 {
164 struct _fb *fbp = &des_cfb;
165 int x;
166 unsigned char *p;
167 register int state;
168
169 switch (dir) {
170 case TELNET_DIR_DECRYPT:
171 /*
172 * This is simply a request to have the other side
173 * start output (our input). He will negotiate an
174 * IV so we need not look for it.
175 */
176 state = fbp->state[dir];
177 if (state == ENCR_STATE_FAILED)
178 state = ENCR_STATE_IN_PROGRESS;
179 break;
180
181 case TELNET_DIR_ENCRYPT:
182 state = fbp->state[dir];
183 if (state == ENCR_STATE_FAILED)
184 state = ENCR_STATE_IN_PROGRESS;
185 else if ((state & ENCR_STATE_NO_SEND_IV) == 0)
186 break;
187
188 if (!fbp->validkey) {
189 fbp->need_start = B_TRUE;
190 break;
191 }
192 state &= ~ENCR_STATE_NO_SEND_IV;
193 state |= ENCR_STATE_NO_RECV_IV;
194 if (encrypt_debug_mode)
195 (void) printf(gettext("Creating new feed\r\n"));
196 /*
197 * Create a random feed and send it over.
198 */
199 {
200 krb5_data d;
201 krb5_error_code code;
202
203 d.data = (char *)fbp->temp_feed;
204 d.length = sizeof (fbp->temp_feed);
205
206 code = krb5_c_random_make_octets(telnet_context, &d);
207 if (code != 0)
208 return (ENCR_STATE_FAILED);
209 }
210
211 p = fbp->fb_feed + 3;
212 *p++ = ENCRYPT_IS;
213 p++;
214 *p++ = FB64_IV;
215 for (x = 0; x < sizeof (Block); ++x) {
216 if ((*p++ = fbp->temp_feed[x]) == IAC)
217 *p++ = IAC;
218 }
219 *p++ = IAC;
220 *p++ = SE;
221 printsub('>', &fbp->fb_feed[2], p - &fbp->fb_feed[2]);
222 (void) net_write(fbp->fb_feed, p - fbp->fb_feed);
223 break;
224 default:
225 return (ENCR_STATE_FAILED);
226 }
227 return (fbp->state[dir] = state);
228 }
229
230 /*
231 * Returns:
232 * -1: some error. Negotiation is done, encryption not ready.
233 * 0: Successful, initial negotiation all done.
234 * 1: successful, negotiation not done yet.
235 */
236 int
cfb64_is(unsigned char * data,int cnt)237 cfb64_is(unsigned char *data, int cnt)
238 {
239 unsigned char *p;
240 struct _fb *fbp = &des_cfb;
241 register int state = fbp->state[TELNET_DIR_DECRYPT];
242
243 if (cnt-- < 1)
244 goto failure;
245
246 switch (*data++) {
247 case FB64_IV:
248 if (cnt != sizeof (Block)) {
249 if (encrypt_debug_mode)
250 (void) printf(gettext(
251 "CFB64: initial vector failed "
252 "on size\r\n"));
253 state = ENCR_STATE_FAILED;
254 goto failure;
255 }
256
257 if (encrypt_debug_mode)
258 (void) printf(gettext(
259 "CFB64: initial vector received\r\n"));
260
261 if (encrypt_debug_mode)
262 (void) printf(gettext(
263 "Initializing Decrypt stream\r\n"));
264
265 cfb64_stream_iv((void *)data,
266 &fbp->streams[TELNET_DIR_DECRYPT]);
267
268 p = fbp->fb_feed + 3;
269 *p++ = ENCRYPT_REPLY;
270 p++;
271 *p++ = FB64_IV_OK;
272 *p++ = IAC;
273 *p++ = SE;
274 printsub('>', &fbp->fb_feed[2], p - &fbp->fb_feed[2]);
275 (void) net_write(fbp->fb_feed, p - fbp->fb_feed);
276
277 state = fbp->state[TELNET_DIR_DECRYPT] = ENCR_STATE_IN_PROGRESS;
278 break;
279
280 default:
281 if (encrypt_debug_mode) {
282 (void) printf(gettext(
283 "Unknown option type: %d\r\n"), *(data-1));
284 printd(data, cnt);
285 (void) printf("\r\n");
286 }
287 /* FALL THROUGH */
288 failure:
289 /*
290 * We failed. Send an FB64_IV_BAD option
291 * to the other side so it will know that
292 * things failed.
293 */
294 p = fbp->fb_feed + 3;
295 *p++ = ENCRYPT_REPLY;
296 p++;
297 *p++ = FB64_IV_BAD;
298 *p++ = IAC;
299 *p++ = SE;
300 printsub('>', &fbp->fb_feed[2], p - &fbp->fb_feed[2]);
301 (void) net_write(fbp->fb_feed, p - fbp->fb_feed);
302
303 break;
304 }
305 return (fbp->state[TELNET_DIR_DECRYPT] = state);
306 }
307
308 /*
309 * Returns:
310 * -1: some error. Negotiation is done, encryption not ready.
311 * 0: Successful, initial negotiation all done.
312 * 1: successful, negotiation not done yet.
313 */
314 int
cfb64_reply(unsigned char * data,int cnt)315 cfb64_reply(unsigned char *data, int cnt)
316 {
317 struct _fb *fbp = &des_cfb;
318 register int state = fbp->state[TELNET_DIR_ENCRYPT];
319
320 if (cnt-- < 1)
321 goto failure;
322
323 switch (*data++) {
324 case FB64_IV_OK:
325 cfb64_stream_iv(fbp->temp_feed,
326 &fbp->streams[TELNET_DIR_ENCRYPT]);
327 if (state == ENCR_STATE_FAILED)
328 state = ENCR_STATE_IN_PROGRESS;
329 state &= ~ENCR_STATE_NO_RECV_IV;
330 encrypt_send_keyid(TELNET_DIR_ENCRYPT,
331 (unsigned char *)"\0", 1, 1);
332 break;
333
334 case FB64_IV_BAD:
335 (void) memset(fbp->temp_feed, 0, sizeof (Block));
336 cfb64_stream_iv(fbp->temp_feed,
337 &fbp->streams[TELNET_DIR_ENCRYPT]);
338 state = ENCR_STATE_FAILED;
339 break;
340
341 default:
342 if (encrypt_debug_mode) {
343 (void) printf(gettext(
344 "Unknown option type: %d\r\n"), data[-1]);
345 printd(data, cnt);
346 (void) printf("\r\n");
347 }
348 /* FALL THROUGH */
349 failure:
350 state = ENCR_STATE_FAILED;
351 break;
352 }
353 return (fbp->state[TELNET_DIR_ENCRYPT] = state);
354 }
355
356 void
cfb64_session(Session_Key * key)357 cfb64_session(Session_Key *key)
358 {
359 struct _fb *fbp = &des_cfb;
360
361 if (!key || key->type != SK_DES) {
362 if (encrypt_debug_mode)
363 (void) printf(gettext(
364 "Can't set DES's session key (%d != %d)\r\n"),
365 key ? key->type : -1, SK_DES);
366 return;
367 }
368
369 fbp->validkey = B_TRUE;
370
371 cfb64_stream_key(key->data, &fbp->streams[TELNET_DIR_ENCRYPT]);
372 cfb64_stream_key(key->data, &fbp->streams[TELNET_DIR_DECRYPT]);
373
374 /*
375 * Now look to see if cfb64_start() was was waiting for
376 * the key to show up. If so, go ahead an call it now
377 * that we have the key.
378 */
379 if (fbp->need_start) {
380 fbp->need_start = B_FALSE;
381 (void) cfb64_start(TELNET_DIR_ENCRYPT);
382 }
383 }
384
385 /*
386 * We only accept a keyid of 0. If we get a keyid of
387 * 0, then mark the state as SUCCESS.
388 */
389 int
cfb64_keyid(dir,kp,lenp)390 cfb64_keyid(dir, kp, lenp)
391 int dir, *lenp;
392 unsigned char *kp;
393 {
394 struct _fb *fbp = &des_cfb;
395 register int state = fbp->state[dir];
396
397 if (*lenp != 1 || (*kp != '\0')) {
398 *lenp = 0;
399 return (state);
400 }
401
402 if (state == ENCR_STATE_FAILED)
403 state = ENCR_STATE_IN_PROGRESS;
404
405 state &= ~ENCR_STATE_NO_KEYID;
406
407 return (fbp->state[dir] = state);
408 }
409
410 /*
411 * Print ENCRYPT suboptions to NetTrace when "set opt" is used
412 */
413 void
cfb64_printsub(unsigned char * data,int cnt,unsigned char * buf,int buflen)414 cfb64_printsub(unsigned char *data, int cnt, unsigned char *buf, int buflen)
415 {
416 char lbuf[ENCR_LBUF_BUFSIZ];
417 register int i;
418 char *cp;
419 unsigned char type[] = "CFB64";
420
421 buf[buflen-1] = '\0'; /* make sure it's NULL terminated */
422 buflen -= 1;
423
424 switch (data[2]) {
425 case FB64_IV:
426 (void) snprintf(lbuf, ENCR_LBUF_BUFSIZ, "%s_IV", type);
427 cp = lbuf;
428 goto common;
429
430 case FB64_IV_OK:
431 (void) snprintf(lbuf, ENCR_LBUF_BUFSIZ, "%s_IV_OK", type);
432 cp = lbuf;
433 goto common;
434
435 case FB64_IV_BAD:
436 (void) snprintf(lbuf, ENCR_LBUF_BUFSIZ, "%s_IV_BAD", type);
437 cp = lbuf;
438 goto common;
439
440 default:
441 (void) snprintf(lbuf, ENCR_LBUF_BUFSIZ, " %d (unknown)",
442 data[2]);
443 cp = lbuf;
444 common:
445 for (; (buflen > 0) && (*buf = *cp++); buf++)
446 buflen--;
447 for (i = 3; i < cnt; i++) {
448 (void) snprintf(lbuf, ENCR_LBUF_BUFSIZ, " %d", data[i]);
449 for (cp = lbuf; (buflen > 0) && (*buf = *cp++); buf++)
450 buflen--;
451 }
452 break;
453 }
454 }
455
456
457 static void
cfb64_stream_iv(Block seed,register struct stinfo * stp)458 cfb64_stream_iv(Block seed, register struct stinfo *stp)
459 {
460 (void) memcpy((void *)stp->str_iv, (void *)seed, sizeof (Block));
461 (void) memcpy((void *)stp->str_output, (void *)seed, sizeof (Block));
462
463 stp->str_index = sizeof (Block);
464 }
465
466 void
cfb64_stream_key(Block key,register struct stinfo * stp)467 cfb64_stream_key(Block key, register struct stinfo *stp)
468 {
469 (void) memcpy((void *)stp->str_keybytes, (void *)key, sizeof (Block));
470 stp->str_key.length = DES_BLOCKSIZE;
471 stp->str_key.contents = stp->str_keybytes;
472 /*
473 * the original version of this code uses des ecb mode, but
474 * it only ever does one block at a time. cbc with a zero iv
475 * is identical
476 */
477 /* this is a kerberos enctype, not a telopt enctype */
478 stp->str_key.enctype = ENCTYPE_DES_CBC_RAW;
479
480 (void) memcpy((void *)stp->str_output, (void *)stp->str_iv,
481 sizeof (Block));
482
483 stp->str_index = sizeof (Block);
484 }
485
486 /*
487 * DES 64 bit Cipher Feedback
488 *
489 * key --->+-----+
490 * +->| DES |--+
491 * | +-----+ |
492 * | v
493 * INPUT --(--------->(+)+---> DATA
494 * | |
495 * +-------------+
496 *
497 *
498 * Given:
499 * iV: Initial vector, 64 bits (8 bytes) long.
500 * Dn: the nth chunk of 64 bits (8 bytes) of data to encrypt (decrypt).
501 * On: the nth chunk of 64 bits (8 bytes) of encrypted (decrypted) output.
502 *
503 * V0 = DES(iV, key)
504 * On = Dn ^ Vn
505 * V(n+1) = DES(On, key)
506 */
507
508 void
cfb64_encrypt(register unsigned char * s,int c)509 cfb64_encrypt(register unsigned char *s, int c)
510 {
511 register struct stinfo *stp =
512 &des_cfb.streams[TELNET_DIR_ENCRYPT];
513 register int index;
514
515 index = stp->str_index;
516 while (c-- > 0) {
517 if (index == sizeof (Block)) {
518 Block b;
519 ecb_encrypt(stp, stp->str_output, b);
520 (void) memcpy((void *)stp->str_feed, (void *)b,
521 sizeof (Block));
522 index = 0;
523 }
524
525 /* On encryption, we store (feed ^ data) which is cypher */
526 *s = stp->str_output[index] = (stp->str_feed[index] ^ *s);
527 s++;
528 index++;
529 }
530 stp->str_index = index;
531 }
532
533 int
cfb64_decrypt(int data)534 cfb64_decrypt(int data)
535 {
536 register struct stinfo *stp =
537 &des_cfb.streams[TELNET_DIR_DECRYPT];
538 int index;
539
540 if (data == -1) {
541 /*
542 * Back up one byte. It is assumed that we will
543 * never back up more than one byte. If we do, this
544 * may or may not work.
545 */
546 if (stp->str_index)
547 --stp->str_index;
548 return (0);
549 }
550
551 index = stp->str_index++;
552 if (index == sizeof (Block)) {
553 Block b;
554 ecb_encrypt(stp, stp->str_output, b);
555 (void) memcpy((void *)stp->str_feed, (void *)b, sizeof (Block));
556 stp->str_index = 1; /* Next time will be 1 */
557 index = 0; /* But now use 0 */
558 }
559
560 /* On decryption we store (data) which is cypher. */
561 stp->str_output[index] = data;
562 return (data ^ stp->str_feed[index]);
563 }
564