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