1/* 2 * Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2010 Apple Inc. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include <config.h> 37 38#ifdef ENABLE_NTLM 39 40#include <stdio.h> 41#include <stdlib.h> 42#include <assert.h> 43#include <string.h> 44#include <ctype.h> 45#include <errno.h> 46#include <limits.h> 47 48#include <roken.h> 49 50#include <wind.h> 51#include <parse_units.h> 52#include <krb5.h> 53 54 55#define HC_DEPRECATED_CRYPTO 56 57#include "krb5-types.h" 58#include "crypto-headers.h" 59 60#include <heimntlm.h> 61 62/*! \mainpage Heimdal NTLM library 63 * 64 * \section intro Introduction 65 * 66 * Heimdal libheimntlm library is a implementation of the NTLM 67 * protocol, both version 1 and 2. The GSS-API mech that uses this 68 * library adds support for transport encryption and integrity 69 * checking. 70 * 71 * NTLM is a protocol for mutual authentication, its still used in 72 * many protocol where Kerberos is not support, one example is 73 * EAP/X802.1x mechanism LEAP from Microsoft and Cisco. 74 * 75 * This is a support library for the core protocol, its used in 76 * Heimdal to implement and GSS-API mechanism. There is also support 77 * in the KDC to do remote digest authenticiation, this to allow 78 * services to authenticate users w/o direct access to the users ntlm 79 * hashes (same as Kerberos arcfour enctype keys). 80 * 81 * More information about the NTLM protocol can found here 82 * http://davenport.sourceforge.net/ntlm.html . 83 * 84 * The Heimdal projects web page: http://www.h5l.org/ 85 * 86 * @section ntlm_example NTLM Example 87 * 88 * Example to to use @ref test_ntlm.c . 89 * 90 * @example test_ntlm.c 91 * 92 * Example how to use the NTLM primitives. 93 * 94 */ 95 96/** @defgroup ntlm_core Heimdal NTLM library 97 * 98 * The NTLM core functions implement the string2key generation 99 * function, message encode and decode function, and the hash function 100 * functions. 101 */ 102 103struct sec_buffer { 104 uint16_t length; 105 uint16_t allocated; 106 uint32_t offset; 107}; 108 109static const unsigned char ntlmsigature[8] = "NTLMSSP\x00"; 110 111time_t heim_ntlm_time_skew = 300; 112 113/* 114 * 115 */ 116 117#define CHECK(f, e) \ 118 do { \ 119 ret = f; \ 120 if (ret != (ssize_t)(e)) { \ 121 ret = HNTLM_ERR_DECODE; \ 122 goto out; \ 123 } \ 124 } while(/*CONSTCOND*/0) 125 126#define CHECK_SIZE(f, e) \ 127 do { \ 128 ssize_t sret = f; \ 129 if (sret != (ssize_t)(e)) { \ 130 ret = HNTLM_ERR_DECODE; \ 131 goto out; \ 132 } \ 133 } while(/*CONSTCOND*/0) 134 135#define CHECK_OFFSET(f, e) \ 136 do { \ 137 off_t sret = f; \ 138 if (sret != (e)) { \ 139 ret = HNTLM_ERR_DECODE; \ 140 goto out; \ 141 } \ 142 } while(/*CONSTCOND*/0) 143 144 145static struct units ntlm_flag_units[] = { 146#define ntlm_flag(x) { #x, NTLM_##x } 147 ntlm_flag(ENC_56), 148 ntlm_flag(NEG_KEYEX), 149 ntlm_flag(ENC_128), 150 ntlm_flag(MBZ1), 151 ntlm_flag(MBZ2), 152 ntlm_flag(MBZ3), 153 ntlm_flag(NEG_VERSION), 154 ntlm_flag(MBZ4), 155 ntlm_flag(NEG_TARGET_INFO), 156 ntlm_flag(NON_NT_SESSION_KEY), 157 ntlm_flag(MBZ5), 158 ntlm_flag(NEG_IDENTIFY), 159 ntlm_flag(NEG_NTLM2), 160 ntlm_flag(TARGET_SHARE), 161 ntlm_flag(TARGET_SERVER), 162 ntlm_flag(TARGET_DOMAIN), 163 ntlm_flag(NEG_ALWAYS_SIGN), 164 ntlm_flag(MBZ6), 165 ntlm_flag(OEM_SUPPLIED_WORKSTATION), 166 ntlm_flag(OEM_SUPPLIED_DOMAIN), 167 ntlm_flag(NEG_ANONYMOUS), 168 ntlm_flag(NEG_NT_ONLY), 169 ntlm_flag(NEG_NTLM), 170 ntlm_flag(MBZ8), 171 ntlm_flag(NEG_LM_KEY), 172 ntlm_flag(NEG_DATAGRAM), 173 ntlm_flag(NEG_SEAL), 174 ntlm_flag(NEG_SIGN), 175 ntlm_flag(MBZ9), 176 ntlm_flag(NEG_TARGET), 177 ntlm_flag(NEG_OEM), 178 ntlm_flag(NEG_UNICODE), 179#undef ntlm_flag 180 {NULL, 0} 181}; 182 183size_t 184heim_ntlm_unparse_flags(uint32_t flags, char *s, size_t len) 185{ 186 return unparse_flags(flags, ntlm_flag_units, s, len); 187} 188 189 190/** 191 * heim_ntlm_free_buf frees the ntlm buffer 192 * 193 * @param p buffer to be freed 194 * 195 * @ingroup ntlm_core 196 */ 197 198void 199heim_ntlm_free_buf(struct ntlm_buf *p) 200{ 201 if (p->data) 202 free(p->data); 203 p->data = NULL; 204 p->length = 0; 205} 206 207 208static int 209ascii2ucs2le(const char *string, int up, struct ntlm_buf *buf) 210{ 211 uint16_t *data; 212 size_t len, n; 213 uint8_t *p; 214 int ret; 215 216 ret = wind_utf8ucs2_length(string, &len); 217 if (ret) 218 return ret; 219 if (len > UINT_MAX / sizeof(data[0])) 220 return ERANGE; 221 222 data = malloc(len * sizeof(data[0])); 223 if (data == NULL) 224 return ENOMEM; 225 226 ret = wind_utf8ucs2(string, data, &len); 227 if (ret) { 228 free(data); 229 return ret; 230 } 231 232 if (len == 0) { 233 free(data); 234 buf->data = NULL; 235 buf->length = 0; 236 return 0; 237 } 238 239 /* uppercase string, only handle ascii right now */ 240 if (up) { 241 for (n = 0; n < len ; n++) { 242 if (data[n] < 128) 243 data[n] = toupper((int)data[n]); 244 } 245 } 246 247 buf->length = len * 2; 248 p = buf->data = malloc(buf->length); 249 if (buf->data == NULL && len != 0) { 250 free(data); 251 heim_ntlm_free_buf(buf); 252 return ENOMEM; 253 } 254 255 for (n = 0; n < len ; n++) { 256 p[(n * 2) + 0] = (data[n] ) & 0xff; 257 p[(n * 2) + 1] = (data[n] >> 8) & 0xff; 258 } 259 memset(data, 0, sizeof(data[0]) * len); 260 free(data); 261 262 return 0; 263} 264 265/* 266 * Sizes in bytes 267 */ 268 269#define SIZE_SEC_BUFFER (2+2+4) 270#define SIZE_OS_VERSION (8) 271 272/* 273 * 274 */ 275 276static krb5_error_code 277ret_sec_buffer(krb5_storage *sp, struct sec_buffer *buf) 278{ 279 krb5_error_code ret; 280 CHECK(krb5_ret_uint16(sp, &buf->length), 0); 281 CHECK(krb5_ret_uint16(sp, &buf->allocated), 0); 282 CHECK(krb5_ret_uint32(sp, &buf->offset), 0); 283out: 284 return ret; 285} 286 287static krb5_error_code 288store_sec_buffer(krb5_storage *sp, const struct sec_buffer *buf) 289{ 290 krb5_error_code ret; 291 CHECK(krb5_store_uint16(sp, buf->length), 0); 292 CHECK(krb5_store_uint16(sp, buf->allocated), 0); 293 CHECK(krb5_store_uint32(sp, buf->offset), 0); 294out: 295 return ret; 296} 297 298/* 299 * Strings are either OEM or UNICODE. The later is encoded as ucs2 on 300 * wire, but using utf8 in memory. 301 */ 302 303static size_t 304len_string(int ucs2, const char *s) 305{ 306 if (ucs2) { 307 size_t len; 308 int ret; 309 310 ret = wind_utf8ucs2_length(s, &len); 311 if (ret == 0) 312 return len * 2; 313 return strlen(s) * 5 * 2; 314 } else { 315 return strlen(s); 316 } 317} 318 319/* 320 * 321 */ 322 323static krb5_error_code 324ret_string(krb5_storage *sp, int ucs2, size_t len, char **s) 325{ 326 krb5_error_code ret; 327 uint16_t *data = NULL; 328 329 *s = malloc(len + 1); 330 if (*s == NULL) 331 return ENOMEM; 332 CHECK_SIZE(krb5_storage_read(sp, *s, len), len); 333 334 (*s)[len] = '\0'; 335 336 if (ucs2) { 337 unsigned int flags = WIND_RW_LE; 338 size_t utf16len = len / 2; 339 size_t utf8len; 340 341 data = malloc(utf16len * sizeof(data[0])); 342 if (data == NULL) { 343 free(*s); *s = NULL; 344 ret = ENOMEM; 345 goto out; 346 } 347 348 ret = wind_ucs2read(*s, len, &flags, data, &utf16len); 349 free(*s); *s = NULL; 350 if (ret) { 351 goto out; 352 } 353 354 CHECK(wind_ucs2utf8_length(data, utf16len, &utf8len), 0); 355 356 utf8len += 1; 357 358 *s = malloc(utf8len); 359 if (s == NULL) { 360 ret = ENOMEM; 361 goto out; 362 } 363 364 CHECK(wind_ucs2utf8(data, utf16len, *s, &utf8len), 0); 365 } 366 ret = 0; 367 out: 368 if (data) 369 free(data); 370 371 return ret; 372} 373 374 375 376static krb5_error_code 377ret_sec_string(krb5_storage *sp, int ucs2, struct sec_buffer *desc, char **s) 378{ 379 krb5_error_code ret = 0; 380 CHECK_OFFSET(krb5_storage_seek(sp, desc->offset, SEEK_SET), desc->offset); 381 CHECK(ret_string(sp, ucs2, desc->length, s), 0); 382 out: 383 return ret; 384} 385 386static krb5_error_code 387put_string(krb5_storage *sp, int ucs2, const char *s) 388{ 389 krb5_error_code ret; 390 struct ntlm_buf buf; 391 392 if (ucs2) { 393 ret = ascii2ucs2le(s, 0, &buf); 394 if (ret) 395 return ret; 396 } else { 397 buf.data = rk_UNCONST(s); 398 buf.length = strlen(s); 399 } 400 401 CHECK_SIZE(krb5_storage_write(sp, buf.data, buf.length), buf.length); 402 if (ucs2) 403 heim_ntlm_free_buf(&buf); 404 ret = 0; 405out: 406 return ret; 407} 408 409/* 410 * 411 */ 412 413static krb5_error_code 414ret_buf(krb5_storage *sp, struct sec_buffer *desc, struct ntlm_buf *buf) 415{ 416 krb5_error_code ret; 417 418 buf->data = malloc(desc->length); 419 buf->length = desc->length; 420 CHECK_OFFSET(krb5_storage_seek(sp, desc->offset, SEEK_SET), desc->offset); 421 CHECK_SIZE(krb5_storage_read(sp, buf->data, buf->length), buf->length); 422 ret = 0; 423out: 424 return ret; 425} 426 427static krb5_error_code 428put_buf(krb5_storage *sp, const struct ntlm_buf *buf) 429{ 430 krb5_error_code ret; 431 CHECK_SIZE(krb5_storage_write(sp, buf->data, buf->length), buf->length); 432 ret = 0; 433out: 434 return ret; 435} 436 437/** 438 * Frees the ntlm_targetinfo message 439 * 440 * @param ti targetinfo to be freed 441 * 442 * @ingroup ntlm_core 443 */ 444 445void 446heim_ntlm_free_targetinfo(struct ntlm_targetinfo *ti) 447{ 448 free(ti->servername); 449 free(ti->domainname); 450 free(ti->dnsdomainname); 451 free(ti->dnsservername); 452 free(ti->dnstreename); 453 free(ti->targetname); 454 heim_ntlm_free_buf(&ti->channel_bindings); 455 memset(ti, 0, sizeof(*ti)); 456} 457 458static int 459encode_ti_string(krb5_storage *out, uint16_t type, int ucs2, char *s) 460{ 461 krb5_error_code ret; 462 CHECK(krb5_store_uint16(out, type), 0); 463 CHECK(krb5_store_uint16(out, len_string(ucs2, s)), 0); 464 CHECK(put_string(out, ucs2, s), 0); 465out: 466 return ret; 467} 468 469/** 470 * Encodes a ntlm_targetinfo message. 471 * 472 * @param ti the ntlm_targetinfo message to encode. 473 * @param ucs2 ignored 474 * @param data is the return buffer with the encoded message, should be 475 * freed with heim_ntlm_free_buf(). 476 * 477 * @return In case of success 0 is return, an errors, a errno in what 478 * went wrong. 479 * 480 * @ingroup ntlm_core 481 */ 482 483int 484heim_ntlm_encode_targetinfo(const struct ntlm_targetinfo *ti, 485 int ucs2, 486 struct ntlm_buf *data) 487{ 488 krb5_error_code ret; 489 krb5_storage *out; 490 491 data->data = NULL; 492 data->length = 0; 493 494 out = krb5_storage_emem(); 495 if (out == NULL) 496 return ENOMEM; 497 498 krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE); 499 500 if (ti->servername) 501 CHECK(encode_ti_string(out, 1, ucs2, ti->servername), 0); 502 if (ti->domainname) 503 CHECK(encode_ti_string(out, 2, ucs2, ti->domainname), 0); 504 if (ti->dnsservername) 505 CHECK(encode_ti_string(out, 3, ucs2, ti->dnsservername), 0); 506 if (ti->dnsdomainname) 507 CHECK(encode_ti_string(out, 4, ucs2, ti->dnsdomainname), 0); 508 if (ti->dnstreename) 509 CHECK(encode_ti_string(out, 5, ucs2, ti->dnstreename), 0); 510 if (ti->avflags) { 511 CHECK(krb5_store_uint16(out, 6), 0); 512 CHECK(krb5_store_uint16(out, 4), 0); 513 CHECK(krb5_store_uint32(out, ti->avflags), 0); 514 } 515 if (ti->timestamp) { 516 CHECK(krb5_store_uint16(out, 7), 0); 517 CHECK(krb5_store_uint16(out, 8), 0); 518 CHECK(krb5_store_uint32(out, ti->timestamp & 0xffffffff), 0); 519 CHECK(krb5_store_uint32(out, (ti->timestamp >> 32) & 0xffffffff), 0); 520 } 521 if (ti->targetname) { 522 CHECK(encode_ti_string(out, 9, ucs2, ti->targetname), 0); 523 } 524 if (ti->channel_bindings.length) { 525 CHECK(krb5_store_uint16(out, 10), 0); 526 CHECK(krb5_store_uint16(out, ti->channel_bindings.length), 0); 527 CHECK_SIZE(krb5_storage_write(out, ti->channel_bindings.data, ti->channel_bindings.length), ti->channel_bindings.length); 528 } 529 530 /* end tag */ 531 CHECK(krb5_store_int16(out, 0), 0); 532 CHECK(krb5_store_int16(out, 0), 0); 533 534 { 535 krb5_data d; 536 ret = krb5_storage_to_data(out, &d); 537 data->data = d.data; 538 data->length = d.length; 539 } 540out: 541 krb5_storage_free(out); 542 return ret; 543} 544 545/** 546 * Decodes an NTLM targetinfo message 547 * 548 * @param data input data buffer with the encode NTLM targetinfo message 549 * @param ucs2 if the strings should be encoded with ucs2 (selected by flag in message). 550 * @param ti the decoded target info, should be freed with heim_ntlm_free_targetinfo(). 551 * 552 * @return In case of success 0 is return, an errors, a errno in what 553 * went wrong. 554 * 555 * @ingroup ntlm_core 556 */ 557 558int 559heim_ntlm_decode_targetinfo(const struct ntlm_buf *data, 560 int ucs2, 561 struct ntlm_targetinfo *ti) 562{ 563 uint16_t type, len; 564 krb5_storage *in; 565 int ret = 0, done = 0; 566 567 memset(ti, 0, sizeof(*ti)); 568 569 if (data->length == 0) 570 return 0; 571 572 in = krb5_storage_from_readonly_mem(data->data, data->length); 573 if (in == NULL) 574 return ENOMEM; 575 krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE); 576 577 while (!done) { 578 CHECK(krb5_ret_uint16(in, &type), 0); 579 CHECK(krb5_ret_uint16(in, &len), 0); 580 581 switch (type) { 582 case 0: 583 done = 1; 584 break; 585 case 1: 586 CHECK(ret_string(in, ucs2, len, &ti->servername), 0); 587 break; 588 case 2: 589 CHECK(ret_string(in, ucs2, len, &ti->domainname), 0); 590 break; 591 case 3: 592 CHECK(ret_string(in, ucs2, len, &ti->dnsservername), 0); 593 break; 594 case 4: 595 CHECK(ret_string(in, ucs2, len, &ti->dnsdomainname), 0); 596 break; 597 case 5: 598 CHECK(ret_string(in, ucs2, len, &ti->dnstreename), 0); 599 break; 600 case 6: 601 CHECK(krb5_ret_uint32(in, &ti->avflags), 0); 602 break; 603 case 7: { 604 uint32_t tmp; 605 CHECK(krb5_ret_uint32(in, &tmp), 0); 606 ti->timestamp = tmp; 607 CHECK(krb5_ret_uint32(in, &tmp), 0); 608 ti->timestamp |= ((uint64_t)tmp) << 32; 609 break; 610 } 611 case 9: 612 CHECK(ret_string(in, 1, len, &ti->targetname), 0); 613 break; 614 case 10: 615 ti->channel_bindings.data = malloc(len); 616 if (ti->channel_bindings.data == NULL) { 617 ret = ENOMEM; 618 goto out; 619 } 620 ti->channel_bindings.length = len; 621 CHECK_SIZE(krb5_storage_read(in, ti->channel_bindings.data, len), len); 622 break; 623 default: 624 krb5_storage_seek(in, len, SEEK_CUR); 625 break; 626 } 627 } 628 out: 629 if (in) 630 krb5_storage_free(in); 631 return ret; 632} 633 634static krb5_error_code 635encode_os_version(krb5_storage *out) 636{ 637 krb5_error_code ret; 638 CHECK(krb5_store_uint8(out, 0x06), 0); 639 CHECK(krb5_store_uint8(out, 0x01), 0); 640 CHECK(krb5_store_uint16(out, 0x1db0), 0); 641 CHECK(krb5_store_uint8(out, 0x0f), 0); /* ntlm version 15 */ 642 CHECK(krb5_store_uint8(out, 0x00), 0); 643 CHECK(krb5_store_uint8(out, 0x00), 0); 644 CHECK(krb5_store_uint8(out, 0x00), 0); 645 out: 646 return ret; 647} 648 649/** 650 * Frees the ntlm_type1 message 651 * 652 * @param data message to be freed 653 * 654 * @ingroup ntlm_core 655 */ 656 657void 658heim_ntlm_free_type1(struct ntlm_type1 *data) 659{ 660 if (data->domain) 661 free(data->domain); 662 if (data->hostname) 663 free(data->hostname); 664 memset(data, 0, sizeof(*data)); 665} 666 667int 668heim_ntlm_decode_type1(const struct ntlm_buf *buf, struct ntlm_type1 *data) 669{ 670 krb5_error_code ret; 671 unsigned char sig[8]; 672 uint32_t type; 673 struct sec_buffer domain, hostname; 674 krb5_storage *in; 675 int ucs2; 676 677 memset(data, 0, sizeof(*data)); 678 679 in = krb5_storage_from_readonly_mem(buf->data, buf->length); 680 if (in == NULL) { 681 ret = ENOMEM; 682 goto out; 683 } 684 krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE); 685 686 CHECK_SIZE(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig)); 687 CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0); 688 CHECK(krb5_ret_uint32(in, &type), 0); 689 CHECK(type, 1); 690 CHECK(krb5_ret_uint32(in, &data->flags), 0); 691 692 ucs2 = !!(data->flags & NTLM_NEG_UNICODE); 693 694 /* 695 * domain and hostname are unconditionally encoded regardless of 696 * NTLMSSP_NEGOTIATE_OEM_{HOSTNAME,WORKSTATION}_SUPPLIED flag 697 */ 698 CHECK(ret_sec_buffer(in, &domain), 0); 699 CHECK(ret_sec_buffer(in, &hostname), 0); 700 701 if (data->flags & NTLM_NEG_VERSION) { 702 CHECK(krb5_ret_uint32(in, &data->os[0]), 0); 703 CHECK(krb5_ret_uint32(in, &data->os[1]), 0); 704 } 705 706 if (data->flags & NTLM_OEM_SUPPLIED_DOMAIN) 707 CHECK(ret_sec_string(in, ucs2, &domain, &data->domain), 0); 708 if (data->flags & NTLM_OEM_SUPPLIED_WORKSTATION) 709 CHECK(ret_sec_string(in, ucs2, &hostname, &data->hostname), 0); 710 711out: 712 if (in) 713 krb5_storage_free(in); 714 if (ret) 715 heim_ntlm_free_type1(data); 716 717 return ret; 718} 719 720/** 721 * Encodes an ntlm_type1 message. 722 * 723 * @param type1 the ntlm_type1 message to encode. 724 * @param data is the return buffer with the encoded message, should be 725 * freed with heim_ntlm_free_buf(). 726 * 727 * @return In case of success 0 is return, an errors, a errno in what 728 * went wrong. 729 * 730 * @ingroup ntlm_core 731 */ 732 733int 734heim_ntlm_encode_type1(const struct ntlm_type1 *type1, struct ntlm_buf *data) 735{ 736 krb5_error_code ret; 737 struct sec_buffer domain, hostname; 738 krb5_storage *out; 739 uint32_t base, flags; 740 int ucs2 = 0; 741 742 flags = type1->flags; 743 base = 16; 744 745 if (flags & NTLM_NEG_UNICODE) 746 ucs2 = 1; 747 748 if (type1->domain) { 749 base += SIZE_SEC_BUFFER; 750 flags |= NTLM_OEM_SUPPLIED_DOMAIN; 751 } 752 if (type1->hostname) { 753 base += SIZE_SEC_BUFFER; 754 flags |= NTLM_OEM_SUPPLIED_WORKSTATION; 755 } 756 if (flags & NTLM_NEG_VERSION) 757 base += SIZE_OS_VERSION; /* os */ 758 759 if (type1->domain) { 760 domain.offset = base; 761 domain.length = len_string(ucs2, type1->domain); 762 domain.allocated = domain.length; 763 } else { 764 domain.offset = 0; 765 domain.length = 0; 766 domain.allocated = 0; 767 } 768 769 if (type1->hostname) { 770 hostname.offset = domain.allocated + domain.offset; 771 hostname.length = len_string(ucs2, type1->hostname); 772 hostname.allocated = hostname.length; 773 } else { 774 hostname.offset = 0; 775 hostname.length = 0; 776 hostname.allocated = 0; 777 } 778 779 out = krb5_storage_emem(); 780 if (out == NULL) 781 return ENOMEM; 782 783 krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE); 784 CHECK_SIZE(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)), 785 sizeof(ntlmsigature)); 786 CHECK(krb5_store_uint32(out, 1), 0); 787 CHECK(krb5_store_uint32(out, flags), 0); 788 789 CHECK(store_sec_buffer(out, &domain), 0); 790 CHECK(store_sec_buffer(out, &hostname), 0); 791 792 if (flags & NTLM_NEG_VERSION) { 793 CHECK(encode_os_version(out), 0); 794 } 795 if (type1->domain) 796 CHECK(put_string(out, ucs2, type1->domain), 0); 797 if (type1->hostname) 798 CHECK(put_string(out, ucs2, type1->hostname), 0); 799 800 { 801 krb5_data d; 802 ret = krb5_storage_to_data(out, &d); 803 data->data = d.data; 804 data->length = d.length; 805 } 806out: 807 krb5_storage_free(out); 808 809 return ret; 810} 811 812/** 813 * Frees the ntlm_type2 message 814 * 815 * @param data message to be freed 816 * 817 * @ingroup ntlm_core 818 */ 819 820void 821heim_ntlm_free_type2(struct ntlm_type2 *data) 822{ 823 if (data->targetname) 824 free(data->targetname); 825 heim_ntlm_free_buf(&data->targetinfo); 826 memset(data, 0, sizeof(*data)); 827} 828 829int 830heim_ntlm_decode_type2(const struct ntlm_buf *buf, struct ntlm_type2 *type2) 831{ 832 krb5_error_code ret; 833 unsigned char sig[8]; 834 uint32_t type, ctx[2]; 835 struct sec_buffer targetname, targetinfo; 836 krb5_storage *in; 837 int ucs2 = 0; 838 839 memset(type2, 0, sizeof(*type2)); 840 841 in = krb5_storage_from_readonly_mem(buf->data, buf->length); 842 if (in == NULL) { 843 ret = ENOMEM; 844 goto out; 845 } 846 krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE); 847 848 CHECK_SIZE(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig)); 849 CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0); 850 CHECK(krb5_ret_uint32(in, &type), 0); 851 CHECK(type, 2); 852 853 CHECK(ret_sec_buffer(in, &targetname), 0); 854 CHECK(krb5_ret_uint32(in, &type2->flags), 0); 855 if (type2->flags & NTLM_NEG_UNICODE) 856 ucs2 = 1; 857 CHECK_SIZE(krb5_storage_read(in, type2->challenge, sizeof(type2->challenge)), 858 sizeof(type2->challenge)); 859 CHECK(krb5_ret_uint32(in, &ctx[0]), 0); /* context */ 860 CHECK(krb5_ret_uint32(in, &ctx[1]), 0); 861 CHECK(ret_sec_buffer(in, &targetinfo), 0); 862 /* os version */ 863 if (type2->flags & NTLM_NEG_VERSION) { 864 CHECK(krb5_ret_uint32(in, &type2->os[0]), 0); 865 CHECK(krb5_ret_uint32(in, &type2->os[1]), 0); 866 } 867 868 CHECK(ret_sec_string(in, ucs2, &targetname, &type2->targetname), 0); 869 CHECK(ret_buf(in, &targetinfo, &type2->targetinfo), 0); 870 ret = 0; 871 872out: 873 if (in) 874 krb5_storage_free(in); 875 if (ret) 876 heim_ntlm_free_type2(type2); 877 878 return ret; 879} 880 881/** 882 * Encodes an ntlm_type2 message. 883 * 884 * @param type2 the ntlm_type2 message to encode. 885 * @param data is the return buffer with the encoded message, should be 886 * freed with heim_ntlm_free_buf(). 887 * 888 * @return In case of success 0 is return, an errors, a errno in what 889 * went wrong. 890 * 891 * @ingroup ntlm_core 892 */ 893 894int 895heim_ntlm_encode_type2(const struct ntlm_type2 *type2, struct ntlm_buf *data) 896{ 897 struct sec_buffer targetname, targetinfo; 898 krb5_error_code ret; 899 krb5_storage *out = NULL; 900 uint32_t base; 901 int ucs2 = 0; 902 903 base = 48; 904 905 if (type2->flags & NTLM_NEG_VERSION) 906 base += SIZE_OS_VERSION; 907 908 if (type2->flags & NTLM_NEG_UNICODE) 909 ucs2 = 1; 910 911 targetname.offset = base; 912 targetname.length = len_string(ucs2, type2->targetname); 913 targetname.allocated = targetname.length; 914 915 targetinfo.offset = targetname.allocated + targetname.offset; 916 targetinfo.length = type2->targetinfo.length; 917 targetinfo.allocated = type2->targetinfo.length; 918 919 out = krb5_storage_emem(); 920 if (out == NULL) 921 return ENOMEM; 922 923 krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE); 924 CHECK_SIZE(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)), 925 sizeof(ntlmsigature)); 926 CHECK(krb5_store_uint32(out, 2), 0); 927 CHECK(store_sec_buffer(out, &targetname), 0); 928 CHECK(krb5_store_uint32(out, type2->flags), 0); 929 CHECK_SIZE(krb5_storage_write(out, type2->challenge, sizeof(type2->challenge)), 930 sizeof(type2->challenge)); 931 CHECK(krb5_store_uint32(out, 0), 0); /* context */ 932 CHECK(krb5_store_uint32(out, 0), 0); 933 CHECK(store_sec_buffer(out, &targetinfo), 0); 934 /* os version */ 935 if (type2->flags & NTLM_NEG_VERSION) { 936 CHECK(encode_os_version(out), 0); 937 } 938 CHECK(put_string(out, ucs2, type2->targetname), 0); 939 CHECK_SIZE(krb5_storage_write(out, type2->targetinfo.data, 940 type2->targetinfo.length), 941 type2->targetinfo.length); 942 943 { 944 krb5_data d; 945 ret = krb5_storage_to_data(out, &d); 946 data->data = d.data; 947 data->length = d.length; 948 } 949 950out: 951 krb5_storage_free(out); 952 953 return ret; 954} 955 956/** 957 * Frees the ntlm_type3 message 958 * 959 * @param data message to be freed 960 * 961 * @ingroup ntlm_core 962 */ 963 964void 965heim_ntlm_free_type3(struct ntlm_type3 *data) 966{ 967 heim_ntlm_free_buf(&data->lm); 968 heim_ntlm_free_buf(&data->ntlm); 969 if (data->targetname) 970 free(data->targetname); 971 if (data->username) 972 free(data->username); 973 if (data->ws) 974 free(data->ws); 975 heim_ntlm_free_buf(&data->sessionkey); 976 memset(data, 0, sizeof(*data)); 977} 978 979/* 980 * 981 */ 982 983int 984heim_ntlm_decode_type3(const struct ntlm_buf *buf, 985 int ucs2, 986 struct ntlm_type3 *type3) 987{ 988 krb5_error_code ret; 989 unsigned char sig[8]; 990 uint32_t type; 991 krb5_storage *in; 992 struct sec_buffer lm, ntlm, target, username, sessionkey, ws; 993 uint32_t min_offset = 0xffffffff; 994 995 memset(type3, 0, sizeof(*type3)); 996 memset(&sessionkey, 0, sizeof(sessionkey)); 997 998 in = krb5_storage_from_readonly_mem(buf->data, buf->length); 999 if (in == NULL) { 1000 ret = ENOMEM; 1001 goto out; 1002 } 1003 krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE); 1004 1005 CHECK_SIZE(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig)); 1006 CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0); 1007 CHECK(krb5_ret_uint32(in, &type), 0); 1008 CHECK(type, 3); 1009 CHECK(ret_sec_buffer(in, &lm), 0); 1010 if (lm.allocated) 1011 min_offset = MIN(min_offset, lm.offset); 1012 CHECK(ret_sec_buffer(in, &ntlm), 0); 1013 if (ntlm.allocated) 1014 min_offset = MIN(min_offset, ntlm.offset); 1015 CHECK(ret_sec_buffer(in, &target), 0); 1016 min_offset = MIN(min_offset, target.offset); 1017 CHECK(ret_sec_buffer(in, &username), 0); 1018 min_offset = MIN(min_offset, username.offset); 1019 CHECK(ret_sec_buffer(in, &ws), 0); 1020 if (ws.allocated) 1021 min_offset = MIN(min_offset, ws.offset); 1022 1023 if (min_offset >= 52) { 1024 CHECK(ret_sec_buffer(in, &sessionkey), 0); 1025 min_offset = MIN(min_offset, sessionkey.offset); 1026 CHECK(krb5_ret_uint32(in, &type3->flags), 0); 1027 } 1028 if (min_offset >= 52 + SIZE_SEC_BUFFER + 4 + SIZE_OS_VERSION) { 1029 CHECK(krb5_ret_uint32(in, &type3->os[0]), 0); 1030 CHECK(krb5_ret_uint32(in, &type3->os[1]), 0); 1031 } 1032 if (min_offset >= 52 + SIZE_SEC_BUFFER + 4 + SIZE_OS_VERSION + 16) { 1033 type3->mic_offset = 52 + SIZE_SEC_BUFFER + 4 + SIZE_OS_VERSION; 1034 CHECK_SIZE(krb5_storage_read(in, type3->mic, sizeof(type3->mic)), sizeof(type3->mic)); 1035 } else 1036 type3->mic_offset = 0; 1037 CHECK(ret_buf(in, &lm, &type3->lm), 0); 1038 CHECK(ret_buf(in, &ntlm, &type3->ntlm), 0); 1039 CHECK(ret_sec_string(in, ucs2, &target, &type3->targetname), 0); 1040 CHECK(ret_sec_string(in, ucs2, &username, &type3->username), 0); 1041 CHECK(ret_sec_string(in, ucs2, &ws, &type3->ws), 0); 1042 if (sessionkey.offset) 1043 CHECK(ret_buf(in, &sessionkey, &type3->sessionkey), 0); 1044 1045out: 1046 if (in) 1047 krb5_storage_free(in); 1048 if (ret) 1049 heim_ntlm_free_type3(type3); 1050 1051 return ret; 1052} 1053 1054/** 1055 * Encodes an ntlm_type3 message. 1056 * 1057 * @param type3 the ntlm_type3 message to encode. 1058 * @param data is the return buffer with the encoded message, should be 1059 * freed with heim_ntlm_free_buf(). 1060 * 1061 * @return In case of success 0 is return, an errors, a errno in what 1062 * went wrong. 1063 * 1064 * @ingroup ntlm_core 1065 */ 1066 1067int 1068heim_ntlm_encode_type3(const struct ntlm_type3 *type3, struct ntlm_buf *data, size_t *mic_offset) 1069{ 1070 struct sec_buffer lm, ntlm, target, username, sessionkey, ws; 1071 krb5_error_code ret; 1072 krb5_storage *out = NULL; 1073 uint32_t base; 1074 int ucs2 = 0; 1075 1076 memset(&lm, 0, sizeof(lm)); 1077 memset(&ntlm, 0, sizeof(ntlm)); 1078 memset(&target, 0, sizeof(target)); 1079 memset(&username, 0, sizeof(username)); 1080 memset(&ws, 0, sizeof(ws)); 1081 memset(&sessionkey, 0, sizeof(sessionkey)); 1082 1083 base = 52; 1084 1085 base += 8; /* sessionkey sec buf */ 1086 base += 4; /* flags */ 1087 if (type3->flags & NTLM_NEG_VERSION) 1088 base += SIZE_OS_VERSION; /* os flags */ 1089 1090 if (mic_offset) { 1091 *mic_offset = base; 1092 base += 16; 1093 } 1094 1095 if (type3->flags & NTLM_NEG_UNICODE) 1096 ucs2 = 1; 1097 1098 target.offset = base; 1099 target.length = len_string(ucs2, type3->targetname); 1100 target.allocated = target.length; 1101 1102 username.offset = target.offset + target.allocated; 1103 username.length = len_string(ucs2, type3->username); 1104 username.allocated = username.length; 1105 1106 ws.offset = username.offset + username.allocated; 1107 ws.length = len_string(ucs2, type3->ws); 1108 ws.allocated = ws.length; 1109 1110 lm.offset = ws.offset + ws.allocated; 1111 lm.length = type3->lm.length; 1112 lm.allocated = type3->lm.length; 1113 1114 ntlm.offset = lm.offset + lm.allocated; 1115 ntlm.length = type3->ntlm.length; 1116 ntlm.allocated = ntlm.length; 1117 1118 sessionkey.offset = ntlm.offset + ntlm.allocated; 1119 sessionkey.length = type3->sessionkey.length; 1120 sessionkey.allocated = type3->sessionkey.length; 1121 1122 out = krb5_storage_emem(); 1123 if (out == NULL) 1124 return ENOMEM; 1125 1126 krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE); 1127 CHECK_SIZE(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)), 1128 sizeof(ntlmsigature)); 1129 CHECK(krb5_store_uint32(out, 3), 0); 1130 1131 CHECK(store_sec_buffer(out, &lm), 0); 1132 CHECK(store_sec_buffer(out, &ntlm), 0); 1133 CHECK(store_sec_buffer(out, &target), 0); 1134 CHECK(store_sec_buffer(out, &username), 0); 1135 CHECK(store_sec_buffer(out, &ws), 0); 1136 CHECK(store_sec_buffer(out, &sessionkey), 0); 1137 CHECK(krb5_store_uint32(out, type3->flags), 0); 1138 1139 /* os version */ 1140 if (type3->flags & NTLM_NEG_VERSION) { 1141 CHECK(encode_os_version(out), 0); 1142 } 1143 1144 if (mic_offset) { 1145 static const uint8_t buf[16] = { 0 }; 1146 CHECK_SIZE(krb5_storage_write(out, buf, sizeof(buf)), sizeof(buf)); 1147 } 1148 1149 CHECK(put_string(out, ucs2, type3->targetname), 0); 1150 CHECK(put_string(out, ucs2, type3->username), 0); 1151 CHECK(put_string(out, ucs2, type3->ws), 0); 1152 CHECK(put_buf(out, &type3->lm), 0); 1153 CHECK(put_buf(out, &type3->ntlm), 0); 1154 CHECK(put_buf(out, &type3->sessionkey), 0); 1155 1156 { 1157 krb5_data d; 1158 ret = krb5_storage_to_data(out, &d); 1159 data->data = d.data; 1160 data->length = d.length; 1161 } 1162 1163out: 1164 krb5_storage_free(out); 1165 1166 return ret; 1167} 1168 1169 1170/* 1171 * 1172 */ 1173 1174static void 1175splitandenc(unsigned char *hash, 1176 unsigned char *challenge, 1177 unsigned char *answer) 1178{ 1179 EVP_CIPHER_CTX ctx; 1180 unsigned char key[8]; 1181 1182 key[0] = hash[0]; 1183 key[1] = (hash[0] << 7) | (hash[1] >> 1); 1184 key[2] = (hash[1] << 6) | (hash[2] >> 2); 1185 key[3] = (hash[2] << 5) | (hash[3] >> 3); 1186 key[4] = (hash[3] << 4) | (hash[4] >> 4); 1187 key[5] = (hash[4] << 3) | (hash[5] >> 5); 1188 key[6] = (hash[5] << 2) | (hash[6] >> 6); 1189 key[7] = (hash[6] << 1); 1190 1191 EVP_CIPHER_CTX_init(&ctx); 1192 1193 EVP_CipherInit_ex(&ctx, EVP_des_cbc(), NULL, key, NULL, 1); 1194 EVP_Cipher(&ctx, answer, challenge, 8); 1195 EVP_CIPHER_CTX_cleanup(&ctx); 1196 memset(key, 0, sizeof(key)); 1197} 1198 1199/** 1200 * Calculate the NTLM key, the password is assumed to be in UTF8. 1201 * 1202 * @param password password to calcute the key for. 1203 * @param key calcuted key, should be freed with heim_ntlm_free_buf(). 1204 * 1205 * @return In case of success 0 is return, an errors, a errno in what 1206 * went wrong. 1207 * 1208 * @ingroup ntlm_core 1209 */ 1210 1211int 1212heim_ntlm_nt_key(const char *password, struct ntlm_buf *key) 1213{ 1214 struct ntlm_buf buf; 1215 CCDigestRef m; 1216 int ret; 1217 1218 key->data = malloc(CC_MD4_DIGEST_LENGTH); 1219 if (key->data == NULL) 1220 return ENOMEM; 1221 key->length = CC_MD4_DIGEST_LENGTH; 1222 1223 ret = ascii2ucs2le(password, 0, &buf); 1224 if (ret) { 1225 heim_ntlm_free_buf(key); 1226 return ret; 1227 } 1228 1229 m = CCDigestCreate(kCCDigestMD4); 1230 if (m == NULL) { 1231 heim_ntlm_free_buf(key); 1232 heim_ntlm_free_buf(&buf); 1233 return ENOMEM; 1234 } 1235 1236 CCDigestUpdate(m, buf.data, buf.length); 1237 CCDigestFinal(m, key->data); 1238 CCDigestDestroy(m); 1239 1240 heim_ntlm_free_buf(&buf); 1241 return 0; 1242} 1243 1244/** 1245 * Calculate NTLMv1 response hash 1246 * 1247 * @param key the ntlm v1 key 1248 * @param len length of key 1249 * @param challenge sent by the server 1250 * @param answer calculated answer, should be freed with heim_ntlm_free_buf(). 1251 * 1252 * @return In case of success 0 is return, an errors, a errno in what 1253 * went wrong. 1254 * 1255 * @ingroup ntlm_core 1256 */ 1257 1258int 1259heim_ntlm_calculate_ntlm1(void *key, size_t len, 1260 unsigned char challenge[8], 1261 struct ntlm_buf *answer) 1262{ 1263 unsigned char res[21]; 1264 1265 if (len != CC_MD4_DIGEST_LENGTH) 1266 return HNTLM_ERR_INVALID_LENGTH; 1267 1268 memcpy(res, key, len); 1269 memset(&res[CC_MD4_DIGEST_LENGTH], 0, sizeof(res) - CC_MD4_DIGEST_LENGTH); 1270 1271 answer->data = malloc(24); 1272 if (answer->data == NULL) 1273 return ENOMEM; 1274 answer->length = 24; 1275 1276 splitandenc(&res[0], challenge, ((unsigned char *)answer->data) + 0); 1277 splitandenc(&res[7], challenge, ((unsigned char *)answer->data) + 8); 1278 splitandenc(&res[14], challenge, ((unsigned char *)answer->data) + 16); 1279 1280 return 0; 1281} 1282 1283int 1284heim_ntlm_v1_base_session(void *key, size_t len, 1285 struct ntlm_buf *session) 1286{ 1287 CCDigestRef m; 1288 1289 session->length = CC_MD4_DIGEST_LENGTH; 1290 session->data = malloc(session->length); 1291 if (session->data == NULL) { 1292 session->length = 0; 1293 return ENOMEM; 1294 } 1295 1296 m = CCDigestCreate(kCCDigestMD4); 1297 if (m == NULL) { 1298 heim_ntlm_free_buf(session); 1299 return ENOMEM; 1300 } 1301 CCDigestUpdate(m, key, len); 1302 CCDigestFinal(m, session->data); 1303 CCDigestDestroy(m); 1304 1305 return 0; 1306} 1307 1308int 1309heim_ntlm_v2_base_session(void *key, size_t len, 1310 struct ntlm_buf *ntlmResponse, 1311 struct ntlm_buf *session) 1312{ 1313 if (ntlmResponse->length <= 16) 1314 return HNTLM_ERR_INVALID_LENGTH; 1315 1316 session->data = malloc(16); 1317 if (session->data == NULL) 1318 return ENOMEM; 1319 session->length = 16; 1320 1321 /* Note: key is the NTLMv2 key */ 1322 CCHmac(kCCHmacAlgMD5, key, len, ntlmResponse->data, 16, session->data); 1323 1324 return 0; 1325} 1326 1327 1328int 1329heim_ntlm_keyex_wrap(struct ntlm_buf *base_session, 1330 struct ntlm_buf *session, 1331 struct ntlm_buf *encryptedSession) 1332{ 1333 EVP_CIPHER_CTX c; 1334 int ret; 1335 1336 if (base_session->length != CC_MD4_DIGEST_LENGTH) 1337 return HNTLM_ERR_INVALID_LENGTH; 1338 1339 session->length = CC_MD4_DIGEST_LENGTH; 1340 session->data = malloc(session->length); 1341 if (session->data == NULL) { 1342 session->length = 0; 1343 return ENOMEM; 1344 } 1345 encryptedSession->length = CC_MD4_DIGEST_LENGTH; 1346 encryptedSession->data = malloc(encryptedSession->length); 1347 if (encryptedSession->data == NULL) { 1348 heim_ntlm_free_buf(session); 1349 encryptedSession->length = 0; 1350 return ENOMEM; 1351 } 1352 1353 EVP_CIPHER_CTX_init(&c); 1354 1355 ret = EVP_CipherInit_ex(&c, EVP_rc4(), NULL, base_session->data, NULL, 1); 1356 if (ret != 1) { 1357 EVP_CIPHER_CTX_cleanup(&c); 1358 heim_ntlm_free_buf(encryptedSession); 1359 heim_ntlm_free_buf(session); 1360 return HNTLM_ERR_CRYPTO; 1361 } 1362 1363 if (CCRandomCopyBytes(kCCRandomDefault, session->data, session->length)) { 1364 EVP_CIPHER_CTX_cleanup(&c); 1365 heim_ntlm_free_buf(encryptedSession); 1366 heim_ntlm_free_buf(session); 1367 return HNTLM_ERR_RAND; 1368 } 1369 1370 EVP_Cipher(&c, encryptedSession->data, session->data, encryptedSession->length); 1371 EVP_CIPHER_CTX_cleanup(&c); 1372 1373 return 0; 1374 1375 1376 1377} 1378 1379/** 1380 * Generates an NTLMv1 session random with assosited session master key. 1381 * 1382 * @param key the ntlm v1 key 1383 * @param len length of key 1384 * @param session generated session nonce, should be freed with heim_ntlm_free_buf(). 1385 * @param master calculated session master key, should be freed with heim_ntlm_free_buf(). 1386 * 1387 * @return In case of success 0 is return, an errors, a errno in what 1388 * went wrong. 1389 * 1390 * @ingroup ntlm_core 1391 */ 1392 1393int 1394heim_ntlm_build_ntlm1_master(void *key, size_t len, 1395 struct ntlm_buf *session, 1396 struct ntlm_buf *master) 1397{ 1398 struct ntlm_buf sess; 1399 int ret; 1400 1401 ret = heim_ntlm_v1_base_session(key, len, &sess); 1402 if (ret) 1403 return ret; 1404 1405 ret = heim_ntlm_keyex_wrap(&sess, session, master); 1406 heim_ntlm_free_buf(&sess); 1407 1408 return ret; 1409} 1410 1411/** 1412 * Generates an NTLMv2 session random with associated session master key. 1413 * 1414 * @param key the NTLMv2 key 1415 * @param len length of key 1416 * @param blob the NTLMv2 "blob" 1417 * @param session generated session nonce, should be freed with heim_ntlm_free_buf(). 1418 * @param master calculated session master key, should be freed with heim_ntlm_free_buf(). 1419 * 1420 * @return In case of success 0 is return, an errors, a errno in what 1421 * went wrong. 1422 * 1423 * @ingroup ntlm_core 1424 */ 1425 1426 1427int 1428heim_ntlm_build_ntlm2_master(void *key, size_t len, 1429 struct ntlm_buf *blob, 1430 struct ntlm_buf *session, 1431 struct ntlm_buf *master) 1432{ 1433 struct ntlm_buf sess; 1434 int ret; 1435 1436 ret = heim_ntlm_v2_base_session(key, len, blob, &sess); 1437 if (ret) 1438 return ret; 1439 1440 ret = heim_ntlm_keyex_wrap(&sess, session, master); 1441 heim_ntlm_free_buf(&sess); 1442 1443 return ret; 1444} 1445 1446/** 1447 * Given a key and encrypted session, unwrap the session key 1448 * 1449 * @param baseKey the sessionBaseKey 1450 * @param encryptedSession encrypted session, type3.session field. 1451 * @param session generated session nonce, should be freed with heim_ntlm_free_buf(). 1452 * 1453 * @return In case of success 0 is return, an errors, a errno in what 1454 * went wrong. 1455 * 1456 * @ingroup ntlm_core 1457 */ 1458 1459int 1460heim_ntlm_keyex_unwrap(struct ntlm_buf *baseKey, 1461 struct ntlm_buf *encryptedSession, 1462 struct ntlm_buf *session) 1463{ 1464 EVP_CIPHER_CTX c; 1465 1466 memset(session, 0, sizeof(*session)); 1467 1468 if (encryptedSession->length != CC_MD4_DIGEST_LENGTH) 1469 return HNTLM_ERR_INVALID_LENGTH; 1470 if (baseKey->length != CC_MD4_DIGEST_LENGTH) 1471 return HNTLM_ERR_INVALID_LENGTH; 1472 1473 session->length = CC_MD4_DIGEST_LENGTH; 1474 session->data = malloc(session->length); 1475 if (session->data == NULL) { 1476 session->length = 0; 1477 return ENOMEM; 1478 } 1479 EVP_CIPHER_CTX_init(&c); 1480 1481 if (EVP_CipherInit_ex(&c, EVP_rc4(), NULL, baseKey->data, NULL, 0) != 1) { 1482 EVP_CIPHER_CTX_cleanup(&c); 1483 heim_ntlm_free_buf(session); 1484 return HNTLM_ERR_CRYPTO; 1485 } 1486 1487 EVP_Cipher(&c, session->data, encryptedSession->data, session->length); 1488 EVP_CIPHER_CTX_cleanup(&c); 1489 1490 return 0; 1491} 1492 1493 1494/** 1495 * Generates an NTLMv2 session key. 1496 * 1497 * @param key the ntlm key 1498 * @param len length of key 1499 * @param username name of the user, as sent in the message, assumed to be in UTF8. 1500 * @param target the name of the target, assumed to be in UTF8. 1501 * @param upper_case_target upper case the target, should not be used only for legacy systems 1502 * @param ntlmv2 the ntlmv2 session key 1503 * 1504 * @return 0 on success, or an error code on failure. 1505 * 1506 * @ingroup ntlm_core 1507 */ 1508 1509int 1510heim_ntlm_ntlmv2_key(const void *key, size_t len, 1511 const char *username, 1512 const char *target, 1513 int upper_case_target, 1514 unsigned char ntlmv2[16]) 1515{ 1516 int ret; 1517 CCHmacContext c; 1518 1519 CCHmacInit(&c, kCCHmacAlgMD5, key, len); 1520 { 1521 struct ntlm_buf buf; 1522 /* uppercase username and turn it into ucs2-le */ 1523 ret = ascii2ucs2le(username, 1, &buf); 1524 if (ret) 1525 goto out; 1526 CCHmacUpdate(&c, buf.data, buf.length); 1527 free(buf.data); 1528 /* turn target into ucs2-le */ 1529 ret = ascii2ucs2le(target, upper_case_target, &buf); 1530 if (ret) 1531 goto out; 1532 CCHmacUpdate(&c, buf.data, buf.length); 1533 free(buf.data); 1534 } 1535 CCHmacFinal(&c, ntlmv2); 1536 out: 1537 memset(&c, 0, sizeof(c)); 1538 1539 return ret; 1540} 1541 1542/* 1543 * 1544 */ 1545 1546#define NTTIME_EPOCH 0x019DB1DED53E8000LL 1547 1548uint64_t 1549heim_ntlm_unix2ts_time(time_t unix_time) 1550{ 1551 long long wt; 1552 wt = unix_time * (uint64_t)10000000 + (uint64_t)NTTIME_EPOCH; 1553 return wt; 1554} 1555 1556time_t 1557heim_ntlm_ts2unixtime(uint64_t t) 1558{ 1559 t = ((t - (uint64_t)NTTIME_EPOCH) / (uint64_t)10000000); 1560 if (t > (((uint64_t)(time_t)(~(uint64_t)0)) >> 1)) 1561 return 0; 1562 return (time_t)t; 1563} 1564 1565/** 1566 * Calculate LMv2 response 1567 * 1568 * @param key the ntlm key 1569 * @param len length of key 1570 * @param username name of the user, as sent in the message, assumed to be in UTF8. 1571 * @param target the name of the target, assumed to be in UTF8. 1572 * @param serverchallenge challenge as sent by the server in the type2 message. 1573 * @param ntlmv2 calculated session key 1574 * @param answer ntlm response answer, should be freed with heim_ntlm_free_buf(). 1575 * 1576 * @return In case of success 0 is return, an errors, a errno in what 1577 * went wrong. 1578 * 1579 * @ingroup ntlm_core 1580 */ 1581 1582int 1583heim_ntlm_calculate_lm2(const void *key, size_t len, 1584 const char *username, 1585 const char *target, 1586 const unsigned char serverchallenge[8], 1587 unsigned char ntlmv2[16], 1588 struct ntlm_buf *answer) 1589{ 1590 unsigned char clientchallenge[8]; 1591 1592 if (CCRandomCopyBytes(kCCRandomDefault, clientchallenge, sizeof(clientchallenge))) 1593 return HNTLM_ERR_RAND; 1594 1595 /* calculate ntlmv2 key */ 1596 1597 heim_ntlm_ntlmv2_key(key, len, username, target, 0, ntlmv2); 1598 1599 answer->data = malloc(24); 1600 if (answer->data == NULL) 1601 return ENOMEM; 1602 answer->length = 24; 1603 1604 heim_ntlm_derive_ntlm2_sess(ntlmv2, clientchallenge, 8, 1605 serverchallenge, answer->data); 1606 1607 memcpy((uint8_t *)answer->data + 16, clientchallenge, 8); 1608 1609 return 0; 1610} 1611 1612 1613/** 1614 * Calculate NTLMv2 response 1615 * 1616 * @param key the ntlm key 1617 * @param len length of key 1618 * @param username name of the user, as sent in the message, assumed to be in UTF8. 1619 * @param target the name of the target, assumed to be in UTF8. 1620 * @param serverchallenge challenge as sent by the server in the type2 message. 1621 * @param infotarget infotarget as sent by the server in the type2 message. 1622 * @param ntlmv2 calculated session key 1623 * @param answer ntlm response answer, should be freed with heim_ntlm_free_buf(). 1624 * 1625 * @return In case of success 0 is return, an errors, a errno in what 1626 * went wrong. 1627 * 1628 * @ingroup ntlm_core 1629 */ 1630 1631int 1632heim_ntlm_calculate_ntlm2(const void *key, size_t len, 1633 const char *username, 1634 const char *target, 1635 const unsigned char serverchallenge[8], 1636 const struct ntlm_buf *infotarget, 1637 unsigned char ntlmv2[16], 1638 struct ntlm_buf *answer) 1639{ 1640 krb5_error_code ret; 1641 krb5_data data; 1642 unsigned char ntlmv2answer[16]; 1643 krb5_storage *sp; 1644 unsigned char clientchallenge[8]; 1645 uint64_t t; 1646 1647 t = heim_ntlm_unix2ts_time(time(NULL)); 1648 1649 if (CCRandomCopyBytes(kCCRandomDefault, clientchallenge, sizeof(clientchallenge))) 1650 return HNTLM_ERR_RAND; 1651 1652 /* calculate ntlmv2 key */ 1653 1654 heim_ntlm_ntlmv2_key(key, len, username, target, 0, ntlmv2); 1655 1656 /* calculate and build ntlmv2 answer */ 1657 1658 sp = krb5_storage_emem(); 1659 if (sp == NULL) 1660 return ENOMEM; 1661 krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); 1662 1663 CHECK(krb5_store_uint32(sp, 0x00000101), 0); 1664 CHECK(krb5_store_uint32(sp, 0), 0); 1665 /* timestamp le 64 bit ts */ 1666 CHECK(krb5_store_uint32(sp, t & 0xffffffff), 0); 1667 CHECK(krb5_store_uint32(sp, t >> 32), 0); 1668 1669 CHECK_SIZE(krb5_storage_write(sp, clientchallenge, 8), 8); 1670 1671 CHECK(krb5_store_uint32(sp, 0), 0); /* Z(4) */ 1672 CHECK_SIZE(krb5_storage_write(sp, infotarget->data, infotarget->length), 1673 infotarget->length); 1674 1675 /* 1676 * These last 4 bytes(Z(4)) are not documented by MicroSoft and 1677 * SnowLeopard doesn't send them, Lion expected them to be there, 1678 * so we have to continue to send them. That is ok, since everyone 1679 * else (except Snow) seems to do that too. 1680 */ 1681 CHECK(krb5_store_uint32(sp, 0), 0); /* Z(4) */ 1682 1683 CHECK(krb5_storage_to_data(sp, &data), 0); 1684 krb5_storage_free(sp); 1685 sp = NULL; 1686 1687 heim_ntlm_derive_ntlm2_sess(ntlmv2, data.data, data.length, serverchallenge, ntlmv2answer); 1688 1689 sp = krb5_storage_emem(); 1690 if (sp == NULL) { 1691 krb5_data_free(&data); 1692 return ENOMEM; 1693 } 1694 1695 CHECK_SIZE(krb5_storage_write(sp, ntlmv2answer, 16), 16); 1696 CHECK_SIZE(krb5_storage_write(sp, data.data, data.length), data.length); 1697 krb5_data_free(&data); 1698 1699 CHECK(krb5_storage_to_data(sp, &data), 0); 1700 krb5_storage_free(sp); 1701 sp = NULL; 1702 1703 answer->data = data.data; 1704 answer->length = data.length; 1705 1706 return 0; 1707out: 1708 if (sp) 1709 krb5_storage_free(sp); 1710 return ret; 1711} 1712 1713static const int authtimediff = 3600 * 2; /* 2 hours */ 1714 1715static int 1716verify_ntlm2(const void *key, size_t len, 1717 const char *username, 1718 const char *target, 1719 int upper_case_target, 1720 time_t now, 1721 const unsigned char serverchallenge[8], 1722 const struct ntlm_buf *answer, 1723 struct ntlm_buf *infotarget, 1724 unsigned char ntlmv2[16]) 1725{ 1726 krb5_error_code ret; 1727 unsigned char clientanswer[16]; 1728 unsigned char clientnonce[8]; 1729 unsigned char serveranswer[16]; 1730 krb5_storage *sp; 1731 uint64_t t; 1732 time_t authtime; 1733 uint32_t temp; 1734 1735 infotarget->length = 0; 1736 infotarget->data = NULL; 1737 1738 if (answer->length < 16) 1739 return HNTLM_ERR_INVALID_LENGTH; 1740 1741 if (now == 0) 1742 now = time(NULL); 1743 1744 /* calculate ntlmv2 key */ 1745 1746 heim_ntlm_ntlmv2_key(key, len, username, target, upper_case_target, ntlmv2); 1747 1748 /* calculate and build ntlmv2 answer */ 1749 1750 sp = krb5_storage_from_readonly_mem(answer->data, answer->length); 1751 if (sp == NULL) 1752 return ENOMEM; 1753 krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); 1754 1755 CHECK_SIZE(krb5_storage_read(sp, clientanswer, 16), 16); 1756 1757 CHECK(krb5_ret_uint32(sp, &temp), 0); 1758 CHECK(temp, 0x00000101); 1759 CHECK(krb5_ret_uint32(sp, &temp), 0); 1760 CHECK(temp, 0); 1761 /* timestamp le 64 bit ts */ 1762 CHECK(krb5_ret_uint32(sp, &temp), 0); 1763 t = temp; 1764 CHECK(krb5_ret_uint32(sp, &temp), 0); 1765 t |= ((uint64_t)temp)<< 32; 1766 1767 authtime = heim_ntlm_ts2unixtime(t); 1768 1769 if (abs((int)(authtime - now)) > authtimediff) { 1770 ret = HNTLM_ERR_TIME_SKEW; 1771 goto out; 1772 } 1773 1774 /* client challenge */ 1775 CHECK_SIZE(krb5_storage_read(sp, clientnonce, 8), 8); 1776 1777 CHECK(krb5_ret_uint32(sp, &temp), 0); /* Z(4) */ 1778 1779 /* let pick up targetinfo */ 1780 infotarget->length = answer->length - (size_t)krb5_storage_seek(sp, 0, SEEK_CUR); 1781 if (infotarget->length < 4) { 1782 ret = HNTLM_ERR_INVALID_LENGTH; 1783 goto out; 1784 } 1785 infotarget->data = malloc(infotarget->length); 1786 if (infotarget->data == NULL) { 1787 ret = ENOMEM; 1788 goto out; 1789 } 1790 CHECK_SIZE(krb5_storage_read(sp, infotarget->data, infotarget->length), 1791 infotarget->length); 1792 1793 krb5_storage_free(sp); 1794 sp = NULL; 1795 1796 if (answer->length < 16) { 1797 ret = HNTLM_ERR_INVALID_LENGTH; 1798 goto out; 1799 } 1800 1801 heim_ntlm_derive_ntlm2_sess(ntlmv2, 1802 ((unsigned char *)answer->data) + 16, answer->length - 16, 1803 serverchallenge, 1804 serveranswer); 1805 1806 if (memcmp(serveranswer, clientanswer, 16) != 0) { 1807 heim_ntlm_free_buf(infotarget); 1808 return HNTLM_ERR_AUTH; 1809 } 1810 1811 return 0; 1812out: 1813 heim_ntlm_free_buf(infotarget); 1814 if (sp) 1815 krb5_storage_free(sp); 1816 return ret; 1817} 1818 1819/** 1820 * Verify NTLMv2 response. 1821 * 1822 * @param key the ntlm key 1823 * @param len length of key 1824 * @param username name of the user, as sent in the message, assumed to be in UTF8. 1825 * @param target the name of the target, assumed to be in UTF8. 1826 * @param now the time now (0 if the library should pick it up itself) 1827 * @param serverchallenge challenge as sent by the server in the type2 message. 1828 * @param answer ntlm response answer, should be freed with heim_ntlm_free_buf(). 1829 * @param infotarget infotarget as sent by the server in the type2 message. 1830 * @param ntlmv2 calculated session key 1831 * 1832 * @return In case of success 0 is return, an errors, a errno in what 1833 * went wrong. 1834 * 1835 * @ingroup ntlm_core 1836 */ 1837 1838int 1839heim_ntlm_verify_ntlm2(const void *key, size_t len, 1840 const char *username, 1841 const char *target, 1842 time_t now, 1843 const unsigned char serverchallenge[8], 1844 const struct ntlm_buf *answer, 1845 struct ntlm_buf *infotarget, 1846 unsigned char ntlmv2[16]) 1847{ 1848 int ret; 1849 1850 /** 1851 * First check with the domain as the client passed it to the function. 1852 */ 1853 1854 ret = verify_ntlm2(key, len, username, target, 0, now, 1855 serverchallenge, answer, infotarget, ntlmv2); 1856 1857 /** 1858 * Second check with domain uppercased. 1859 */ 1860 1861 if (ret) 1862 ret = verify_ntlm2(key, len, username, target, 1, now, 1863 serverchallenge, answer, infotarget, ntlmv2); 1864 1865 /** 1866 * Third check with empty domain. 1867 */ 1868 if (ret) 1869 ret = verify_ntlm2(key, len, username, "", 0, now, 1870 serverchallenge, answer, infotarget, ntlmv2); 1871 return ret; 1872} 1873 1874/* 1875 * Calculate the NTLM2 Session Response 1876 * 1877 * @param clnt_nonce client nonce 1878 * @param svr_chal server challage 1879 * @param ntlm2_hash ntlm hash 1880 * @param lm The LM response, should be freed with heim_ntlm_free_buf(). 1881 * @param ntlm The NTLM response, should be freed with heim_ntlm_free_buf(). 1882 * 1883 * @return In case of success 0 is return, an errors, a errno in what 1884 * went wrong. 1885 * 1886 * @ingroup ntlm_core 1887 */ 1888 1889int 1890heim_ntlm_calculate_ntlm2_sess(const unsigned char clnt_nonce[8], 1891 const unsigned char svr_chal[8], 1892 const unsigned char ntlm_hash[16], 1893 struct ntlm_buf *lm, 1894 struct ntlm_buf *ntlm) 1895{ 1896 unsigned char ntlm2_sess_hash[8]; 1897 unsigned char res[21], *resp; 1898 int code; 1899 1900 code = heim_ntlm_calculate_ntlm2_sess_hash(clnt_nonce, svr_chal, 1901 ntlm2_sess_hash); 1902 if (code) { 1903 return code; 1904 } 1905 1906 lm->data = malloc(24); 1907 if (lm->data == NULL) { 1908 return ENOMEM; 1909 } 1910 lm->length = 24; 1911 1912 ntlm->data = malloc(24); 1913 if (ntlm->data == NULL) { 1914 free(lm->data); 1915 lm->data = NULL; 1916 return ENOMEM; 1917 } 1918 ntlm->length = 24; 1919 1920 /* first setup the lm resp */ 1921 memset(lm->data, 0, 24); 1922 memcpy(lm->data, clnt_nonce, 8); 1923 1924 memset(res, 0, sizeof(res)); 1925 memcpy(res, ntlm_hash, 16); 1926 1927 resp = ntlm->data; 1928 splitandenc(&res[0], ntlm2_sess_hash, resp + 0); 1929 splitandenc(&res[7], ntlm2_sess_hash, resp + 8); 1930 splitandenc(&res[14], ntlm2_sess_hash, resp + 16); 1931 1932 return 0; 1933} 1934 1935 1936/* 1937 * Calculate the NTLM2 Session "Verifier" 1938 * 1939 * @param clnt_nonce client nonce 1940 * @param svr_chal server challage 1941 * @param hash The NTLM session verifier 1942 * 1943 * @return In case of success 0 is return, an errors, a errno in what 1944 * went wrong. 1945 * 1946 * @ingroup ntlm_core 1947 */ 1948 1949int 1950heim_ntlm_calculate_ntlm2_sess_hash(const unsigned char clnt_nonce[8], 1951 const unsigned char svr_chal[8], 1952 unsigned char verifier[8]) 1953{ 1954 unsigned char ntlm2_sess_hash[CC_MD5_DIGEST_LENGTH]; 1955 CCDigestRef m; 1956 1957 m = CCDigestCreate(kCCDigestMD5); 1958 if (m == NULL) 1959 return ENOMEM; 1960 1961 CCDigestUpdate(m, svr_chal, 8); /* session nonce part 1 */ 1962 CCDigestUpdate(m, clnt_nonce, 8); /* session nonce part 2 */ 1963 CCDigestFinal(m, ntlm2_sess_hash); /* will only use first 8 bytes */ 1964 CCDigestDestroy(m); 1965 1966 memcpy(verifier, ntlm2_sess_hash, 8); 1967 1968 return 0; 1969} 1970 1971 1972/* 1973 * Derive a NTLM2 session key 1974 * 1975 * @param sessionkey session key from domain controller 1976 * @param clnt_nonce client nonce 1977 * @param svr_chal server challenge 1978 * @param derivedkey salted session key 1979 * 1980 * @return In case of success 0 is return, an errors, a errno in what 1981 * went wrong. 1982 * 1983 * @ingroup ntlm_core 1984 */ 1985 1986void 1987heim_ntlm_derive_ntlm2_sess(const unsigned char sessionkey[16], 1988 const unsigned char *clnt_nonce, size_t clnt_nonce_length, 1989 const unsigned char svr_chal[8], 1990 unsigned char derivedkey[16]) 1991{ 1992 CCHmacContext c; 1993 1994 /* HMAC(Ksession, serverchallenge || clientchallenge) */ 1995 CCHmacInit(&c, kCCHmacAlgMD5, sessionkey, 16); 1996 CCHmacUpdate(&c, svr_chal, 8); 1997 CCHmacUpdate(&c, clnt_nonce, clnt_nonce_length); 1998 CCHmacFinal(&c, derivedkey); 1999 memset(&c, 0, sizeof(c)); 2000} 2001 2002/* 2003 * 2004 */ 2005 2006static const uint8_t shapad1[40] = 2007 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 2008 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; 2009static const uint8_t shapad2[40] = 2010 "\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2" 2011 "\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2"; 2012 2013static const uint8_t Magic1[27] = 2014 {0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 2015 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d, 2016 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79}; 2017 2018static const uint8_t Magic2[84] = 2019 {0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, 2020 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, 2021 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 2022 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79, 2023 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 2024 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, 2025 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 2026 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, 2027 0x6b, 0x65, 0x79, 0x2e}; 2028 2029static const uint8_t Magic3[84] = { 2030 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, 2031 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, 2032 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 2033 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, 2034 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 2035 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 2036 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 2037 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 2038 0x6b, 0x65, 0x79, 0x2e 2039}; 2040 2041/** 2042 * 2043 * @ingroup ntlm_core 2044 */ 2045 2046int 2047heim_ntlm_mppe_getmasterkey(const uint8_t ntlmHashHash[16], 2048 const uint8_t ntlmResponse[24], 2049 uint8_t masterKey[16]) 2050{ 2051 CCDigestRef m; 2052 2053 m = CCDigestCreate(kCCDigestSHA1); 2054 if (m == NULL) 2055 return ENOMEM; 2056 2057 CCDigestUpdate(m, ntlmHashHash, 16); 2058 CCDigestUpdate(m, ntlmResponse, 24); 2059 CCDigestUpdate(m, Magic1, sizeof(Magic1)); 2060 2061 CCDigestFinal(m, masterKey); 2062 CCDigestDestroy(m); 2063 2064 return 0; 2065} 2066 2067/** 2068 * 2069 * @ingroup ntlm_core 2070 */ 2071 2072int 2073heim_ntlm_mppe_getasymmetricstartkey(const uint8_t MasterKey[16], uint8_t *MasterXKey, size_t len, 2074 int isSend, int isServer) 2075{ 2076 const uint8_t *s = (!!isSend) ^ (!!isSend) ? Magic2 : Magic3; 2077 2078 unsigned char res[CC_SHA1_DIGEST_LENGTH]; 2079 CCDigestRef m; 2080 2081 if (sizeof(res) < len) 2082 return EINVAL; 2083 2084 m = CCDigestCreate(kCCDigestSHA1); 2085 if (m == NULL) 2086 return ENOMEM; 2087 2088 CCDigestUpdate(m, MasterKey, 16); 2089 CCDigestUpdate(m, shapad1, sizeof(shapad1)); 2090 CCDigestUpdate(m, s, 84); 2091 CCDigestUpdate(m, shapad2, sizeof(shapad2)); 2092 CCDigestFinal(m, res); 2093 CCDigestDestroy(m); 2094 2095 memcpy(MasterXKey, res, len); 2096 2097 return 0; 2098} 2099 2100int 2101heim_ntlm_mppe_getsessionkey(const uint8_t ntlmHash[16], 2102 const uint8_t ntlmResponse[24], 2103 int isServer, 2104 size_t len, uint8_t *sendKey, uint8_t *recvKey) 2105{ 2106 uint8_t hashHash[CC_MD4_DIGEST_LENGTH]; 2107 uint8_t masterKey[CC_SHA1_DIGEST_LENGTH]; 2108 int ret; 2109 2110 CC_MD4(ntlmHash, 16, hashHash); 2111 ret = heim_ntlm_mppe_getmasterkey(hashHash, ntlmResponse, masterKey); 2112 memset(hashHash, 0, sizeof(hashHash)); 2113 if (ret) 2114 return ret; 2115 2116 ret = heim_ntlm_mppe_getasymmetricstartkey(masterKey, sendKey, len, TRUE, isServer); 2117 if (ret == 0) 2118 ret = heim_ntlm_mppe_getasymmetricstartkey(masterKey, recvKey, len, FALSE, isServer); 2119 memset(masterKey, 0, sizeof(masterKey)); 2120 2121 return ret; 2122} 2123 2124 2125#endif /* ENABLE_NTLM */ 2126