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