/ lib / akadmin / admin.c
admin.c
   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  
  59  static ODNodeRef node;
  60  
  61  static ODRecordRef
  62  find_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  
  91  int
  92  principal_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  
 127  int
 128  principal_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  
 178  int
 179  password_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  
 254  static int
 255  principal_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  
 287  int
 288  principal_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  
 295  int
 296  principal_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  
 303  int
 304  principal_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  
 339  int
 340  principal_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;
 372  out:
 373      if (keys)
 374  	CFRelease(keys);
 375      if (record)
 376  	CFRelease(record);
 377      return error;
 378  }
 379  
 380  int
 381  print_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  
 444  int
 445  principal_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  
 452  int
 453  principal_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  
 489  int
 490  principal_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  
 497  int
 498  alias_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  
 505  int
 506  alias_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  
 513  int
 514  appleid_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  
 521  int
 522  appleid_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  
 529  int
 530  appleid_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  
 537  int
 538  appleid_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  
 546  int
 547  principal_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  
 579  int
 580  default_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  
 599  int
 600  dump(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  
 666  int
 667  load(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  
 727  int
 728  keyset(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  
 829  int
 830  srp_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  
 878  static void
 879  set_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  
 891  static void
 892  create_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  
1066  static char *
1067  sha1_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  
1083  static CFStringRef
1084  realmOfIdentity(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  
1110  static CFStringRef kRealName = CFSTR("dsAttrTypeStandard:RealName");
1111  static CFStringRef kKerberosKDC = CFSTR("KerberosKDC");
1112  
1113  static int
1114  verifyRecord(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  
1144  static int
1145  verifyKerberosKDCRecord(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  
1212  static void
1213  remove_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  
1253  int
1254  setup_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  
1315  int
1316  help(void *opt, int argc, char **argv)
1317  {
1318      sl_slc_help(commands, argc, argv);
1319      return 0;
1320  }
1321  
1322  static int help_flag;
1323  static int version_flag;
1324  static char *local_path_string;
1325  
1326  static 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  
1332  static int num_args = sizeof(args) / sizeof(args[0]);
1333  
1334  static void
1335  usage(int ret)
1336  {
1337      arg_printusage (args, num_args, NULL, "node-path command ...");
1338      exit (ret);
1339  }
1340  
1341  int
1342  main(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  }