Home | History | Annotate | Download | only in telnet
      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