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