1/* 2 * Copyright (c) 2009 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 37#include "hdb_locl.h" 38#include <heimbase.h> 39#include <hx509.h> 40#include <base64.h> 41#include <rfc2459_asn1.h> 42#include <ifaddrs.h> 43#include <heimbase.h> 44#include <roken.h> 45 46#ifdef HAVE_OPENDIRECTORY 47 48#include <CoreFoundation/CoreFoundation.h> 49#include <OpenDirectory/OpenDirectory.h> 50#ifdef __APPLE_PRIVATE__ 51#include <OpenDirectory/OpenDirectoryPriv.h> 52#include <DirectoryServer/DirectoryServer.h> 53#include <dlfcn.h> 54#endif 55#include <SystemConfiguration/SCPreferences.h> 56 57struct iter_ctx { 58 CFIndex idx; 59 CFArrayRef odRecordArray; 60} iter; 61 62struct hdb_od; 63 64typedef krb5_error_code (*hod_locate_record)(krb5_context, struct hdb_od *, krb5_principal, unsigned, int, int *, ODRecordRef *); 65 66typedef struct hdb_od { 67 CFStringRef rootName; 68 ODNodeRef rootNode; 69 CFArrayRef inNodeAttributes; 70 CFArrayRef inKDCAttributes; 71 CFArrayRef inComputerOrUsers; 72 CFStringRef restoreRoot; 73 char *LKDCRealm; 74 SCDynamicStoreRef *store; 75 char *ntlmDomain; 76 struct iter_ctx iter; 77 hod_locate_record locate_record; 78} *hdb_od; 79 80static CFStringRef kRealName = CFSTR("dsAttrTypeStandard:RealName"); 81 82static const char wellknown_lkdc[] = "WELLKNOWN:COM.APPLE.LKDC"; 83 84/* 85 * 86 */ 87 88typedef struct hdb_entry_ex_ctx { 89 krb5_principal principal; 90 ODRecordRef record; 91} hdb_entry_ex_ctx; 92 93static void 94free_hod_ctx(krb5_context context, hdb_entry_ex *entry) 95{ 96 hdb_entry_ex_ctx *ctx = entry->ctx; 97 if (ctx) { 98 if (ctx->record) 99 CFRelease(ctx->record); 100 free(ctx); 101 } 102 entry->ctx = NULL; 103} 104 105 106static hdb_entry_ex_ctx * 107get_ex_ctx(hdb_entry_ex *entry) 108{ 109 if (entry->ctx == NULL) { 110 entry->ctx = calloc(1, sizeof(*entry->ctx)); 111 if (entry->ctx == NULL) 112 return NULL; 113 entry->free_entry = free_hod_ctx; 114 } 115 return entry->ctx; 116} 117 118/* 119 * 120 */ 121 122static int 123is_lkdc(hdb_od d, const char *realm) 124{ 125 return d->LKDCRealm != NULL && krb5_realm_is_lkdc(realm); 126} 127 128static krb5_error_code 129map_service(krb5_context context, krb5_const_principal principal, krb5_principal *eprinc) 130{ 131 const char *service; 132 krb5_error_code ret; 133 134 ret = krb5_copy_principal(context, principal, eprinc); 135 if (ret) 136 return ret; 137 138 service = krb5_principal_get_comp_string(context, principal, 0); 139 if (strcasecmp(service, "afpserver") == 0 || 140 strcasecmp(service, "cifs") == 0 || 141 strcasecmp(service, "smb") == 0 || 142 strcasecmp(service, "vnc") == 0) { 143 144 free((*eprinc)->name.name_string.val[0]); 145 (*eprinc)->name.name_string.val[0] = strdup("host"); 146 } 147 return 0; 148} 149 150static bool 151tryAgainP(krb5_context context, int *tryAgain, CFErrorRef *error) 152{ 153 if (*tryAgain <= 1) 154 return 0; 155 156 if (error && *error) { 157 CFRelease(*error); 158 *error = NULL; 159 } 160 (*tryAgain)--; 161 162 krb5_warnx(context, "try fetching result again"); 163 164 return 1; 165} 166 167#define MAX_TRIES 3 168 169 170/* 171 * Returns the matching User or Computer record, if not, returns the node itself 172 */ 173 174static ODRecordRef 175HODODNodeCopyLinkageRecordFromAuthenticationData(krb5_context context, ODNodeRef node, ODRecordRef record) 176{ 177 CFMutableArrayRef types = NULL; 178 CFArrayRef linkageArray = NULL, resultArray = NULL; 179 CFTypeRef userLinkage; 180 ODQueryRef query = NULL; 181 CFErrorRef error = NULL; 182 ODRecordRef res; 183 int tryAgain = MAX_TRIES; 184 185 types = CFArrayCreateMutable(NULL, 2, &kCFTypeArrayCallBacks); 186 if (types == NULL) 187 goto out; 188 189 CFArrayAppendValue(types, kODRecordTypeUsers); 190 CFArrayAppendValue(types, kODRecordTypeComputers); 191 192 linkageArray = ODRecordCopyValues(record, CFSTR("dsAttrTypeNative:userLinkage"), &error); 193 if (linkageArray == NULL) 194 goto out; 195 196 if (CFArrayGetCount(linkageArray) == 0) 197 goto out; 198 199 userLinkage = CFArrayGetValueAtIndex(linkageArray, 0); 200 if (userLinkage == NULL) 201 goto out; 202 203 do { 204 query = ODQueryCreateWithNode(NULL, node, types, 205 CFSTR("dsAttrTypeNative:entryUUID"), 206 kODMatchEqualTo, 207 userLinkage, NULL, 1, &error); 208 if (query == NULL) 209 goto out; 210 211 resultArray = ODQueryCopyResults(query, FALSE, &error); 212 CFRelease(query); 213 if (resultArray == NULL && error == NULL) 214 tryAgain = 0; 215 } while(resultArray == NULL && tryAgainP(context, &tryAgain, &error)); 216 if (resultArray == NULL) 217 goto out; 218 219 if (CFArrayGetCount(resultArray) == 0) 220 goto out; 221 222 res = (ODRecordRef)CFArrayGetValueAtIndex(resultArray, 0); 223 if (res) 224 record = res; 225 226out: 227 CFRetain(record); 228 if (error) 229 CFRelease(error); 230 if (linkageArray) 231 CFRelease(linkageArray); 232 if (resultArray) 233 CFRelease(resultArray); 234 if (types) 235 CFRelease(types); 236 return record; 237 238} 239 240/* 241 * 242 */ 243 244static krb5_error_code 245od2hdb_usercert(krb5_context context, hdb_od d, 246 CFArrayRef data, unsigned flags, hdb_entry_ex *entry) 247{ 248 HDB_extension ext; 249 void *ptr; 250 krb5_error_code ret; 251 CFIndex i; 252 253 memset(&ext, 0, sizeof(ext)); 254 255 ext.data.element = choice_HDB_extension_data_pkinit_cert; 256 257 for (i = 0; i < CFArrayGetCount(data); i++) { 258 CFDataRef c = (CFDataRef) CFArrayGetValueAtIndex(data, i); 259 krb5_data cd; 260 261 if (CFGetTypeID((CFTypeRef)c) != CFDataGetTypeID()) 262 continue; 263 264 ret = krb5_data_copy(&cd, CFDataGetBytePtr(c), 265 CFDataGetLength(c)); 266 if (ret) { 267 free_HDB_extension(&ext); 268 return ENOMEM; 269 } 270 271 ext.data.u.pkinit_cert.len += 1; 272 273 ptr = realloc(ext.data.u.pkinit_cert.val, 274 sizeof(ext.data.u.pkinit_cert.val[0]) * ext.data.u.pkinit_cert.len); 275 if (ptr == NULL) { 276 krb5_data_free(&cd); 277 free_HDB_extension(&ext); 278 return ENOMEM; 279 } 280 ext.data.u.pkinit_cert.val = ptr; 281 282 memset(&ext.data.u.pkinit_cert.val[ext.data.u.pkinit_cert.len - 1], 283 0, sizeof(ext.data.u.pkinit_cert.val[0])); 284 285 ext.data.u.pkinit_cert.val[ext.data.u.pkinit_cert.len - 1].cert = cd; 286 } 287 288 ret = hdb_replace_extension(context, &entry->entry, &ext); 289 free_HDB_extension(&ext); 290 291 return ret; 292} 293 294/* 295 * 296 */ 297 298static krb5_error_code 299nodeCreateWithName(krb5_context context, hdb_od d, ODNodeRef *node) 300{ 301 ODSessionRef session = kODSessionDefault; 302 303 *node = NULL; 304 305#ifdef __APPLE_PRIVATE__ 306 if (d->restoreRoot) { 307 CFMutableDictionaryRef options; 308 309 options = CFDictionaryCreateMutable(NULL, 0, 310 &kCFTypeDictionaryKeyCallBacks, 311 &kCFTypeDictionaryValueCallBacks); 312 if (options == NULL) 313 return ENOMEM; 314 315 CFDictionaryAddValue(options, kODSessionLocalPath, d->restoreRoot); 316 317 session = ODSessionCreate(kCFAllocatorDefault, options, NULL); 318 CFRelease(options); 319 if (session == NULL) { 320 krb5_set_error_message(context, HDB_ERR_DB_INUSE, "failed to create session"); 321 return HDB_ERR_DB_INUSE; 322 } 323 } 324#endif 325 326 *node = ODNodeCreateWithName(kCFAllocatorDefault, 327 session, 328 d->rootName, 329 NULL); 330 if (session) 331 CFRelease(session); 332 333 if (*node == NULL) { 334 char *restoreRoot = NULL, *path = NULL; 335 336 if (d->restoreRoot) 337 restoreRoot = rk_cfstring2cstring(d->restoreRoot); 338 path = rk_cfstring2cstring(d->rootName); 339 krb5_set_error_message(context, HDB_ERR_DB_INUSE, "failed to create root node: %s %s", 340 path ? path : "<nopath>", 341 restoreRoot ? restoreRoot : ""); 342 free(restoreRoot); 343 free(path); 344 return HDB_ERR_DB_INUSE; 345 } 346 347 return 0; 348} 349 350 351 352/* 353 * 354 */ 355 356static krb5_error_code 357od2hdb_null(krb5_context context, hdb_od d, 358 CFArrayRef data, unsigned flags, hdb_entry_ex *entry) 359{ 360 return 0; 361} 362 363/* 364 * 365 */ 366 367static krb5_error_code 368map_lkdc_principal(krb5_context context, krb5_principal principal, const char *from, const char *to) 369{ 370 size_t num = krb5_principal_get_num_comp(context, principal); 371 krb5_error_code ret; 372 373 if (strcasecmp(principal->realm, from) == 0) { 374 ret = krb5_principal_set_realm(context, principal, to); 375 if (ret) 376 return ret; 377 } 378 379 if (num == 2 && strcasecmp(principal->name.name_string.val[1], from) == 0) { 380 free(principal->name.name_string.val[1]); 381 principal->name.name_string.val[1] = strdup(to); 382 if (principal->name.name_string.val[1] == NULL) 383 return ENOMEM; 384 } 385 return 0; 386} 387 388/* 389 * 390 */ 391 392static krb5_error_code 393od2hdb_principal(krb5_context context, hdb_od d, 394 CFArrayRef data, unsigned flags, hdb_entry_ex *entry) 395{ 396 hdb_entry_ex_ctx *ctx = entry->ctx; 397 krb5_error_code ret; 398 char *str; 399 400 /* the magic store principal in hdb entry was found, lets skip this step */ 401 if (ctx && ctx->principal) 402 return 0; 403 404 if ((flags & HDB_F_CANON) == 0) 405 return 0; 406 407 if (entry->entry.principal) { 408 krb5_free_principal(context, entry->entry.principal); 409 entry->entry.principal = NULL; 410 } 411 412 str = rk_cfstring2cstring((CFStringRef)CFArrayGetValueAtIndex(data, 0)); 413 if (str == NULL) 414 return ENOMEM; 415 416 ret = krb5_parse_name(context, str, &entry->entry.principal); 417 free(str); 418 if (ret) 419 return ret; 420 421 if (d->LKDCRealm) { 422 ret = map_lkdc_principal(context, entry->entry.principal, wellknown_lkdc, d->LKDCRealm); 423 if (ret) 424 return ret; 425 } 426 427 return 0; 428} 429 430static krb5_error_code 431od2hdb_def_principal(krb5_context context, hdb_od d, 432 int flags, hdb_entry_ex *entry) 433{ 434 if (entry->entry.principal) 435 return 0; 436 return HDB_ERR_NOENTRY; 437} 438 439static krb5_error_code 440hdb2od_principal_lkdc(krb5_context context, hdb_od d, ODAttributeType type, 441 unsigned flags, hdb_entry_ex *entry, ODRecordRef record) 442{ 443 size_t num = krb5_principal_get_num_comp(context, entry->entry.principal); 444 CFMutableArrayRef array = NULL; 445 krb5_principal principal = NULL; 446 krb5_error_code ret = 0; 447 CFStringRef element; 448 char *user; 449 450 if (d->LKDCRealm == NULL) 451 return 0; 452 453 /* 454 * "User" LKDC names are implicitly stored 455 */ 456 if (num == 1) 457 return 0; 458 459 ret = krb5_copy_principal(context, entry->entry.principal, &principal); 460 if (ret) 461 return ret; 462 463 ret = map_lkdc_principal(context, principal, d->LKDCRealm, wellknown_lkdc); 464 if (ret) 465 goto out; 466 467 /* 2 entry nodes are servers :/ */ 468 if (num == 2) { 469 krb5_principal eprinc; 470 type = CFSTR("dsAttrTypeNative:KerberosServerName"); 471 472 ret = map_service(context, principal, &eprinc); 473 if (ret) 474 goto out; 475 krb5_free_principal(context, principal); 476 principal = eprinc; 477 } 478 479 array = CFArrayCreateMutable(kCFAllocatorDefault, 1, 480 &kCFTypeArrayCallBacks); 481 if (array == NULL) { 482 ret = ENOMEM; 483 goto out; 484 } 485 486 ret = krb5_unparse_name(context, principal, &user); 487 if (ret) 488 goto out; 489 490 element = CFStringCreateWithCString(kCFAllocatorDefault, user, kCFStringEncodingUTF8); 491 if (element == NULL) { 492 ret = ENOMEM; 493 goto out; 494 } 495 496 CFArrayAppendValue(array, element); 497 CFRelease(element); 498 499 bool r = ODRecordSetValue(record, type, array, NULL); 500 if (!r) 501 ret = HDB_ERR_UK_SERROR; 502 503 out: 504 if (principal) 505 krb5_free_principal(context, principal); 506 if (array) 507 CFRelease(array); 508 509 return ret; 510} 511 512 513static krb5_error_code 514hdb2od_principal_server(krb5_context context, hdb_od d, ODAttributeType type, 515 unsigned flags, hdb_entry_ex *entry, ODRecordRef record) 516{ 517 krb5_error_code ret; 518 CFStringRef name; 519 char *user; 520 521 if (d->LKDCRealm) 522 return 0; 523 524 ret = krb5_unparse_name(context, entry->entry.principal, &user); 525 if (ret) 526 return ret; 527 528 name = CFStringCreateWithCString(NULL, user, kCFStringEncodingUTF8); 529 free(user); 530 if (name == NULL) 531 return ENOMEM; 532 533 bool r = ODRecordSetValue(record, type, name, NULL); 534 CFRelease(name); 535 if (!r) 536 return HDB_ERR_UK_SERROR; 537 538 return 0; 539} 540 541/* 542 * 543 */ 544 545static krb5_error_code 546od2hdb_alias(krb5_context context, hdb_od d, 547 CFArrayRef data, unsigned flags, hdb_entry_ex *entry) 548{ 549 hdb_entry_ex_ctx *ctx = entry->ctx; 550 krb5_error_code ret; 551 krb5_principal principal; 552 char *str; 553 554 /* skip aliases on magic principal since they don't have aliases */ 555 if (ctx && ctx->principal) 556 return 0; 557 558 str = rk_cfstring2cstring((CFStringRef)CFArrayGetValueAtIndex(data, 0)); 559 if (str == NULL) 560 return ENOMEM; 561 562 ret = krb5_parse_name(context, str, &principal); 563 free(str); 564 if (ret) 565 return ret; 566 567 krb5_free_principal(context, principal); 568 569 return 0; 570} 571/* 572 * 573 */ 574 575static krb5_error_code 576od2hdb_keys(krb5_context context, hdb_od d, 577 CFArrayRef akeys, unsigned flags, hdb_entry_ex *entry) 578{ 579 CFIndex i, count = CFArrayGetCount(akeys); 580 krb5_error_code ret; 581 hdb_keyset_aapl *keys; 582 int32_t specific_match = -1, general_match = -1; 583 uint32_t specific_kvno = 0, general_kvno = 0; 584 CFIndex len; 585 586 if (count == 0) 587 return ENOMEM; 588 589 keys = calloc(count, sizeof(keys[0])); 590 if (keys == NULL) 591 return ENOMEM; 592 593 ret = 0; 594 for (i = 0; i < count; i++) { 595 CFTypeRef type = CFArrayGetValueAtIndex(akeys, i); 596 void *ptr; 597 598 if (CFGetTypeID(type) == CFDataGetTypeID()) { 599 CFDataRef data = (CFDataRef) type; 600 601 len = CFDataGetLength(data); 602 ptr = malloc(len); 603 if (ptr == NULL) { 604 ret = ENOMEM; 605 goto out; 606 } 607 memcpy(ptr, CFDataGetBytePtr(data), len); 608 } else if (CFGetTypeID(type) == CFStringGetTypeID()) { 609 CFStringRef cfstr = (CFStringRef)type; 610 char *str; 611 612 str = rk_cfstring2cstring(cfstr); 613 if (str == NULL) { 614 ret = ENOMEM; 615 goto out; 616 } 617 ptr = malloc(strlen(str)); 618 if (ptr == NULL) { 619 free(str); 620 ret = ENOMEM; 621 goto out; 622 } 623 624 ret = base64_decode(str, ptr); 625 free(str); 626 if (ret < 0) { 627 free(ptr); 628 ret = EINVAL; 629 goto out; 630 } 631 632 len = ret; 633 634 } else { 635 ret = EINVAL; /* XXX */ 636 goto out; 637 } 638 639 ret = decode_hdb_keyset_aapl(ptr, len, &keys[i], NULL); 640 free(ptr); 641 if (ret) 642 goto out; 643 644 if (keys[i].principal) { 645 if (krb5_principal_compare(context, keys[i].principal, entry->entry.principal)) { 646 647 if (keys[i].kvno > specific_kvno) { 648 hdb_entry_ex_ctx *ctx = get_ex_ctx(entry); 649 if (ctx == NULL) { 650 ret = ENOMEM; 651 goto out; 652 } 653 ctx->principal = entry->entry.principal; 654 655 specific_match = (int32_t)i; 656 specific_kvno = keys[i].kvno; 657 } 658 } 659 } else { 660 if (keys[i].kvno > general_kvno) { 661 general_match = (uint32_t)i; 662 general_kvno = keys[i].kvno; 663 } 664 } 665 } 666 667 if (specific_match != -1) { 668 i = specific_match; 669 entry->entry.kvno = specific_kvno; 670 } else if (general_match != -1) { 671 i = general_match; 672 entry->entry.kvno = general_kvno; 673 } else { 674 ret = HDB_ERR_NOENTRY; 675 goto out; 676 } 677 678 free_Keys(&entry->entry.keys); 679 680 entry->entry.keys.len = keys[i].keys.len; 681 entry->entry.keys.val = keys[i].keys.val; 682 keys[i].keys.val = NULL; 683 keys[i].keys.len = 0; 684 685 out: 686 for (i = 0; i < count; i++) 687 free_hdb_keyset_aapl(&keys[i]); 688 free(keys); 689 690 return ret; 691} 692 693static krb5_error_code 694hdb2od_keys(krb5_context context, hdb_od d, ODAttributeType type, 695 unsigned flags, hdb_entry_ex *entry, ODRecordRef record) 696{ 697 CFMutableArrayRef array; 698 heim_octet_string data; 699 krb5_error_code ret; 700 CFDataRef element; 701 hdb_keyset_aapl key; 702 size_t size; 703 704 /* if this is a change password operation, the keys have already been updated with ->hdb_password */ 705 if (flags & HDB_F_CHANGE_PASSWORD) 706 return 0; 707 708 /* this overwrites other keys that are stored in the special "alias" keyset for server */ 709 710 key.kvno = entry->entry.kvno; 711 key.keys.len = entry->entry.keys.len; 712 key.keys.val = entry->entry.keys.val; 713 key.principal = NULL; 714 715 array = CFArrayCreateMutable(kCFAllocatorDefault, 1, 716 &kCFTypeArrayCallBacks); 717 if (array == NULL) { 718 ret = ENOMEM; 719 goto out; 720 } 721 722 ASN1_MALLOC_ENCODE(hdb_keyset_aapl, data.data, data.length, &key, &size, ret); 723 if (ret) 724 goto out; 725 if (data.length != size) 726 krb5_abortx(context, "internal asn.1 encoder error"); 727 728 element = CFDataCreate(kCFAllocatorDefault, data.data, data.length); 729 if (element == NULL) { 730 ret = ENOMEM; 731 goto out; 732 } 733 734 CFArrayAppendValue(array, element); 735 CFRelease(element); 736 737 bool r = ODRecordSetValue(record, type, array, NULL); 738 if (!r) 739 ret = HDB_ERR_UK_SERROR; 740 741 out: 742 if (array) 743 CFRelease(array); 744 745 return ret; 746} 747 748/* 749 * 750 */ 751 752static krb5_error_code 753od2hdb_flags(krb5_context context, hdb_od d, 754 CFArrayRef data, unsigned qflags, hdb_entry_ex *entry) 755{ 756 int flags; 757 char *str; 758 759 str = rk_cfstring2cstring((CFStringRef)CFArrayGetValueAtIndex(data, 0)); 760 if (str == NULL) 761 return ENOMEM; 762 763 flags = atoi(str); 764 free(str); 765 entry->entry.flags = int2HDBFlags(flags); 766 return 0; 767} 768 769static krb5_error_code 770od2hdb_def_flags(krb5_context context, hdb_od d, 771 int flags, hdb_entry_ex *entry) 772{ 773 entry->entry.flags.client = 1; 774 entry->entry.flags.forwardable = 1; 775 entry->entry.flags.renewable = 1; 776 entry->entry.flags.proxiable = 1; 777 778 return 0; 779} 780 781static krb5_error_code 782hdb2od_flags(krb5_context context, hdb_od d, ODAttributeType type, 783 unsigned flags, hdb_entry_ex *entry, ODRecordRef record) 784{ 785 int eflags = HDBFlags2int(entry->entry.flags); 786 CFStringRef value; 787 788 value = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, 789 CFSTR("%d"), eflags); 790 if (value == NULL) 791 return ENOMEM; 792 793 bool r = ODRecordSetValue(record, type, value, NULL); 794 CFRelease(value); 795 if (!r) 796 return HDB_ERR_UK_SERROR; 797 798 return 0; 799} 800 801static krb5_error_code 802od2hdb_altsecids(krb5_context context, hdb_od d, 803 CFArrayRef akeys, unsigned flags, hdb_entry_ex *entry) 804{ 805 CFIndex i, count = CFArrayGetCount(akeys); 806 krb5_error_code ret; 807 808 if (count == 0) 809 return ENOMEM; 810 811 for (i = 0; i < count; i++) { 812 CFStringRef name = CFArrayGetValueAtIndex(akeys, i); 813 char *str, *str0, *subj; 814 815 if (CFGetTypeID(name) != CFStringGetTypeID()) 816 continue; 817 818 if (!CFStringHasPrefix(name, CFSTR("X509:<T>"))) 819 continue; 820 821 /* split out X509:<T>(.*)<S>(.*) from the string and store */ 822 823 str0 = str = rk_cfstring2cstring(name); 824 str += 8; /* skip X509:<T> */ 825 826 subj = strstr(str, "<S>"); 827 if (subj == NULL) { 828 free(str0); 829 continue; 830 } 831 *subj = '\0'; 832 subj += 3; /* skip <S> */ 833 834 /* make sure that there is no <T> or <S> in the string */ 835 if (strstr(subj, "<T>") || strstr(subj, "<S>") || 836 strstr(str, "<T>") || strstr(str, "<S>")) 837 { 838 free(str0); 839 continue; 840 } 841 842 ret = hdb_entry_set_pkinit_acl(&entry->entry, subj, NULL, str); 843 free(str0); 844 if (ret) 845 return ret; 846 } 847 return 0; 848} 849 850/* 851 * 852 */ 853 854static krb5_error_code 855od2hdb_acl_rights(krb5_context context, hdb_od d, 856 CFArrayRef data, unsigned flags, hdb_entry_ex *entry) 857{ 858 char *str; 859 860 str = rk_cfstring2cstring((CFStringRef)CFArrayGetValueAtIndex(data, 0)); 861 if (str == NULL) 862 return ENOMEM; 863 864 entry->entry.acl_rights = malloc(sizeof(*entry->entry.acl_rights)); 865 if (entry->entry.acl_rights == NULL) { 866 free(str); 867 return ENOMEM; 868 } 869 870 *entry->entry.acl_rights = atoi(str); 871 free(str); 872 873 return 0; 874} 875 876/* 877 * 878 */ 879 880typedef krb5_error_code 881(*od2hdb_func)(krb5_context, hdb_od, CFArrayRef, unsigned flags, hdb_entry_ex *); 882 883typedef krb5_error_code 884(*od2hdb_default)(krb5_context, hdb_od, int, hdb_entry_ex *); 885 886typedef krb5_error_code 887(*hdb2od_func)(krb5_context, hdb_od, ODAttributeType, 888 unsigned flags, hdb_entry_ex *, ODRecordRef); 889 890 891static const struct key { 892 const char *desc; 893 CFStringRef name; 894 int flags; 895#define MULTI_VALUE 1 896#define OPTIONAL_VALUE 2 897#define SERVER_VALUE 4 898#define DELETE_KEY 8 899 od2hdb_func od2hdb; 900 od2hdb_default od2hdb_def; 901 hdb2od_func hdb2od; 902} keys[] = { 903 { 904 "keyset", 905 CFSTR("dsAttrTypeNative:draft-krbKeySet"), OPTIONAL_VALUE | MULTI_VALUE | SERVER_VALUE | DELETE_KEY, 906 od2hdb_keys, 907 NULL, 908 hdb2od_keys 909 }, 910 { 911 "KerberosKeys", 912 CFSTR("dsAttrTypeNative:KerberosKeys"), OPTIONAL_VALUE | MULTI_VALUE | DELETE_KEY, 913 od2hdb_keys, 914 NULL, 915 hdb2od_keys 916 }, 917 { 918 "username", 919 CFSTR("dsAttrTypeNative:KerberosUserName"), OPTIONAL_VALUE | DELETE_KEY, 920 od2hdb_principal, 921 od2hdb_def_principal, 922 hdb2od_principal_lkdc 923 }, 924 { 925 "principalName", 926 CFSTR("dsAttrTypeNative:draft-krbPrincipalName"), OPTIONAL_VALUE | SERVER_VALUE | DELETE_KEY, 927 od2hdb_principal, 928 NULL, 929 hdb2od_principal_server 930 }, 931 { 932 "principalAlias", 933 CFSTR("dsAttrTypeNative:draft-krbPrincipalAliases"), MULTI_VALUE | OPTIONAL_VALUE | DELETE_KEY, 934 od2hdb_alias, 935 NULL, 936 NULL 937 }, 938 { 939 "KerberosFlags", 940 CFSTR("dsAttrTypeNative:KerberosFlags"), DELETE_KEY, 941 od2hdb_flags, 942 od2hdb_def_flags, 943 hdb2od_flags 944 }, 945 { 946 "KerberosPolicy", 947 CFSTR("dsAttrTypeNative:draft-krbTicketPolicy"), OPTIONAL_VALUE | SERVER_VALUE | DELETE_KEY, 948 od2hdb_flags, 949 NULL, 950 hdb2od_flags 951 }, 952 { 953 "MaxLife", 954 CFSTR("dsAttrTypeNative:KerberosMaxLife"), OPTIONAL_VALUE | DELETE_KEY, 955 od2hdb_null, 956 NULL, 957 NULL 958 }, 959 { 960 "MaxRenewLife", 961 CFSTR("dsAttrTypeNative:KerberosMaxRenew"), OPTIONAL_VALUE | DELETE_KEY, 962 od2hdb_null, 963 NULL, 964 NULL 965 }, 966 { 967 "UserCertificate", 968 CFSTR("dsAttrTypeStandard:UserCertificate"), MULTI_VALUE | OPTIONAL_VALUE, 969 od2hdb_usercert, 970 NULL, 971 NULL 972 }, 973 { 974 "AltSecurityIdentities", 975 CFSTR("dsAttrTypeStandard:AltSecurityIdentities"), MULTI_VALUE | OPTIONAL_VALUE, 976 od2hdb_altsecids, 977 NULL, 978 NULL 979 }, 980 { 981 "principalACL", 982 CFSTR("dsAttrTypeNative:draft-krbPrincipalACL"), OPTIONAL_VALUE, 983 od2hdb_acl_rights, 984 NULL, 985 NULL 986 } 987 988}; 989static const int num_keys = sizeof(keys)/sizeof(keys[0]); 990 991/* 992 * Policy mappings 993 */ 994 995static int 996booleanPolicy(CFDictionaryRef policy, CFStringRef key) 997{ 998 CFTypeRef val = CFDictionaryGetValue(policy, key); 999 1000 if (val == NULL) 1001 return 0; 1002 1003 if (CFGetTypeID(val) == CFStringGetTypeID()) { 1004 if (CFStringCompare(val, CFSTR("0"), kCFCompareNumerically) != kCFCompareEqualTo) 1005 return 1; 1006 } else if (CFGetTypeID(val) == CFNumberGetTypeID()) { 1007 int num; 1008 if (CFNumberGetValue(val, kCFNumberIntType, &num)) 1009 return num; 1010 } 1011 return 0; 1012} 1013 1014static time_t 1015timePolicy(CFDictionaryRef policy, CFStringRef key) 1016{ 1017 CFTypeRef val = CFDictionaryGetValue(policy, key); 1018 1019 if (val == NULL) 1020 return 0; 1021 1022 if (CFGetTypeID(val) == CFStringGetTypeID()) { 1023 return (time_t)CFStringGetIntValue(val); 1024 } else if (CFGetTypeID(val) == CFNumberGetTypeID()) { 1025 long num; 1026 if (CFNumberGetValue(val, kCFNumberLongType, &num)) 1027 return num; 1028 } 1029 return 0; 1030} 1031 1032static krb5_error_code 1033apply_policy(hdb_entry *entry, CFDictionaryRef policy) 1034{ 1035 /* 1036 * Historically, we have not applied any policies to admin users. 1037 */ 1038 if (booleanPolicy(policy, CFSTR("isAdminUser"))) 1039 return 0; 1040 1041 entry->flags.invalid = booleanPolicy(policy, CFSTR("isDisabled")); 1042 1043 if (booleanPolicy(policy, CFSTR("newPasswordRequired"))) { 1044 if (entry->pw_end == NULL) { 1045 entry->pw_end = malloc(sizeof(*entry->pw_end)); 1046 if (entry->pw_end == NULL) 1047 return ENOMEM; 1048 } 1049 *entry->pw_end = time(NULL) - 60; /* expired on 60s ago */ 1050 } else if (booleanPolicy(policy, CFSTR("usingExpirationDate"))) { 1051 time_t t = timePolicy(policy, CFSTR("expirationDateGMT")); 1052 if (t > 0) { 1053 if (entry->pw_end == NULL) { 1054 entry->pw_end = malloc(sizeof(*entry->pw_end)); 1055 if (entry->pw_end == NULL) 1056 return ENOMEM; 1057 } 1058 *entry->pw_end = t; 1059 } 1060 } 1061 if (booleanPolicy(policy, CFSTR("usingHardExpirationDate"))) { 1062 time_t t = timePolicy(policy, CFSTR("hardExpireDateGMT")); 1063 if (t > 0) { 1064 if (entry->valid_end == NULL) { 1065 entry->valid_end = malloc(sizeof(*entry->valid_end)); 1066 if (entry->valid_end == NULL) 1067 return ENOMEM; 1068 } 1069 *entry->valid_end = t; 1070 } 1071 } 1072 return 0; 1073} 1074 1075/* 1076 * HDB entry points 1077 */ 1078 1079static krb5_error_code 1080hod_close(krb5_context context, HDB *db) 1081{ 1082 return 0; 1083} 1084 1085static krb5_error_code 1086hod_destroy(krb5_context context, HDB *db) 1087{ 1088 hdb_od d = (hdb_od)db->hdb_db; 1089 krb5_error_code ret; 1090 1091 if (d->rootNode) 1092 CFRelease(d->rootNode); 1093 if (d->inNodeAttributes) 1094 CFRelease(d->inNodeAttributes); 1095 if (d->inKDCAttributes) 1096 CFRelease(d->inKDCAttributes); 1097 if (d->LKDCRealm) 1098 free(d->LKDCRealm); 1099 1100 ret = hdb_clear_master_key (context, db); 1101 free(db->hdb_name); 1102 free(db); 1103 return ret; 1104} 1105 1106static krb5_error_code 1107hod_lock(krb5_context context, HDB *db, int operation) 1108{ 1109 return 0; 1110} 1111 1112static krb5_error_code 1113hod_unlock(krb5_context context, HDB *db) 1114{ 1115 return 0; 1116} 1117 1118static krb5_error_code 1119od_record2entry(krb5_context context, HDB * db, ODRecordRef cfRecord, 1120 unsigned flags, hdb_entry_ex * entry) 1121{ 1122 hdb_od d = (hdb_od)db->hdb_db; 1123 krb5_error_code ret = HDB_ERR_NOENTRY; 1124 int i; 1125 1126 for (i = 0; i < num_keys; i++) { 1127 CFArrayRef data; 1128 1129 data = ODRecordCopyValues(cfRecord, keys[i].name, NULL); 1130 if (data == NULL) { 1131 if (keys[i].flags & OPTIONAL_VALUE) 1132 continue; 1133 if (keys[i].od2hdb_def) { 1134 ret = (*keys[i].od2hdb_def)(context, d, flags, entry); 1135 if (ret) 1136 goto out; 1137 continue; 1138 } 1139 krb5_warnx(context, "Failed to copy non-optional value %s", keys[i].desc); 1140 ret = HDB_ERR_NOENTRY; 1141 goto out; 1142 } 1143 1144 if ((keys[i].flags & MULTI_VALUE) && CFArrayGetCount(data) < 1) { 1145 krb5_warnx(context, "Expected multivalue got zero %s for", keys[i].desc); 1146 CFRelease(data); 1147 ret = HDB_ERR_NOENTRY; 1148 goto out; 1149 } else if ((keys[i].flags & MULTI_VALUE) == 0 && CFArrayGetCount(data) != 1) { 1150 krb5_warnx(context, "Expected single-value got non zero %d for %s", 1151 (int)CFArrayGetCount(data), keys[i].desc); 1152 CFRelease(data); 1153 ret = HDB_ERR_NOENTRY; 1154 goto out; 1155 } 1156 1157 ret = (*keys[i].od2hdb)(context, d, data, flags, entry); 1158 CFRelease(data); 1159 if (ret) { 1160 krb5_warn(context, ret, "od2hdb failed for %s", keys[i].desc); 1161 goto out; 1162 } 1163 } 1164 1165 /* 1166 * If entry didn't contain a principal, something is wrong with 1167 * the entry, lets skip it. 1168 */ 1169 if (entry->entry.principal == NULL) { 1170 krb5_warnx(context, "principal missing"); 1171 ret = HDB_ERR_NOENTRY; 1172 goto out; 1173 } 1174 1175 /* Not recorded in the OD backend, make something up */ 1176 ret = krb5_parse_name(context, "hdb/od@WELL-KNOWN:OD-BACKEND", 1177 &entry->entry.created_by.principal); 1178 if (ret) 1179 goto out; 1180 1181 entry->entry.created_by.time = time(NULL); 1182 1183 if ((flags & HDB_F_GET_KRBTGT) == 0 && !krb5_principal_is_krbtgt(context, entry->entry.principal)) { 1184 ODRecordRef userrecord; 1185 1186 userrecord = HODODNodeCopyLinkageRecordFromAuthenticationData(context, d->rootNode, cfRecord); 1187 if (userrecord) { 1188 CFDictionaryRef policy; 1189 1190 policy = ODRecordCopyEffectivePolicies(userrecord, NULL); 1191 CFRelease(userrecord); 1192 1193 if (policy) { 1194 ret = apply_policy(&entry->entry, policy); 1195 CFRelease(policy); 1196 if (ret) 1197 goto out; 1198 } 1199 } 1200 } 1201 1202 if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { 1203 ret = hdb_unseal_keys(context, db, &entry->entry); 1204 if(ret) { 1205 krb5_warn(context, ret, "unseal keys failed"); 1206 goto out; 1207 } 1208 } 1209 1210out: 1211 return ret; 1212} 1213 1214static krb5_error_code 1215hod_nextkey(krb5_context context, HDB * db, unsigned flags, 1216 hdb_entry_ex * entry) 1217{ 1218 ODRecordRef cfRecord = NULL; 1219 hdb_od d = (hdb_od)db->hdb_db; 1220 krb5_error_code ret; 1221 1222 memset(entry, 0, sizeof(*entry)); 1223 1224 heim_assert(d->iter.odRecordArray != NULL, "hdb: hod_next w/o hod_first"); 1225 1226 while (d->iter.idx < CFArrayGetCount(d->iter.odRecordArray)) { 1227 cfRecord = (ODRecordRef) CFArrayGetValueAtIndex(d->iter.odRecordArray, d->iter.idx); 1228 d->iter.idx++; 1229 if (cfRecord == NULL) { 1230 ret = HDB_ERR_NOENTRY; 1231 goto out; 1232 } 1233 1234 CFArrayRef kerberosKeysArray = ODRecordCopyValues(cfRecord, CFSTR("dsAttrTypeNative:KerberosKeys"), NULL); 1235 if (kerberosKeysArray == NULL) { 1236 cfRecord = NULL; 1237 continue; 1238 } 1239 if (CFArrayGetCount(kerberosKeysArray) == 0) { 1240 CFRelease(kerberosKeysArray); 1241 cfRecord = NULL; 1242 continue; 1243 } 1244 CFRelease(kerberosKeysArray); 1245 1246 break; 1247 } 1248 1249 if (cfRecord) { 1250 CFStringRef name = ODRecordGetRecordName(cfRecord); 1251 char *str; 1252 1253 str = rk_cfstring2cstring(name); 1254 if (str == NULL) { 1255 ret = ENOENT; 1256 goto out; 1257 } 1258 1259 ret = krb5_make_principal(context, &entry->entry.principal, 1260 d->LKDCRealm, str, NULL); 1261 free(str); 1262 if (ret) 1263 goto out; 1264 1265 ret = od_record2entry(context, db, cfRecord, flags, entry); 1266 } else 1267 ret = HDB_ERR_NOENTRY; 1268 1269out: 1270 if (ret) { 1271 free_hdb_entry(&entry->entry); 1272 1273 if (d->iter.odRecordArray) { 1274 CFRelease(d->iter.odRecordArray); 1275 d->iter.odRecordArray = NULL; 1276 d->iter.idx = 0; 1277 } 1278 } 1279 1280 return ret; 1281} 1282 1283static krb5_error_code 1284hod_firstkey(krb5_context context, HDB *db, 1285 unsigned flags, hdb_entry_ex *entry) 1286{ 1287 int ret = 0; 1288 ODQueryRef query = NULL; 1289 hdb_od d = (hdb_od)db->hdb_db; 1290 int tryAgain = MAX_TRIES; 1291 1292 if (d->LKDCRealm == NULL) { 1293 krb5_set_error_message(context, EINVAL, "iteration over database only supported for DSLocal"); 1294 return EINVAL; 1295 } 1296 1297 heim_assert(d->iter.odRecordArray == NULL, "hdb: more then one iteration at the same time"); 1298 1299 do { 1300 CFErrorRef error = NULL; 1301 1302 query = ODQueryCreateWithNode(NULL, d->rootNode, 1303 kODRecordTypeUsers, NULL, 1304 kODMatchAny, 1305 NULL, 1306 kODAttributeTypeAllAttributes, 1307 0, 1308 &error); 1309 if (query == NULL) { 1310 ret = HDB_ERR_NOENTRY; 1311 goto out; 1312 } 1313 1314 d->iter.odRecordArray = ODQueryCopyResults(query, FALSE, &error); 1315 CFRelease(query); 1316 if (d->iter.odRecordArray == NULL && error == NULL) 1317 tryAgain = 0; 1318 if (error) 1319 CFRelease(error); 1320 } while(d->iter.odRecordArray == NULL && tryAgainP(context, &tryAgain, NULL)); 1321 1322 if (d->iter.odRecordArray == NULL) { 1323 ret = HDB_ERR_NOENTRY; 1324 goto out; 1325 } 1326 d->iter.idx = 0; 1327 1328 return hod_nextkey(context, db, flags, entry); 1329out: 1330 return ret; 1331} 1332 1333static krb5_error_code 1334hod_open(krb5_context context, HDB * db, int flags, mode_t mode) 1335{ 1336 hdb_od d = (hdb_od)db->hdb_db; 1337 1338 if (d->rootNode == NULL) 1339 return nodeCreateWithName(context, d, &d->rootNode); 1340 1341 return 0; 1342} 1343 1344static krb5_error_code 1345lkdc_locate_record(krb5_context context, hdb_od d, krb5_principal principal, 1346 unsigned flags, int createp, int *createdp, ODRecordRef *result) 1347{ 1348 ODMatchType matchtype = kODMatchEqualTo; 1349 CFStringRef queryString = NULL; 1350 ODRecordRef cfRecord; 1351 ODQueryRef query = NULL; 1352 CFArrayRef res = NULL; 1353 CFStringRef attr; 1354 krb5_error_code ret = 0; 1355 krb5_principal eprinc = NULL; 1356 char *kuser = NULL; 1357 const char *node = NULL; 1358 CFTypeRef rtype = NULL; 1359 int upflags = 0; 1360 int tryAgain = MAX_TRIES; 1361 1362 *result = NULL; 1363 1364 /* 1365 * Really need something like CrackName for uniform unparsing 1366 */ 1367 1368 if (principal->name.name_type == KRB5_NT_X509_GENERAL_NAME) { 1369#ifdef PKINIT 1370 char *anode = NULL; 1371 hx509_name name = NULL; 1372 GeneralName gn; 1373 ssize_t len; 1374 void *buf; 1375 Name n; 1376 1377 memset(&gn, 0, sizeof(gn)); 1378 memset(&n, 0, sizeof(n)); 1379 1380 if (krb5_principal_get_num_comp(context, principal) != 1) { 1381 krb5_warnx(context, "wrong length of x509 name"); 1382 return HDB_ERR_NOENTRY; 1383 } 1384 1385 len = strlen(principal->name.name_string.val[0]); 1386 buf = malloc(len); 1387 if (buf == NULL) 1388 return HDB_ERR_NOENTRY; 1389 1390 len = base64_decode(principal->name.name_string.val[0], buf); 1391 if (len <= 0) { 1392 free(buf); 1393 return HDB_ERR_NOENTRY; 1394 } 1395 1396 ret = decode_GeneralName(buf, len, &gn, NULL); 1397 free(buf); 1398 if (ret) { 1399 krb5_warnx(context, "x500 GeneralName malformed: %d", ret); 1400 return HDB_ERR_NOENTRY; 1401 } 1402 1403 if (gn.element != choice_GeneralName_directoryName) { 1404 krb5_warnx(context, "x500 name not directory Name"); 1405 free_GeneralName(&gn); 1406 return HDB_ERR_NOENTRY; 1407 } 1408 1409 n.element = choice_Name_rdnSequence; 1410 n.u.rdnSequence.len = gn.u.directoryName.u.rdnSequence.len; 1411 n.u.rdnSequence.val = gn.u.directoryName.u.rdnSequence.val; 1412 1413 ret = hx509_name_from_Name(&n, &name); 1414 free_GeneralName(&gn); 1415 if (ret) 1416 return HDB_ERR_NOENTRY; 1417 1418 attr = kODAttributeTypeRecordName; 1419 rtype = kODRecordTypeUsers; 1420 1421 ret = hx509_name_to_string(name, &anode); 1422 hx509_name_free(&name); 1423 if (ret) 1424 return HDB_ERR_NOENTRY; 1425 1426 queryString = CFStringCreateWithCString(NULL, anode, kCFStringEncodingUTF8); 1427 free(anode); 1428 if (queryString == NULL) { 1429 ret = HDB_ERR_NOENTRY; 1430 goto out; 1431 } 1432 createp = 0; 1433#else 1434 return HDB_ERR_NOENTRY; 1435#endif 1436 } else if (principal->name.name_type == KRB5_NT_NTLM) { 1437 attr = kODAttributeTypeRecordName; 1438 rtype = kODRecordTypeUsers; 1439 matchtype = kODMatchInsensitiveEqualTo; 1440 1441 if (krb5_principal_get_num_comp(context, principal) != 1) { 1442 krb5_warnx(context, "wrong length of ntlm name"); 1443 return HDB_ERR_NOENTRY; 1444 } 1445 1446 if (d->ntlmDomain == NULL) { 1447 krb5_warnx(context, "NTLM domain not configured"); 1448 return HDB_ERR_NOENTRY; 1449 } 1450 1451 krb5_principal_set_realm(context, principal, d->ntlmDomain); 1452 node = krb5_principal_get_comp_string(context, principal, 0); 1453 1454 } else if (krb5_principal_is_pku2u(context, principal)) { 1455 attr = kODAttributeTypeRecordName; 1456 if (flags & HDB_F_GET_CLIENT) { 1457 int num = krb5_principal_get_num_comp(context, principal); 1458 if (num != 1) { 1459 ret = HDB_ERR_NOENTRY; 1460 goto out; 1461 } 1462 rtype = kODRecordTypeUsers; 1463 upflags = KRB5_PRINCIPAL_UNPARSE_NO_REALM; 1464 upflags |= KRB5_PRINCIPAL_UNPARSE_DISPLAY; 1465 } else { 1466 node = "localhost"; 1467 rtype = kODRecordTypeComputers; 1468 } 1469 } else if (is_lkdc(d, principal->realm)) { 1470 /* check if user is a LKDC user, just look for RecordName then */ 1471 int num = krb5_principal_get_num_comp(context, principal); 1472 const char *comp0; 1473 size_t lencomp0; 1474 1475 if (num < 1) { 1476 ret = HDB_ERR_NOENTRY; 1477 goto out; 1478 } 1479 1480 ret = map_lkdc_principal(context, principal, d->LKDCRealm, wellknown_lkdc); 1481 if (ret) 1482 goto out; 1483 1484 comp0 = krb5_principal_get_comp_string(context, principal, 0); 1485 lencomp0 = strlen(comp0); 1486 if (lencomp0 == 0) { 1487 ret = HDB_ERR_NOENTRY; 1488 goto out; 1489 } 1490 1491 if (num == 2 || (num == 1 && comp0[lencomp0 - 1] == '$')) { 1492 const char *host; 1493 1494 attr = kODAttributeTypeRecordName; 1495 1496 ret = map_service(context, principal, &eprinc); 1497 if (ret) { 1498 ret = HDB_ERR_NOENTRY; 1499 goto out; 1500 } 1501 principal = eprinc; 1502 1503 host = krb5_principal_get_comp_string(context, principal, 1); 1504 comp0 = krb5_principal_get_comp_string(context, principal, 0); 1505 1506 if (strcasecmp(KRB5_TGS_NAME, comp0) == 0) { 1507 node = "_krbtgt"; 1508 rtype = kODRecordTypeUsers; 1509 } else if (num == 2 && is_lkdc(d, host)) { 1510 node = "localhost"; 1511 rtype = kODRecordTypeComputers; 1512 } else if (num == 2) { 1513 node = host; 1514 rtype = kODRecordTypeComputers; 1515 } else { 1516 node = comp0; 1517 rtype = kODRecordTypeComputers; 1518 } 1519 1520 } else if (num == 1) { 1521 attr = kODAttributeTypeRecordName; 1522 rtype = kODRecordTypeUsers; 1523 upflags = KRB5_PRINCIPAL_UNPARSE_NO_REALM; 1524 upflags |= KRB5_PRINCIPAL_UNPARSE_DISPLAY; 1525 } else { 1526 ret = HDB_ERR_NOENTRY; 1527 goto out; 1528 } 1529 } else { 1530 /* managed realm, lets check for real entries */ 1531 1532 ret = krb5_unparse_name_flags(context, principal, KRB5_PRINCIPAL_UNPARSE_DISPLAY, &kuser); 1533 if (ret) 1534 goto out; 1535 1536 rtype = d->inComputerOrUsers; 1537 matchtype = kODMatchCompoundExpression; 1538 attr = NULL; 1539 1540 queryString = CFStringCreateWithFormat(NULL, NULL, CFSTR("(|(%@=%s)(%@=%s))"), 1541 CFSTR("dsAttrTypeNative:KerberosUserName"), 1542 kuser, 1543 CFSTR("dsAttrTypeNative:KerberosServerName"), 1544 kuser); 1545 } 1546 1547 if (queryString == NULL) { 1548 if (node) { 1549 queryString = CFStringCreateWithCString(NULL, node, kCFStringEncodingUTF8); 1550 } else { 1551 ret = krb5_unparse_name_flags(context, principal, upflags, &kuser); 1552 if (ret) 1553 goto out; 1554 1555 queryString = CFStringCreateWithCString(NULL, kuser, kCFStringEncodingUTF8); 1556 } 1557 } 1558 if (queryString == NULL) { 1559 ret = ENOMEM; 1560 goto out; 1561 } 1562 1563 do { 1564 CFErrorRef error = NULL; 1565 1566 query = ODQueryCreateWithNode(NULL, d->rootNode, 1567 rtype, attr, 1568 matchtype, 1569 queryString, 1570 d->inNodeAttributes, 1571 1, 1572 NULL); 1573 if (query == NULL) { 1574 CFRelease(queryString); 1575 ret = ENOMEM; 1576 goto out; 1577 } 1578 1579 res = ODQueryCopyResults(query, FALSE, &error); 1580 if (res == NULL && error == NULL) 1581 tryAgain = 0; 1582 if (error) 1583 CFRelease(error); 1584 } while(res == NULL && tryAgainP(context, &tryAgain, NULL)); 1585 1586 CFRelease(queryString); 1587 if (res == NULL) { 1588 ret = HDB_ERR_NOENTRY; 1589 goto out; 1590 } 1591 1592 if (CFArrayGetCount(res) == 0 && node && createp) { 1593 1594 queryString = CFStringCreateWithCString(NULL, node, kCFStringEncodingUTF8); 1595 1596 cfRecord = ODNodeCopyRecord(d->rootNode, rtype, queryString, d->inNodeAttributes, NULL); 1597 if (cfRecord == NULL) { 1598 CFMutableDictionaryRef attributes; 1599 1600 attributes = CFDictionaryCreateMutable(NULL, 0, 1601 &kCFTypeDictionaryKeyCallBacks, 1602 &kCFTypeDictionaryValueCallBacks); 1603 1604 cfRecord = ODNodeCreateRecord(d->rootNode, 1605 rtype, 1606 queryString, 1607 attributes, 1608 NULL); 1609 CFRelease(attributes); 1610 } 1611 CFRelease(queryString); 1612 1613 if (cfRecord == NULL) { 1614 ret = HDB_ERR_UK_RERROR; 1615 goto out; 1616 } 1617 1618 if (createdp) 1619 *createdp = TRUE; 1620 1621 } else if (CFArrayGetCount(res) == 1) { 1622 cfRecord = (ODRecordRef) CFArrayGetValueAtIndex(res, 0); 1623 if (cfRecord == NULL) { 1624 ret = HDB_ERR_NOENTRY; 1625 goto out; 1626 } 1627 CFRetain(cfRecord); 1628 1629 } else { 1630 ret = HDB_ERR_NOENTRY; 1631 goto out; 1632 } 1633 1634 *result = cfRecord; 1635 1636 out: 1637 if (kuser) 1638 free(kuser); 1639 if (eprinc) 1640 krb5_free_principal(context, eprinc); 1641 if (res) 1642 CFRelease(res); 1643 if (query) 1644 CFRelease(query); 1645 return ret; 1646} 1647 1648static krb5_error_code 1649server_locate_record(krb5_context context, hdb_od d, krb5_principal principal, 1650 unsigned flags, int createp, int *createdp, ODRecordRef *result) 1651{ 1652 ODRecordRef cfRecord = NULL; 1653 krb5_error_code ret; 1654 ODMatchType matchtype; 1655 CFStringRef queryString = NULL; 1656 ODQueryRef query = NULL; 1657 CFArrayRef res = NULL; 1658 CFStringRef attr; 1659 char *kuser = NULL; 1660 CFStringRef client_key, server_key; 1661 int tryAgain = MAX_TRIES; 1662 1663 *result = NULL; 1664 1665 ret = krb5_unparse_name_flags(context, principal, KRB5_PRINCIPAL_UNPARSE_DISPLAY, &kuser); 1666 if (ret) 1667 goto out; 1668 1669 client_key = CFSTR("dsAttrTypeNative:draft-krbPrincipalName"); 1670 server_key = CFSTR("dsAttrTypeNative:draft-krbPrincipalAliases"); 1671 1672 /* XXX support referrals here */ 1673 1674 if (flags & HDB_F_GET_KRBTGT) { 1675 matchtype = kODMatchEqualTo; 1676 attr = client_key; 1677 queryString = CFStringCreateWithCString(NULL, kuser, kCFStringEncodingUTF8); 1678 } else { 1679 matchtype = kODMatchCompoundExpression; 1680 attr = NULL; 1681 queryString = CFStringCreateWithFormat(NULL, NULL, CFSTR("(|(%@=%s)(%@=%s))"), 1682 server_key, 1683 kuser, 1684 client_key, 1685 kuser); 1686 } 1687 if (queryString == NULL) { 1688 ret = HDB_ERR_NOENTRY; 1689 goto out; 1690 } 1691 1692 do { 1693 CFErrorRef error = NULL; 1694 1695 query = ODQueryCreateWithNode(NULL, d->rootNode, 1696 kODRecordTypeUserAuthenticationData, 1697 attr, 1698 matchtype, 1699 queryString, 1700 d->inNodeAttributes, 1701 2, 1702 NULL); 1703 if (query == NULL) { 1704 CFRelease(queryString); 1705 ret = ENOMEM; 1706 goto out; 1707 } 1708 1709 res = ODQueryCopyResults(query, FALSE, &error); 1710 if (res == NULL && error == NULL) 1711 tryAgain = 0; 1712 if (error) 1713 CFRelease(error); 1714 } while(res == NULL && tryAgainP(context, &tryAgain, NULL)); 1715 CFRelease(queryString); 1716 1717 if (res == NULL) { 1718 ret = HDB_ERR_NOENTRY; 1719 goto out; 1720 } 1721 1722 1723 CFIndex count = CFArrayGetCount(res); 1724 if (count == 0 && createp) { 1725 CFMutableDictionaryRef attributes; 1726 CFStringRef name; 1727 CFUUIDRef uuid; 1728 1729 uuid = CFUUIDCreate(NULL); 1730 if (uuid == NULL) { 1731 ret = HDB_ERR_NOENTRY; 1732 goto out; 1733 } 1734 1735 name = CFUUIDCreateString(NULL, uuid); 1736 CFRelease(uuid); 1737 if (name == NULL) { 1738 ret = HDB_ERR_NOENTRY; 1739 goto out; 1740 } 1741 1742 attributes = CFDictionaryCreateMutable(NULL, 0, 1743 &kCFTypeDictionaryKeyCallBacks, 1744 &kCFTypeDictionaryValueCallBacks); 1745 if (attributes == NULL) { 1746 CFRelease(name); 1747 ret = HDB_ERR_NOENTRY; 1748 goto out; 1749 } 1750 1751 cfRecord = ODNodeCreateRecord(d->rootNode, 1752 kODRecordTypeUserAuthenticationData, 1753 name, 1754 attributes, 1755 NULL); 1756 CFRelease(name); 1757 CFRelease(attributes); 1758 if (cfRecord == NULL) { 1759 ret = HDB_ERR_NOENTRY; 1760 goto out; 1761 } 1762 1763 if (createdp) 1764 *createdp = 1; 1765 1766 } else if (count != 1) { 1767 ret = HDB_ERR_NOENTRY; 1768 goto out; 1769 } else { 1770 cfRecord = (ODRecordRef) CFArrayGetValueAtIndex(res, 0); 1771 if (cfRecord == NULL) { 1772 ret = HDB_ERR_NOENTRY; 1773 goto out; 1774 } 1775 CFRetain(cfRecord); 1776 } 1777 *result = cfRecord; 1778 1779out: 1780 if (res) 1781 CFRelease(res); 1782 if (query) 1783 CFRelease(query); 1784 if (kuser) 1785 free(kuser); 1786 1787 return ret; 1788} 1789 1790 1791static krb5_error_code 1792hod_lkdc_fetch(krb5_context context, HDB * db, krb5_const_principal principal, 1793 unsigned flags, krb5_kvno kvno, hdb_entry_ex * entry) 1794{ 1795 krb5_principal eprincipal = NULL; 1796 hdb_od d = (hdb_od)db->hdb_db; 1797 ODRecordRef cfRecord = NULL; 1798 krb5_error_code ret; 1799 1800 ret = krb5_copy_principal(context, principal, &eprincipal); 1801 if (ret) 1802 goto out; 1803 1804 ret = d->locate_record(context, d, eprincipal, flags, FALSE, NULL, &cfRecord); 1805 if (ret) 1806 goto out; 1807 1808 if (is_lkdc(d, eprincipal->realm)) 1809 map_lkdc_principal(context, eprincipal, wellknown_lkdc, d->LKDCRealm); 1810 1811 /* set the principal to the username for users in pku2u and lkdc */ 1812 if (krb5_principal_is_pku2u(context, eprincipal) || 1813 (krb5_principal_is_lkdc(context, eprincipal) && krb5_principal_get_num_comp(context, eprincipal) == 1)) 1814 { 1815 CFStringRef name = ODRecordGetRecordName(cfRecord); 1816 char *str; 1817 1818 str = rk_cfstring2cstring(name); 1819 if (str == NULL) { 1820 ret = ENOENT; 1821 goto out; 1822 } 1823 1824 ret = krb5_make_principal(context, &entry->entry.principal, 1825 eprincipal->realm, str, NULL); 1826 free(str); 1827 if (ret) 1828 goto out; 1829 } else { 1830 ret = krb5_copy_principal(context, eprincipal, &entry->entry.principal); 1831 if (ret) 1832 goto out; 1833 } 1834 1835 ret = od_record2entry(context, db, cfRecord, flags, entry); 1836 if (ret) 1837 goto out; 1838 1839 out: 1840 if (eprincipal) 1841 krb5_free_principal(context, eprincipal); 1842 if (cfRecord) 1843 CFRelease(cfRecord); 1844 if (ret) { 1845 free_hdb_entry(&entry->entry); 1846 memset(&entry->entry, 0, sizeof(entry->entry)); 1847 } 1848 1849 return ret; 1850} 1851 1852static krb5_error_code 1853hod_server_fetch(krb5_context context, HDB * db, krb5_const_principal principal, 1854 unsigned flags, krb5_kvno kvno, hdb_entry_ex * entry) 1855{ 1856 krb5_principal eprincipal = NULL; 1857 hdb_od d = (hdb_od)db->hdb_db; 1858 ODRecordRef cfRecord = NULL; 1859 krb5_error_code ret; 1860 1861 1862 ret = krb5_copy_principal(context, principal, &eprincipal); 1863 if (ret) 1864 goto out; 1865 1866 ret = d->locate_record(context, d, eprincipal, flags, FALSE, NULL, &cfRecord); 1867 if (ret) 1868 goto out; 1869 1870 ret = krb5_copy_principal(context, eprincipal, &entry->entry.principal); 1871 if (ret) 1872 goto out; 1873 1874 ret = od_record2entry(context, db, cfRecord, flags, entry); 1875 if (ret) 1876 goto out; 1877 1878out: 1879 if (cfRecord) 1880 CFRelease(cfRecord); 1881 if (eprincipal) 1882 krb5_free_principal(context, eprincipal); 1883 1884 if (ret) { 1885 free_hdb_entry(&entry->entry); 1886 memset(&entry->entry, 0, sizeof(entry->entry)); 1887 } 1888 1889 return ret; 1890} 1891 1892 1893static krb5_error_code 1894hod_store(krb5_context context, HDB * db, unsigned flags, 1895 hdb_entry_ex * entry) 1896{ 1897 krb5_principal eprincipal = NULL; 1898 hdb_od d = (hdb_od)db->hdb_db; 1899 ODRecordRef record = NULL; 1900 krb5_error_code ret; 1901 int i, created = 0; 1902 1903 ret = krb5_copy_principal(context, entry->entry.principal, &eprincipal); 1904 if (ret) 1905 goto out; 1906 1907 ret = d->locate_record(context, d, eprincipal, flags, (flags & HDB_F_REPLACE) == 0, &created, &record); 1908 if (ret) 1909 goto out; 1910 1911 for (i = 0; i < num_keys; i++) { 1912 if (keys[i].hdb2od == NULL) 1913 continue; 1914 1915 /* logical XOR between server values and LKDC connection */ 1916 if ((!!(keys[i].flags & SERVER_VALUE)) ^ (!d->LKDCRealm)) 1917 continue; 1918 1919 ret = (*keys[i].hdb2od)(context, d, keys[i].name, flags, entry, record); 1920 if (ret) 1921 goto out; 1922 } 1923 1924 if ((flags & HDB_F_CHANGE_PASSWORD) == 0) { 1925 ret = hdb_seal_keys(context, db, &entry->entry); 1926 if(ret) 1927 goto out; 1928 } 1929 1930 ODRecordSynchronize(record, NULL); 1931 1932 out: 1933 if (record) { 1934 if (ret && created) 1935 ODRecordDelete(record, NULL); 1936 CFRelease(record); 1937 } 1938 if (eprincipal) 1939 krb5_free_principal(context, eprincipal); 1940 1941 return ret; 1942} 1943 1944static krb5_error_code 1945hod_remove(krb5_context context, HDB *db, krb5_const_principal principal) 1946{ 1947 krb5_principal eprincipal = NULL; 1948 hdb_od d = (hdb_od)db->hdb_db; 1949 ODRecordRef record = NULL; 1950 CFMutableArrayRef array = NULL; 1951 krb5_error_code ret; 1952 int i; 1953 1954 ret = krb5_copy_principal(context, principal, &eprincipal); 1955 if (ret) 1956 goto out; 1957 1958 ret = d->locate_record(context, d, eprincipal, 0, FALSE, NULL, &record); 1959 if (ret) 1960 goto out; 1961 1962 array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 1963 if (array == NULL) { 1964 ret = ENOMEM; 1965 goto out; 1966 } 1967 1968 for (i = 0; i < num_keys; i++) { 1969 1970 if ((keys[i].flags & DELETE_KEY) == 0) 1971 continue; 1972 1973 if (!ODRecordSetValue(record, keys[i].name, array, NULL)) { 1974 ret = HDB_ERR_UK_SERROR; 1975 break; 1976 } 1977 } 1978 1979 ODRecordSynchronize(record, NULL); 1980 1981 out: 1982 if (eprincipal) 1983 krb5_free_principal(context, eprincipal); 1984 if (array) 1985 CFRelease(array); 1986 if (record) 1987 CFRelease(record); 1988 return ret; 1989} 1990 1991 1992static krb5_error_code 1993hod_get_realms(krb5_context context, HDB *db, krb5_realm **realms) 1994{ 1995 hdb_od d = (hdb_od)db->hdb_db; 1996 1997 *realms = NULL; 1998 1999 if (d->LKDCRealm) { 2000 *realms = calloc(2, sizeof(realms[0])); 2001 if (*realms == NULL) 2002 return ENOMEM; 2003 (*realms)[0] = strdup(d->LKDCRealm); 2004 if ((*realms)[0] == NULL) { 2005 free(*realms); 2006 *realms = NULL; 2007 return ENOMEM; 2008 } 2009 (*realms)[1] = NULL; 2010 } 2011 2012 return 0; 2013} 2014 2015static void 2016update_ntlm(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info) 2017{ 2018 hdb_od d = info; 2019 CFDictionaryRef settings; 2020 CFStringRef n; 2021 2022 if (store == NULL || info == NULL) 2023 return; 2024 2025 settings = (CFDictionaryRef)SCDynamicStoreCopyValue(store, CFSTR("com.apple.smb")); 2026 if (settings == NULL) 2027 return; 2028 2029 n = CFDictionaryGetValue(settings, CFSTR("NetBIOSName")); 2030 if (n == NULL || CFGetTypeID(n) != CFStringGetTypeID()) 2031 goto fin; 2032 2033 if (d->ntlmDomain) 2034 free(d->ntlmDomain); 2035 d->ntlmDomain = rk_cfstring2cstring(n); 2036 strupr(d->ntlmDomain); 2037 2038fin: 2039 CFRelease(settings); 2040 return; 2041} 2042 2043static void 2044ntlm_notification(hdb_od d) 2045{ 2046 SCDynamicStoreRef store; 2047 dispatch_queue_t queue; 2048 SCDynamicStoreContext context; 2049 2050 memset(&context, 0, sizeof(context)); 2051 context.info = (void*)d; 2052 2053 store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("com.apple.Kerberos.hdb-od"), update_ntlm, (void *)&context); 2054 if (store == NULL) 2055 return; 2056 2057 CFTypeRef key[] = {CFSTR("com.apple.smb")}; 2058 CFArrayRef nkeys = CFArrayCreate(kCFAllocatorDefault, key, 1, NULL); 2059 SCDynamicStoreSetNotificationKeys(store, nkeys, NULL); 2060 CFRelease(nkeys); 2061 2062 queue = dispatch_queue_create("com.apple.hdb-od.ntlm-name", NULL); 2063 if (queue == NULL) { 2064 CFRelease(store); 2065 errx(1, "dispatch_queue_create"); 2066 } 2067 2068 SCDynamicStoreSetDispatchQueue(store, queue); 2069 CFRelease(store); 2070 2071 dispatch_sync(queue, ^{ update_ntlm(store, NULL, d); }); 2072} 2073 2074static krb5_error_code 2075hod_get_ntlm_domain(krb5_context context, struct HDB *db, char **name) 2076{ 2077 hdb_od d = (hdb_od)db->hdb_db; 2078 if (d->ntlmDomain == NULL) { 2079 krb5_set_error_message(context, EINVAL, "no ntlm domain"); 2080 return EINVAL; 2081 } 2082 *name = strdup(d->ntlmDomain); 2083 return 0; 2084} 2085 2086 2087#ifdef __APPLE_PRIVATE__ 2088 2089static struct { 2090 typeof(DSChangePasswordWithPolicy) *_HDBDSChangePasswordWithPolicy; 2091 typeof(DSUpdateLoginStatus) *_HDBDSUpdateLoginStatus; 2092} ds_ptrs; 2093 2094static void 2095DirectoryServer_framework(void) 2096{ 2097 static dispatch_once_t once; 2098 2099 /* 2100 * Do softlinking to avoid having a dependency on DirectoryServer framework 2101 */ 2102 2103 dispatch_once(&once, ^{ 2104 void *ptr = dlopen("/System/Library/PrivateFrameworks/DirectoryServer.framework/DirectoryServer", RTLD_LAZY); 2105 if (ptr) { 2106 ds_ptrs._HDBDSChangePasswordWithPolicy = dlsym(ptr, "DSChangePasswordWithPolicy"); 2107 ds_ptrs._HDBDSUpdateLoginStatus = dlsym(ptr, "DSUpdateLoginStatus"); 2108 } 2109 }); 2110} 2111 2112static krb5_error_code 2113hod_password(krb5_context context, struct HDB *db, hdb_entry_ex *entry, const char *password, int flags) 2114{ 2115 char *user; 2116 int ret; 2117 2118 DirectoryServer_framework(); 2119 2120 if (ds_ptrs._HDBDSChangePasswordWithPolicy == NULL) 2121 return EINVAL; 2122 2123 if (krb5_principal_get_num_comp(context, entry->entry.principal) != 1) 2124 return EINVAL; 2125 2126 user = (char *)krb5_principal_get_comp_string(context, entry->entry.principal, 0); 2127 2128 ret = ds_ptrs._HDBDSChangePasswordWithPolicy(user, password, (flags & HDB_PWD_CONDITIONAL) == 0); 2129 if (ret != 0) { 2130 krb5_set_error_message(context, EINVAL, "DSPasswordServer rejected password"); 2131 return EINVAL; 2132 } 2133 2134 return 0; 2135} 2136 2137static krb5_error_code 2138hod_auth_status(krb5_context context, struct HDB *db, hdb_entry_ex *entry, int status) 2139{ 2140 char *user; 2141 2142 DirectoryServer_framework(); 2143 2144 if (ds_ptrs._HDBDSUpdateLoginStatus == NULL) 2145 return EINVAL; 2146 2147 if (krb5_principal_get_num_comp(context, entry->entry.principal) != 1) 2148 return EINVAL; 2149 2150 user = (char *)krb5_principal_get_comp_string(context, entry->entry.principal, 0); 2151 2152 /* DS and HDB status code matches, so just pass them though */ 2153 ds_ptrs._HDBDSUpdateLoginStatus(user, status); 2154 2155 return 0; 2156} 2157 2158#endif 2159 2160 2161krb5_error_code 2162hdb_od_create(krb5_context context, HDB ** db, const char *arg) 2163{ 2164 CFMutableArrayRef attrs; 2165 ODNodeRef localRef = NULL; 2166 ODRecordRef kdcConfRef = NULL; 2167 CFArrayRef data = NULL; 2168 krb5_error_code ret; 2169 hdb_od d = NULL; 2170 char *path = NULL; 2171 int i; 2172 2173 *db = calloc(1, sizeof(**db)); 2174 if (*db == NULL) { 2175 ret = ENOMEM; 2176 krb5_set_error_message(context, ret, "malloc: out of memory"); 2177 goto out; 2178 } 2179 memset(*db, 0, sizeof(**db)); 2180 2181 path = strdup(arg); 2182 if (path == NULL) { 2183 ret = ENOMEM; 2184 krb5_set_error_message(context, ret, "malloc: out of memory"); 2185 goto out; 2186 } 2187 2188 d = calloc(1, sizeof(*d)); 2189 if (d == NULL) { 2190 ret = ENOMEM; 2191 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 2192 goto out; 2193 } 2194 2195 { 2196 char *p = strchr(path, '&'); 2197 if (p) { 2198 *p++ = '\0'; 2199 d->restoreRoot = CFStringCreateWithCString(NULL, p, kCFStringEncodingUTF8); 2200 } 2201 } 2202 2203 d->rootName = CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8); 2204 if (d == NULL) { 2205 ret = ENOMEM; 2206 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 2207 goto out; 2208 } 2209 2210 attrs = CFArrayCreateMutable(NULL, num_keys, &kCFTypeArrayCallBacks); 2211 for (i = 0; i < num_keys; i++) 2212 CFArrayAppendValue(attrs, keys[i].name); 2213 d->inNodeAttributes = attrs; 2214 2215 attrs = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks); 2216 CFArrayAppendValue(attrs, kRealName); 2217 d->inKDCAttributes = attrs; 2218 2219 attrs = CFArrayCreateMutable(NULL, 2, &kCFTypeArrayCallBacks); 2220 CFArrayAppendValue(attrs, kODRecordTypeComputers); 2221 CFArrayAppendValue(attrs, kODRecordTypeUsers); 2222 2223 d->inComputerOrUsers = attrs; 2224 2225 (*db)->hdb_db = d; 2226 2227 (*db)->hdb_capability_flags = HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL; 2228 (*db)->hdb_master_key_set = 0; 2229 (*db)->hdb_openp = 0; 2230 (*db)->hdb_open = hod_open; 2231 (*db)->hdb_close = hod_close; 2232 (*db)->hdb_store = hod_store; 2233 (*db)->hdb_remove = hod_remove; 2234 (*db)->hdb_firstkey = hod_firstkey; 2235 (*db)->hdb_nextkey = hod_nextkey; 2236 (*db)->hdb_lock = hod_lock; 2237 (*db)->hdb_unlock = hod_unlock; 2238 (*db)->hdb_rename = NULL; 2239 (*db)->hdb__get = NULL; 2240 (*db)->hdb__put = NULL; 2241 (*db)->hdb__del = NULL; 2242 (*db)->hdb_destroy = hod_destroy; 2243 (*db)->hdb_get_realms = hod_get_realms; 2244 (*db)->hdb_get_ntlm_domain = hod_get_ntlm_domain; 2245 2246 2247 /* 2248 * The /Local/Default realm is the LKDC realm, so lets pick up the 2249 * LKDC configuration that we will use later. 2250 */ 2251 2252 if (strncmp(path, "/Local/", 7) == 0) { 2253 2254 /* 2255 * Pull out KerberosLocalKDC configuration 2256 */ 2257 2258 ret = nodeCreateWithName(context, d, &localRef); 2259 if (ret) 2260 goto out; 2261 2262 kdcConfRef = ODNodeCopyRecord(localRef, kODRecordTypeConfiguration, 2263 CFSTR("KerberosKDC"), 2264 d->inKDCAttributes, NULL); 2265 if (kdcConfRef == NULL) { 2266 ret = HDB_ERR_NOENTRY; 2267 krb5_set_error_message(context, ret, "Failed to find KerberosKDC node"); 2268 goto out; 2269 } 2270 2271 data = ODRecordCopyValues(kdcConfRef, kRealName, NULL); 2272 if (data == NULL) { 2273 ret = HDB_ERR_NOENTRY; 2274 krb5_set_error_message(context, ret, "Failed to copy RealName from KerberosKDC node"); 2275 goto out; 2276 } 2277 2278 if (CFArrayGetCount(data) != 1) { 2279 ret = HDB_ERR_NOENTRY; 2280 krb5_set_error_message(context, ret, "Found RealName %d from KerberosKDC", 2281 (int)CFArrayGetCount(data)); 2282 goto out; 2283 } 2284 d->LKDCRealm = rk_cfstring2cstring((CFStringRef)CFArrayGetValueAtIndex(data, 0)); 2285 if (d->LKDCRealm == NULL) { 2286 ret = HDB_ERR_NOENTRY; 2287 krb5_set_error_message(context, ret, "failed to find realm"); 2288 goto out; 2289 } 2290 2291 CFRelease(data); data = NULL; 2292 CFRelease(kdcConfRef); kdcConfRef = NULL; 2293 CFRelease(localRef); localRef = NULL; 2294 2295 ntlm_notification(d); 2296 2297 (*db)->hdb_fetch_kvno = hod_lkdc_fetch; 2298 d->locate_record = lkdc_locate_record; 2299 2300 } else { 2301 /* hod_password interface is stupid, pipe can fail and we might not expect it to */ 2302 /* Not the right place for this. */ 2303#ifdef HAVE_SIGACTION 2304 struct sigaction sa; 2305 2306 sa.sa_flags = 0; 2307 sa.sa_handler = SIG_IGN; 2308 sigemptyset(&sa.sa_mask); 2309 2310 sigaction(SIGPIPE, &sa, NULL); 2311#else 2312 signal(SIGPIPE, SIG_IGN); 2313#endif /* HAVE_SIGACTION */ 2314 2315 (*db)->hdb_capability_flags |= HDB_CAP_F_HANDLE_PASSWORDS; 2316#ifdef __APPLE_PRIVATE__ 2317 (*db)->hdb_password = hod_password; 2318#endif 2319 (*db)->hdb_fetch_kvno = hod_server_fetch; 2320 d->locate_record = server_locate_record; 2321 2322 (*db)->hdb_auth_status = hod_auth_status; 2323 } 2324 2325 2326 free(path); 2327 2328 return 0; 2329 2330 out: 2331 if (path) 2332 free(path); 2333 if (data) 2334 CFRelease(data); 2335 if (kdcConfRef) 2336 CFRelease(kdcConfRef); 2337 if (localRef) 2338 CFRelease(localRef); 2339 2340 if (*db) { 2341 free(*db); 2342 *db = NULL; 2343 } 2344 if (d) { 2345 if (d->rootName) 2346 CFRelease(d->rootName); 2347 if (d->restoreRoot) 2348 CFRelease(d->restoreRoot); 2349 free(d); 2350 } 2351 2352 return ret; 2353} 2354 2355#endif 2356