1 /* $NetBSD: digest.c,v 1.3 2018/02/05 16:00:52 christos Exp $ */ 2 3 /* 4 * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include "kdc_locl.h" 37 #include <krb5/hex.h> 38 39 #ifdef DIGEST 40 41 #define MS_CHAP_V2 0x20 42 #define CHAP_MD5 0x10 43 #define DIGEST_MD5 0x08 44 #define NTLM_V2 0x04 45 #define NTLM_V1_SESSION 0x02 46 #define NTLM_V1 0x01 47 48 const struct units _kdc_digestunits[] = { 49 {"ms-chap-v2", 1U << 5}, 50 {"chap-md5", 1U << 4}, 51 {"digest-md5", 1U << 3}, 52 {"ntlm-v2", 1U << 2}, 53 {"ntlm-v1-session", 1U << 1}, 54 {"ntlm-v1", 1U << 0}, 55 {NULL, 0} 56 }; 57 58 59 static krb5_error_code 60 get_digest_key(krb5_context context, 61 krb5_kdc_configuration *config, 62 hdb_entry_ex *server, 63 krb5_crypto *crypto) 64 { 65 krb5_error_code ret; 66 krb5_enctype enctype; 67 Key *key; 68 69 ret = _kdc_get_preferred_key(context, 70 config, 71 server, 72 "digest-service", 73 &enctype, 74 &key); 75 if (ret) 76 return ret; 77 return krb5_crypto_init(context, &key->key, 0, crypto); 78 } 79 80 /* 81 * 82 */ 83 84 static char * 85 get_ntlm_targetname(krb5_context context, 86 hdb_entry_ex *client) 87 { 88 char *targetname, *p; 89 90 targetname = strdup(krb5_principal_get_realm(context, 91 client->entry.principal)); 92 if (targetname == NULL) 93 return NULL; 94 95 p = strchr(targetname, '.'); 96 if (p) 97 *p = '\0'; 98 99 strupr(targetname); 100 return targetname; 101 } 102 103 static krb5_error_code 104 fill_targetinfo(krb5_context context, 105 char *targetname, 106 hdb_entry_ex *client, 107 krb5_data *data) 108 { 109 struct ntlm_targetinfo ti; 110 krb5_error_code ret; 111 struct ntlm_buf d; 112 krb5_principal p; 113 const char *str; 114 115 memset(&ti, 0, sizeof(ti)); 116 117 ti.domainname = targetname; 118 p = client->entry.principal; 119 str = krb5_principal_get_comp_string(context, p, 0); 120 if (str != NULL && 121 (strcmp("host", str) == 0 || 122 strcmp("ftp", str) == 0 || 123 strcmp("imap", str) == 0 || 124 strcmp("pop", str) == 0 || 125 strcmp("smtp", str))) 126 { 127 str = krb5_principal_get_comp_string(context, p, 1); 128 ti.dnsservername = rk_UNCONST(str); 129 } 130 131 ret = heim_ntlm_encode_targetinfo(&ti, 1, &d); 132 if (ret) 133 return ret; 134 135 data->data = d.data; 136 data->length = d.length; 137 138 return 0; 139 } 140 141 142 static const unsigned char ms_chap_v2_magic1[39] = { 143 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, 144 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65, 145 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, 146 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 147 }; 148 static const unsigned char ms_chap_v2_magic2[41] = { 149 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B, 150 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F, 151 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E, 152 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, 153 0x6E 154 }; 155 static const unsigned char ms_rfc3079_magic1[27] = { 156 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 157 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d, 158 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 159 }; 160 161 /* 162 * 163 */ 164 165 static krb5_error_code 166 get_password_entry(krb5_context context, 167 krb5_kdc_configuration *config, 168 const char *username, 169 char **password) 170 { 171 krb5_principal clientprincipal; 172 krb5_error_code ret; 173 hdb_entry_ex *user; 174 HDB *db; 175 176 /* get username */ 177 ret = krb5_parse_name(context, username, &clientprincipal); 178 if (ret) 179 return ret; 180 181 ret = _kdc_db_fetch(context, config, clientprincipal, 182 HDB_F_GET_CLIENT, NULL, &db, &user); 183 krb5_free_principal(context, clientprincipal); 184 if (ret) 185 return ret; 186 187 ret = hdb_entry_get_password(context, db, &user->entry, password); 188 if (ret || password == NULL) { 189 if (ret == 0) { 190 ret = EINVAL; 191 krb5_set_error_message(context, ret, "password missing"); 192 } 193 memset(user, 0, sizeof(*user)); 194 } 195 _kdc_free_ent (context, user); 196 return ret; 197 } 198 199 /* 200 * 201 */ 202 203 krb5_error_code 204 _kdc_do_digest(krb5_context context, 205 krb5_kdc_configuration *config, 206 const struct DigestREQ *req, krb5_data *reply, 207 const char *from, struct sockaddr *addr) 208 { 209 krb5_error_code ret = 0; 210 krb5_ticket *ticket = NULL; 211 krb5_auth_context ac = NULL; 212 krb5_keytab id = NULL; 213 krb5_crypto crypto = NULL; 214 DigestReqInner ireq; 215 DigestRepInner r; 216 DigestREP rep; 217 krb5_flags ap_req_options; 218 krb5_data buf; 219 size_t size; 220 krb5_storage *sp = NULL; 221 Checksum res; 222 hdb_entry_ex *server = NULL, *user = NULL; 223 hdb_entry_ex *client = NULL; 224 char *client_name = NULL, *password = NULL; 225 krb5_data serverNonce; 226 227 if(!config->enable_digest) { 228 kdc_log(context, config, 0, 229 "Rejected digest request (disabled) from %s", from); 230 return KRB5KDC_ERR_POLICY; 231 } 232 233 krb5_data_zero(&buf); 234 krb5_data_zero(reply); 235 krb5_data_zero(&serverNonce); 236 memset(&ireq, 0, sizeof(ireq)); 237 memset(&r, 0, sizeof(r)); 238 memset(&rep, 0, sizeof(rep)); 239 memset(&res, 0, sizeof(res)); 240 241 kdc_log(context, config, 0, "Digest request from %s", from); 242 243 ret = krb5_kt_resolve(context, "HDBGET:", &id); 244 if (ret) { 245 kdc_log(context, config, 0, "Can't open database for digest"); 246 goto out; 247 } 248 249 ret = krb5_rd_req(context, 250 &ac, 251 &req->apReq, 252 NULL, 253 id, 254 &ap_req_options, 255 &ticket); 256 if (ret) 257 goto out; 258 259 /* check the server principal in the ticket matches digest/R@R */ 260 { 261 krb5_principal principal = NULL; 262 const char *p, *rr; 263 264 ret = krb5_ticket_get_server(context, ticket, &principal); 265 if (ret) 266 goto out; 267 268 ret = EINVAL; 269 krb5_set_error_message(context, ret, "Wrong digest server principal used"); 270 p = krb5_principal_get_comp_string(context, principal, 0); 271 if (p == NULL) { 272 krb5_free_principal(context, principal); 273 goto out; 274 } 275 if (strcmp(p, KRB5_DIGEST_NAME) != 0) { 276 krb5_free_principal(context, principal); 277 goto out; 278 } 279 280 p = krb5_principal_get_comp_string(context, principal, 1); 281 if (p == NULL) { 282 krb5_free_principal(context, principal); 283 goto out; 284 } 285 rr = krb5_principal_get_realm(context, principal); 286 if (rr == NULL) { 287 krb5_free_principal(context, principal); 288 goto out; 289 } 290 if (strcmp(p, rr) != 0) { 291 krb5_free_principal(context, principal); 292 goto out; 293 } 294 krb5_clear_error_message(context); 295 296 ret = _kdc_db_fetch(context, config, principal, 297 HDB_F_GET_SERVER, NULL, NULL, &server); 298 if (ret) 299 goto out; 300 301 krb5_free_principal(context, principal); 302 } 303 304 /* check the client is allowed to do digest auth */ 305 { 306 krb5_principal principal = NULL; 307 308 ret = krb5_ticket_get_client(context, ticket, &principal); 309 if (ret) 310 goto out; 311 312 ret = krb5_unparse_name(context, principal, &client_name); 313 if (ret) { 314 krb5_free_principal(context, principal); 315 goto out; 316 } 317 318 ret = _kdc_db_fetch(context, config, principal, 319 HDB_F_GET_CLIENT, NULL, NULL, &client); 320 krb5_free_principal(context, principal); 321 if (ret) 322 goto out; 323 324 if (client->entry.flags.allow_digest == 0) { 325 kdc_log(context, config, 0, 326 "Client %s tried to use digest " 327 "but is not allowed to", 328 client_name); 329 ret = KRB5KDC_ERR_POLICY; 330 krb5_set_error_message(context, ret, 331 "Client is not permitted to use digest"); 332 goto out; 333 } 334 } 335 336 /* unpack request */ 337 { 338 krb5_keyblock *key; 339 340 ret = krb5_auth_con_getremotesubkey(context, ac, &key); 341 if (ret) 342 goto out; 343 if (key == NULL) { 344 ret = EINVAL; 345 krb5_set_error_message(context, ret, "digest: remote subkey not found"); 346 goto out; 347 } 348 349 ret = krb5_crypto_init(context, key, 0, &crypto); 350 krb5_free_keyblock (context, key); 351 if (ret) 352 goto out; 353 } 354 355 ret = krb5_decrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT, 356 &req->innerReq, &buf); 357 krb5_crypto_destroy(context, crypto); 358 crypto = NULL; 359 if (ret) 360 goto out; 361 362 ret = decode_DigestReqInner(buf.data, buf.length, &ireq, NULL); 363 krb5_data_free(&buf); 364 if (ret) { 365 krb5_set_error_message(context, ret, "Failed to decode digest inner request"); 366 goto out; 367 } 368 369 kdc_log(context, config, 0, "Valid digest request from %s (%s)", 370 client_name, from); 371 372 /* 373 * Process the inner request 374 */ 375 376 switch (ireq.element) { 377 case choice_DigestReqInner_init: { 378 unsigned char server_nonce[16], identifier; 379 380 RAND_bytes(&identifier, sizeof(identifier)); 381 RAND_bytes(server_nonce, sizeof(server_nonce)); 382 383 server_nonce[0] = kdc_time & 0xff; 384 server_nonce[1] = (kdc_time >> 8) & 0xff; 385 server_nonce[2] = (kdc_time >> 16) & 0xff; 386 server_nonce[3] = (kdc_time >> 24) & 0xff; 387 388 r.element = choice_DigestRepInner_initReply; 389 390 hex_encode(server_nonce, sizeof(server_nonce), &r.u.initReply.nonce); 391 if (r.u.initReply.nonce == NULL) { 392 ret = ENOMEM; 393 krb5_set_error_message(context, ret, "Failed to decode server nonce"); 394 goto out; 395 } 396 397 sp = krb5_storage_emem(); 398 if (sp == NULL) { 399 ret = ENOMEM; 400 krb5_set_error_message(context, ret, "malloc: out of memory"); 401 goto out; 402 } 403 ret = krb5_store_stringz(sp, ireq.u.init.type); 404 if (ret) { 405 krb5_clear_error_message(context); 406 goto out; 407 } 408 409 if (ireq.u.init.channel) { 410 char *s; 411 int aret; 412 413 aret = asprintf(&s, "%s-%s:%s", r.u.initReply.nonce, 414 ireq.u.init.channel->cb_type, 415 ireq.u.init.channel->cb_binding); 416 if (aret == -1 || s == NULL) { 417 ret = ENOMEM; 418 krb5_set_error_message(context, ret, 419 "Failed to allocate channel binding"); 420 goto out; 421 } 422 free(r.u.initReply.nonce); 423 r.u.initReply.nonce = s; 424 } 425 426 ret = krb5_store_stringz(sp, r.u.initReply.nonce); 427 if (ret) { 428 krb5_clear_error_message(context); 429 goto out; 430 } 431 432 if (strcasecmp(ireq.u.init.type, "CHAP") == 0) { 433 int aret; 434 435 r.u.initReply.identifier = 436 malloc(sizeof(*r.u.initReply.identifier)); 437 if (r.u.initReply.identifier == NULL) { 438 ret = ENOMEM; 439 krb5_set_error_message(context, ret, "malloc: out of memory"); 440 goto out; 441 } 442 443 aret = asprintf(r.u.initReply.identifier, "%02X", identifier&0xff); 444 if (aret == -1 || *r.u.initReply.identifier == NULL) { 445 ret = ENOMEM; 446 krb5_set_error_message(context, ret, "malloc: out of memory"); 447 goto out; 448 } 449 450 } else 451 r.u.initReply.identifier = NULL; 452 453 if (ireq.u.init.hostname) { 454 ret = krb5_store_stringz(sp, *ireq.u.init.hostname); 455 if (ret) { 456 krb5_clear_error_message(context); 457 goto out; 458 } 459 } 460 461 ret = krb5_storage_to_data(sp, &buf); 462 if (ret) { 463 krb5_clear_error_message(context); 464 goto out; 465 } 466 467 ret = get_digest_key(context, config, server, &crypto); 468 if (ret) 469 goto out; 470 471 ret = krb5_create_checksum(context, 472 crypto, 473 KRB5_KU_DIGEST_OPAQUE, 474 0, 475 buf.data, 476 buf.length, 477 &res); 478 krb5_crypto_destroy(context, crypto); 479 crypto = NULL; 480 krb5_data_free(&buf); 481 if (ret) 482 goto out; 483 484 ASN1_MALLOC_ENCODE(Checksum, buf.data, buf.length, &res, &size, ret); 485 free_Checksum(&res); 486 if (ret) { 487 krb5_set_error_message(context, ret, "Failed to encode " 488 "checksum in digest request"); 489 goto out; 490 } 491 if (size != buf.length) 492 krb5_abortx(context, "ASN1 internal error"); 493 494 hex_encode(buf.data, buf.length, &r.u.initReply.opaque); 495 free(buf.data); 496 krb5_data_zero(&buf); 497 if (r.u.initReply.opaque == NULL) { 498 krb5_clear_error_message(context); 499 ret = ENOMEM; 500 goto out; 501 } 502 503 kdc_log(context, config, 0, "Digest %s init request successful from %s", 504 ireq.u.init.type, from); 505 506 break; 507 } 508 case choice_DigestReqInner_digestRequest: { 509 sp = krb5_storage_emem(); 510 if (sp == NULL) { 511 ret = ENOMEM; 512 krb5_set_error_message(context, ret, "malloc: out of memory"); 513 goto out; 514 } 515 ret = krb5_store_stringz(sp, ireq.u.digestRequest.type); 516 if (ret) { 517 krb5_clear_error_message(context); 518 goto out; 519 } 520 521 krb5_store_stringz(sp, ireq.u.digestRequest.serverNonce); 522 523 if (ireq.u.digestRequest.hostname) { 524 ret = krb5_store_stringz(sp, *ireq.u.digestRequest.hostname); 525 if (ret) { 526 krb5_clear_error_message(context); 527 goto out; 528 } 529 } 530 531 buf.length = strlen(ireq.u.digestRequest.opaque); 532 buf.data = malloc(buf.length); 533 if (buf.data == NULL) { 534 ret = ENOMEM; 535 krb5_set_error_message(context, ret, "malloc: out of memory"); 536 goto out; 537 } 538 539 ret = hex_decode(ireq.u.digestRequest.opaque, buf.data, buf.length); 540 if (ret <= 0) { 541 ret = ENOMEM; 542 krb5_set_error_message(context, ret, "Failed to decode opaque"); 543 goto out; 544 } 545 buf.length = ret; 546 547 ret = decode_Checksum(buf.data, buf.length, &res, NULL); 548 free(buf.data); 549 krb5_data_zero(&buf); 550 if (ret) { 551 krb5_set_error_message(context, ret, 552 "Failed to decode digest Checksum"); 553 goto out; 554 } 555 556 ret = krb5_storage_to_data(sp, &buf); 557 if (ret) { 558 krb5_clear_error_message(context); 559 goto out; 560 } 561 562 serverNonce.length = strlen(ireq.u.digestRequest.serverNonce); 563 serverNonce.data = malloc(serverNonce.length); 564 if (serverNonce.data == NULL) { 565 ret = ENOMEM; 566 krb5_set_error_message(context, ret, "malloc: out of memory"); 567 goto out; 568 } 569 570 /* 571 * CHAP does the checksum of the raw nonce, but do it for all 572 * types, since we need to check the timestamp. 573 */ 574 { 575 ssize_t ssize; 576 577 ssize = hex_decode(ireq.u.digestRequest.serverNonce, 578 serverNonce.data, serverNonce.length); 579 if (ssize <= 0) { 580 ret = ENOMEM; 581 krb5_set_error_message(context, ret, "Failed to decode serverNonce"); 582 goto out; 583 } 584 serverNonce.length = ssize; 585 } 586 587 ret = get_digest_key(context, config, server, &crypto); 588 if (ret) 589 goto out; 590 591 ret = krb5_verify_checksum(context, crypto, 592 KRB5_KU_DIGEST_OPAQUE, 593 buf.data, buf.length, &res); 594 free_Checksum(&res); 595 krb5_data_free(&buf); 596 krb5_crypto_destroy(context, crypto); 597 crypto = NULL; 598 if (ret) 599 goto out; 600 601 /* verify time */ 602 { 603 unsigned char *p = serverNonce.data; 604 uint32_t t; 605 606 if (serverNonce.length < 4) { 607 ret = EINVAL; 608 krb5_set_error_message(context, ret, "server nonce too short"); 609 goto out; 610 } 611 t = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); 612 613 if (labs((kdc_time & 0xffffffff) - t) > context->max_skew) { 614 ret = EINVAL; 615 krb5_set_error_message(context, ret, "time screw in server nonce "); 616 goto out; 617 } 618 } 619 620 if (strcasecmp(ireq.u.digestRequest.type, "CHAP") == 0) { 621 EVP_MD_CTX *ctx; 622 unsigned char md[MD5_DIGEST_LENGTH]; 623 char *mdx; 624 char idx; 625 626 if ((config->digests_allowed & CHAP_MD5) == 0) { 627 kdc_log(context, config, 0, "Digest CHAP MD5 not allowed"); 628 goto out; 629 } 630 631 if (ireq.u.digestRequest.identifier == NULL) { 632 ret = EINVAL; 633 krb5_set_error_message(context, ret, "Identifier missing " 634 "from CHAP request"); 635 goto out; 636 } 637 638 if (hex_decode(*ireq.u.digestRequest.identifier, &idx, 1) != 1) { 639 ret = EINVAL; 640 krb5_set_error_message(context, ret, "failed to decode identifier"); 641 goto out; 642 } 643 644 ret = get_password_entry(context, config, 645 ireq.u.digestRequest.username, 646 &password); 647 if (ret) 648 goto out; 649 650 ctx = EVP_MD_CTX_create(); 651 652 EVP_DigestInit_ex(ctx, EVP_md5(), NULL); 653 EVP_DigestUpdate(ctx, &idx, 1); 654 EVP_DigestUpdate(ctx, password, strlen(password)); 655 EVP_DigestUpdate(ctx, serverNonce.data, serverNonce.length); 656 EVP_DigestFinal_ex(ctx, md, NULL); 657 658 EVP_MD_CTX_destroy(ctx); 659 660 hex_encode(md, sizeof(md), &mdx); 661 if (mdx == NULL) { 662 krb5_clear_error_message(context); 663 ret = ENOMEM; 664 goto out; 665 } 666 667 r.element = choice_DigestRepInner_response; 668 669 ret = strcasecmp(mdx, ireq.u.digestRequest.responseData); 670 free(mdx); 671 if (ret == 0) { 672 r.u.response.success = TRUE; 673 } else { 674 kdc_log(context, config, 0, 675 "CHAP reply mismatch for %s", 676 ireq.u.digestRequest.username); 677 r.u.response.success = FALSE; 678 } 679 680 } else if (strcasecmp(ireq.u.digestRequest.type, "SASL-DIGEST-MD5") == 0) { 681 EVP_MD_CTX *ctx; 682 unsigned char md[MD5_DIGEST_LENGTH]; 683 char *mdx; 684 char *A1, *A2; 685 686 if ((config->digests_allowed & DIGEST_MD5) == 0) { 687 kdc_log(context, config, 0, "Digest SASL MD5 not allowed"); 688 goto out; 689 } 690 691 if (ireq.u.digestRequest.nonceCount == NULL) 692 goto out; 693 if (ireq.u.digestRequest.clientNonce == NULL) 694 goto out; 695 if (ireq.u.digestRequest.qop == NULL) 696 goto out; 697 if (ireq.u.digestRequest.realm == NULL) 698 goto out; 699 700 ret = get_password_entry(context, config, 701 ireq.u.digestRequest.username, 702 &password); 703 if (ret) 704 goto failed; 705 706 ctx = EVP_MD_CTX_create(); 707 708 EVP_DigestInit_ex(ctx, EVP_md5(), NULL); 709 EVP_DigestUpdate(ctx, ireq.u.digestRequest.username, 710 strlen(ireq.u.digestRequest.username)); 711 EVP_DigestUpdate(ctx, ":", 1); 712 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.realm, 713 strlen(*ireq.u.digestRequest.realm)); 714 EVP_DigestUpdate(ctx, ":", 1); 715 EVP_DigestUpdate(ctx, password, strlen(password)); 716 EVP_DigestFinal_ex(ctx, md, NULL); 717 718 EVP_DigestInit_ex(ctx, EVP_md5(), NULL); 719 EVP_DigestUpdate(ctx, md, sizeof(md)); 720 EVP_DigestUpdate(ctx, ":", 1); 721 EVP_DigestUpdate(ctx, ireq.u.digestRequest.serverNonce, 722 strlen(ireq.u.digestRequest.serverNonce)); 723 EVP_DigestUpdate(ctx, ":", 1); 724 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.nonceCount, 725 strlen(*ireq.u.digestRequest.nonceCount)); 726 if (ireq.u.digestRequest.authid) { 727 EVP_DigestUpdate(ctx, ":", 1); 728 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.authid, 729 strlen(*ireq.u.digestRequest.authid)); 730 } 731 EVP_DigestFinal_ex(ctx, md, NULL); 732 hex_encode(md, sizeof(md), &A1); 733 if (A1 == NULL) { 734 ret = ENOMEM; 735 krb5_set_error_message(context, ret, "malloc: out of memory"); 736 EVP_MD_CTX_destroy(ctx); 737 goto failed; 738 } 739 740 EVP_DigestInit_ex(ctx, EVP_md5(), NULL); 741 EVP_DigestUpdate(ctx, 742 "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1); 743 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.uri, 744 strlen(*ireq.u.digestRequest.uri)); 745 746 /* conf|int */ 747 if (strcmp(ireq.u.digestRequest.digest, "clear") != 0) { 748 static char conf_zeros[] = ":00000000000000000000000000000000"; 749 EVP_DigestUpdate(ctx, conf_zeros, sizeof(conf_zeros) - 1); 750 } 751 752 EVP_DigestFinal_ex(ctx, md, NULL); 753 754 hex_encode(md, sizeof(md), &A2); 755 if (A2 == NULL) { 756 ret = ENOMEM; 757 krb5_set_error_message(context, ret, "malloc: out of memory"); 758 free(A1); 759 goto failed; 760 } 761 762 EVP_DigestInit_ex(ctx, EVP_md5(), NULL); 763 EVP_DigestUpdate(ctx, A1, strlen(A2)); 764 EVP_DigestUpdate(ctx, ":", 1); 765 EVP_DigestUpdate(ctx, ireq.u.digestRequest.serverNonce, 766 strlen(ireq.u.digestRequest.serverNonce)); 767 EVP_DigestUpdate(ctx, ":", 1); 768 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.nonceCount, 769 strlen(*ireq.u.digestRequest.nonceCount)); 770 EVP_DigestUpdate(ctx, ":", 1); 771 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.clientNonce, 772 strlen(*ireq.u.digestRequest.clientNonce)); 773 EVP_DigestUpdate(ctx, ":", 1); 774 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.qop, 775 strlen(*ireq.u.digestRequest.qop)); 776 EVP_DigestUpdate(ctx, ":", 1); 777 EVP_DigestUpdate(ctx, A2, strlen(A2)); 778 779 EVP_DigestFinal_ex(ctx, md, NULL); 780 781 EVP_MD_CTX_destroy(ctx); 782 783 free(A1); 784 free(A2); 785 786 hex_encode(md, sizeof(md), &mdx); 787 if (mdx == NULL) { 788 krb5_clear_error_message(context); 789 ret = ENOMEM; 790 goto out; 791 } 792 793 r.element = choice_DigestRepInner_response; 794 ret = strcasecmp(mdx, ireq.u.digestRequest.responseData); 795 free(mdx); 796 if (ret == 0) { 797 r.u.response.success = TRUE; 798 } else { 799 kdc_log(context, config, 0, 800 "DIGEST-MD5 reply mismatch for %s", 801 ireq.u.digestRequest.username); 802 r.u.response.success = FALSE; 803 } 804 805 } else if (strcasecmp(ireq.u.digestRequest.type, "MS-CHAP-V2") == 0) { 806 unsigned char md[SHA_DIGEST_LENGTH], challenge[SHA_DIGEST_LENGTH]; 807 krb5_principal clientprincipal = NULL; 808 char *mdx; 809 const char *username; 810 struct ntlm_buf answer; 811 Key *key = NULL; 812 EVP_MD_CTX *ctp; 813 814 if ((config->digests_allowed & MS_CHAP_V2) == 0) { 815 kdc_log(context, config, 0, "MS-CHAP-V2 not allowed"); 816 goto failed; 817 } 818 819 if (ireq.u.digestRequest.clientNonce == NULL) { 820 ret = EINVAL; 821 krb5_set_error_message(context, ret, 822 "MS-CHAP-V2 clientNonce missing"); 823 goto failed; 824 } 825 if (serverNonce.length != 16) { 826 ret = EINVAL; 827 krb5_set_error_message(context, ret, 828 "MS-CHAP-V2 serverNonce wrong length"); 829 goto failed; 830 } 831 832 /* strip of the domain component */ 833 username = strchr(ireq.u.digestRequest.username, '\\'); 834 if (username == NULL) 835 username = ireq.u.digestRequest.username; 836 else 837 username++; 838 839 ctp = EVP_MD_CTX_create(); 840 841 /* ChallengeHash */ 842 EVP_DigestInit_ex(ctp, EVP_sha1(), NULL); 843 { 844 ssize_t ssize; 845 krb5_data clientNonce; 846 847 clientNonce.length = strlen(*ireq.u.digestRequest.clientNonce); 848 clientNonce.data = malloc(clientNonce.length); 849 if (clientNonce.data == NULL) { 850 ret = ENOMEM; 851 krb5_set_error_message(context, ret, 852 "malloc: out of memory"); 853 EVP_MD_CTX_destroy(ctp); 854 goto out; 855 } 856 857 ssize = hex_decode(*ireq.u.digestRequest.clientNonce, 858 clientNonce.data, clientNonce.length); 859 if (ssize != 16) { 860 ret = ENOMEM; 861 krb5_set_error_message(context, ret, 862 "Failed to decode clientNonce"); 863 EVP_MD_CTX_destroy(ctp); 864 goto out; 865 } 866 EVP_DigestUpdate(ctp, clientNonce.data, ssize); 867 free(clientNonce.data); 868 } 869 EVP_DigestUpdate(ctp, serverNonce.data, serverNonce.length); 870 EVP_DigestUpdate(ctp, username, strlen(username)); 871 872 EVP_DigestFinal_ex(ctp, challenge, NULL); 873 874 EVP_MD_CTX_destroy(ctp); 875 876 /* NtPasswordHash */ 877 ret = krb5_parse_name(context, username, &clientprincipal); 878 if (ret) 879 goto failed; 880 881 ret = _kdc_db_fetch(context, config, clientprincipal, 882 HDB_F_GET_CLIENT, NULL, NULL, &user); 883 krb5_free_principal(context, clientprincipal); 884 if (ret) { 885 krb5_set_error_message(context, ret, 886 "MS-CHAP-V2 user %s not in database", 887 username); 888 goto failed; 889 } 890 891 ret = hdb_enctype2key(context, &user->entry, NULL, 892 ETYPE_ARCFOUR_HMAC_MD5, &key); 893 if (ret) { 894 krb5_set_error_message(context, ret, 895 "MS-CHAP-V2 missing arcfour key %s", 896 username); 897 goto failed; 898 } 899 900 /* ChallengeResponse */ 901 ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data, 902 key->key.keyvalue.length, 903 challenge, &answer); 904 if (ret) { 905 krb5_set_error_message(context, ret, "NTLM missing arcfour key"); 906 goto failed; 907 } 908 909 hex_encode(answer.data, answer.length, &mdx); 910 if (mdx == NULL) { 911 free(answer.data); 912 krb5_clear_error_message(context); 913 ret = ENOMEM; 914 goto out; 915 } 916 917 r.element = choice_DigestRepInner_response; 918 ret = strcasecmp(mdx, ireq.u.digestRequest.responseData); 919 if (ret == 0) { 920 r.u.response.success = TRUE; 921 } else { 922 kdc_log(context, config, 0, 923 "MS-CHAP-V2 hash mismatch for %s", 924 ireq.u.digestRequest.username); 925 r.u.response.success = FALSE; 926 } 927 free(mdx); 928 929 if (r.u.response.success) { 930 unsigned char hashhash[MD4_DIGEST_LENGTH]; 931 EVP_MD_CTX *ctxp; 932 933 ctxp = EVP_MD_CTX_create(); 934 935 /* hashhash */ 936 { 937 EVP_DigestInit_ex(ctxp, EVP_md4(), NULL); 938 EVP_DigestUpdate(ctxp, 939 key->key.keyvalue.data, 940 key->key.keyvalue.length); 941 EVP_DigestFinal_ex(ctxp, hashhash, NULL); 942 } 943 944 /* GenerateAuthenticatorResponse */ 945 EVP_DigestInit_ex(ctxp, EVP_sha1(), NULL); 946 EVP_DigestUpdate(ctxp, hashhash, sizeof(hashhash)); 947 EVP_DigestUpdate(ctxp, answer.data, answer.length); 948 EVP_DigestUpdate(ctxp, ms_chap_v2_magic1, 949 sizeof(ms_chap_v2_magic1)); 950 EVP_DigestFinal_ex(ctxp, md, NULL); 951 952 EVP_DigestInit_ex(ctxp, EVP_sha1(), NULL); 953 EVP_DigestUpdate(ctxp, md, sizeof(md)); 954 EVP_DigestUpdate(ctxp, challenge, 8); 955 EVP_DigestUpdate(ctxp, ms_chap_v2_magic2, 956 sizeof(ms_chap_v2_magic2)); 957 EVP_DigestFinal_ex(ctxp, md, NULL); 958 959 r.u.response.rsp = calloc(1, sizeof(*r.u.response.rsp)); 960 if (r.u.response.rsp == NULL) { 961 free(answer.data); 962 krb5_clear_error_message(context); 963 EVP_MD_CTX_destroy(ctxp); 964 ret = ENOMEM; 965 goto out; 966 } 967 968 hex_encode(md, sizeof(md), r.u.response.rsp); 969 if (r.u.response.rsp == NULL) { 970 free(answer.data); 971 krb5_clear_error_message(context); 972 EVP_MD_CTX_destroy(ctxp); 973 ret = ENOMEM; 974 goto out; 975 } 976 977 /* get_master, rfc 3079 3.4 */ 978 EVP_DigestInit_ex(ctxp, EVP_sha1(), NULL); 979 EVP_DigestUpdate(ctxp, hashhash, 16); 980 EVP_DigestUpdate(ctxp, answer.data, answer.length); 981 EVP_DigestUpdate(ctxp, ms_rfc3079_magic1, 982 sizeof(ms_rfc3079_magic1)); 983 EVP_DigestFinal_ex(ctxp, md, NULL); 984 985 free(answer.data); 986 987 EVP_MD_CTX_destroy(ctxp); 988 989 r.u.response.session_key = 990 calloc(1, sizeof(*r.u.response.session_key)); 991 if (r.u.response.session_key == NULL) { 992 krb5_clear_error_message(context); 993 ret = ENOMEM; 994 goto out; 995 } 996 997 ret = krb5_data_copy(r.u.response.session_key, md, 16); 998 if (ret) { 999 krb5_clear_error_message(context); 1000 goto out; 1001 } 1002 } 1003 1004 } else { 1005 int aret; 1006 1007 r.element = choice_DigestRepInner_error; 1008 aret = asprintf(&r.u.error.reason, "Unsupported digest type %s", 1009 ireq.u.digestRequest.type); 1010 if (aret == -1 || r.u.error.reason == NULL) { 1011 ret = ENOMEM; 1012 krb5_set_error_message(context, ret, "malloc: out of memory"); 1013 goto out; 1014 } 1015 r.u.error.code = EINVAL; 1016 } 1017 1018 kdc_log(context, config, 0, "Digest %s request successful %s", 1019 ireq.u.digestRequest.type, ireq.u.digestRequest.username); 1020 1021 break; 1022 } 1023 case choice_DigestReqInner_ntlmInit: 1024 1025 if ((config->digests_allowed & (NTLM_V1|NTLM_V1_SESSION|NTLM_V2)) == 0) { 1026 kdc_log(context, config, 0, "NTLM not allowed"); 1027 goto failed; 1028 } 1029 1030 r.element = choice_DigestRepInner_ntlmInitReply; 1031 1032 r.u.ntlmInitReply.flags = NTLM_NEG_UNICODE; 1033 1034 if ((ireq.u.ntlmInit.flags & NTLM_NEG_UNICODE) == 0) { 1035 kdc_log(context, config, 0, "NTLM client have no unicode"); 1036 goto failed; 1037 } 1038 1039 if (ireq.u.ntlmInit.flags & NTLM_NEG_NTLM) 1040 r.u.ntlmInitReply.flags |= NTLM_NEG_NTLM; 1041 else { 1042 kdc_log(context, config, 0, "NTLM client doesn't support NTLM"); 1043 goto failed; 1044 } 1045 1046 r.u.ntlmInitReply.flags |= 1047 NTLM_NEG_TARGET | 1048 NTLM_TARGET_DOMAIN | 1049 NTLM_ENC_128; 1050 1051 #define ALL \ 1052 NTLM_NEG_SIGN| \ 1053 NTLM_NEG_SEAL| \ 1054 NTLM_NEG_ALWAYS_SIGN| \ 1055 NTLM_NEG_NTLM2_SESSION| \ 1056 NTLM_NEG_KEYEX 1057 1058 r.u.ntlmInitReply.flags |= (ireq.u.ntlmInit.flags & (ALL)); 1059 1060 #undef ALL 1061 1062 r.u.ntlmInitReply.targetname = 1063 get_ntlm_targetname(context, client); 1064 if (r.u.ntlmInitReply.targetname == NULL) { 1065 ret = ENOMEM; 1066 krb5_set_error_message(context, ret, "malloc: out of memory"); 1067 goto out; 1068 } 1069 r.u.ntlmInitReply.challenge.data = malloc(8); 1070 if (r.u.ntlmInitReply.challenge.data == NULL) { 1071 ret = ENOMEM; 1072 krb5_set_error_message(context, ret, "malloc: out of memory"); 1073 goto out; 1074 } 1075 r.u.ntlmInitReply.challenge.length = 8; 1076 if (RAND_bytes(r.u.ntlmInitReply.challenge.data, 1077 r.u.ntlmInitReply.challenge.length) != 1) 1078 { 1079 ret = ENOMEM; 1080 krb5_set_error_message(context, ret, "out of random error"); 1081 goto out; 1082 } 1083 /* XXX fix targetinfo */ 1084 ALLOC(r.u.ntlmInitReply.targetinfo); 1085 if (r.u.ntlmInitReply.targetinfo == NULL) { 1086 ret = ENOMEM; 1087 krb5_set_error_message(context, ret, "malloc: out of memory"); 1088 goto out; 1089 } 1090 1091 ret = fill_targetinfo(context, 1092 r.u.ntlmInitReply.targetname, 1093 client, 1094 r.u.ntlmInitReply.targetinfo); 1095 if (ret) { 1096 ret = ENOMEM; 1097 krb5_set_error_message(context, ret, "malloc: out of memory"); 1098 goto out; 1099 } 1100 1101 /* 1102 * Save data encryted in opaque for the second part of the 1103 * ntlm authentication 1104 */ 1105 sp = krb5_storage_emem(); 1106 if (sp == NULL) { 1107 ret = ENOMEM; 1108 krb5_set_error_message(context, ret, "malloc: out of memory"); 1109 goto out; 1110 } 1111 1112 ret = krb5_storage_write(sp, r.u.ntlmInitReply.challenge.data, 8); 1113 if (ret != 8) { 1114 ret = ENOMEM; 1115 krb5_set_error_message(context, ret, "storage write challenge"); 1116 goto out; 1117 } 1118 ret = krb5_store_uint32(sp, r.u.ntlmInitReply.flags); 1119 if (ret) { 1120 krb5_clear_error_message(context); 1121 goto out; 1122 } 1123 1124 ret = krb5_storage_to_data(sp, &buf); 1125 if (ret) { 1126 krb5_clear_error_message(context); 1127 goto out; 1128 } 1129 1130 ret = get_digest_key(context, config, server, &crypto); 1131 if (ret) 1132 goto out; 1133 1134 ret = krb5_encrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE, 1135 buf.data, buf.length, &r.u.ntlmInitReply.opaque); 1136 krb5_data_free(&buf); 1137 krb5_crypto_destroy(context, crypto); 1138 crypto = NULL; 1139 if (ret) 1140 goto out; 1141 1142 kdc_log(context, config, 0, "NTLM init from %s", from); 1143 1144 break; 1145 1146 case choice_DigestReqInner_ntlmRequest: { 1147 krb5_principal clientprincipal; 1148 unsigned char sessionkey[16]; 1149 unsigned char challenge[8]; 1150 uint32_t flags; 1151 Key *key = NULL; 1152 int version; 1153 1154 r.element = choice_DigestRepInner_ntlmResponse; 1155 r.u.ntlmResponse.success = 0; 1156 r.u.ntlmResponse.flags = 0; 1157 r.u.ntlmResponse.sessionkey = NULL; 1158 r.u.ntlmResponse.tickets = NULL; 1159 1160 /* get username */ 1161 ret = krb5_parse_name(context, 1162 ireq.u.ntlmRequest.username, 1163 &clientprincipal); 1164 if (ret) 1165 goto failed; 1166 1167 ret = _kdc_db_fetch(context, config, clientprincipal, 1168 HDB_F_GET_CLIENT, NULL, NULL, &user); 1169 krb5_free_principal(context, clientprincipal); 1170 if (ret) { 1171 krb5_set_error_message(context, ret, "NTLM user %s not in database", 1172 ireq.u.ntlmRequest.username); 1173 goto failed; 1174 } 1175 1176 ret = get_digest_key(context, config, server, &crypto); 1177 if (ret) 1178 goto failed; 1179 1180 ret = krb5_decrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE, 1181 ireq.u.ntlmRequest.opaque.data, 1182 ireq.u.ntlmRequest.opaque.length, &buf); 1183 krb5_crypto_destroy(context, crypto); 1184 crypto = NULL; 1185 if (ret) { 1186 kdc_log(context, config, 0, 1187 "Failed to decrypt nonce from %s", from); 1188 goto failed; 1189 } 1190 1191 sp = krb5_storage_from_data(&buf); 1192 if (sp == NULL) { 1193 ret = ENOMEM; 1194 krb5_set_error_message(context, ret, "malloc: out of memory"); 1195 goto out; 1196 } 1197 1198 ret = krb5_storage_read(sp, challenge, sizeof(challenge)); 1199 if (ret != sizeof(challenge)) { 1200 ret = ENOMEM; 1201 krb5_set_error_message(context, ret, "NTLM storage read challenge"); 1202 goto out; 1203 } 1204 ret = krb5_ret_uint32(sp, &flags); 1205 if (ret) { 1206 krb5_set_error_message(context, ret, "NTLM storage read flags"); 1207 goto out; 1208 } 1209 krb5_storage_free(sp); 1210 sp = NULL; 1211 krb5_data_free(&buf); 1212 1213 if ((flags & NTLM_NEG_NTLM) == 0) { 1214 ret = EINVAL; 1215 krb5_set_error_message(context, ret, "NTLM not negotiated"); 1216 goto out; 1217 } 1218 1219 ret = hdb_enctype2key(context, &user->entry, NULL, 1220 ETYPE_ARCFOUR_HMAC_MD5, &key); 1221 if (ret) { 1222 krb5_set_error_message(context, ret, "NTLM missing arcfour key"); 1223 goto out; 1224 } 1225 1226 /* check if this is NTLMv2 */ 1227 if (ireq.u.ntlmRequest.ntlm.length != 24) { 1228 struct ntlm_buf infotarget, answer; 1229 char *targetname; 1230 1231 if ((config->digests_allowed & NTLM_V2) == 0) { 1232 kdc_log(context, config, 0, "NTLM v2 not allowed"); 1233 goto out; 1234 } 1235 1236 version = 2; 1237 1238 targetname = get_ntlm_targetname(context, client); 1239 if (targetname == NULL) { 1240 ret = ENOMEM; 1241 krb5_set_error_message(context, ret, "malloc: out of memory"); 1242 goto out; 1243 } 1244 1245 answer.length = ireq.u.ntlmRequest.ntlm.length; 1246 answer.data = ireq.u.ntlmRequest.ntlm.data; 1247 1248 ret = heim_ntlm_verify_ntlm2(key->key.keyvalue.data, 1249 key->key.keyvalue.length, 1250 ireq.u.ntlmRequest.username, 1251 targetname, 1252 0, 1253 challenge, 1254 &answer, 1255 &infotarget, 1256 sessionkey); 1257 free(targetname); 1258 if (ret) { 1259 krb5_set_error_message(context, ret, "NTLM v2 verify failed"); 1260 goto failed; 1261 } 1262 1263 /* XXX verify infotarget matches client (checksum ?) */ 1264 1265 free(infotarget.data); 1266 /* */ 1267 1268 } else { 1269 struct ntlm_buf answer; 1270 1271 version = 1; 1272 1273 if (flags & NTLM_NEG_NTLM2_SESSION) { 1274 unsigned char sessionhash[MD5_DIGEST_LENGTH]; 1275 EVP_MD_CTX *ctx; 1276 1277 if ((config->digests_allowed & NTLM_V1_SESSION) == 0) { 1278 kdc_log(context, config, 0, "NTLM v1-session not allowed"); 1279 ret = EINVAL; 1280 goto failed; 1281 } 1282 1283 if (ireq.u.ntlmRequest.lm.length != 24) { 1284 ret = EINVAL; 1285 krb5_set_error_message(context, ret, "LM hash have wrong length " 1286 "for NTLM session key"); 1287 goto failed; 1288 } 1289 1290 ctx = EVP_MD_CTX_create(); 1291 1292 EVP_DigestInit_ex(ctx, EVP_md5(), NULL); 1293 1294 EVP_DigestUpdate(ctx, challenge, sizeof(challenge)); 1295 EVP_DigestUpdate(ctx, ireq.u.ntlmRequest.lm.data, 8); 1296 EVP_DigestFinal_ex(ctx, sessionhash, NULL); 1297 memcpy(challenge, sessionhash, sizeof(challenge)); 1298 1299 EVP_MD_CTX_destroy(ctx); 1300 1301 } else { 1302 if ((config->digests_allowed & NTLM_V1) == 0) { 1303 kdc_log(context, config, 0, "NTLM v1 not allowed"); 1304 goto failed; 1305 } 1306 } 1307 1308 ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data, 1309 key->key.keyvalue.length, 1310 challenge, &answer); 1311 if (ret) { 1312 krb5_set_error_message(context, ret, "NTLM missing arcfour key"); 1313 goto failed; 1314 } 1315 1316 if (ireq.u.ntlmRequest.ntlm.length != answer.length || 1317 memcmp(ireq.u.ntlmRequest.ntlm.data, answer.data, answer.length) != 0) 1318 { 1319 free(answer.data); 1320 ret = EINVAL; 1321 krb5_set_error_message(context, ret, "NTLM hash mismatch"); 1322 goto failed; 1323 } 1324 free(answer.data); 1325 1326 { 1327 EVP_MD_CTX *ctx; 1328 1329 ctx = EVP_MD_CTX_create(); 1330 1331 EVP_DigestInit_ex(ctx, EVP_md4(), NULL); 1332 EVP_DigestUpdate(ctx, 1333 key->key.keyvalue.data, 1334 key->key.keyvalue.length); 1335 EVP_DigestFinal_ex(ctx, sessionkey, NULL); 1336 1337 EVP_MD_CTX_destroy(ctx); 1338 } 1339 } 1340 1341 if (ireq.u.ntlmRequest.sessionkey) { 1342 unsigned char masterkey[MD4_DIGEST_LENGTH]; 1343 EVP_CIPHER_CTX *rc4; 1344 size_t len; 1345 1346 if ((flags & NTLM_NEG_KEYEX) == 0) { 1347 ret = EINVAL; 1348 krb5_set_error_message(context, ret, 1349 "NTLM client failed to neg key " 1350 "exchange but still sent key"); 1351 goto failed; 1352 } 1353 1354 len = ireq.u.ntlmRequest.sessionkey->length; 1355 if (len != sizeof(masterkey)){ 1356 ret = EINVAL; 1357 krb5_set_error_message(context, ret, 1358 "NTLM master key wrong length: %lu", 1359 (unsigned long)len); 1360 goto failed; 1361 } 1362 1363 1364 #if OPENSSL_VERSION_NUMBER < 0x10100000UL 1365 EVP_CIPHER_CTX rc4s; 1366 rc4 = &rc4s; 1367 EVP_CIPHER_CTX_init(rc4); 1368 #else 1369 rc4 = EVP_CIPHER_CTX_new(); 1370 #endif 1371 EVP_CipherInit_ex(rc4, EVP_rc4(), NULL, sessionkey, NULL, 1); 1372 EVP_Cipher(rc4, 1373 masterkey, ireq.u.ntlmRequest.sessionkey->data, 1374 sizeof(masterkey)); 1375 #if OPENSSL_VERSION_NUMBER < 0x10100000UL 1376 EVP_CIPHER_CTX_cleanup(rc4); 1377 #else 1378 EVP_CIPHER_CTX_free(rc4); 1379 #endif 1380 r.u.ntlmResponse.sessionkey = 1381 malloc(sizeof(*r.u.ntlmResponse.sessionkey)); 1382 if (r.u.ntlmResponse.sessionkey == NULL) { 1383 ret = EINVAL; 1384 krb5_set_error_message(context, ret, "malloc: out of memory"); 1385 goto out; 1386 } 1387 1388 ret = krb5_data_copy(r.u.ntlmResponse.sessionkey, 1389 masterkey, sizeof(masterkey)); 1390 if (ret) { 1391 krb5_set_error_message(context, ret, "malloc: out of memory"); 1392 goto out; 1393 } 1394 } 1395 1396 r.u.ntlmResponse.success = 1; 1397 kdc_log(context, config, 0, "NTLM version %d successful for %s", 1398 version, ireq.u.ntlmRequest.username); 1399 break; 1400 } 1401 case choice_DigestReqInner_supportedMechs: 1402 1403 kdc_log(context, config, 0, "digest supportedMechs from %s", from); 1404 1405 r.element = choice_DigestRepInner_supportedMechs; 1406 memset(&r.u.supportedMechs, 0, sizeof(r.u.supportedMechs)); 1407 1408 if (config->digests_allowed & NTLM_V1) 1409 r.u.supportedMechs.ntlm_v1 = 1; 1410 if (config->digests_allowed & NTLM_V1_SESSION) 1411 r.u.supportedMechs.ntlm_v1_session = 1; 1412 if (config->digests_allowed & NTLM_V2) 1413 r.u.supportedMechs.ntlm_v2 = 1; 1414 if (config->digests_allowed & DIGEST_MD5) 1415 r.u.supportedMechs.digest_md5 = 1; 1416 if (config->digests_allowed & CHAP_MD5) 1417 r.u.supportedMechs.chap_md5 = 1; 1418 if (config->digests_allowed & MS_CHAP_V2) 1419 r.u.supportedMechs.ms_chap_v2 = 1; 1420 break; 1421 1422 default: { 1423 const char *s; 1424 ret = EINVAL; 1425 krb5_set_error_message(context, ret, "unknown operation to digest"); 1426 1427 failed: 1428 1429 s = krb5_get_error_message(context, ret); 1430 if (s == NULL) { 1431 krb5_clear_error_message(context); 1432 goto out; 1433 } 1434 1435 kdc_log(context, config, 0, "Digest failed with: %s", s); 1436 1437 r.element = choice_DigestRepInner_error; 1438 r.u.error.reason = strdup("unknown error"); 1439 krb5_free_error_message(context, s); 1440 if (r.u.error.reason == NULL) { 1441 ret = ENOMEM; 1442 krb5_set_error_message(context, ret, "malloc: out of memory"); 1443 goto out; 1444 } 1445 r.u.error.code = EINVAL; 1446 break; 1447 } 1448 } 1449 1450 ASN1_MALLOC_ENCODE(DigestRepInner, buf.data, buf.length, &r, &size, ret); 1451 if (ret) { 1452 krb5_set_error_message(context, ret, "Failed to encode inner digest reply"); 1453 goto out; 1454 } 1455 if (size != buf.length) 1456 krb5_abortx(context, "ASN1 internal error"); 1457 1458 krb5_auth_con_addflags(context, ac, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL); 1459 1460 ret = krb5_mk_rep (context, ac, &rep.apRep); 1461 if (ret) 1462 goto out; 1463 1464 { 1465 krb5_keyblock *key; 1466 1467 ret = krb5_auth_con_getlocalsubkey(context, ac, &key); 1468 if (ret) 1469 goto out; 1470 1471 ret = krb5_crypto_init(context, key, 0, &crypto); 1472 krb5_free_keyblock (context, key); 1473 if (ret) 1474 goto out; 1475 } 1476 1477 ret = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT, 1478 buf.data, buf.length, 0, 1479 &rep.innerRep); 1480 1481 ASN1_MALLOC_ENCODE(DigestREP, reply->data, reply->length, &rep, &size, ret); 1482 if (ret) { 1483 krb5_set_error_message(context, ret, "Failed to encode digest reply"); 1484 goto out; 1485 } 1486 if (size != reply->length) 1487 krb5_abortx(context, "ASN1 internal error"); 1488 1489 1490 out: 1491 if (ac) 1492 krb5_auth_con_free(context, ac); 1493 if (ret) 1494 krb5_warn(context, ret, "Digest request from %s failed", from); 1495 if (ticket) 1496 krb5_free_ticket(context, ticket); 1497 if (id) 1498 krb5_kt_close(context, id); 1499 if (crypto) 1500 krb5_crypto_destroy(context, crypto); 1501 if (sp) 1502 krb5_storage_free(sp); 1503 if (user) 1504 _kdc_free_ent (context, user); 1505 if (server) 1506 _kdc_free_ent (context, server); 1507 if (client) 1508 _kdc_free_ent (context, client); 1509 if (password) { 1510 memset(password, 0, strlen(password)); 1511 free (password); 1512 } 1513 if (client_name) 1514 free (client_name); 1515 krb5_data_free(&buf); 1516 krb5_data_free(&serverNonce); 1517 free_Checksum(&res); 1518 free_DigestREP(&rep); 1519 free_DigestRepInner(&r); 1520 free_DigestReqInner(&ireq); 1521 1522 return ret; 1523 } 1524 1525 #endif /* DIGEST */ 1526