1/* 2 * Copyright (c) 2006 - 2008 Kungliga Tekniska H��gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#include <config.h> 35 36#include <stdio.h> 37#include <stdlib.h> 38#include <assert.h> 39#include <string.h> 40#include <ctype.h> 41#include <errno.h> 42#include <limits.h> 43 44#include <krb5.h> 45#include <roken.h> 46 47#define HC_DEPRECATED_CRYPTO 48 49#include "krb5-types.h" 50#include "crypto-headers.h" 51 52#include <heimntlm.h> 53 54/*! \mainpage Heimdal NTLM library 55 * 56 * \section intro Introduction 57 * 58 * Heimdal libheimntlm library is a implementation of the NTLM 59 * protocol, both version 1 and 2. The GSS-API mech that uses this 60 * library adds support for transport encryption and integrity 61 * checking. 62 * 63 * NTLM is a protocol for mutual authentication, its still used in 64 * many protocol where Kerberos is not support, one example is 65 * EAP/X802.1x mechanism LEAP from Microsoft and Cisco. 66 * 67 * This is a support library for the core protocol, its used in 68 * Heimdal to implement and GSS-API mechanism. There is also support 69 * in the KDC to do remote digest authenticiation, this to allow 70 * services to authenticate users w/o direct access to the users ntlm 71 * hashes (same as Kerberos arcfour enctype keys). 72 * 73 * More information about the NTLM protocol can found here 74 * http://davenport.sourceforge.net/ntlm.html . 75 * 76 * The Heimdal projects web page: http://www.h5l.org/ 77 * 78 * @section ntlm_example NTLM Example 79 * 80 * Example to to use @ref test_ntlm.c . 81 * 82 * @example test_ntlm.c 83 * 84 * Example how to use the NTLM primitives. 85 * 86 */ 87 88/** @defgroup ntlm_core Heimdal NTLM library 89 * 90 * The NTLM core functions implement the string2key generation 91 * function, message encode and decode function, and the hash function 92 * functions. 93 */ 94 95struct sec_buffer { 96 uint16_t length; 97 uint16_t allocated; 98 uint32_t offset; 99}; 100 101static const unsigned char ntlmsigature[8] = "NTLMSSP\x00"; 102 103/* 104 * 105 */ 106 107#define CHECK(f, e) \ 108 do { ret = f ; if (ret != (e)) { ret = EINVAL; goto out; } } while(0) 109 110/** 111 * heim_ntlm_free_buf frees the ntlm buffer 112 * 113 * @param p buffer to be freed 114 * 115 * @ingroup ntlm_core 116 */ 117 118void 119heim_ntlm_free_buf(struct ntlm_buf *p) 120{ 121 if (p->data) 122 free(p->data); 123 p->data = NULL; 124 p->length = 0; 125} 126 127 128static int 129ascii2ucs2le(const char *string, int up, struct ntlm_buf *buf) 130{ 131 unsigned char *p; 132 size_t len, i; 133 134 len = strlen(string); 135 if (len / 2 > UINT_MAX) 136 return ERANGE; 137 138 buf->length = len * 2; 139 buf->data = malloc(buf->length); 140 if (buf->data == NULL && len != 0) { 141 heim_ntlm_free_buf(buf); 142 return ENOMEM; 143 } 144 145 p = buf->data; 146 for (i = 0; i < len; i++) { 147 unsigned char t = (unsigned char)string[i]; 148 if (t & 0x80) { 149 heim_ntlm_free_buf(buf); 150 return EINVAL; 151 } 152 if (up) 153 t = toupper(t); 154 p[(i * 2) + 0] = t; 155 p[(i * 2) + 1] = 0; 156 } 157 return 0; 158} 159 160/* 161 * 162 */ 163 164static krb5_error_code 165ret_sec_buffer(krb5_storage *sp, struct sec_buffer *buf) 166{ 167 krb5_error_code ret; 168 CHECK(krb5_ret_uint16(sp, &buf->length), 0); 169 CHECK(krb5_ret_uint16(sp, &buf->allocated), 0); 170 CHECK(krb5_ret_uint32(sp, &buf->offset), 0); 171out: 172 return ret; 173} 174 175static krb5_error_code 176store_sec_buffer(krb5_storage *sp, const struct sec_buffer *buf) 177{ 178 krb5_error_code ret; 179 CHECK(krb5_store_uint16(sp, buf->length), 0); 180 CHECK(krb5_store_uint16(sp, buf->allocated), 0); 181 CHECK(krb5_store_uint32(sp, buf->offset), 0); 182out: 183 return ret; 184} 185 186/* 187 * Strings are either OEM or UNICODE. The later is encoded as ucs2 on 188 * wire, but using utf8 in memory. 189 */ 190 191static krb5_error_code 192len_string(int ucs2, const char *s) 193{ 194 size_t len = strlen(s); 195 if (ucs2) 196 len *= 2; 197 return len; 198} 199 200static krb5_error_code 201ret_string(krb5_storage *sp, int ucs2, struct sec_buffer *desc, char **s) 202{ 203 krb5_error_code ret; 204 205 *s = malloc(desc->length + 1); 206 CHECK(krb5_storage_seek(sp, desc->offset, SEEK_SET), desc->offset); 207 CHECK(krb5_storage_read(sp, *s, desc->length), desc->length); 208 (*s)[desc->length] = '\0'; 209 210 if (ucs2) { 211 size_t i; 212 for (i = 0; i < desc->length / 2; i++) { 213 (*s)[i] = (*s)[i * 2]; 214 if ((*s)[i * 2 + 1]) { 215 free(*s); 216 *s = NULL; 217 return EINVAL; 218 } 219 } 220 (*s)[i] = '\0'; 221 } 222 ret = 0; 223out: 224 return ret; 225 226 return 0; 227} 228 229static krb5_error_code 230put_string(krb5_storage *sp, int ucs2, const char *s) 231{ 232 krb5_error_code ret; 233 struct ntlm_buf buf; 234 235 if (ucs2) { 236 ret = ascii2ucs2le(s, 0, &buf); 237 if (ret) 238 return ret; 239 } else { 240 buf.data = rk_UNCONST(s); 241 buf.length = strlen(s); 242 } 243 244 CHECK(krb5_storage_write(sp, buf.data, buf.length), buf.length); 245 if (ucs2) 246 heim_ntlm_free_buf(&buf); 247 ret = 0; 248out: 249 return ret; 250} 251 252/* 253 * 254 */ 255 256static krb5_error_code 257ret_buf(krb5_storage *sp, struct sec_buffer *desc, struct ntlm_buf *buf) 258{ 259 krb5_error_code ret; 260 261 buf->data = malloc(desc->length); 262 buf->length = desc->length; 263 CHECK(krb5_storage_seek(sp, desc->offset, SEEK_SET), desc->offset); 264 CHECK(krb5_storage_read(sp, buf->data, buf->length), buf->length); 265 ret = 0; 266out: 267 return ret; 268} 269 270static krb5_error_code 271put_buf(krb5_storage *sp, const struct ntlm_buf *buf) 272{ 273 krb5_error_code ret; 274 CHECK(krb5_storage_write(sp, buf->data, buf->length), buf->length); 275 ret = 0; 276out: 277 return ret; 278} 279 280/** 281 * Frees the ntlm_targetinfo message 282 * 283 * @param ti targetinfo to be freed 284 * 285 * @ingroup ntlm_core 286 */ 287 288void 289heim_ntlm_free_targetinfo(struct ntlm_targetinfo *ti) 290{ 291 free(ti->servername); 292 free(ti->domainname); 293 free(ti->dnsdomainname); 294 free(ti->dnsservername); 295 memset(ti, 0, sizeof(*ti)); 296} 297 298static int 299encode_ti_blob(krb5_storage *out, uint16_t type, int ucs2, char *s) 300{ 301 krb5_error_code ret; 302 CHECK(krb5_store_uint16(out, type), 0); 303 CHECK(krb5_store_uint16(out, len_string(ucs2, s)), 0); 304 CHECK(put_string(out, ucs2, s), 0); 305out: 306 return ret; 307} 308 309/** 310 * Encodes a ntlm_targetinfo message. 311 * 312 * @param ti the ntlm_targetinfo message to encode. 313 * @param ucs2 if the strings should be encoded with ucs2 (selected by flag in message). 314 * @param data is the return buffer with the encoded message, should be 315 * freed with heim_ntlm_free_buf(). 316 * 317 * @return In case of success 0 is return, an errors, a errno in what 318 * went wrong. 319 * 320 * @ingroup ntlm_core 321 */ 322 323int 324heim_ntlm_encode_targetinfo(const struct ntlm_targetinfo *ti, 325 int ucs2, 326 struct ntlm_buf *data) 327{ 328 krb5_error_code ret; 329 krb5_storage *out; 330 331 data->data = NULL; 332 data->length = 0; 333 334 out = krb5_storage_emem(); 335 if (out == NULL) 336 return ENOMEM; 337 338 if (ti->servername) 339 CHECK(encode_ti_blob(out, 1, ucs2, ti->servername), 0); 340 if (ti->domainname) 341 CHECK(encode_ti_blob(out, 2, ucs2, ti->domainname), 0); 342 if (ti->dnsservername) 343 CHECK(encode_ti_blob(out, 3, ucs2, ti->dnsservername), 0); 344 if (ti->dnsdomainname) 345 CHECK(encode_ti_blob(out, 4, ucs2, ti->dnsdomainname), 0); 346 347 /* end tag */ 348 CHECK(krb5_store_int16(out, 0), 0); 349 CHECK(krb5_store_int16(out, 0), 0); 350 351 { 352 krb5_data d; 353 ret = krb5_storage_to_data(out, &d); 354 data->data = d.data; 355 data->length = d.length; 356 } 357out: 358 krb5_storage_free(out); 359 return ret; 360} 361 362/** 363 * Decodes an NTLM targetinfo message 364 * 365 * @param data input data buffer with the encode NTLM targetinfo message 366 * @param ucs2 if the strings should be encoded with ucs2 (selected by flag in message). 367 * @param ti the decoded target info, should be freed with heim_ntlm_free_targetinfo(). 368 * 369 * @return In case of success 0 is return, an errors, a errno in what 370 * went wrong. 371 * 372 * @ingroup ntlm_core 373 */ 374 375int 376heim_ntlm_decode_targetinfo(const struct ntlm_buf *data, 377 int ucs2, 378 struct ntlm_targetinfo *ti) 379{ 380 memset(ti, 0, sizeof(*ti)); 381 return 0; 382} 383 384/** 385 * Frees the ntlm_type1 message 386 * 387 * @param data message to be freed 388 * 389 * @ingroup ntlm_core 390 */ 391 392void 393heim_ntlm_free_type1(struct ntlm_type1 *data) 394{ 395 if (data->domain) 396 free(data->domain); 397 if (data->hostname) 398 free(data->hostname); 399 memset(data, 0, sizeof(*data)); 400} 401 402int 403heim_ntlm_decode_type1(const struct ntlm_buf *buf, struct ntlm_type1 *data) 404{ 405 krb5_error_code ret; 406 unsigned char sig[8]; 407 uint32_t type; 408 struct sec_buffer domain, hostname; 409 krb5_storage *in; 410 411 memset(data, 0, sizeof(*data)); 412 413 in = krb5_storage_from_readonly_mem(buf->data, buf->length); 414 if (in == NULL) { 415 ret = EINVAL; 416 goto out; 417 } 418 krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE); 419 420 CHECK(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig)); 421 CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0); 422 CHECK(krb5_ret_uint32(in, &type), 0); 423 CHECK(type, 1); 424 CHECK(krb5_ret_uint32(in, &data->flags), 0); 425 if (data->flags & NTLM_SUPPLIED_DOMAIN) 426 CHECK(ret_sec_buffer(in, &domain), 0); 427 if (data->flags & NTLM_SUPPLIED_WORKSTAION) 428 CHECK(ret_sec_buffer(in, &hostname), 0); 429#if 0 430 if (domain.offset > 32) { 431 CHECK(krb5_ret_uint32(in, &data->os[0]), 0); 432 CHECK(krb5_ret_uint32(in, &data->os[1]), 0); 433 } 434#endif 435 if (data->flags & NTLM_SUPPLIED_DOMAIN) 436 CHECK(ret_string(in, 0, &domain, &data->domain), 0); 437 if (data->flags & NTLM_SUPPLIED_WORKSTAION) 438 CHECK(ret_string(in, 0, &hostname, &data->hostname), 0); 439 440out: 441 if (in) 442 krb5_storage_free(in); 443 if (ret) 444 heim_ntlm_free_type1(data); 445 446 return ret; 447} 448 449/** 450 * Encodes an ntlm_type1 message. 451 * 452 * @param type1 the ntlm_type1 message to encode. 453 * @param data is the return buffer with the encoded message, should be 454 * freed with heim_ntlm_free_buf(). 455 * 456 * @return In case of success 0 is return, an errors, a errno in what 457 * went wrong. 458 * 459 * @ingroup ntlm_core 460 */ 461 462int 463heim_ntlm_encode_type1(const struct ntlm_type1 *type1, struct ntlm_buf *data) 464{ 465 krb5_error_code ret; 466 struct sec_buffer domain, hostname; 467 krb5_storage *out; 468 uint32_t base, flags; 469 470 flags = type1->flags; 471 base = 16; 472 473 if (type1->domain) { 474 base += 8; 475 flags |= NTLM_SUPPLIED_DOMAIN; 476 } 477 if (type1->hostname) { 478 base += 8; 479 flags |= NTLM_SUPPLIED_WORKSTAION; 480 } 481 if (type1->os[0]) 482 base += 8; 483 484 if (type1->domain) { 485 domain.offset = base; 486 domain.length = len_string(0, type1->domain); 487 domain.allocated = domain.length; 488 } 489 if (type1->hostname) { 490 hostname.offset = domain.allocated + domain.offset; 491 hostname.length = len_string(0, type1->hostname); 492 hostname.allocated = hostname.length; 493 } 494 495 out = krb5_storage_emem(); 496 if (out == NULL) 497 return ENOMEM; 498 499 krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE); 500 CHECK(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)), 501 sizeof(ntlmsigature)); 502 CHECK(krb5_store_uint32(out, 1), 0); 503 CHECK(krb5_store_uint32(out, flags), 0); 504 505 if (type1->domain) 506 CHECK(store_sec_buffer(out, &domain), 0); 507 if (type1->hostname) 508 CHECK(store_sec_buffer(out, &hostname), 0); 509 if (type1->os[0]) { 510 CHECK(krb5_store_uint32(out, type1->os[0]), 0); 511 CHECK(krb5_store_uint32(out, type1->os[1]), 0); 512 } 513 if (type1->domain) 514 CHECK(put_string(out, 0, type1->domain), 0); 515 if (type1->hostname) 516 CHECK(put_string(out, 0, type1->hostname), 0); 517 518 { 519 krb5_data d; 520 ret = krb5_storage_to_data(out, &d); 521 data->data = d.data; 522 data->length = d.length; 523 } 524out: 525 krb5_storage_free(out); 526 527 return ret; 528} 529 530/** 531 * Frees the ntlm_type2 message 532 * 533 * @param data message to be freed 534 * 535 * @ingroup ntlm_core 536 */ 537 538void 539heim_ntlm_free_type2(struct ntlm_type2 *data) 540{ 541 if (data->targetname) 542 free(data->targetname); 543 heim_ntlm_free_buf(&data->targetinfo); 544 memset(data, 0, sizeof(*data)); 545} 546 547int 548heim_ntlm_decode_type2(const struct ntlm_buf *buf, struct ntlm_type2 *type2) 549{ 550 krb5_error_code ret; 551 unsigned char sig[8]; 552 uint32_t type, ctx[2]; 553 struct sec_buffer targetname, targetinfo; 554 krb5_storage *in; 555 int ucs2 = 0; 556 557 memset(type2, 0, sizeof(*type2)); 558 559 in = krb5_storage_from_readonly_mem(buf->data, buf->length); 560 if (in == NULL) { 561 ret = EINVAL; 562 goto out; 563 } 564 krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE); 565 566 CHECK(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig)); 567 CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0); 568 CHECK(krb5_ret_uint32(in, &type), 0); 569 CHECK(type, 2); 570 571 CHECK(ret_sec_buffer(in, &targetname), 0); 572 CHECK(krb5_ret_uint32(in, &type2->flags), 0); 573 if (type2->flags & NTLM_NEG_UNICODE) 574 ucs2 = 1; 575 CHECK(krb5_storage_read(in, type2->challange, sizeof(type2->challange)), 576 sizeof(type2->challange)); 577 CHECK(krb5_ret_uint32(in, &ctx[0]), 0); /* context */ 578 CHECK(krb5_ret_uint32(in, &ctx[1]), 0); 579 CHECK(ret_sec_buffer(in, &targetinfo), 0); 580 /* os version */ 581#if 0 582 CHECK(krb5_ret_uint32(in, &type2->os[0]), 0); 583 CHECK(krb5_ret_uint32(in, &type2->os[1]), 0); 584#endif 585 586 CHECK(ret_string(in, ucs2, &targetname, &type2->targetname), 0); 587 CHECK(ret_buf(in, &targetinfo, &type2->targetinfo), 0); 588 ret = 0; 589 590out: 591 if (in) 592 krb5_storage_free(in); 593 if (ret) 594 heim_ntlm_free_type2(type2); 595 596 return ret; 597} 598 599/** 600 * Encodes an ntlm_type2 message. 601 * 602 * @param type2 the ntlm_type2 message to encode. 603 * @param data is the return buffer with the encoded message, should be 604 * freed with heim_ntlm_free_buf(). 605 * 606 * @return In case of success 0 is return, an errors, a errno in what 607 * went wrong. 608 * 609 * @ingroup ntlm_core 610 */ 611 612int 613heim_ntlm_encode_type2(const struct ntlm_type2 *type2, struct ntlm_buf *data) 614{ 615 struct sec_buffer targetname, targetinfo; 616 krb5_error_code ret; 617 krb5_storage *out = NULL; 618 uint32_t base; 619 int ucs2 = 0; 620 621 if (type2->os[0]) 622 base = 56; 623 else 624 base = 48; 625 626 if (type2->flags & NTLM_NEG_UNICODE) 627 ucs2 = 1; 628 629 targetname.offset = base; 630 targetname.length = len_string(ucs2, type2->targetname); 631 targetname.allocated = targetname.length; 632 633 targetinfo.offset = targetname.allocated + targetname.offset; 634 targetinfo.length = type2->targetinfo.length; 635 targetinfo.allocated = type2->targetinfo.length; 636 637 out = krb5_storage_emem(); 638 if (out == NULL) 639 return ENOMEM; 640 641 krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE); 642 CHECK(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)), 643 sizeof(ntlmsigature)); 644 CHECK(krb5_store_uint32(out, 2), 0); 645 CHECK(store_sec_buffer(out, &targetname), 0); 646 CHECK(krb5_store_uint32(out, type2->flags), 0); 647 CHECK(krb5_storage_write(out, type2->challange, sizeof(type2->challange)), 648 sizeof(type2->challange)); 649 CHECK(krb5_store_uint32(out, 0), 0); /* context */ 650 CHECK(krb5_store_uint32(out, 0), 0); 651 CHECK(store_sec_buffer(out, &targetinfo), 0); 652 /* os version */ 653 if (type2->os[0]) { 654 CHECK(krb5_store_uint32(out, type2->os[0]), 0); 655 CHECK(krb5_store_uint32(out, type2->os[1]), 0); 656 } 657 CHECK(put_string(out, ucs2, type2->targetname), 0); 658 CHECK(krb5_storage_write(out, type2->targetinfo.data, 659 type2->targetinfo.length), 660 type2->targetinfo.length); 661 662 { 663 krb5_data d; 664 ret = krb5_storage_to_data(out, &d); 665 data->data = d.data; 666 data->length = d.length; 667 } 668 669out: 670 krb5_storage_free(out); 671 672 return ret; 673} 674 675/** 676 * Frees the ntlm_type3 message 677 * 678 * @param data message to be freed 679 * 680 * @ingroup ntlm_core 681 */ 682 683void 684heim_ntlm_free_type3(struct ntlm_type3 *data) 685{ 686 heim_ntlm_free_buf(&data->lm); 687 heim_ntlm_free_buf(&data->ntlm); 688 if (data->targetname) 689 free(data->targetname); 690 if (data->username) 691 free(data->username); 692 if (data->ws) 693 free(data->ws); 694 heim_ntlm_free_buf(&data->sessionkey); 695 memset(data, 0, sizeof(*data)); 696} 697 698/* 699 * 700 */ 701 702int 703heim_ntlm_decode_type3(const struct ntlm_buf *buf, 704 int ucs2, 705 struct ntlm_type3 *type3) 706{ 707 krb5_error_code ret; 708 unsigned char sig[8]; 709 uint32_t type; 710 krb5_storage *in; 711 struct sec_buffer lm, ntlm, target, username, sessionkey, ws; 712 713 memset(type3, 0, sizeof(*type3)); 714 memset(&sessionkey, 0, sizeof(sessionkey)); 715 716 in = krb5_storage_from_readonly_mem(buf->data, buf->length); 717 if (in == NULL) { 718 ret = EINVAL; 719 goto out; 720 } 721 krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE); 722 723 CHECK(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig)); 724 CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0); 725 CHECK(krb5_ret_uint32(in, &type), 0); 726 CHECK(type, 3); 727 CHECK(ret_sec_buffer(in, &lm), 0); 728 CHECK(ret_sec_buffer(in, &ntlm), 0); 729 CHECK(ret_sec_buffer(in, &target), 0); 730 CHECK(ret_sec_buffer(in, &username), 0); 731 CHECK(ret_sec_buffer(in, &ws), 0); 732 if (lm.offset >= 60) { 733 CHECK(ret_sec_buffer(in, &sessionkey), 0); 734 } 735 if (lm.offset >= 64) { 736 CHECK(krb5_ret_uint32(in, &type3->flags), 0); 737 } 738 if (lm.offset >= 72) { 739 CHECK(krb5_ret_uint32(in, &type3->os[0]), 0); 740 CHECK(krb5_ret_uint32(in, &type3->os[1]), 0); 741 } 742 CHECK(ret_buf(in, &lm, &type3->lm), 0); 743 CHECK(ret_buf(in, &ntlm, &type3->ntlm), 0); 744 CHECK(ret_string(in, ucs2, &target, &type3->targetname), 0); 745 CHECK(ret_string(in, ucs2, &username, &type3->username), 0); 746 CHECK(ret_string(in, ucs2, &ws, &type3->ws), 0); 747 if (sessionkey.offset) 748 CHECK(ret_buf(in, &sessionkey, &type3->sessionkey), 0); 749 750out: 751 if (in) 752 krb5_storage_free(in); 753 if (ret) 754 heim_ntlm_free_type3(type3); 755 756 return ret; 757} 758 759/** 760 * Encodes an ntlm_type3 message. 761 * 762 * @param type3 the ntlm_type3 message to encode. 763 * @param data is the return buffer with the encoded message, should be 764 * freed with heim_ntlm_free_buf(). 765 * 766 * @return In case of success 0 is return, an errors, a errno in what 767 * went wrong. 768 * 769 * @ingroup ntlm_core 770 */ 771 772int 773heim_ntlm_encode_type3(const struct ntlm_type3 *type3, struct ntlm_buf *data) 774{ 775 struct sec_buffer lm, ntlm, target, username, sessionkey, ws; 776 krb5_error_code ret; 777 krb5_storage *out = NULL; 778 uint32_t base; 779 int ucs2 = 0; 780 781 memset(&lm, 0, sizeof(lm)); 782 memset(&ntlm, 0, sizeof(ntlm)); 783 memset(&target, 0, sizeof(target)); 784 memset(&username, 0, sizeof(username)); 785 memset(&ws, 0, sizeof(ws)); 786 memset(&sessionkey, 0, sizeof(sessionkey)); 787 788 base = 52; 789 if (type3->sessionkey.length) { 790 base += 8; /* sessionkey sec buf */ 791 base += 4; /* flags */ 792 } 793 if (type3->os[0]) { 794 base += 8; 795 } 796 797 if (type3->flags & NTLM_NEG_UNICODE) 798 ucs2 = 1; 799 800 lm.offset = base; 801 lm.length = type3->lm.length; 802 lm.allocated = type3->lm.length; 803 804 ntlm.offset = lm.offset + lm.allocated; 805 ntlm.length = type3->ntlm.length; 806 ntlm.allocated = ntlm.length; 807 808 target.offset = ntlm.offset + ntlm.allocated; 809 target.length = len_string(ucs2, type3->targetname); 810 target.allocated = target.length; 811 812 username.offset = target.offset + target.allocated; 813 username.length = len_string(ucs2, type3->username); 814 username.allocated = username.length; 815 816 ws.offset = username.offset + username.allocated; 817 ws.length = len_string(ucs2, type3->ws); 818 ws.allocated = ws.length; 819 820 sessionkey.offset = ws.offset + ws.allocated; 821 sessionkey.length = type3->sessionkey.length; 822 sessionkey.allocated = type3->sessionkey.length; 823 824 out = krb5_storage_emem(); 825 if (out == NULL) 826 return ENOMEM; 827 828 krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE); 829 CHECK(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)), 830 sizeof(ntlmsigature)); 831 CHECK(krb5_store_uint32(out, 3), 0); 832 833 CHECK(store_sec_buffer(out, &lm), 0); 834 CHECK(store_sec_buffer(out, &ntlm), 0); 835 CHECK(store_sec_buffer(out, &target), 0); 836 CHECK(store_sec_buffer(out, &username), 0); 837 CHECK(store_sec_buffer(out, &ws), 0); 838 /* optional */ 839 if (type3->sessionkey.length) { 840 CHECK(store_sec_buffer(out, &sessionkey), 0); 841 CHECK(krb5_store_uint32(out, type3->flags), 0); 842 } 843#if 0 844 CHECK(krb5_store_uint32(out, 0), 0); /* os0 */ 845 CHECK(krb5_store_uint32(out, 0), 0); /* os1 */ 846#endif 847 848 CHECK(put_buf(out, &type3->lm), 0); 849 CHECK(put_buf(out, &type3->ntlm), 0); 850 CHECK(put_string(out, ucs2, type3->targetname), 0); 851 CHECK(put_string(out, ucs2, type3->username), 0); 852 CHECK(put_string(out, ucs2, type3->ws), 0); 853 CHECK(put_buf(out, &type3->sessionkey), 0); 854 855 { 856 krb5_data d; 857 ret = krb5_storage_to_data(out, &d); 858 data->data = d.data; 859 data->length = d.length; 860 } 861 862out: 863 krb5_storage_free(out); 864 865 return ret; 866} 867 868 869/* 870 * 871 */ 872 873static void 874splitandenc(unsigned char *hash, 875 unsigned char *challange, 876 unsigned char *answer) 877{ 878 DES_cblock key; 879 DES_key_schedule sched; 880 881 ((unsigned char*)key)[0] = hash[0]; 882 ((unsigned char*)key)[1] = (hash[0] << 7) | (hash[1] >> 1); 883 ((unsigned char*)key)[2] = (hash[1] << 6) | (hash[2] >> 2); 884 ((unsigned char*)key)[3] = (hash[2] << 5) | (hash[3] >> 3); 885 ((unsigned char*)key)[4] = (hash[3] << 4) | (hash[4] >> 4); 886 ((unsigned char*)key)[5] = (hash[4] << 3) | (hash[5] >> 5); 887 ((unsigned char*)key)[6] = (hash[5] << 2) | (hash[6] >> 6); 888 ((unsigned char*)key)[7] = (hash[6] << 1); 889 890 DES_set_odd_parity(&key); 891 DES_set_key_unchecked(&key, &sched); 892 DES_ecb_encrypt((DES_cblock *)challange, (DES_cblock *)answer, &sched, 1); 893 memset(&sched, 0, sizeof(sched)); 894 memset(key, 0, sizeof(key)); 895} 896 897/** 898 * Calculate the NTLM key, the password is assumed to be in UTF8. 899 * 900 * @param password password to calcute the key for. 901 * @param key calcuted key, should be freed with heim_ntlm_free_buf(). 902 * 903 * @return In case of success 0 is return, an errors, a errno in what 904 * went wrong. 905 * 906 * @ingroup ntlm_core 907 */ 908 909int 910heim_ntlm_nt_key(const char *password, struct ntlm_buf *key) 911{ 912 struct ntlm_buf buf; 913 MD4_CTX ctx; 914 int ret; 915 916 key->data = malloc(MD5_DIGEST_LENGTH); 917 if (key->data == NULL) 918 return ENOMEM; 919 key->length = MD5_DIGEST_LENGTH; 920 921 ret = ascii2ucs2le(password, 0, &buf); 922 if (ret) { 923 heim_ntlm_free_buf(key); 924 return ret; 925 } 926 MD4_Init(&ctx); 927 MD4_Update(&ctx, buf.data, buf.length); 928 MD4_Final(key->data, &ctx); 929 heim_ntlm_free_buf(&buf); 930 return 0; 931} 932 933/** 934 * Calculate NTLMv1 response hash 935 * 936 * @param key the ntlm v1 key 937 * @param len length of key 938 * @param challange sent by the server 939 * @param answer calculated answer, should be freed with heim_ntlm_free_buf(). 940 * 941 * @return In case of success 0 is return, an errors, a errno in what 942 * went wrong. 943 * 944 * @ingroup ntlm_core 945 */ 946 947int 948heim_ntlm_calculate_ntlm1(void *key, size_t len, 949 unsigned char challange[8], 950 struct ntlm_buf *answer) 951{ 952 unsigned char res[21]; 953 954 if (len != MD4_DIGEST_LENGTH) 955 return EINVAL; 956 957 memcpy(res, key, len); 958 memset(&res[MD4_DIGEST_LENGTH], 0, sizeof(res) - MD4_DIGEST_LENGTH); 959 960 answer->data = malloc(24); 961 if (answer->data == NULL) 962 return ENOMEM; 963 answer->length = 24; 964 965 splitandenc(&res[0], challange, ((unsigned char *)answer->data) + 0); 966 splitandenc(&res[7], challange, ((unsigned char *)answer->data) + 8); 967 splitandenc(&res[14], challange, ((unsigned char *)answer->data) + 16); 968 969 return 0; 970} 971 972/** 973 * Generates an NTLMv1 session random with assosited session master key. 974 * 975 * @param key the ntlm v1 key 976 * @param len length of key 977 * @param session generated session nonce, should be freed with heim_ntlm_free_buf(). 978 * @param master calculated session master key, should be freed with heim_ntlm_free_buf(). 979 * 980 * @return In case of success 0 is return, an errors, a errno in what 981 * went wrong. 982 * 983 * @ingroup ntlm_core 984 */ 985 986int 987heim_ntlm_build_ntlm1_master(void *key, size_t len, 988 struct ntlm_buf *session, 989 struct ntlm_buf *master) 990{ 991 RC4_KEY rc4; 992 993 memset(master, 0, sizeof(*master)); 994 memset(session, 0, sizeof(*session)); 995 996 if (len != MD4_DIGEST_LENGTH) 997 return EINVAL; 998 999 session->length = MD4_DIGEST_LENGTH; 1000 session->data = malloc(session->length); 1001 if (session->data == NULL) { 1002 session->length = 0; 1003 return EINVAL; 1004 } 1005 master->length = MD4_DIGEST_LENGTH; 1006 master->data = malloc(master->length); 1007 if (master->data == NULL) { 1008 heim_ntlm_free_buf(master); 1009 heim_ntlm_free_buf(session); 1010 return EINVAL; 1011 } 1012 1013 { 1014 unsigned char sessionkey[MD4_DIGEST_LENGTH]; 1015 MD4_CTX ctx; 1016 1017 MD4_Init(&ctx); 1018 MD4_Update(&ctx, key, len); 1019 MD4_Final(sessionkey, &ctx); 1020 1021 RC4_set_key(&rc4, sizeof(sessionkey), sessionkey); 1022 } 1023 1024 if (RAND_bytes(session->data, session->length) != 1) { 1025 heim_ntlm_free_buf(master); 1026 heim_ntlm_free_buf(session); 1027 return EINVAL; 1028 } 1029 1030 RC4(&rc4, master->length, session->data, master->data); 1031 memset(&rc4, 0, sizeof(rc4)); 1032 1033 return 0; 1034} 1035 1036/** 1037 * Generates an NTLMv2 session key. 1038 * 1039 * @param key the ntlm key 1040 * @param len length of key 1041 * @param username name of the user, as sent in the message, assumed to be in UTF8. 1042 * @param target the name of the target, assumed to be in UTF8. 1043 * @param ntlmv2 the ntlmv2 session key 1044 * 1045 * @return 0 on success, or an error code on failure. 1046 * 1047 * @ingroup ntlm_core 1048 */ 1049 1050int 1051heim_ntlm_ntlmv2_key(const void *key, size_t len, 1052 const char *username, 1053 const char *target, 1054 unsigned char ntlmv2[16]) 1055{ 1056 int ret; 1057 unsigned int hmaclen; 1058 HMAC_CTX c; 1059 1060 HMAC_CTX_init(&c); 1061 HMAC_Init_ex(&c, key, len, EVP_md5(), NULL); 1062 { 1063 struct ntlm_buf buf; 1064 /* uppercase username and turn it into ucs2-le */ 1065 ret = ascii2ucs2le(username, 1, &buf); 1066 if (ret) 1067 goto out; 1068 HMAC_Update(&c, buf.data, buf.length); 1069 free(buf.data); 1070 /* uppercase target and turn into ucs2-le */ 1071 ret = ascii2ucs2le(target, 1, &buf); 1072 if (ret) 1073 goto out; 1074 HMAC_Update(&c, buf.data, buf.length); 1075 free(buf.data); 1076 } 1077 HMAC_Final(&c, ntlmv2, &hmaclen); 1078 out: 1079 HMAC_CTX_cleanup(&c); 1080 1081 return ret; 1082} 1083 1084/* 1085 * 1086 */ 1087 1088#define NTTIME_EPOCH 0x019DB1DED53E8000LL 1089 1090static uint64_t 1091unix2nttime(time_t unix_time) 1092{ 1093 long long wt; 1094 wt = unix_time * (uint64_t)10000000 + (uint64_t)NTTIME_EPOCH; 1095 return wt; 1096} 1097 1098static time_t 1099nt2unixtime(uint64_t t) 1100{ 1101 t = ((t - (uint64_t)NTTIME_EPOCH) / (uint64_t)10000000); 1102 if (t > (((time_t)(~(uint64_t)0)) >> 1)) 1103 return 0; 1104 return (time_t)t; 1105} 1106 1107 1108/** 1109 * Calculate NTLMv2 response 1110 * 1111 * @param key the ntlm key 1112 * @param len length of key 1113 * @param username name of the user, as sent in the message, assumed to be in UTF8. 1114 * @param target the name of the target, assumed to be in UTF8. 1115 * @param serverchallange challange as sent by the server in the type2 message. 1116 * @param infotarget infotarget as sent by the server in the type2 message. 1117 * @param ntlmv2 calculated session key 1118 * @param answer ntlm response answer, should be freed with heim_ntlm_free_buf(). 1119 * 1120 * @return In case of success 0 is return, an errors, a errno in what 1121 * went wrong. 1122 * 1123 * @ingroup ntlm_core 1124 */ 1125 1126int 1127heim_ntlm_calculate_ntlm2(const void *key, size_t len, 1128 const char *username, 1129 const char *target, 1130 const unsigned char serverchallange[8], 1131 const struct ntlm_buf *infotarget, 1132 unsigned char ntlmv2[16], 1133 struct ntlm_buf *answer) 1134{ 1135 krb5_error_code ret; 1136 krb5_data data; 1137 unsigned int hmaclen; 1138 unsigned char ntlmv2answer[16]; 1139 krb5_storage *sp; 1140 unsigned char clientchallange[8]; 1141 HMAC_CTX c; 1142 uint64_t t; 1143 1144 t = unix2nttime(time(NULL)); 1145 1146 if (RAND_bytes(clientchallange, sizeof(clientchallange)) != 1) 1147 return EINVAL; 1148 1149 /* calculate ntlmv2 key */ 1150 1151 heim_ntlm_ntlmv2_key(key, len, username, target, ntlmv2); 1152 1153 /* calculate and build ntlmv2 answer */ 1154 1155 sp = krb5_storage_emem(); 1156 if (sp == NULL) 1157 return ENOMEM; 1158 krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); 1159 1160 CHECK(krb5_store_uint32(sp, 0x00000101), 0); 1161 CHECK(krb5_store_uint32(sp, 0), 0); 1162 /* timestamp le 64 bit ts */ 1163 CHECK(krb5_store_uint32(sp, t & 0xffffffff), 0); 1164 CHECK(krb5_store_uint32(sp, t >> 32), 0); 1165 1166 CHECK(krb5_storage_write(sp, clientchallange, 8), 8); 1167 1168 CHECK(krb5_store_uint32(sp, 0), 0); /* unknown but zero will work */ 1169 CHECK(krb5_storage_write(sp, infotarget->data, infotarget->length), 1170 infotarget->length); 1171 CHECK(krb5_store_uint32(sp, 0), 0); /* unknown but zero will work */ 1172 1173 CHECK(krb5_storage_to_data(sp, &data), 0); 1174 krb5_storage_free(sp); 1175 sp = NULL; 1176 1177 HMAC_CTX_init(&c); 1178 HMAC_Init_ex(&c, ntlmv2, 16, EVP_md5(), NULL); 1179 HMAC_Update(&c, serverchallange, 8); 1180 HMAC_Update(&c, data.data, data.length); 1181 HMAC_Final(&c, ntlmv2answer, &hmaclen); 1182 HMAC_CTX_cleanup(&c); 1183 1184 sp = krb5_storage_emem(); 1185 if (sp == NULL) { 1186 krb5_data_free(&data); 1187 return ENOMEM; 1188 } 1189 1190 CHECK(krb5_storage_write(sp, ntlmv2answer, 16), 16); 1191 CHECK(krb5_storage_write(sp, data.data, data.length), data.length); 1192 krb5_data_free(&data); 1193 1194 CHECK(krb5_storage_to_data(sp, &data), 0); 1195 krb5_storage_free(sp); 1196 sp = NULL; 1197 1198 answer->data = data.data; 1199 answer->length = data.length; 1200 1201 return 0; 1202out: 1203 if (sp) 1204 krb5_storage_free(sp); 1205 return ret; 1206} 1207 1208static const int authtimediff = 3600 * 2; /* 2 hours */ 1209 1210/** 1211 * Verify NTLMv2 response. 1212 * 1213 * @param key the ntlm key 1214 * @param len length of key 1215 * @param username name of the user, as sent in the message, assumed to be in UTF8. 1216 * @param target the name of the target, assumed to be in UTF8. 1217 * @param now the time now (0 if the library should pick it up itself) 1218 * @param serverchallange challange as sent by the server in the type2 message. 1219 * @param answer ntlm response answer, should be freed with heim_ntlm_free_buf(). 1220 * @param infotarget infotarget as sent by the server in the type2 message. 1221 * @param ntlmv2 calculated session key 1222 * 1223 * @return In case of success 0 is return, an errors, a errno in what 1224 * went wrong. 1225 * 1226 * @ingroup ntlm_core 1227 */ 1228 1229int 1230heim_ntlm_verify_ntlm2(const void *key, size_t len, 1231 const char *username, 1232 const char *target, 1233 time_t now, 1234 const unsigned char serverchallange[8], 1235 const struct ntlm_buf *answer, 1236 struct ntlm_buf *infotarget, 1237 unsigned char ntlmv2[16]) 1238{ 1239 krb5_error_code ret; 1240 unsigned int hmaclen; 1241 unsigned char clientanswer[16]; 1242 unsigned char clientnonce[8]; 1243 unsigned char serveranswer[16]; 1244 krb5_storage *sp; 1245 HMAC_CTX c; 1246 uint64_t t; 1247 time_t authtime; 1248 uint32_t temp; 1249 1250 infotarget->length = 0; 1251 infotarget->data = NULL; 1252 1253 if (answer->length < 16) 1254 return EINVAL; 1255 1256 if (now == 0) 1257 now = time(NULL); 1258 1259 /* calculate ntlmv2 key */ 1260 1261 heim_ntlm_ntlmv2_key(key, len, username, target, ntlmv2); 1262 1263 /* calculate and build ntlmv2 answer */ 1264 1265 sp = krb5_storage_from_readonly_mem(answer->data, answer->length); 1266 if (sp == NULL) 1267 return ENOMEM; 1268 krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); 1269 1270 CHECK(krb5_storage_read(sp, clientanswer, 16), 16); 1271 1272 CHECK(krb5_ret_uint32(sp, &temp), 0); 1273 CHECK(temp, 0x00000101); 1274 CHECK(krb5_ret_uint32(sp, &temp), 0); 1275 CHECK(temp, 0); 1276 /* timestamp le 64 bit ts */ 1277 CHECK(krb5_ret_uint32(sp, &temp), 0); 1278 t = temp; 1279 CHECK(krb5_ret_uint32(sp, &temp), 0); 1280 t |= ((uint64_t)temp)<< 32; 1281 1282 authtime = nt2unixtime(t); 1283 1284 if (abs((int)(authtime - now)) > authtimediff) { 1285 ret = EINVAL; 1286 goto out; 1287 } 1288 1289 /* client challange */ 1290 CHECK(krb5_storage_read(sp, clientnonce, 8), 8); 1291 1292 CHECK(krb5_ret_uint32(sp, &temp), 0); /* unknown */ 1293 1294 /* should really unparse the infotarget, but lets pick up everything */ 1295 infotarget->length = answer->length - krb5_storage_seek(sp, 0, SEEK_CUR); 1296 infotarget->data = malloc(infotarget->length); 1297 if (infotarget->data == NULL) { 1298 ret = ENOMEM; 1299 goto out; 1300 } 1301 CHECK(krb5_storage_read(sp, infotarget->data, infotarget->length), 1302 infotarget->length); 1303 /* XXX remove the unknown ?? */ 1304 krb5_storage_free(sp); 1305 sp = NULL; 1306 1307 HMAC_CTX_init(&c); 1308 HMAC_Init_ex(&c, ntlmv2, 16, EVP_md5(), NULL); 1309 HMAC_Update(&c, serverchallange, 8); 1310 HMAC_Update(&c, ((unsigned char *)answer->data) + 16, answer->length - 16); 1311 HMAC_Final(&c, serveranswer, &hmaclen); 1312 HMAC_CTX_cleanup(&c); 1313 1314 if (memcmp(serveranswer, clientanswer, 16) != 0) { 1315 heim_ntlm_free_buf(infotarget); 1316 return EINVAL; 1317 } 1318 1319 return 0; 1320out: 1321 heim_ntlm_free_buf(infotarget); 1322 if (sp) 1323 krb5_storage_free(sp); 1324 return ret; 1325} 1326 1327 1328/* 1329 * Calculate the NTLM2 Session Response 1330 * 1331 * @param clnt_nonce client nonce 1332 * @param svr_chal server challage 1333 * @param ntlm2_hash ntlm hash 1334 * @param lm The LM response, should be freed with heim_ntlm_free_buf(). 1335 * @param ntlm The NTLM response, should be freed with heim_ntlm_free_buf(). 1336 * 1337 * @return In case of success 0 is return, an errors, a errno in what 1338 * went wrong. 1339 * 1340 * @ingroup ntlm_core 1341 */ 1342 1343int 1344heim_ntlm_calculate_ntlm2_sess(const unsigned char clnt_nonce[8], 1345 const unsigned char svr_chal[8], 1346 const unsigned char ntlm_hash[16], 1347 struct ntlm_buf *lm, 1348 struct ntlm_buf *ntlm) 1349{ 1350 unsigned char ntlm2_sess_hash[MD5_DIGEST_LENGTH]; 1351 unsigned char res[21], *resp; 1352 MD5_CTX md5; 1353 1354 lm->data = malloc(24); 1355 if (lm->data == NULL) 1356 return ENOMEM; 1357 lm->length = 24; 1358 1359 ntlm->data = malloc(24); 1360 if (ntlm->data == NULL) { 1361 free(lm->data); 1362 lm->data = NULL; 1363 return ENOMEM; 1364 } 1365 ntlm->length = 24; 1366 1367 /* first setup the lm resp */ 1368 memset(lm->data, 0, 24); 1369 memcpy(lm->data, clnt_nonce, 8); 1370 1371 MD5_Init(&md5); 1372 MD5_Update(&md5, svr_chal, 8); /* session nonce part 1 */ 1373 MD5_Update(&md5, clnt_nonce, 8); /* session nonce part 2 */ 1374 MD5_Final(ntlm2_sess_hash, &md5); /* will only use first 8 bytes */ 1375 1376 memset(res, 0, sizeof(res)); 1377 memcpy(res, ntlm_hash, 16); 1378 1379 resp = ntlm->data; 1380 splitandenc(&res[0], ntlm2_sess_hash, resp + 0); 1381 splitandenc(&res[7], ntlm2_sess_hash, resp + 8); 1382 splitandenc(&res[14], ntlm2_sess_hash, resp + 16); 1383 1384 return 0; 1385} 1386