1/* 2 * Copyright (c) 2006 - 2007 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2009 - 2011 Apple Inc. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#define HC_DEPRECATED_CRYPTO 37 38#include "headers.h" 39 40typedef struct pk_client_params pk_client_params; 41struct DigestREQ; 42struct Kx509Request; 43typedef struct kdc_request_desc *kdc_request_t; 44 45#include <kdc-private.h> 46 47#include <asn1-common.h> 48#include <digest_asn1.h> 49#include <heimntlm.h> 50#include <roken.h> 51 52#include <gssapi.h> 53#include <gssapi_spi.h> 54#include <gssapi_ntlm.h> 55 56#include "heimbase.h" 57 58#ifdef HAVE_OPENDIRECTORY 59 60#include <CoreFoundation/CoreFoundation.h> 61#include <OpenDirectory/OpenDirectory.h> 62#include <SystemConfiguration/SCPreferences.h> 63 64#ifdef __APPLE_PRIVATE__ 65#include <OpenDirectory/OpenDirectoryPriv.h> 66#endif 67 68 69#endif 70 71#if defined(__APPLE_PRIVATE__) && !defined(__APPLE_TARGET_EMBEDDED__) 72#include <dlfcn.h> 73#define HAVE_APPLE_NETLOGON 1 74#endif 75 76 77#ifdef __APPLE__ 78#include <sandbox.h> 79 80static int sandbox_flag = 1; 81#endif 82static int use_unix_flag = 0; 83static int help_flag = 0; 84static int version_flag = 0; 85 86 87 88static krb5_kdc_configuration *config = NULL; 89 90static void 91fillin_hostinfo(struct ntlm_targetinfo *ti); 92 93static const char * 94derive_version_ntq(const NTLMRequest2 *ntq) 95{ 96 if ((ntq->ntlmFlags & NTLM_NEG_NTLM2_SESSION) && 97 (ntq->lmChallengeResponse.length >= 8) && 98 (ntq->ntChallengeResponse.length == 24)) 99 return "ntlmv1-with-v2-session"; 100 if (ntq->ntChallengeResponse.length == 24) 101 return "ntlmv1"; 102 if (ntq->ntChallengeResponse.length > 24) 103 return "ntlmv2"; 104 if (ntq->ntChallengeResponse.length == 0) 105 return "lm"; 106 return "unknown"; 107} 108 109static void 110log_complete(krb5_context context, 111 const char *type, 112 const NTLMReply *ntp, 113 krb5_error_code ret, 114 const char *ntlm_version) 115{ 116 117 if (ntp->success) { 118 char str[256] = { 0 } ; 119 heim_ntlm_unparse_flags(ntp->ntlmFlags, str, sizeof(str)); 120 121 kdc_log(context, config, 1, "digest-request %s: ok user=%s\\%s proto=%s flags: %s", 122 type, 123 ntp->domain, ntp->user, ntlm_version, str); 124 } else 125 kdc_log(context, config, 1, "digest-request: %s failed with %d proto=%s", type, ret, ntlm_version); 126} 127 128 129#ifdef ENABLE_NTLM 130 131static int 132validate_local(krb5_context context, 133 const char *key, 134 const char *local, 135 const char *client) 136{ 137 if (local == NULL) 138 return 0; 139 if (client == NULL || strcmp(local, client) != 0) { 140 kdc_log(context, config, 1, "validate failed for key %s: %s client: %s", 141 key, local, client ? client : "<not sent>"); 142 return EAUTH; 143 } 144 return 0; 145} 146 147/* 148 * Validate that the client sent back the expected target info, 149 * the important keys are: 150 * - servername 151 * - timestamp 152 */ 153 154static int 155validate_targetinfo(krb5_context context, 156 struct ntlm_buf *tibuf, 157 const char *domain, 158 const char *acceptorUser, 159 uint32_t *avflags) 160{ 161 struct ntlm_targetinfo localti; 162 struct ntlm_targetinfo ti; 163 int ret; 164 165 if (tibuf->length == 0) 166 return EAUTH; 167 168 memset(&localti, 0, sizeof(localti)); 169 170 fillin_hostinfo(&localti); 171 172 ret = heim_ntlm_decode_targetinfo(tibuf, 1, &ti); 173 if (ret) 174 goto out; 175 176 if (ti.timestamp) { 177 time_t now = time(NULL); 178 /* 179 * "now" should be equal or forward in compared to 180 * ti.timestamp, but lets be liberal here. 181 */ 182 if (krb5_time_abs(now, heim_ntlm_ts2unixtime(ti.timestamp)) > heim_ntlm_time_skew) { 183 ret = EAUTH; 184 goto out; 185 } 186 } 187 188 ret = validate_local(context, "servername", localti.servername, ti.servername); 189 if (ret) 190 goto out; 191 ret = validate_local(context, "domainname", domain, ti.domainname); 192 if (ret) 193 goto out; 194 if (domain == NULL) { 195 ret = validate_local(context, "domainname", localti.domainname, ti.domainname); 196 if (ret) 197 goto out; 198 } 199 200 *avflags = ti.avflags; 201 202 /* 203 * Check that the service that the client send is matching what 204 * the server claims to be. 205 */ 206 if (acceptorUser[0] != '\0' && ti.targetname) { 207 const char *p = strchr(ti.targetname, '/'); 208 size_t len = strlen(acceptorUser); 209 210 if (p && len != (p - ti.targetname) && 211 strncasecmp(ti.targetname, acceptorUser, len) != 0) 212 { 213 ret = EAUTH; 214 goto out; 215 } 216 } 217 218 /* more validation here 219 char *dnstreename; 220 char *targetname; 221 uint32_t avflags; 222 struct ntlm_buf channel_bindings; 223 */ 224 225 out: 226 heim_ntlm_free_targetinfo(&ti); 227 heim_ntlm_free_targetinfo(&localti); 228 229 return ret; 230} 231 232 233 234struct ntlm_ftable { 235 void *ctx; 236 krb5_error_code (*init)(krb5_context, void **); 237 void (*fini)(void *); 238 int (*ti)(void *, struct ntlm_targetinfo *ti); 239 uint32_t (*filter_flags)(krb5_context, void *); 240 int (*authenticate)(void *ctx, 241 krb5_context context, 242 const NTLMRequest2 *ntq, 243 void *response_ctx, 244 void (response)(void *, const NTLMReply *reply)); 245}; 246 247static uint32_t 248kdc_flags(krb5_context context, void *ctx) 249{ 250 return 0; 251} 252 253 254static int 255kdc_authenticate(void *ctx, 256 krb5_context context, 257 const NTLMRequest2 *ntq, 258 void *response_ctx, 259 void (response)(void *, const NTLMReply *ntp)) 260{ 261 krb5_principal client = NULL; 262 unsigned char sessionkey[16]; 263 krb5_data sessionkeydata; 264 hdb_entry_ex *user = NULL; 265 HDB *clientdb = NULL; 266 Key *key = NULL; 267 NTLMReply ntp; 268 const char *ntlm_version = "unknown"; 269 int ret; 270 271 krb5_data_zero(&sessionkeydata); 272 memset(&ntp, 0, sizeof(ntp)); 273 274 kdc_log(context, config, 1, "digest-request: user=%s\\%s", 275 ntq->loginDomainName, ntq->loginUserName); 276 277 if (ntq->lmchallenge.length != 8){ 278 ret = EINVAL; 279 goto failed; 280 } 281 282 if (ntq->ntChallengeResponse.length == 0) { 283 ret = EINVAL; 284 goto failed; 285 } 286 287 ret = krb5_make_principal(context, &client, ntq->loginDomainName, 288 ntq->loginUserName, NULL); 289 if (ret) 290 goto failed; 291 292 krb5_principal_set_type(context, client, KRB5_NT_NTLM); 293 294 ret = _kdc_db_fetch(context, config, client, 295 HDB_F_GET_CLIENT, NULL, &clientdb, &user); 296 if (ret) 297 goto failed; 298 299 if (user->entry.principal->name.name_string.len < 1) { 300 kdc_log(context, config, 2, 301 "digest-request: user %s\\%s, have weired name", 302 ntq->loginDomainName, ntq->loginUserName); 303 goto failed; 304 } 305 306 ntp.domain = user->entry.principal->realm; 307 ntp.user = user->entry.principal->name.name_string.val[0]; 308 309 ret = hdb_enctype2key(context, &user->entry, 310 ETYPE_ARCFOUR_HMAC_MD5, &key); 311 if (ret) { 312 kdc_log(context, config, 2, 313 "digest-request: user %s\\%s, missing NTLM key", 314 ntp.domain, ntp.user); 315 goto failed; 316 } 317 318 kdc_log(context, config, 2, 319 "digest-request: found user, processing ntlm request", ret); 320 321 if (ntq->ntChallengeResponse.length != 24) { 322 struct ntlm_buf targetinfo, answer; 323 unsigned char ntlmv2[16]; 324 325 ntlm_version = "ntlmv2"; 326 327 if (!gss_mo_get(GSS_NTLM_MECHANISM, GSS_C_NTLM_V2, NULL)) { 328 kdc_log(context, config, 2, "NTLMv2 disabled"); 329 goto failed; 330 } 331 332#if 0 /* if there is an LM2 challange we should check it */ 333 if (ntq->lmChallengeResponse.length < 8) { 334 kdc_log(context, config, 2, 335 "lmChallengeResponse field is < 8 (%d)", 336 (int)ntq->lmChallengeResponse.length); 337 goto failed; 338 } 339#endif 340 341 answer.length = ntq->ntChallengeResponse.length; 342 answer.data = ntq->ntChallengeResponse.data; 343 344 ret = heim_ntlm_verify_ntlm2(key->key.keyvalue.data, 345 key->key.keyvalue.length, 346 ntq->loginUserName, 347 ntq->loginDomainName, 348 0, 349 ntq->lmchallenge.data, 350 &answer, 351 &targetinfo, 352 ntlmv2); 353 if (ret) { 354 if (clientdb->hdb_auth_status) 355 clientdb->hdb_auth_status(context, clientdb, user, 356 HDB_AUTH_WRONG_PASSWORD); 357 358 kdc_log(context, config, 2, 359 "digest-request: verify ntlm2 hash failed", ret); 360 goto failed; 361 } 362 363 if (clientdb->hdb_auth_status) 364 clientdb->hdb_auth_status(context, clientdb, user, 365 HDB_AUTH_SUCCESS); 366 367 { 368 size_t len = MIN(ntq->ntChallengeResponse.length, 16); 369 CCHmacContext c; 370 371 kdc_log(context, config, 2, 372 "digest-request: basic key"); 373 374 CCHmacInit(&c, kCCHmacAlgMD5, ntlmv2, sizeof(ntlmv2)); 375 CCHmacUpdate(&c, ntq->ntChallengeResponse.data, len); 376 CCHmacFinal(&c, sessionkey); 377 memset(&c, 0, sizeof(c)); 378 } 379 380 memset(ntlmv2, 0, sizeof(ntlmv2)); 381 382 ret = validate_targetinfo(context, &targetinfo, ntq->t2targetname, ntq->acceptorUser, 383 &ntp.avflags); 384 if (ret) { 385 kdc_log(context, config, 2, "NTLMv2 targetinfo validation failed"); 386 goto failed; 387 } 388 389 ntp.targetinfo.data = targetinfo.data; 390 ntp.targetinfo.length = targetinfo.length; 391 392 } else { 393 struct ntlm_buf answer; 394 uint8_t challenge[8]; 395 uint8_t usk[16]; 396 CCDigestRef digest; 397 398 ntlm_version = "ntlmv1"; 399 400 if (!gss_mo_get(GSS_NTLM_MECHANISM, GSS_C_NTLM_V1, NULL)) { 401 kdc_log(context, config, 2, "NTLMv1 disabled"); 402 goto failed; 403 } 404 405 if (ntq->lmchallenge.length != 8) { 406 ret = EINVAL; 407 goto failed; 408 } 409 410 if (ntq->ntlmFlags & NTLM_NEG_NTLM2_SESSION) { 411 unsigned char sessionhash[CC_MD5_DIGEST_LENGTH]; 412 413 /* the first first 8 bytes is the challenge, what is the other 16 bytes ? */ 414 if (ntq->lmChallengeResponse.length != 24) { 415 ret = EINVAL; 416 goto failed; 417 } 418 419 digest = CCDigestCreate(kCCDigestMD5); 420 CCDigestUpdate(digest, ntq->lmchallenge.data, 8); 421 CCDigestUpdate(digest, ntq->lmChallengeResponse.data, 8); 422 CCDigestFinal(digest, sessionhash); 423 CCDigestDestroy(digest); 424 memcpy(challenge, sessionhash, sizeof(challenge)); 425 } else { 426 memcpy(challenge, ntq->lmchallenge.data, ntq->lmchallenge.length); 427 } 428 429 ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data, 430 key->key.keyvalue.length, 431 challenge, &answer); 432 if (ret) 433 goto failed; 434 435 if (ntq->ntChallengeResponse.length != answer.length || 436 memcmp(ntq->ntChallengeResponse.data, answer.data, answer.length) != 0) { 437 free(answer.data); 438 kdc_log(context, config, 2, 439 "digest-request: verify ntlm1 hash failed", ret); 440 ret = EINVAL; 441 goto failed; 442 } 443 free(answer.data); 444 445 digest = CCDigestCreate(kCCDigestMD4); 446 CCDigestUpdate(digest, 447 key->key.keyvalue.data, 448 key->key.keyvalue.length); 449 CCDigestFinal(digest, usk); 450 CCDigestDestroy(digest); 451 452 453 if ((ntq->ntlmFlags & NTLM_NEG_NTLM2_SESSION) != 0) { 454 CCHmacContext hc; 455 456 CCHmacInit(&hc, kCCHmacAlgMD5, usk, sizeof(usk)); 457 CCHmacUpdate(&hc, ntq->lmchallenge.data, 8); 458 CCHmacUpdate(&hc, ntq->lmChallengeResponse.data, 8); 459 CCHmacFinal(&hc, sessionkey); 460 memset(&hc, 0, sizeof(hc)); 461 } else { 462 memcpy(sessionkey, usk, sizeof(sessionkey)); 463 } 464 } 465 466 if (ntq->ntlmFlags & NTLM_NEG_KEYEX) { 467 struct ntlm_buf base, enc, sess; 468 469 base.data = sessionkey; 470 base.length = sizeof(sessionkey); 471 enc.data = ntq->encryptedSessionKey.data; 472 enc.length = ntq->encryptedSessionKey.length; 473 474 ret = heim_ntlm_keyex_unwrap(&base, &enc, &sess); 475 if (ret != 0) 476 goto failed; 477 if (sess.length != sizeof(sessionkey)) { 478 heim_ntlm_free_buf(&sess); 479 ret = EINVAL; 480 goto failed; 481 } 482 memcpy(sessionkey, sess.data, sizeof(sessionkey)); 483 heim_ntlm_free_buf(&sess); 484 } 485 486 sessionkeydata.data = sessionkey; 487 sessionkeydata.length = sizeof(sessionkey); 488 ntp.sessionkey = &sessionkeydata; 489 490 ntp.success = 1; 491 ntp.ntlmFlags = ntq->ntlmFlags; 492 493 (*response)(response_ctx, &ntp); 494 495failed: 496 log_complete(context, "kdc", &ntp, ret, ntlm_version); 497 498 if (ntp.targetinfo.data) 499 free(ntp.targetinfo.data); 500 501 if (user) 502 _kdc_free_ent (context, user); 503 504 if (client) 505 krb5_free_principal(context, client); 506 507 return ret; 508} 509 510/* 511 * 512 */ 513 514#ifdef HAVE_OPENDIRECTORY 515 516struct ntlmod { 517 ODNodeRef node; 518}; 519 520/* 521 * 522 */ 523 524static CFArrayRef meta_keys; 525static char *ntlmDomain; 526 527static void 528update_ntlm(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info) 529{ 530 CFDictionaryRef settings; 531 CFStringRef n; 532 533 if (store == NULL) 534 return; 535 536 settings = (CFDictionaryRef)SCDynamicStoreCopyValue(store, CFSTR("com.apple.smb")); 537 if (settings == NULL) 538 return; 539 540 n = CFDictionaryGetValue(settings, CFSTR("NetBIOSName")); 541 if (n == NULL || CFGetTypeID(n) != CFStringGetTypeID()) 542 goto fin; 543 544 if (ntlmDomain) 545 free(ntlmDomain); 546 ntlmDomain = rk_cfstring2cstring(n); 547 strupr(ntlmDomain); 548 549fin: 550 CFRelease(settings); 551 return; 552} 553 554static void 555ntlm_notification(void) 556{ 557 SCDynamicStoreRef store; 558 dispatch_queue_t queue; 559 560 store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("com.apple.Kerberos.gss-od"), update_ntlm, NULL); 561 if (store == NULL) 562 return; 563 564 CFTypeRef key[] = {CFSTR("com.apple.smb")}; 565 CFArrayRef keys = CFArrayCreate(kCFAllocatorDefault, key, 1, NULL); 566 SCDynamicStoreSetNotificationKeys(store, keys, NULL); 567 CFRelease(keys); 568 569 queue = dispatch_queue_create("com.apple.gss-od.ntlm-name", NULL); 570 if (queue == NULL) { 571 CFRelease(store); 572 errx(1, "dispatch_queue_create"); 573 } 574 575 SCDynamicStoreSetDispatchQueue(store, queue); 576 CFRelease(store); 577 578 dispatch_sync(queue, ^{ update_ntlm(store, NULL, NULL); }); 579} 580 581 582static void 583load_meta_keys(void) 584{ 585 CFMutableArrayRef keys; 586 587 keys = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks); 588 CFArrayAppendValue(keys, kODAttributeTypeMetaNodeLocation); 589 meta_keys = keys; 590} 591 592 593static int 594od_init(krb5_context context, void **ctx) 595{ 596 static dispatch_once_t once; 597 struct ntlmod *c; 598 599 dispatch_once(&once, ^{ 600 ntlm_notification(); 601 load_meta_keys(); 602 }); 603 604 if (ntlmDomain == NULL) 605 return ENOENT; 606 607 c = calloc(1, sizeof(*c)); 608 if (c == NULL) 609 return ENOMEM; 610 611 c->node = ODNodeCreateWithNodeType(kCFAllocatorDefault, 612 kODSessionDefault, 613 kODNodeTypeAuthentication, NULL); 614 if (c->node == NULL) { 615 free(c); 616 return ENOENT; 617 } 618 619 *ctx = c; 620 621 return 0; 622} 623 624 625static uint32_t 626od_flags(krb5_context context, void *ctx) 627{ 628 CFArrayRef subnodes; 629 CFIndex count, n; 630 ODNodeRef node; 631 632 if (gss_mo_get(GSS_NTLM_MECHANISM, GSS_C_NTLM_SESSION_KEY, NULL)) 633 return 0; 634 635#ifdef __APPLE_PRIVATE__ 636 subnodes = ODSessionCopySessionKeySupport(kODSessionDefault); 637 if (subnodes) { 638 int haveLDAPSessionKeySupport = 0; 639 640 count = CFArrayGetCount(subnodes); 641 for (n = 0; n < count; n++) { 642 CFStringRef name; 643 644 node = (ODNodeRef)CFArrayGetValueAtIndex(subnodes, n); 645 646 name = ODNodeGetName(node); 647 if (name && CFStringHasPrefix(name, CFSTR("/LDAPv3/"))) { 648 kdc_log(context, config, 2, "digest-request: have /LDAPv3/ nodes with signing"); 649 haveLDAPSessionKeySupport = 1; 650 break; 651 } 652 } 653 CFRelease(subnodes); 654 655 if (haveLDAPSessionKeySupport) 656 return 0; 657 658 kdc_log(context, config, 2, "digest-request: have no LDAPv3 nodes with signing"); 659 660 } else { 661 kdc_log(context, config, 2, "digest-request: have no nodes with signing"); 662 } 663#endif 664 665 node = ODNodeCreateWithName(kCFAllocatorDefault, kODSessionDefault, CFSTR("/LDAPv3"), NULL); 666 if (node == NULL) 667 return 0; 668 669 subnodes = ODNodeCopySubnodeNames(node, NULL); 670 CFRelease(node); 671 672 if (subnodes == NULL) 673 return 0; 674 675 count = CFArrayGetCount(subnodes); 676 CFRelease(subnodes); 677 if (count == 0) 678 return 0; 679 680 /* 681 * Drat, we have OD configured. So no session key support, we must turn that off for everyone! 682 */ 683 684 return NTLM_NEG_SIGN | NTLM_NEG_SEAL | 685 NTLM_NEG_ALWAYS_SIGN | NTLM_NEG_NTLM2_SESSION | 686 NTLM_NEG_KEYEX; 687} 688 689static int 690od_authenticate(void *ctx, 691 krb5_context context, 692 const NTLMRequest2 *ntq, 693 void *response_ctx, 694 void (response)(void *, const NTLMReply *ntp)) 695{ 696 struct ntlmod *c = ctx; 697 CFArrayRef outAuthItems = NULL; 698 CFMutableArrayRef authItems = NULL; 699 ODRecordRef record = NULL; 700 Boolean b; 701 CFStringRef username = NULL, domain = NULL; 702 CFErrorRef error = NULL; 703 CFMutableDataRef challenge = NULL, resp = NULL; 704 CFArrayRef values = NULL; 705 NTLMReply ntp; 706 int ret = EINVAL; 707 708 if (ntlmDomain == NULL || ctx == NULL) { 709 kdc_log(context, config, 2, "digest-request: no ntlmDomain"); 710 return EINVAL; 711 } 712 713 memset(&ntp, 0, sizeof(ntp)); 714 715 username = CFStringCreateWithCString(NULL, ntq->loginUserName, 716 kCFStringEncodingUTF8); 717 if (username == NULL) { 718 ret = ENOMEM; 719 goto out; 720 } 721 722 challenge = CFDataCreateMutable(NULL, 0); 723 if (challenge == NULL) { 724 ret = ENOMEM; 725 goto out; 726 } 727 CFDataAppendBytes(challenge, ntq->lmchallenge.data, ntq->lmchallenge.length); 728 729 resp = CFDataCreateMutable(NULL, 0); 730 if (response == NULL) { 731 ret = ENOMEM; 732 goto out; 733 } 734 CFDataAppendBytes(resp, ntq->ntChallengeResponse.data, ntq->ntChallengeResponse.length); 735 736 domain = CFStringCreateWithCString(NULL, ntq->loginDomainName, kCFStringEncodingUTF8); 737 if (domain == NULL) { 738 ret = ENOMEM; 739 goto out; 740 } 741 742 authItems = CFArrayCreateMutable(NULL, 5, &kCFTypeArrayCallBacks); 743 if (authItems == NULL) { 744 ret = ENOMEM; 745 goto out; 746 } 747 748 CFArrayAppendValue(authItems, username); 749 CFArrayAppendValue(authItems, challenge); 750 CFArrayAppendValue(authItems, resp); 751 CFArrayAppendValue(authItems, username); 752 CFArrayAppendValue(authItems, domain); 753 754 record = ODNodeCopyRecord(c->node, kODRecordTypeUsers, 755 username, meta_keys, &error); 756 if (record == NULL) { 757 kdc_log(context, config, 2, "digest-request: failed to find user %s in OD", ntq->loginUserName); 758 ret = ENOENT; 759 goto out; 760 } 761 762 values = ODRecordCopyValues(record, kODAttributeTypeMetaNodeLocation, NULL); 763 if (values && CFArrayGetCount(values) > 0) { 764 CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(values, 0); 765 if (CFStringGetTypeID() != CFGetTypeID(str)) { 766 ret = EINVAL; 767 goto out; 768 } 769 if (!CFStringHasPrefix(str, CFSTR("/LDAPv3/"))) { 770 kdc_log(context, config, 2, "digest-request: user not in /LDAPv3"); 771 ret = ENOENT; 772 goto out; 773 } 774 } 775 776 b = ODRecordVerifyPasswordExtended(record, 777 kODAuthenticationTypeNTLMv2WithSessionKey, 778 authItems, &outAuthItems, NULL, 779 &error); 780 781 if (!b) { 782 kdc_log(context, config, 2, "digest-request: authentication failed"); 783 ret = ENOENT; 784 goto out; 785 } 786 787 ntp.ntlmFlags = ntq->ntlmFlags; 788 789 /* 790 * If the NTLMv2 session key is supported, it is returned in the 791 * output buffer 792 */ 793 794 if (outAuthItems && CFArrayGetCount(outAuthItems) > 0) { 795 CFDataRef data = (CFDataRef)CFArrayGetValueAtIndex(outAuthItems, 0); 796 if (CFGetTypeID(data) == CFDataGetTypeID() && CFDataGetLength(data) >= 16) { 797 ntp.sessionkey = calloc(1, sizeof(*ntp.sessionkey)); 798 if (ntp.sessionkey == NULL) { 799 ret = ENOMEM; 800 goto out; 801 } 802 krb5_data_copy(ntp.sessionkey, CFDataGetBytePtr(data), 16); 803 804 if (ntp.ntlmFlags & NTLM_NEG_KEYEX) { 805 struct ntlm_buf base, enc, sess; 806 807 base.data = ntp.sessionkey->data; 808 base.length = ntp.sessionkey->length; 809 enc.data = ntq->encryptedSessionKey.data; 810 enc.length = ntq->encryptedSessionKey.length; 811 812 ret = heim_ntlm_keyex_unwrap(&base, &enc, &sess); 813 if (ret != 0) 814 goto out; 815 if (sess.length != ntp.sessionkey->length) { 816 heim_ntlm_free_buf(&sess); 817 ret = EINVAL; 818 goto out; 819 } 820 memcpy(ntp.sessionkey->data, sess.data, ntp.sessionkey->length); 821 heim_ntlm_free_buf(&sess); 822 } 823 } 824 825 } else { 826 /* 827 * No ntlmv2 session key or keyex for OD (this it too late to strip of these flags) 828 */ 829 ntp.ntlmFlags &= ~(NTLM_NEG_KEYEX|NTLM_NEG_NTLM2_SESSION); 830 } 831 832 833 /* 834 * If no session key was passed back, strip of SIGN/SEAL 835 */ 836 837 if (ntp.sessionkey == NULL || ntp.sessionkey->length == 0) 838 ntp.ntlmFlags &= ~(NTLM_NEG_SIGN|NTLM_NEG_SEAL|NTLM_NEG_ALWAYS_SIGN); 839 840 841 ntp.user = ntq->loginUserName; 842 ntp.domain = ntlmDomain; 843 844 ntp.success = 1; 845 response(response_ctx, &ntp); 846 ret = 0; 847 848out: 849 log_complete(context, "od", &ntp, ret, derive_version_ntq(ntq)); 850 851 if (values) 852 CFRelease(values); 853 if (error) 854 CFRelease(error); 855 if (challenge) 856 CFRelease(challenge); 857 if (resp) 858 CFRelease(resp); 859 if (username) 860 CFRelease(username); 861 if (domain) 862 CFRelease(domain); 863 if (authItems) 864 CFRelease(authItems); 865 if (outAuthItems) 866 CFRelease(outAuthItems); 867 if (record) 868 CFRelease(record); 869 870 return ret; 871} 872 873#endif 874 875/* 876 * 877 */ 878 879static uint32_t 880guest_flags(krb5_context context, void *ctx) 881{ 882 return 0; 883} 884 885static int 886guest_authenticate(void *ctx, 887 krb5_context context, 888 const NTLMRequest2 *ntq, 889 void *response_ctx, 890 void (response)(void *, const NTLMReply *ntp)) 891{ 892 unsigned char sessionkey[16]; 893 krb5_data sessionkeydata; 894 NTLMReply ntp; 895 int is_guest = (strcasecmp("GUEST", ntq->loginUserName) == 0); 896 int ret = EINVAL; 897 898 memset(&ntp, 0, sizeof(ntp)); 899 900 if (is_guest || (ntq->ntlmFlags & NTLM_NEG_ANONYMOUS)) { 901 kdc_log(context, config, 2, 902 "digest-request: found guest, let in"); 903 ntp.avflags |= NTLM_TI_AV_FLAG_GUEST; 904 ntp.user = "GUEST"; 905 906#ifdef HAVE_OPENDIRECTORY 907 ntp.domain = ntlmDomain; 908#endif 909 if (ntp.domain == NULL) 910 ntp.domain = ntq->loginDomainName; 911 912 if (ntp.user == NULL || ntp.domain == NULL) 913 goto out; 914 915 if (is_guest) { 916 /* no password */ 917 CCDigest(kCCDigestMD4, (void *)"", 0, sessionkey); 918 } 919 } else { 920 ret = EINVAL; 921 goto out; 922 } 923 924 if ((ntq->ntlmFlags & NTLM_NEG_ANONYMOUS)) { 925 926 } else if (ntq->ntChallengeResponse.length != 24) { 927 size_t len = MIN(ntq->ntChallengeResponse.length, 16); 928 struct ntlm_buf targetinfo, answer; 929 uint8_t ntlmv2[16]; 930 CCHmacContext c; 931 932 answer.length = ntq->ntChallengeResponse.length; 933 answer.data = ntq->ntChallengeResponse.data; 934 935 ret = heim_ntlm_verify_ntlm2(sessionkey, 936 sizeof(sessionkey), 937 ntq->loginUserName, 938 ntq->loginDomainName, 939 0, 940 ntq->lmchallenge.data, 941 &answer, 942 &targetinfo, 943 ntlmv2); 944 if (ret) { 945 kdc_log(context, config, 2, 946 "digest-request: guest authentication failed: %d", ret); 947 goto out; 948 } 949 950 free(targetinfo.data); 951 952 kdc_log(context, config, 2, 953 "digest-request: basic key"); 954 955 CCHmacInit(&c, kCCHmacAlgMD5, ntlmv2, sizeof(ntlmv2)); 956 CCHmacUpdate(&c, ntq->ntChallengeResponse.data, len); 957 CCHmacFinal(&c, sessionkey); 958 memset(&c, 0, sizeof(c)); 959 } else { 960 CCDigestRef digest; 961 uint8_t usk[16]; 962 963 digest = CCDigestCreate(kCCDigestMD4); 964 CCDigestUpdate(digest, sessionkey, sizeof(sessionkey)); 965 CCDigestFinal(digest, usk); 966 CCDigestDestroy(digest); 967 968 if ((ntq->ntlmFlags & NTLM_NEG_NTLM2_SESSION) != 0) { 969 CCHmacContext hc; 970 971 CCHmacInit(&hc, kCCHmacAlgMD5, usk, sizeof(usk)); 972 CCHmacUpdate(&hc, ntq->lmchallenge.data, 8); 973 CCHmacUpdate(&hc, ntq->lmChallengeResponse.data, 8); 974 CCHmacFinal(&hc, sessionkey); 975 memset(&hc, 0, sizeof(hc)); 976 } else { 977 memcpy(sessionkey, usk, sizeof(sessionkey)); 978 } 979 980 } 981 982 if (ntq->ntlmFlags & NTLM_NEG_KEYEX) { 983 struct ntlm_buf base, enc, sess; 984 985 base.data = sessionkey; 986 base.length = sizeof(sessionkey); 987 enc.data = ntq->encryptedSessionKey.data; 988 enc.length = ntq->encryptedSessionKey.length; 989 990 ret = heim_ntlm_keyex_unwrap(&base, &enc, &sess); 991 if (ret != 0) 992 goto out; 993 if (sess.length != sizeof(sessionkey)) { 994 heim_ntlm_free_buf(&sess); 995 ret = EINVAL; 996 goto out; 997 } 998 memcpy(sessionkey, sess.data, sizeof(sessionkey)); 999 heim_ntlm_free_buf(&sess); 1000 } 1001 1002 sessionkeydata.data = sessionkey; 1003 sessionkeydata.length = sizeof(sessionkey); 1004 ntp.sessionkey = &sessionkeydata; 1005 1006 ntp.ntlmFlags = ntq->ntlmFlags; 1007 ntp.success = 1; 1008 1009 response(response_ctx, &ntp); 1010 ret = 0; 1011out: 1012 log_complete(context, "guest", &ntp, ret, derive_version_ntq(ntq)); 1013 1014 return ret; 1015} 1016 1017#ifdef HAVE_APPLE_NETLOGON 1018 1019#define LOGON_USER 0x00000001L 1020#define LOGON_NOENCRYPTION 0x00000002L 1021#define LOGON_CACHED_ACCOUNT 0x00000004L 1022#define LOGON_USED_LM_PASSWORD 0x00000008L 1023#define LOGON_EXTRA_SIDS 0x00000020L 1024#define LOGON_SUBAUTH_SESSION_KEY 0x00000040L 1025#define LOGON_SERVER_TRUST_ACCOUNT 0x00000080L 1026#define LOGON_NTLMV2_ENABLED 0x00000100L 1027#define LOGON_RESOURCE_GROUPS 0x00000200L 1028#define LOGON_PROFILE_PATH_RETURNED 0x00000400L 1029#define LOGON_NT_V2 0x00000800L 1030#define LOGON_LM_V2 0x00001000L 1031#define LOGON_NTLM_V2 0x00002000L 1032 1033#define NTLM_LOGON_REQ_VERSION 1 1034 1035typedef struct _NTLM_LOGON_REQ { 1036 uint32_t Version; 1037 const char *LogonDomainName; 1038 const char *UserName; 1039 const char *Workstation; 1040 uint8_t LmChallenge[8]; 1041 const uint8_t *NtChallengeResponse; 1042 uint16_t NtChallengeResponseLength; 1043 const uint8_t *LmChallengeResponse; 1044 uint16_t LmChallengeResponseLength; 1045} NTLM_LOGON_REQ, *PNTLM_LOGON_REQ; 1046 1047typedef uint32_t 1048(*NTLM_LOGON_FN)(const NTLM_LOGON_REQ *LogonReq, 1049 uint8_t **pAuthData, 1050 size_t *pAuthDataSize, 1051 char **pAccountName, 1052 char **pAccountDomain, 1053 uint8_t SessionKey[16], 1054 uint32_t *Flags); 1055 1056typedef enum _NETLOGON_PROBE_STATUS { 1057 NETLOGON_PROBE_UNAVAILABLE = -1, /* Could not reach RPC server */ 1058 NETLOGON_PROBE_NOT_CONFIGURED = 0, /* RPC server available but not joined to domain */ 1059 NETLOGON_PROBE_CONFIGURED = 1, /* RPC server available and joined to domain */ 1060 NETLOGON_PROBE_CONNECTED = 2 /* RPC server available and secure channel up */ 1061} NETLOGON_PROBE_STATUS; 1062 1063typedef NETLOGON_PROBE_STATUS 1064(*NET_LOGON_PROBE_FN)(const char *, char **); 1065 1066/* 1067 * 1068 */ 1069 1070struct ntlmnetr { 1071 OM_uint32 flags; 1072 char *domain; 1073}; 1074 1075/* 1076 * 1077 */ 1078 1079static char *netlogonDomain; 1080static char *netlogonServer; 1081 1082 1083static void 1084update_domain(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info) 1085{ 1086 CFDictionaryRef settings; 1087 CFStringRef n; 1088 size_t len; 1089 1090 if (store == NULL) 1091 return; 1092 1093 settings = (CFDictionaryRef)SCDynamicStoreCopyValue(store, CFSTR("com.apple.opendirectoryd.ActiveDirectory")); 1094 if (settings == NULL) 1095 return; 1096 1097 n = CFDictionaryGetValue(settings, CFSTR("DomainNameFlat")); 1098 if (n == NULL || CFGetTypeID(n) != CFStringGetTypeID()) 1099 goto fin; 1100 1101 if (netlogonDomain) 1102 free(netlogonDomain); 1103 netlogonDomain = rk_cfstring2cstring(n); 1104 1105 n = CFDictionaryGetValue(settings, CFSTR("TrustAccount")); 1106 if (n == NULL || CFGetTypeID(n) != CFStringGetTypeID()) 1107 goto fin; 1108 1109 if (netlogonServer) 1110 free(netlogonServer); 1111 netlogonServer = rk_cfstring2cstring(n); 1112 1113 len = strlen(netlogonServer); 1114 if (len > 1 && netlogonServer[len - 1] == '$') 1115 netlogonServer[len - 1] = '\0'; 1116 1117 strupr(netlogonServer); 1118 1119fin: 1120 CFRelease(settings); 1121 return; 1122} 1123 1124 1125static void 1126domain_notification(void) 1127{ 1128 SCDynamicStoreRef store; 1129 dispatch_queue_t queue; 1130 1131 store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("com.apple.Kerberos.gss-netlogon"), update_domain, NULL); 1132 if (store == NULL) 1133 return; 1134 1135 CFTypeRef key[] = {CFSTR("com.apple.opendirectoryd.ActiveDirectory")}; 1136 CFArrayRef keys = CFArrayCreate(kCFAllocatorDefault, key, 1, NULL); 1137 SCDynamicStoreSetNotificationKeys(store, keys, NULL); 1138 CFRelease(keys); 1139 1140 queue = dispatch_queue_create("com.apple.gss-netlogon.ntlm-name", NULL); 1141 if (queue == NULL) { 1142 CFRelease(store); 1143 errx(1, "dispatch_queue_create"); 1144 } 1145 1146 SCDynamicStoreSetDispatchQueue(store, queue); 1147 1148 dispatch_sync(queue, ^{ update_domain(store, NULL, NULL); }); 1149} 1150 1151 1152/* 1153 * 1154 */ 1155 1156static void *nt_framework_handle = NULL; 1157static NET_LOGON_PROBE_FN netLogonProbe = NULL; 1158static NTLM_LOGON_FN nTLMLogon = NULL; 1159 1160#define NT_FRAMEWORK_PATH "/System/Library/PrivateFrameworks/nt.framework/nt" 1161 1162static void 1163init_nt_framework(void *ctx) 1164{ 1165 nt_framework_handle = dlopen(NT_FRAMEWORK_PATH, RTLD_LOCAL); 1166 if (nt_framework_handle == NULL) 1167 return; 1168 1169 netLogonProbe = dlsym(nt_framework_handle, "NetLogonProbe"); 1170 nTLMLogon = dlsym(nt_framework_handle, "NTLMLogon"); 1171 1172 1173 if (netLogonProbe && nTLMLogon) { 1174 domain_notification(); 1175 } 1176} 1177 1178 1179 1180static int 1181netr_init(krb5_context context, void **ctx) 1182{ 1183 static heim_base_once_t init_once = HEIM_BASE_ONCE_INIT; 1184 NETLOGON_PROBE_STATUS probeStatus; 1185 struct ntlmnetr *c; 1186 char *domain; 1187 1188 heim_base_once_f(&init_once, NULL, init_nt_framework); 1189 1190 if (netLogonProbe == NULL || nTLMLogon == NULL) 1191 return ENOENT; 1192 1193 probeStatus = netLogonProbe(NULL, &domain); 1194 kdc_log(context, config, 1, "digest-request: netr probe %d", (int)probeStatus); 1195 switch (probeStatus) { 1196 case NETLOGON_PROBE_CONFIGURED: 1197 case NETLOGON_PROBE_CONNECTED: 1198 break; 1199 default: 1200 return ENOENT; 1201 } 1202 1203 c = calloc(1, sizeof(*c)); 1204 if (c == NULL) { 1205 free(domain); 1206 return ENOMEM; 1207 } 1208 c->domain = domain; 1209 1210 *ctx = c; 1211 1212 return GSS_S_COMPLETE; 1213} 1214 1215static uint32_t 1216netr_flags(krb5_context context, void *ctx) 1217{ 1218 return 0; 1219} 1220 1221static int 1222netr_ti(void *ctx, struct ntlm_targetinfo *ti) 1223{ 1224 if (netlogonServer == NULL || netlogonDomain == NULL) 1225 return EINVAL; 1226 1227 if ((ti->servername = strdup(netlogonServer)) == NULL) 1228 return ENOMEM; 1229 1230 if ((ti->domainname = strdup(netlogonDomain)) == NULL) { 1231 free(ti->servername); 1232 return ENOMEM; 1233 } 1234 1235 return 0; 1236} 1237 1238/* 1239 * 1240 */ 1241 1242static int 1243netr_authenticate(void *ctx, 1244 krb5_context context, 1245 const NTLMRequest2 *ntq, 1246 void *response_ctx, 1247 void (response)(void *, const NTLMReply *ntp)) 1248{ 1249 struct ntlmnetr *c = ctx; 1250 OM_uint32 ret; 1251 NTLM_LOGON_REQ logonReq; 1252 krb5_data sessionkeydata; 1253 uint8_t sessionkey[16]; 1254 uint32_t userFlags; 1255 int ntlm2; 1256 NTLMReply ntp; 1257 krb5_data pac; 1258 1259 if (netlogonDomain == NULL || netlogonServer == NULL || ctx == NULL) 1260 return EINVAL; 1261 1262 memset(&ntp, 0, sizeof(ntp)); 1263 1264 krb5_data_zero(&sessionkeydata); 1265 1266 logonReq.Version = NTLM_LOGON_REQ_VERSION; 1267 logonReq.LogonDomainName = ntq->loginDomainName; 1268 logonReq.UserName = ntq->loginUserName; 1269 logonReq.Workstation = ntq->workstation; 1270 1271 /* 1272 * From http://davenport.sourceforge.net/ntlm.html: 1273 * 1274 * "Instead of sending the server challenge directly 1275 * over the NetLogon pipe to the domain controller, 1276 * the server sends the MD5 hash of the server challenge 1277 * concatenated with the client nonce (lifted from the LM 1278 * response field)." 1279 * 1280 * Unfortunately, the client doesn't give us any indication 1281 * whether it's using NTLMv1 with NTLM2 security or rather 1282 * NTLMv2. It appears we can only determine this from the 1283 * response length, which will be >24 for NTLMv2. 1284 */ 1285 ntlm2 = (ntq->ntlmFlags & NTLM_NEG_NTLM2_SESSION) && 1286 (ntq->lmChallengeResponse.length >= 8) && 1287 (ntq->ntChallengeResponse.length == 24); 1288 1289 if (ntlm2) { 1290 ret = heim_ntlm_calculate_ntlm2_sess_hash(ntq->lmChallengeResponse.data, 1291 ntq->lmchallenge.data, 1292 logonReq.LmChallenge); 1293 if (ret) 1294 goto out; 1295 } else { 1296 memcpy(logonReq.LmChallenge, ntq->lmchallenge.data, 8); 1297 c->flags &= ~(LOGON_NTLMV2_ENABLED); 1298 } 1299 1300 logonReq.NtChallengeResponseLength = ntq->ntChallengeResponse.length; 1301 logonReq.NtChallengeResponse = ntq->ntChallengeResponse.data; 1302 logonReq.LmChallengeResponseLength = ntq->lmChallengeResponse.length; 1303 logonReq.LmChallengeResponse = ntq->lmChallengeResponse.data; 1304 1305 ret = nTLMLogon(&logonReq, 1306 (uint8_t **)&pac.data, 1307 &pac.length, 1308 &ntp.user, 1309 &ntp.domain, 1310 sessionkey, 1311 &userFlags); 1312 if (ret) { 1313 kdc_log(context, config, 1, "digest-request netr: failed user=%s\\%s DC status code %08x", 1314 ntq->loginDomainName, 1315 ntq->loginUserName, 1316 ret); 1317 goto out; 1318 } 1319 1320 /* XXX copy into ntp */ 1321 free(pac.data); 1322 1323 if (ntlm2) { 1324 /* http://davenport.sourceforge.net/ntlm.html#theNtlm2SessionResponseUserSessionKey */ 1325 heim_ntlm_derive_ntlm2_sess(sessionkey, 1326 ntq->lmChallengeResponse.data, 8, 1327 ntq->lmchallenge.data, 1328 sessionkey); 1329 } 1330 1331 ntp.ntlmFlags = ntq->ntlmFlags; 1332 1333 /* 1334 * If the client sent a NTLMv2 response (as opposed to a 1335 * NTLMv1 reponse with NTLM2 security), then we also need 1336 * to negotiate a NTLM2 session key. 1337 */ 1338 if (userFlags & LOGON_NTLMV2_ENABLED) 1339 ntp.ntlmFlags |= NTLM_NEG_NTLM2_SESSION; 1340 1341 if (ntq->ntlmFlags & NTLM_NEG_KEYEX) { 1342 struct ntlm_buf base, enc, sess; 1343 1344 base.data = sessionkey; 1345 base.length = sizeof(sessionkey); 1346 enc.data = ntq->encryptedSessionKey.data; 1347 enc.length = ntq->encryptedSessionKey.length; 1348 1349 ret = heim_ntlm_keyex_unwrap(&base, &enc, &sess); 1350 if (ret != 0) 1351 goto out; 1352 if (sess.length != sizeof(sessionkey)) { 1353 heim_ntlm_free_buf(&sess); 1354 ret = EINVAL; 1355 goto out; 1356 } 1357 memcpy(sessionkey, sess.data, sizeof(sessionkey)); 1358 heim_ntlm_free_buf(&sess); 1359 } 1360 1361 sessionkeydata.data = sessionkey; 1362 sessionkeydata.length = sizeof(sessionkey); 1363 ntp.sessionkey = &sessionkeydata; 1364 1365 ntp.success = 1; 1366 1367 response(response_ctx, &ntp); 1368 ret = 0; 1369 1370out: 1371 log_complete(context, "netr", &ntp, ret, derive_version_ntq(ntq)); 1372 1373 if (ntp.user) 1374 free(ntp.user); 1375 if (ntp.domain) 1376 free(ntp.domain); 1377 return ret; 1378} 1379 1380#endif 1381 1382/* 1383 * 1384 */ 1385 1386struct ntlm_ftable backends[] = { 1387#ifdef HAVE_APPLE_NETLOGON 1388 { 1389 NULL, 1390 netr_init, 1391 NULL, 1392 netr_ti, 1393 netr_flags, 1394 netr_authenticate 1395 }, 1396#endif 1397#ifdef HAVE_OPENDIRECTORY 1398 { 1399 NULL, 1400 od_init, 1401 NULL, 1402 NULL, 1403 od_flags, 1404 od_authenticate 1405 }, 1406#endif 1407 { 1408 NULL, 1409 NULL, 1410 NULL, 1411 NULL, 1412 kdc_flags, 1413 kdc_authenticate 1414 }, 1415 { 1416 NULL, 1417 NULL, 1418 NULL, 1419 NULL, 1420 guest_flags, 1421 guest_authenticate 1422 } 1423}; 1424 1425struct callback { 1426 heim_sipc_call cctx; 1427 heim_ipc_complete complete; 1428}; 1429 1430static void 1431complete_callback(void *ctx, const NTLMReply *ntp) 1432{ 1433 struct callback *c = ctx; 1434 heim_idata rep; 1435 size_t size; 1436 int ret; 1437 1438 ASN1_MALLOC_ENCODE(NTLMReply, rep.data, rep.length, ntp, &size, ret); 1439 if (ret) 1440 return; 1441 if (rep.length != size) 1442 abort(); 1443 1444 c->complete(c->cctx, ret, &rep); 1445 1446 free(rep.data); 1447} 1448 1449/* 1450 * 1451 */ 1452 1453static void 1454fillin_hostinfo(struct ntlm_targetinfo *ti) 1455{ 1456 char local_hostname[MAXHOSTNAMELEN], *p; 1457 1458 if (netlogonServer && ti->servername == NULL) 1459 ti->servername = strdup(netlogonServer); 1460 1461 if (gethostname(local_hostname, sizeof(local_hostname)) < 0) 1462 return; 1463 local_hostname[sizeof(local_hostname) - 1] = '\0'; 1464 1465 /* make up a server name */ 1466 ti->dnsservername = strdup(local_hostname); 1467 1468 p = strchr(local_hostname, '.'); 1469 if (p) { 1470 *p = '\0'; 1471 p++; 1472 } 1473 if (ti->servername == NULL) { 1474 strupr(local_hostname); 1475 ti->servername = strdup(local_hostname); 1476 } 1477 1478 if (p && p[0]) 1479 ti->dnsdomainname = strdup(p); 1480} 1481 1482 1483/* 1484 * Given the configuration, try to return the NTLM domain that is our 1485 * default DOMAIN. 1486 * 1487 * If we are not bound, pick the first in realm configuration that 1488 * matches the users preference. 1489 * 1490 * If we find no configuration, just return BUILTIN. 1491 */ 1492 1493static krb5_error_code 1494get_ntlm_domain(krb5_context context, 1495 krb5_kdc_configuration *lconfig, 1496 const char *indomain, 1497 struct ntlm_targetinfo *ti) 1498{ 1499 char *domain = NULL; 1500 krb5_error_code ret; 1501 unsigned int i; 1502 1503 for (i = 0; i < sizeof(backends) / sizeof(backends[0]); i++) { 1504 if (backends[i].ti == NULL) 1505 continue; 1506 1507 memset(ti, 0, sizeof(*ti)); 1508 ret = backends[i].ti(backends[i].ctx, ti); 1509 if (ret) 1510 continue; 1511 1512 if (indomain == NULL || strcmp(indomain, ti->domainname) == 0) 1513 goto dnsinfo; 1514 1515 heim_ntlm_free_targetinfo(ti); 1516 } 1517 1518 for (i = 0; i < lconfig->num_db; i++) { 1519 if (lconfig->db[i]->hdb_get_ntlm_domain == NULL) 1520 continue; 1521 ret = lconfig->db[i]->hdb_get_ntlm_domain(context, 1522 lconfig->db[i], 1523 &domain); 1524 if (ret == 0 && domain) 1525 break; 1526 } 1527 if (domain == NULL) 1528 domain = strdup("BUILTIN"); 1529 1530 memset(ti, 0, sizeof(*ti)); 1531 1532 ti->domainname = domain; 1533 1534 dnsinfo: 1535 1536 fillin_hostinfo(ti); 1537 1538 return 0; 1539} 1540 1541 1542/* 1543 * process type1/init request 1544 */ 1545 1546static void 1547process_NTLMInit(krb5_context context, 1548 NTLMInit *ni, 1549 heim_ipc_complete complete, 1550 heim_sipc_call cctx) 1551{ 1552 heim_idata rep = { 0, NULL }; 1553 struct ntlm_targetinfo ti; 1554 NTLMInitReply ir; 1555 char *indomain = NULL; 1556 size_t size, n; 1557 int ret = 0; 1558 1559 memset(&ir, 0, sizeof(ir)); 1560 memset(&ti, 0, sizeof(ti)); 1561 1562 kdc_log(context, config, 1, "digest-request: init request"); 1563 1564 ir.ntlmNegFlags = 0; 1565 for (n = 0; n < sizeof(backends) / sizeof(backends[0]); n++) 1566 ir.ntlmNegFlags |= backends[n].filter_flags(context, backends[n].ctx); 1567 1568 if (ni->domain && (*ni->domain)[0] != '\0') 1569 indomain = *ni->domain; 1570 1571 ret = get_ntlm_domain(context, config, indomain, &ti); 1572 if (ret) 1573 goto failed; 1574 1575 ti.timestamp = heim_ntlm_unix2ts_time(time(NULL)); 1576 1577 kdc_log(context, config, 1, "digest-request: init return domain: %s server: %s indomain was: %s", 1578 ti.domainname, ti.servername, indomain ? indomain : "<NULL>"); 1579 1580 { 1581 struct ntlm_buf d; 1582 1583 ret = heim_ntlm_encode_targetinfo(&ti, 1, &d); 1584 if (ret) 1585 goto failed; 1586 ir.targetinfo.data = d.data; 1587 ir.targetinfo.length = d.length; 1588 } 1589 1590 ASN1_MALLOC_ENCODE(NTLMInitReply, rep.data, rep.length, &ir, &size, ret); 1591 free_NTLMInitReply(&ir); 1592 if (ret) 1593 goto failed; 1594 if (rep.length != size) 1595 abort(); 1596 1597failed: 1598 if (ret) 1599 kdc_log(context, config, 1, "digest-request: %d", ret); 1600 1601 heim_ntlm_free_targetinfo(&ti); 1602 1603 (*complete)(cctx, ret, &rep); 1604 free(rep.data); 1605 1606 return; 1607} 1608 1609/* 1610 * 1611 */ 1612 1613static void 1614ntlm_service(void *ctx, const heim_idata *req, 1615 const heim_icred cred, 1616 heim_ipc_complete complete, 1617 heim_sipc_call cctx) 1618{ 1619 krb5_context context = ctx; 1620 static dispatch_once_t once; 1621 NTLMRequest2 ntq; 1622 NTLMInit ni; 1623 1624 kdc_log(context, config, 1, "digest-request: uid=%d", 1625 (int)heim_ipc_cred_get_uid(cred)); 1626 1627 if (heim_ipc_cred_get_uid(cred) != 0) { 1628 (*complete)(cctx, EPERM, NULL); 1629 return; 1630 } 1631 1632 dispatch_once(&once, ^{ 1633 size_t n; 1634 for (n = 0; n < sizeof(backends) / sizeof(backends[0]); n++) 1635 if (backends[n].init) 1636 backends[n].init(context, &backends[n].ctx); 1637 }); 1638 1639 /* check if its a type1/init request */ 1640 if (decode_NTLMInit(req->data, req->length, &ni, NULL) == 0) { 1641 process_NTLMInit(context, &ni, complete, cctx); 1642 free_NTLMInit(&ni); 1643 } else if (decode_NTLMRequest2(req->data, req->length, &ntq, NULL) == 0) { 1644 struct callback c = { 1645 .complete = complete, 1646 .cctx = cctx 1647 }; 1648 size_t n; 1649 int ret = EPERM; 1650 1651 for (n = 0; n < sizeof(backends) / sizeof(backends[0]); n++) { 1652 ret = backends[n].authenticate(backends[n].ctx, context, &ntq, &c, complete_callback); 1653 if (ret == 0) 1654 break; 1655 } 1656 free_NTLMRequest2(&ntq); 1657 if (ret) 1658 (*complete)(cctx, ret, NULL); 1659 } 1660} 1661 1662 1663#endif 1664 1665static struct getargs args[] = { 1666#ifdef __APPLE__ 1667 { "sandbox", 0, arg_negative_flag, &sandbox_flag, 1668 "use sandbox or not" 1669 }, 1670#endif /* __APPLE__ */ 1671 { "unix", 0, arg_flag, &use_unix_flag, 1672 "support unix sockets" 1673 }, 1674 { "help", 'h', arg_flag, &help_flag }, 1675 { "version", 'v', arg_flag, &version_flag } 1676}; 1677 1678static int num_args = sizeof(args) / sizeof(args[0]); 1679 1680static void 1681usage(int ret) 1682{ 1683 arg_printusage (args, num_args, NULL, ""); 1684 exit (ret); 1685} 1686 1687 1688int 1689main(int argc, char **argv) 1690{ 1691 krb5_context context; 1692 int ret, optidx = 0; 1693 1694 setprogname(argv[0]); 1695 1696 __gss_ntlm_is_digest_service = 1; 1697 1698 if (getarg(args, num_args, argc, argv, &optidx)) 1699 usage(1); 1700 1701 if (help_flag) 1702 usage(0); 1703 1704 if (version_flag) { 1705 print_version(NULL); 1706 exit(0); 1707 } 1708 1709 ret = krb5_init_context(&context); 1710 if (ret) 1711 krb5_errx(context, 1, "krb5_init_context"); 1712 1713 ret = krb5_kdc_get_config(context, &config); 1714 if (ret) 1715 krb5_err(context, 1, ret, "krb5_kdc_default_config"); 1716 1717 kdc_openlog(context, "digest-service", config); 1718 1719 ret = krb5_kdc_set_dbinfo(context, config); 1720 if (ret) 1721 krb5_err(context, 1, ret, "krb5_kdc_set_dbinfo"); 1722 1723#ifdef __APPLE__ 1724 if (sandbox_flag) { 1725 char *errorstring; 1726 ret = sandbox_init("kdc", SANDBOX_NAMED, &errorstring); 1727 if (ret) 1728 errx(1, "sandbox_init failed: %d: %s", ret, errorstring); 1729 } 1730#endif 1731 1732#ifdef ENABLE_NTLM 1733#if __APPLE__ 1734 { 1735 heim_sipc mach; 1736 heim_sipc_launchd_mach_init("org.h5l.ntlm-service", 1737 ntlm_service, context, &mach); 1738 } 1739#endif 1740 if (use_unix_flag) { 1741 heim_sipc un; 1742 heim_sipc_service_unix("org.h5l.ntlm-service", ntlm_service, NULL, &un); 1743 } 1744#endif 1745 heim_sipc_timeout(60); 1746 1747 heim_ipc_main(); 1748 return 0; 1749} 1750