1/* 2 * Copyright (c) 2010 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2010 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#include "config.h" 37 38#include "HeimODAdmin.h" 39 40#ifdef __APPLE_PRIVATE__ 41#include <OpenDirectory/OpenDirectoryPriv.h> 42#endif 43 44#include <krb5.h> 45#include <heimbase.h> 46#include <hx509.h> 47#include <asn1-common.h> 48#include <hdb.h> 49#include "admin.h" 50 51 52#define kHeimODKerberosKeys CFSTR("dsAttrTypeNative:KerberosKeys") 53#define kHeimODKerberosFlags CFSTR("dsAttrTypeNative:KerberosFlags") 54#define kHeimODKerberosUserName CFSTR("dsAttrTypeNative:KerberosUserName") 55#define kHeimODKerberosServerName CFSTR("dsAttrTypeNative:KerberosServerName") 56static CFStringRef kHeimASI = CFSTR("dsAttrTypeStandard:AltSecurityIdentities"); 57static CFStringRef kHeimSRP = CFSTR("dsAttrTypeNative:HeimdalSRPKey"); 58 59static CFStringRef kRealName = CFSTR("dsAttrTypeStandard:RealName"); 60 61#define kHeimLDAPKerberosUserName CFSTR("dsAttrTypeNative:draft-krbPrincipalName") 62#define kHeimLDAPKerberosServerName CFSTR("dsAttrTypeNative:draft-krbPrincipalAliases") 63#define kHeimLDAPKerberosKeys CFSTR("dsAttrTypeNative:draft-krbKeySet") 64#define kHeimLDAPKerberosFlags CFSTR("dsAttrTypeNative:draft-krbTicketPolicy") 65 66 67 68static CFStringRef remove_keys_client[] = { 69 kHeimODKerberosUserName, 70 kHeimODKerberosServerName, 71 kHeimODKerberosKeys, 72 kHeimODKerberosFlags, 73 CFSTR("dsAttrTypeNative:KerberosMaxLife"), 74 CFSTR("dsAttrTypeNative:KerberosMaxRenew"), 75 CFSTR("dsAttrTypeNative:KerberosValidStart"), 76 CFSTR("dsAttrTypeNative:KerberosValidEnd") 77}; 78static const int num_remove_keys_client = sizeof(remove_keys_client) / sizeof(remove_keys_client[0]); 79 80static CFStringRef remove_keys_server[] = { 81 kHeimLDAPKerberosUserName, 82 kHeimLDAPKerberosServerName, 83 kHeimLDAPKerberosKeys, 84 kHeimLDAPKerberosFlags 85}; 86static const int num_remove_keys_server = sizeof(remove_keys_server) / sizeof(remove_keys_server[0]); 87 88 89#define kHeimODKerberosACL CFSTR("dsAttrTypeNative:KerberosACL") 90#define kHeimLDAPKerberosACL CFSTR("dsAttrTypeNative:draft-krbPrincipalACL") 91 92struct s2k { 93 CFStringRef s; 94 unsigned int k; 95}; 96 97struct s2k FlagsKeys[] = { 98 { kPrincipalFlagInitial, (1<<0) }, 99 { kPrincipalFlagForwardable, (1<<1) }, 100 { kPrincipalFlagProxyable, (1<<2) }, 101 { kPrincipalFlagRenewable, (1<<3) }, 102 { kPrincipalFlagServer, (1<<5) }, 103 { kPrincipalFlagInvalid, (1<<7) }, 104 { kPrincipalFlagRequireStrongPreAuthentication, (1<<8) }, 105 { kPrincipalFlagPasswordChangeService, (1<<9) }, 106 { kPrincipalFlagOKAsDelegate, (1<<11) }, 107 { kPrincipalFlagImmutable, (1<<13) }, 108 { NULL } 109}; 110 111struct s2k ACLKeys[] = { 112 { kHeimODACLChangePassword, KADM5_PRIV_CPW }, 113 { kHeimODACLList, KADM5_PRIV_LIST }, 114 { kHeimODACLDelete, KADM5_PRIV_DELETE }, 115 { kHeimODACLModify, KADM5_PRIV_MODIFY }, 116 { kHeimODACLAdd, KADM5_PRIV_ADD }, 117 { kHeimODACLGet, KADM5_PRIV_GET }, 118 { NULL } 119}; 120 121static CFIndex 122createError(CFErrorRef *error, CFIndex errorcode, CFStringRef fmt, ...) 123 CF_FORMAT_FUNCTION(3, 4); 124 125static CFIndex 126createError(CFErrorRef *error, CFIndex errorcode, CFStringRef fmt, ...) 127{ 128 void *keys[1] = { (void *)CFSTR("HODErrorMessage") }; 129 void *values[1]; 130 va_list va; 131 132 if (error == NULL) 133 return errorcode; 134 135 va_start(va, fmt); 136 values[0] = (void *)CFStringCreateWithFormatAndArguments(NULL, NULL, fmt, va); 137 va_end(va); 138 if (values[0] == NULL) { 139 *error = NULL; 140 return errorcode; 141 } 142 143 if (*error) 144 CFRelease(*error); 145 146 *error = CFErrorCreateWithUserInfoKeysAndValues(NULL, CFSTR("com.apple.Heimdal.ODAdmin"), errorcode, 147 (const void * const *)keys, 148 (const void * const *)values, 1); 149 CFRelease(values[0]); 150 return errorcode; 151} 152 153static char * 154cfstring2cstring(CFStringRef string) 155{ 156 CFIndex l; 157 char *s; 158 159 s = (char *)CFStringGetCStringPtr(string, kCFStringEncodingUTF8); 160 if (s) 161 return strdup(s); 162 163 l = CFStringGetMaximumSizeForEncoding(CFStringGetLength(string), kCFStringEncodingUTF8) + 1; 164 s = malloc(l); 165 if (s == NULL) 166 return NULL; 167 168 if (!CFStringGetCString(string, s, l, kCFStringEncodingUTF8)) { 169 free (s); 170 return NULL; 171 } 172 173 return s; 174} 175 176static krb5_principal 177krb5_principalCreateFromString(krb5_context context, CFStringRef principal) 178{ 179 krb5_principal princ; 180 krb5_error_code ret; 181 char *princstr; 182 183 princstr = cfstring2cstring(principal); 184 if (princstr == NULL) 185 return NULL; 186 187 ret = krb5_parse_name(context, princstr, &princ); 188 free(princstr); 189 if (ret) 190 return NULL; 191 192 return princ; 193} 194 195static CFStringRef 196CFStringCreateWithkrb5_principal(krb5_context context, 197 krb5_const_principal principal) 198{ 199 krb5_error_code ret; 200 CFStringRef value; 201 char *name; 202 203 ret = krb5_unparse_name(context, principal, &name); 204 if (ret) 205 return NULL; 206 207 value = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingUTF8); 208 krb5_xfree(name); 209 210 return value; 211} 212 213 214 215static unsigned int 216string2int(const struct s2k *t, CFStringRef s) 217{ 218 while (t->s) { 219 if (CFStringCompare(s, t->s, kCFCompareCaseInsensitive) == kCFCompareEqualTo) 220 return t->k; 221 t++; 222 } 223 224 return 0; 225} 226 227static bool 228is_record_server_location(ODRecordRef record) 229{ 230 CFArrayRef values; 231 bool is_server = false; 232 233 values = ODRecordCopyValues(record, kODAttributeTypeMetaNodeLocation, NULL); 234 if (values == NULL) 235 return false; 236 237 if (CFArrayGetCount(values) > 0) { 238 CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(values, 0); 239 if (CFStringGetTypeID() == CFGetTypeID(str) && 240 CFStringHasPrefix(str, CFSTR("/LDAPv3/"))) 241 { 242 is_server = true; 243 } 244 } 245 CFRelease(values); 246 247 return is_server; 248} 249 250static bool 251force_server(void) 252{ 253 static dispatch_once_t once; 254 static bool force = false; 255 256 dispatch_once(&once, ^{ 257 CFBooleanRef b; 258 b = CFPreferencesCopyAppValue(CFSTR("ForceHeimODServerMode"), 259 CFSTR("com.apple.Kerberos")); 260 if (b && CFGetTypeID(b) == CFBooleanGetTypeID()) 261 force = CFBooleanGetValue(b); 262 if (b) 263 CFRelease(b); 264 }); 265 return force; 266} 267 268static bool 269is_record_server(ODRecordRef record) 270{ 271 if (force_server()) 272 return true; 273 274 return is_record_server_location(record); 275} 276 277static ODRecordRef 278copyDataRecord(ODNodeRef node, ODRecordRef record, 279 CFStringRef ckey, CFStringRef skey, 280 CFStringRef *key, bool *server_record_p, 281 CFErrorRef *error) 282{ 283 /* XXX copy user node */ 284 bool is_server = is_record_server_location(record); 285 286 if (is_server) { 287 288 if (force_server()) { 289 CFRetain(record); 290 } else { 291#ifdef __APPLE_PRIVATE__ 292 if (CFStringCompare(ODRecordGetRecordType(record), kODRecordTypeUserAuthenticationData, 0) == kCFCompareEqualTo) { 293 CFRetain(record); 294 } else { 295 record = ODNodeCopyRecordAuthenticationData(node, record, error); 296 if (record == NULL && error != NULL && *error == NULL) 297 createError(error, 1, CFSTR("can't map user record to UserAuthenticationRecord")); 298 } 299#else 300 CFRetain(record); 301#endif 302 } 303 304 if (key) 305 *key = skey; 306 } else { 307 CFRetain(record); 308 309 if (key) 310 *key = ckey; 311 } 312 313 if (server_record_p) 314 *server_record_p = is_server; 315 316 return record; 317} 318 319 320static unsigned int 321flags2int(const struct s2k *t, CFTypeRef type) 322{ 323 unsigned int nflags = 0; 324 325 if (t == NULL) 326 abort(); 327 328 if (CFGetTypeID(type) == CFStringGetTypeID()) { 329 330 nflags = string2int(t, type); 331 332 } else if (CFGetTypeID(type) == CFArrayGetTypeID()) { 333 CFIndex n; 334 335 for (n = 0; n < CFArrayGetCount(type); n++) { 336 CFTypeRef el = CFArrayGetValueAtIndex(type, n); 337 338 if (CFGetTypeID(el) == CFStringGetTypeID()) 339 nflags |= string2int(t, el); 340 } 341 } 342 return nflags; 343} 344 345static CFArrayRef 346CopyInt2flags(const struct s2k *t, unsigned long n) 347{ 348 CFMutableArrayRef array; 349 350 array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 351 if (array == NULL) 352 return NULL; 353 354 while (t->s) { 355 if (t->k & n) 356 CFArrayAppendValue(array, t->s); 357 t++; 358 } 359 360 return array; 361} 362 363/* 364 * 365 */ 366 367int 368HeimODCreateRealm(ODNodeRef node, CFStringRef realm, CFErrorRef *error) 369{ 370 /* create 371 * - krbtgt/realm 372 * - kadmin/admin 373 * - kadmin/changepw 374 * - WELLKNOWN/ANONYMOUS 375 */ 376 if (error) 377 *error = NULL; 378 return 0; 379} 380 381/* 382 * 383 */ 384 385int 386HeimODCreatePrincipalData(ODNodeRef node, ODRecordRef record, CFTypeRef flags, CFStringRef principal, CFErrorRef *error) 387{ 388 CFStringRef element; 389 bool r = false; 390 bool is_server = false; 391 unsigned int nflags = 392 (1<<1) /* forwardable */ | 393 (1<<2) /* proxiable */ | 394 (1<<6) /* client */ | 395 (1<<7) /* invalid */; 396 CFStringRef kflags, kusername; 397 ODRecordRef datarecord = NULL; 398 399 if (error) 400 *error = NULL; 401 402 datarecord = copyDataRecord(node, record, NULL, NULL, NULL, &is_server, error); 403 if (datarecord == NULL) 404 goto out; 405 406 if (is_server) { 407 kflags = kHeimLDAPKerberosFlags; 408 kusername = kHeimLDAPKerberosUserName; 409 } else { 410 kflags = kHeimODKerberosFlags; 411 kusername = kHeimODKerberosUserName; 412 } 413 414 if (flags) 415 nflags |= flags2int(FlagsKeys, flags); 416 417 element = CFStringCreateWithFormat(kCFAllocatorDefault, 418 NULL, CFSTR("%u"), nflags); 419 if (element == NULL) { 420 createError(error, ENOMEM, CFSTR("out of memory")); 421 goto out; 422 } 423 424 r = ODRecordSetValue(datarecord, kflags, element, error); 425 CFRelease(element); 426 if (!r) 427 goto out; 428 429 if (principal) { 430 r = ODRecordSetValue(datarecord, kusername, principal, error); 431 if (!r) 432 goto out; 433 } 434 435 if (!ODRecordSynchronize(datarecord, NULL)) 436 r = false; 437 438out: 439 if (datarecord) 440 CFRelease(datarecord); 441 return r ? 0 : 1; 442} 443 444/* 445 * 446 */ 447 448int 449HeimODRemovePrincipalData(ODNodeRef node, ODRecordRef record, CFStringRef principal, CFErrorRef *error) 450{ 451 CFMutableArrayRef array; 452 ODRecordRef datarecord; 453 CFStringRef *remove_keys; 454 int num_remove_keys; 455 bool is_server = false; 456 CFIndex i; 457 int ret = 1; 458 459 datarecord = copyDataRecord(node, record, NULL, NULL, NULL, &is_server, error); 460 if (datarecord == NULL) 461 goto out; 462 463 if (is_server) { 464 remove_keys = remove_keys_server; 465 num_remove_keys = num_remove_keys_server; 466 } else { 467 remove_keys = remove_keys_client; 468 num_remove_keys = num_remove_keys_client; 469 } 470 471 array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 472 if (array == NULL) { 473 createError(error, 1, CFSTR("out of memory")); 474 goto out; 475 } 476 477 for (i = 0; i < num_remove_keys; i++) 478 ODRecordSetValue(datarecord, remove_keys[i], array, NULL); 479 480 CFRelease(array); 481 482 if (!ODRecordSynchronize(datarecord, error)) 483 ret = 1; 484 else 485 ret = 0; 486 487 out: 488 if (datarecord) 489 CFRelease(datarecord); 490 491 return ret; 492} 493 494/* 495 * 496 */ 497 498static unsigned long 499getflags(ODRecordRef datarecord, CFStringRef kflags, CFErrorRef *error) 500{ 501 CFArrayRef array; 502 CFStringRef s; 503 int32_t n; 504 505 array = ODRecordCopyValues(datarecord, kflags, error); 506 if (array == NULL) 507 return 0; 508 509 if (CFArrayGetCount(array) < 1) { 510 CFRelease(array); 511 return 0; 512 } 513 s = CFArrayGetValueAtIndex(array, 0); 514 515 if (CFStringGetTypeID() != CFGetTypeID(s)) { 516 CFRelease(array); 517 return 0; 518 } 519 520 n = CFStringGetIntValue(s); 521 CFRelease(array); 522 523 if (n == INT_MAX || n == INT_MIN) 524 return 0; 525 526 return n; 527} 528 529static int 530flagop(ODRecordRef datarecord, 531 CFStringRef kflags, 532 CFTypeRef flags, 533 const struct s2k *keys, CFErrorRef *error, 534 unsigned long (^op)(unsigned long oldflags, unsigned long newflags)) 535{ 536 unsigned long uflags, oldflags; 537 CFStringRef element; 538 bool r = false; 539 540 uflags = flags2int(keys, flags); 541 if (uflags == 0) { 542 createError(error, 1, CFSTR("failed to parse input flags")); 543 goto out; 544 } 545 546 oldflags = getflags(datarecord, kflags, error); 547 548 uflags = op(oldflags, uflags); 549 550 element = CFStringCreateWithFormat(kCFAllocatorDefault, 551 NULL, CFSTR("%lu"), uflags); 552 if (element == NULL) { 553 createError(error, ENOMEM, CFSTR("out of memory")); 554 goto out; 555 } 556 557 r = ODRecordSetValue(datarecord, kflags, element, error); 558 CFRelease(element); 559 560 out: 561 if (!r) 562 return 1; 563 return 0; 564} 565 566/* 567 * 568 */ 569 570int 571HeimODSetKerberosFlags(ODNodeRef node, ODRecordRef record, CFTypeRef flags, CFErrorRef *error) 572{ 573 int ret; 574 ODRecordRef datarecord; 575 bool is_server; 576 CFStringRef kflags; 577 578 datarecord = copyDataRecord(node, record, 579 kHeimODKerberosFlags, kHeimLDAPKerberosFlags, 580 &kflags, &is_server, error); 581 if (datarecord == NULL) 582 return 1; 583 584 ret = flagop(datarecord, kflags, flags, FlagsKeys, error, ^(unsigned long oldflags, unsigned long newflags) { 585 return (oldflags | newflags); 586 }); 587 588 if (ret == 0 && !ODRecordSynchronize(datarecord, error)) 589 ret = 1; 590 591 CFRelease(datarecord); 592 593 return ret; 594} 595 596/* 597 * 598 */ 599 600CFArrayRef 601HeimODCopyKerberosFlags(ODNodeRef node, ODRecordRef record, CFErrorRef *error) 602{ 603 unsigned long uflags; 604 CFArrayRef flags = NULL; 605 CFStringRef kflags; 606 607 ODRecordRef datarecord; 608 609 datarecord = copyDataRecord(node, record, 610 kHeimODKerberosFlags, kHeimLDAPKerberosFlags, 611 &kflags, NULL, error); 612 if (datarecord == NULL) 613 goto out; 614 615 uflags = getflags(datarecord, kflags, error); 616 if (uflags == 0) { 617 createError(error, 1, CFSTR("failed to parse input flags")); 618 goto out; 619 } 620 621 flags = CopyInt2flags(FlagsKeys, uflags); 622 623 out: 624 if (datarecord) 625 CFRelease(datarecord); 626 return flags; 627} 628 629/* 630 * 631 */ 632 633int 634HeimODClearKerberosFlags(ODNodeRef node, ODRecordRef record, CFTypeRef flags, CFErrorRef *error) 635{ 636 ODRecordRef datarecord; 637 int ret; 638 bool is_server; 639 CFStringRef kflags; 640 641 datarecord = copyDataRecord(node, record, NULL, NULL, NULL, &is_server, error); 642 if (datarecord == NULL) 643 return 1; 644 645 if (is_server) { 646 kflags = kHeimLDAPKerberosFlags; 647 } else { 648 kflags = kHeimODKerberosFlags; 649 } 650 651 ret = flagop(datarecord, kflags, flags, FlagsKeys, error, ^(unsigned long oldflags, unsigned long newflags) { 652 return (oldflags & (~newflags)); 653 }); 654 655 if (ret == 0 && !ODRecordSynchronize(datarecord, error)) 656 ret = 1; 657 658 CFRelease(datarecord); 659 return ret; 660} 661 662/* 663 * 664 */ 665 666static CFTypeRef 667CopyReMapACL(CFTypeRef flags, CFErrorRef *error) 668{ 669 if (CFGetTypeID(flags) == CFStringGetTypeID() && CFStringCompare(flags, kHeimODACLAll, kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 670 CFMutableArrayRef nflags; 671 CFIndex n; 672 673 nflags = CFArrayCreateMutable(NULL, sizeof(ACLKeys) / sizeof(ACLKeys[0]), &kCFTypeArrayCallBacks); 674 if (nflags == NULL) { 675 createError(error, ENOMEM, CFSTR("out of memory")); 676 return NULL; 677 } 678 for (n = 0; ACLKeys[n].s; n++) 679 CFArrayAppendValue(nflags, ACLKeys[n].s); 680 flags = nflags; 681 } else { 682 CFRetain(flags); 683 } 684 return flags; 685} 686 687/* 688 * 689 */ 690 691int 692HeimODSetACL(ODNodeRef node, ODRecordRef record, CFTypeRef flags, CFErrorRef *error) 693{ 694 int ret; 695 ODRecordRef datarecord; 696 CFStringRef kflags; 697 698 datarecord = copyDataRecord(node, record, 699 kHeimODKerberosACL, kHeimLDAPKerberosACL, 700 &kflags, NULL, error); 701 if (datarecord == NULL) 702 return 1; 703 704 flags = CopyReMapACL(flags, error); 705 if (flags == NULL) 706 return 1; 707 708 ret = flagop(datarecord, kflags, flags, ACLKeys, error, ^(unsigned long oldflags, unsigned long newflags) { 709 return newflags; 710 }); 711 712 if (ret == 0 && !ODRecordSynchronize(datarecord, error)) 713 ret = 1; 714 715 CFRelease(datarecord); 716 717 CFRelease(flags); 718 719 return ret; 720} 721 722CFArrayRef 723HeimODCopyACL(ODNodeRef node, ODRecordRef record, CFErrorRef *error) 724{ 725 unsigned long uflags; 726 CFArrayRef flags = NULL; 727 CFStringRef kflags; 728 729 ODRecordRef datarecord; 730 731 datarecord = copyDataRecord(node, record, 732 kHeimODKerberosACL, kHeimLDAPKerberosACL, 733 &kflags, NULL, error); 734 if (datarecord == NULL) 735 goto out; 736 737 uflags = getflags(datarecord, kflags, error); 738 if (uflags == 0) { 739 createError(error, 1, CFSTR("failed to parse input flags")); 740 goto out; 741 } 742 743 flags = CopyInt2flags(ACLKeys, uflags); 744 745 out: 746 if (datarecord) 747 CFRelease(datarecord); 748 return flags; 749} 750 751 752int 753HeimODClearACL(ODNodeRef node, ODRecordRef record, CFTypeRef flags, CFErrorRef *error) 754{ 755 ODRecordRef datarecord; 756 int ret; 757 bool is_server; 758 CFStringRef kflags; 759 760 datarecord = copyDataRecord(node, record, kHeimODKerberosACL, kHeimLDAPKerberosACL, &kflags, &is_server, error); 761 if (datarecord == NULL) 762 return 1; 763 764 flags = CopyReMapACL(flags, error); 765 if (flags == NULL) 766 return 1; 767 768 ret = flagop(datarecord, kflags, flags, ACLKeys, error, ^(unsigned long oldflags, unsigned long newflags) { 769 return (oldflags & (~newflags)); 770 }); 771 772 if (ret == 0 && !ODRecordSynchronize(datarecord, error)) 773 ret = 1; 774 775 CFRelease(flags); 776 CFRelease(datarecord); 777 return ret; 778} 779 780/* 781 * 782 */ 783 784int 785HeimODAddServerAlias(ODNodeRef node, ODRecordRef record, CFStringRef alias, CFErrorRef *error) 786{ 787 CFErrorRef localerror = NULL; 788 bool r = false; 789 CFStringRef key; 790 ODRecordRef datarecord; 791 792 datarecord = copyDataRecord(node, record, 793 kHeimODKerberosServerName, 794 kHeimLDAPKerberosServerName, &key, 795 NULL, error); 796 797 if (datarecord == NULL) 798 goto out; 799 800 r = ODRecordAddValue(datarecord, key, alias, &localerror); 801 if (!r) { 802 /* check for duplicate errors and ignore them */ 803 if (localerror && CFErrorGetCode(localerror) == kODErrorRecordAttributeValueSchemaError) { 804 CFRelease(localerror); 805 r = true; 806 } else if (error) 807 *error = localerror; 808 else if (localerror) 809 CFRelease(localerror); 810 811 goto out; 812 } 813 814 if (!ODRecordSynchronize(datarecord, error)) 815 r = false; 816out: 817 if (datarecord) 818 CFRelease(datarecord); 819 return r ? 0 : 1; 820} 821 822/* 823 * 824 */ 825 826int 827HeimODRemoveServerAlias(ODNodeRef node, ODRecordRef record, CFStringRef alias, CFErrorRef *error) 828{ 829 bool r = false; 830 CFStringRef key; 831 ODRecordRef datarecord; 832 833 datarecord = copyDataRecord(node, record, 834 kHeimODKerberosServerName, 835 kHeimLDAPKerberosServerName, &key, 836 NULL, error); 837 838 if (datarecord == NULL) 839 goto out; 840 841 r = ODRecordRemoveValue(datarecord, key, alias, error); 842 if (!r) 843 goto out; 844 845 if (!ODRecordSynchronize(datarecord, error)) 846 r = false; 847out: 848 if (datarecord) 849 CFRelease(datarecord); 850 return r ? 0 : 1; 851} 852 853/* 854 * 855 */ 856 857CFArrayRef 858HeimODCopyServerAliases(ODNodeRef node, ODRecordRef record, CFErrorRef *error) 859{ 860 ODRecordRef datarecord; 861 CFStringRef key; 862 CFArrayRef res = NULL; 863 864 datarecord = copyDataRecord(node, record, 865 kHeimODKerberosServerName, 866 kHeimLDAPKerberosServerName, &key, 867 NULL, error); 868 if (datarecord == NULL) 869 goto out; 870 871 res = ODRecordCopyValues(datarecord, key, error); 872 out: 873 if (datarecord) 874 CFRelease(datarecord); 875 return res; 876} 877 878int 879HeimODSetKerberosMaxLife(ODNodeRef node, ODRecordRef record, time_t t, CFErrorRef *error) 880{ 881 if (error) 882 *error = NULL; 883 return 0; 884} 885 886/* 887 * 888 */ 889 890time_t 891HeimODGetKerberosMaxLife(ODNodeRef node, ODRecordRef record, CFErrorRef *error) 892{ 893 if (error) 894 *error = NULL; 895 return 0; 896} 897 898/* 899 * 900 */ 901 902int 903HeimODSetKerberosMaxRenewable(ODNodeRef node, ODRecordRef record, time_t t, CFErrorRef *error) 904{ 905 if (error) 906 *error = NULL; 907 return 0; 908} 909 910/* 911 * 912 */ 913 914time_t 915HeimODGetKerberosMaxRenewable(ODNodeRef node, ODRecordRef record, CFErrorRef *error) 916{ 917 if (error) 918 *error = NULL; 919 abort(); 920} 921 922static CFStringRef 923getkeykey(ODRecordRef record) 924{ 925 if (is_record_server(record)) 926 return kHeimLDAPKerberosKeys; 927 return kHeimODKerberosKeys; 928} 929 930static unsigned 931last_kvno_array(CFArrayRef array, krb5_context context, krb5_principal principal) 932{ 933 krb5_kvno kvno = 0; 934 CFIndex i; 935 936 for (i = 0; i < CFArrayGetCount(array); i++) { 937 CFDataRef key = CFArrayGetValueAtIndex(array, i); 938 hdb_keyset_aapl keyset; 939 int ret; 940 941 if (key == NULL || CFGetTypeID(key) != CFDataGetTypeID()) 942 continue; 943 944 ret = decode_hdb_keyset_aapl(CFDataGetBytePtr(key), CFDataGetLength(key), &keyset, NULL); 945 if (ret) 946 continue; 947 948 if (principal) { 949 if (keyset.principal == NULL || krb5_principal_compare(context, keyset.principal, principal) == 0) { 950 free_hdb_keyset_aapl(&keyset); 951 continue; 952 } 953 } else { 954 if (keyset.principal) { 955 free_hdb_keyset_aapl(&keyset); 956 continue; 957 } 958 } 959 960 if (kvno < keyset.kvno) 961 kvno = keyset.kvno; 962 963 free_hdb_keyset_aapl(&keyset); 964 } 965 return kvno; 966} 967 968static unsigned 969last_kvno_record(ODRecordRef datarecord, krb5_context context, krb5_principal principal) 970{ 971 CFArrayRef array; 972 unsigned kvno = 0; 973 974 array = ODRecordCopyValues(datarecord, getkeykey(datarecord), NULL); 975 if (array == NULL) 976 return 0; 977 978 kvno = last_kvno_array(array, context, principal); 979 980 CFRelease(array); 981 982 return kvno; 983} 984 985static krb5_enctype 986find_enctype(krb5_context context, CFStringRef string) 987{ 988 krb5_enctype ret; 989 krb5_enctype e; 990 char *str = cfstring2cstring(string); 991 992 ret = krb5_string_to_enctype(context, str, &e); 993 free(str); 994 if (ret) 995 return ENCTYPE_NULL; 996 return e; 997} 998 999static void 1000applier(const void *value, void *context) 1001{ 1002 void (^block)(const void *value) = context; 1003 block(value); 1004} 1005 1006static void 1007arrayapplyblock(CFArrayRef theArray, CFRange range, void (^block)(const void *value)) 1008{ 1009 CFArrayApplyFunction(theArray, range, applier, block); 1010} 1011 1012static char * 1013get_lkdc_realm(void) 1014{ 1015 ODNodeRef localRef = NULL; 1016 ODRecordRef kdcConfRef = NULL; 1017 CFArrayRef data = NULL; 1018 char *LKDCRealm = NULL; 1019 1020 localRef = ODNodeCreateWithName(kCFAllocatorDefault, kODSessionDefault, 1021 CFSTR("/Local/Default"), NULL); 1022 if (localRef == NULL) 1023 goto out; 1024 1025 kdcConfRef = ODNodeCopyRecord(localRef, kODRecordTypeConfiguration, 1026 CFSTR("KerberosKDC"), NULL, NULL); 1027 if (kdcConfRef == NULL) 1028 goto out; 1029 1030 data = ODRecordCopyValues(kdcConfRef, kRealName, NULL); 1031 if (data == NULL) 1032 goto out; 1033 1034 if (CFArrayGetCount(data) != 1) 1035 goto out; 1036 1037 LKDCRealm = cfstring2cstring((CFStringRef)CFArrayGetValueAtIndex(data, 0)); 1038 1039 out: 1040 if (data) 1041 CFRelease(data); 1042 if (kdcConfRef) 1043 CFRelease(kdcConfRef); 1044 if (localRef) 1045 CFRelease(localRef); 1046 1047 return LKDCRealm; 1048} 1049 1050/* 1051 * 1052 */ 1053 1054static CFDataRef 1055update_keys(krb5_context context, 1056 CFArrayRef enctypes, 1057 unsigned kvno, 1058 krb5_principal princ, 1059 int include_principal, 1060 const char *password, 1061 CFErrorRef *error) 1062{ 1063 heim_octet_string data; 1064 __block hdb_keyset_aapl key; 1065 __block krb5_error_code ret; 1066 CFArrayRef defaultenctypes = NULL; 1067 CFDataRef element = NULL; 1068 size_t size = 0; 1069 1070 memset(&key, 0, sizeof(key)); 1071 1072 if (error) 1073 *error = NULL; 1074 1075 1076 if (enctypes == NULL) { 1077 enctypes = defaultenctypes = HeimODCopyDefaultEnctypes(error); 1078 if (enctypes == NULL) 1079 return NULL; 1080 } 1081 1082 key.kvno = kvno; 1083 1084 if (include_principal) { 1085 ret = krb5_copy_principal(context, princ, &key.principal); 1086 if (ret) { 1087 createError(error, ret, CFSTR("out of memory")); 1088 goto out; 1089 } 1090 } 1091 1092 1093 arrayapplyblock(enctypes, CFRangeMake(0, CFArrayGetCount(enctypes)), ^(const void *value) { 1094 void *ptr; 1095 1096 if (CFGetTypeID(value) != CFStringGetTypeID()) 1097 return; 1098 1099 krb5_enctype e = find_enctype(context, (CFStringRef)value); 1100 if (e == ENCTYPE_NULL) 1101 return; 1102 1103 ptr = realloc(key.keys.val, (key.keys.len + 1) * sizeof(key.keys.val[0])); 1104 if (ptr == NULL) 1105 return; 1106 key.keys.val = ptr; 1107 1108 /* clear new entry */ 1109 memset(&key.keys.val[key.keys.len], 0, sizeof(key.keys.val[0])); 1110 1111 if (password) { 1112 krb5_data pw, opaque; 1113 krb5_salt salt; 1114 1115 krb5_get_pw_salt(context, princ, &salt); 1116 1117 pw.data = (void *)password; 1118 pw.length = strlen(password); 1119 1120 krb5_data_zero(&opaque); 1121 1122 ret = krb5_string_to_key_data_salt_opaque (context, 1123 e, 1124 pw, 1125 salt, 1126 opaque, 1127 &key.keys.val[key.keys.len].key); 1128 1129 /* Now set salt */ 1130 key.keys.val[key.keys.len].salt = calloc(1, sizeof(*key.keys.val[key.keys.len].salt)); 1131 if (key.keys.val[key.keys.len].salt == NULL) 1132 abort(); 1133 1134 key.keys.val[key.keys.len].salt->type = salt.salttype; 1135 krb5_data_zero(&key.keys.val[key.keys.len].salt->salt); 1136 1137 ret = krb5_data_copy(&key.keys.val[key.keys.len].salt->salt, 1138 salt.saltvalue.data, 1139 salt.saltvalue.length); 1140 if (ret) 1141 abort(); 1142 1143 krb5_free_salt(context, salt); 1144 1145 } else { 1146 ret = krb5_generate_random_keyblock(context, e, &key.keys.val[key.keys.len].key); 1147 } 1148 if (ret) 1149 abort(); 1150 1151 key.keys.len++; 1152 1153 }); 1154 if (key.keys.len == 0) { 1155 ret = 1; 1156 createError(error, ret, CFSTR("no Kerberos enctypes found")); 1157 goto out; 1158 } 1159 1160#if 0 /* XXX */ 1161 /* seal using master key */ 1162 for (i = 0; i < key.keys.len; i++) { 1163 ret = hdb_seal_key_mkey(context, &key.keys.val[i], mkey); 1164 if (ret) abort(); 1165 } 1166#endif 1167 1168 ASN1_MALLOC_ENCODE(hdb_keyset_aapl, data.data, data.length, &key, &size, ret); 1169 if (ret) { 1170 createError(error, ret, CFSTR("Failed to encode keyset")); 1171 goto out; 1172 } 1173 if (data.length != size) 1174 krb5_abortx(context, "internal asn.1 encoder error"); 1175 1176 element = CFDataCreate(kCFAllocatorDefault, data.data, data.length); 1177 free(data.data); 1178 if (element == NULL) { 1179 ret = ENOMEM; 1180 goto out; 1181 } 1182 1183out: 1184 if (defaultenctypes) 1185 CFRelease(defaultenctypes); 1186 1187 free_hdb_keyset_aapl(&key); 1188 1189 return element; 1190} 1191 1192CFArrayRef 1193HeimODCreateSRPKeys(CFArrayRef srptype, /* XXX should be list of srp types */ 1194 CFStringRef principal, 1195 CFTypeRef password, 1196 unsigned long flags, 1197 CFErrorRef *error) 1198{ 1199 krb5_principal princ = NULL; 1200 krb5_context context = NULL; 1201 CFArrayRef array = NULL; 1202 char *passwdstr = NULL; 1203 krb5_error_code ret; 1204 CFDataRef element; 1205 void const *el[1]; 1206 krb5_data data; 1207 size_t size = 0; 1208 hdb_srp srp; 1209 1210 memset(&srp, 0, sizeof(srp)); 1211 1212 ret = krb5_init_context(&context); 1213 if (ret) { 1214 createError(error, 1, CFSTR("can't create kerberos context")); 1215 return NULL; 1216 } 1217 1218 if (CFGetTypeID(password) == CFDataGetTypeID()) { 1219 1220 passwdstr = malloc(CFDataGetLength(password) + 1); 1221 if (passwdstr == NULL) { 1222 createError(error, ENOMEM, CFSTR("out of memory")); 1223 goto out; 1224 } 1225 1226 memcpy(passwdstr, CFDataGetBytePtr(password), CFDataGetLength(password)); 1227 passwdstr[CFDataGetLength(password)] = '\0'; 1228 1229 } else if (CFGetTypeID(password) == CFStringGetTypeID()) { 1230 1231 passwdstr = cfstring2cstring(password); 1232 if (passwdstr == NULL) { 1233 createError(error, ENOMEM, CFSTR("out of memory")); 1234 goto out; 1235 } 1236 1237 } else { 1238 createError(error, 1, CFSTR("password not a string")); 1239 goto out; 1240 } 1241 1242 princ = krb5_principalCreateFromString(context, principal); 1243 if (princ == NULL) { 1244 createError(error, ENOMEM, CFSTR("out of memory")); 1245 goto out; 1246 } 1247 1248 1249 ret = hdb_set_srp_verifier(context, 1250 KRB5_SRP_GROUP_RFC5054_4096_PBKDF2_SHA512, 1251 princ, 1252 passwdstr, 1253 0, 1254 &srp); 1255 if (ret) { 1256 goto out; 1257 } 1258 1259 ASN1_MALLOC_ENCODE(hdb_srp, data.data, data.length, &srp, &size, ret); 1260 free_hdb_srp(&srp); 1261 if (ret) 1262 goto out; 1263 if (size != data.length) 1264 krb5_abortx(context, "internal asn.1 encoder error"); 1265 1266 element = CFDataCreate(kCFAllocatorDefault, data.data, data.length); 1267 free(data.data); 1268 if (element == NULL) 1269 goto out; 1270 1271 el[0] = element; 1272 1273 array = CFArrayCreate(kCFAllocatorDefault, el, 1, &kCFTypeArrayCallBacks); 1274 CFRelease(element); 1275 if (array == NULL) { 1276 ret = ENOMEM; 1277 goto out; 1278 } 1279 1280 out: 1281 if (context) 1282 krb5_free_context(context); 1283 if (passwdstr) 1284 free(passwdstr); 1285 1286 free_hdb_srp(&srp); 1287 1288 return array; 1289} 1290 1291 1292 1293static int 1294SetSRP(krb5_context context, ODNodeRef node, ODRecordRef datarecord, 1295 krb5_const_principal principal, CFTypeRef password, 1296 unsigned long flags, 1297 CFErrorRef *error) 1298{ 1299 CFStringRef princ = NULL; 1300 CFArrayRef array = NULL; 1301 int ret; 1302 1303 princ = CFStringCreateWithkrb5_principal(context, principal); 1304 if (princ == NULL) 1305 return 1; 1306 1307 array = HeimODCreateSRPKeys(NULL, princ, password, flags, error); 1308 CFRelease(princ); 1309 if (array == NULL) 1310 return 1; 1311 1312 if ((flags & kHeimODAdminSetKeysAppendKey) == 0) { 1313 bool r = ODRecordSetValue(datarecord, kHeimSRP, array, error); 1314 if (!r) { 1315 ret = EINVAL; 1316 goto out; 1317 } 1318 1319 ret = 0; 1320 1321 } else { 1322 ret = EINVAL; 1323 } 1324 1325 out: 1326 if (array) 1327 CFRelease(array); 1328 1329 return ret; 1330} 1331 1332bool 1333HeimODSetVerifiers(ODNodeRef node, ODRecordRef record, CFStringRef principal, CFArrayRef types, CFTypeRef password, unsigned long flags, CFErrorRef *error) 1334{ 1335 ODRecordRef datarecord = NULL; 1336 CFArrayRef array = NULL; 1337 bool res = false; 1338 1339 datarecord = copyDataRecord(node, record, NULL, NULL, NULL, NULL, error); 1340 if (datarecord == NULL) 1341 goto out; 1342 1343 array = HeimODCreateSRPKeys(types, principal, password, 0, error); 1344 if (array == NULL) 1345 goto out; 1346 1347 if (!ODRecordSetValue(datarecord, kHeimSRP, array, error)) 1348 goto out; 1349 1350 if (!ODRecordSynchronize(datarecord, error)) 1351 goto out; 1352 1353 res = true; 1354 out: 1355 if (datarecord) 1356 CFRelease(datarecord); 1357 if (array) 1358 CFRelease(array); 1359 1360 return res; 1361} 1362 1363int 1364HeimODSetKeys(ODNodeRef node, ODRecordRef record, CFStringRef principal, CFArrayRef enctypes, CFTypeRef password, unsigned long flags, CFErrorRef *error) 1365{ 1366 krb5_context context; 1367 CFDataRef element = NULL; 1368 __block krb5_error_code ret; 1369 krb5_principal princ = NULL; 1370 ODRecordRef datarecord = NULL; 1371 char *passwordstr = NULL; 1372 bool is_server; 1373 unsigned kvno; 1374 1375 ret = krb5_init_context(&context); 1376 if (ret) { 1377 createError(error, 1, CFSTR("can't create kerberos context")); 1378 return 1; 1379 } 1380 1381 datarecord = copyDataRecord(node, record, NULL, NULL, NULL, &is_server, error); 1382 if (datarecord == NULL) 1383 goto out; 1384 1385 if (principal) { 1386 princ = krb5_principalCreateFromString(context, principal); 1387 } else if (is_server == 0) { 1388 CFStringRef name = ODRecordGetRecordName(record); 1389 char *str, *princstr; 1390 1391 str = cfstring2cstring(name); 1392 if (str == NULL) { 1393 ret = ENOENT; 1394 goto out; 1395 } 1396 asprintf(&princstr, "%s@%s", str, KRB5_LKDC_REALM_NAME); 1397 free(str); 1398 1399 ret = krb5_parse_name(context, princstr, &princ); 1400 free(princstr); 1401 if (ret) { 1402 createError(error, ret, CFSTR("failed to parse principal")); 1403 goto out; 1404 } 1405 1406 } else { 1407 CFArrayRef array; 1408 CFStringRef user; 1409 1410 array = ODRecordCopyValues(datarecord, kHeimLDAPKerberosUserName, error); 1411 if (array == NULL) { 1412 ret = 1; 1413 goto out; 1414 } 1415 1416 if (CFArrayGetCount(array) < 1) { 1417 CFRelease(array); 1418 ret = 1; 1419 createError(error, ret, CFSTR("can't find user principal name")); 1420 goto out; 1421 } 1422 1423 user = CFArrayGetValueAtIndex(array, 0); 1424 if (user == NULL || CFGetTypeID(user) != CFStringGetTypeID()) { 1425 CFRelease(array); 1426 ret = 1; 1427 createError(error, ret, CFSTR("user principal name not a string")); 1428 goto out; 1429 } 1430 1431 princ = krb5_principalCreateFromString(context, user); 1432 1433 CFRelease(array); 1434 } 1435 if (princ == NULL) { 1436 createError(error, ENOMEM, CFSTR("out of memory")); 1437 ret = ENOMEM; 1438 goto out; 1439 } 1440 1441 1442 /* 1443 * Covert if we have a password, otherwise we'll use a random key. 1444 */ 1445 1446 if (password) { 1447 if (CFDataGetTypeID() == CFGetTypeID(password)) { 1448 CFDataRef data = (CFDataRef)password; 1449 passwordstr = malloc(CFDataGetLength(data) + 1); 1450 if (passwordstr == NULL) { 1451 createError(error, ENOMEM, CFSTR("out of memory")); 1452 ret = 1; 1453 goto out; 1454 } 1455 1456 memcpy(passwordstr, CFDataGetBytePtr(data), CFDataGetLength(data)); 1457 passwordstr[CFDataGetLength(data)] = '\0'; 1458 1459 } else if (CFStringGetTypeID() == CFGetTypeID(password)) { 1460 passwordstr = cfstring2cstring(password); 1461 if (passwordstr == NULL) { 1462 createError(error, ENOMEM, CFSTR("out of memory")); 1463 ret = 1; 1464 goto out; 1465 } 1466 } else { 1467 createError(error, EINVAL, CFSTR("invalid string type")); 1468 ret = 1; 1469 goto out; 1470 } 1471 } 1472 1473 kvno = last_kvno_record(datarecord, context, principal ? princ : NULL) + 1; 1474 1475 /* 1476 * If it the LKDC domain, gets go and get the realm from the 1477 * local realm database to make sure the we don't use the same 1478 * salting for every principal out there. 1479 */ 1480 1481 if (krb5_principal_is_lkdc(context, princ)) { 1482 char *realm = get_lkdc_realm(); 1483 if (realm == NULL) { 1484 ret = 1; 1485 goto out; 1486 } 1487 krb5_principal_set_realm(context, princ, realm); 1488 free(realm); 1489 } 1490 1491 /* build the new keyset */ 1492 element = update_keys(context, enctypes, kvno, princ, principal != 0, passwordstr, error); 1493 if (element == NULL) { 1494 ret = 1; 1495 goto out; 1496 } 1497 1498 if ((flags & kHeimODAdminSetKeysAppendKey) == 0) { 1499 CFMutableArrayRef array; 1500 1501 array = CFArrayCreateMutable(kCFAllocatorDefault, 1, 1502 &kCFTypeArrayCallBacks); 1503 if (array == NULL) { 1504 ret = ENOMEM; 1505 goto out; 1506 } 1507 CFArrayAppendValue(array, element); 1508 1509 bool r = ODRecordSetValue(datarecord, getkeykey(datarecord), array, error); 1510 CFRelease(array); 1511 if (!r) { 1512 ret = EINVAL; 1513 goto out; 1514 } 1515 1516 } else { 1517 bool r = ODRecordAddValue(datarecord, getkeykey(datarecord), element, error); 1518 if (!r) { 1519 ret = EINVAL; 1520 goto out; 1521 } 1522 } 1523 1524 if (password && !is_server) { 1525 ret = SetSRP(context, node, datarecord, princ, password, flags, error); 1526 if (ret) 1527 goto out; 1528 } 1529 1530 if (!ODRecordSynchronize(datarecord, error)) 1531 ret = EINVAL; 1532 1533 out: 1534 if (princ) 1535 krb5_free_principal(context, princ); 1536 if (passwordstr) { 1537 memset(passwordstr, 0, strlen(passwordstr)); 1538 free(passwordstr); 1539 } 1540 if (datarecord) 1541 CFRelease(datarecord); 1542 if (context) 1543 krb5_free_context(context); 1544 if (element) 1545 CFRelease(element); 1546 1547 return ret; 1548} 1549 1550static void 1551delete_enctypes(krb5_context context, CFArrayRef enctypes, CFMutableArrayRef keyset) 1552{ 1553 CFIndex n, m, count; 1554 krb5_enctype *etlist; 1555 unsigned o; 1556 1557 count = CFArrayGetCount(enctypes); 1558 if (count == 0) 1559 return; 1560 1561 etlist = calloc(count + 1, sizeof(*etlist)); 1562 if (etlist == NULL) 1563 return; 1564 1565 for (n = m = 0; n < count; n++) { 1566 CFStringRef str = CFArrayGetValueAtIndex(enctypes, n); 1567 if (CFGetTypeID(str) != CFStringGetTypeID()) 1568 continue; 1569 etlist[m] = find_enctype(context, str); 1570 if (etlist[m] != ETYPE_NULL) 1571 m++; 1572 } 1573 if (m == 0) { 1574 free(etlist); 1575 return; 1576 } 1577 1578 count = CFArrayGetCount(keyset); 1579 1580 for (n = 0; n < count; n++) { 1581 krb5_error_code ret; 1582 hdb_keyset_aapl key; 1583 size_t sz; 1584 int found = 0; 1585 1586 CFDataRef el = CFArrayGetValueAtIndex(keyset, n); 1587 if (CFGetTypeID(el) != CFDataGetTypeID()) 1588 continue; 1589 1590 ret = decode_hdb_keyset_aapl(CFDataGetBytePtr(el), CFDataGetLength(el), &key, &sz); 1591 if (ret) 1592 continue; 1593 1594 for (m = 0; etlist[m] != ETYPE_NULL; m++) { 1595 for (o = 0; o < key.keys.len; o++) { 1596 if (key.keys.val[o].key.keytype == etlist[m]) { 1597 /* delete key and shift down keys */ 1598 free_Key(&key.keys.val[o]); 1599 if (o + 1 != key.keys.len) 1600 memmove(&key.keys.val[o], &key.keys.val[o + 1], sizeof(key.keys.val[o]) * (key.keys.len - o - 1)); 1601 found = 1; 1602 key.keys.len--; 1603 o--; 1604 if (key.keys.len == 0) 1605 free(key.keys.val); 1606 } 1607 } 1608 } 1609 if (found && key.keys.len == 0) { 1610 CFArrayRemoveValueAtIndex(keyset, n); 1611 count--; 1612 n--; 1613 } else if (found) { 1614 /* found a key, re-encode and put back in the array */ 1615 size_t length; 1616 void *data; 1617 1618 ASN1_MALLOC_ENCODE(hdb_keyset_aapl, data, length, &key, &sz, ret); 1619 if (ret) { 1620 free(etlist); 1621 return; 1622 } 1623 if (length != sz) 1624 krb5_abortx(context, "internal asn1 error"); 1625 1626 CFDataRef d = CFDataCreate(NULL, data, length); 1627 if (d == NULL) 1628 abort(); 1629 1630 CFArraySetValueAtIndex(keyset, n, d); 1631 CFRelease(d); 1632 } 1633 free_hdb_keyset_aapl(&key); 1634 } 1635 1636 free(etlist); 1637 return; 1638} 1639 1640CFArrayRef 1641HeimODModifyKeys(CFArrayRef prevKeyset, 1642 CFStringRef principal, 1643 CFArrayRef enctypes, 1644 CFTypeRef password, 1645 unsigned long flags, 1646 CFErrorRef *error) 1647{ 1648 CFMutableArrayRef keyset = NULL; 1649 krb5_context context = NULL; 1650 char *passwordstr = NULL; 1651 krb5_principal princ = NULL; 1652 CFDataRef element = NULL; 1653 krb5_error_code ret; 1654 int32_t kvno; 1655 1656 if (error) 1657 *error = NULL; 1658 1659 ret = krb5_init_context(&context); 1660 if (ret) { 1661 createError(error, 1, CFSTR("can't create kerberos context")); 1662 return NULL; 1663 } 1664 1665 /* check if we are deleting the password */ 1666 1667 if (flags & kHeimODAdminDeleteEnctypes) { 1668 if (prevKeyset == NULL) 1669 goto out; 1670 1671 keyset = CFArrayCreateMutableCopy(NULL, 0, prevKeyset); 1672 if (keyset == NULL) 1673 goto out; 1674 1675 delete_enctypes(context, enctypes, keyset); 1676 1677 } else { 1678 1679 princ = krb5_principalCreateFromString(context, principal); 1680 if (princ == NULL) { 1681 createError(error, ENOMEM, CFSTR("out of memory")); 1682 goto out; 1683 } 1684 1685 if (password) { 1686 if (CFDataGetTypeID() == CFGetTypeID(password)) { 1687 CFDataRef data = (CFDataRef)password; 1688 passwordstr = malloc(CFDataGetLength(data) + 1); 1689 if (passwordstr == NULL) { 1690 createError(error, ENOMEM, CFSTR("out of memory")); 1691 goto out; 1692 } 1693 1694 memcpy(passwordstr, CFDataGetBytePtr(data), CFDataGetLength(data)); 1695 passwordstr[CFDataGetLength(data)] = '\0'; 1696 1697 } else if (CFStringGetTypeID() == CFGetTypeID(password)) { 1698 passwordstr = cfstring2cstring(password); 1699 if (passwordstr == NULL) { 1700 createError(error, ENOMEM, CFSTR("out of memory")); 1701 goto out; 1702 } 1703 } else { 1704 createError(error, EINVAL, CFSTR("invalid string type")); 1705 goto out; 1706 } 1707 } 1708 1709 if (prevKeyset) { 1710 kvno = last_kvno_array(prevKeyset, context, NULL); 1711 kvno++; 1712 } else { 1713 kvno = 1; 1714 } 1715 1716 element = update_keys(context, enctypes, kvno, princ, 0, passwordstr, error); 1717 if (element == NULL) 1718 goto out; 1719 1720 if (prevKeyset && (flags & kHeimODAdminSetKeysAppendKey) != 0) 1721 keyset = CFArrayCreateMutableCopy(NULL, 0, prevKeyset); 1722 else 1723 keyset = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 1724 if (keyset == NULL) 1725 goto out; 1726 1727 CFArrayAppendValue(keyset, element); 1728 } 1729 1730out: 1731 if (passwordstr) { 1732 memset(passwordstr, 0, strlen(passwordstr)); 1733 free(passwordstr); 1734 } 1735 if (element) 1736 CFRelease(element); 1737 if (princ) 1738 krb5_free_principal(context, princ); 1739 if (context) 1740 krb5_free_context(context); 1741 1742 return keyset; 1743} 1744 1745CFStringRef 1746HeimODKeysetToString(CFDataRef element, CFErrorRef *error) 1747{ 1748 krb5_context context; 1749 krb5_error_code ret; 1750 CFMutableStringRef str; 1751 hdb_keyset_aapl keyset; 1752 size_t sz, n; 1753 1754 ret = krb5_init_context(&context); 1755 if (ret) { 1756 createError(error, ret, CFSTR("failed to create context")); 1757 return NULL; 1758 } 1759 1760 ret = decode_hdb_keyset_aapl(CFDataGetBytePtr(element), 1761 CFDataGetLength(element), 1762 &keyset, &sz); 1763 if (ret) 1764 return NULL; 1765 1766 str = CFStringCreateMutable(NULL, 0); 1767 if (str == NULL) 1768 goto out; 1769 1770 CFStringAppendFormat(str, NULL, CFSTR("key kvno %d"), keyset.kvno); 1771 1772 if (keyset.principal) { 1773 char *p = NULL; 1774 if (krb5_unparse_name(context, keyset.principal, &p) != 0) { 1775 CFRelease(str); str = NULL; 1776 goto out; 1777 } 1778 1779 CFStringAppendFormat(str, NULL, CFSTR(" principal: %s"), p); 1780 free(p); 1781 } 1782 1783 for (n = 0; n < keyset.keys.len; n++) { 1784 Key *key = &keyset.keys.val[n]; 1785 size_t i; 1786 1787 CFStringAppendFormat(str, NULL, CFSTR(" [key# %d:"), (int)n); 1788 if (key->mkvno) 1789 CFStringAppendFormat(str, NULL, CFSTR(" masterkey %d:"), (int)*key->mkvno); 1790 if (key->salt) 1791 CFStringAppendFormat(str, NULL, CFSTR(" salt %d:"), (int)key->salt->type); 1792 1793 char *enctype = NULL; 1794 (void)krb5_enctype_to_string(context, key->key.keytype, &enctype); 1795 CFStringAppendFormat(str, NULL, CFSTR(" enctype %s(%d):"), enctype, key->key.keytype); 1796 free(enctype); 1797 1798 for(i = 0; i < key->key.keyvalue.length; i++) 1799 CFStringAppendFormat(str, NULL, CFSTR("%02x"), ((uint8_t *)key->key.keyvalue.data)[i]); 1800 1801 CFStringAppendFormat(str, NULL, CFSTR("]")); 1802 } 1803 1804 1805out: 1806 if (context) 1807 krb5_free_context(context); 1808 1809 free_hdb_keyset_aapl(&keyset); 1810 1811 return str; 1812} 1813 1814/* 1815 * 1816 */ 1817 1818CFArrayRef 1819HeimODCopyDefaultEnctypes(CFErrorRef *error) 1820{ 1821 CFMutableArrayRef enctypes; 1822 1823 enctypes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 1824 if (enctypes == NULL) { 1825 createError(error, ENOMEM, CFSTR("Can't create enctype array ref")); 1826 return NULL; 1827 } 1828 1829 CFArrayAppendValue(enctypes, CFSTR("aes256-cts-hmac-sha1-96")); 1830 CFArrayAppendValue(enctypes, CFSTR("aes128-cts-hmac-sha1-96")); 1831 CFArrayAppendValue(enctypes, CFSTR("des3-cbc-sha1")); 1832 1833 return enctypes; 1834} 1835 1836 1837/* 1838 * 1839 */ 1840 1841int 1842HeimODAddSubjectAltCertName(ODNodeRef node, ODRecordRef record, CFStringRef subject, CFStringRef issuer, CFErrorRef *error) 1843{ 1844 abort(); 1845 *error = NULL; 1846 return 0; 1847} 1848 1849 1850#ifdef PKINIT 1851static SecCertificateRef 1852find_ta(SecCertificateRef incert) 1853{ 1854 SecTrustResultType resultType = kSecTrustResultDeny; 1855 SecPolicyRef policy = NULL; 1856 SecTrustRef trust = NULL; 1857 SecCertificateRef cert = NULL; 1858 CFMutableArrayRef inArray = NULL; 1859 OSStatus ret; 1860 1861 policy = SecPolicyCreateBasicX509(); 1862 1863 inArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 1864 1865 CFArrayAppendValue(inArray, incert); 1866 1867 ret = SecTrustCreateWithCertificates(inArray, policy, &trust); 1868 if (ret) 1869 goto out; 1870 1871 ret = SecTrustEvaluate(trust, &resultType); 1872 if (ret) 1873 goto out; 1874 1875 if (resultType != kSecTrustResultUnspecified && resultType != kSecTrustResultProceed) 1876 goto out; 1877 1878 CFIndex certCount = SecTrustGetCertificateCount(trust); 1879 if (certCount <= 0) 1880 goto out; 1881 1882 cert = SecTrustGetCertificateAtIndex(trust, certCount - 1); 1883 1884 CFRetain(cert); 1885 1886 out: 1887 if (policy) 1888 CFRelease(policy); 1889 if (trust) 1890 CFRelease(trust); 1891 if (inArray) 1892 CFRelease(inArray); 1893 1894 return cert; 1895} 1896#endif 1897 1898/* 1899 * 1900 */ 1901 1902int 1903HeimODAddCertificate(ODNodeRef node, ODRecordRef record, SecCertificateRef ref, CFErrorRef *error) 1904{ 1905#ifdef PKINIT 1906 CFStringRef leaf = NULL, anchor = NULL; 1907 SecCertificateRef ta = NULL; 1908 hx509_name subject = NULL, taname = NULL; 1909 char *subjectstr = NULL, *tastr = NULL; 1910 hx509_context context = NULL; 1911 CFDataRef data = NULL; 1912 hx509_cert cert = NULL; 1913 CFStringRef ace = NULL; 1914 int ret; 1915 1916 ta = find_ta(ref); 1917 if (ta == NULL) { 1918 ret = EINVAL; 1919 createError(error, ret, CFSTR("Failed to find trust anchor")); 1920 goto out; 1921 } 1922 1923 ret = hx509_context_init(&context); 1924 if (ret) { 1925 ret = EINVAL; 1926 createError(error, ret, CFSTR("Failed to create hx509 context")); 1927 goto out; 1928 } 1929 1930 data = SecCertificateCopyData(ref); 1931 if (data == NULL) { 1932 ret = ENOMEM; 1933 createError(error, ret, CFSTR("out of memory")); 1934 goto out; 1935 } 1936 1937 ret = hx509_cert_init_data(context, CFDataGetBytePtr(data), CFDataGetLength(data), &cert); 1938 CFRelease(data); 1939 if (ret) { 1940 createError(error, ret, CFSTR("failed to decode certificate")); 1941 goto out; 1942 } 1943 1944 ret = hx509_cert_get_subject(cert, &subject); 1945 hx509_cert_free(cert); 1946 if (ret) { 1947 createError(error, ret, CFSTR("out of memory")); 1948 goto out; 1949 } 1950 1951 data = SecCertificateCopyData(ta); 1952 if (data == NULL) { 1953 ret = ENOMEM; 1954 createError(error, ret, CFSTR("out of memory")); 1955 goto out; 1956 } 1957 1958 ret = hx509_cert_init_data(context, CFDataGetBytePtr(data), CFDataGetLength(data), &cert); 1959 CFRelease(data); 1960 if (ret) { 1961 createError(error, ret, CFSTR("failed to decode certificate")); 1962 goto out; 1963 } 1964 1965 ret = hx509_cert_get_subject(cert, &taname); 1966 hx509_cert_free(cert); 1967 if (ret) { 1968 createError(error, ret, CFSTR("out of memory")); 1969 goto out; 1970 } 1971 1972 ret = hx509_name_to_string(subject, &subjectstr); 1973 if (ret) { 1974 createError(error, ret, CFSTR("out of memory")); 1975 goto out; 1976 } 1977 ret = hx509_name_to_string(taname, &tastr); 1978 if (ret) { 1979 createError(error, ret, CFSTR("out of memory")); 1980 goto out; 1981 } 1982 1983 leaf = CFStringCreateWithCString(kCFAllocatorDefault, subjectstr, kCFStringEncodingUTF8); 1984 if (leaf == NULL) { 1985 ret = ENOMEM; 1986 createError(error, ret, CFSTR("out of memory")); 1987 goto out; 1988 } 1989 1990 anchor = CFStringCreateWithCString(kCFAllocatorDefault, tastr, kCFStringEncodingUTF8); 1991 if (anchor == NULL) { 1992 ret = ENOMEM; 1993 createError(error, ret, CFSTR("out of memory")); 1994 goto out; 1995 } 1996 1997 if (!HeimODAddCertificateSubjectAndTrustAnchor(node, record, leaf, anchor, error)) 1998 ret = EINVAL; 1999 2000 out: 2001 if (leaf) 2002 CFRelease(leaf); 2003 if (anchor) 2004 CFRelease(anchor); 2005 if (taname) 2006 hx509_name_free(&taname); 2007 if (subject) 2008 hx509_name_free(&subject); 2009 if (tastr) 2010 free(tastr); 2011 if (subjectstr) 2012 free(subjectstr); 2013 if (ace) 2014 CFRelease(ace); 2015 if (ta) 2016 CFRelease(ta); 2017 if (context) 2018 hx509_context_free(&context); 2019 return ret; 2020#else 2021 return EINVAL; 2022#endif 2023} 2024 2025/* 2026 * 2027 */ 2028 2029int 2030HeimODAddSubjectAltCertSHA1Digest(ODNodeRef node, ODRecordRef record, CFDataRef hash, CFErrorRef *error) 2031{ 2032 abort(); 2033 *error = NULL; 2034 return 0; 2035} 2036 2037/* 2038 * 2039 */ 2040 2041CFArrayRef 2042HeimODCopySubjectAltNames(ODNodeRef node, ODRecordRef record, CFErrorRef *error) 2043{ 2044 abort(); 2045 *error = NULL; 2046 return 0; 2047} 2048 2049/* 2050 * 2051 */ 2052 2053int 2054HeimODRemoveSubjectAltElement(ODNodeRef node, ODRecordRef record, CFTypeRef element, CFErrorRef *error) 2055{ 2056 abort(); 2057 *error = NULL; 2058 return 0; 2059} 2060 2061int 2062HeimODAddCertificateSubjectAndTrustAnchor(ODNodeRef node, ODRecordRef record, CFStringRef leafSubject, CFStringRef trustAnchorSubject, CFErrorRef *error) 2063{ 2064 CFArrayRef array; 2065 CFStringRef ace; 2066 2067 if (error) 2068 *error = NULL; 2069 2070 ace = CFStringCreateWithFormat(NULL, 0, CFSTR("X509:<T>%@<S>%@"), trustAnchorSubject, leafSubject); 2071 if (ace == NULL) { 2072 createError(error, ENOMEM, CFSTR("out of memory")); 2073 return 1; 2074 } 2075 2076 /* filter out dups */ 2077 array = ODRecordCopyValues(record, kHeimASI, NULL); 2078 if (array) { 2079 __block int found_match = 0; 2080 arrayapplyblock(array, CFRangeMake(0, CFArrayGetCount(array)), ^(const void *value) { 2081 if (CFGetTypeID(value) != CFStringGetTypeID()) 2082 return; 2083 2084 if (CFStringCompare(value, ace, 0) == kCFCompareEqualTo) 2085 found_match = 1; 2086 }); 2087 CFRelease(array); 2088 if (found_match) { 2089 CFRelease(ace); 2090 return 0; 2091 } 2092 } 2093 2094 bool r = ODRecordAddValue(record, kHeimASI, ace, error); 2095 CFRelease(ace); 2096 if (!r) 2097 return 1; 2098 2099 if (!ODRecordSynchronize(record, error)) 2100 return 1; 2101 2102 return 0; 2103} 2104 2105int 2106HeimODRemoveCertificateSubjectAndTrustAnchor(ODNodeRef node, ODRecordRef record, CFStringRef leafSubject, CFStringRef trustAnchorSubject, CFErrorRef *error) 2107{ 2108 CFStringRef ace; 2109 CFErrorRef e = NULL; 2110 2111 if (error) 2112 *error = NULL; 2113 2114 ace = CFStringCreateWithFormat(NULL, 0, CFSTR("X509:<T>%@<S>%@"), trustAnchorSubject, leafSubject); 2115 if (ace == NULL) { 2116 createError(error, ENOMEM, CFSTR("out of memory")); 2117 return 1; 2118 } 2119 2120 bool r = ODRecordRemoveValue(record, kHeimASI, ace, &e); 2121 CFRelease(ace); 2122 if (!r && e == NULL) 2123 return 0; /* entry didn't exists */ 2124 else if (!r) { 2125 if (error) 2126 *error = e; 2127 else 2128 CFRelease(e); 2129 return 1; 2130 } 2131 2132 if (!ODRecordSynchronize(record, error)) 2133 return 1; 2134 2135 return 0; 2136} 2137 2138int 2139HeimODAddAppleIDAlias(ODNodeRef node, ODRecordRef record, CFStringRef alias, CFErrorRef *error) 2140{ 2141 CFArrayRef array; 2142 2143 if (error) 2144 *error = NULL; 2145 2146 if (is_record_server(record)) { 2147 createError(error, EINVAL, CFSTR("AppleID alias not supported for Server Mode")); 2148 return 1; 2149 } 2150 2151 array = ODRecordCopyValues(record, kODAttributeTypeRecordName, NULL); 2152 if (array) { 2153 __block int found_match = 0; 2154 arrayapplyblock(array, CFRangeMake(0, CFArrayGetCount(array)), ^(const void *value) { 2155 if (CFGetTypeID(value) != CFStringGetTypeID()) 2156 return; 2157 2158 if (CFStringCompare(value, alias, 0) == kCFCompareEqualTo) 2159 found_match = 1; 2160 }); 2161 CFRelease(array); 2162 if (found_match) 2163 return 0; 2164 } 2165 2166 bool r = ODRecordAddValue(record, kODAttributeTypeRecordName, alias, error); 2167 if (!r) 2168 return 1; 2169 2170 if (!ODRecordSynchronize(record, error)) 2171 return 1; 2172 2173 return 0; 2174} 2175 2176int 2177HeimODRemoveAppleIDAlias(ODNodeRef node, ODRecordRef record, CFStringRef alias, CFErrorRef *error) 2178{ 2179 CFErrorRef e = NULL; 2180 2181 if (error) 2182 *error = NULL; 2183 2184 if (is_record_server(record)) { 2185 createError(error, EINVAL, CFSTR("AppleID alias not supported for Server Mode")); 2186 return 1; 2187 } 2188 2189 bool r = ODRecordRemoveValue(record, kODAttributeTypeRecordName, alias, &e); 2190 if (!r && e == NULL) 2191 return 0; /* entry didn't exists */ 2192 else if (!r) { 2193 if (error) 2194 *error = e; 2195 else 2196 CFRelease(e); 2197 return 1; 2198 } 2199 2200 if (!ODRecordSynchronize(record, error)) 2201 return 1; 2202 2203 return 0; 2204} 2205 2206struct dumploadkeys { 2207 CFStringRef dumpkey; 2208 CFStringRef clientkey; 2209 CFStringRef serverkey; 2210 unsigned long flags; 2211#define LOAD_SERVER 1 2212#define LOAD_CLIENT 2 2213#define LOAD_SERVER_APPEND 4 2214#define DUMP 8 2215 bool (*load)(krb5_context, ODRecordRef, CFDictionaryRef, CFStringRef, CFTypeRef, unsigned long flags, CFErrorRef *); 2216 CFArrayRef (*dump_record)(ODRecordRef, CFArrayRef, CFErrorRef *); 2217 CFArrayRef (*dump_entry)(krb5_context, hdb_entry *, CFErrorRef *); 2218}; 2219 2220static bool 2221load_simple(krb5_context context, ODRecordRef record, CFDictionaryRef dict, CFStringRef key, CFTypeRef data, unsigned long flags, CFErrorRef *error) 2222{ 2223 bool ret = false; 2224 2225 if (flags & LOAD_SERVER_APPEND) { 2226 CFIndex n, num = CFArrayGetCount(data); 2227 for (n = 0; n < num; n++) { 2228 ret = ODRecordAddValue(record, key, CFArrayGetValueAtIndex(data, n), error); 2229 if (!ret) 2230 return false; 2231 } 2232 } else { 2233 ret = ODRecordSetValue(record, key, data, error); 2234 } 2235 if (!ret && *error == NULL) 2236 createError(error, 1, CFSTR("Failed to load the key %@ with value %@"), key, data); 2237 return ret; 2238} 2239 2240 2241static CFArrayRef 2242dump_simple(ODRecordRef record, CFArrayRef val, CFErrorRef *error) 2243{ 2244 return CFRetain(val); 2245} 2246 2247static CFArrayRef 2248edump_principal(krb5_context context, hdb_entry *entry, CFErrorRef *error) 2249{ 2250 CFArrayRef array; 2251 CFStringRef value; 2252 2253 value = CFStringCreateWithkrb5_principal(context, entry->principal); 2254 if (value == NULL) 2255 return NULL; 2256 2257 array = CFArrayCreate(kCFAllocatorDefault, (const void **)&value, 1, &kCFTypeArrayCallBacks); 2258 CFRelease(value); 2259 2260 return array; 2261} 2262 2263static CFArrayRef 2264edump_flags(krb5_context context, hdb_entry *entry, CFErrorRef *error) 2265{ 2266 CFArrayRef array = NULL; 2267 CFStringRef value; 2268 2269 value = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), HDBFlags2int(entry->flags)); 2270 if (value == NULL) 2271 return NULL; 2272 2273 array = CFArrayCreate(kCFAllocatorDefault, (const void **)&value, 1, &kCFTypeArrayCallBacks); 2274 CFRelease(value); 2275 2276 return array; 2277} 2278 2279static bool 2280load_keys(krb5_context context, ODRecordRef record, CFDictionaryRef dict, CFStringRef key, CFTypeRef data, unsigned long flags, CFErrorRef *error) 2281{ 2282 CFMutableArrayRef newdata = NULL; 2283 krb5_principal p = NULL; 2284 int ret; 2285 bool b; 2286 2287 /* need to make sure that keys are propperly mapped */ 2288 if (flags & LOAD_SERVER_APPEND) { 2289 CFIndex n, num; 2290 hdb_keyset_aapl keyset; 2291 2292 memset(&keyset, 0, sizeof(keyset)); 2293 2294 newdata = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 2295 if (newdata == NULL) 2296 return false; 2297 2298 CFArrayRef aprinc = CFDictionaryGetValue(dict, CFSTR("KerberosPrincipal")); 2299 if (aprinc == NULL || CFGetTypeID(aprinc) != CFArrayGetTypeID()) { 2300 createError(error, 1, CFSTR("Failed to find principal in load dictionary")); 2301 b = false; 2302 goto out; 2303 } 2304 CFStringRef princ = CFArrayGetValueAtIndex(aprinc, 0); 2305 if (princ == NULL || CFGetTypeID(princ) != CFStringGetTypeID()) { 2306 createError(error, 1, CFSTR("Failed to find principal in load dictionary")); 2307 b = false; 2308 goto out; 2309 } 2310 char *str = cfstring2cstring(princ); 2311 if (str == NULL) { 2312 createError(error, ENOMEM, CFSTR("out of memory")); 2313 b = false; 2314 goto out; 2315 } 2316 2317 ret = krb5_parse_name(context, str, &p); 2318 free(str); 2319 if (ret) { 2320 createError(error, ret, CFSTR("failed to parse name")); 2321 b = false; 2322 goto out; 2323 } 2324 2325 2326 if (CFGetTypeID(data) != CFArrayGetTypeID()) 2327 abort(); 2328 2329 num = CFArrayGetCount(data); 2330 2331 for (n = 0; n < num; n++) { 2332 CFDataRef d = CFArrayGetValueAtIndex(data, n); 2333 2334 ret = decode_hdb_keyset_aapl(CFDataGetBytePtr(d), CFDataGetLength(d), &keyset, NULL); 2335 if (ret) { 2336 createError(error, 1, CFSTR("failed to decode hdb_keyset")); 2337 b = false; 2338 goto out; 2339 } 2340 2341 if (keyset.principal == NULL) { 2342 size_t len = 0, size = 0; 2343 void *to; 2344 2345 (void)krb5_copy_principal(context, p, &keyset.principal); 2346 2347 ASN1_MALLOC_ENCODE(hdb_keyset_aapl, to, size, &keyset, &len, ret); 2348 if (ret) { 2349 free_hdb_keyset_aapl(&keyset); 2350 createError(error, ret, CFSTR("failed to parse name")); 2351 b = false; 2352 goto out; 2353 } 2354 if (len != size) 2355 abort(); 2356 2357 CFDataRef keydata = CFDataCreate(NULL, to, len); 2358 free(to); 2359 free_hdb_keyset_aapl(&keyset); 2360 if (keydata == NULL) { 2361 createError(error, ENOMEM, CFSTR("out of memory")); 2362 b = false; 2363 goto out; 2364 } 2365 CFArrayAppendValue(newdata, keydata); 2366 CFRelease(keydata); 2367 } else { 2368 free_hdb_keyset_aapl(&keyset); 2369 CFArrayAppendValue(newdata, d); 2370 } 2371 } 2372 data = newdata; 2373 } 2374 2375 b = load_simple(context, record, dict, key, data, flags, error); 2376out: 2377 if (p) 2378 krb5_free_principal(context, p); 2379 if (newdata) 2380 CFRelease(newdata); 2381 return b; 2382} 2383 2384 2385static CFArrayRef 2386edump_keys(krb5_context context, hdb_entry *entry, CFErrorRef *error) 2387{ 2388 CFArrayRef array = NULL; 2389 krb5_error_code ret; 2390 krb5_data data; 2391 CFDataRef value; 2392 hdb_keyset_aapl key; 2393 size_t size = 0; 2394 2395 key.kvno = entry->kvno; 2396 key.keys.len = entry->keys.len; 2397 key.keys.val = entry->keys.val; 2398 key.principal = NULL; 2399 2400 ASN1_MALLOC_ENCODE(hdb_keyset_aapl, data.data, data.length, &key, &size, ret); 2401 if (ret) 2402 return NULL; 2403 if (data.length != size) 2404 krb5_abortx(context, "internal asn.1 encoder error"); 2405 2406 value = CFDataCreate(kCFAllocatorDefault, data.data, data.length); 2407 free(data.data); 2408 if (value == NULL) 2409 return NULL; 2410 2411 array = CFArrayCreate(kCFAllocatorDefault, (const void **)&value, 1, &kCFTypeArrayCallBacks); 2412 CFRelease(value); 2413 2414 return array; 2415} 2416 2417 2418struct dumploadkeys dlkeys[] = { 2419 { 2420 CFSTR("KerberosPrincipal"), 2421 kHeimODKerberosUserName, 2422 kHeimLDAPKerberosUserName, 2423 LOAD_SERVER | DUMP, 2424 load_simple, 2425 dump_simple, 2426 edump_principal 2427 }, 2428 { 2429 CFSTR("KerberosPrincipal"), 2430 kHeimODKerberosUserName, 2431 kHeimLDAPKerberosServerName, 2432 LOAD_SERVER | LOAD_SERVER_APPEND, 2433 load_simple, 2434 dump_simple, 2435 edump_principal 2436 }, 2437 { 2438 CFSTR("KerberosServerPrincipal"), 2439 kHeimODKerberosServerName, 2440 kHeimLDAPKerberosServerName, 2441 LOAD_SERVER | LOAD_CLIENT | DUMP, 2442 load_simple, 2443 dump_simple, 2444 edump_principal 2445 }, 2446 { 2447 CFSTR("KerberosFlags"), 2448 kHeimODKerberosFlags, 2449 kHeimLDAPKerberosFlags, 2450 LOAD_CLIENT | LOAD_SERVER | DUMP, 2451 load_simple, 2452 dump_simple, 2453 edump_flags 2454 }, 2455 { 2456 CFSTR("KerberosKeys"), 2457 kHeimODKerberosKeys, 2458 kHeimLDAPKerberosKeys, 2459 LOAD_CLIENT | LOAD_SERVER | LOAD_SERVER_APPEND | DUMP, 2460 load_keys, 2461 dump_simple, 2462 edump_keys 2463 } 2464}; 2465 2466 2467CFDictionaryRef 2468HeimODDumpRecord(ODNodeRef node, ODRecordRef record, CFStringRef principal, CFErrorRef *error) 2469{ 2470 ODRecordRef datarecord; 2471 CFMutableDictionaryRef dict; 2472 bool is_server; 2473 size_t n; 2474 2475 if (error == NULL) 2476 abort(); 2477 2478 dict = CFDictionaryCreateMutable(NULL, 0, 2479 &kCFTypeDictionaryKeyCallBacks, 2480 &kCFTypeDictionaryValueCallBacks); 2481 if (dict == NULL) { 2482 createError(error, ENOMEM, CFSTR("out of memory")); 2483 return NULL; 2484 } 2485 2486 CFDictionaryAddValue(dict, CFSTR("version"), CFSTR("1")); 2487 2488 datarecord = copyDataRecord(node, record, NULL, NULL, NULL, &is_server, error); 2489 if (datarecord == NULL) { 2490 CFRelease(dict); dict = NULL; 2491 goto out; 2492 } 2493 2494 for (n = 0; n < sizeof(dlkeys) / sizeof(dlkeys[0]); n++) { 2495 CFStringRef key = is_server ? dlkeys[n].serverkey : dlkeys[n].clientkey; 2496 CFArrayRef values; 2497 2498 if ((dlkeys[n].flags & DUMP) == 0) 2499 continue; 2500 2501 values = ODRecordCopyValues(datarecord, key, NULL); 2502 if (values == NULL) 2503 continue; 2504 2505 CFArrayRef v = dlkeys[n].dump_record(datarecord, values, error); 2506 if (v) { 2507 CFDictionaryAddValue(dict, dlkeys[n].dumpkey, v); 2508 CFRelease(v); 2509 } 2510 2511 CFRelease(values); 2512 if (*error) { 2513 CFRelease(dict); dict = NULL; 2514 break; 2515 } 2516 } 2517 2518 out: 2519 if (datarecord) 2520 CFRelease(datarecord); 2521 2522 return dict; 2523} 2524 2525bool 2526HeimODLoadRecord(ODNodeRef node, ODRecordRef record, CFDictionaryRef dict, 2527 unsigned long flags, CFErrorRef *error) 2528{ 2529 unsigned long loadflags = 0; 2530 ODRecordRef datarecord; 2531 bool is_server; 2532 bool ret = false; 2533 size_t n; 2534 krb5_error_code status; 2535 krb5_context context; 2536 2537 if (error == NULL) 2538 abort(); 2539 2540 status = krb5_init_context(&context); 2541 if (status) { 2542 createError(error, status, CFSTR("can't create kerberos context")); 2543 return false; 2544 } 2545 2546 datarecord = copyDataRecord(node, record, NULL, NULL, NULL, &is_server, error); 2547 if (datarecord == NULL) 2548 goto out; 2549 2550 loadflags |= is_server ? LOAD_SERVER : LOAD_CLIENT; 2551 if (flags & kHeimODAdminLoadAsAppend) 2552 loadflags |= LOAD_SERVER_APPEND; 2553 2554 for (n = 0; n < sizeof(dlkeys) / sizeof(dlkeys[0]); n++) { 2555 CFStringRef key = is_server ? dlkeys[n].serverkey : dlkeys[n].clientkey; 2556 CFTypeRef values; 2557 2558 if ((dlkeys[n].flags & loadflags) != loadflags) 2559 continue; 2560 2561 values = CFDictionaryGetValue(dict, dlkeys[n].dumpkey); 2562 if (values == NULL) 2563 continue; 2564 2565 ret = dlkeys[n].load(context, datarecord, dict, key, values, loadflags, error); 2566 if (!ret) 2567 goto out; 2568 } 2569 2570 ret = ODRecordSynchronize(record, error); 2571 if (!ret) { 2572 if (*error == NULL) 2573 createError(error, 1, CFSTR("Failed to syncronize the record")); 2574 goto out; 2575 } 2576 2577 ret = true; 2578 2579 out: 2580 if (context) 2581 krb5_free_context(context); 2582 if (datarecord) 2583 CFRelease(datarecord); 2584 2585 return ret; 2586} 2587 2588CFDictionaryRef 2589HeimODDumpHdbEntry(struct hdb_entry *entry, CFErrorRef *error) 2590{ 2591 CFMutableDictionaryRef dict = NULL; 2592 krb5_context context; 2593 krb5_error_code ret; 2594 size_t n; 2595 2596 if (error == NULL) 2597 abort(); 2598 2599 ret = krb5_init_context(&context); 2600 if (ret) { 2601 createError(error, 1, CFSTR("can't create kerberos context")); 2602 goto out; 2603 } 2604 2605 dict = CFDictionaryCreateMutable(NULL, 0, 2606 &kCFTypeDictionaryKeyCallBacks, 2607 &kCFTypeDictionaryValueCallBacks); 2608 if (dict == NULL) { 2609 createError(error, ENOMEM, CFSTR("out of memory")); 2610 goto out; 2611 } 2612 2613 CFDictionaryAddValue(dict, CFSTR("version"), CFSTR("1")); 2614 2615 for (n = 0; n < sizeof(dlkeys) / sizeof(dlkeys[0]); n++) { 2616 2617 if ((dlkeys[n].flags & DUMP) == 0) 2618 continue; 2619 2620 CFArrayRef v = dlkeys[n].dump_entry(context, entry, error); 2621 if (v) { 2622 CFDictionaryAddValue(dict, dlkeys[n].dumpkey, v); 2623 CFRelease(v); 2624 } 2625 2626 if (*error) { 2627 CFRelease(dict); dict = NULL; 2628 break; 2629 } 2630 } 2631 2632 out: 2633 krb5_free_context(context); 2634 2635 return dict; 2636} 2637