#include "config.h"
#include "HeimODAdmin.h"
#ifdef __APPLE_PRIVATE__
#include <OpenDirectory/OpenDirectoryPriv.h>
#endif
#include <Security/Security.h>
#include <CommonCrypto/CommonDigest.h>
#include <Heimdal/krb5.h>
#include <hdb.h>
#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;
CFWriteStreamRef stream = CFWriteStreamCreateWithFile(NULL, url);
if (stream == NULL)
goto out;
if (!CFWriteStreamOpen(stream))
goto out;
CFWriteStreamWrite(stream, CFDataGetBytePtr(xmldata), CFDataGetLength(xmldata));
CFWriteStreamClose(stream);
CFRelease(stream);
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;
}
static void
set_if_lkdc_or_empty(CFStringRef key, CFStringRef value, CFStringRef prop)
{
CFStringRef orig = CFPreferencesCopyValue(key, prop, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
if (orig == NULL ||
(CFGetTypeID(orig) == CFStringGetTypeID() && CFStringFindWithOptions(orig, CFSTR("@LKDC:SHA"), CFRangeMake(0, CFStringGetLength(orig)), 0, NULL)))
CFPreferencesSetValue(key, value, prop, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
if (orig)
CFRelease(orig);
}
static void
create_random_server_principal(krb5_context context,
const char *record_name,
CFStringRef realm,
int krbtgt,
int verbose,
int add_to_keytab)
{
ODRecordRef record = NULL;
CFMutableArrayRef flags;
char *realmstr = NULL;
CFDictionaryRef dump = NULL;
if (verbose) printf("creating %s\n", record_name);
record = find_record(record_name);
if (record == NULL) {
warnx("failed to create node %s", record_name);
goto out;
}
if (HeimODCreatePrincipalData(node, record, NULL, NULL, NULL)) {
warnx("HeimODCreatePrincipalData failed for %s", record_name);
goto out;
}
if (verbose) printf("\tsetting flags\n");
flags = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (flags == NULL) {
warnx("out of memory");
goto out;
}
#if 0
if (krbtgt)
CFArrayAppendValue(flags, kPrincipalFlagInitial);
#endif
CFArrayAppendValue(flags, kPrincipalFlagForwardable);
CFArrayAppendValue(flags, kPrincipalFlagProxyable);
CFArrayAppendValue(flags, kPrincipalFlagRenewable);
CFArrayAppendValue(flags, kPrincipalFlagServer);
HeimODSetKerberosFlags(node, record, flags, NULL);
CFRelease(flags);
if (verbose) printf("\tsetting (random) keys\n");
HeimODSetKeys(node, record, NULL, NULL, NULL, 0, NULL);
if (verbose) printf("\tmaking entry valid\n");
HeimODClearKerberosFlags(node, record, kPrincipalFlagInvalid, NULL);
if (!ODRecordSynchronize(record, NULL))
warnx("failed to save %s", record_name);
else if (verbose)
printf("\tsaved!\n");
if (add_to_keytab) {
char *users[] = { "afpserver", "cifs", "vnc", "host" };
krb5_principal principals[sizeof(users)/sizeof(users[0])] = { 0 };
krb5_keytab_entry entry;
krb5_error_code ret;
krb5_keytab keytab;
size_t n, m, o, count;
CFErrorRef error = NULL;
memset(&entry, 0, sizeof(entry));
realmstr = rk_cfstring2cstring(realm);
if (realmstr == NULL)
errx(1, "failed gettng realm");
for (n = 0; n < sizeof(users)/sizeof(users[0]); n++)
krb5_make_principal(context, &principals[n], realmstr, users[n], realmstr, NULL);
if (verbose)
printf("\tstoring entry to keytab!\n");
ret = krb5_kt_default (context, &keytab);
if (ret) {
krb5_warn(context, ret, "krb5_kt_default");
goto out;
}
dump = HeimODDumpRecord(node, record, NULL, &error);
if (dump == NULL) {
warnx("failed dump record %s", record_name);
if (error)
CFRelease(error);
goto out;
}
CFArrayRef keys = CFDictionaryGetValue(dump, CFSTR("KerberosKeys"));
if (keys == NULL) {
warnx("failed to get keys for record %s", record_name);
goto out;
}
entry.timestamp = (uint32_t)time(NULL);
entry.flags = 0;
count = CFArrayGetCount(keys);
for (m = 0; m < count; m++) {
hdb_keyset_aapl keyset;
CFDataRef d = CFArrayGetValueAtIndex(keys, m);
ret = decode_hdb_keyset_aapl(CFDataGetBytePtr(d), CFDataGetLength(d), &keyset, NULL);
if (ret)
errx(1, "failed to decode kerberos key");
if (keyset.principal) {
free_hdb_keyset_aapl(&keyset);
continue;
}
entry.vno = keyset.kvno;
for (o = 0; o < keyset.keys.len; o++) {
if (keyset.keys.val[o].mkvno)
continue;
entry.keyblock = keyset.keys.val[o].key;
if (verbose)
printf("\t\tentries for vno %d - enctype: %d\n",
entry.vno, entry.keyblock.keytype);
for (n = 0; n < sizeof(principals)/sizeof(principals[0]); n++) {
entry.principal = principals[n];
ret = krb5_kt_add_entry(context, keytab, &entry);
if (ret)
krb5_warn(context, ret, "failed to store keytab entry");
}
}
free_hdb_keyset_aapl(&keyset);
}
ret = krb5_kt_close(context, keytab);
if (ret) {
krb5_warn(context, ret, "krb5_kt_close");
goto out;
}
for (n = 0; n < sizeof(users)/sizeof(users[0]); n++)
krb5_free_principal(context, principals[n]);
if (verbose) printf("\tsetting up afp/smb configuration\n");
CFStringRef afpconf = CFStringCreateWithFormat(NULL, NULL, CFSTR("afpserver/%@@%@"), realm, realm);
set_if_lkdc_or_empty(CFSTR("kerberosPrincipal"), afpconf,
CFSTR("/Library/Preferences/com.apple.AppleFileServer"));
CFRelease(afpconf);
set_if_lkdc_or_empty(CFSTR("LocalKerberosRealm"), realm,
CFSTR("/Library/Preferences/SystemConfiguration/com.apple.smb.server"));
}
if (verbose)
printf("\tdone %s\n", record_name);
out:
if (dump)
CFRelease(dump);
if (realmstr)
free(realmstr);
if (record)
CFRelease(record);
}
static char *
sha1_hash(const void *data, size_t len)
{
char *outstr, *cpOut;
unsigned char digest[CC_SHA1_DIGEST_LENGTH];
unsigned i;
CC_SHA1(data, (CC_LONG)len, digest);
cpOut = outstr = (char *)malloc((2 * CC_SHA1_DIGEST_LENGTH) + 1);
if (outstr == NULL)
return NULL;
for(i = 0; i < sizeof(digest); i++, cpOut += 2)
sprintf(cpOut, "%02X", (unsigned)digest[i]);
*cpOut = '\0';
return outstr;
}
static CFStringRef
realmOfIdentity(SecIdentityRef identity)
{
SecCertificateRef cert;
CFStringRef realm;
char *cert_hash;
CFDataRef data;
if (SecIdentityCopyCertificate(identity, &cert) != noErr)
errx(1, "failed to turn identity into certificate");
data = SecCertificateCopyData(cert);
CFRelease(cert);
if (data == NULL)
errx(1, "SecCertificateCopyData");
cert_hash = sha1_hash(CFDataGetBytePtr(data), CFDataGetLength(data));
CFRelease(data);
if (cert_hash == NULL)
errx(1, "error obtaining cert hash");
realm = CFStringCreateWithFormat(NULL, NULL, CFSTR("LKDC:SHA1.%s"), cert_hash);
free(cert_hash);
return realm;
}
static CFStringRef kRealName = CFSTR("dsAttrTypeStandard:RealName");
static CFStringRef kKerberosKDC = CFSTR("KerberosKDC");
static int
verifyRecord(ODRecordRef kdcConf, CFStringRef realm)
{
CFArrayRef data = NULL;
CFStringRef storedRealm = NULL;
data = ODRecordCopyValues(kdcConf, kRealName, NULL);
if (data == NULL)
return 0;
if (CFArrayGetCount(data) != 1) {
CFRelease(data);
return 0;
}
storedRealm = (CFStringRef)CFArrayGetValueAtIndex(data, 0);
if (storedRealm == NULL) {
CFRelease(data);
return 0;
}
if (!CFEqual(storedRealm, realm)) {
CFRelease(data);
return 0;
}
CFRelease(data);
return 1;
}
static int
verifyKerberosKDCRecord(CFStringRef realm, int force_update, int verbose)
{
ODRecordRef kdcConf = NULL;
int error = 1;
CFArrayRef array = NULL;
if (verbose) printf("try to find configuration node\n");
kdcConf = ODNodeCopyRecord(node, kODRecordTypeConfiguration, kKerberosKDC, NULL, NULL);
if (kdcConf == NULL) {
CFDictionaryRef attributes = NULL;
if (verbose) printf("\tnot found, adding record\n");
array = CFArrayCreate(NULL, (const void **)&realm, 1, &kCFTypeArrayCallBacks);
attributes = CFDictionaryCreate(NULL,
(const void **)&kRealName,
(const void **)&array,
1,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
kdcConf = ODNodeCreateRecord(node,
kODRecordTypeConfiguration,
kKerberosKDC,
attributes,
NULL);
if (kdcConf == NULL)
errx(1, "failed to create KerberosKDC node");
if (!ODRecordSynchronize(kdcConf, NULL))
warnx("ODRecordSynchronize failed");
else
error = 0;
CFRelease(attributes);
} else if (force_update || !verifyRecord(kdcConf, realm)) {
if (verbose) printf("\tfound, but wrong/missing realm\n");
array = CFArrayCreate(NULL, (const void **)&realm, 1, &kCFTypeArrayCallBacks);
bool r = ODRecordSetValue(kdcConf, kRealName, array, NULL);
if (!r)
errx(1, "ODRecordSetValue");
if (!ODRecordSynchronize(kdcConf, NULL))
warnx("ODRecordSynchronize failed");
else
error = 0;
} else {
if (verbose) printf("\tfound, all ok!\n");
error = 0;
}
if (array)
CFRelease(array);
if (kdcConf)
CFRelease(kdcConf);
return error;
}
static void
remove_lkdc_keytab_entrys(krb5_context context)
{
krb5_keytab keytab;
krb5_kt_cursor cursor;
krb5_keytab_entry entry;
krb5_error_code ret;
ret = krb5_kt_default (context, &keytab);
if (ret) {
krb5_warn(context, ret, "krb5_kt_default");
return;
}
ret = krb5_kt_start_seq_get(context, keytab, &cursor);
if (ret) {
krb5_warn(context, ret, "krb5_kt_start_seq_get");
return;
}
while ((ret = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0){
if (krb5_principal_is_lkdc(context, entry.principal))
krb5_kt_remove_entry(context, keytab, &entry);
krb5_kt_free_entry(context, &entry);
}
ret = krb5_kt_end_seq_get(context, keytab, &cursor);
if (ret) {
krb5_warn(context, 1, "krb5_kt_end_seq_get");
return;
}
ret = krb5_kt_close(context, keytab);
if (ret) {
krb5_warn(context, ret, "krb5_kt_close");
return;
}
}
int
setup_lkdc(struct setup_lkdc_options *opt, int argc, char **argv)
{
krb5_context context = NULL;
SecIdentityRef kdc = NULL;
CFStringRef realm = NULL;
int error = 1;
if (krb5_init_context (&context) != 0) {
warnx("krb5_context");
goto out;
}
if (opt->keytab_flag) {
if (opt->verbose_flag)
printf("removing LKDC keytab entries\n");
remove_lkdc_keytab_entrys(context);
}
if (opt->verbose_flag)
printf("checking for LKDC kdc certificate\n");
if (opt->kdc_certificate_flag ||
SecIdentityCopySystemIdentity(kSecIdentityDomainKerberosKDC, &kdc, NULL) != noErr) {
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)");
goto out;
}
realm = realmOfIdentity(kdc);
if (realm == NULL) {
warnx("failed to get hash of certificate");
goto out;
}
if (verifyKerberosKDCRecord(realm, opt->kdc_certificate_flag, opt->verbose_flag)) {
warnx("failed to verify KDC record");
goto out;
}
create_random_server_principal(context, "/Users/_krbtgt", realm, 1, opt->verbose_flag, 0);
create_random_server_principal(context, "/Computers/localhost", realm, 0, opt->verbose_flag, 1);
if (opt->verbose_flag)
printf("done\n");
error = 0;
out:
if (realm)
CFRelease(realm);
if (kdc)
CFRelease(kdc);
krb5_free_context(context);
return error;
}
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;
}