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 <Security/Security.h> 45#include <CommonCrypto/CommonDigest.h> 46#include <Heimdal/krb5.h> 47#include <hdb.h> 48 49 50#include <roken.h> 51#include <getarg.h> 52#include <sl.h> 53#include <err.h> 54#include <hex.h> 55#include "base64.h" 56 57#include "hod-commands.h" 58 59static ODNodeRef node; 60 61static ODRecordRef 62find_record(const char *entry) 63{ 64 CFStringRef recordname; 65 ODRecordRef record; 66 ODRecordType type = kODRecordTypeUsers; 67 68 if (strncmp(entry, "/Users/", 7) == 0) { 69 type = kODRecordTypeUsers; 70 entry += 7; 71 } else if (strncmp(entry, "/Computers/", 11) == 0) { 72 type = kODRecordTypeComputers; 73 entry += 11; 74 } else if (entry[0] == '/') { 75 warnx("unknown type for entry: %s", entry); 76 return NULL; 77 } 78 79 recordname = CFStringCreateWithCString(kCFAllocatorDefault, entry, kCFStringEncodingUTF8); 80 if (recordname == NULL) 81 return NULL; 82 83 record = ODNodeCopyRecord(node, type, recordname, NULL, NULL); 84 if (record == NULL) 85 warnx("ODNodeCopyRecord failed"); 86 CFRelease(recordname); 87 return record; 88} 89 90 91int 92principal_create(void *opt, int argc, char **argv) 93{ 94 CFStringRef principal = NULL; 95 ODRecordRef record = NULL; 96 int error = 1; 97 98 if (argc < 1) 99 errx(1, "missing record"); 100 101 record = find_record(argv[0]); 102 if (record == NULL) 103 goto out; 104 105 if (argc > 1) { 106 principal = CFStringCreateWithCString(kCFAllocatorDefault, argv[1], 107 kCFStringEncodingUTF8); 108 if (principal == NULL) 109 goto out; 110 } 111 112 if (HeimODCreatePrincipalData(node, record, NULL, principal, NULL)) { 113 warnx("HeimODCreatePrincipalData failed"); 114 goto out; 115 } 116 117 error = 0; 118 119 out: 120 if (record) 121 CFRelease(record); 122 if (principal) 123 CFRelease(principal); 124 return error; 125} 126 127int 128principal_add_cert(struct principal_add_cert_options *opt, int argc, char **argv) 129{ 130 SecCertificateRef cert = NULL; 131 OSStatus status = 0; 132 ODRecordRef record; 133 134 record = find_record(argv[0]); 135 if (record == NULL) 136 goto out; 137 138 if (opt->use_default_sharing_identity_flag) { 139 SecIdentityRef ref; 140 141 ref = SecIdentityCopyPreferred(CFSTR("com.apple.system.DefaultSharingIdentity"), NULL, NULL); 142 if (ref == NULL) { 143 status = 1; 144 goto out; 145 } 146 147 status = SecIdentityCopyCertificate(ref, &cert); 148 CFRelease(ref); 149 if (status) 150 goto out; 151 } else { 152 printf("don't know how to get certificate"); 153 status = 1; 154 goto out; 155 } 156 157 CFErrorRef error = NULL; 158 159 status = HeimODAddCertificate(node, record, cert, &error); 160 if (error) { 161 CFShow(error); 162 CFRelease(error); 163 } 164 if (status) { 165 warnx("HeimODAddCertificate failed"); 166 goto out; 167 } 168 169 out: 170 if (record) 171 CFRelease(record); 172 if (cert) 173 CFRelease(cert); 174 175 return (int)status; 176} 177 178int 179password_command(struct password_options *opt, int argc, char **argv) 180{ 181 CFMutableArrayRef enctypes = NULL; 182 ODRecordRef record = NULL; 183 CFErrorRef cferror = NULL; 184 CFStringRef principal = NULL, password = NULL; 185 unsigned long flags = 0; 186 int error = 1; 187 int i; 188 189 if (opt->encryption_types_strings.num_strings) { 190 191 enctypes = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 192 if (enctypes == NULL) { 193 warn("CFArrayCreateMutable"); 194 return 1; 195 } 196 197 for (i = 0; i < opt->encryption_types_strings.num_strings; i++) { 198 CFStringRef el; 199 200 el = CFStringCreateWithCString(kCFAllocatorDefault, opt->encryption_types_strings.strings[i], kCFStringEncodingUTF8); 201 if (el == NULL) { 202 warn("CFStringCreateWithCString"); 203 CFRelease(enctypes); 204 return 1; 205 } 206 CFArrayAppendValue(enctypes, el); 207 CFRelease(el); 208 } 209 } 210 211 if (opt->append_flag) 212 flags |= kHeimODAdminSetKeysAppendKey; 213 214 record = find_record(argv[0]); 215 if (record == NULL) 216 goto out; 217 218 password = CFStringCreateWithCString(kCFAllocatorDefault, argv[1], kCFStringEncodingUTF8); 219 if (password == NULL) 220 goto out; 221 222 if (argc > 2) { 223 principal = CFStringCreateWithCString(kCFAllocatorDefault, argv[2], kCFStringEncodingUTF8); 224 if (principal == NULL) 225 goto out; 226 } 227 228 error = HeimODSetKeys(node, record, principal, enctypes, password, flags, &cferror); 229 if (cferror) { 230 CFRelease(cferror); 231 cferror = NULL; 232 } 233 234 if (!ODRecordSynchronize(record, NULL)) { 235 warnx("ODRecordSynchronize failed"); 236 goto out; 237 } 238 239 error = 0; 240 241 out: 242 if (record) 243 CFRelease(record); 244 if (enctypes) 245 CFRelease(enctypes); 246 if (principal) 247 CFRelease(principal); 248 if (password) 249 CFRelease(password); 250 251 return error; 252} 253 254static int 255principal_opflags(int argc, char **argv, void (^op)(ODNodeRef node, ODRecordRef record, CFStringRef flag)) 256{ 257 ODRecordRef record = NULL; 258 int error = 1; 259 260 record = find_record(argv[0]); 261 if (record == NULL) 262 goto out; 263 264 argv++; 265 argc--; 266 267 while(argc) { 268 CFStringRef flag = CFStringCreateWithCString(kCFAllocatorDefault, argv[0], kCFStringEncodingUTF8); 269 if (flag == NULL) 270 goto out; 271 272 op(node, record, flag); 273 274 CFRelease(flag); 275 276 argc--; 277 argv++; 278 } 279 280 error = 0; 281 out: 282 if (record) 283 CFRelease(record); 284 return error; 285} 286 287int 288principal_clearflags(void *opt, int argc, char **argv) 289{ 290 return principal_opflags(argc, argv, ^(ODNodeRef lnode, ODRecordRef record, CFStringRef flag) { 291 HeimODClearKerberosFlags(lnode, record, flag, NULL); 292 }); 293} 294 295int 296principal_setflags(void *opt, int argc, char **argv) 297{ 298 return principal_opflags(argc, argv, ^(ODNodeRef lnode, ODRecordRef record, CFStringRef flag) { 299 HeimODSetKerberosFlags(lnode, record, flag, NULL); 300 }); 301} 302 303int 304principal_getflags(void *opt, int argc, char **argv) 305{ 306 ODRecordRef record = NULL; 307 CFArrayRef flags = NULL; 308 int error = 1; 309 CFIndex n; 310 311 record = find_record(argv[0]); 312 if (record == NULL) { 313 warnx("failed to find %s", argv[0]); 314 goto out; 315 } 316 317 flags = HeimODCopyKerberosFlags(node, record, NULL); 318 if (flags == NULL) { 319 warnx("no flags for %s", argv[0]); 320 goto out; 321 } 322 323 CFShow(CFSTR("Flags:")); 324 325 for (n = 0; n < CFArrayGetCount(flags); n++) { 326 CFStringRef s = CFArrayGetValueAtIndex(flags, n); 327 CFShow(s); 328 } 329 330 error = 0; 331 out: 332 if (flags) 333 CFRelease(flags); 334 if (record) 335 CFRelease(record); 336 return error; 337} 338 339int 340principal_get_keyinfo(void *opt, int argc, char **argv) 341{ 342 ODRecordRef record = NULL; 343 CFArrayRef keys = NULL; 344 CFIndex n, count; 345 int error = 1; 346 347 record = find_record(argv[0]); 348 if (record == NULL) { 349 warnx("failed to find %s", argv[0]); 350 goto out; 351 } 352 353 keys = ODRecordCopyValues(record, CFSTR("dsAttrTypeNative:KerberosKeys"), NULL); 354 if (keys == NULL) { 355 printf("no keys available\n"); 356 goto out; 357 } 358 359 count = CFArrayGetCount(keys); 360 for (n = 0; n < count; n++) { 361 CFDataRef el = CFArrayGetValueAtIndex(keys, n); 362 if (el == NULL || CFGetTypeID(el) != CFDataGetTypeID()) 363 continue; 364 CFStringRef str = HeimODKeysetToString(el, NULL); 365 if (str) { 366 CFShow(str); 367 CFRelease(str); 368 } 369 } 370 371 error = 0; 372out: 373 if (keys) 374 CFRelease(keys); 375 if (record) 376 CFRelease(record); 377 return error; 378} 379 380int 381print_keyset(void *opt, int argc, char **argv) 382{ 383 char *instr = NULL; 384 int ret; 385 void *data; 386 CFDataRef el; 387 CFStringRef str; 388 389 if (argc == 0) { 390 char buf[1024]; 391 392 while (fgets(buf, sizeof(buf), stdin) != NULL) { 393 buf[strcspn(buf, "\r\n")] = '\0'; 394 395 size_t n = 0; 396 397 if (strncmp(&buf[n], "draft-krbKeySet::", 17) == 0) 398 n += 17; 399 400 n += strspn(&buf[n], "\t "); 401 402 if (strlen(&buf[n]) == 0) 403 break; 404 405 char *str2; 406 asprintf(&str2, "%s%s", instr ? instr : "", &buf[n]); 407 free(instr); 408 instr = str2; 409 } 410 if (instr == NULL) { 411 printf("failed to parse line from stdin\n"); 412 return 1; 413 } 414 } else { 415 instr = strdup(argv[0]); 416 } 417 418 data = malloc(strlen(instr)); 419 if (data == NULL) 420 errx(1, "malloc"); 421 422 ret = base64_decode(instr, data); 423 if (ret < 0) { 424 printf("failed to parse as base64: %s\n", argv[0]); 425 return 1; 426 } 427 428 free(instr); 429 430 el = CFDataCreate(NULL, data, ret); 431 432 str = HeimODKeysetToString(el, NULL); 433 if (str) { 434 CFShow(str); 435 CFRelease(str); 436 } else { 437 printf("failed to print keyset\n"); 438 } 439 CFRelease(el); 440 441 return str ? 0 : 1; 442} 443 444int 445principal_setacl(void *opt, int argc, char **argv) 446{ 447 return principal_opflags(argc, argv, ^(ODNodeRef lnode, ODRecordRef record, CFStringRef flag) { 448 HeimODSetACL(lnode, record, flag, NULL); 449 }); 450} 451 452int 453principal_getacl(void *opt, int argc, char **argv) 454{ 455 ODRecordRef record = NULL; 456 CFArrayRef acl = NULL; 457 int error = 1; 458 CFIndex n; 459 460 record = find_record(argv[0]); 461 if (record == NULL) { 462 warnx("failed to find %s", argv[0]); 463 goto out; 464 } 465 466 acl = HeimODCopyACL(node, record, NULL); 467 if (acl == NULL) { 468 warnx("no explicit ACL for %s", argv[0]); 469 goto out; 470 } 471 472 CFShow(CFSTR("ACL:")); 473 474 for (n = 0; n < CFArrayGetCount(acl); n++) { 475 CFStringRef s = CFArrayGetValueAtIndex(acl, n); 476 CFShow(s); 477 } 478 479 error = 0; 480 out: 481 if (acl) 482 CFRelease(acl); 483 if (record) 484 CFRelease(record); 485 return error; 486 487} 488 489int 490principal_clearacl(void *opt, int argc, char **argv) 491{ 492 return principal_opflags(argc, argv, ^(ODNodeRef lnode, ODRecordRef record, CFStringRef flag) { 493 HeimODClearACL(lnode, record, flag, NULL); 494 }); 495} 496 497int 498alias_add(void *opt, int argc, char **argv) 499{ 500 return principal_opflags(argc, argv, ^(ODNodeRef lnode, ODRecordRef record, CFStringRef flag) { 501 HeimODAddServerAlias(lnode, record, flag, NULL); 502 }); 503} 504 505int 506alias_remove(void *opt, int argc, char **argv) 507{ 508 return principal_opflags(argc, argv, ^(ODNodeRef lnode, ODRecordRef record, CFStringRef flag) { 509 HeimODRemoveServerAlias(lnode, record, flag, NULL); 510 }); 511} 512 513int 514appleid_alias_add(void *opt, int argc, char **argv) 515{ 516 return principal_opflags(argc, argv, ^(ODNodeRef lnode, ODRecordRef record, CFStringRef flag) { 517 HeimODAddAppleIDAlias(lnode, record, flag, NULL); 518 }); 519} 520 521int 522appleid_alias_remove(void *opt, int argc, char **argv) 523{ 524 return principal_opflags(argc, argv, ^(ODNodeRef lnode, ODRecordRef record, CFStringRef flag) { 525 HeimODRemoveAppleIDAlias(lnode, record, flag, NULL); 526 }); 527} 528 529int 530appleid_cert_add(void *opt, int argc, char **argv) 531{ 532 return principal_opflags(argc, argv, ^(ODNodeRef lnode, ODRecordRef record, CFStringRef flag) { 533 HeimODAddCertificateSubjectAndTrustAnchor(lnode, record, flag, CFSTR("CN=Apple Root CA,OU=Apple Certification Authority,O=Apple Inc.,C=US"), NULL); 534 }); 535} 536 537int 538appleid_cert_remove(void *opt, int argc, char **argv) 539{ 540 return principal_opflags(argc, argv, ^(ODNodeRef lnode, ODRecordRef record, CFStringRef flag) { 541 HeimODRemoveAppleIDAlias(lnode, record, flag, NULL); 542 }); 543} 544 545 546int 547principal_delete(void *opt, int argc, char **argv) 548{ 549 CFStringRef principal = NULL; 550 ODRecordRef record = NULL; 551 int error = 1; 552 553 if (argc < 1) 554 errx(1, "missing record"); 555 556 record = find_record(argv[0]); 557 if (record == NULL) 558 goto out; 559 560 if (argc > 1) { 561 principal = CFStringCreateWithCString(kCFAllocatorDefault, argv[1], kCFStringEncodingUTF8); 562 if (principal == NULL) 563 goto out; 564 } 565 566 if (HeimODRemovePrincipalData(node, record, principal, NULL)) 567 goto out; 568 569 error = 0; 570 571 out: 572 if (record) 573 CFRelease(record); 574 if (principal) 575 CFRelease(principal); 576 return error; 577} 578 579int 580default_enctypes(void *opt, int argc, char **argv) 581{ 582 CFArrayRef enctypes = HeimODCopyDefaultEnctypes(NULL); 583 CFIndex n; 584 585 if (enctypes == NULL) 586 errx(1, "malloc"); 587 588 CFShow(CFSTR("Default enctypes:")); 589 590 for (n = 0; n < CFArrayGetCount(enctypes); n++) { 591 CFStringRef s = CFArrayGetValueAtIndex(enctypes, n); 592 CFShow(s); 593 } 594 CFRelease(enctypes); 595 return 0; 596} 597 598 599int 600dump(void *opt, int argc, char **argv) 601{ 602 CFStringRef principal = NULL; 603 CFErrorRef error = NULL; 604 CFDictionaryRef entrydump = NULL; 605 CFDataRef xmldata = NULL; 606 ODRecordRef record; 607 CFStringRef fn = NULL; 608 CFURLRef url = NULL; 609 int ret = 1; 610 611 record = find_record(argv[0]); 612 if (record == NULL) 613 goto out; 614 615 if (argc > 1) { 616 principal = CFStringCreateWithCString(kCFAllocatorDefault, argv[1], kCFStringEncodingUTF8); 617 if (principal == NULL) 618 goto out; 619 } 620 621 entrydump = HeimODDumpRecord(node, record, principal, &error); 622 if (entrydump == NULL) 623 errx(1, "dump failed"); 624 625 fn = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s.plist"), argv[0]); 626 if (fn == NULL) 627 goto out; 628 629 url = CFURLCreateWithFileSystemPath(NULL, fn, kCFURLPOSIXPathStyle, false); 630 if (url == NULL) 631 goto out; 632 633 xmldata = CFPropertyListCreateData(NULL, entrydump, kCFPropertyListXMLFormat_v1_0, 0, NULL); 634 if (xmldata == NULL) 635 goto out; 636 637 CFWriteStreamRef stream = CFWriteStreamCreateWithFile(NULL, url); 638 if (stream == NULL) 639 goto out; 640 641 if (!CFWriteStreamOpen(stream)) { 642 CFRelease(stream); 643 goto out; 644 } 645 646 CFWriteStreamWrite(stream, CFDataGetBytePtr(xmldata), CFDataGetLength(xmldata)); 647 CFWriteStreamClose(stream); 648 CFRelease(stream); 649 650 ret = 0; 651 652 out: 653 if (principal) 654 CFRelease(principal); 655 if (url) 656 CFRelease(url); 657 if (fn) 658 CFRelease(fn); 659 if (xmldata) 660 CFRelease(xmldata); 661 if (entrydump) 662 CFRelease(entrydump); 663 return ret; 664} 665 666int 667load(struct load_options *opt, int argc, char **argv) 668{ 669 CFDictionaryRef entrydump = NULL; 670 CFErrorRef error = NULL; 671 ODRecordRef record; 672 CFStringRef fn = NULL; 673 CFReadStreamRef stream = NULL; 674 CFURLRef url = NULL; 675 char *path = argv[1]; 676 int ret = 1; 677 unsigned long flags = 0; 678 679 if (opt->append_flag) 680 flags |= kHeimODAdminLoadAsAppend; 681 682 record = find_record(argv[0]); 683 if (record == NULL) 684 goto out; 685 686 if (argc > 1) 687 fn = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s"), path); 688 else 689 fn = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s.plist"), argv[0]); 690 if (fn == NULL) 691 goto out; 692 693 url = CFURLCreateWithFileSystemPath(NULL, fn, kCFURLPOSIXPathStyle, false); 694 if (url == NULL) 695 goto out; 696 697 stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, url); 698 if (stream == NULL) 699 goto out; 700 701 if (!CFReadStreamOpen(stream)) 702 goto out; 703 704 entrydump = (CFDictionaryRef)CFPropertyListCreateWithStream(NULL, stream, 0, 0, NULL, &error); 705 if (entrydump == NULL) 706 goto out; 707 708 if (!HeimODLoadRecord(node, record, entrydump, flags, &error)) 709 errx(1, "HeimODLoadRecord failed"); 710 711 ret = 0; 712 out: 713 if (fn) 714 CFRelease(fn); 715 if (url) 716 CFRelease(url); 717 if (stream) 718 CFRelease(stream); 719 if (entrydump) 720 CFRelease(entrydump); 721 if (record) 722 CFRelease(record); 723 return ret; 724} 725 726 727int 728keyset(struct keyset_options *opt, int argc, char **argv) 729{ 730 const char *principalstr = argv[0], *passwordstr = argv[1]; 731 CFMutableArrayRef prevKeys = NULL, enctypes = NULL; 732 CFArrayRef keys; 733 CFStringRef principal = NULL, password = NULL; 734 CFIndex count, n; 735 unsigned long flags = 0; 736 737 printf("principal: %s password: %s\n", principalstr, passwordstr); 738 739 /* copy in old keys */ 740 if (opt->old_keyset_strings.num_strings) { 741 prevKeys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 742 if (prevKeys == NULL) 743 errx(1, "out of memory"); 744 745 for (n = 0; n < opt->old_keyset_strings.num_strings; n++) { 746 size_t len = strlen(opt->old_keyset_strings.strings[n]); 747 void *data = malloc(len); 748 ssize_t ret; 749 ret = rk_hex_decode(opt->old_keyset_strings.strings[n], data, len); 750 if (ret < 0) 751 errx(1, "failed to parse as hex: %s", opt->old_keyset_strings.strings[n]); 752 CFDataRef el = CFDataCreate(NULL, data, ret); 753 if (el == NULL) 754 errx(1, "out of memory"); 755 free(data); 756 CFArrayAppendValue(prevKeys, el); 757 CFRelease(el); 758 } 759 } 760 761 if (opt->enctype_strings.num_strings) { 762 763 enctypes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 764 if (enctypes == NULL) 765 errx(1, "out of memory"); 766 767 for (n = 0; n < opt->enctype_strings.num_strings; n++) { 768 CFStringRef str = CFStringCreateWithCString(NULL, opt->enctype_strings.strings[n], kCFStringEncodingUTF8); 769 if (str == NULL) 770 errx(1, "out of memory"); 771 CFArrayAppendValue(enctypes, str); 772 CFRelease(str); 773 } 774 } 775 776 if (opt->delete_flag) { 777 flags |= kHeimODAdminDeleteEnctypes; 778 } else { 779 if (argc < 2) 780 errx(1, "missing principal and password arguments that is needed when adding new keys"); 781 782 principal = CFStringCreateWithCString(NULL, principalstr, kCFStringEncodingUTF8); 783 password = CFStringCreateWithCString(NULL, passwordstr, kCFStringEncodingUTF8); 784 if (principal == NULL || password == NULL) 785 errx(1, "out of memory"); 786 787 if (opt->append_flag) 788 flags |= kHeimODAdminAppendKeySet; 789 } 790 791 792 keys = HeimODModifyKeys(prevKeys, principal, enctypes, password, flags, NULL); 793 if (keys == NULL) 794 errx(1, "HeimODModifyKeys failed"); 795 796 count = CFArrayGetCount(keys); 797 for (n = 0; n < count; n++) { 798 CFStringRef value; 799 char *str; 800 801 CFDataRef element = CFArrayGetValueAtIndex(keys, n); 802 if (CFGetTypeID(element) != CFDataGetTypeID()) 803 continue; 804 805 value = HeimODKeysetToString(element, NULL); 806 if (value == NULL) 807 errx(1, "HeimODKeysetToString failed"); 808 809 CFShow(value); 810 CFRelease(value); 811 812 hex_encode(CFDataGetBytePtr(element), CFDataGetLength(element), &str); 813 printf("raw: %s\n", str); 814 } 815 816 if (enctypes) 817 CFRelease(enctypes); 818 if (principal) 819 CFRelease(principal); 820 if (password) 821 CFRelease(password); 822 if (prevKeys) 823 CFRelease(prevKeys); 824 CFRelease(keys); 825 826 return 0; 827} 828 829int 830srp_verifier(struct srp_verifier_options *opt, int argc, char **argv) 831{ 832 const char *principalstr = argv[0], *passwordstr = argv[1]; 833 CFMutableArrayRef types = NULL; 834 CFArrayRef verifiers = NULL; 835 CFStringRef principal = NULL, password = NULL; 836 ODRecordRef record; 837 CFIndex n; 838 839 record = find_record(principalstr); 840 if (record == NULL) 841 errx(1, "failed to find %s", principalstr); 842 843 if (opt->type_strings.num_strings) { 844 845 types = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 846 if (types == NULL) 847 errx(1, "out of memory"); 848 849 for (n = 0; n < opt->type_strings.num_strings; n++) { 850 CFStringRef str = CFStringCreateWithCString(NULL, opt->type_strings.strings[n], kCFStringEncodingUTF8); 851 if (str == NULL) 852 errx(1, "out of memory"); 853 CFArrayAppendValue(types, str); 854 CFRelease(str); 855 } 856 } 857 858 principal = CFStringCreateWithCString(NULL, principalstr, kCFStringEncodingUTF8); 859 password = CFStringCreateWithCString(NULL, passwordstr, kCFStringEncodingUTF8); 860 if (principal == NULL || password == NULL) 861 errx(1, "out of memory"); 862 863 if (!HeimODSetVerifiers(node, record, principal, types, password, 0, NULL)) 864 errx(1, "HeimODSetVerifiers"); 865 866 if (types) 867 CFRelease(types); 868 if (principal) 869 CFRelease(principal); 870 if (password) 871 CFRelease(password); 872 if (verifiers) 873 CFRelease(verifiers); 874 875 return 0; 876} 877 878static void 879set_if_lkdc_or_empty(CFStringRef key, CFStringRef value, CFStringRef prop) 880{ 881 CFStringRef orig = CFPreferencesCopyValue(key, prop, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 882 883 if (orig == NULL || 884 (CFGetTypeID(orig) == CFStringGetTypeID() && CFStringFindWithOptions(orig, CFSTR("@LKDC:SHA"), CFRangeMake(0, CFStringGetLength(orig)), 0, NULL))) 885 CFPreferencesSetValue(key, value, prop, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 886 887 if (orig) 888 CFRelease(orig); 889} 890 891static void 892create_random_server_principal(krb5_context context, 893 const char *record_name, 894 CFStringRef realm, 895 int krbtgt, 896 int verbose, 897 int add_to_keytab) 898{ 899 ODRecordRef record = NULL; 900 CFMutableArrayRef flags; 901 char *realmstr = NULL; 902 CFDictionaryRef dump = NULL; 903 904 if (verbose) printf("creating %s\n", record_name); 905 906 record = find_record(record_name); 907 if (record == NULL) { 908 warnx("failed to find user %s", record_name); 909 goto out; 910 } 911 912 if (HeimODCreatePrincipalData(node, record, NULL, NULL, NULL)) { 913 warnx("HeimODCreatePrincipalData failed for %s", record_name); 914 goto out; 915 } 916 917 if (verbose) printf("\tsetting flags\n"); 918 919 flags = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 920 if (flags == NULL) { 921 warnx("out of memory"); 922 goto out; 923 } 924#if 0 925 if (krbtgt) 926 CFArrayAppendValue(flags, kPrincipalFlagInitial); 927#endif 928 CFArrayAppendValue(flags, kPrincipalFlagForwardable); 929 CFArrayAppendValue(flags, kPrincipalFlagProxyable); 930 CFArrayAppendValue(flags, kPrincipalFlagRenewable); 931 CFArrayAppendValue(flags, kPrincipalFlagServer); 932 933 HeimODSetKerberosFlags(node, record, flags, NULL); 934 CFRelease(flags); 935 936 if (verbose) printf("\tsetting (random) keys\n"); 937 HeimODSetKeys(node, record, NULL, NULL, NULL, 0, NULL); 938 if (verbose) printf("\tmaking entry valid\n"); 939 HeimODClearKerberosFlags(node, record, kPrincipalFlagInvalid, NULL); 940 941 if (!ODRecordSynchronize(record, NULL)) 942 warnx("failed to save %s", record_name); 943 else if (verbose) 944 printf("\tsaved!\n"); 945 946 if (add_to_keytab) { 947 char *users[] = { "afpserver", "cifs", "vnc", "host" }; 948 krb5_principal principals[sizeof(users)/sizeof(users[0])] = { 0 }; 949 krb5_keytab_entry entry; 950 krb5_error_code ret; 951 krb5_keytab keytab; 952 size_t n, m, o, count; 953 CFErrorRef error = NULL; 954 955 memset(&entry, 0, sizeof(entry)); 956 957 958 realmstr = rk_cfstring2cstring(realm); 959 if (realmstr == NULL) 960 errx(1, "failed gettng realm"); 961 962 963 for (n = 0; n < sizeof(users)/sizeof(users[0]); n++) 964 krb5_make_principal(context, &principals[n], realmstr, users[n], realmstr, NULL); 965 966 if (verbose) 967 printf("\tstoring entry to keytab!\n"); 968 969 ret = krb5_kt_default (context, &keytab); 970 if (ret) { 971 krb5_warn(context, ret, "krb5_kt_default"); 972 goto out; 973 } 974 975 dump = HeimODDumpRecord(node, record, NULL, &error); 976 if (dump == NULL) { 977 warnx("failed dump record %s", record_name); 978 if (error) 979 CFRelease(error); 980 goto out; 981 } 982 983 CFArrayRef keys = CFDictionaryGetValue(dump, CFSTR("KerberosKeys")); 984 if (keys == NULL) { 985 warnx("failed to get keys for record %s", record_name); 986 goto out; 987 } 988 989 entry.timestamp = (uint32_t)time(NULL); 990 entry.flags = 0; 991 992 count = CFArrayGetCount(keys); 993 for (m = 0; m < count; m++) { 994 hdb_keyset_aapl keyset; 995 996 CFDataRef d = CFArrayGetValueAtIndex(keys, m); 997 998 ret = decode_hdb_keyset_aapl(CFDataGetBytePtr(d), CFDataGetLength(d), &keyset, NULL); 999 if (ret) 1000 errx(1, "failed to decode kerberos key"); 1001 1002 if (keyset.principal) { 1003 free_hdb_keyset_aapl(&keyset); 1004 continue; 1005 } 1006 1007 entry.vno = keyset.kvno; 1008 1009 for (o = 0; o < keyset.keys.len; o++) { 1010 /* skip keys encrypted with a master key */ 1011 if (keyset.keys.val[o].mkvno) 1012 continue; 1013 1014 entry.keyblock = keyset.keys.val[o].key; 1015 1016 if (verbose) 1017 printf("\t\tentries for vno %d - enctype: %d\n", 1018 entry.vno, entry.keyblock.keytype); 1019 1020 for (n = 0; n < sizeof(principals)/sizeof(principals[0]); n++) { 1021 entry.principal = principals[n]; 1022 ret = krb5_kt_add_entry(context, keytab, &entry); 1023 if (ret) 1024 krb5_warn(context, ret, "failed to store keytab entry"); 1025 } 1026 } 1027 1028 free_hdb_keyset_aapl(&keyset); 1029 1030 } 1031 1032 1033 ret = krb5_kt_close(context, keytab); 1034 if (ret) { 1035 krb5_warn(context, ret, "krb5_kt_close"); 1036 goto out; 1037 } 1038 1039 for (n = 0; n < sizeof(users)/sizeof(users[0]); n++) 1040 krb5_free_principal(context, principals[n]); 1041 1042 if (verbose) printf("\tsetting up afp/smb configuration\n"); 1043 1044 CFStringRef afpconf = CFStringCreateWithFormat(NULL, NULL, CFSTR("afpserver/%@@%@"), realm, realm); 1045 1046 set_if_lkdc_or_empty(CFSTR("kerberosPrincipal"), afpconf, 1047 CFSTR("/Library/Preferences/com.apple.AppleFileServer")); 1048 CFRelease(afpconf); 1049 1050 set_if_lkdc_or_empty(CFSTR("LocalKerberosRealm"), realm, 1051 CFSTR("/Library/Preferences/SystemConfiguration/com.apple.smb.server")); 1052 } 1053 if (verbose) 1054 printf("\tdone %s\n", record_name); 1055 1056 1057 out: 1058 if (dump) 1059 CFRelease(dump); 1060 if (realmstr) 1061 free(realmstr); 1062 if (record) 1063 CFRelease(record); 1064} 1065 1066static char * 1067sha1_hash(const void *data, size_t len) 1068{ 1069 char *outstr, *cpOut; 1070 unsigned char digest[CC_SHA1_DIGEST_LENGTH]; 1071 unsigned i; 1072 1073 CC_SHA1(data, (CC_LONG)len, digest); 1074 cpOut = outstr = (char *)malloc((2 * CC_SHA1_DIGEST_LENGTH) + 1); 1075 if (outstr == NULL) 1076 return NULL; 1077 for(i = 0; i < sizeof(digest); i++, cpOut += 2) 1078 sprintf(cpOut, "%02X", (unsigned)digest[i]); 1079 *cpOut = '\0'; 1080 return outstr; 1081} 1082 1083static CFStringRef 1084realmOfIdentity(SecIdentityRef identity) 1085{ 1086 SecCertificateRef cert; 1087 CFStringRef realm; 1088 char *cert_hash; 1089 CFDataRef data; 1090 1091 if (SecIdentityCopyCertificate(identity, &cert) != noErr) 1092 errx(1, "failed to turn identity into certificate"); 1093 1094 data = SecCertificateCopyData(cert); 1095 CFRelease(cert); 1096 if (data == NULL) 1097 errx(1, "SecCertificateCopyData"); 1098 1099 cert_hash = sha1_hash(CFDataGetBytePtr(data), CFDataGetLength(data)); 1100 CFRelease(data); 1101 if (cert_hash == NULL) 1102 errx(1, "error obtaining cert hash"); 1103 1104 realm = CFStringCreateWithFormat(NULL, NULL, CFSTR("LKDC:SHA1.%s"), cert_hash); 1105 free(cert_hash); 1106 1107 return realm; 1108} 1109 1110static CFStringRef kRealName = CFSTR("dsAttrTypeStandard:RealName"); 1111static CFStringRef kKerberosKDC = CFSTR("KerberosKDC"); 1112 1113static int 1114verifyRecord(ODRecordRef kdcConf, CFStringRef realm) 1115{ 1116 CFArrayRef data = NULL; 1117 CFStringRef storedRealm = NULL; 1118 1119 data = ODRecordCopyValues(kdcConf, kRealName, NULL); 1120 if (data == NULL) 1121 return 0; 1122 1123 if (CFArrayGetCount(data) != 1) { 1124 CFRelease(data); 1125 return 0; 1126 } 1127 storedRealm = (CFStringRef)CFArrayGetValueAtIndex(data, 0); 1128 if (storedRealm == NULL) { 1129 CFRelease(data); 1130 return 0; 1131 } 1132 1133 if (!CFEqual(storedRealm, realm)) { 1134 //warnx("certificate %s not same as realm in configuraton %s", storedRealm, realm); 1135 CFRelease(data); 1136 return 0; 1137 } 1138 1139 CFRelease(data); 1140 return 1; 1141} 1142 1143 1144static int 1145verifyKerberosKDCRecord(CFStringRef realm, int force_update, int verbose) 1146{ 1147 ODRecordRef kdcConf = NULL; 1148 int error = 1; 1149 CFArrayRef array = NULL; 1150 1151 if (verbose) printf("try to find configuration node\n"); 1152 1153 /* check that Configuration/KerberosKDC exists and have the right value */ 1154 kdcConf = ODNodeCopyRecord(node, kODRecordTypeConfiguration, kKerberosKDC, NULL, NULL); 1155 if (kdcConf == NULL) { 1156 CFDictionaryRef attributes = NULL; 1157 1158 if (verbose) printf("\tnot found, adding record\n"); 1159 1160 array = CFArrayCreate(NULL, (const void **)&realm, 1, &kCFTypeArrayCallBacks); 1161 1162 attributes = CFDictionaryCreate(NULL, 1163 (const void **)&kRealName, 1164 (const void **)&array, 1165 1, 1166 &kCFTypeDictionaryKeyCallBacks, 1167 &kCFTypeDictionaryValueCallBacks); 1168 1169 kdcConf = ODNodeCreateRecord(node, 1170 kODRecordTypeConfiguration, 1171 kKerberosKDC, 1172 attributes, 1173 NULL); 1174 if (kdcConf == NULL) 1175 errx(1, "failed to create KerberosKDC node"); 1176 1177 if (!ODRecordSynchronize(kdcConf, NULL)) 1178 warnx("ODRecordSynchronize failed"); 1179 else 1180 error = 0; 1181 1182 CFRelease(attributes); 1183 1184 } else if (force_update || !verifyRecord(kdcConf, realm)) { 1185 1186 if (verbose) printf("\tfound, but wrong/missing realm\n"); 1187 1188 array = CFArrayCreate(NULL, (const void **)&realm, 1, &kCFTypeArrayCallBacks); 1189 1190 bool r = ODRecordSetValue(kdcConf, kRealName, array, NULL); 1191 if (!r) 1192 errx(1, "ODRecordSetValue"); 1193 1194 if (!ODRecordSynchronize(kdcConf, NULL)) 1195 warnx("ODRecordSynchronize failed"); 1196 else 1197 error = 0; 1198 1199 } else { 1200 if (verbose) printf("\tfound, all ok!\n"); 1201 error = 0; 1202 } 1203 1204 if (array) 1205 CFRelease(array); 1206 if (kdcConf) 1207 CFRelease(kdcConf); 1208 1209 return error; 1210} 1211 1212static void 1213remove_lkdc_keytab_entrys(krb5_context context) 1214{ 1215 krb5_keytab keytab; 1216 krb5_kt_cursor cursor; 1217 krb5_keytab_entry entry; 1218 krb5_error_code ret; 1219 1220 ret = krb5_kt_default (context, &keytab); 1221 if (ret) { 1222 krb5_warn(context, ret, "krb5_kt_default"); 1223 return; 1224 } 1225 1226 ret = krb5_kt_start_seq_get(context, keytab, &cursor); 1227 if (ret) { 1228 krb5_warn(context, ret, "krb5_kt_start_seq_get"); 1229 return; 1230 } 1231 1232 while ((ret = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0){ 1233 if (krb5_principal_is_lkdc(context, entry.principal)) 1234 krb5_kt_remove_entry(context, keytab, &entry); 1235 1236 krb5_kt_free_entry(context, &entry); 1237 } 1238 ret = krb5_kt_end_seq_get(context, keytab, &cursor); 1239 if (ret) { 1240 krb5_warn(context, 1, "krb5_kt_end_seq_get"); 1241 return; 1242 } 1243 1244 ret = krb5_kt_close(context, keytab); 1245 if (ret) { 1246 krb5_warn(context, ret, "krb5_kt_close"); 1247 return; 1248 } 1249} 1250 1251 1252 1253int 1254setup_lkdc(struct setup_lkdc_options *opt, int argc, char **argv) 1255{ 1256 krb5_context context = NULL; 1257 SecIdentityRef kdc = NULL; 1258 CFStringRef realm = NULL; 1259 int error = 1; 1260 1261 if (krb5_init_context (&context) != 0) { 1262 warnx("krb5_context"); 1263 goto out; 1264 } 1265 1266 /* clean out kebtab from LKDC credentials */ 1267 if (opt->keytab_flag) { 1268 if (opt->verbose_flag) 1269 printf("removing LKDC keytab entries\n"); 1270 remove_lkdc_keytab_entrys(context); 1271 } 1272 1273 /* find LKDC domain name */ 1274 if (opt->verbose_flag) 1275 printf("checking for LKDC kdc certificate\n"); 1276 1277 if (opt->kdc_certificate_flag || 1278 SecIdentityCopySystemIdentity(kSecIdentityDomainKerberosKDC, &kdc, NULL) != noErr) { 1279 warnx("failed to find KDC certificate and we can't create one (run sudo certtool /usr/bin/certtool C com.apple.kerberos.kdc u P v x=S)"); 1280 goto out; 1281 } 1282 1283 realm = realmOfIdentity(kdc); 1284 if (realm == NULL) { 1285 warnx("failed to get hash of certificate"); 1286 goto out; 1287 } 1288 1289 if (verifyKerberosKDCRecord(realm, opt->kdc_certificate_flag, opt->verbose_flag)) { 1290 warnx("failed to verify KDC record"); 1291 goto out; 1292 } 1293 1294 /* create users */ 1295 create_random_server_principal(context, "/Users/_krbtgt", realm, 1, opt->verbose_flag, 0); 1296 create_random_server_principal(context, "/Users/_krbfast", realm, 1, opt->verbose_flag, 0); 1297 create_random_server_principal(context, "/Computers/localhost", realm, 0, opt->verbose_flag, 1); 1298 1299 if (opt->verbose_flag) 1300 printf("done\n"); 1301 1302 error = 0; 1303 1304 out: 1305 if (realm) 1306 CFRelease(realm); 1307 if (kdc) 1308 CFRelease(kdc); 1309 1310 krb5_free_context(context); 1311 1312 return error; 1313} 1314 1315int 1316help(void *opt, int argc, char **argv) 1317{ 1318 sl_slc_help(commands, argc, argv); 1319 return 0; 1320} 1321 1322static int help_flag; 1323static int version_flag; 1324static char *local_path_string; 1325 1326static struct getargs args[] = { 1327 { "local-path", 0, arg_string, &local_path_string, "OD Local path string" }, 1328 { "help", 'h', arg_flag, &help_flag }, 1329 { "version", 'v', arg_flag, &version_flag } 1330}; 1331 1332static int num_args = sizeof(args) / sizeof(args[0]); 1333 1334static void 1335usage(int ret) 1336{ 1337 arg_printusage (args, num_args, NULL, "node-path command ..."); 1338 exit (ret); 1339} 1340 1341int 1342main(int argc, char **argv) 1343{ 1344 ODSessionRef session = kODSessionDefault; 1345 int ret, exit_status = 0; 1346 CFStringRef root; 1347 int optidx = 0; 1348 1349 setprogname(argv[0]); 1350 1351 if(getarg(args, num_args, argc, argv, &optidx)) 1352 usage(1); 1353 1354 if (help_flag) 1355 usage (0); 1356 1357 if (version_flag) { 1358 print_version(NULL); 1359 exit(0); 1360 } 1361 1362 argc -= optidx; 1363 argv += optidx; 1364 1365 if (argc < 2) 1366 errx(1, "command missing ODNode to operate on and node"); 1367 1368 if (strcmp(".", argv[0]) == 0) 1369 root = CFSTR("/Local/Default"); 1370 else 1371 root = CFStringCreateWithCString(kCFAllocatorDefault, argv[0], kCFStringEncodingUTF8); 1372 if (root == NULL) 1373 errx(1, "out of memory"); 1374 1375 if (local_path_string) { 1376#ifdef __APPLE_PRIVATE__ 1377 CFMutableDictionaryRef options; 1378 1379 options = CFDictionaryCreateMutable(NULL, 0, 1380 &kCFTypeDictionaryKeyCallBacks, 1381 &kCFTypeDictionaryValueCallBacks); 1382 if (options == NULL) 1383 errx(1, "out of memory"); 1384 1385 CFStringRef local_path = CFStringCreateWithCString(kCFAllocatorDefault, local_path_string, kCFStringEncodingUTF8); 1386 CFDictionaryAddValue(options, kODSessionLocalPath, local_path); 1387 CFRelease(local_path); 1388 1389 session = ODSessionCreate(kCFAllocatorDefault, options, NULL); 1390 CFRelease(options); 1391#else 1392 errx(1, "no supported"); 1393#endif 1394 } 1395 1396 1397 CFErrorRef error; 1398 node = ODNodeCreateWithName(kCFAllocatorDefault, session, root, &error); 1399 1400 if (node == NULL) { 1401 if (error) 1402 CFShow(error); 1403 errx(1, "ODNodeCreateWithName failed"); 1404 } 1405 1406 if (session) 1407 CFRelease(session); 1408 1409 argc -= 1; 1410 argv += 1; 1411 1412 ret = sl_command(commands, argc, argv); 1413 if(ret == -1) { 1414 warnx("unrecognized command: %s", argv[0]); 1415 exit_status = 2; 1416 } else if (ret == -2) 1417 ret = 0; 1418 if (ret != 0) 1419 exit_status = 1; 1420 1421 CFRelease(node); 1422 CFRelease(root); 1423 1424 return exit_status; 1425} 1426