1/*
2 * Copyright (c) 2009 Kungliga Tekniska H�gskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2009 - 2011 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
37#include "hdb_locl.h"
38#include <heimbase.h>
39#include <hx509.h>
40#include <base64.h>
41#include <rfc2459_asn1.h>
42#include <ifaddrs.h>
43#include <heimbase.h>
44#include <roken.h>
45
46#ifdef HAVE_OPENDIRECTORY
47
48#include <CoreFoundation/CoreFoundation.h>
49#include <OpenDirectory/OpenDirectory.h>
50#ifdef __APPLE_PRIVATE__
51#include <OpenDirectory/OpenDirectoryPriv.h>
52#include <DirectoryServer/DirectoryServer.h>
53#include <dlfcn.h>
54#endif
55#include <SystemConfiguration/SCPreferences.h>
56
57struct iter_ctx {
58    CFIndex idx;
59    CFArrayRef odRecordArray;
60} iter;
61
62struct hdb_od;
63
64typedef krb5_error_code (*hod_locate_record)(krb5_context, struct hdb_od *, krb5_principal, unsigned, int, int *, ODRecordRef *);
65
66typedef struct hdb_od {
67    CFStringRef rootName;
68    ODNodeRef rootNode;
69    CFArrayRef inNodeAttributes;
70    CFArrayRef inKDCAttributes;
71    CFArrayRef inComputerOrUsers;
72    CFStringRef restoreRoot;
73    char *LKDCRealm;
74    SCDynamicStoreRef *store;
75    char *ntlmDomain;
76    struct iter_ctx iter;
77    hod_locate_record locate_record;
78} *hdb_od;
79
80static CFStringRef kRealName = CFSTR("dsAttrTypeStandard:RealName");
81
82static const char wellknown_lkdc[] = "WELLKNOWN:COM.APPLE.LKDC";
83
84/*
85 *
86 */
87
88typedef struct hdb_entry_ex_ctx {
89    krb5_principal principal;
90    ODRecordRef record;
91} hdb_entry_ex_ctx;
92
93static void
94free_hod_ctx(krb5_context context, hdb_entry_ex *entry)
95{
96    hdb_entry_ex_ctx *ctx = entry->ctx;
97    if (ctx) {
98	if (ctx->record)
99	    CFRelease(ctx->record);
100	free(ctx);
101    }
102    entry->ctx = NULL;
103}
104
105
106static hdb_entry_ex_ctx *
107get_ex_ctx(hdb_entry_ex *entry)
108{
109    if (entry->ctx == NULL) {
110	entry->ctx = calloc(1, sizeof(*entry->ctx));
111	if (entry->ctx == NULL)
112	    return NULL;
113	entry->free_entry = free_hod_ctx;
114    }
115    return entry->ctx;
116}
117
118/*
119 *
120 */
121
122static int
123is_lkdc(hdb_od d, const char *realm)
124{
125    return d->LKDCRealm != NULL && krb5_realm_is_lkdc(realm);
126}
127
128static krb5_error_code
129map_service(krb5_context context, krb5_const_principal principal, krb5_principal *eprinc)
130{
131    const char *service;
132    krb5_error_code ret;
133
134    ret = krb5_copy_principal(context, principal, eprinc);
135    if (ret)
136	return ret;
137
138    service = krb5_principal_get_comp_string(context, principal, 0);
139    if (strcasecmp(service, "afpserver") == 0 ||
140	strcasecmp(service, "cifs") == 0 ||
141	strcasecmp(service, "smb") == 0 ||
142	strcasecmp(service, "vnc") == 0) {
143
144	free((*eprinc)->name.name_string.val[0]);
145	(*eprinc)->name.name_string.val[0] = strdup("host");
146    }
147    return 0;
148}
149
150static bool
151tryAgainP(krb5_context context, int *tryAgain, CFErrorRef *error)
152{
153    if (*tryAgain <= 1)
154	return 0;
155
156    if (error && *error) {
157	CFRelease(*error);
158	*error = NULL;
159    }
160    (*tryAgain)--;
161
162    krb5_warnx(context, "try fetching result again");
163
164    return 1;
165}
166
167#define MAX_TRIES	3
168
169
170/*
171 * Returns the matching User or Computer record, if not, returns the node itself
172 */
173
174static ODRecordRef
175HODODNodeCopyLinkageRecordFromAuthenticationData(krb5_context context, ODNodeRef node, ODRecordRef record)
176{
177    CFMutableArrayRef types = NULL;
178    CFArrayRef linkageArray = NULL, resultArray = NULL;
179    CFTypeRef userLinkage;
180    ODQueryRef query = NULL;
181    CFErrorRef error = NULL;
182    ODRecordRef res;
183    int tryAgain = MAX_TRIES;
184
185    types = CFArrayCreateMutable(NULL, 2, &kCFTypeArrayCallBacks);
186    if (types == NULL)
187	goto out;
188
189    CFArrayAppendValue(types, kODRecordTypeUsers);
190    CFArrayAppendValue(types, kODRecordTypeComputers);
191
192    linkageArray = ODRecordCopyValues(record, CFSTR("dsAttrTypeNative:userLinkage"), &error);
193    if (linkageArray == NULL)
194	goto out;
195
196    if (CFArrayGetCount(linkageArray) == 0)
197	goto out;
198
199    userLinkage = CFArrayGetValueAtIndex(linkageArray, 0);
200    if (userLinkage == NULL)
201	goto out;
202
203    do {
204	query = ODQueryCreateWithNode(NULL, node, types,
205				      CFSTR("dsAttrTypeNative:entryUUID"),
206				      kODMatchEqualTo,
207				      userLinkage, NULL, 1, &error);
208	if (query == NULL)
209	    goto out;
210
211	resultArray = ODQueryCopyResults(query, FALSE, &error);
212        CFRelease(query);
213	if (resultArray == NULL && error == NULL)
214	    tryAgain = 0;
215    } while(resultArray == NULL && tryAgainP(context, &tryAgain, &error));
216    if (resultArray == NULL)
217	goto out;
218
219    if (CFArrayGetCount(resultArray) == 0)
220	goto out;
221
222    res = (ODRecordRef)CFArrayGetValueAtIndex(resultArray, 0);
223    if (res)
224	record = res;
225
226out:
227    CFRetain(record);
228    if (error)
229	CFRelease(error);
230    if (linkageArray)
231	CFRelease(linkageArray);
232    if (resultArray)
233	CFRelease(resultArray);
234    if (types)
235	CFRelease(types);
236    return record;
237
238}
239
240/*
241 *
242 */
243
244static krb5_error_code
245od2hdb_usercert(krb5_context context, hdb_od d,
246		CFArrayRef data, unsigned flags, hdb_entry_ex *entry)
247{
248    HDB_extension ext;
249    void *ptr;
250    krb5_error_code ret;
251    CFIndex i;
252
253    memset(&ext, 0, sizeof(ext));
254
255    ext.data.element = choice_HDB_extension_data_pkinit_cert;
256
257    for (i = 0; i < CFArrayGetCount(data); i++) {
258	CFDataRef c = (CFDataRef) CFArrayGetValueAtIndex(data, i);
259	krb5_data cd;
260
261	if (CFGetTypeID((CFTypeRef)c) != CFDataGetTypeID())
262	    continue;
263
264	ret = krb5_data_copy(&cd, CFDataGetBytePtr(c),
265			     CFDataGetLength(c));
266	if (ret) {
267	    free_HDB_extension(&ext);
268	    return ENOMEM;
269	}
270
271	ext.data.u.pkinit_cert.len += 1;
272
273	ptr = realloc(ext.data.u.pkinit_cert.val,
274		      sizeof(ext.data.u.pkinit_cert.val[0]) * ext.data.u.pkinit_cert.len);
275	if (ptr == NULL) {
276	    krb5_data_free(&cd);
277	    free_HDB_extension(&ext);
278	    return ENOMEM;
279	}
280	ext.data.u.pkinit_cert.val = ptr;
281
282	memset(&ext.data.u.pkinit_cert.val[ext.data.u.pkinit_cert.len - 1],
283	       0, sizeof(ext.data.u.pkinit_cert.val[0]));
284
285	ext.data.u.pkinit_cert.val[ext.data.u.pkinit_cert.len - 1].cert = cd;
286    }
287
288    ret = hdb_replace_extension(context, &entry->entry, &ext);
289    free_HDB_extension(&ext);
290
291    return ret;
292}
293
294/*
295 *
296 */
297
298static krb5_error_code
299nodeCreateWithName(krb5_context context, hdb_od d, ODNodeRef *node)
300{
301    ODSessionRef session = kODSessionDefault;
302
303    *node = NULL;
304
305#ifdef __APPLE_PRIVATE__
306    if (d->restoreRoot) {
307	CFMutableDictionaryRef options;
308
309	options = CFDictionaryCreateMutable(NULL, 0,
310					    &kCFTypeDictionaryKeyCallBacks,
311					    &kCFTypeDictionaryValueCallBacks);
312	if (options == NULL)
313	    return ENOMEM;
314
315	CFDictionaryAddValue(options, kODSessionLocalPath, d->restoreRoot);
316
317	session = ODSessionCreate(kCFAllocatorDefault, options, NULL);
318	CFRelease(options);
319	if (session == NULL) {
320	    krb5_set_error_message(context, HDB_ERR_DB_INUSE, "failed to create session");
321	    return HDB_ERR_DB_INUSE;
322	}
323    }
324#endif
325
326    *node = ODNodeCreateWithName(kCFAllocatorDefault,
327				 session,
328				 d->rootName,
329				 NULL);
330    if (session)
331	CFRelease(session);
332
333    if (*node == NULL) {
334	char *restoreRoot = NULL, *path = NULL;
335
336	if (d->restoreRoot)
337	    restoreRoot = rk_cfstring2cstring(d->restoreRoot);
338	path = rk_cfstring2cstring(d->rootName);
339	krb5_set_error_message(context, HDB_ERR_DB_INUSE, "failed to create root node: %s %s",
340			       path ? path : "<nopath>",
341			       restoreRoot ? restoreRoot : "");
342	free(restoreRoot);
343	free(path);
344	return HDB_ERR_DB_INUSE;
345    }
346
347    return 0;
348}
349
350
351
352/*
353 *
354 */
355
356static krb5_error_code
357od2hdb_null(krb5_context context, hdb_od d,
358	    CFArrayRef data, unsigned flags, hdb_entry_ex *entry)
359{
360    return 0;
361}
362
363/*
364 *
365 */
366
367static krb5_error_code
368map_lkdc_principal(krb5_context context, krb5_principal principal, const char *from, const char *to)
369{
370    size_t num = krb5_principal_get_num_comp(context, principal);
371    krb5_error_code ret;
372
373    if (strcasecmp(principal->realm, from) == 0) {
374	ret = krb5_principal_set_realm(context, principal, to);
375	if (ret)
376	    return ret;
377    }
378
379    if (num == 2 && strcasecmp(principal->name.name_string.val[1], from) == 0) {
380	free(principal->name.name_string.val[1]);
381	principal->name.name_string.val[1] = strdup(to);
382	if (principal->name.name_string.val[1] == NULL)
383	    return ENOMEM;
384    }
385    return 0;
386}
387
388/*
389 *
390 */
391
392static krb5_error_code
393od2hdb_principal(krb5_context context, hdb_od d,
394		 CFArrayRef data, unsigned flags, hdb_entry_ex *entry)
395{
396    hdb_entry_ex_ctx *ctx = entry->ctx;
397    krb5_error_code ret;
398    char *str;
399
400    /* the magic store principal in hdb entry was found, lets skip this step */
401    if (ctx && ctx->principal)
402	return 0;
403
404    if ((flags & HDB_F_CANON) == 0)
405	return 0;
406
407    if (entry->entry.principal) {
408	krb5_free_principal(context, entry->entry.principal);
409	entry->entry.principal = NULL;
410    }
411
412    str = rk_cfstring2cstring((CFStringRef)CFArrayGetValueAtIndex(data, 0));
413    if (str == NULL)
414	return ENOMEM;
415
416    ret = krb5_parse_name(context, str, &entry->entry.principal);
417    free(str);
418    if (ret)
419	return ret;
420
421    if (d->LKDCRealm) {
422	ret = map_lkdc_principal(context, entry->entry.principal, wellknown_lkdc, d->LKDCRealm);
423	if (ret)
424	    return ret;
425    }
426
427    return 0;
428}
429
430static krb5_error_code
431od2hdb_def_principal(krb5_context context, hdb_od d,
432		     int flags, hdb_entry_ex *entry)
433{
434    if (entry->entry.principal)
435	return 0;
436    return HDB_ERR_NOENTRY;
437}
438
439static krb5_error_code
440hdb2od_principal_lkdc(krb5_context context, hdb_od d, ODAttributeType type,
441		      unsigned flags, hdb_entry_ex *entry, ODRecordRef record)
442{
443    size_t num = krb5_principal_get_num_comp(context, entry->entry.principal);
444    CFMutableArrayRef array = NULL;
445    krb5_principal principal = NULL;
446    krb5_error_code ret = 0;
447    CFStringRef element;
448    char *user;
449
450    if (d->LKDCRealm == NULL)
451	return 0;
452
453    /*
454     * "User" LKDC names are implicitly stored
455     */
456    if (num == 1)
457	return 0;
458
459    ret = krb5_copy_principal(context, entry->entry.principal, &principal);
460    if (ret)
461	return ret;
462
463    ret = map_lkdc_principal(context, principal, d->LKDCRealm, wellknown_lkdc);
464    if (ret)
465	goto out;
466
467    /* 2 entry nodes are servers :/ */
468    if (num == 2) {
469	krb5_principal eprinc;
470	type = CFSTR("dsAttrTypeNative:KerberosServerName");
471
472	ret = map_service(context, principal, &eprinc);
473	if (ret)
474	    goto out;
475	krb5_free_principal(context, principal);
476	principal = eprinc;
477    }
478
479    array = CFArrayCreateMutable(kCFAllocatorDefault, 1,
480				 &kCFTypeArrayCallBacks);
481    if (array == NULL) {
482	ret = ENOMEM;
483	goto out;
484    }
485
486    ret = krb5_unparse_name(context, principal, &user);
487    if (ret)
488	goto out;
489
490    element = CFStringCreateWithCString(kCFAllocatorDefault, user, kCFStringEncodingUTF8);
491    if (element == NULL) {
492	ret = ENOMEM;
493	goto out;
494    }
495
496    CFArrayAppendValue(array, element);
497    CFRelease(element);
498
499    bool r = ODRecordSetValue(record, type, array, NULL);
500    if (!r)
501	ret = HDB_ERR_UK_SERROR;
502
503 out:
504    if (principal)
505	krb5_free_principal(context, principal);
506    if (array)
507	CFRelease(array);
508
509    return ret;
510}
511
512
513static krb5_error_code
514hdb2od_principal_server(krb5_context context, hdb_od d, ODAttributeType type,
515			unsigned flags, hdb_entry_ex *entry, ODRecordRef record)
516{
517    krb5_error_code ret;
518    CFStringRef name;
519    char *user;
520
521    if (d->LKDCRealm)
522	return 0;
523
524    ret = krb5_unparse_name(context, entry->entry.principal, &user);
525    if (ret)
526	return ret;
527
528    name = CFStringCreateWithCString(NULL, user, kCFStringEncodingUTF8);
529    free(user);
530    if (name == NULL)
531	return ENOMEM;
532
533    bool r = ODRecordSetValue(record, type, name, NULL);
534    CFRelease(name);
535    if (!r)
536	return HDB_ERR_UK_SERROR;
537
538    return 0;
539}
540
541/*
542 *
543 */
544
545static krb5_error_code
546od2hdb_alias(krb5_context context, hdb_od d,
547	     CFArrayRef data, unsigned flags, hdb_entry_ex *entry)
548{
549    hdb_entry_ex_ctx *ctx = entry->ctx;
550    krb5_error_code ret;
551    krb5_principal principal;
552    char *str;
553
554    /* skip aliases on magic principal since they don't have aliases */
555    if (ctx && ctx->principal)
556	return 0;
557
558    str = rk_cfstring2cstring((CFStringRef)CFArrayGetValueAtIndex(data, 0));
559    if (str == NULL)
560	return ENOMEM;
561
562    ret = krb5_parse_name(context, str, &principal);
563    free(str);
564    if (ret)
565	return ret;
566
567    krb5_free_principal(context, principal);
568
569    return 0;
570}
571/*
572 *
573 */
574
575static krb5_error_code
576od2hdb_keys(krb5_context context, hdb_od d,
577	    CFArrayRef akeys, unsigned flags, hdb_entry_ex *entry)
578{
579    CFIndex i, count = CFArrayGetCount(akeys);
580    krb5_error_code ret;
581    hdb_keyset_aapl *keys;
582    int32_t specific_match = -1, general_match = -1;
583    uint32_t specific_kvno = 0, general_kvno = 0;
584    CFIndex len;
585
586    if (count == 0)
587	return ENOMEM;
588
589    keys = calloc(count, sizeof(keys[0]));
590    if (keys == NULL)
591	return ENOMEM;
592
593    ret = 0;
594    for (i = 0; i < count; i++) {
595	CFTypeRef type = CFArrayGetValueAtIndex(akeys, i);
596	void *ptr;
597
598	if (CFGetTypeID(type) == CFDataGetTypeID()) {
599	    CFDataRef data = (CFDataRef) type;
600
601	    len = CFDataGetLength(data);
602	    ptr = malloc(len);
603	    if (ptr == NULL) {
604		ret = ENOMEM;
605		goto out;
606	    }
607	    memcpy(ptr, CFDataGetBytePtr(data), len);
608	} else if (CFGetTypeID(type) == CFStringGetTypeID()) {
609	    CFStringRef cfstr = (CFStringRef)type;
610	    char *str;
611
612	    str = rk_cfstring2cstring(cfstr);
613	    if (str == NULL) {
614		ret = ENOMEM;
615		goto out;
616	    }
617	    ptr = malloc(strlen(str));
618	    if (ptr == NULL) {
619		free(str);
620		ret = ENOMEM;
621		goto out;
622	    }
623
624	    ret = base64_decode(str, ptr);
625	    free(str);
626	    if (ret < 0) {
627		free(ptr);
628		ret = EINVAL;
629		goto out;
630	    }
631
632	    len = ret;
633
634	} else {
635	    ret = EINVAL; /* XXX */
636	    goto out;
637	}
638
639	ret = decode_hdb_keyset_aapl(ptr, len, &keys[i], NULL);
640	free(ptr);
641	if (ret)
642	    goto out;
643
644	if (keys[i].principal) {
645	    if (krb5_principal_compare(context, keys[i].principal, entry->entry.principal)) {
646
647		if (keys[i].kvno > specific_kvno) {
648		    hdb_entry_ex_ctx *ctx = get_ex_ctx(entry);
649		    if (ctx == NULL) {
650			ret = ENOMEM;
651			goto out;
652		    }
653		    ctx->principal = entry->entry.principal;
654
655		    specific_match = (int32_t)i;
656		    specific_kvno = keys[i].kvno;
657		}
658	    }
659	} else {
660	    if (keys[i].kvno > general_kvno) {
661		general_match = (uint32_t)i;
662		general_kvno = keys[i].kvno;
663	    }
664	}
665    }
666
667    if (specific_match != -1) {
668	i = specific_match;
669	entry->entry.kvno = specific_kvno;
670    } else if (general_match != -1) {
671	i = general_match;
672	entry->entry.kvno = general_kvno;
673    } else {
674	ret = HDB_ERR_NOENTRY;
675	goto out;
676    }
677
678    free_Keys(&entry->entry.keys);
679
680    entry->entry.keys.len = keys[i].keys.len;
681    entry->entry.keys.val = keys[i].keys.val;
682    keys[i].keys.val = NULL;
683    keys[i].keys.len = 0;
684
685 out:
686    for (i = 0; i < count; i++)
687	free_hdb_keyset_aapl(&keys[i]);
688    free(keys);
689
690    return ret;
691}
692
693static krb5_error_code
694hdb2od_keys(krb5_context context, hdb_od d, ODAttributeType type,
695	    unsigned flags, hdb_entry_ex *entry, ODRecordRef record)
696{
697    CFMutableArrayRef array;
698    heim_octet_string data;
699    krb5_error_code ret;
700    CFDataRef element;
701    hdb_keyset_aapl key;
702    size_t size;
703
704    /* if this is a change password operation, the keys have already been updated with ->hdb_password */
705    if (flags & HDB_F_CHANGE_PASSWORD)
706	return 0;
707
708    /* this overwrites other keys that are stored in the special "alias" keyset for server */
709
710    key.kvno = entry->entry.kvno;
711    key.keys.len = entry->entry.keys.len;
712    key.keys.val = entry->entry.keys.val;
713    key.principal = NULL;
714
715    array = CFArrayCreateMutable(kCFAllocatorDefault, 1,
716				 &kCFTypeArrayCallBacks);
717    if (array == NULL) {
718	ret = ENOMEM;
719	goto out;
720    }
721
722    ASN1_MALLOC_ENCODE(hdb_keyset_aapl, data.data, data.length, &key, &size, ret);
723    if (ret)
724	goto out;
725    if (data.length != size)
726	krb5_abortx(context, "internal asn.1 encoder error");
727
728    element = CFDataCreate(kCFAllocatorDefault, data.data, data.length);
729    if (element == NULL) {
730	ret = ENOMEM;
731	goto out;
732    }
733
734    CFArrayAppendValue(array, element);
735    CFRelease(element);
736
737    bool r = ODRecordSetValue(record, type, array, NULL);
738    if (!r)
739	ret = HDB_ERR_UK_SERROR;
740
741 out:
742    if (array)
743	CFRelease(array);
744
745    return ret;
746}
747
748/*
749 *
750 */
751
752static krb5_error_code
753od2hdb_flags(krb5_context context, hdb_od d,
754	     CFArrayRef data, unsigned qflags, hdb_entry_ex *entry)
755{
756    int flags;
757    char *str;
758
759    str = rk_cfstring2cstring((CFStringRef)CFArrayGetValueAtIndex(data, 0));
760    if (str == NULL)
761	return ENOMEM;
762
763    flags = atoi(str);
764    free(str);
765    entry->entry.flags = int2HDBFlags(flags);
766    return 0;
767}
768
769static krb5_error_code
770od2hdb_def_flags(krb5_context context, hdb_od d,
771		 int flags, hdb_entry_ex *entry)
772{
773    entry->entry.flags.client = 1;
774    entry->entry.flags.forwardable = 1;
775    entry->entry.flags.renewable = 1;
776    entry->entry.flags.proxiable = 1;
777
778    return 0;
779}
780
781static krb5_error_code
782hdb2od_flags(krb5_context context, hdb_od d, ODAttributeType type,
783	     unsigned flags, hdb_entry_ex *entry, ODRecordRef record)
784{
785    int eflags = HDBFlags2int(entry->entry.flags);
786    CFStringRef value;
787
788    value = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
789				       CFSTR("%d"), eflags);
790    if (value == NULL)
791	return ENOMEM;
792
793    bool r = ODRecordSetValue(record, type, value, NULL);
794    CFRelease(value);
795    if (!r)
796	return HDB_ERR_UK_SERROR;
797
798    return 0;
799}
800
801static krb5_error_code
802od2hdb_altsecids(krb5_context context, hdb_od d,
803		 CFArrayRef akeys, unsigned flags, hdb_entry_ex *entry)
804{
805    CFIndex i, count = CFArrayGetCount(akeys);
806    krb5_error_code ret;
807
808    if (count == 0)
809	return ENOMEM;
810
811    for (i = 0; i < count; i++) {
812	CFStringRef name = CFArrayGetValueAtIndex(akeys, i);
813	char *str, *str0, *subj;
814
815	if (CFGetTypeID(name) != CFStringGetTypeID())
816	    continue;
817
818	if (!CFStringHasPrefix(name, CFSTR("X509:<T>")))
819	    continue;
820
821	/* split out X509:<T>(.*)<S>(.*) from the string and store */
822
823	str0 = str = rk_cfstring2cstring(name);
824	str += 8; /* skip X509:<T> */
825
826	subj = strstr(str, "<S>");
827	if (subj == NULL) {
828	    free(str0);
829	    continue;
830	}
831	*subj = '\0';
832	subj += 3; /* skip <S> */
833
834	/* make sure that there is no <T> or <S> in the string */
835	if (strstr(subj, "<T>") || strstr(subj, "<S>") ||
836	    strstr(str, "<T>") || strstr(str, "<S>"))
837	{
838	    free(str0);
839	    continue;
840	}
841
842	ret = hdb_entry_set_pkinit_acl(&entry->entry, subj, NULL, str);
843	free(str0);
844	if (ret)
845	    return ret;
846    }
847    return 0;
848}
849
850/*
851 *
852 */
853
854static krb5_error_code
855od2hdb_acl_rights(krb5_context context, hdb_od d,
856		  CFArrayRef data, unsigned flags, hdb_entry_ex *entry)
857{
858    char *str;
859
860    str = rk_cfstring2cstring((CFStringRef)CFArrayGetValueAtIndex(data, 0));
861    if (str == NULL)
862	return ENOMEM;
863
864    entry->entry.acl_rights = malloc(sizeof(*entry->entry.acl_rights));
865    if (entry->entry.acl_rights == NULL) {
866	free(str);
867	return ENOMEM;
868    }
869
870    *entry->entry.acl_rights = atoi(str);
871    free(str);
872
873    return 0;
874}
875
876/*
877 *
878 */
879
880typedef krb5_error_code
881(*od2hdb_func)(krb5_context, hdb_od, CFArrayRef, unsigned flags, hdb_entry_ex *);
882
883typedef krb5_error_code
884(*od2hdb_default)(krb5_context, hdb_od, int, hdb_entry_ex *);
885
886typedef krb5_error_code
887(*hdb2od_func)(krb5_context, hdb_od, ODAttributeType,
888	       unsigned flags, hdb_entry_ex *, ODRecordRef);
889
890
891static const struct key {
892    const char *desc;
893    CFStringRef name;
894    int flags;
895#define MULTI_VALUE	1
896#define OPTIONAL_VALUE	2
897#define SERVER_VALUE	4
898#define DELETE_KEY	8
899    od2hdb_func od2hdb;
900    od2hdb_default od2hdb_def;
901    hdb2od_func hdb2od;
902} keys[] = {
903    {
904	"keyset",
905	CFSTR("dsAttrTypeNative:draft-krbKeySet"), OPTIONAL_VALUE | MULTI_VALUE | SERVER_VALUE | DELETE_KEY,
906	od2hdb_keys,
907	NULL,
908	hdb2od_keys
909    },
910    {
911	"KerberosKeys",
912	CFSTR("dsAttrTypeNative:KerberosKeys"), OPTIONAL_VALUE | MULTI_VALUE | DELETE_KEY,
913	od2hdb_keys,
914	NULL,
915	hdb2od_keys
916    },
917    {
918	"username",
919	CFSTR("dsAttrTypeNative:KerberosUserName"), OPTIONAL_VALUE | DELETE_KEY,
920	od2hdb_principal,
921	od2hdb_def_principal,
922	hdb2od_principal_lkdc
923    },
924    {
925	"principalName",
926	CFSTR("dsAttrTypeNative:draft-krbPrincipalName"), OPTIONAL_VALUE | SERVER_VALUE | DELETE_KEY,
927	od2hdb_principal,
928	NULL,
929	hdb2od_principal_server
930    },
931    {
932	"principalAlias",
933	CFSTR("dsAttrTypeNative:draft-krbPrincipalAliases"), MULTI_VALUE | OPTIONAL_VALUE | DELETE_KEY,
934	od2hdb_alias,
935	NULL,
936	NULL
937    },
938    {
939	"KerberosFlags",
940	CFSTR("dsAttrTypeNative:KerberosFlags"), DELETE_KEY,
941	od2hdb_flags,
942	od2hdb_def_flags,
943	hdb2od_flags
944    },
945    {
946	"KerberosPolicy",
947	CFSTR("dsAttrTypeNative:draft-krbTicketPolicy"), OPTIONAL_VALUE | SERVER_VALUE | DELETE_KEY,
948	od2hdb_flags,
949	NULL,
950	hdb2od_flags
951    },
952    {
953	"MaxLife",
954	CFSTR("dsAttrTypeNative:KerberosMaxLife"), OPTIONAL_VALUE | DELETE_KEY,
955	od2hdb_null,
956	NULL,
957	NULL
958    },
959    {
960	"MaxRenewLife",
961	CFSTR("dsAttrTypeNative:KerberosMaxRenew"), OPTIONAL_VALUE | DELETE_KEY,
962	od2hdb_null,
963	NULL,
964	NULL
965    },
966    {
967	"UserCertificate",
968	CFSTR("dsAttrTypeStandard:UserCertificate"), MULTI_VALUE | OPTIONAL_VALUE,
969	od2hdb_usercert,
970	NULL,
971	NULL
972    },
973    {
974	"AltSecurityIdentities",
975	CFSTR("dsAttrTypeStandard:AltSecurityIdentities"), MULTI_VALUE | OPTIONAL_VALUE,
976	od2hdb_altsecids,
977	NULL,
978	NULL
979    },
980    {
981	"principalACL",
982	CFSTR("dsAttrTypeNative:draft-krbPrincipalACL"), OPTIONAL_VALUE,
983	od2hdb_acl_rights,
984	NULL,
985	NULL
986    }
987
988};
989static const int num_keys = sizeof(keys)/sizeof(keys[0]);
990
991/*
992 * Policy mappings
993 */
994
995static int
996booleanPolicy(CFDictionaryRef policy, CFStringRef key)
997{
998    CFTypeRef val = CFDictionaryGetValue(policy, key);
999
1000    if (val == NULL)
1001	return 0;
1002
1003    if (CFGetTypeID(val) == CFStringGetTypeID()) {
1004	if (CFStringCompare(val, CFSTR("0"), kCFCompareNumerically) != kCFCompareEqualTo)
1005	    return 1;
1006    } else if (CFGetTypeID(val) == CFNumberGetTypeID()) {
1007	int num;
1008	if (CFNumberGetValue(val, kCFNumberIntType, &num))
1009	    return num;
1010    }
1011    return 0;
1012}
1013
1014static time_t
1015timePolicy(CFDictionaryRef policy, CFStringRef key)
1016{
1017    CFTypeRef val = CFDictionaryGetValue(policy, key);
1018
1019    if (val == NULL)
1020	return 0;
1021
1022    if (CFGetTypeID(val) == CFStringGetTypeID()) {
1023	return (time_t)CFStringGetIntValue(val);
1024    } else if (CFGetTypeID(val) == CFNumberGetTypeID()) {
1025	long num;
1026	if (CFNumberGetValue(val, kCFNumberLongType, &num))
1027	    return num;
1028    }
1029    return 0;
1030}
1031
1032static krb5_error_code
1033apply_policy(hdb_entry *entry, CFDictionaryRef policy)
1034{
1035    /*
1036     * Historically, we have not applied any policies to admin users.
1037     */
1038    if (booleanPolicy(policy, CFSTR("isAdminUser")))
1039	return 0;
1040
1041    entry->flags.invalid = booleanPolicy(policy, CFSTR("isDisabled"));
1042
1043    if (booleanPolicy(policy, CFSTR("newPasswordRequired"))) {
1044	if (entry->pw_end == NULL) {
1045	    entry->pw_end = malloc(sizeof(*entry->pw_end));
1046	    if (entry->pw_end == NULL)
1047		return ENOMEM;
1048	}
1049	*entry->pw_end = time(NULL) - 60; /* expired on 60s ago */
1050    } else if (booleanPolicy(policy, CFSTR("usingExpirationDate"))) {
1051	time_t t = timePolicy(policy, CFSTR("expirationDateGMT"));
1052	if (t > 0) {
1053	    if (entry->pw_end == NULL) {
1054		entry->pw_end = malloc(sizeof(*entry->pw_end));
1055		if (entry->pw_end == NULL)
1056		    return ENOMEM;
1057	    }
1058	    *entry->pw_end = t;
1059	}
1060    }
1061    if (booleanPolicy(policy, CFSTR("usingHardExpirationDate"))) {
1062	time_t t = timePolicy(policy, CFSTR("hardExpireDateGMT"));
1063	if (t > 0) {
1064	    if (entry->valid_end == NULL) {
1065		entry->valid_end = malloc(sizeof(*entry->valid_end));
1066		if (entry->valid_end == NULL)
1067		    return ENOMEM;
1068	    }
1069	    *entry->valid_end = t;
1070	}
1071    }
1072    return 0;
1073}
1074
1075/*
1076 * HDB entry points
1077 */
1078
1079static krb5_error_code
1080hod_close(krb5_context context, HDB *db)
1081{
1082    return 0;
1083}
1084
1085static krb5_error_code
1086hod_destroy(krb5_context context, HDB *db)
1087{
1088    hdb_od d = (hdb_od)db->hdb_db;
1089    krb5_error_code ret;
1090
1091    if (d->rootNode)
1092	CFRelease(d->rootNode);
1093    if (d->inNodeAttributes)
1094	CFRelease(d->inNodeAttributes);
1095    if (d->inKDCAttributes)
1096	CFRelease(d->inKDCAttributes);
1097    if (d->LKDCRealm)
1098	free(d->LKDCRealm);
1099
1100    ret = hdb_clear_master_key (context, db);
1101    free(db->hdb_name);
1102    free(db);
1103    return ret;
1104}
1105
1106static krb5_error_code
1107hod_lock(krb5_context context, HDB *db, int operation)
1108{
1109    return 0;
1110}
1111
1112static krb5_error_code
1113hod_unlock(krb5_context context, HDB *db)
1114{
1115    return 0;
1116}
1117
1118static krb5_error_code
1119od_record2entry(krb5_context context, HDB * db, ODRecordRef cfRecord,
1120		unsigned flags, hdb_entry_ex * entry)
1121{
1122    hdb_od d = (hdb_od)db->hdb_db;
1123    krb5_error_code ret = HDB_ERR_NOENTRY;
1124    int i;
1125
1126    for (i = 0; i < num_keys; i++) {
1127	CFArrayRef data;
1128
1129	data = ODRecordCopyValues(cfRecord, keys[i].name, NULL);
1130	if (data == NULL) {
1131	    if (keys[i].flags & OPTIONAL_VALUE)
1132		continue;
1133	    if (keys[i].od2hdb_def) {
1134		ret = (*keys[i].od2hdb_def)(context, d, flags, entry);
1135		if (ret)
1136		    goto out;
1137		continue;
1138	    }
1139	    krb5_warnx(context, "Failed to copy non-optional value %s", keys[i].desc);
1140	    ret = HDB_ERR_NOENTRY;
1141	    goto out;
1142	}
1143
1144	if ((keys[i].flags & MULTI_VALUE) && CFArrayGetCount(data) < 1) {
1145	    krb5_warnx(context, "Expected multivalue got zero %s for", keys[i].desc);
1146	    CFRelease(data);
1147	    ret = HDB_ERR_NOENTRY;
1148	    goto out;
1149	} else if ((keys[i].flags & MULTI_VALUE) == 0 && CFArrayGetCount(data) != 1) {
1150	    krb5_warnx(context, "Expected single-value got non zero %d for %s",
1151		       (int)CFArrayGetCount(data), keys[i].desc);
1152	    CFRelease(data);
1153	    ret = HDB_ERR_NOENTRY;
1154	    goto out;
1155	}
1156
1157	ret = (*keys[i].od2hdb)(context, d, data, flags, entry);
1158	CFRelease(data);
1159	if (ret) {
1160	    krb5_warn(context, ret, "od2hdb failed for %s", keys[i].desc);
1161	    goto out;
1162	}
1163    }
1164
1165    /*
1166     * If entry didn't contain a principal, something is wrong with
1167     * the entry, lets skip it.
1168     */
1169    if (entry->entry.principal == NULL) {
1170	krb5_warnx(context, "principal missing");
1171	ret = HDB_ERR_NOENTRY;
1172	goto out;
1173    }
1174
1175    /* Not recorded in the OD backend, make something up */
1176    ret = krb5_parse_name(context, "hdb/od@WELL-KNOWN:OD-BACKEND",
1177			  &entry->entry.created_by.principal);
1178    if (ret)
1179	goto out;
1180
1181    entry->entry.created_by.time = time(NULL);
1182
1183    if ((flags & HDB_F_GET_KRBTGT) == 0 && !krb5_principal_is_krbtgt(context, entry->entry.principal)) {
1184        ODRecordRef userrecord;
1185
1186	userrecord = HODODNodeCopyLinkageRecordFromAuthenticationData(context, d->rootNode, cfRecord);
1187	if (userrecord) {
1188	    CFDictionaryRef policy;
1189
1190	    policy = ODRecordCopyEffectivePolicies(userrecord, NULL);
1191	    CFRelease(userrecord);
1192
1193	    if (policy) {
1194		ret = apply_policy(&entry->entry, policy);
1195		CFRelease(policy);
1196		if (ret)
1197		    goto out;
1198	    }
1199	}
1200    }
1201
1202    if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
1203        ret = hdb_unseal_keys(context, db, &entry->entry);
1204        if(ret) {
1205	    krb5_warn(context, ret, "unseal keys failed");
1206	    goto out;
1207	}
1208    }
1209
1210out:
1211    return ret;
1212}
1213
1214static krb5_error_code
1215hod_nextkey(krb5_context context, HDB * db, unsigned flags,
1216	     hdb_entry_ex * entry)
1217{
1218    ODRecordRef cfRecord = NULL;
1219    hdb_od d = (hdb_od)db->hdb_db;
1220    krb5_error_code ret;
1221
1222    memset(entry, 0, sizeof(*entry));
1223
1224    heim_assert(d->iter.odRecordArray != NULL, "hdb: hod_next w/o hod_first");
1225
1226    while (d->iter.idx < CFArrayGetCount(d->iter.odRecordArray)) {
1227	cfRecord = (ODRecordRef) CFArrayGetValueAtIndex(d->iter.odRecordArray, d->iter.idx);
1228	d->iter.idx++;
1229	if (cfRecord == NULL) {
1230	    ret = HDB_ERR_NOENTRY;
1231	    goto out;
1232	}
1233
1234	CFArrayRef kerberosKeysArray = ODRecordCopyValues(cfRecord, CFSTR("dsAttrTypeNative:KerberosKeys"), NULL);
1235	if (kerberosKeysArray == NULL) {
1236	    cfRecord = NULL;
1237	    continue;
1238	}
1239	if (CFArrayGetCount(kerberosKeysArray) == 0) {
1240	    CFRelease(kerberosKeysArray);
1241	    cfRecord = NULL;
1242	    continue;
1243	}
1244	CFRelease(kerberosKeysArray);
1245
1246	break;
1247    }
1248
1249    if (cfRecord) {
1250	CFStringRef name = ODRecordGetRecordName(cfRecord);
1251	char *str;
1252
1253	str = rk_cfstring2cstring(name);
1254	if (str == NULL) {
1255	    ret = ENOENT;
1256	    goto out;
1257	}
1258
1259	ret = krb5_make_principal(context, &entry->entry.principal,
1260				  d->LKDCRealm, str, NULL);
1261	free(str);
1262	if (ret)
1263	    goto out;
1264
1265	ret = od_record2entry(context, db, cfRecord, flags, entry);
1266    } else
1267	ret = HDB_ERR_NOENTRY;
1268
1269out:
1270    if (ret) {
1271	free_hdb_entry(&entry->entry);
1272
1273	if (d->iter.odRecordArray) {
1274	    CFRelease(d->iter.odRecordArray);
1275	    d->iter.odRecordArray = NULL;
1276	    d->iter.idx = 0;
1277	}
1278    }
1279
1280    return ret;
1281}
1282
1283static krb5_error_code
1284hod_firstkey(krb5_context context, HDB *db,
1285	     unsigned flags, hdb_entry_ex *entry)
1286{
1287    int ret = 0;
1288    ODQueryRef query = NULL;
1289    hdb_od d = (hdb_od)db->hdb_db;
1290    int tryAgain = MAX_TRIES;
1291
1292    if (d->LKDCRealm == NULL) {
1293	krb5_set_error_message(context, EINVAL, "iteration over database only supported for DSLocal");
1294	return EINVAL;
1295    }
1296
1297    heim_assert(d->iter.odRecordArray == NULL, "hdb: more then one iteration at the same time");
1298
1299    do {
1300	CFErrorRef error = NULL;
1301
1302	query = ODQueryCreateWithNode(NULL, d->rootNode,
1303				      kODRecordTypeUsers, NULL,
1304				      kODMatchAny,
1305				      NULL,
1306				      kODAttributeTypeAllAttributes,
1307				      0,
1308				      &error);
1309	if (query == NULL) {
1310	    ret = HDB_ERR_NOENTRY;
1311	    goto out;
1312	}
1313
1314	d->iter.odRecordArray = ODQueryCopyResults(query, FALSE, &error);
1315	CFRelease(query);
1316	if (d->iter.odRecordArray == NULL && error == NULL)
1317	    tryAgain = 0;
1318	if (error)
1319	    CFRelease(error);
1320    } while(d->iter.odRecordArray == NULL && tryAgainP(context, &tryAgain, NULL));
1321
1322    if (d->iter.odRecordArray == NULL) {
1323	ret = HDB_ERR_NOENTRY;
1324	goto out;
1325    }
1326    d->iter.idx = 0;
1327
1328    return hod_nextkey(context, db, flags, entry);
1329out:
1330    return ret;
1331}
1332
1333static krb5_error_code
1334hod_open(krb5_context context, HDB * db, int flags, mode_t mode)
1335{
1336    hdb_od d = (hdb_od)db->hdb_db;
1337
1338    if (d->rootNode == NULL)
1339	return nodeCreateWithName(context, d, &d->rootNode);
1340
1341    return 0;
1342}
1343
1344static krb5_error_code
1345lkdc_locate_record(krb5_context context, hdb_od d, krb5_principal principal,
1346		   unsigned flags, int createp, int *createdp, ODRecordRef *result)
1347{
1348    ODMatchType matchtype = kODMatchEqualTo;
1349    CFStringRef queryString = NULL;
1350    ODRecordRef cfRecord;
1351    ODQueryRef query = NULL;
1352    CFArrayRef res = NULL;
1353    CFStringRef attr;
1354    krb5_error_code ret = 0;
1355    krb5_principal eprinc = NULL;
1356    char *kuser = NULL;
1357    const char *node = NULL;
1358    CFTypeRef rtype = NULL;
1359    int upflags = 0;
1360    int tryAgain = MAX_TRIES;
1361
1362    *result = NULL;
1363
1364    /*
1365     * Really need something like CrackName for uniform unparsing
1366     */
1367
1368    if (principal->name.name_type == KRB5_NT_X509_GENERAL_NAME) {
1369#ifdef PKINIT
1370	char *anode = NULL;
1371	hx509_name name = NULL;
1372	GeneralName gn;
1373	ssize_t len;
1374	void *buf;
1375	Name n;
1376
1377	memset(&gn, 0, sizeof(gn));
1378	memset(&n, 0, sizeof(n));
1379
1380	if (krb5_principal_get_num_comp(context, principal) != 1) {
1381	    krb5_warnx(context, "wrong length of x509 name");
1382	    return HDB_ERR_NOENTRY;
1383	}
1384
1385	len = strlen(principal->name.name_string.val[0]);
1386	buf = malloc(len);
1387	if (buf == NULL)
1388	    return HDB_ERR_NOENTRY;
1389
1390	len = base64_decode(principal->name.name_string.val[0], buf);
1391	if (len <= 0) {
1392	    free(buf);
1393	    return HDB_ERR_NOENTRY;
1394	}
1395
1396	ret = decode_GeneralName(buf, len, &gn, NULL);
1397	free(buf);
1398	if (ret) {
1399	    krb5_warnx(context, "x500 GeneralName malformed: %d", ret);
1400	    return HDB_ERR_NOENTRY;
1401	}
1402
1403	if (gn.element != choice_GeneralName_directoryName) {
1404	    krb5_warnx(context, "x500 name not directory Name");
1405	    free_GeneralName(&gn);
1406	    return HDB_ERR_NOENTRY;
1407	}
1408
1409	n.element = choice_Name_rdnSequence;
1410	n.u.rdnSequence.len = gn.u.directoryName.u.rdnSequence.len;
1411	n.u.rdnSequence.val = gn.u.directoryName.u.rdnSequence.val;
1412
1413	ret = hx509_name_from_Name(&n, &name);
1414	free_GeneralName(&gn);
1415	if (ret)
1416	    return HDB_ERR_NOENTRY;
1417
1418	attr = kODAttributeTypeRecordName;
1419	rtype = kODRecordTypeUsers;
1420
1421	ret = hx509_name_to_string(name, &anode);
1422	hx509_name_free(&name);
1423	if (ret)
1424	    return HDB_ERR_NOENTRY;
1425
1426	queryString = CFStringCreateWithCString(NULL, anode, kCFStringEncodingUTF8);
1427	free(anode);
1428	if (queryString == NULL) {
1429	    ret = HDB_ERR_NOENTRY;
1430	    goto out;
1431	}
1432	createp = 0;
1433#else
1434	return HDB_ERR_NOENTRY;
1435#endif
1436    } else if (principal->name.name_type == KRB5_NT_NTLM) {
1437	attr = kODAttributeTypeRecordName;
1438	rtype = kODRecordTypeUsers;
1439	matchtype = kODMatchInsensitiveEqualTo;
1440
1441	if (krb5_principal_get_num_comp(context, principal) != 1) {
1442	    krb5_warnx(context, "wrong length of ntlm name");
1443	    return HDB_ERR_NOENTRY;
1444	}
1445
1446	if (d->ntlmDomain == NULL) {
1447	    krb5_warnx(context, "NTLM domain not configured");
1448	    return HDB_ERR_NOENTRY;
1449	}
1450
1451	krb5_principal_set_realm(context, principal, d->ntlmDomain);
1452	node = krb5_principal_get_comp_string(context, principal, 0);
1453
1454    } else if (krb5_principal_is_pku2u(context, principal)) {
1455	attr = kODAttributeTypeRecordName;
1456	if (flags & HDB_F_GET_CLIENT) {
1457	    int num = krb5_principal_get_num_comp(context, principal);
1458	    if (num != 1) {
1459		ret = HDB_ERR_NOENTRY;
1460		goto out;
1461	    }
1462	    rtype = kODRecordTypeUsers;
1463	    upflags = KRB5_PRINCIPAL_UNPARSE_NO_REALM;
1464	    upflags |= KRB5_PRINCIPAL_UNPARSE_DISPLAY;
1465	} else {
1466	    node = "localhost";
1467	    rtype = kODRecordTypeComputers;
1468	}
1469    } else if (is_lkdc(d, principal->realm)) {
1470	/* check if user is a LKDC user, just look for RecordName then */
1471	int num = krb5_principal_get_num_comp(context, principal);
1472	const char *comp0;
1473	size_t lencomp0;
1474
1475	if (num < 1) {
1476	    ret = HDB_ERR_NOENTRY;
1477	    goto out;
1478	}
1479
1480	ret = map_lkdc_principal(context, principal, d->LKDCRealm, wellknown_lkdc);
1481	if (ret)
1482	    goto out;
1483
1484	comp0 = krb5_principal_get_comp_string(context, principal, 0);
1485	lencomp0 = strlen(comp0);
1486	if (lencomp0 == 0) {
1487	    ret = HDB_ERR_NOENTRY;
1488	    goto out;
1489	}
1490
1491	if (num == 2 || (num == 1 && comp0[lencomp0 - 1] == '$')) {
1492	    const char *host;
1493
1494	    attr = kODAttributeTypeRecordName;
1495
1496	    ret = map_service(context, principal, &eprinc);
1497	    if (ret) {
1498		ret = HDB_ERR_NOENTRY;
1499		goto out;
1500	    }
1501	    principal = eprinc;
1502
1503	    host = krb5_principal_get_comp_string(context, principal, 1);
1504	    comp0 = krb5_principal_get_comp_string(context, principal, 0);
1505
1506	    if (strcasecmp(KRB5_TGS_NAME, comp0) == 0) {
1507		node = "_krbtgt";
1508		rtype = kODRecordTypeUsers;
1509	    } else if (num == 2 && is_lkdc(d, host)) {
1510		node = "localhost";
1511		rtype = kODRecordTypeComputers;
1512	    } else if (num == 2) {
1513		node = host;
1514		rtype = kODRecordTypeComputers;
1515	    } else {
1516		node = comp0;
1517		rtype = kODRecordTypeComputers;
1518	    }
1519
1520	} else if (num == 1) {
1521	    attr = kODAttributeTypeRecordName;
1522	    rtype = kODRecordTypeUsers;
1523	    upflags = KRB5_PRINCIPAL_UNPARSE_NO_REALM;
1524	    upflags |= KRB5_PRINCIPAL_UNPARSE_DISPLAY;
1525	} else {
1526	    ret = HDB_ERR_NOENTRY;
1527	    goto out;
1528	}
1529    } else {
1530	/* managed realm, lets check for real entries */
1531
1532	ret = krb5_unparse_name_flags(context, principal, KRB5_PRINCIPAL_UNPARSE_DISPLAY, &kuser);
1533	if (ret)
1534	    goto out;
1535
1536	rtype = d->inComputerOrUsers;
1537	matchtype = kODMatchCompoundExpression;
1538	attr = NULL;
1539
1540	queryString = CFStringCreateWithFormat(NULL, NULL, CFSTR("(|(%@=%s)(%@=%s))"),
1541					       CFSTR("dsAttrTypeNative:KerberosUserName"),
1542					       kuser,
1543					       CFSTR("dsAttrTypeNative:KerberosServerName"),
1544					       kuser);
1545    }
1546
1547    if (queryString == NULL) {
1548	if (node) {
1549	    queryString = CFStringCreateWithCString(NULL, node, kCFStringEncodingUTF8);
1550	} else {
1551	    ret = krb5_unparse_name_flags(context, principal, upflags, &kuser);
1552	    if (ret)
1553		goto out;
1554
1555	    queryString = CFStringCreateWithCString(NULL, kuser, kCFStringEncodingUTF8);
1556	}
1557    }
1558    if (queryString == NULL) {
1559	ret = ENOMEM;
1560	goto out;
1561    }
1562
1563    do {
1564	CFErrorRef error = NULL;
1565
1566	query = ODQueryCreateWithNode(NULL, d->rootNode,
1567				      rtype, attr,
1568				      matchtype,
1569				      queryString,
1570				      d->inNodeAttributes,
1571				      1,
1572				      NULL);
1573	if (query == NULL) {
1574	    CFRelease(queryString);
1575	    ret = ENOMEM;
1576	    goto out;
1577	}
1578
1579	res = ODQueryCopyResults(query, FALSE, &error);
1580	if (res == NULL && error == NULL)
1581	    tryAgain = 0;
1582	if (error)
1583	    CFRelease(error);
1584    } while(res == NULL && tryAgainP(context, &tryAgain, NULL));
1585
1586    CFRelease(queryString);
1587    if (res == NULL) {
1588	ret = HDB_ERR_NOENTRY;
1589	goto out;
1590    }
1591
1592    if (CFArrayGetCount(res) == 0 && node && createp) {
1593
1594	queryString = CFStringCreateWithCString(NULL, node, kCFStringEncodingUTF8);
1595
1596	cfRecord = ODNodeCopyRecord(d->rootNode, rtype, queryString, d->inNodeAttributes, NULL);
1597	if (cfRecord == NULL) {
1598	    CFMutableDictionaryRef attributes;
1599
1600	    attributes = CFDictionaryCreateMutable(NULL, 0,
1601						   &kCFTypeDictionaryKeyCallBacks,
1602						   &kCFTypeDictionaryValueCallBacks);
1603
1604	    cfRecord = ODNodeCreateRecord(d->rootNode,
1605					  rtype,
1606					  queryString,
1607					  attributes,
1608					  NULL);
1609	    CFRelease(attributes);
1610	}
1611	CFRelease(queryString);
1612
1613	if (cfRecord == NULL) {
1614	    ret = HDB_ERR_UK_RERROR;
1615	    goto out;
1616	}
1617
1618	if (createdp)
1619	    *createdp = TRUE;
1620
1621    } else if (CFArrayGetCount(res) == 1) {
1622	cfRecord = (ODRecordRef) CFArrayGetValueAtIndex(res, 0);
1623	if (cfRecord == NULL) {
1624	    ret = HDB_ERR_NOENTRY;
1625	    goto out;
1626	}
1627	CFRetain(cfRecord);
1628
1629    } else {
1630	ret = HDB_ERR_NOENTRY;
1631	goto out;
1632    }
1633
1634    *result = cfRecord;
1635
1636 out:
1637    if (kuser)
1638	free(kuser);
1639    if (eprinc)
1640	krb5_free_principal(context, eprinc);
1641    if (res)
1642	CFRelease(res);
1643    if (query)
1644	CFRelease(query);
1645    return ret;
1646}
1647
1648static krb5_error_code
1649server_locate_record(krb5_context context, hdb_od d, krb5_principal principal,
1650		     unsigned flags, int createp, int *createdp, ODRecordRef *result)
1651{
1652    ODRecordRef cfRecord = NULL;
1653    krb5_error_code ret;
1654    ODMatchType matchtype;
1655    CFStringRef queryString = NULL;
1656    ODQueryRef query = NULL;
1657    CFArrayRef res = NULL;
1658    CFStringRef attr;
1659    char *kuser = NULL;
1660    CFStringRef client_key, server_key;
1661    int tryAgain = MAX_TRIES;
1662
1663    *result = NULL;
1664
1665    ret = krb5_unparse_name_flags(context, principal, KRB5_PRINCIPAL_UNPARSE_DISPLAY, &kuser);
1666    if (ret)
1667	goto out;
1668
1669    client_key = CFSTR("dsAttrTypeNative:draft-krbPrincipalName");
1670    server_key = CFSTR("dsAttrTypeNative:draft-krbPrincipalAliases");
1671
1672    /* XXX support referrals here */
1673
1674    if (flags & HDB_F_GET_KRBTGT) {
1675	matchtype = kODMatchEqualTo;
1676	attr = client_key;
1677	queryString = CFStringCreateWithCString(NULL, kuser, kCFStringEncodingUTF8);
1678    } else {
1679	matchtype = kODMatchCompoundExpression;
1680	attr = NULL;
1681	queryString = CFStringCreateWithFormat(NULL, NULL, CFSTR("(|(%@=%s)(%@=%s))"),
1682					       server_key,
1683					       kuser,
1684					       client_key,
1685					       kuser);
1686    }
1687    if (queryString == NULL) {
1688	ret = HDB_ERR_NOENTRY;
1689	goto out;
1690    }
1691
1692    do {
1693	CFErrorRef error = NULL;
1694
1695	query = ODQueryCreateWithNode(NULL, d->rootNode,
1696				      kODRecordTypeUserAuthenticationData,
1697				      attr,
1698				      matchtype,
1699				      queryString,
1700				      d->inNodeAttributes,
1701				      2,
1702				      NULL);
1703	if (query == NULL) {
1704	    CFRelease(queryString);
1705	    ret = ENOMEM;
1706	    goto out;
1707	}
1708
1709	res = ODQueryCopyResults(query, FALSE, &error);
1710	if (res == NULL && error == NULL)
1711	    tryAgain = 0;
1712	if (error)
1713	    CFRelease(error);
1714    } while(res == NULL && tryAgainP(context, &tryAgain, NULL));
1715    CFRelease(queryString);
1716
1717    if (res == NULL) {
1718	ret = HDB_ERR_NOENTRY;
1719	goto out;
1720    }
1721
1722
1723    CFIndex count = CFArrayGetCount(res);
1724    if (count == 0 && createp) {
1725	CFMutableDictionaryRef attributes;
1726	CFStringRef name;
1727	CFUUIDRef uuid;
1728
1729	uuid = CFUUIDCreate(NULL);
1730	if (uuid == NULL) {
1731	    ret = HDB_ERR_NOENTRY;
1732	    goto out;
1733	}
1734
1735	name = CFUUIDCreateString(NULL, uuid);
1736	CFRelease(uuid);
1737	if (name == NULL) {
1738	    ret = HDB_ERR_NOENTRY;
1739	    goto out;
1740	}
1741
1742	attributes = CFDictionaryCreateMutable(NULL, 0,
1743					       &kCFTypeDictionaryKeyCallBacks,
1744					       &kCFTypeDictionaryValueCallBacks);
1745	if (attributes == NULL) {
1746	    CFRelease(name);
1747	    ret = HDB_ERR_NOENTRY;
1748	    goto out;
1749	}
1750
1751	cfRecord = ODNodeCreateRecord(d->rootNode,
1752				      kODRecordTypeUserAuthenticationData,
1753				      name,
1754				      attributes,
1755				      NULL);
1756	CFRelease(name);
1757	CFRelease(attributes);
1758	if (cfRecord == NULL) {
1759	    ret = HDB_ERR_NOENTRY;
1760	    goto out;
1761	}
1762
1763	if (createdp)
1764	    *createdp = 1;
1765
1766    } else if (count != 1) {
1767	ret = HDB_ERR_NOENTRY;
1768	goto out;
1769    } else {
1770	cfRecord = (ODRecordRef) CFArrayGetValueAtIndex(res, 0);
1771	if (cfRecord == NULL) {
1772	    ret = HDB_ERR_NOENTRY;
1773	    goto out;
1774	}
1775	CFRetain(cfRecord);
1776    }
1777    *result = cfRecord;
1778
1779out:
1780    if (res)
1781	CFRelease(res);
1782    if (query)
1783	CFRelease(query);
1784    if (kuser)
1785	free(kuser);
1786
1787    return ret;
1788}
1789
1790
1791static krb5_error_code
1792hod_lkdc_fetch(krb5_context context, HDB * db, krb5_const_principal principal,
1793	       unsigned flags, krb5_kvno kvno, hdb_entry_ex * entry)
1794{
1795    krb5_principal eprincipal = NULL;
1796    hdb_od d = (hdb_od)db->hdb_db;
1797    ODRecordRef cfRecord = NULL;
1798    krb5_error_code ret;
1799
1800    ret = krb5_copy_principal(context, principal, &eprincipal);
1801    if (ret)
1802	goto out;
1803
1804    ret = d->locate_record(context, d, eprincipal, flags, FALSE, NULL, &cfRecord);
1805    if (ret)
1806	goto out;
1807
1808    if (is_lkdc(d, eprincipal->realm))
1809	map_lkdc_principal(context, eprincipal, wellknown_lkdc, d->LKDCRealm);
1810
1811    /* set the principal to the username for users in pku2u and lkdc */
1812    if (krb5_principal_is_pku2u(context, eprincipal) ||
1813	(krb5_principal_is_lkdc(context, eprincipal) && krb5_principal_get_num_comp(context, eprincipal) == 1))
1814    {
1815	CFStringRef name = ODRecordGetRecordName(cfRecord);
1816	char *str;
1817
1818	str = rk_cfstring2cstring(name);
1819	if (str == NULL) {
1820	    ret = ENOENT;
1821	    goto out;
1822	}
1823
1824	ret = krb5_make_principal(context, &entry->entry.principal,
1825				  eprincipal->realm, str, NULL);
1826	free(str);
1827	if (ret)
1828	    goto out;
1829    } else {
1830	ret = krb5_copy_principal(context, eprincipal, &entry->entry.principal);
1831	if (ret)
1832	    goto out;
1833    }
1834
1835    ret = od_record2entry(context, db, cfRecord, flags, entry);
1836    if (ret)
1837	goto out;
1838
1839 out:
1840    if (eprincipal)
1841	krb5_free_principal(context, eprincipal);
1842    if (cfRecord)
1843	CFRelease(cfRecord);
1844    if (ret) {
1845	free_hdb_entry(&entry->entry);
1846	memset(&entry->entry, 0, sizeof(entry->entry));
1847    }
1848
1849    return ret;
1850}
1851
1852static krb5_error_code
1853hod_server_fetch(krb5_context context, HDB * db, krb5_const_principal principal,
1854		 unsigned flags, krb5_kvno kvno, hdb_entry_ex * entry)
1855{
1856    krb5_principal eprincipal = NULL;
1857    hdb_od d = (hdb_od)db->hdb_db;
1858    ODRecordRef cfRecord = NULL;
1859    krb5_error_code ret;
1860
1861
1862    ret = krb5_copy_principal(context, principal, &eprincipal);
1863    if (ret)
1864	goto out;
1865
1866    ret = d->locate_record(context, d, eprincipal, flags, FALSE, NULL, &cfRecord);
1867    if (ret)
1868        goto out;
1869
1870    ret = krb5_copy_principal(context, eprincipal, &entry->entry.principal);
1871    if (ret)
1872	goto out;
1873
1874    ret = od_record2entry(context, db, cfRecord, flags, entry);
1875    if (ret)
1876	goto out;
1877
1878out:
1879    if (cfRecord)
1880        CFRelease(cfRecord);
1881    if (eprincipal)
1882	krb5_free_principal(context, eprincipal);
1883
1884    if (ret) {
1885	free_hdb_entry(&entry->entry);
1886	memset(&entry->entry, 0, sizeof(entry->entry));
1887    }
1888
1889    return ret;
1890}
1891
1892
1893static krb5_error_code
1894hod_store(krb5_context context, HDB * db, unsigned flags,
1895	  hdb_entry_ex * entry)
1896{
1897    krb5_principal eprincipal = NULL;
1898    hdb_od d = (hdb_od)db->hdb_db;
1899    ODRecordRef record = NULL;
1900    krb5_error_code ret;
1901    int i, created = 0;
1902
1903    ret = krb5_copy_principal(context, entry->entry.principal, &eprincipal);
1904    if (ret)
1905	goto out;
1906
1907    ret = d->locate_record(context, d, eprincipal, flags, (flags & HDB_F_REPLACE) == 0, &created, &record);
1908    if (ret)
1909	goto out;
1910
1911    for (i = 0; i < num_keys; i++) {
1912	if (keys[i].hdb2od == NULL)
1913	    continue;
1914
1915	/* logical XOR between server values and LKDC connection */
1916	if ((!!(keys[i].flags & SERVER_VALUE)) ^ (!d->LKDCRealm))
1917	    continue;
1918
1919	ret = (*keys[i].hdb2od)(context, d, keys[i].name, flags, entry, record);
1920	if (ret)
1921	    goto out;
1922    }
1923
1924    if ((flags & HDB_F_CHANGE_PASSWORD) == 0) {
1925	ret = hdb_seal_keys(context, db, &entry->entry);
1926	if(ret)
1927	    goto out;
1928    }
1929
1930    ODRecordSynchronize(record, NULL);
1931
1932 out:
1933    if (record) {
1934	if (ret && created)
1935	    ODRecordDelete(record, NULL);
1936	CFRelease(record);
1937    }
1938    if (eprincipal)
1939	krb5_free_principal(context, eprincipal);
1940
1941    return ret;
1942}
1943
1944static krb5_error_code
1945hod_remove(krb5_context context, HDB *db, krb5_const_principal principal)
1946{
1947    krb5_principal eprincipal = NULL;
1948    hdb_od d = (hdb_od)db->hdb_db;
1949    ODRecordRef record = NULL;
1950    CFMutableArrayRef array = NULL;
1951    krb5_error_code ret;
1952    int i;
1953
1954    ret = krb5_copy_principal(context, principal, &eprincipal);
1955    if (ret)
1956	goto out;
1957
1958    ret = d->locate_record(context, d, eprincipal, 0, FALSE, NULL, &record);
1959    if (ret)
1960	goto out;
1961
1962    array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
1963    if (array == NULL) {
1964	ret = ENOMEM;
1965	goto out;
1966    }
1967
1968    for (i = 0; i < num_keys; i++) {
1969
1970	if ((keys[i].flags & DELETE_KEY) == 0)
1971	    continue;
1972
1973	if (!ODRecordSetValue(record, keys[i].name, array, NULL)) {
1974	    ret = HDB_ERR_UK_SERROR;
1975	    break;
1976	}
1977    }
1978
1979    ODRecordSynchronize(record, NULL);
1980
1981 out:
1982    if (eprincipal)
1983	krb5_free_principal(context, eprincipal);
1984    if (array)
1985	CFRelease(array);
1986    if (record)
1987	CFRelease(record);
1988    return ret;
1989}
1990
1991
1992static krb5_error_code
1993hod_get_realms(krb5_context context, HDB *db, krb5_realm **realms)
1994{
1995    hdb_od d = (hdb_od)db->hdb_db;
1996
1997    *realms = NULL;
1998
1999    if (d->LKDCRealm) {
2000	*realms = calloc(2, sizeof(realms[0]));
2001	if (*realms == NULL)
2002	    return ENOMEM;
2003	(*realms)[0] = strdup(d->LKDCRealm);
2004	if ((*realms)[0] == NULL) {
2005	    free(*realms);
2006	    *realms = NULL;
2007	    return ENOMEM;
2008	}
2009	(*realms)[1] = NULL;
2010    }
2011
2012    return 0;
2013}
2014
2015static void
2016update_ntlm(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
2017{
2018    hdb_od d = info;
2019    CFDictionaryRef settings;
2020    CFStringRef n;
2021
2022    if (store == NULL || info == NULL)
2023	return;
2024
2025    settings = (CFDictionaryRef)SCDynamicStoreCopyValue(store, CFSTR("com.apple.smb"));
2026    if (settings == NULL)
2027	return;
2028
2029    n = CFDictionaryGetValue(settings, CFSTR("NetBIOSName"));
2030    if (n == NULL || CFGetTypeID(n) != CFStringGetTypeID())
2031	goto fin;
2032
2033    if (d->ntlmDomain)
2034	free(d->ntlmDomain);
2035    d->ntlmDomain = rk_cfstring2cstring(n);
2036    strupr(d->ntlmDomain);
2037
2038fin:
2039    CFRelease(settings);
2040    return;
2041}
2042
2043static void
2044ntlm_notification(hdb_od d)
2045{
2046    SCDynamicStoreRef store;
2047    dispatch_queue_t queue;
2048    SCDynamicStoreContext context;
2049
2050    memset(&context, 0, sizeof(context));
2051    context.info = (void*)d;
2052
2053    store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("com.apple.Kerberos.hdb-od"), update_ntlm, (void *)&context);
2054    if (store == NULL)
2055	return;
2056
2057    CFTypeRef key[] = {CFSTR("com.apple.smb")};
2058    CFArrayRef nkeys = CFArrayCreate(kCFAllocatorDefault, key, 1, NULL);
2059    SCDynamicStoreSetNotificationKeys(store, nkeys, NULL);
2060    CFRelease(nkeys);
2061
2062    queue = dispatch_queue_create("com.apple.hdb-od.ntlm-name", NULL);
2063    if (queue == NULL) {
2064	CFRelease(store);
2065	errx(1, "dispatch_queue_create");
2066    }
2067
2068    SCDynamicStoreSetDispatchQueue(store, queue);
2069    CFRelease(store);
2070
2071    dispatch_sync(queue, ^{ update_ntlm(store, NULL, d); });
2072}
2073
2074static krb5_error_code
2075hod_get_ntlm_domain(krb5_context context, struct HDB *db, char **name)
2076{
2077    hdb_od d = (hdb_od)db->hdb_db;
2078    if (d->ntlmDomain == NULL) {
2079	krb5_set_error_message(context, EINVAL, "no ntlm domain");
2080	return EINVAL;
2081    }
2082    *name = strdup(d->ntlmDomain);
2083    return 0;
2084}
2085
2086
2087#ifdef __APPLE_PRIVATE__
2088
2089static struct  {
2090    typeof(DSChangePasswordWithPolicy) *_HDBDSChangePasswordWithPolicy;
2091    typeof(DSUpdateLoginStatus) *_HDBDSUpdateLoginStatus;
2092} ds_ptrs;
2093
2094static void
2095DirectoryServer_framework(void)
2096{
2097    static dispatch_once_t once;
2098
2099    /*
2100     * Do softlinking to avoid having a dependency on DirectoryServer framework
2101     */
2102
2103    dispatch_once(&once, ^{
2104	    void *ptr = dlopen("/System/Library/PrivateFrameworks/DirectoryServer.framework/DirectoryServer", RTLD_LAZY);
2105	    if (ptr) {
2106		ds_ptrs._HDBDSChangePasswordWithPolicy = dlsym(ptr, "DSChangePasswordWithPolicy");
2107		ds_ptrs._HDBDSUpdateLoginStatus = dlsym(ptr, "DSUpdateLoginStatus");
2108	    }
2109	});
2110}
2111
2112static krb5_error_code
2113hod_password(krb5_context context, struct HDB *db, hdb_entry_ex *entry, const char *password, int flags)
2114{
2115    char *user;
2116    int ret;
2117
2118    DirectoryServer_framework();
2119
2120    if (ds_ptrs._HDBDSChangePasswordWithPolicy == NULL)
2121	return EINVAL;
2122
2123    if (krb5_principal_get_num_comp(context, entry->entry.principal) != 1)
2124	return EINVAL;
2125
2126    user = (char *)krb5_principal_get_comp_string(context, entry->entry.principal, 0);
2127
2128    ret = ds_ptrs._HDBDSChangePasswordWithPolicy(user, password, (flags & HDB_PWD_CONDITIONAL) == 0);
2129    if (ret != 0) {
2130	krb5_set_error_message(context, EINVAL, "DSPasswordServer rejected password");
2131	return EINVAL;
2132    }
2133
2134    return 0;
2135}
2136
2137static krb5_error_code
2138hod_auth_status(krb5_context context, struct HDB *db, hdb_entry_ex *entry, int status)
2139{
2140    char *user;
2141
2142    DirectoryServer_framework();
2143
2144    if (ds_ptrs._HDBDSUpdateLoginStatus == NULL)
2145	return EINVAL;
2146
2147    if (krb5_principal_get_num_comp(context, entry->entry.principal) != 1)
2148	return EINVAL;
2149
2150    user = (char *)krb5_principal_get_comp_string(context, entry->entry.principal, 0);
2151
2152    /* DS and HDB status code matches, so just pass them though */
2153    ds_ptrs._HDBDSUpdateLoginStatus(user, status);
2154
2155    return 0;
2156}
2157
2158#endif
2159
2160
2161krb5_error_code
2162hdb_od_create(krb5_context context, HDB ** db, const char *arg)
2163{
2164    CFMutableArrayRef attrs;
2165    ODNodeRef localRef = NULL;
2166    ODRecordRef kdcConfRef = NULL;
2167    CFArrayRef data = NULL;
2168    krb5_error_code ret;
2169    hdb_od d = NULL;
2170    char *path = NULL;
2171    int i;
2172
2173    *db = calloc(1, sizeof(**db));
2174    if (*db == NULL) {
2175	ret = ENOMEM;
2176	krb5_set_error_message(context, ret, "malloc: out of memory");
2177	goto out;
2178    }
2179    memset(*db, 0, sizeof(**db));
2180
2181    path = strdup(arg);
2182    if (path == NULL) {
2183	ret = ENOMEM;
2184	krb5_set_error_message(context, ret, "malloc: out of memory");
2185	goto out;
2186    }
2187
2188    d = calloc(1, sizeof(*d));
2189    if (d == NULL) {
2190	ret = ENOMEM;
2191	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
2192	goto out;
2193    }
2194
2195    {
2196	char *p = strchr(path, '&');
2197	if (p) {
2198	    *p++ = '\0';
2199	    d->restoreRoot = CFStringCreateWithCString(NULL, p, kCFStringEncodingUTF8);
2200	}
2201    }
2202
2203    d->rootName = CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8);
2204    if (d == NULL) {
2205	ret = ENOMEM;
2206	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
2207	goto out;
2208    }
2209
2210    attrs = CFArrayCreateMutable(NULL, num_keys, &kCFTypeArrayCallBacks);
2211    for (i = 0; i < num_keys; i++)
2212	CFArrayAppendValue(attrs, keys[i].name);
2213    d->inNodeAttributes = attrs;
2214
2215    attrs = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks);
2216    CFArrayAppendValue(attrs, kRealName);
2217    d->inKDCAttributes = attrs;
2218
2219    attrs = CFArrayCreateMutable(NULL, 2, &kCFTypeArrayCallBacks);
2220    CFArrayAppendValue(attrs, kODRecordTypeComputers);
2221    CFArrayAppendValue(attrs, kODRecordTypeUsers);
2222
2223    d->inComputerOrUsers = attrs;
2224
2225    (*db)->hdb_db = d;
2226
2227    (*db)->hdb_capability_flags = HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL;
2228    (*db)->hdb_master_key_set = 0;
2229    (*db)->hdb_openp = 0;
2230    (*db)->hdb_open = hod_open;
2231    (*db)->hdb_close = hod_close;
2232    (*db)->hdb_store = hod_store;
2233    (*db)->hdb_remove = hod_remove;
2234    (*db)->hdb_firstkey = hod_firstkey;
2235    (*db)->hdb_nextkey = hod_nextkey;
2236    (*db)->hdb_lock = hod_lock;
2237    (*db)->hdb_unlock = hod_unlock;
2238    (*db)->hdb_rename = NULL;
2239    (*db)->hdb__get = NULL;
2240    (*db)->hdb__put = NULL;
2241    (*db)->hdb__del = NULL;
2242    (*db)->hdb_destroy = hod_destroy;
2243    (*db)->hdb_get_realms = hod_get_realms;
2244    (*db)->hdb_get_ntlm_domain = hod_get_ntlm_domain;
2245
2246
2247    /*
2248     * The /Local/Default realm is the LKDC realm, so lets pick up the
2249     * LKDC configuration that we will use later.
2250     */
2251
2252    if (strncmp(path, "/Local/", 7) == 0) {
2253
2254	/*
2255	 * Pull out KerberosLocalKDC configuration
2256	 */
2257
2258	ret = nodeCreateWithName(context, d, &localRef);
2259	if (ret)
2260	    goto out;
2261
2262	kdcConfRef = ODNodeCopyRecord(localRef, kODRecordTypeConfiguration,
2263				      CFSTR("KerberosKDC"),
2264				      d->inKDCAttributes, NULL);
2265	if (kdcConfRef == NULL) {
2266	    ret = HDB_ERR_NOENTRY;
2267	    krb5_set_error_message(context, ret, "Failed to find KerberosKDC node");
2268	    goto out;
2269	}
2270
2271	data = ODRecordCopyValues(kdcConfRef, kRealName, NULL);
2272	if (data == NULL) {
2273	    ret = HDB_ERR_NOENTRY;
2274	    krb5_set_error_message(context, ret, "Failed to copy RealName from KerberosKDC node");
2275	    goto out;
2276	}
2277
2278	if (CFArrayGetCount(data) != 1) {
2279	    ret = HDB_ERR_NOENTRY;
2280	    krb5_set_error_message(context, ret, "Found RealName %d from KerberosKDC",
2281				   (int)CFArrayGetCount(data));
2282	    goto out;
2283	}
2284	d->LKDCRealm = rk_cfstring2cstring((CFStringRef)CFArrayGetValueAtIndex(data, 0));
2285	if (d->LKDCRealm == NULL) {
2286	    ret = HDB_ERR_NOENTRY;
2287	    krb5_set_error_message(context, ret, "failed to find realm");
2288	    goto out;
2289	}
2290
2291	CFRelease(data); data = NULL;
2292	CFRelease(kdcConfRef); kdcConfRef = NULL;
2293	CFRelease(localRef); localRef = NULL;
2294
2295	ntlm_notification(d);
2296
2297	(*db)->hdb_fetch_kvno = hod_lkdc_fetch;
2298	d->locate_record = lkdc_locate_record;
2299
2300    } else {
2301	/* hod_password interface is stupid, pipe can fail and we might not expect it to */
2302	/* Not the right place for this. */
2303#ifdef HAVE_SIGACTION
2304	struct sigaction sa;
2305
2306	sa.sa_flags = 0;
2307	sa.sa_handler = SIG_IGN;
2308	sigemptyset(&sa.sa_mask);
2309
2310	sigaction(SIGPIPE, &sa, NULL);
2311#else
2312	signal(SIGPIPE, SIG_IGN);
2313#endif /* HAVE_SIGACTION */
2314
2315	(*db)->hdb_capability_flags |= HDB_CAP_F_HANDLE_PASSWORDS;
2316#ifdef __APPLE_PRIVATE__
2317	(*db)->hdb_password = hod_password;
2318#endif
2319	(*db)->hdb_fetch_kvno = hod_server_fetch;
2320	d->locate_record = server_locate_record;
2321
2322	(*db)->hdb_auth_status = hod_auth_status;
2323    }
2324
2325
2326    free(path);
2327
2328    return 0;
2329
2330  out:
2331    if (path)
2332	free(path);
2333    if (data)
2334	CFRelease(data);
2335    if (kdcConfRef)
2336	CFRelease(kdcConfRef);
2337    if (localRef)
2338	CFRelease(localRef);
2339
2340    if (*db) {
2341	free(*db);
2342	*db = NULL;
2343    }
2344    if (d) {
2345	if (d->rootName)
2346	    CFRelease(d->rootName);
2347	if (d->restoreRoot)
2348	    CFRelease(d->restoreRoot);
2349	free(d);
2350    }
2351
2352    return ret;
2353}
2354
2355#endif
2356