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