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