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