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