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