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 <krb5.h>
45#include <heimbase.h>
46#include <hx509.h>
47#include <asn1-common.h>
48#include <hdb.h>
49#include "admin.h"
50
51
52#define kHeimODKerberosKeys CFSTR("dsAttrTypeNative:KerberosKeys")
53#define kHeimODKerberosFlags CFSTR("dsAttrTypeNative:KerberosFlags")
54#define kHeimODKerberosUserName CFSTR("dsAttrTypeNative:KerberosUserName")
55#define kHeimODKerberosServerName CFSTR("dsAttrTypeNative:KerberosServerName")
56static CFStringRef kHeimASI = CFSTR("dsAttrTypeStandard:AltSecurityIdentities");
57static CFStringRef kHeimSRP = CFSTR("dsAttrTypeNative:HeimdalSRPKey");
58
59static CFStringRef kRealName = CFSTR("dsAttrTypeStandard:RealName");
60
61#define kHeimLDAPKerberosUserName CFSTR("dsAttrTypeNative:draft-krbPrincipalName")
62#define kHeimLDAPKerberosServerName CFSTR("dsAttrTypeNative:draft-krbPrincipalAliases")
63#define kHeimLDAPKerberosKeys CFSTR("dsAttrTypeNative:draft-krbKeySet")
64#define kHeimLDAPKerberosFlags CFSTR("dsAttrTypeNative:draft-krbTicketPolicy")
65
66
67
68static CFStringRef remove_keys_client[] = {
69    kHeimODKerberosUserName,
70    kHeimODKerberosServerName,
71    kHeimODKerberosKeys,
72    kHeimODKerberosFlags,
73    CFSTR("dsAttrTypeNative:KerberosMaxLife"),
74    CFSTR("dsAttrTypeNative:KerberosMaxRenew"),
75    CFSTR("dsAttrTypeNative:KerberosValidStart"),
76    CFSTR("dsAttrTypeNative:KerberosValidEnd")
77};
78static const int num_remove_keys_client = sizeof(remove_keys_client) / sizeof(remove_keys_client[0]);
79
80static CFStringRef remove_keys_server[] = {
81    kHeimLDAPKerberosUserName,
82    kHeimLDAPKerberosServerName,
83    kHeimLDAPKerberosKeys,
84    kHeimLDAPKerberosFlags
85};
86static const int num_remove_keys_server = sizeof(remove_keys_server) / sizeof(remove_keys_server[0]);
87
88
89#define kHeimODKerberosACL CFSTR("dsAttrTypeNative:KerberosACL")
90#define kHeimLDAPKerberosACL CFSTR("dsAttrTypeNative:draft-krbPrincipalACL")
91
92struct s2k {
93    CFStringRef s;
94    unsigned int k;
95};
96
97struct s2k FlagsKeys[] = {
98    { kPrincipalFlagInitial, 		(1<<0) },
99    { kPrincipalFlagForwardable,	(1<<1) },
100    { kPrincipalFlagProxyable,		(1<<2) },
101    { kPrincipalFlagRenewable,		(1<<3) },
102    { kPrincipalFlagServer,		(1<<5) },
103    { kPrincipalFlagInvalid,		(1<<7) },
104    { kPrincipalFlagRequireStrongPreAuthentication, (1<<8) },
105    { kPrincipalFlagPasswordChangeService, (1<<9) },
106    { kPrincipalFlagOKAsDelegate,	(1<<11) },
107    { kPrincipalFlagImmutable,		(1<<13) },
108    { NULL }
109};
110
111struct s2k ACLKeys[] = {
112    { kHeimODACLChangePassword,		KADM5_PRIV_CPW },
113    { kHeimODACLList,			KADM5_PRIV_LIST },
114    { kHeimODACLDelete,			KADM5_PRIV_DELETE },
115    { kHeimODACLModify,			KADM5_PRIV_MODIFY },
116    { kHeimODACLAdd,			KADM5_PRIV_ADD },
117    { kHeimODACLGet,			KADM5_PRIV_GET },
118    { NULL }
119};
120
121static CFIndex
122createError(CFErrorRef *error, CFIndex errorcode, CFStringRef fmt, ...)
123    CF_FORMAT_FUNCTION(3, 4);
124
125static CFIndex
126createError(CFErrorRef *error, CFIndex errorcode, CFStringRef fmt, ...)
127{
128    void *keys[1] = { (void *)CFSTR("HODErrorMessage") };
129    void *values[1];
130    va_list va;
131
132    if (error == NULL)
133	return errorcode;
134
135    va_start(va, fmt);
136    values[0] = (void *)CFStringCreateWithFormatAndArguments(NULL, NULL, fmt, va);
137    va_end(va);
138    if (values[0] == NULL) {
139	*error = NULL;
140	return errorcode;
141    }
142
143    if (*error)
144	CFRelease(*error);
145
146    *error = CFErrorCreateWithUserInfoKeysAndValues(NULL, CFSTR("com.apple.Heimdal.ODAdmin"), errorcode,
147						    (const void * const *)keys,
148						    (const void * const *)values, 1);
149    CFRelease(values[0]);
150    return errorcode;
151}
152
153static char *
154cfstring2cstring(CFStringRef string)
155{
156    CFIndex l;
157    char *s;
158
159    s = (char *)CFStringGetCStringPtr(string, kCFStringEncodingUTF8);
160    if (s)
161	return strdup(s);
162
163    l = CFStringGetMaximumSizeForEncoding(CFStringGetLength(string), kCFStringEncodingUTF8) + 1;
164    s = malloc(l);
165    if (s == NULL)
166	return NULL;
167
168    if (!CFStringGetCString(string, s, l, kCFStringEncodingUTF8)) {
169	free (s);
170	return NULL;
171    }
172
173    return s;
174}
175
176static krb5_principal
177krb5_principalCreateFromString(krb5_context context, CFStringRef principal)
178{
179    krb5_principal princ;
180    krb5_error_code ret;
181    char *princstr;
182
183    princstr = cfstring2cstring(principal);
184    if (princstr == NULL)
185	return NULL;
186
187    ret = krb5_parse_name(context, princstr, &princ);
188    free(princstr);
189    if (ret)
190	return NULL;
191
192    return princ;
193}
194
195static CFStringRef
196CFStringCreateWithkrb5_principal(krb5_context context,
197				 krb5_const_principal principal)
198{
199    krb5_error_code ret;
200    CFStringRef value;
201    char *name;
202
203    ret = krb5_unparse_name(context, principal, &name);
204    if (ret)
205	return NULL;
206
207    value = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingUTF8);
208    krb5_xfree(name);
209
210    return value;
211}
212
213
214
215static unsigned int
216string2int(const struct s2k *t, CFStringRef s)
217{
218    while (t->s) {
219	if (CFStringCompare(s, t->s, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
220	    return t->k;
221	t++;
222    }
223
224    return 0;
225}
226
227static bool
228is_record_server_location(ODRecordRef record)
229{
230    CFArrayRef values;
231    bool is_server = false;
232
233    values = ODRecordCopyValues(record, kODAttributeTypeMetaNodeLocation, NULL);
234    if (values == NULL)
235	return false;
236
237    if (CFArrayGetCount(values) > 0) {
238	CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(values, 0);
239	if (CFStringGetTypeID() == CFGetTypeID(str) &&
240	    CFStringHasPrefix(str, CFSTR("/LDAPv3/")))
241	{
242	    is_server = true;
243	}
244    }
245    CFRelease(values);
246
247    return is_server;
248}
249
250static bool
251force_server(void)
252{
253    static dispatch_once_t once;
254    static bool force = false;
255
256    dispatch_once(&once, ^{
257	    CFBooleanRef b;
258	    b = CFPreferencesCopyAppValue(CFSTR("ForceHeimODServerMode"),
259					  CFSTR("com.apple.Kerberos"));
260	    if (b && CFGetTypeID(b) == CFBooleanGetTypeID())
261		force = CFBooleanGetValue(b);
262	    if (b)
263		CFRelease(b);
264	});
265    return force;
266}
267
268static bool
269is_record_server(ODRecordRef record)
270{
271    if (force_server())
272	return true;
273
274    return is_record_server_location(record);
275}
276
277static ODRecordRef
278copyDataRecord(ODNodeRef node, ODRecordRef record,
279	       CFStringRef ckey, CFStringRef skey,
280	       CFStringRef *key, bool *server_record_p,
281	       CFErrorRef *error)
282{
283    /* XXX copy user node */
284    bool is_server = is_record_server_location(record);
285
286    if (is_server) {
287
288	if (force_server()) {
289	    CFRetain(record);
290	} else {
291#ifdef __APPLE_PRIVATE__
292            if (CFStringCompare(ODRecordGetRecordType(record), kODRecordTypeUserAuthenticationData, 0) == kCFCompareEqualTo) {
293                CFRetain(record);
294            } else {
295	        record = ODNodeCopyRecordAuthenticationData(node, record, error);
296	        if (record == NULL && error != NULL && *error == NULL)
297		    createError(error, 1, CFSTR("can't map user record to UserAuthenticationRecord"));
298            }
299#else
300	    CFRetain(record);
301#endif
302	}
303
304	if (key)
305	    *key = skey;
306    } else {
307	CFRetain(record);
308
309	if (key)
310	    *key = ckey;
311    }
312
313    if (server_record_p)
314	*server_record_p = is_server;
315
316    return record;
317}
318
319
320static unsigned int
321flags2int(const struct s2k *t, CFTypeRef type)
322{
323    unsigned int nflags = 0;
324
325    if (t == NULL)
326	abort();
327
328    if (CFGetTypeID(type) == CFStringGetTypeID()) {
329
330	nflags = string2int(t, type);
331
332    } else if (CFGetTypeID(type) == CFArrayGetTypeID()) {
333	CFIndex n;
334
335	for (n = 0; n < CFArrayGetCount(type); n++) {
336	    CFTypeRef el = CFArrayGetValueAtIndex(type, n);
337
338	    if (CFGetTypeID(el) == CFStringGetTypeID())
339		nflags |= string2int(t, el);
340	}
341    }
342    return nflags;
343}
344
345static CFArrayRef
346CopyInt2flags(const struct s2k *t, unsigned long n)
347{
348    CFMutableArrayRef array;
349
350    array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
351    if (array == NULL)
352	return NULL;
353
354    while (t->s) {
355	if (t->k & n)
356	    CFArrayAppendValue(array, t->s);
357	t++;
358    }
359
360    return array;
361}
362
363/*
364 *
365 */
366
367int
368HeimODCreateRealm(ODNodeRef node, CFStringRef realm, CFErrorRef *error)
369{
370    /* create
371     * - krbtgt/realm
372     * - kadmin/admin
373     * - kadmin/changepw
374     * - WELLKNOWN/ANONYMOUS
375     */
376    if (error)
377	*error = NULL;
378    return 0;
379}
380
381/*
382 *
383 */
384
385int
386HeimODCreatePrincipalData(ODNodeRef node, ODRecordRef record, CFTypeRef flags, CFStringRef principal, CFErrorRef *error)
387{
388    CFStringRef element;
389    bool r = false;
390    bool is_server = false;
391    unsigned int nflags =
392	(1<<1) /* forwardable */ |
393	(1<<2) /* proxiable */ |
394	(1<<6) /* client */ |
395	(1<<7) /* invalid */;
396    CFStringRef kflags, kusername;
397    ODRecordRef datarecord = NULL;
398
399    if (error)
400	*error = NULL;
401
402    datarecord = copyDataRecord(node, record, NULL, NULL, NULL, &is_server, error);
403    if (datarecord == NULL)
404	goto out;
405
406    if (is_server) {
407	kflags = kHeimLDAPKerberosFlags;
408	kusername = kHeimLDAPKerberosUserName;
409    } else {
410	kflags = kHeimODKerberosFlags;
411	kusername = kHeimODKerberosUserName;
412    }
413
414    if (flags)
415	nflags |= flags2int(FlagsKeys, flags);
416
417    element = CFStringCreateWithFormat(kCFAllocatorDefault,
418				       NULL, CFSTR("%u"), nflags);
419    if (element == NULL) {
420	createError(error, ENOMEM, CFSTR("out of memory"));
421	goto out;
422    }
423
424    r = ODRecordSetValue(datarecord, kflags, element, error);
425    CFRelease(element);
426    if (!r)
427	goto out;
428
429    if (principal) {
430	r = ODRecordSetValue(datarecord, kusername, principal, error);
431	if (!r)
432	    goto out;
433    }
434
435    if (!ODRecordSynchronize(datarecord, NULL))
436	r = false;
437
438out:
439    if (datarecord)
440	CFRelease(datarecord);
441    return r ? 0 : 1;
442}
443
444/*
445 *
446 */
447
448int
449HeimODRemovePrincipalData(ODNodeRef node, ODRecordRef record, CFStringRef principal, CFErrorRef *error)
450{
451    CFMutableArrayRef array;
452    ODRecordRef datarecord;
453    CFStringRef *remove_keys;
454    int num_remove_keys;
455    bool is_server = false;
456    CFIndex i;
457    int ret = 1;
458
459    datarecord = copyDataRecord(node, record, NULL, NULL, NULL, &is_server, error);
460    if (datarecord == NULL)
461	goto out;
462
463    if (is_server) {
464	remove_keys = remove_keys_server;
465	num_remove_keys = num_remove_keys_server;
466    } else {
467	remove_keys = remove_keys_client;
468	num_remove_keys = num_remove_keys_client;
469    }
470
471    array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
472    if (array == NULL) {
473	createError(error, 1, CFSTR("out of memory"));
474	goto out;
475    }
476
477    for (i = 0; i < num_remove_keys; i++)
478	ODRecordSetValue(datarecord, remove_keys[i], array, NULL);
479
480    CFRelease(array);
481
482    if (!ODRecordSynchronize(datarecord, error))
483	ret = 1;
484    else
485	ret = 0;
486
487 out:
488    if (datarecord)
489	CFRelease(datarecord);
490
491    return ret;
492}
493
494/*
495 *
496 */
497
498static unsigned long
499getflags(ODRecordRef datarecord, CFStringRef kflags, CFErrorRef *error)
500{
501    CFArrayRef array;
502    CFStringRef s;
503    int32_t n;
504
505    array = ODRecordCopyValues(datarecord, kflags, error);
506    if (array == NULL)
507	return 0;
508
509    if (CFArrayGetCount(array) < 1) {
510	CFRelease(array);
511	return 0;
512    }
513    s = CFArrayGetValueAtIndex(array, 0);
514
515    if (CFStringGetTypeID() != CFGetTypeID(s)) {
516	CFRelease(array);
517	return 0;
518    }
519
520    n = CFStringGetIntValue(s);
521    CFRelease(array);
522
523    if (n == INT_MAX || n == INT_MIN)
524	return 0;
525
526    return n;
527}
528
529static int
530flagop(ODRecordRef datarecord,
531       CFStringRef kflags,
532       CFTypeRef flags,
533       const struct s2k *keys, CFErrorRef *error,
534       unsigned long (^op)(unsigned long oldflags, unsigned long newflags))
535{
536    unsigned long uflags, oldflags;
537    CFStringRef element;
538    bool r = false;
539
540    uflags = flags2int(keys, flags);
541    if (uflags == 0) {
542	createError(error, 1, CFSTR("failed to parse input flags"));
543	goto out;
544    }
545
546    oldflags = getflags(datarecord, kflags, error);
547
548    uflags = op(oldflags, uflags);
549
550    element = CFStringCreateWithFormat(kCFAllocatorDefault,
551				       NULL, CFSTR("%lu"), uflags);
552    if (element == NULL) {
553	createError(error, ENOMEM, CFSTR("out of memory"));
554	goto out;
555    }
556
557    r = ODRecordSetValue(datarecord, kflags, element, error);
558    CFRelease(element);
559
560 out:
561    if (!r)
562	return 1;
563    return 0;
564}
565
566/*
567 *
568 */
569
570int
571HeimODSetKerberosFlags(ODNodeRef node, ODRecordRef record, CFTypeRef flags, CFErrorRef *error)
572{
573    int ret;
574    ODRecordRef datarecord;
575    bool is_server;
576    CFStringRef kflags;
577
578    datarecord = copyDataRecord(node, record,
579				kHeimODKerberosFlags, kHeimLDAPKerberosFlags,
580				&kflags, &is_server, error);
581    if (datarecord == NULL)
582	return 1;
583
584    ret = flagop(datarecord, kflags, flags, FlagsKeys, error, ^(unsigned long oldflags, unsigned long newflags) {
585	    return (oldflags | newflags);
586	});
587
588    if (ret == 0 && !ODRecordSynchronize(datarecord, error))
589	ret = 1;
590
591    CFRelease(datarecord);
592
593    return ret;
594}
595
596/*
597 *
598 */
599
600CFArrayRef
601HeimODCopyKerberosFlags(ODNodeRef node, ODRecordRef record, CFErrorRef *error)
602{
603    unsigned long uflags;
604    CFArrayRef flags = NULL;
605    CFStringRef kflags;
606
607    ODRecordRef datarecord;
608
609    datarecord = copyDataRecord(node, record,
610				kHeimODKerberosFlags, kHeimLDAPKerberosFlags,
611				&kflags, NULL, error);
612    if (datarecord == NULL)
613	goto out;
614
615    uflags = getflags(datarecord, kflags, error);
616    if (uflags == 0) {
617	createError(error, 1, CFSTR("failed to parse input flags"));
618	goto out;
619    }
620
621    flags = CopyInt2flags(FlagsKeys, uflags);
622
623 out:
624    if (datarecord)
625	CFRelease(datarecord);
626    return flags;
627}
628
629/*
630 *
631 */
632
633int
634HeimODClearKerberosFlags(ODNodeRef node, ODRecordRef record, CFTypeRef flags, CFErrorRef *error)
635{
636    ODRecordRef datarecord;
637    int ret;
638    bool is_server;
639    CFStringRef kflags;
640
641    datarecord = copyDataRecord(node, record, NULL, NULL, NULL, &is_server, error);
642    if (datarecord == NULL)
643	return 1;
644
645    if (is_server) {
646	kflags = kHeimLDAPKerberosFlags;
647    } else {
648	kflags = kHeimODKerberosFlags;
649    }
650
651    ret = flagop(datarecord, kflags, flags, FlagsKeys, error, ^(unsigned long oldflags, unsigned long newflags) {
652	    return (oldflags & (~newflags));
653	});
654
655    if (ret == 0 && !ODRecordSynchronize(datarecord, error))
656	ret = 1;
657
658    CFRelease(datarecord);
659    return ret;
660}
661
662/*
663 *
664 */
665
666static CFTypeRef
667CopyReMapACL(CFTypeRef flags, CFErrorRef *error)
668{
669    if (CFGetTypeID(flags) == CFStringGetTypeID() && CFStringCompare(flags, kHeimODACLAll, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
670	CFMutableArrayRef nflags;
671	CFIndex n;
672
673	nflags = CFArrayCreateMutable(NULL, sizeof(ACLKeys) / sizeof(ACLKeys[0]), &kCFTypeArrayCallBacks);
674	if (nflags == NULL) {
675	    createError(error, ENOMEM, CFSTR("out of memory"));
676	    return NULL;
677	}
678	for (n = 0; ACLKeys[n].s; n++)
679	    CFArrayAppendValue(nflags, ACLKeys[n].s);
680	flags = nflags;
681    } else {
682	CFRetain(flags);
683    }
684    return flags;
685}
686
687/*
688 *
689 */
690
691int
692HeimODSetACL(ODNodeRef node, ODRecordRef record, CFTypeRef flags, CFErrorRef *error)
693{
694    int ret;
695    ODRecordRef datarecord;
696    CFStringRef kflags;
697
698    datarecord = copyDataRecord(node, record,
699				kHeimODKerberosACL, kHeimLDAPKerberosACL,
700				&kflags, NULL, error);
701    if (datarecord == NULL)
702	return 1;
703
704    flags = CopyReMapACL(flags, error);
705    if (flags == NULL)
706	return 1;
707
708    ret = flagop(datarecord, kflags, flags, ACLKeys, error, ^(unsigned long oldflags, unsigned long newflags) {
709	    return newflags;
710	});
711
712    if (ret == 0 && !ODRecordSynchronize(datarecord, error))
713	ret = 1;
714
715    CFRelease(datarecord);
716
717    CFRelease(flags);
718
719    return ret;
720}
721
722CFArrayRef
723HeimODCopyACL(ODNodeRef node, ODRecordRef record, CFErrorRef *error)
724{
725    unsigned long uflags;
726    CFArrayRef flags = NULL;
727    CFStringRef kflags;
728
729    ODRecordRef datarecord;
730
731    datarecord = copyDataRecord(node, record,
732				kHeimODKerberosACL, kHeimLDAPKerberosACL,
733				&kflags, NULL, error);
734    if (datarecord == NULL)
735	goto out;
736
737    uflags = getflags(datarecord, kflags, error);
738    if (uflags == 0) {
739	createError(error, 1, CFSTR("failed to parse input flags"));
740	goto out;
741    }
742
743    flags = CopyInt2flags(ACLKeys, uflags);
744
745 out:
746    if (datarecord)
747	CFRelease(datarecord);
748    return flags;
749}
750
751
752int
753HeimODClearACL(ODNodeRef node, ODRecordRef record, CFTypeRef flags, CFErrorRef *error)
754{
755    ODRecordRef datarecord;
756    int ret;
757    bool is_server;
758    CFStringRef kflags;
759
760    datarecord = copyDataRecord(node, record, kHeimODKerberosACL, kHeimLDAPKerberosACL, &kflags, &is_server, error);
761    if (datarecord == NULL)
762	return 1;
763
764    flags = CopyReMapACL(flags, error);
765    if (flags == NULL)
766	return 1;
767
768    ret = flagop(datarecord, kflags, flags, ACLKeys, error, ^(unsigned long oldflags, unsigned long newflags) {
769	return (oldflags & (~newflags));
770    });
771
772    if (ret == 0 && !ODRecordSynchronize(datarecord, error))
773	ret = 1;
774
775    CFRelease(flags);
776    CFRelease(datarecord);
777    return ret;
778}
779
780/*
781 *
782 */
783
784int
785HeimODAddServerAlias(ODNodeRef node, ODRecordRef record, CFStringRef alias, CFErrorRef *error)
786{
787    CFErrorRef localerror = NULL;
788    bool r = false;
789    CFStringRef key;
790    ODRecordRef datarecord;
791
792    datarecord = copyDataRecord(node, record,
793				kHeimODKerberosServerName,
794				kHeimLDAPKerberosServerName, &key,
795				NULL, error);
796
797    if (datarecord == NULL)
798	goto out;
799
800    r = ODRecordAddValue(datarecord, key, alias, &localerror);
801    if (!r) {
802	/* check for duplicate errors and ignore them */
803	if (localerror && CFErrorGetCode(localerror) == kODErrorRecordAttributeValueSchemaError) {
804	    CFRelease(localerror);
805	    r = true;
806	} else if (error)
807	    *error = localerror;
808	else if (localerror)
809	    CFRelease(localerror);
810
811	goto out;
812    }
813
814    if (!ODRecordSynchronize(datarecord, error))
815	r = false;
816out:
817    if (datarecord)
818	CFRelease(datarecord);
819    return r ? 0 : 1;
820}
821
822/*
823 *
824 */
825
826int
827HeimODRemoveServerAlias(ODNodeRef node, ODRecordRef record, CFStringRef alias, CFErrorRef *error)
828{
829    bool r = false;
830    CFStringRef key;
831    ODRecordRef datarecord;
832
833    datarecord = copyDataRecord(node, record,
834				kHeimODKerberosServerName,
835				kHeimLDAPKerberosServerName, &key,
836				NULL, error);
837
838    if (datarecord == NULL)
839	goto out;
840
841    r = ODRecordRemoveValue(datarecord, key, alias, error);
842    if (!r)
843	goto out;
844
845    if (!ODRecordSynchronize(datarecord, error))
846	r = false;
847out:
848    if (datarecord)
849	CFRelease(datarecord);
850    return r ? 0 : 1;
851}
852
853/*
854 *
855 */
856
857CFArrayRef
858HeimODCopyServerAliases(ODNodeRef node, ODRecordRef record, CFErrorRef *error)
859{
860    ODRecordRef datarecord;
861    CFStringRef key;
862    CFArrayRef res = NULL;
863
864    datarecord = copyDataRecord(node, record,
865				kHeimODKerberosServerName,
866				kHeimLDAPKerberosServerName, &key,
867				NULL, error);
868    if (datarecord == NULL)
869	goto out;
870
871    res = ODRecordCopyValues(datarecord, key, error);
872 out:
873    if (datarecord)
874	CFRelease(datarecord);
875    return res;
876}
877
878int
879HeimODSetKerberosMaxLife(ODNodeRef node, ODRecordRef record, time_t t, CFErrorRef *error)
880{
881    if (error)
882	*error = NULL;
883    return 0;
884}
885
886/*
887 *
888 */
889
890time_t
891HeimODGetKerberosMaxLife(ODNodeRef node, ODRecordRef record, CFErrorRef *error)
892{
893    if (error)
894	*error = NULL;
895    return 0;
896}
897
898/*
899 *
900 */
901
902int
903HeimODSetKerberosMaxRenewable(ODNodeRef node, ODRecordRef record, time_t t, CFErrorRef *error)
904{
905    if (error)
906	*error = NULL;
907    return 0;
908}
909
910/*
911 *
912 */
913
914time_t
915HeimODGetKerberosMaxRenewable(ODNodeRef node, ODRecordRef record, CFErrorRef *error)
916{
917    if (error)
918	*error = NULL;
919    abort();
920}
921
922static CFStringRef
923getkeykey(ODRecordRef record)
924{
925    if (is_record_server(record))
926	return kHeimLDAPKerberosKeys;
927    return kHeimODKerberosKeys;
928}
929
930static unsigned
931last_kvno_array(CFArrayRef array, krb5_context context, krb5_principal principal)
932{
933    krb5_kvno kvno = 0;
934    CFIndex i;
935
936    for (i = 0; i < CFArrayGetCount(array); i++) {
937	CFDataRef key = CFArrayGetValueAtIndex(array, i);
938	hdb_keyset_aapl keyset;
939	int ret;
940
941	if (key == NULL || CFGetTypeID(key) != CFDataGetTypeID())
942	    continue;
943
944	ret = decode_hdb_keyset_aapl(CFDataGetBytePtr(key), CFDataGetLength(key), &keyset, NULL);
945	if (ret)
946	    continue;
947
948	if (principal) {
949	    if (keyset.principal == NULL || krb5_principal_compare(context, keyset.principal, principal) == 0) {
950		free_hdb_keyset_aapl(&keyset);
951		continue;
952	    }
953	} else {
954	    if (keyset.principal) {
955		free_hdb_keyset_aapl(&keyset);
956		continue;
957	    }
958	}
959
960	if (kvno < keyset.kvno)
961	    kvno = keyset.kvno;
962
963	free_hdb_keyset_aapl(&keyset);
964    }
965    return kvno;
966}
967
968static unsigned
969last_kvno_record(ODRecordRef datarecord, krb5_context context, krb5_principal principal)
970{
971    CFArrayRef array;
972    unsigned kvno = 0;
973
974    array = ODRecordCopyValues(datarecord, getkeykey(datarecord), NULL);
975    if (array == NULL)
976	return 0;
977
978    kvno = last_kvno_array(array, context, principal);
979
980    CFRelease(array);
981
982    return kvno;
983}
984
985static krb5_enctype
986find_enctype(krb5_context context, CFStringRef string)
987{
988    krb5_enctype ret;
989    krb5_enctype e;
990    char *str = cfstring2cstring(string);
991
992    ret = krb5_string_to_enctype(context, str, &e);
993    free(str);
994    if (ret)
995	return ENCTYPE_NULL;
996    return e;
997}
998
999static void
1000applier(const void *value, void *context)
1001{
1002    void (^block)(const void *value) = context;
1003    block(value);
1004}
1005
1006static void
1007arrayapplyblock(CFArrayRef theArray, CFRange range, void (^block)(const void *value))
1008{
1009    CFArrayApplyFunction(theArray, range, applier, block);
1010}
1011
1012static char *
1013get_lkdc_realm(void)
1014{
1015    ODNodeRef localRef = NULL;
1016    ODRecordRef kdcConfRef = NULL;
1017    CFArrayRef data = NULL;
1018    char *LKDCRealm = NULL;
1019
1020    localRef = ODNodeCreateWithName(kCFAllocatorDefault, kODSessionDefault,
1021				    CFSTR("/Local/Default"), NULL);
1022    if (localRef == NULL)
1023	goto out;
1024
1025    kdcConfRef = ODNodeCopyRecord(localRef, kODRecordTypeConfiguration,
1026				  CFSTR("KerberosKDC"), NULL, NULL);
1027    if (kdcConfRef == NULL)
1028	goto out;
1029
1030    data = ODRecordCopyValues(kdcConfRef, kRealName, NULL);
1031    if (data == NULL)
1032	goto out;
1033
1034    if (CFArrayGetCount(data) != 1)
1035	goto out;
1036
1037    LKDCRealm = cfstring2cstring((CFStringRef)CFArrayGetValueAtIndex(data, 0));
1038
1039 out:
1040    if (data)
1041	CFRelease(data);
1042    if (kdcConfRef)
1043	CFRelease(kdcConfRef);
1044    if (localRef)
1045	CFRelease(localRef);
1046
1047    return LKDCRealm;
1048}
1049
1050/*
1051 *
1052 */
1053
1054static CFDataRef
1055update_keys(krb5_context context,
1056	    CFArrayRef enctypes,
1057	    unsigned kvno,
1058	    krb5_principal princ,
1059	    int include_principal,
1060	    const char *password,
1061	    CFErrorRef *error)
1062{
1063    heim_octet_string data;
1064    __block hdb_keyset_aapl key;
1065    __block krb5_error_code ret;
1066    CFArrayRef defaultenctypes = NULL;
1067    CFDataRef element = NULL;
1068    size_t size = 0;
1069
1070    memset(&key, 0, sizeof(key));
1071
1072    if (error)
1073	*error = NULL;
1074
1075
1076    if (enctypes == NULL) {
1077	enctypes = defaultenctypes = HeimODCopyDefaultEnctypes(error);
1078	if (enctypes == NULL)
1079	    return NULL;
1080    }
1081
1082    key.kvno = kvno;
1083
1084    if (include_principal) {
1085	ret = krb5_copy_principal(context, princ, &key.principal);
1086	if (ret) {
1087	    createError(error, ret, CFSTR("out of memory"));
1088	    goto out;
1089	}
1090    }
1091
1092
1093    arrayapplyblock(enctypes, CFRangeMake(0, CFArrayGetCount(enctypes)), ^(const void *value) {
1094	void *ptr;
1095
1096	if (CFGetTypeID(value) != CFStringGetTypeID())
1097	    return;
1098
1099	krb5_enctype e = find_enctype(context, (CFStringRef)value);
1100	if (e == ENCTYPE_NULL)
1101	    return;
1102
1103	ptr = realloc(key.keys.val, (key.keys.len + 1) * sizeof(key.keys.val[0]));
1104	if (ptr == NULL)
1105	    return;
1106	key.keys.val = ptr;
1107
1108	/* clear new entry */
1109	memset(&key.keys.val[key.keys.len], 0, sizeof(key.keys.val[0]));
1110
1111	if (password) {
1112	    krb5_data pw, opaque;
1113	    krb5_salt salt;
1114
1115	    krb5_get_pw_salt(context, princ, &salt);
1116
1117	    pw.data = (void *)password;
1118	    pw.length = strlen(password);
1119
1120	    krb5_data_zero(&opaque);
1121
1122	    ret = krb5_string_to_key_data_salt_opaque (context,
1123						       e,
1124						       pw,
1125						       salt,
1126						       opaque,
1127						       &key.keys.val[key.keys.len].key);
1128
1129	    /* Now set salt */
1130	    key.keys.val[key.keys.len].salt = calloc(1, sizeof(*key.keys.val[key.keys.len].salt));
1131	    if (key.keys.val[key.keys.len].salt == NULL)
1132		abort();
1133
1134	    key.keys.val[key.keys.len].salt->type = salt.salttype;
1135	    krb5_data_zero(&key.keys.val[key.keys.len].salt->salt);
1136
1137	    ret = krb5_data_copy(&key.keys.val[key.keys.len].salt->salt,
1138				 salt.saltvalue.data,
1139				 salt.saltvalue.length);
1140	    if (ret)
1141		abort();
1142
1143	    krb5_free_salt(context, salt);
1144
1145	} else {
1146	    ret = krb5_generate_random_keyblock(context, e, &key.keys.val[key.keys.len].key);
1147	}
1148	if (ret)
1149	    abort();
1150
1151	key.keys.len++;
1152
1153    });
1154    if (key.keys.len == 0) {
1155	ret = 1;
1156	createError(error, ret, CFSTR("no Kerberos enctypes found"));
1157	goto out;
1158    }
1159
1160#if 0 /* XXX */
1161    /* seal using master key */
1162    for (i = 0; i < key.keys.len; i++) {
1163	ret = hdb_seal_key_mkey(context, &key.keys.val[i], mkey);
1164	if (ret) abort();
1165    }
1166#endif
1167
1168    ASN1_MALLOC_ENCODE(hdb_keyset_aapl, data.data, data.length, &key, &size, ret);
1169    if (ret) {
1170	createError(error, ret, CFSTR("Failed to encode keyset"));
1171	goto out;
1172    }
1173    if (data.length != size)
1174	krb5_abortx(context, "internal asn.1 encoder error");
1175
1176    element = CFDataCreate(kCFAllocatorDefault, data.data, data.length);
1177    free(data.data);
1178    if (element == NULL) {
1179	ret = ENOMEM;
1180	goto out;
1181    }
1182
1183out:
1184    if (defaultenctypes)
1185	CFRelease(defaultenctypes);
1186
1187    free_hdb_keyset_aapl(&key);
1188
1189    return element;
1190}
1191
1192CFArrayRef
1193HeimODCreateSRPKeys(CFArrayRef srptype, /* XXX should be list of srp types */
1194		    CFStringRef principal,
1195		    CFTypeRef password,
1196		    unsigned long flags,
1197		    CFErrorRef *error)
1198{
1199    krb5_principal princ = NULL;
1200    krb5_context context = NULL;
1201    CFArrayRef array = NULL;
1202    char *passwdstr = NULL;
1203    krb5_error_code ret;
1204    CFDataRef element;
1205    void const *el[1];
1206    krb5_data data;
1207    size_t size = 0;
1208    hdb_srp srp;
1209
1210    memset(&srp, 0, sizeof(srp));
1211
1212    ret = krb5_init_context(&context);
1213    if (ret) {
1214	createError(error, 1, CFSTR("can't create kerberos context"));
1215	return NULL;
1216    }
1217
1218    if (CFGetTypeID(password) == CFDataGetTypeID()) {
1219
1220	passwdstr = malloc(CFDataGetLength(password) + 1);
1221	if (passwdstr == NULL) {
1222	    createError(error, ENOMEM, CFSTR("out of memory"));
1223	    goto out;
1224	}
1225
1226	memcpy(passwdstr, CFDataGetBytePtr(password), CFDataGetLength(password));
1227	passwdstr[CFDataGetLength(password)] = '\0';
1228
1229    } else if (CFGetTypeID(password) == CFStringGetTypeID()) {
1230
1231	passwdstr = cfstring2cstring(password);
1232	if (passwdstr == NULL) {
1233	    createError(error, ENOMEM, CFSTR("out of memory"));
1234	    goto out;
1235	}
1236
1237    } else {
1238	createError(error, 1, CFSTR("password not a string"));
1239	goto out;
1240    }
1241
1242    princ = krb5_principalCreateFromString(context, principal);
1243    if (princ == NULL) {
1244	createError(error, ENOMEM, CFSTR("out of memory"));
1245	goto out;
1246    }
1247
1248
1249    ret = hdb_set_srp_verifier(context,
1250			       KRB5_SRP_GROUP_RFC5054_4096_PBKDF2_SHA512,
1251			       princ,
1252			       passwdstr,
1253			       0,
1254			       &srp);
1255    if (ret) {
1256	goto out;
1257    }
1258
1259    ASN1_MALLOC_ENCODE(hdb_srp, data.data, data.length, &srp, &size, ret);
1260    free_hdb_srp(&srp);
1261    if (ret)
1262	goto out;
1263    if (size != data.length)
1264	krb5_abortx(context, "internal asn.1 encoder error");
1265
1266    element = CFDataCreate(kCFAllocatorDefault, data.data, data.length);
1267    free(data.data);
1268    if (element == NULL)
1269	goto out;
1270
1271    el[0] = element;
1272
1273    array = CFArrayCreate(kCFAllocatorDefault, el, 1, &kCFTypeArrayCallBacks);
1274    CFRelease(element);
1275    if (array == NULL) {
1276	ret = ENOMEM;
1277	goto out;
1278    }
1279
1280 out:
1281    if (context)
1282	krb5_free_context(context);
1283    if (passwdstr)
1284	free(passwdstr);
1285
1286    free_hdb_srp(&srp);
1287
1288    return array;
1289}
1290
1291
1292
1293static int
1294SetSRP(krb5_context context, ODNodeRef node, ODRecordRef datarecord,
1295       krb5_const_principal principal, CFTypeRef password,
1296       unsigned long flags,
1297       CFErrorRef *error)
1298{
1299    CFStringRef princ = NULL;
1300    CFArrayRef array = NULL;
1301    int ret;
1302
1303    princ = CFStringCreateWithkrb5_principal(context, principal);
1304    if (princ == NULL)
1305	return 1;
1306
1307    array = HeimODCreateSRPKeys(NULL, princ, password, flags, error);
1308    CFRelease(princ);
1309    if (array == NULL)
1310	return 1;
1311
1312    if ((flags & kHeimODAdminSetKeysAppendKey) == 0) {
1313	bool r = ODRecordSetValue(datarecord, kHeimSRP, array, error);
1314	if (!r) {
1315	    ret = EINVAL;
1316	    goto out;
1317	}
1318
1319	ret = 0;
1320
1321    } else {
1322	ret = EINVAL;
1323    }
1324
1325 out:
1326    if (array)
1327	CFRelease(array);
1328
1329    return ret;
1330}
1331
1332bool
1333HeimODSetVerifiers(ODNodeRef node, ODRecordRef record, CFStringRef principal, CFArrayRef types, CFTypeRef password, unsigned long flags, CFErrorRef *error)
1334{
1335    ODRecordRef datarecord = NULL;
1336    CFArrayRef array = NULL;
1337    bool res = false;
1338
1339    datarecord = copyDataRecord(node, record, NULL, NULL, NULL, NULL, error);
1340    if (datarecord == NULL)
1341	goto out;
1342
1343    array = HeimODCreateSRPKeys(types, principal, password, 0, error);
1344    if (array == NULL)
1345	goto out;
1346
1347    if (!ODRecordSetValue(datarecord, kHeimSRP, array, error))
1348	goto out;
1349
1350    if (!ODRecordSynchronize(datarecord, error))
1351	goto out;
1352
1353    res = true;
1354 out:
1355    if (datarecord)
1356	CFRelease(datarecord);
1357    if (array)
1358	CFRelease(array);
1359
1360    return res;
1361}
1362
1363int
1364HeimODSetKeys(ODNodeRef node, ODRecordRef record, CFStringRef principal, CFArrayRef enctypes, CFTypeRef password, unsigned long flags, CFErrorRef *error)
1365{
1366    krb5_context context;
1367    CFDataRef element = NULL;
1368    __block krb5_error_code ret;
1369    krb5_principal princ = NULL;
1370    ODRecordRef datarecord = NULL;
1371    char *passwordstr = NULL;
1372    bool is_server;
1373    unsigned kvno;
1374
1375    ret = krb5_init_context(&context);
1376    if (ret) {
1377	createError(error, 1, CFSTR("can't create kerberos context"));
1378	return 1;
1379    }
1380
1381    datarecord = copyDataRecord(node, record, NULL, NULL, NULL, &is_server, error);
1382    if (datarecord == NULL)
1383	goto out;
1384
1385    if (principal) {
1386	princ = krb5_principalCreateFromString(context, principal);
1387    } else if (is_server == 0) {
1388	CFStringRef name = ODRecordGetRecordName(record);
1389	char *str, *princstr;
1390
1391	str = cfstring2cstring(name);
1392	if (str == NULL) {
1393	    ret = ENOENT;
1394	    goto out;
1395	}
1396	asprintf(&princstr, "%s@%s", str, KRB5_LKDC_REALM_NAME);
1397	free(str);
1398
1399	ret = krb5_parse_name(context, princstr, &princ);
1400	free(princstr);
1401	if (ret) {
1402	    createError(error, ret, CFSTR("failed to parse principal"));
1403	    goto out;
1404	}
1405
1406    } else {
1407	CFArrayRef array;
1408	CFStringRef user;
1409
1410	array = ODRecordCopyValues(datarecord, kHeimLDAPKerberosUserName, error);
1411	if (array == NULL) {
1412	    ret = 1;
1413	    goto out;
1414	}
1415
1416	if (CFArrayGetCount(array) < 1) {
1417	    CFRelease(array);
1418	    ret = 1;
1419	    createError(error, ret, CFSTR("can't find user principal name"));
1420	    goto out;
1421	}
1422
1423	user = CFArrayGetValueAtIndex(array, 0);
1424	if (user == NULL || CFGetTypeID(user) != CFStringGetTypeID()) {
1425	    CFRelease(array);
1426	    ret = 1;
1427	    createError(error, ret, CFSTR("user principal name not a string"));
1428	    goto out;
1429	}
1430
1431	princ = krb5_principalCreateFromString(context, user);
1432
1433	CFRelease(array);
1434    }
1435    if (princ == NULL) {
1436	createError(error, ENOMEM, CFSTR("out of memory"));
1437	ret = ENOMEM;
1438	goto out;
1439    }
1440
1441
1442    /*
1443     * Covert if we have a password, otherwise we'll use a random key.
1444     */
1445
1446    if (password) {
1447	if (CFDataGetTypeID() == CFGetTypeID(password)) {
1448	    CFDataRef data = (CFDataRef)password;
1449	    passwordstr = malloc(CFDataGetLength(data) + 1);
1450	    if (passwordstr == NULL) {
1451		createError(error, ENOMEM, CFSTR("out of memory"));
1452		ret = 1;
1453		goto out;
1454	    }
1455
1456	    memcpy(passwordstr, CFDataGetBytePtr(data), CFDataGetLength(data));
1457	    passwordstr[CFDataGetLength(data)] = '\0';
1458
1459	} else if (CFStringGetTypeID() == CFGetTypeID(password)) {
1460	    passwordstr = cfstring2cstring(password);
1461	    if (passwordstr == NULL) {
1462		createError(error, ENOMEM, CFSTR("out of memory"));
1463		ret = 1;
1464		goto out;
1465	    }
1466	} else {
1467	    createError(error, EINVAL, CFSTR("invalid string type"));
1468	    ret = 1;
1469	    goto out;
1470	}
1471    }
1472
1473    kvno = last_kvno_record(datarecord, context, principal ? princ : NULL) + 1;
1474
1475    /*
1476     * If it the LKDC domain, gets go and get the realm from the
1477     * local realm database to make sure the we don't use the same
1478     * salting for every principal out there.
1479     */
1480
1481    if (krb5_principal_is_lkdc(context, princ)) {
1482	char *realm = get_lkdc_realm();
1483	if (realm == NULL) {
1484	    ret = 1;
1485	    goto out;
1486	}
1487	krb5_principal_set_realm(context, princ, realm);
1488	free(realm);
1489    }
1490
1491    /* build the new keyset */
1492    element = update_keys(context, enctypes, kvno, princ, principal != 0, passwordstr, error);
1493    if (element == NULL) {
1494	ret = 1;
1495	goto out;
1496    }
1497
1498    if ((flags & kHeimODAdminSetKeysAppendKey) == 0) {
1499	CFMutableArrayRef array;
1500
1501	array = CFArrayCreateMutable(kCFAllocatorDefault, 1,
1502				     &kCFTypeArrayCallBacks);
1503	if (array == NULL) {
1504	    ret = ENOMEM;
1505	    goto out;
1506	}
1507	CFArrayAppendValue(array, element);
1508
1509	bool r = ODRecordSetValue(datarecord, getkeykey(datarecord), array, error);
1510	CFRelease(array);
1511	if (!r) {
1512	    ret = EINVAL;
1513	    goto out;
1514	}
1515
1516    } else {
1517	bool r = ODRecordAddValue(datarecord, getkeykey(datarecord), element, error);
1518	if (!r) {
1519	    ret = EINVAL;
1520	    goto out;
1521	}
1522    }
1523
1524    if (password && !is_server) {
1525	ret = SetSRP(context, node, datarecord, princ, password, flags, error);
1526	if (ret)
1527	    goto out;
1528    }
1529
1530    if (!ODRecordSynchronize(datarecord, error))
1531	ret = EINVAL;
1532
1533 out:
1534    if (princ)
1535	krb5_free_principal(context, princ);
1536    if (passwordstr) {
1537	memset(passwordstr, 0, strlen(passwordstr));
1538	free(passwordstr);
1539    }
1540    if (datarecord)
1541	CFRelease(datarecord);
1542    if (context)
1543	krb5_free_context(context);
1544    if (element)
1545	CFRelease(element);
1546
1547    return ret;
1548}
1549
1550static void
1551delete_enctypes(krb5_context context, CFArrayRef enctypes, CFMutableArrayRef keyset)
1552{
1553    CFIndex n, m, count;
1554    krb5_enctype *etlist;
1555    unsigned o;
1556
1557    count = CFArrayGetCount(enctypes);
1558    if (count == 0)
1559	return;
1560
1561    etlist = calloc(count + 1, sizeof(*etlist));
1562    if (etlist == NULL)
1563	return;
1564
1565    for (n = m = 0; n < count; n++) {
1566	CFStringRef str = CFArrayGetValueAtIndex(enctypes, n);
1567	if (CFGetTypeID(str) != CFStringGetTypeID())
1568	    continue;
1569	etlist[m] = find_enctype(context, str);
1570	if (etlist[m] != ETYPE_NULL)
1571	    m++;
1572    }
1573    if (m == 0) {
1574	free(etlist);
1575	return;
1576    }
1577
1578    count = CFArrayGetCount(keyset);
1579
1580    for (n = 0; n < count; n++) {
1581	krb5_error_code ret;
1582	hdb_keyset_aapl key;
1583	size_t sz;
1584	int found = 0;
1585
1586	CFDataRef el = CFArrayGetValueAtIndex(keyset, n);
1587	if (CFGetTypeID(el) != CFDataGetTypeID())
1588	    continue;
1589
1590	ret = decode_hdb_keyset_aapl(CFDataGetBytePtr(el), CFDataGetLength(el), &key, &sz);
1591	if (ret)
1592	    continue;
1593
1594	for (m = 0; etlist[m] != ETYPE_NULL; m++) {
1595	    for (o = 0; o < key.keys.len; o++) {
1596		if (key.keys.val[o].key.keytype == etlist[m]) {
1597		    /* delete key and shift down keys */
1598		    free_Key(&key.keys.val[o]);
1599		    if (o + 1 != key.keys.len)
1600			memmove(&key.keys.val[o], &key.keys.val[o + 1], sizeof(key.keys.val[o]) * (key.keys.len - o - 1));
1601		    found = 1;
1602		    key.keys.len--;
1603		    o--;
1604		    if (key.keys.len == 0)
1605			free(key.keys.val);
1606		}
1607	    }
1608	}
1609	if (found && key.keys.len == 0) {
1610	    CFArrayRemoveValueAtIndex(keyset, n);
1611	    count--;
1612	    n--;
1613	} else if (found) {
1614	    /* found a key, re-encode and put back in the array */
1615	    size_t length;
1616	    void *data;
1617
1618	    ASN1_MALLOC_ENCODE(hdb_keyset_aapl, data, length, &key, &sz, ret);
1619	    if (ret) {
1620            free(etlist);
1621		return;
1622        }
1623	    if (length != sz)
1624		krb5_abortx(context, "internal asn1 error");
1625
1626	    CFDataRef d = CFDataCreate(NULL, data, length);
1627	    if (d == NULL)
1628		abort();
1629
1630	    CFArraySetValueAtIndex(keyset, n, d);
1631	    CFRelease(d);
1632	}
1633	free_hdb_keyset_aapl(&key);
1634    }
1635
1636    free(etlist);
1637    return;
1638}
1639
1640CFArrayRef
1641HeimODModifyKeys(CFArrayRef prevKeyset,
1642		 CFStringRef principal,
1643		 CFArrayRef enctypes,
1644		 CFTypeRef password,
1645		 unsigned long flags,
1646		 CFErrorRef *error)
1647{
1648    CFMutableArrayRef keyset = NULL;
1649    krb5_context context = NULL;
1650    char *passwordstr = NULL;
1651    krb5_principal princ = NULL;
1652    CFDataRef element = NULL;
1653    krb5_error_code ret;
1654    int32_t kvno;
1655
1656    if (error)
1657	*error = NULL;
1658
1659    ret = krb5_init_context(&context);
1660    if (ret) {
1661	createError(error, 1, CFSTR("can't create kerberos context"));
1662	return NULL;
1663    }
1664
1665    /* check if we are deleting the password */
1666
1667    if (flags & kHeimODAdminDeleteEnctypes) {
1668	if (prevKeyset == NULL)
1669	    goto out;
1670
1671	keyset = CFArrayCreateMutableCopy(NULL, 0, prevKeyset);
1672	if (keyset == NULL)
1673	    goto out;
1674
1675	delete_enctypes(context, enctypes, keyset);
1676
1677    } else {
1678
1679	princ = krb5_principalCreateFromString(context, principal);
1680	if (princ == NULL) {
1681	    createError(error, ENOMEM, CFSTR("out of memory"));
1682	    goto out;
1683	}
1684
1685        if (password) {
1686            if (CFDataGetTypeID() == CFGetTypeID(password)) {
1687                CFDataRef data = (CFDataRef)password;
1688                passwordstr = malloc(CFDataGetLength(data) + 1);
1689                if (passwordstr == NULL) {
1690                    createError(error, ENOMEM, CFSTR("out of memory"));
1691                    goto out;
1692                }
1693
1694                memcpy(passwordstr, CFDataGetBytePtr(data), CFDataGetLength(data));
1695                passwordstr[CFDataGetLength(data)] = '\0';
1696
1697            } else if (CFStringGetTypeID() == CFGetTypeID(password)) {
1698                passwordstr = cfstring2cstring(password);
1699                if (passwordstr == NULL) {
1700                    createError(error, ENOMEM, CFSTR("out of memory"));
1701                    goto out;
1702                }
1703            } else {
1704                createError(error, EINVAL, CFSTR("invalid string type"));
1705                goto out;
1706            }
1707        }
1708
1709	if (prevKeyset) {
1710	    kvno = last_kvno_array(prevKeyset, context, NULL);
1711	    kvno++;
1712	} else {
1713	    kvno = 1;
1714	}
1715
1716	element = update_keys(context, enctypes, kvno, princ, 0, passwordstr, error);
1717	if (element == NULL)
1718	    goto out;
1719
1720	if (prevKeyset && (flags & kHeimODAdminSetKeysAppendKey) != 0)
1721	    keyset = CFArrayCreateMutableCopy(NULL, 0, prevKeyset);
1722	else
1723	    keyset = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1724	if (keyset == NULL)
1725	    goto out;
1726
1727	CFArrayAppendValue(keyset, element);
1728    }
1729
1730out:
1731    if (passwordstr) {
1732	memset(passwordstr, 0, strlen(passwordstr));
1733	free(passwordstr);
1734    }
1735    if (element)
1736	CFRelease(element);
1737    if (princ)
1738	krb5_free_principal(context, princ);
1739    if (context)
1740	krb5_free_context(context);
1741
1742    return keyset;
1743}
1744
1745CFStringRef
1746HeimODKeysetToString(CFDataRef element, CFErrorRef *error)
1747{
1748    krb5_context context;
1749    krb5_error_code ret;
1750    CFMutableStringRef str;
1751    hdb_keyset_aapl keyset;
1752    size_t sz, n;
1753
1754    ret = krb5_init_context(&context);
1755    if (ret) {
1756	createError(error, ret, CFSTR("failed to create context"));
1757	return NULL;
1758    }
1759
1760    ret = decode_hdb_keyset_aapl(CFDataGetBytePtr(element),
1761			    CFDataGetLength(element),
1762			    &keyset, &sz);
1763    if (ret)
1764	return NULL;
1765
1766    str = CFStringCreateMutable(NULL, 0);
1767    if (str == NULL)
1768	goto out;
1769
1770    CFStringAppendFormat(str, NULL, CFSTR("key kvno %d"), keyset.kvno);
1771
1772    if (keyset.principal) {
1773	char *p = NULL;
1774	if (krb5_unparse_name(context, keyset.principal, &p) != 0) {
1775	    CFRelease(str); str = NULL;
1776	    goto out;
1777	}
1778
1779	CFStringAppendFormat(str, NULL, CFSTR(" principal: %s"), p);
1780	free(p);
1781    }
1782
1783    for (n = 0; n < keyset.keys.len; n++) {
1784	Key *key = &keyset.keys.val[n];
1785	size_t i;
1786
1787	CFStringAppendFormat(str, NULL, CFSTR(" [key# %d:"), (int)n);
1788	if (key->mkvno)
1789	    CFStringAppendFormat(str, NULL, CFSTR(" masterkey %d:"), (int)*key->mkvno);
1790	if (key->salt)
1791	    CFStringAppendFormat(str, NULL, CFSTR(" salt %d:"), (int)key->salt->type);
1792
1793	char *enctype = NULL;
1794	(void)krb5_enctype_to_string(context, key->key.keytype, &enctype);
1795	CFStringAppendFormat(str, NULL, CFSTR(" enctype %s(%d):"), enctype, key->key.keytype);
1796	free(enctype);
1797
1798	for(i = 0; i < key->key.keyvalue.length; i++)
1799	    CFStringAppendFormat(str, NULL, CFSTR("%02x"), ((uint8_t *)key->key.keyvalue.data)[i]);
1800
1801	CFStringAppendFormat(str, NULL, CFSTR("]"));
1802    }
1803
1804
1805out:
1806    if (context)
1807	krb5_free_context(context);
1808
1809    free_hdb_keyset_aapl(&keyset);
1810
1811    return str;
1812}
1813
1814/*
1815 *
1816 */
1817
1818CFArrayRef
1819HeimODCopyDefaultEnctypes(CFErrorRef *error)
1820{
1821    CFMutableArrayRef enctypes;
1822
1823    enctypes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1824    if (enctypes == NULL) {
1825	createError(error, ENOMEM, CFSTR("Can't create enctype array ref"));
1826	return NULL;
1827    }
1828
1829    CFArrayAppendValue(enctypes, CFSTR("aes256-cts-hmac-sha1-96"));
1830    CFArrayAppendValue(enctypes, CFSTR("aes128-cts-hmac-sha1-96"));
1831    CFArrayAppendValue(enctypes, CFSTR("des3-cbc-sha1"));
1832
1833    return enctypes;
1834}
1835
1836
1837/*
1838 *
1839 */
1840
1841int
1842HeimODAddSubjectAltCertName(ODNodeRef node, ODRecordRef record, CFStringRef subject, CFStringRef issuer, CFErrorRef *error)
1843{
1844    abort();
1845    *error = NULL;
1846    return 0;
1847}
1848
1849
1850#ifdef PKINIT
1851static SecCertificateRef
1852find_ta(SecCertificateRef incert)
1853{
1854    SecTrustResultType resultType = kSecTrustResultDeny;
1855    SecPolicyRef policy = NULL;
1856    SecTrustRef trust = NULL;
1857    SecCertificateRef cert = NULL;
1858    CFMutableArrayRef inArray = NULL;
1859    OSStatus ret;
1860
1861    policy = SecPolicyCreateBasicX509();
1862
1863    inArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1864
1865    CFArrayAppendValue(inArray, incert);
1866
1867    ret = SecTrustCreateWithCertificates(inArray, policy, &trust);
1868    if (ret)
1869	goto out;
1870
1871    ret = SecTrustEvaluate(trust, &resultType);
1872    if (ret)
1873	goto out;
1874
1875    if (resultType != kSecTrustResultUnspecified && resultType != kSecTrustResultProceed)
1876	goto out;
1877
1878    CFIndex certCount = SecTrustGetCertificateCount(trust);
1879    if (certCount <= 0)
1880	goto out;
1881
1882    cert = SecTrustGetCertificateAtIndex(trust, certCount - 1);
1883
1884    CFRetain(cert);
1885
1886 out:
1887    if (policy)
1888	CFRelease(policy);
1889    if (trust)
1890	CFRelease(trust);
1891    if (inArray)
1892	CFRelease(inArray);
1893
1894    return cert;
1895}
1896#endif
1897
1898/*
1899 *
1900 */
1901
1902int
1903HeimODAddCertificate(ODNodeRef node, ODRecordRef record, SecCertificateRef ref, CFErrorRef *error)
1904{
1905#ifdef PKINIT
1906    CFStringRef leaf = NULL, anchor = NULL;
1907    SecCertificateRef ta = NULL;
1908    hx509_name subject = NULL, taname = NULL;
1909    char *subjectstr = NULL, *tastr = NULL;
1910    hx509_context context = NULL;
1911    CFDataRef data = NULL;
1912    hx509_cert cert = NULL;
1913    CFStringRef ace = NULL;
1914    int ret;
1915
1916    ta = find_ta(ref);
1917    if (ta == NULL) {
1918	ret = EINVAL;
1919	createError(error, ret, CFSTR("Failed to find trust anchor"));
1920	goto out;
1921    }
1922
1923    ret = hx509_context_init(&context);
1924    if (ret) {
1925	ret = EINVAL;
1926	createError(error, ret, CFSTR("Failed to create hx509 context"));
1927	goto out;
1928    }
1929
1930    data = SecCertificateCopyData(ref);
1931    if (data == NULL) {
1932	ret = ENOMEM;
1933	createError(error, ret, CFSTR("out of memory"));
1934	goto out;
1935    }
1936
1937    ret = hx509_cert_init_data(context, CFDataGetBytePtr(data), CFDataGetLength(data), &cert);
1938    CFRelease(data);
1939    if (ret) {
1940	createError(error, ret, CFSTR("failed to decode certificate"));
1941	goto out;
1942    }
1943
1944    ret = hx509_cert_get_subject(cert, &subject);
1945    hx509_cert_free(cert);
1946    if (ret) {
1947	createError(error, ret, CFSTR("out of memory"));
1948	goto out;
1949    }
1950
1951    data = SecCertificateCopyData(ta);
1952    if (data == NULL) {
1953	ret = ENOMEM;
1954	createError(error, ret, CFSTR("out of memory"));
1955	goto out;
1956    }
1957
1958    ret = hx509_cert_init_data(context, CFDataGetBytePtr(data), CFDataGetLength(data), &cert);
1959    CFRelease(data);
1960    if (ret) {
1961	createError(error, ret, CFSTR("failed to decode certificate"));
1962	goto out;
1963    }
1964
1965    ret = hx509_cert_get_subject(cert, &taname);
1966    hx509_cert_free(cert);
1967    if (ret) {
1968	createError(error, ret, CFSTR("out of memory"));
1969	goto out;
1970    }
1971
1972    ret = hx509_name_to_string(subject, &subjectstr);
1973    if (ret) {
1974	createError(error, ret, CFSTR("out of memory"));
1975	goto out;
1976    }
1977    ret = hx509_name_to_string(taname, &tastr);
1978    if (ret) {
1979	createError(error, ret, CFSTR("out of memory"));
1980	goto out;
1981    }
1982
1983    leaf = CFStringCreateWithCString(kCFAllocatorDefault, subjectstr, kCFStringEncodingUTF8);
1984    if (leaf == NULL) {
1985	ret = ENOMEM;
1986	createError(error, ret, CFSTR("out of memory"));
1987	goto out;
1988    }
1989
1990    anchor = CFStringCreateWithCString(kCFAllocatorDefault, tastr, kCFStringEncodingUTF8);
1991    if (anchor == NULL) {
1992	ret = ENOMEM;
1993	createError(error, ret, CFSTR("out of memory"));
1994	goto out;
1995    }
1996
1997    if (!HeimODAddCertificateSubjectAndTrustAnchor(node, record, leaf, anchor, error))
1998	ret = EINVAL;
1999
2000 out:
2001    if (leaf)
2002	CFRelease(leaf);
2003    if (anchor)
2004	CFRelease(anchor);
2005    if (taname)
2006	hx509_name_free(&taname);
2007    if (subject)
2008	hx509_name_free(&subject);
2009    if (tastr)
2010	free(tastr);
2011    if (subjectstr)
2012	free(subjectstr);
2013    if (ace)
2014	CFRelease(ace);
2015    if (ta)
2016	CFRelease(ta);
2017    if (context)
2018	hx509_context_free(&context);
2019    return ret;
2020#else
2021    return EINVAL;
2022#endif
2023}
2024
2025/*
2026 *
2027 */
2028
2029int
2030HeimODAddSubjectAltCertSHA1Digest(ODNodeRef node, ODRecordRef record, CFDataRef hash, CFErrorRef *error)
2031{
2032    abort();
2033    *error = NULL;
2034    return 0;
2035}
2036
2037/*
2038 *
2039 */
2040
2041CFArrayRef
2042HeimODCopySubjectAltNames(ODNodeRef node, ODRecordRef record, CFErrorRef *error)
2043{
2044    abort();
2045    *error = NULL;
2046    return 0;
2047}
2048
2049/*
2050 *
2051 */
2052
2053int
2054HeimODRemoveSubjectAltElement(ODNodeRef node, ODRecordRef record, CFTypeRef element, CFErrorRef *error)
2055{
2056    abort();
2057    *error = NULL;
2058    return 0;
2059}
2060
2061int
2062HeimODAddCertificateSubjectAndTrustAnchor(ODNodeRef node, ODRecordRef record, CFStringRef leafSubject, CFStringRef trustAnchorSubject, CFErrorRef *error)
2063{
2064    CFArrayRef array;
2065    CFStringRef ace;
2066
2067    if (error)
2068	*error = NULL;
2069
2070    ace = CFStringCreateWithFormat(NULL, 0, CFSTR("X509:<T>%@<S>%@"), trustAnchorSubject, leafSubject);
2071    if (ace == NULL) {
2072	createError(error, ENOMEM, CFSTR("out of memory"));
2073	return 1;
2074    }
2075
2076    /* filter out dups */
2077    array = ODRecordCopyValues(record, kHeimASI, NULL);
2078    if (array) {
2079	__block int found_match = 0;
2080	arrayapplyblock(array, CFRangeMake(0, CFArrayGetCount(array)), ^(const void *value) {
2081	    if (CFGetTypeID(value) != CFStringGetTypeID())
2082		return;
2083
2084	    if (CFStringCompare(value, ace, 0) == kCFCompareEqualTo)
2085		found_match = 1;
2086	    });
2087	CFRelease(array);
2088	if (found_match) {
2089	    CFRelease(ace);
2090	    return 0;
2091	}
2092    }
2093
2094    bool r = ODRecordAddValue(record, kHeimASI, ace, error);
2095    CFRelease(ace);
2096    if (!r)
2097	return 1;
2098
2099    if (!ODRecordSynchronize(record, error))
2100	return 1;
2101
2102    return 0;
2103}
2104
2105int
2106HeimODRemoveCertificateSubjectAndTrustAnchor(ODNodeRef node, ODRecordRef record, CFStringRef leafSubject, CFStringRef trustAnchorSubject, CFErrorRef *error)
2107{
2108    CFStringRef ace;
2109    CFErrorRef e = NULL;
2110
2111    if (error)
2112	*error = NULL;
2113
2114    ace = CFStringCreateWithFormat(NULL, 0, CFSTR("X509:<T>%@<S>%@"), trustAnchorSubject, leafSubject);
2115    if (ace == NULL) {
2116	createError(error, ENOMEM, CFSTR("out of memory"));
2117	return 1;
2118    }
2119
2120    bool r = ODRecordRemoveValue(record, kHeimASI, ace, &e);
2121    CFRelease(ace);
2122    if (!r && e == NULL)
2123	return 0; /* entry didn't exists */
2124    else if (!r) {
2125	if (error)
2126	    *error = e;
2127	else
2128	    CFRelease(e);
2129	return 1;
2130    }
2131
2132    if (!ODRecordSynchronize(record, error))
2133	return 1;
2134
2135    return 0;
2136}
2137
2138int
2139HeimODAddAppleIDAlias(ODNodeRef node, ODRecordRef record, CFStringRef alias, CFErrorRef *error)
2140{
2141    CFArrayRef array;
2142
2143    if (error)
2144	*error = NULL;
2145
2146    if (is_record_server(record)) {
2147	createError(error, EINVAL, CFSTR("AppleID alias not supported for Server Mode"));
2148	return 1;
2149    }
2150
2151    array = ODRecordCopyValues(record, kODAttributeTypeRecordName, NULL);
2152    if (array) {
2153	__block int found_match = 0;
2154	arrayapplyblock(array, CFRangeMake(0, CFArrayGetCount(array)), ^(const void *value) {
2155	    if (CFGetTypeID(value) != CFStringGetTypeID())
2156		return;
2157
2158	    if (CFStringCompare(value, alias, 0) == kCFCompareEqualTo)
2159		found_match = 1;
2160	    });
2161	CFRelease(array);
2162	if (found_match)
2163	    return 0;
2164    }
2165
2166    bool r = ODRecordAddValue(record, kODAttributeTypeRecordName, alias, error);
2167    if (!r)
2168	return 1;
2169
2170    if (!ODRecordSynchronize(record, error))
2171	return 1;
2172
2173    return 0;
2174}
2175
2176int
2177HeimODRemoveAppleIDAlias(ODNodeRef node, ODRecordRef record, CFStringRef alias, CFErrorRef *error)
2178{
2179    CFErrorRef e = NULL;
2180
2181    if (error)
2182	*error = NULL;
2183
2184    if (is_record_server(record)) {
2185	createError(error, EINVAL, CFSTR("AppleID alias not supported for Server Mode"));
2186	return 1;
2187    }
2188
2189    bool r = ODRecordRemoveValue(record, kODAttributeTypeRecordName, alias, &e);
2190    if (!r && e == NULL)
2191	return 0; /* entry didn't exists */
2192    else if (!r) {
2193	if (error)
2194	    *error = e;
2195	else
2196	    CFRelease(e);
2197	return 1;
2198    }
2199
2200    if (!ODRecordSynchronize(record, error))
2201	return 1;
2202
2203    return 0;
2204}
2205
2206struct dumploadkeys {
2207    CFStringRef dumpkey;
2208    CFStringRef clientkey;
2209    CFStringRef serverkey;
2210    unsigned long flags;
2211#define LOAD_SERVER		1
2212#define LOAD_CLIENT		2
2213#define LOAD_SERVER_APPEND	4
2214#define DUMP			8
2215    bool (*load)(krb5_context, ODRecordRef, CFDictionaryRef, CFStringRef, CFTypeRef, unsigned long flags, CFErrorRef *);
2216    CFArrayRef (*dump_record)(ODRecordRef, CFArrayRef, CFErrorRef *);
2217    CFArrayRef (*dump_entry)(krb5_context, hdb_entry *, CFErrorRef *);
2218};
2219
2220static bool
2221load_simple(krb5_context context, ODRecordRef record, CFDictionaryRef dict, CFStringRef key, CFTypeRef data, unsigned long flags, CFErrorRef *error)
2222{
2223    bool ret = false;
2224
2225    if (flags & LOAD_SERVER_APPEND) {
2226	CFIndex n, num = CFArrayGetCount(data);
2227	for (n = 0; n < num; n++) {
2228	    ret = ODRecordAddValue(record, key, CFArrayGetValueAtIndex(data, n), error);
2229	    if (!ret)
2230		return false;
2231	}
2232    } else {
2233	ret = ODRecordSetValue(record, key, data, error);
2234    }
2235    if (!ret && *error == NULL)
2236	createError(error, 1, CFSTR("Failed to load the key %@ with value %@"), key, data);
2237    return ret;
2238}
2239
2240
2241static CFArrayRef
2242dump_simple(ODRecordRef record, CFArrayRef val, CFErrorRef *error)
2243{
2244    return CFRetain(val);
2245}
2246
2247static CFArrayRef
2248edump_principal(krb5_context context, hdb_entry *entry, CFErrorRef *error)
2249{
2250    CFArrayRef array;
2251    CFStringRef value;
2252
2253    value = CFStringCreateWithkrb5_principal(context, entry->principal);
2254    if (value == NULL)
2255	return NULL;
2256
2257    array = CFArrayCreate(kCFAllocatorDefault, (const void **)&value, 1, &kCFTypeArrayCallBacks);
2258    CFRelease(value);
2259
2260    return array;
2261}
2262
2263static CFArrayRef
2264edump_flags(krb5_context context, hdb_entry *entry, CFErrorRef *error)
2265{
2266    CFArrayRef array = NULL;
2267    CFStringRef value;
2268
2269    value = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), HDBFlags2int(entry->flags));
2270    if (value == NULL)
2271	return NULL;
2272
2273    array = CFArrayCreate(kCFAllocatorDefault, (const void **)&value, 1, &kCFTypeArrayCallBacks);
2274    CFRelease(value);
2275
2276    return array;
2277}
2278
2279static bool
2280load_keys(krb5_context context, ODRecordRef record, CFDictionaryRef dict, CFStringRef key, CFTypeRef data, unsigned long flags, CFErrorRef *error)
2281{
2282    CFMutableArrayRef newdata = NULL;
2283    krb5_principal p = NULL;
2284    int ret;
2285    bool b;
2286
2287    /* need to make sure that keys are propperly mapped */
2288    if (flags & LOAD_SERVER_APPEND) {
2289	CFIndex n, num;
2290	hdb_keyset_aapl keyset;
2291
2292	memset(&keyset, 0, sizeof(keyset));
2293
2294	newdata = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2295	if (newdata == NULL)
2296	    return false;
2297
2298	CFArrayRef aprinc = CFDictionaryGetValue(dict, CFSTR("KerberosPrincipal"));
2299	if (aprinc == NULL || CFGetTypeID(aprinc) != CFArrayGetTypeID()) {
2300	    createError(error, 1, CFSTR("Failed to find principal in load dictionary"));
2301	    b = false;
2302	    goto out;
2303	}
2304	CFStringRef princ = CFArrayGetValueAtIndex(aprinc, 0);
2305	if (princ == NULL || CFGetTypeID(princ) != CFStringGetTypeID()) {
2306	    createError(error, 1, CFSTR("Failed to find principal in load dictionary"));
2307	    b = false;
2308	    goto out;
2309	}
2310	char *str = cfstring2cstring(princ);
2311	if (str == NULL) {
2312	    createError(error, ENOMEM, CFSTR("out of memory"));
2313	    b = false;
2314	    goto out;
2315	}
2316
2317	ret = krb5_parse_name(context, str, &p);
2318	free(str);
2319	if (ret) {
2320	    createError(error, ret, CFSTR("failed to parse name"));
2321	    b = false;
2322	    goto out;
2323	}
2324
2325
2326	if (CFGetTypeID(data) != CFArrayGetTypeID())
2327	    abort();
2328
2329	num = CFArrayGetCount(data);
2330
2331	for (n = 0; n < num; n++) {
2332	    CFDataRef d = CFArrayGetValueAtIndex(data, n);
2333
2334	    ret = decode_hdb_keyset_aapl(CFDataGetBytePtr(d), CFDataGetLength(d), &keyset, NULL);
2335	    if (ret) {
2336		createError(error, 1, CFSTR("failed to decode hdb_keyset"));
2337		b = false;
2338		goto out;
2339	    }
2340
2341	    if (keyset.principal == NULL) {
2342		size_t len = 0, size = 0;
2343		void *to;
2344
2345		(void)krb5_copy_principal(context, p, &keyset.principal);
2346
2347		ASN1_MALLOC_ENCODE(hdb_keyset_aapl, to, size, &keyset, &len, ret);
2348		if (ret) {
2349		    free_hdb_keyset_aapl(&keyset);
2350		    createError(error, ret, CFSTR("failed to parse name"));
2351		    b = false;
2352		    goto out;
2353		}
2354		if (len != size)
2355		    abort();
2356
2357		CFDataRef keydata = CFDataCreate(NULL, to, len);
2358		free(to);
2359		free_hdb_keyset_aapl(&keyset);
2360		if (keydata == NULL) {
2361		    createError(error, ENOMEM, CFSTR("out of memory"));
2362		    b = false;
2363		    goto out;
2364		}
2365		CFArrayAppendValue(newdata, keydata);
2366		CFRelease(keydata);
2367	    } else {
2368		free_hdb_keyset_aapl(&keyset);
2369		CFArrayAppendValue(newdata, d);
2370	    }
2371	}
2372	data = newdata;
2373    }
2374
2375    b = load_simple(context, record, dict, key, data, flags, error);
2376out:
2377    if (p)
2378	krb5_free_principal(context, p);
2379    if (newdata)
2380	CFRelease(newdata);
2381    return b;
2382}
2383
2384
2385static CFArrayRef
2386edump_keys(krb5_context context, hdb_entry *entry, CFErrorRef *error)
2387{
2388    CFArrayRef array = NULL;
2389    krb5_error_code ret;
2390    krb5_data data;
2391    CFDataRef value;
2392    hdb_keyset_aapl key;
2393    size_t size = 0;
2394
2395    key.kvno = entry->kvno;
2396    key.keys.len = entry->keys.len;
2397    key.keys.val = entry->keys.val;
2398    key.principal = NULL;
2399
2400    ASN1_MALLOC_ENCODE(hdb_keyset_aapl, data.data, data.length, &key, &size, ret);
2401    if (ret)
2402	return NULL;
2403    if (data.length != size)
2404	krb5_abortx(context, "internal asn.1 encoder error");
2405
2406    value = CFDataCreate(kCFAllocatorDefault, data.data, data.length);
2407    free(data.data);
2408    if (value == NULL)
2409	return NULL;
2410
2411    array = CFArrayCreate(kCFAllocatorDefault, (const void **)&value, 1, &kCFTypeArrayCallBacks);
2412    CFRelease(value);
2413
2414    return array;
2415}
2416
2417
2418struct dumploadkeys dlkeys[] = {
2419    {
2420	CFSTR("KerberosPrincipal"),
2421	kHeimODKerberosUserName,
2422	kHeimLDAPKerberosUserName,
2423	LOAD_SERVER | DUMP,
2424	load_simple,
2425	dump_simple,
2426	edump_principal
2427    },
2428    {
2429	CFSTR("KerberosPrincipal"),
2430	kHeimODKerberosUserName,
2431	kHeimLDAPKerberosServerName,
2432	LOAD_SERVER | LOAD_SERVER_APPEND,
2433	load_simple,
2434	dump_simple,
2435	edump_principal
2436    },
2437    {
2438	CFSTR("KerberosServerPrincipal"),
2439	kHeimODKerberosServerName,
2440	kHeimLDAPKerberosServerName,
2441	LOAD_SERVER | LOAD_CLIENT | DUMP,
2442	load_simple,
2443	dump_simple,
2444	edump_principal
2445    },
2446    {
2447	CFSTR("KerberosFlags"),
2448	kHeimODKerberosFlags,
2449	kHeimLDAPKerberosFlags,
2450	LOAD_CLIENT | LOAD_SERVER | DUMP,
2451	load_simple,
2452	dump_simple,
2453	edump_flags
2454    },
2455    {
2456	CFSTR("KerberosKeys"),
2457	kHeimODKerberosKeys,
2458	kHeimLDAPKerberosKeys,
2459	LOAD_CLIENT | LOAD_SERVER | LOAD_SERVER_APPEND | DUMP,
2460	load_keys,
2461	dump_simple,
2462	edump_keys
2463    }
2464};
2465
2466
2467CFDictionaryRef
2468HeimODDumpRecord(ODNodeRef node, ODRecordRef record, CFStringRef principal, CFErrorRef *error)
2469{
2470    ODRecordRef datarecord;
2471    CFMutableDictionaryRef dict;
2472    bool is_server;
2473    size_t n;
2474
2475    if (error == NULL)
2476	abort();
2477
2478    dict = CFDictionaryCreateMutable(NULL, 0,
2479				     &kCFTypeDictionaryKeyCallBacks,
2480				     &kCFTypeDictionaryValueCallBacks);
2481    if (dict == NULL) {
2482	createError(error, ENOMEM, CFSTR("out of memory"));
2483	return NULL;
2484    }
2485
2486    CFDictionaryAddValue(dict, CFSTR("version"), CFSTR("1"));
2487
2488    datarecord = copyDataRecord(node, record, NULL, NULL, NULL, &is_server, error);
2489    if (datarecord == NULL) {
2490	CFRelease(dict); dict = NULL;
2491	goto out;
2492    }
2493
2494    for (n = 0; n < sizeof(dlkeys) / sizeof(dlkeys[0]); n++) {
2495	CFStringRef key = is_server ? dlkeys[n].serverkey : dlkeys[n].clientkey;
2496	CFArrayRef values;
2497
2498	if ((dlkeys[n].flags & DUMP) == 0)
2499	    continue;
2500
2501	values = ODRecordCopyValues(datarecord, key, NULL);
2502	if (values == NULL)
2503	    continue;
2504
2505	CFArrayRef v = dlkeys[n].dump_record(datarecord, values, error);
2506	if (v) {
2507	    CFDictionaryAddValue(dict, dlkeys[n].dumpkey, v);
2508	    CFRelease(v);
2509	}
2510
2511	CFRelease(values);
2512	if (*error) {
2513	    CFRelease(dict); dict = NULL;
2514	    break;
2515	}
2516    }
2517
2518 out:
2519    if (datarecord)
2520	CFRelease(datarecord);
2521
2522    return dict;
2523}
2524
2525bool
2526HeimODLoadRecord(ODNodeRef node, ODRecordRef record, CFDictionaryRef dict,
2527		 unsigned long flags, CFErrorRef *error)
2528{
2529    unsigned long loadflags = 0;
2530    ODRecordRef datarecord;
2531    bool is_server;
2532    bool ret = false;
2533    size_t n;
2534    krb5_error_code status;
2535    krb5_context context;
2536
2537    if (error == NULL)
2538	abort();
2539
2540    status = krb5_init_context(&context);
2541    if (status) {
2542	createError(error, status, CFSTR("can't create kerberos context"));
2543	return false;
2544    }
2545
2546    datarecord = copyDataRecord(node, record, NULL, NULL, NULL, &is_server, error);
2547    if (datarecord == NULL)
2548	goto out;
2549
2550    loadflags |= is_server ? LOAD_SERVER : LOAD_CLIENT;
2551    if (flags & kHeimODAdminLoadAsAppend)
2552	loadflags |= LOAD_SERVER_APPEND;
2553
2554    for (n = 0; n < sizeof(dlkeys) / sizeof(dlkeys[0]); n++) {
2555	CFStringRef key = is_server ? dlkeys[n].serverkey : dlkeys[n].clientkey;
2556	CFTypeRef values;
2557
2558	if ((dlkeys[n].flags & loadflags) != loadflags)
2559	    continue;
2560
2561	values = CFDictionaryGetValue(dict, dlkeys[n].dumpkey);
2562	if (values == NULL)
2563	    continue;
2564
2565	ret = dlkeys[n].load(context, datarecord, dict, key, values, loadflags, error);
2566	if (!ret)
2567	    goto out;
2568    }
2569
2570    ret = ODRecordSynchronize(record, error);
2571    if (!ret) {
2572	if (*error == NULL)
2573	    createError(error, 1, CFSTR("Failed to syncronize the record"));
2574	goto out;
2575    }
2576
2577    ret = true;
2578
2579 out:
2580    if (context)
2581	krb5_free_context(context);
2582    if (datarecord)
2583	CFRelease(datarecord);
2584
2585    return ret;
2586}
2587
2588CFDictionaryRef
2589HeimODDumpHdbEntry(struct hdb_entry *entry, CFErrorRef *error)
2590{
2591    CFMutableDictionaryRef dict = NULL;
2592    krb5_context context;
2593    krb5_error_code ret;
2594    size_t n;
2595
2596    if (error == NULL)
2597	abort();
2598
2599    ret = krb5_init_context(&context);
2600    if (ret) {
2601	createError(error, 1, CFSTR("can't create kerberos context"));
2602	goto out;
2603    }
2604
2605    dict = CFDictionaryCreateMutable(NULL, 0,
2606				     &kCFTypeDictionaryKeyCallBacks,
2607				     &kCFTypeDictionaryValueCallBacks);
2608    if (dict == NULL) {
2609	createError(error, ENOMEM, CFSTR("out of memory"));
2610	goto out;
2611    }
2612
2613    CFDictionaryAddValue(dict, CFSTR("version"), CFSTR("1"));
2614
2615    for (n = 0; n < sizeof(dlkeys) / sizeof(dlkeys[0]); n++) {
2616
2617	if ((dlkeys[n].flags & DUMP) == 0)
2618	    continue;
2619
2620	CFArrayRef v = dlkeys[n].dump_entry(context, entry, error);
2621	if (v) {
2622	    CFDictionaryAddValue(dict, dlkeys[n].dumpkey, v);
2623	    CFRelease(v);
2624	}
2625
2626	if (*error) {
2627	    CFRelease(dict); dict = NULL;
2628	    break;
2629	}
2630    }
2631
2632 out:
2633    krb5_free_context(context);
2634
2635    return dict;
2636}
2637