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