#include "config.h"
#include "HeimODAdmin.h"
#ifdef __APPLE_PRIVATE__
#include <OpenDirectory/OpenDirectoryPriv.h>
#endif
#include <roken.h>
#include <getarg.h>
#include <sl.h>
#include <err.h>
#include <hex.h>
#include "hod-commands.h"
static ODNodeRef node;
static ODRecordRef
find_record(const char *entry)
{
CFStringRef recordname;
ODRecordRef record;
ODRecordType type = kODRecordTypeUsers;
if (strncmp(entry, "/Users/", 7) == 0) {
type = kODRecordTypeUsers;
entry += 7;
} else if (strncmp(entry, "/Computers/", 11) == 0) {
type = kODRecordTypeComputers;
entry += 11;
} else if (entry[0] == '/') {
warnx("unknown type for entry: %s", entry);
return NULL;
}
recordname = CFStringCreateWithCString(kCFAllocatorDefault, entry, kCFStringEncodingUTF8);
if (recordname == NULL)
return NULL;
record = ODNodeCopyRecord(node, type, recordname, NULL, NULL);
if (record == NULL)
warnx("ODNodeCopyRecord failed");
CFRelease(recordname);
return record;
}
int
principal_create(void *opt, int argc, char **argv)
{
CFStringRef principal = NULL;
ODRecordRef record = NULL;
int error = 1;
if (argc < 1)
errx(1, "missing record");
record = find_record(argv[0]);
if (record == NULL)
goto out;
if (argc > 1) {
principal = CFStringCreateWithCString(kCFAllocatorDefault, argv[1],
kCFStringEncodingUTF8);
if (principal == NULL)
goto out;
}
if (HeimODCreatePrincipalData(node, record, NULL, principal, NULL)) {
warnx("HeimODCreatePrincipalData failed");
goto out;
}
error = 0;
out:
if (record)
CFRelease(record);
if (principal)
CFRelease(principal);
return error;
}
int
principal_add_cert(struct principal_add_cert_options *opt, int argc, char **argv)
{
SecCertificateRef cert = NULL;
OSStatus status = 0;
ODRecordRef record;
record = find_record(argv[0]);
if (record == NULL)
goto out;
if (opt->use_default_sharing_identity_flag) {
SecIdentityRef ref;
ref = SecIdentityCopyPreferred(CFSTR("com.apple.system.DefaultSharingIdentity"), NULL, NULL);
if (ref == NULL) {
status = 1;
goto out;
}
status = SecIdentityCopyCertificate(ref, &cert);
CFRelease(ref);
if (status)
goto out;
} else {
printf("don't know how to get certificate");
status = 1;
goto out;
}
CFErrorRef error = NULL;
status = HeimODAddCertificate(node, record, cert, &error);
if (error) {
CFShow(error);
CFRelease(error);
}
if (status) {
warnx("HeimODAddCertificate failed");
goto out;
}
out:
if (record)
CFRelease(record);
if (cert)
CFRelease(cert);
return (int)status;
}
int
password_command(struct password_options *opt, int argc, char **argv)
{
CFMutableArrayRef enctypes = NULL;
ODRecordRef record = NULL;
CFErrorRef cferror = NULL;
CFStringRef principal = NULL, password = NULL;
unsigned long flags = 0;
int error = 1;
size_t i;
if (opt->encryption_types_strings.num_strings) {
enctypes = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
if (enctypes == NULL) {
warn("CFArrayCreateMutable");
return 1;
}
for (i = 0; i < opt->encryption_types_strings.num_strings; i++) {
CFStringRef el;
el = CFStringCreateWithCString(kCFAllocatorDefault, opt->encryption_types_strings.strings[i], kCFStringEncodingUTF8);
if (el == NULL) {
warn("CFStringCreateWithCString");
CFRelease(enctypes);
return 1;
}
CFArrayAppendValue(enctypes, el);
CFRelease(el);
}
}
if (opt->append_flag)
flags |= kHeimODAdminSetKeysAppendKey;
record = find_record(argv[0]);
if (record == NULL)
goto out;
password = CFStringCreateWithCString(kCFAllocatorDefault, argv[1], kCFStringEncodingUTF8);
if (password == NULL)
goto out;
if (argc > 2) {
principal = CFStringCreateWithCString(kCFAllocatorDefault, argv[2], kCFStringEncodingUTF8);
if (principal == NULL)
goto out;
}
error = HeimODSetKeys(node, record, principal, enctypes, password, flags, &cferror);
if (cferror) {
CFRelease(cferror);
cferror = NULL;
}
if (!ODRecordSynchronize(record, NULL)) {
warnx("ODRecordSynchronize failed");
goto out;
}
error = 0;
out:
if (record)
CFRelease(record);
if (enctypes)
CFRelease(enctypes);
if (principal)
CFRelease(principal);
if (password)
CFRelease(password);
return error;
}
static int
principal_opflags(int argc, char **argv, void (^op)(ODNodeRef node, ODRecordRef record, CFStringRef flag))
{
ODRecordRef record = NULL;
int error = 1;
record = find_record(argv[0]);
if (record == NULL)
goto out;
argv++;
argc--;
while(argc) {
CFStringRef flag = CFStringCreateWithCString(kCFAllocatorDefault, argv[0], kCFStringEncodingUTF8);
if (flag == NULL)
goto out;
op(node, record, flag);
CFRelease(flag);
argc--;
argv++;
}
error = 0;
out:
if (record)
CFRelease(record);
return error;
}
int
principal_clearflags(void *opt, int argc, char **argv)
{
return principal_opflags(argc, argv, ^(ODNodeRef lnode, ODRecordRef record, CFStringRef flag) {
HeimODClearKerberosFlags(lnode, record, flag, NULL);
});
}
int
principal_setflags(void *opt, int argc, char **argv)
{
return principal_opflags(argc, argv, ^(ODNodeRef lnode, ODRecordRef record, CFStringRef flag) {
HeimODSetKerberosFlags(lnode, record, flag, NULL);
});
}
int
principal_getflags(void *opt, int argc, char **argv)
{
ODRecordRef record = NULL;
CFArrayRef flags = NULL;
int error = 1;
CFIndex n;
record = find_record(argv[0]);
if (record == NULL) {
warnx("failed to find %s", argv[0]);
goto out;
}
flags = HeimODCopyKerberosFlags(node, record, NULL);
if (flags == NULL) {
warnx("no flags for %s", argv[0]);
goto out;
}
CFShow(CFSTR("Flags:"));
for (n = 0; n < CFArrayGetCount(flags); n++) {
CFStringRef s = CFArrayGetValueAtIndex(flags, n);
CFShow(s);
}
error = 0;
out:
if (flags)
CFRelease(flags);
if (record)
CFRelease(record);
return error;
}
int
principal_get_keyinfo(void *opt, int argc, char **argv)
{
ODRecordRef record = NULL;
CFArrayRef keys = NULL;
CFIndex n, count;
int error = 1;
record = find_record(argv[0]);
if (record == NULL) {
warnx("failed to find %s", argv[0]);
goto out;
}
keys = ODRecordCopyValues(record, CFSTR("dsAttrTypeNative:KerberosKeys"), NULL);
if (keys == NULL) {
printf("no keys available\n");
goto out;
}
count = CFArrayGetCount(keys);
for (n = 0; n < count; n++) {
CFDataRef el = CFArrayGetValueAtIndex(keys, n);
if (el == NULL || CFGetTypeID(el) != CFDataGetTypeID())
continue;
CFStringRef str = HeimODKeysetToString(el, NULL);
if (str) {
CFShow(str);
CFRelease(str);
}
}
error = 0;
out:
if (keys)
CFRelease(keys);
if (record)
CFRelease(record);
return error;
}
int
principal_setacl(void *opt, int argc, char **argv)
{
return principal_opflags(argc, argv, ^(ODNodeRef lnode, ODRecordRef record, CFStringRef flag) {
HeimODSetACL(lnode, record, flag, NULL);
});
}
int
principal_getacl(void *opt, int argc, char **argv)
{
ODRecordRef record = NULL;
CFArrayRef acl = NULL;
int error = 1;
CFIndex n;
record = find_record(argv[0]);
if (record == NULL) {
warnx("failed to find %s", argv[0]);
goto out;
}
acl = HeimODCopyACL(node, record, NULL);
if (acl == NULL) {
warnx("no explicit ACL for %s", argv[0]);
goto out;
}
CFShow(CFSTR("ACL:"));
for (n = 0; n < CFArrayGetCount(acl); n++) {
CFStringRef s = CFArrayGetValueAtIndex(acl, n);
CFShow(s);
}
error = 0;
out:
if (acl)
CFRelease(acl);
if (record)
CFRelease(record);
return error;
}
int
principal_clearacl(void *opt, int argc, char **argv)
{
return principal_opflags(argc, argv, ^(ODNodeRef lnode, ODRecordRef record, CFStringRef flag) {
HeimODClearACL(lnode, record, flag, NULL);
});
}
int
alias_add(void *opt, int argc, char **argv)
{
return principal_opflags(argc, argv, ^(ODNodeRef lnode, ODRecordRef record, CFStringRef flag) {
HeimODAddServerAlias(lnode, record, flag, NULL);
});
}
int
alias_remove(void *opt, int argc, char **argv)
{
return principal_opflags(argc, argv, ^(ODNodeRef lnode, ODRecordRef record, CFStringRef flag) {
HeimODRemoveServerAlias(lnode, record, flag, NULL);
});
}
int
appleid_alias_add(void *opt, int argc, char **argv)
{
return principal_opflags(argc, argv, ^(ODNodeRef lnode, ODRecordRef record, CFStringRef flag) {
HeimODAddAppleIDAlias(lnode, record, flag, NULL);
});
}
int
appleid_alias_remove(void *opt, int argc, char **argv)
{
return principal_opflags(argc, argv, ^(ODNodeRef lnode, ODRecordRef record, CFStringRef flag) {
HeimODRemoveAppleIDAlias(lnode, record, flag, NULL);
});
}
int
appleid_cert_add(void *opt, int argc, char **argv)
{
return principal_opflags(argc, argv, ^(ODNodeRef lnode, ODRecordRef record, CFStringRef flag) {
HeimODAddCertificateSubjectAndTrustAnchor(lnode, record, flag, CFSTR("CN=Apple Root CA,OU=Apple Certification Authority,O=Apple Inc.,C=US"), NULL);
});
}
int
appleid_cert_remove(void *opt, int argc, char **argv)
{
return principal_opflags(argc, argv, ^(ODNodeRef lnode, ODRecordRef record, CFStringRef flag) {
HeimODRemoveAppleIDAlias(lnode, record, flag, NULL);
});
}
int
principal_delete(void *opt, int argc, char **argv)
{
CFStringRef principal = NULL;
ODRecordRef record = NULL;
int error = 1;
if (argc < 1)
errx(1, "missing record");
record = find_record(argv[0]);
if (record == NULL)
goto out;
if (argc > 1) {
principal = CFStringCreateWithCString(kCFAllocatorDefault, argv[1], kCFStringEncodingUTF8);
if (principal == NULL)
goto out;
}
if (HeimODRemovePrincipalData(node, record, principal, NULL))
goto out;
error = 0;
out:
if (record)
CFRelease(record);
if (principal)
CFRelease(principal);
return error;
}
int
default_enctypes(void *opt, int argc, char **argv)
{
CFArrayRef enctypes = HeimODCopyDefaultEnctypes(NULL);
CFIndex n;
if (enctypes == NULL)
errx(1, "malloc");
CFShow(CFSTR("Default enctypes:"));
for (n = 0; n < CFArrayGetCount(enctypes); n++) {
CFStringRef s = CFArrayGetValueAtIndex(enctypes, n);
CFShow(s);
}
CFRelease(enctypes);
return 0;
}
int
dump(void *opt, int argc, char **argv)
{
CFStringRef principal = NULL;
CFErrorRef error = NULL;
CFDictionaryRef entrydump = NULL;
CFDataRef xmldata = NULL;
ODRecordRef record;
CFStringRef fn = NULL;
CFURLRef url = NULL;
int ret = 1;
record = find_record(argv[0]);
if (record == NULL)
goto out;
if (argc > 1) {
principal = CFStringCreateWithCString(kCFAllocatorDefault, argv[1], kCFStringEncodingUTF8);
if (principal == NULL)
goto out;
}
entrydump = HeimODDumpRecord(node, record, principal, &error);
if (entrydump == NULL)
errx(1, "dump failed");
fn = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s.plist"), argv[0]);
if (fn == NULL)
goto out;
url = CFURLCreateWithFileSystemPath(NULL, fn, kCFURLPOSIXPathStyle, false);
if (url == NULL)
goto out;
xmldata = CFPropertyListCreateXMLData(NULL, entrydump);
if (xmldata == NULL)
goto out;
CFURLWriteDataAndPropertiesToResource(url, xmldata, NULL, NULL);
ret = 0;
out:
if (principal)
CFRelease(principal);
if (url)
CFRelease(url);
if (fn)
CFRelease(fn);
if (xmldata)
CFRelease(xmldata);
if (entrydump)
CFRelease(entrydump);
return ret;
}
int
load(struct load_options *opt, int argc, char **argv)
{
CFDictionaryRef entrydump = NULL;
CFErrorRef error = NULL;
ODRecordRef record;
CFStringRef fn = NULL;
CFReadStreamRef stream = NULL;
CFURLRef url = NULL;
char *path = argv[1];
int ret = 1;
unsigned long flags = 0;
if (opt->append_flag)
flags |= kHeimODAdminLoadAsAppend;
record = find_record(argv[0]);
if (record == NULL)
goto out;
if (argc > 1)
fn = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s"), path);
else
fn = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s.plist"), argv[0]);
if (fn == NULL)
goto out;
url = CFURLCreateWithFileSystemPath(NULL, fn, kCFURLPOSIXPathStyle, false);
if (url == NULL)
goto out;
stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
if (stream == NULL)
goto out;
if (!CFReadStreamOpen(stream))
goto out;
entrydump = (CFDictionaryRef)CFPropertyListCreateWithStream(NULL, stream, 0, 0, NULL, &error);
if (entrydump == NULL)
goto out;
if (!HeimODLoadRecord(node, record, entrydump, flags, &error))
errx(1, "HeimODLoadRecord failed");
ret = 0;
out:
if (fn)
CFRelease(fn);
if (url)
CFRelease(url);
if (stream)
CFRelease(stream);
if (entrydump)
CFRelease(entrydump);
if (record)
CFRelease(record);
return ret;
}
int
keyset(struct keyset_options *opt, int argc, char **argv)
{
const char *principalstr = argv[0], *passwordstr = argv[1];
CFMutableArrayRef prevKeys = NULL, enctypes = NULL;
CFArrayRef keys;
CFStringRef principal = NULL, password = NULL;
CFIndex count, n;
unsigned long flags = 0;
printf("principal: %s password: %s\n", principalstr, passwordstr);
if (opt->old_keyset_strings.num_strings) {
prevKeys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (prevKeys == NULL)
errx(1, "out of memory");
for (n = 0; n < opt->old_keyset_strings.num_strings; n++) {
size_t len = strlen(opt->old_keyset_strings.strings[n]);
void *data = malloc(len);
ssize_t ret;
ret = rk_hex_decode(opt->old_keyset_strings.strings[n], data, len);
if (ret < 0)
errx(1, "failed to parse as hex: %s", opt->old_keyset_strings.strings[n]);
CFDataRef el = CFDataCreate(NULL, data, ret);
if (el == NULL)
errx(1, "out of memory");
free(data);
CFArrayAppendValue(prevKeys, el);
CFRelease(el);
}
}
if (opt->enctype_strings.num_strings) {
enctypes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (enctypes == NULL)
errx(1, "out of memory");
for (n = 0; n < opt->enctype_strings.num_strings; n++) {
CFStringRef str = CFStringCreateWithCString(NULL, opt->enctype_strings.strings[n], kCFStringEncodingUTF8);
if (str == NULL)
errx(1, "out of memory");
CFArrayAppendValue(enctypes, str);
CFRelease(str);
}
}
if (opt->delete_flag) {
flags |= kHeimODAdminDeleteEnctypes;
} else {
if (argc < 2)
errx(1, "missing principal and password arguments that is needed when adding new keys");
principal = CFStringCreateWithCString(NULL, principalstr, kCFStringEncodingUTF8);
password = CFStringCreateWithCString(NULL, passwordstr, kCFStringEncodingUTF8);
if (principal == NULL || password == NULL)
errx(1, "out of memory");
if (opt->append_flag)
flags |= kHeimODAdminAppendKeySet;
}
keys = HeimODModifyKeys(prevKeys, principal, enctypes, password, flags, NULL);
if (keys == NULL)
errx(1, "HeimODModifyKeys failed");
count = CFArrayGetCount(keys);
for (n = 0; n < count; n++) {
CFStringRef value;
char *str;
CFDataRef element = CFArrayGetValueAtIndex(keys, n);
if (CFGetTypeID(element) != CFDataGetTypeID())
continue;
value = HeimODKeysetToString(element, NULL);
if (value == NULL)
errx(1, "HeimODKeysetToString failed");
CFShow(value);
CFRelease(value);
hex_encode(CFDataGetBytePtr(element), CFDataGetLength(element), &str);
printf("raw: %s\n", str);
}
if (enctypes)
CFRelease(enctypes);
if (principal)
CFRelease(principal);
if (password)
CFRelease(password);
if (prevKeys)
CFRelease(prevKeys);
CFRelease(keys);
return 0;
}
int
help(void *opt, int argc, char **argv)
{
sl_slc_help(commands, argc, argv);
return 0;
}
static int help_flag;
static int version_flag;
static char *local_path_string;
static struct getargs args[] = {
{ "local-path", 0, arg_string, &local_path_string, "OD Local path string" },
{ "help", 'h', arg_flag, &help_flag },
{ "version", 'v', arg_flag, &version_flag }
};
static int num_args = sizeof(args) / sizeof(args[0]);
static void
usage(int ret)
{
arg_printusage (args, num_args, NULL, "node-path command ...");
exit (ret);
}
int
main(int argc, char **argv)
{
ODSessionRef session = kODSessionDefault;
int ret, exit_status = 0;
CFStringRef root;
int optidx = 0;
setprogname(argv[0]);
if(getarg(args, num_args, argc, argv, &optidx))
usage(1);
if (help_flag)
usage (0);
if (version_flag) {
print_version(NULL);
exit(0);
}
argc -= optidx;
argv += optidx;
if (argc < 2)
errx(1, "command missing ODNode to operate on and node");
if (strcmp(".", argv[0]) == 0)
root = CFSTR("/Local/Default");
else
root = CFStringCreateWithCString(kCFAllocatorDefault, argv[0], kCFStringEncodingUTF8);
if (root == NULL)
errx(1, "out of memory");
if (local_path_string) {
#ifdef __APPLE_PRIVATE__
CFMutableDictionaryRef options;
options = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (options == NULL)
errx(1, "out of memory");
CFStringRef local_path = CFStringCreateWithCString(kCFAllocatorDefault, local_path_string, kCFStringEncodingUTF8);
CFDictionaryAddValue(options, kODSessionLocalPath, local_path);
CFRelease(local_path);
session = ODSessionCreate(kCFAllocatorDefault, options, NULL);
CFRelease(options);
#else
errx(1, "no supported");
#endif
}
CFErrorRef error;
node = ODNodeCreateWithName(kCFAllocatorDefault, session, root, &error);
if (node == NULL) {
if (error)
CFShow(error);
errx(1, "ODNodeCreateWithName failed");
}
if (session)
CFRelease(session);
argc -= 1;
argv += 1;
ret = sl_command(commands, argc, argv);
if(ret == -1) {
warnx("unrecognized command: %s", argv[0]);
exit_status = 2;
} else if (ret == -2)
ret = 0;
if (ret != 0)
exit_status = 1;
CFRelease(node);
CFRelease(root);
return exit_status;
}