xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.bin/telnet/enc_des.c (revision 66582b606a8194f7f3ba5b3a3a6dca5b0d346361)
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
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
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
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
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
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
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
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
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
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
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
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
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