xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.bin/telnet/enc_des.c (revision 74ecdb5171c9f3673b9393b1a3dc6f3a65e93895)
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  * Copyright (c) 2016 by Delphix. All rights reserved.
74  */
75 
76 #include <krb5.h>
77 #include <stdio.h>
78 #include <arpa/telnet.h>
79 
80 #ifdef	__STDC__
81 #include <stdlib.h>
82 #endif
83 
84 #include "externs.h"
85 
86 extern	boolean_t encrypt_debug_mode;
87 extern	krb5_context telnet_context;
88 
89 #define	KEYFLAG_SHIFT	2
90 #define	SHIFT_VAL(a, b) (KEYFLAG_SHIFT*((a)+((b)*2)))
91 
92 static	struct _fb {
93 	Block temp_feed;
94 	int state[2];		/* state for each direction */
95 	int keyid[2];		/* keyid for each direction */
96 	int once;
97 	unsigned char fb_feed[64];
98 	boolean_t need_start;
99 	boolean_t validkey;
100 	struct stinfo {
101 		Block		str_output;
102 		Block		str_feed;
103 		Block		str_iv;
104 		unsigned char	str_keybytes[DES_BLOCKSIZE];
105 		krb5_keyblock	str_key;
106 		int		str_index;
107 		int		str_flagshift;
108 	} streams[2];		/* one for encrypt, one for decrypt */
109 } des_cfb;
110 
111 static	void cfb64_stream_iv(Block, struct stinfo *);
112 static	void cfb64_stream_key(Block, struct stinfo *);
113 
114 static void
115 ecb_encrypt(struct stinfo *stp, Block in, Block out)
116 {
117 	krb5_error_code code;
118 	krb5_data din;
119 	krb5_enc_data dout;
120 
121 	din.length = DES_BLOCKSIZE;
122 	din.data = (char *)in;
123 
124 	dout.ciphertext.length = DES_BLOCKSIZE;
125 	dout.ciphertext.data = (char *)out;
126 	/* this is a kerberos enctype, not a telopt enctype */
127 	dout.enctype = ENCTYPE_UNKNOWN;
128 
129 	code = krb5_c_encrypt(telnet_context, &stp->str_key, NULL, NULL,
130 		&din, &dout);
131 	if (code)
132 		(void) fprintf(stderr, gettext(
133 			"Error encrypting stream data (%s)\r\n"), code);
134 }
135 
136 void
137 cfb64_init(void)
138 {
139 	register struct _fb *fbp = &des_cfb;
140 
141 	(void) memset((void *)fbp, 0, sizeof (*fbp));
142 	fbp->state[0] = des_cfb.state[1] = ENCR_STATE_FAILED;
143 	fbp->fb_feed[0] = IAC;
144 	fbp->fb_feed[1] = SB;
145 	fbp->fb_feed[2] = TELOPT_ENCRYPT;
146 	fbp->fb_feed[3] = ENCRYPT_IS;
147 
148 	fbp->fb_feed[4] = TELOPT_ENCTYPE_DES_CFB64;
149 	fbp->streams[TELNET_DIR_DECRYPT].str_flagshift =
150 		SHIFT_VAL(0, CFB);
151 	fbp->streams[TELNET_DIR_ENCRYPT].str_flagshift =
152 		SHIFT_VAL(1, CFB);
153 }
154 
155 
156 /*
157  * Returns:
158  *	-1: some error.  Negotiation is done, encryption not ready.
159  *	 0: Successful, initial negotiation all done.
160  *	 1: successful, negotiation not done yet.
161  *	 2: Not yet.  Other things (like getting the key from
162  *	    Kerberos) have to happen before we can continue.
163  */
164 int
165 cfb64_start(int dir)
166 {
167 	struct _fb *fbp = &des_cfb;
168 	int x;
169 	unsigned char *p;
170 	register int state;
171 
172 	switch (dir) {
173 	case TELNET_DIR_DECRYPT:
174 		/*
175 		 * This is simply a request to have the other side
176 		 * start output (our input).  The other side will negotiate an
177 		 * IV so we need not look for it.
178 		 */
179 		state = fbp->state[dir];
180 		if (state == ENCR_STATE_FAILED)
181 			state = ENCR_STATE_IN_PROGRESS;
182 		break;
183 
184 	case TELNET_DIR_ENCRYPT:
185 		state = fbp->state[dir];
186 		if (state == ENCR_STATE_FAILED)
187 			state = ENCR_STATE_IN_PROGRESS;
188 		else if ((state & ENCR_STATE_NO_SEND_IV) == 0)
189 			break;
190 
191 		if (!fbp->validkey) {
192 			fbp->need_start = B_TRUE;
193 			break;
194 		}
195 		state &= ~ENCR_STATE_NO_SEND_IV;
196 		state |= ENCR_STATE_NO_RECV_IV;
197 		if (encrypt_debug_mode)
198 			(void) printf(gettext("Creating new feed\r\n"));
199 		/*
200 		 * Create a random feed and send it over.
201 		 */
202 		{
203 			krb5_data d;
204 			krb5_error_code code;
205 
206 			d.data = (char *)fbp->temp_feed;
207 			d.length = sizeof (fbp->temp_feed);
208 
209 			code = krb5_c_random_make_octets(telnet_context, &d);
210 			if (code != 0)
211 				return (ENCR_STATE_FAILED);
212 		}
213 
214 		p = fbp->fb_feed + 3;
215 		*p++ = ENCRYPT_IS;
216 		p++;
217 		*p++ = FB64_IV;
218 		for (x = 0; x < sizeof (Block); ++x) {
219 			if ((*p++ = fbp->temp_feed[x]) == IAC)
220 				*p++ = IAC;
221 		}
222 		*p++ = IAC;
223 		*p++ = SE;
224 		printsub('>', &fbp->fb_feed[2], p - &fbp->fb_feed[2]);
225 		(void) net_write(fbp->fb_feed, p - fbp->fb_feed);
226 		break;
227 	default:
228 		return (ENCR_STATE_FAILED);
229 	}
230 	return (fbp->state[dir] = state);
231 }
232 
233 /*
234  * Returns:
235  *	-1: some error.  Negotiation is done, encryption not ready.
236  *	 0: Successful, initial negotiation all done.
237  *	 1: successful, negotiation not done yet.
238  */
239 int
240 cfb64_is(unsigned char *data, int cnt)
241 {
242 	unsigned char *p;
243 	struct _fb *fbp = &des_cfb;
244 	register int state = fbp->state[TELNET_DIR_DECRYPT];
245 
246 	if (cnt-- < 1)
247 		goto failure;
248 
249 	switch (*data++) {
250 	case FB64_IV:
251 		if (cnt != sizeof (Block)) {
252 			if (encrypt_debug_mode)
253 				(void) printf(gettext(
254 					"CFB64: initial vector failed "
255 					"on size\r\n"));
256 			state = ENCR_STATE_FAILED;
257 			goto failure;
258 		}
259 
260 		if (encrypt_debug_mode)
261 			(void) printf(gettext(
262 				"CFB64: initial vector received\r\n"));
263 
264 		if (encrypt_debug_mode)
265 			(void) printf(gettext(
266 				"Initializing Decrypt stream\r\n"));
267 
268 		cfb64_stream_iv((void *)data,
269 			&fbp->streams[TELNET_DIR_DECRYPT]);
270 
271 		p = fbp->fb_feed + 3;
272 		*p++ = ENCRYPT_REPLY;
273 		p++;
274 		*p++ = FB64_IV_OK;
275 		*p++ = IAC;
276 		*p++ = SE;
277 		printsub('>', &fbp->fb_feed[2], p - &fbp->fb_feed[2]);
278 		(void) net_write(fbp->fb_feed, p - fbp->fb_feed);
279 
280 		state = fbp->state[TELNET_DIR_DECRYPT] = ENCR_STATE_IN_PROGRESS;
281 		break;
282 
283 	default:
284 		if (encrypt_debug_mode) {
285 			(void) printf(gettext(
286 				"Unknown option type: %d\r\n"), *(data-1));
287 			printd(data, cnt);
288 			(void) printf("\r\n");
289 		}
290 		/* FALL THROUGH */
291 	failure:
292 		/*
293 		 * We failed.  Send an FB64_IV_BAD option
294 		 * to the other side so it will know that
295 		 * things failed.
296 		 */
297 		p = fbp->fb_feed + 3;
298 		*p++ = ENCRYPT_REPLY;
299 		p++;
300 		*p++ = FB64_IV_BAD;
301 		*p++ = IAC;
302 		*p++ = SE;
303 		printsub('>', &fbp->fb_feed[2], p - &fbp->fb_feed[2]);
304 		(void) net_write(fbp->fb_feed, p - fbp->fb_feed);
305 
306 		break;
307 	}
308 	return (fbp->state[TELNET_DIR_DECRYPT] = state);
309 }
310 
311 /*
312  * Returns:
313  *	-1: some error.  Negotiation is done, encryption not ready.
314  *	 0: Successful, initial negotiation all done.
315  *	 1: successful, negotiation not done yet.
316  */
317 int
318 cfb64_reply(unsigned char *data, int cnt)
319 {
320 	struct _fb *fbp = &des_cfb;
321 	register int state = fbp->state[TELNET_DIR_ENCRYPT];
322 
323 	if (cnt-- < 1)
324 		goto failure;
325 
326 	switch (*data++) {
327 	case FB64_IV_OK:
328 		cfb64_stream_iv(fbp->temp_feed,
329 			&fbp->streams[TELNET_DIR_ENCRYPT]);
330 		if (state == ENCR_STATE_FAILED)
331 			state = ENCR_STATE_IN_PROGRESS;
332 		state &= ~ENCR_STATE_NO_RECV_IV;
333 		encrypt_send_keyid(TELNET_DIR_ENCRYPT,
334 			(unsigned char *)"\0", 1, 1);
335 		break;
336 
337 	case FB64_IV_BAD:
338 		(void) memset(fbp->temp_feed, 0, sizeof (Block));
339 		cfb64_stream_iv(fbp->temp_feed,
340 			&fbp->streams[TELNET_DIR_ENCRYPT]);
341 		state = ENCR_STATE_FAILED;
342 		break;
343 
344 	default:
345 		if (encrypt_debug_mode) {
346 			(void) printf(gettext(
347 				"Unknown option type: %d\r\n"), data[-1]);
348 			printd(data, cnt);
349 			(void) printf("\r\n");
350 		}
351 		/* FALL THROUGH */
352 	failure:
353 		state = ENCR_STATE_FAILED;
354 		break;
355 	}
356 	return (fbp->state[TELNET_DIR_ENCRYPT] = state);
357 }
358 
359 void
360 cfb64_session(Session_Key *key)
361 {
362 	struct _fb *fbp = &des_cfb;
363 
364 	if (!key || key->type != SK_DES) {
365 		if (encrypt_debug_mode)
366 		    (void) printf(gettext(
367 			"Can't set DES's session key (%d != %d)\r\n"),
368 			key ? key->type : -1, SK_DES);
369 		return;
370 	}
371 
372 	fbp->validkey = B_TRUE;
373 
374 	cfb64_stream_key(key->data, &fbp->streams[TELNET_DIR_ENCRYPT]);
375 	cfb64_stream_key(key->data, &fbp->streams[TELNET_DIR_DECRYPT]);
376 
377 	/*
378 	 * Now look to see if cfb64_start() was was waiting for
379 	 * the key to show up.  If so, go ahead an call it now
380 	 * that we have the key.
381 	 */
382 	if (fbp->need_start) {
383 		fbp->need_start = B_FALSE;
384 		(void) cfb64_start(TELNET_DIR_ENCRYPT);
385 	}
386 }
387 
388 /*
389  * We only accept a keyid of 0.  If we get a keyid of
390  * 0, then mark the state as SUCCESS.
391  */
392 int
393 cfb64_keyid(dir, kp, lenp)
394 	int dir, *lenp;
395 	unsigned char *kp;
396 {
397 	struct _fb *fbp = &des_cfb;
398 	register int state = fbp->state[dir];
399 
400 	if (*lenp != 1 || (*kp != '\0')) {
401 		*lenp = 0;
402 		return (state);
403 	}
404 
405 	if (state == ENCR_STATE_FAILED)
406 		state = ENCR_STATE_IN_PROGRESS;
407 
408 	state &= ~ENCR_STATE_NO_KEYID;
409 
410 	return (fbp->state[dir] = state);
411 }
412 
413 /*
414  * Print ENCRYPT suboptions to NetTrace when "set opt" is used
415  */
416 void
417 cfb64_printsub(unsigned char *data, int cnt, unsigned char *buf, int buflen)
418 {
419 	char lbuf[ENCR_LBUF_BUFSIZ];
420 	register int i;
421 	char *cp;
422 	unsigned char type[] = "CFB64";
423 
424 	buf[buflen-1] = '\0';		/* make sure it's NULL terminated */
425 	buflen -= 1;
426 
427 	switch (data[2]) {
428 	case FB64_IV:
429 		(void) snprintf(lbuf, ENCR_LBUF_BUFSIZ, "%s_IV", type);
430 		cp = lbuf;
431 		goto common;
432 
433 	case FB64_IV_OK:
434 		(void) snprintf(lbuf, ENCR_LBUF_BUFSIZ, "%s_IV_OK", type);
435 		cp = lbuf;
436 		goto common;
437 
438 	case FB64_IV_BAD:
439 		(void) snprintf(lbuf, ENCR_LBUF_BUFSIZ, "%s_IV_BAD", type);
440 		cp = lbuf;
441 		goto common;
442 
443 	default:
444 		(void) snprintf(lbuf, ENCR_LBUF_BUFSIZ, " %d (unknown)",
445 			data[2]);
446 		cp = lbuf;
447 	common:
448 		for (; (buflen > 0) && (*buf = *cp++); buf++)
449 			buflen--;
450 		for (i = 3; i < cnt; i++) {
451 			(void) snprintf(lbuf, ENCR_LBUF_BUFSIZ, " %d", data[i]);
452 			for (cp = lbuf; (buflen > 0) && (*buf = *cp++); buf++)
453 				buflen--;
454 		}
455 		break;
456 	}
457 }
458 
459 
460 static void
461 cfb64_stream_iv(Block seed, register struct stinfo *stp)
462 {
463 	(void) memcpy((void *)stp->str_iv,	(void *)seed, sizeof (Block));
464 	(void) memcpy((void *)stp->str_output,	(void *)seed, sizeof (Block));
465 
466 	stp->str_index = sizeof (Block);
467 }
468 
469 void
470 cfb64_stream_key(Block key, register struct stinfo *stp)
471 {
472 	(void) memcpy((void *)stp->str_keybytes, (void *)key, sizeof (Block));
473 	stp->str_key.length = DES_BLOCKSIZE;
474 	stp->str_key.contents = stp->str_keybytes;
475 	/*
476 	 * the original version of this code uses des ecb mode, but
477 	 * it only ever does one block at a time.  cbc with a zero iv
478 	 * is identical
479 	 */
480 	/* this is a kerberos enctype, not a telopt enctype */
481 	stp->str_key.enctype = ENCTYPE_DES_CBC_RAW;
482 
483 	(void) memcpy((void *)stp->str_output, (void *)stp->str_iv,
484 	    sizeof (Block));
485 
486 	stp->str_index = sizeof (Block);
487 }
488 
489 /*
490  * DES 64 bit Cipher Feedback
491  *
492  *     key --->+-----+
493  *          +->| DES |--+
494  *          |  +-----+  |
495  *	    |           v
496  *  INPUT --(--------->(+)+---> DATA
497  *          |             |
498  *	    +-------------+
499  *
500  *
501  * Given:
502  *	iV: Initial vector, 64 bits (8 bytes) long.
503  *	Dn: the nth chunk of 64 bits (8 bytes) of data to encrypt (decrypt).
504  *	On: the nth chunk of 64 bits (8 bytes) of encrypted (decrypted) output.
505  *
506  *	V0 = DES(iV, key)
507  *	On = Dn ^ Vn
508  *	V(n+1) = DES(On, key)
509  */
510 
511 void
512 cfb64_encrypt(register unsigned char *s, int c)
513 {
514 	register struct stinfo *stp =
515 		&des_cfb.streams[TELNET_DIR_ENCRYPT];
516 	register int index;
517 
518 	index = stp->str_index;
519 	while (c-- > 0) {
520 		if (index == sizeof (Block)) {
521 			Block b;
522 			ecb_encrypt(stp, stp->str_output, b);
523 			(void) memcpy((void *)stp->str_feed, (void *)b,
524 				sizeof (Block));
525 			index = 0;
526 		}
527 
528 		/* On encryption, we store (feed ^ data) which is cypher */
529 		*s = stp->str_output[index] = (stp->str_feed[index] ^ *s);
530 		s++;
531 		index++;
532 	}
533 	stp->str_index = index;
534 }
535 
536 int
537 cfb64_decrypt(int data)
538 {
539 	register struct stinfo *stp =
540 		&des_cfb.streams[TELNET_DIR_DECRYPT];
541 	int index;
542 
543 	if (data == -1) {
544 		/*
545 		 * Back up one byte.  It is assumed that we will
546 		 * never back up more than one byte.  If we do, this
547 		 * may or may not work.
548 		 */
549 		if (stp->str_index)
550 			--stp->str_index;
551 		return (0);
552 	}
553 
554 	index = stp->str_index++;
555 	if (index == sizeof (Block)) {
556 		Block b;
557 		ecb_encrypt(stp, stp->str_output, b);
558 		(void) memcpy((void *)stp->str_feed, (void *)b, sizeof (Block));
559 		stp->str_index = 1;	/* Next time will be 1 */
560 		index = 0;		/* But now use 0 */
561 	}
562 
563 	/* On decryption we store (data) which is cypher. */
564 	stp->str_output[index] = data;
565 	return (data ^ stp->str_feed[index]);
566 }
567