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