hdb-ldap.c revision 178825
1/*
2 * Copyright (c) 1999-2001, 2003, PADL Software Pty Ltd.
3 * Copyright (c) 2004, Andrew Bartlett.
4 * Copyright (c) 2003 - 2007, Kungliga Tekniska H�gskolan.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * 3. Neither the name of PADL Software  nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include "hdb_locl.h"
36
37RCSID("$Id: hdb-ldap.c 22071 2007-11-14 20:04:50Z lha $");
38
39#ifdef OPENLDAP
40
41#include <lber.h>
42#include <ldap.h>
43#include <sys/un.h>
44#include <hex.h>
45
46static krb5_error_code LDAP__connect(krb5_context context, HDB *);
47static krb5_error_code LDAP_close(krb5_context context, HDB *);
48
49static krb5_error_code
50LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,
51		   hdb_entry_ex * ent);
52
53static const char *default_structural_object = "account";
54static char *structural_object;
55static krb5_boolean samba_forwardable;
56
57struct hdbldapdb {
58    LDAP *h_lp;
59    int   h_msgid;
60    char *h_base;
61    char *h_url;
62    char *h_createbase;
63};
64
65#define HDB2LDAP(db) (((struct hdbldapdb *)(db)->hdb_db)->h_lp)
66#define HDB2MSGID(db) (((struct hdbldapdb *)(db)->hdb_db)->h_msgid)
67#define HDBSETMSGID(db,msgid) \
68	do { ((struct hdbldapdb *)(db)->hdb_db)->h_msgid = msgid; } while(0)
69#define HDB2BASE(dn) (((struct hdbldapdb *)(db)->hdb_db)->h_base)
70#define HDB2URL(dn) (((struct hdbldapdb *)(db)->hdb_db)->h_url)
71#define HDB2CREATE(db) (((struct hdbldapdb *)(db)->hdb_db)->h_createbase)
72
73/*
74 *
75 */
76
77static char * krb5kdcentry_attrs[] = {
78    "cn",
79    "createTimestamp",
80    "creatorsName",
81    "krb5EncryptionType",
82    "krb5KDCFlags",
83    "krb5Key",
84    "krb5KeyVersionNumber",
85    "krb5MaxLife",
86    "krb5MaxRenew",
87    "krb5PasswordEnd",
88    "krb5PrincipalName",
89    "krb5PrincipalRealm",
90    "krb5ValidEnd",
91    "krb5ValidStart",
92    "modifiersName",
93    "modifyTimestamp",
94    "objectClass",
95    "sambaAcctFlags",
96    "sambaKickoffTime",
97    "sambaNTPassword",
98    "sambaPwdLastSet",
99    "sambaPwdMustChange",
100    "uid",
101    NULL
102};
103
104static char *krb5principal_attrs[] = {
105    "cn",
106    "createTimestamp",
107    "creatorsName",
108    "krb5PrincipalName",
109    "krb5PrincipalRealm",
110    "modifiersName",
111    "modifyTimestamp",
112    "objectClass",
113    "uid",
114    NULL
115};
116
117static int
118LDAP_no_size_limit(krb5_context context, LDAP *lp)
119{
120    int ret, limit = LDAP_NO_LIMIT;
121
122    ret = ldap_set_option(lp, LDAP_OPT_SIZELIMIT, (const void *)&limit);
123    if (ret != LDAP_SUCCESS) {
124	krb5_set_error_string(context, "ldap_set_option: %s",
125			      ldap_err2string(ret));
126	return HDB_ERR_BADVERSION;
127    }
128    return 0;
129}
130
131static int
132check_ldap(krb5_context context, HDB *db, int ret)
133{
134    switch (ret) {
135    case LDAP_SUCCESS:
136	return 0;
137    case LDAP_SERVER_DOWN:
138	LDAP_close(context, db);
139	return 1;
140    default:
141	return 1;
142    }
143}
144
145static krb5_error_code
146LDAP__setmod(LDAPMod *** modlist, int modop, const char *attribute,
147	     int *pIndex)
148{
149    int cMods;
150
151    if (*modlist == NULL) {
152	*modlist = (LDAPMod **)ber_memcalloc(1, sizeof(LDAPMod *));
153	if (*modlist == NULL)
154	    return ENOMEM;
155    }
156
157    for (cMods = 0; (*modlist)[cMods] != NULL; cMods++) {
158	if ((*modlist)[cMods]->mod_op == modop &&
159	    strcasecmp((*modlist)[cMods]->mod_type, attribute) == 0) {
160	    break;
161	}
162    }
163
164    *pIndex = cMods;
165
166    if ((*modlist)[cMods] == NULL) {
167	LDAPMod *mod;
168
169	*modlist = (LDAPMod **)ber_memrealloc(*modlist,
170					      (cMods + 2) * sizeof(LDAPMod *));
171	if (*modlist == NULL)
172	    return ENOMEM;
173
174	(*modlist)[cMods] = (LDAPMod *)ber_memalloc(sizeof(LDAPMod));
175	if ((*modlist)[cMods] == NULL)
176	    return ENOMEM;
177
178	mod = (*modlist)[cMods];
179	mod->mod_op = modop;
180	mod->mod_type = ber_strdup(attribute);
181	if (mod->mod_type == NULL) {
182	    ber_memfree(mod);
183	    (*modlist)[cMods] = NULL;
184	    return ENOMEM;
185	}
186
187	if (modop & LDAP_MOD_BVALUES) {
188	    mod->mod_bvalues = NULL;
189	} else {
190	    mod->mod_values = NULL;
191	}
192
193	(*modlist)[cMods + 1] = NULL;
194    }
195
196    return 0;
197}
198
199static krb5_error_code
200LDAP_addmod_len(LDAPMod *** modlist, int modop, const char *attribute,
201		unsigned char *value, size_t len)
202{
203    krb5_error_code ret;
204    int cMods, i = 0;
205
206    ret = LDAP__setmod(modlist, modop | LDAP_MOD_BVALUES, attribute, &cMods);
207    if (ret)
208	return ret;
209
210    if (value != NULL) {
211	struct berval **bv;
212
213	bv = (*modlist)[cMods]->mod_bvalues;
214	if (bv != NULL) {
215	    for (i = 0; bv[i] != NULL; i++)
216		;
217	    bv = ber_memrealloc(bv, (i + 2) * sizeof(*bv));
218	} else
219	    bv = ber_memalloc(2 * sizeof(*bv));
220	if (bv == NULL)
221	    return ENOMEM;
222
223	(*modlist)[cMods]->mod_bvalues = bv;
224
225	bv[i] = ber_memalloc(sizeof(*bv));;
226	if (bv[i] == NULL)
227	    return ENOMEM;
228
229	bv[i]->bv_val = (void *)value;
230	bv[i]->bv_len = len;
231
232	bv[i + 1] = NULL;
233    }
234
235    return 0;
236}
237
238static krb5_error_code
239LDAP_addmod(LDAPMod *** modlist, int modop, const char *attribute,
240	    const char *value)
241{
242    int cMods, i = 0;
243    krb5_error_code ret;
244
245    ret = LDAP__setmod(modlist, modop, attribute, &cMods);
246    if (ret)
247	return ret;
248
249    if (value != NULL) {
250	char **bv;
251
252	bv = (*modlist)[cMods]->mod_values;
253	if (bv != NULL) {
254	    for (i = 0; bv[i] != NULL; i++)
255		;
256	    bv = ber_memrealloc(bv, (i + 2) * sizeof(*bv));
257	} else
258	    bv = ber_memalloc(2 * sizeof(*bv));
259	if (bv == NULL)
260	    return ENOMEM;
261
262	(*modlist)[cMods]->mod_values = bv;
263
264	bv[i] = ber_strdup(value);
265	if (bv[i] == NULL)
266	    return ENOMEM;
267
268	bv[i + 1] = NULL;
269    }
270
271    return 0;
272}
273
274static krb5_error_code
275LDAP_addmod_generalized_time(LDAPMod *** mods, int modop,
276			     const char *attribute, KerberosTime * time)
277{
278    char buf[22];
279    struct tm *tm;
280
281    /* XXX not threadsafe */
282    tm = gmtime(time);
283    strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", tm);
284
285    return LDAP_addmod(mods, modop, attribute, buf);
286}
287
288static krb5_error_code
289LDAP_addmod_integer(krb5_context context,
290		    LDAPMod *** mods, int modop,
291		    const char *attribute, unsigned long l)
292{
293    krb5_error_code ret;
294    char *buf;
295
296    ret = asprintf(&buf, "%ld", l);
297    if (ret < 0) {
298	krb5_set_error_string(context, "asprintf: out of memory:");
299	return ret;
300    }
301    ret = LDAP_addmod(mods, modop, attribute, buf);
302    free (buf);
303    return ret;
304}
305
306static krb5_error_code
307LDAP_get_string_value(HDB * db, LDAPMessage * entry,
308		      const char *attribute, char **ptr)
309{
310    char **vals;
311    int ret;
312
313    vals = ldap_get_values(HDB2LDAP(db), entry, (char *) attribute);
314    if (vals == NULL) {
315	*ptr = NULL;
316	return HDB_ERR_NOENTRY;
317    }
318
319    *ptr = strdup(vals[0]);
320    if (*ptr == NULL)
321	ret = ENOMEM;
322    else
323	ret = 0;
324
325    ldap_value_free(vals);
326
327    return ret;
328}
329
330static krb5_error_code
331LDAP_get_integer_value(HDB * db, LDAPMessage * entry,
332		       const char *attribute, int *ptr)
333{
334    char **vals;
335
336    vals = ldap_get_values(HDB2LDAP(db), entry, (char *) attribute);
337    if (vals == NULL)
338	return HDB_ERR_NOENTRY;
339
340    *ptr = atoi(vals[0]);
341    ldap_value_free(vals);
342    return 0;
343}
344
345static krb5_error_code
346LDAP_get_generalized_time_value(HDB * db, LDAPMessage * entry,
347				const char *attribute, KerberosTime * kt)
348{
349    char *tmp, *gentime;
350    struct tm tm;
351    int ret;
352
353    *kt = 0;
354
355    ret = LDAP_get_string_value(db, entry, attribute, &gentime);
356    if (ret)
357	return ret;
358
359    tmp = strptime(gentime, "%Y%m%d%H%M%SZ", &tm);
360    if (tmp == NULL) {
361	free(gentime);
362	return HDB_ERR_NOENTRY;
363    }
364
365    free(gentime);
366
367    *kt = timegm(&tm);
368
369    return 0;
370}
371
372static krb5_error_code
373LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent,
374		LDAPMessage * msg, LDAPMod *** pmods)
375{
376    krb5_error_code ret;
377    krb5_boolean is_new_entry;
378    char *tmp = NULL;
379    LDAPMod **mods = NULL;
380    hdb_entry_ex orig;
381    unsigned long oflags, nflags;
382    int i;
383
384    krb5_boolean is_samba_account = FALSE;
385    krb5_boolean is_account = FALSE;
386    krb5_boolean is_heimdal_entry = FALSE;
387    krb5_boolean is_heimdal_principal = FALSE;
388
389    char **values;
390
391    *pmods = NULL;
392
393    if (msg != NULL) {
394
395	ret = LDAP_message2entry(context, db, msg, &orig);
396	if (ret)
397	    goto out;
398
399	is_new_entry = FALSE;
400
401	values = ldap_get_values(HDB2LDAP(db), msg, "objectClass");
402	if (values) {
403	    int num_objectclasses = ldap_count_values(values);
404	    for (i=0; i < num_objectclasses; i++) {
405		if (strcasecmp(values[i], "sambaSamAccount") == 0) {
406		    is_samba_account = TRUE;
407		} else if (strcasecmp(values[i], structural_object) == 0) {
408		    is_account = TRUE;
409		} else if (strcasecmp(values[i], "krb5Principal") == 0) {
410		    is_heimdal_principal = TRUE;
411		} else if (strcasecmp(values[i], "krb5KDCEntry") == 0) {
412		    is_heimdal_entry = TRUE;
413		}
414	    }
415	    ldap_value_free(values);
416	}
417
418	/*
419	 * If this is just a "account" entry and no other objectclass
420	 * is hanging on this entry, it's really a new entry.
421	 */
422	if (is_samba_account == FALSE && is_heimdal_principal == FALSE &&
423	    is_heimdal_entry == FALSE) {
424	    if (is_account == TRUE) {
425		is_new_entry = TRUE;
426	    } else {
427		ret = HDB_ERR_NOENTRY;
428		goto out;
429	    }
430	}
431    } else
432	is_new_entry = TRUE;
433
434    if (is_new_entry) {
435
436	/* to make it perfectly obvious we're depending on
437	 * orig being intiialized to zero */
438	memset(&orig, 0, sizeof(orig));
439
440	ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "top");
441	if (ret)
442	    goto out;
443
444	/* account is the structural object class */
445	if (is_account == FALSE) {
446	    ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass",
447			      structural_object);
448	    is_account = TRUE;
449	    if (ret)
450		goto out;
451	}
452
453	ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "krb5Principal");
454	is_heimdal_principal = TRUE;
455	if (ret)
456	    goto out;
457
458	ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "krb5KDCEntry");
459	is_heimdal_entry = TRUE;
460	if (ret)
461	    goto out;
462    }
463
464    if (is_new_entry ||
465	krb5_principal_compare(context, ent->entry.principal, orig.entry.principal)
466	== FALSE)
467    {
468	if (is_heimdal_principal || is_heimdal_entry) {
469
470	    ret = krb5_unparse_name(context, ent->entry.principal, &tmp);
471	    if (ret)
472		goto out;
473
474	    ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE,
475			      "krb5PrincipalName", tmp);
476	    if (ret) {
477		free(tmp);
478		goto out;
479	    }
480	    free(tmp);
481	}
482
483	if (is_account || is_samba_account) {
484	    ret = krb5_unparse_name_short(context, ent->entry.principal, &tmp);
485	    if (ret)
486		goto out;
487	    ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "uid", tmp);
488	    if (ret) {
489		free(tmp);
490		goto out;
491	    }
492	    free(tmp);
493	}
494    }
495
496    if (is_heimdal_entry && (ent->entry.kvno != orig.entry.kvno || is_new_entry)) {
497	ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
498			    "krb5KeyVersionNumber",
499			    ent->entry.kvno);
500	if (ret)
501	    goto out;
502    }
503
504    if (is_heimdal_entry && ent->entry.valid_start) {
505	if (orig.entry.valid_end == NULL
506	    || (*(ent->entry.valid_start) != *(orig.entry.valid_start))) {
507	    ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,
508					       "krb5ValidStart",
509					       ent->entry.valid_start);
510	    if (ret)
511		goto out;
512	}
513    }
514
515    if (ent->entry.valid_end) {
516 	if (orig.entry.valid_end == NULL || (*(ent->entry.valid_end) != *(orig.entry.valid_end))) {
517	    if (is_heimdal_entry) {
518		ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,
519						   "krb5ValidEnd",
520						   ent->entry.valid_end);
521		if (ret)
522		    goto out;
523            }
524	    if (is_samba_account) {
525		ret = LDAP_addmod_integer(context, &mods,  LDAP_MOD_REPLACE,
526					  "sambaKickoffTime",
527					  *(ent->entry.valid_end));
528		if (ret)
529		    goto out;
530	    }
531   	}
532    }
533
534    if (ent->entry.pw_end) {
535	if (orig.entry.pw_end == NULL || (*(ent->entry.pw_end) != *(orig.entry.pw_end))) {
536	    if (is_heimdal_entry) {
537		ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,
538						   "krb5PasswordEnd",
539						   ent->entry.pw_end);
540		if (ret)
541		    goto out;
542	    }
543
544	    if (is_samba_account) {
545		ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
546					  "sambaPwdMustChange",
547					  *(ent->entry.pw_end));
548		if (ret)
549		    goto out;
550	    }
551	}
552    }
553
554
555#if 0 /* we we have last_pw_change */
556    if (is_samba_account && ent->entry.last_pw_change) {
557	if (orig.entry.last_pw_change == NULL || (*(ent->entry.last_pw_change) != *(orig.entry.last_pw_change))) {
558	    ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
559				      "sambaPwdLastSet",
560				      *(ent->entry.last_pw_change));
561	    if (ret)
562		goto out;
563	}
564    }
565#endif
566
567    if (is_heimdal_entry && ent->entry.max_life) {
568	if (orig.entry.max_life == NULL
569	    || (*(ent->entry.max_life) != *(orig.entry.max_life))) {
570
571	    ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
572				      "krb5MaxLife",
573				      *(ent->entry.max_life));
574	    if (ret)
575		goto out;
576	}
577    }
578
579    if (is_heimdal_entry && ent->entry.max_renew) {
580	if (orig.entry.max_renew == NULL
581	    || (*(ent->entry.max_renew) != *(orig.entry.max_renew))) {
582
583	    ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
584				      "krb5MaxRenew",
585				      *(ent->entry.max_renew));
586	    if (ret)
587		goto out;
588	}
589    }
590
591    oflags = HDBFlags2int(orig.entry.flags);
592    nflags = HDBFlags2int(ent->entry.flags);
593
594    if (is_heimdal_entry && oflags != nflags) {
595
596	ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
597				  "krb5KDCFlags",
598				  nflags);
599	if (ret)
600	    goto out;
601    }
602
603    /* Remove keys if they exists, and then replace keys. */
604    if (!is_new_entry && orig.entry.keys.len > 0) {
605	values = ldap_get_values(HDB2LDAP(db), msg, "krb5Key");
606	if (values) {
607	    ldap_value_free(values);
608
609	    ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5Key", NULL);
610	    if (ret)
611		goto out;
612	}
613    }
614
615    for (i = 0; i < ent->entry.keys.len; i++) {
616
617	if (is_samba_account
618	    && ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) {
619	    char *ntHexPassword;
620	    char *nt;
621
622	    /* the key might have been 'sealed', but samba passwords
623	       are clear in the directory */
624	    ret = hdb_unseal_key(context, db, &ent->entry.keys.val[i]);
625	    if (ret)
626		goto out;
627
628	    nt = ent->entry.keys.val[i].key.keyvalue.data;
629	    /* store in ntPassword, not krb5key */
630	    ret = hex_encode(nt, 16, &ntHexPassword);
631	    if (ret < 0) {
632		krb5_set_error_string(context, "hdb-ldap: failed to "
633				      "hex encode key");
634		ret = ENOMEM;
635		goto out;
636	    }
637	    ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "sambaNTPassword",
638			      ntHexPassword);
639	    free(ntHexPassword);
640	    if (ret)
641		goto out;
642
643	    /* have to kill the LM passwod if it exists */
644	    values = ldap_get_values(HDB2LDAP(db), msg, "sambaLMPassword");
645	    if (values) {
646		ldap_value_free(values);
647		ret = LDAP_addmod(&mods, LDAP_MOD_DELETE,
648				  "sambaLMPassword", NULL);
649		if (ret)
650		    goto out;
651	    }
652
653	} else if (is_heimdal_entry) {
654	    unsigned char *buf;
655	    size_t len, buf_size;
656
657	    ASN1_MALLOC_ENCODE(Key, buf, buf_size, &ent->entry.keys.val[i], &len, ret);
658	    if (ret)
659		goto out;
660	    if(buf_size != len)
661		krb5_abortx(context, "internal error in ASN.1 encoder");
662
663	    /* addmod_len _owns_ the key, doesn't need to copy it */
664	    ret = LDAP_addmod_len(&mods, LDAP_MOD_ADD, "krb5Key", buf, len);
665	    if (ret)
666		goto out;
667	}
668    }
669
670    if (ent->entry.etypes) {
671	int add_krb5EncryptionType = 0;
672
673	/*
674	 * Only add/modify krb5EncryptionType if it's a new heimdal
675	 * entry or krb5EncryptionType already exists on the entry.
676	 */
677
678	if (!is_new_entry) {
679	    values = ldap_get_values(HDB2LDAP(db), msg, "krb5EncryptionType");
680	    if (values) {
681		ldap_value_free(values);
682		ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5EncryptionType",
683				  NULL);
684		if (ret)
685		    goto out;
686		add_krb5EncryptionType = 1;
687	    }
688	} else if (is_heimdal_entry)
689	    add_krb5EncryptionType = 1;
690
691	if (add_krb5EncryptionType) {
692	    for (i = 0; i < ent->entry.etypes->len; i++) {
693		if (is_samba_account &&
694		    ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5)
695		{
696		    ;
697		} else if (is_heimdal_entry) {
698		    ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_ADD,
699					      "krb5EncryptionType",
700					      ent->entry.etypes->val[i]);
701		    if (ret)
702			goto out;
703		}
704	    }
705	}
706    }
707
708    /* for clarity */
709    ret = 0;
710
711 out:
712
713    if (ret == 0)
714	*pmods = mods;
715    else if (mods != NULL) {
716	ldap_mods_free(mods, 1);
717	*pmods = NULL;
718    }
719
720    if (msg)
721	hdb_free_entry(context, &orig);
722
723    return ret;
724}
725
726static krb5_error_code
727LDAP_dn2principal(krb5_context context, HDB * db, const char *dn,
728		  krb5_principal * principal)
729{
730    krb5_error_code ret;
731    int rc;
732    const char *filter = "(objectClass=krb5Principal)";
733    char **values;
734    LDAPMessage *res = NULL, *e;
735
736    ret = LDAP_no_size_limit(context, HDB2LDAP(db));
737    if (ret)
738	goto out;
739
740    rc = ldap_search_s(HDB2LDAP(db), dn, LDAP_SCOPE_SUBTREE,
741		       filter, krb5principal_attrs,
742		       0, &res);
743    if (check_ldap(context, db, rc)) {
744	krb5_set_error_string(context, "ldap_search_s: filter: %s error: %s",
745			      filter, ldap_err2string(rc));
746	ret = HDB_ERR_NOENTRY;
747	goto out;
748    }
749
750    e = ldap_first_entry(HDB2LDAP(db), res);
751    if (e == NULL) {
752	ret = HDB_ERR_NOENTRY;
753	goto out;
754    }
755
756    values = ldap_get_values(HDB2LDAP(db), e, "krb5PrincipalName");
757    if (values == NULL) {
758	ret = HDB_ERR_NOENTRY;
759	goto out;
760    }
761
762    ret = krb5_parse_name(context, values[0], principal);
763    ldap_value_free(values);
764
765  out:
766    if (res)
767	ldap_msgfree(res);
768
769    return ret;
770}
771
772static krb5_error_code
773LDAP__lookup_princ(krb5_context context,
774		   HDB *db,
775		   const char *princname,
776		   const char *userid,
777		   LDAPMessage **msg)
778{
779    krb5_error_code ret;
780    int rc;
781    char *filter = NULL;
782
783    ret = LDAP__connect(context, db);
784    if (ret)
785	return ret;
786
787    rc = asprintf(&filter,
788		  "(&(objectClass=krb5Principal)(krb5PrincipalName=%s))",
789		  princname);
790    if (rc < 0) {
791	krb5_set_error_string(context, "asprintf: out of memory");
792	ret = ENOMEM;
793	goto out;
794    }
795
796    ret = LDAP_no_size_limit(context, HDB2LDAP(db));
797    if (ret)
798	goto out;
799
800    rc = ldap_search_s(HDB2LDAP(db), HDB2BASE(db), LDAP_SCOPE_SUBTREE, filter,
801		       krb5kdcentry_attrs, 0, msg);
802    if (check_ldap(context, db, rc)) {
803	krb5_set_error_string(context, "ldap_search_s: filter: %s - error: %s",
804			      filter, ldap_err2string(rc));
805	ret = HDB_ERR_NOENTRY;
806	goto out;
807    }
808
809    if (userid && ldap_count_entries(HDB2LDAP(db), *msg) == 0) {
810	free(filter);
811	filter = NULL;
812	ldap_msgfree(*msg);
813	*msg = NULL;
814
815	rc = asprintf(&filter,
816	    "(&(|(objectClass=sambaSamAccount)(objectClass=%s))(uid=%s))",
817		      structural_object, userid);
818	if (rc < 0) {
819	    krb5_set_error_string(context, "asprintf: out of memory");
820	    ret = ENOMEM;
821	    goto out;
822	}
823
824	ret = LDAP_no_size_limit(context, HDB2LDAP(db));
825	if (ret)
826	    goto out;
827
828	rc = ldap_search_s(HDB2LDAP(db), HDB2BASE(db), LDAP_SCOPE_SUBTREE,
829			   filter, krb5kdcentry_attrs, 0, msg);
830	if (check_ldap(context, db, rc)) {
831	    krb5_set_error_string(context,
832				  "ldap_search_s: filter: %s error: %s",
833				  filter, ldap_err2string(rc));
834	    ret = HDB_ERR_NOENTRY;
835	    goto out;
836	}
837    }
838
839    ret = 0;
840
841  out:
842    if (filter)
843	free(filter);
844
845    return ret;
846}
847
848static krb5_error_code
849LDAP_principal2message(krb5_context context, HDB * db,
850		       krb5_const_principal princ, LDAPMessage ** msg)
851{
852    char *name, *name_short = NULL;
853    krb5_error_code ret;
854    krb5_realm *r, *r0;
855
856    *msg = NULL;
857
858    ret = krb5_unparse_name(context, princ, &name);
859    if (ret)
860	return ret;
861
862    ret = krb5_get_default_realms(context, &r0);
863    if(ret) {
864	free(name);
865	return ret;
866    }
867    for (r = r0; *r != NULL; r++) {
868	if(strcmp(krb5_principal_get_realm(context, princ), *r) == 0) {
869	    ret = krb5_unparse_name_short(context, princ, &name_short);
870	    if (ret) {
871		krb5_free_host_realm(context, r0);
872		free(name);
873		return ret;
874	    }
875	    break;
876	}
877    }
878    krb5_free_host_realm(context, r0);
879
880    ret = LDAP__lookup_princ(context, db, name, name_short, msg);
881    free(name);
882    free(name_short);
883
884    return ret;
885}
886
887/*
888 * Construct an hdb_entry from a directory entry.
889 */
890static krb5_error_code
891LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,
892		   hdb_entry_ex * ent)
893{
894    char *unparsed_name = NULL, *dn = NULL, *ntPasswordIN = NULL;
895    char *samba_acct_flags = NULL;
896    unsigned long tmp;
897    struct berval **keys;
898    char **values;
899    int tmp_time, i, ret, have_arcfour = 0;
900
901    memset(ent, 0, sizeof(*ent));
902    ent->entry.flags = int2HDBFlags(0);
903
904    ret = LDAP_get_string_value(db, msg, "krb5PrincipalName", &unparsed_name);
905    if (ret == 0) {
906	ret = krb5_parse_name(context, unparsed_name, &ent->entry.principal);
907	if (ret)
908	    goto out;
909    } else {
910	ret = LDAP_get_string_value(db, msg, "uid",
911				    &unparsed_name);
912	if (ret == 0) {
913	    ret = krb5_parse_name(context, unparsed_name, &ent->entry.principal);
914	    if (ret)
915		goto out;
916	} else {
917	    krb5_set_error_string(context, "hdb-ldap: ldap entry missing"
918				  "principal name");
919	    return HDB_ERR_NOENTRY;
920	}
921    }
922
923    {
924	int integer;
925	ret = LDAP_get_integer_value(db, msg, "krb5KeyVersionNumber",
926				     &integer);
927	if (ret)
928	    ent->entry.kvno = 0;
929	else
930	    ent->entry.kvno = integer;
931    }
932
933    keys = ldap_get_values_len(HDB2LDAP(db), msg, "krb5Key");
934    if (keys != NULL) {
935	int i;
936	size_t l;
937
938	ent->entry.keys.len = ldap_count_values_len(keys);
939	ent->entry.keys.val = (Key *) calloc(ent->entry.keys.len, sizeof(Key));
940	if (ent->entry.keys.val == NULL) {
941	    krb5_set_error_string(context, "calloc: out of memory");
942	    ret = ENOMEM;
943	    goto out;
944	}
945	for (i = 0; i < ent->entry.keys.len; i++) {
946	    decode_Key((unsigned char *) keys[i]->bv_val,
947		       (size_t) keys[i]->bv_len, &ent->entry.keys.val[i], &l);
948	}
949	ber_bvecfree(keys);
950    } else {
951#if 1
952	/*
953	 * This violates the ASN1 but it allows a principal to
954	 * be related to a general directory entry without creating
955	 * the keys. Hopefully it's OK.
956	 */
957	ent->entry.keys.len = 0;
958	ent->entry.keys.val = NULL;
959#else
960	ret = HDB_ERR_NOENTRY;
961	goto out;
962#endif
963    }
964
965    values = ldap_get_values(HDB2LDAP(db), msg, "krb5EncryptionType");
966    if (values != NULL) {
967	int i;
968
969	ent->entry.etypes = malloc(sizeof(*(ent->entry.etypes)));
970	if (ent->entry.etypes == NULL) {
971	    krb5_set_error_string(context, "malloc: out of memory");
972	    ret = ENOMEM;
973	    goto out;
974	}
975	ent->entry.etypes->len = ldap_count_values(values);
976	ent->entry.etypes->val = calloc(ent->entry.etypes->len, sizeof(int));
977	if (ent->entry.etypes->val == NULL) {
978	    krb5_set_error_string(context, "malloc: out of memory");
979	    ret = ENOMEM;
980	    goto out;
981	}
982	for (i = 0; i < ent->entry.etypes->len; i++) {
983	    ent->entry.etypes->val[i] = atoi(values[i]);
984	}
985	ldap_value_free(values);
986    }
987
988    for (i = 0; i < ent->entry.keys.len; i++) {
989	if (ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) {
990	    have_arcfour = 1;
991	    break;
992	}
993    }
994
995    /* manually construct the NT (type 23) key */
996    ret = LDAP_get_string_value(db, msg, "sambaNTPassword", &ntPasswordIN);
997    if (ret == 0 && have_arcfour == 0) {
998	unsigned *etypes;
999	Key *keys;
1000	int i;
1001
1002	keys = realloc(ent->entry.keys.val,
1003		       (ent->entry.keys.len + 1) * sizeof(ent->entry.keys.val[0]));
1004	if (keys == NULL) {
1005	    free(ntPasswordIN);
1006	    krb5_set_error_string(context, "malloc: out of memory");
1007	    ret = ENOMEM;
1008	    goto out;
1009	}
1010	ent->entry.keys.val = keys;
1011	memset(&ent->entry.keys.val[ent->entry.keys.len], 0, sizeof(Key));
1012	ent->entry.keys.val[ent->entry.keys.len].key.keytype = ETYPE_ARCFOUR_HMAC_MD5;
1013	ret = krb5_data_alloc (&ent->entry.keys.val[ent->entry.keys.len].key.keyvalue, 16);
1014	if (ret) {
1015	    krb5_set_error_string(context, "malloc: out of memory");
1016	    free(ntPasswordIN);
1017	    ret = ENOMEM;
1018	    goto out;
1019	}
1020	ret = hex_decode(ntPasswordIN,
1021			 ent->entry.keys.val[ent->entry.keys.len].key.keyvalue.data, 16);
1022	ent->entry.keys.len++;
1023
1024	if (ent->entry.etypes == NULL) {
1025	    ent->entry.etypes = malloc(sizeof(*(ent->entry.etypes)));
1026	    if (ent->entry.etypes == NULL) {
1027		krb5_set_error_string(context, "malloc: out of memory");
1028		ret = ENOMEM;
1029		goto out;
1030	    }
1031	    ent->entry.etypes->val = NULL;
1032	    ent->entry.etypes->len = 0;
1033	}
1034
1035	for (i = 0; i < ent->entry.etypes->len; i++)
1036	    if (ent->entry.etypes->val[i] == ETYPE_ARCFOUR_HMAC_MD5)
1037		break;
1038	/* If there is no ARCFOUR enctype, add one */
1039	if (i == ent->entry.etypes->len) {
1040	    etypes = realloc(ent->entry.etypes->val,
1041			     (ent->entry.etypes->len + 1) *
1042			     sizeof(ent->entry.etypes->val[0]));
1043	    if (etypes == NULL) {
1044		krb5_set_error_string(context, "malloc: out of memory");
1045		ret = ENOMEM;
1046		goto out;
1047	    }
1048	    ent->entry.etypes->val = etypes;
1049	    ent->entry.etypes->val[ent->entry.etypes->len] =
1050		ETYPE_ARCFOUR_HMAC_MD5;
1051	    ent->entry.etypes->len++;
1052	}
1053    }
1054
1055    ret = LDAP_get_generalized_time_value(db, msg, "createTimestamp",
1056					  &ent->entry.created_by.time);
1057    if (ret)
1058	ent->entry.created_by.time = time(NULL);
1059
1060    ent->entry.created_by.principal = NULL;
1061
1062    ret = LDAP_get_string_value(db, msg, "creatorsName", &dn);
1063    if (ret == 0) {
1064	if (LDAP_dn2principal(context, db, dn, &ent->entry.created_by.principal)
1065	    != 0) {
1066	    ent->entry.created_by.principal = NULL;
1067	}
1068	free(dn);
1069    }
1070
1071    ent->entry.modified_by = (Event *) malloc(sizeof(Event));
1072    if (ent->entry.modified_by == NULL) {
1073	krb5_set_error_string(context, "malloc: out of memory");
1074	ret = ENOMEM;
1075	goto out;
1076    }
1077    ret = LDAP_get_generalized_time_value(db, msg, "modifyTimestamp",
1078					  &ent->entry.modified_by->time);
1079    if (ret == 0) {
1080	ret = LDAP_get_string_value(db, msg, "modifiersName", &dn);
1081	if (LDAP_dn2principal(context, db, dn, &ent->entry.modified_by->principal))
1082	    ent->entry.modified_by->principal = NULL;
1083	free(dn);
1084    } else {
1085	free(ent->entry.modified_by);
1086	ent->entry.modified_by = NULL;
1087    }
1088
1089    ent->entry.valid_start = malloc(sizeof(*ent->entry.valid_start));
1090    if (ent->entry.valid_start == NULL) {
1091	krb5_set_error_string(context, "malloc: out of memory");
1092	ret = ENOMEM;
1093	goto out;
1094    }
1095    ret = LDAP_get_generalized_time_value(db, msg, "krb5ValidStart",
1096					  ent->entry.valid_start);
1097    if (ret) {
1098	/* OPTIONAL */
1099	free(ent->entry.valid_start);
1100	ent->entry.valid_start = NULL;
1101    }
1102
1103    ent->entry.valid_end = malloc(sizeof(*ent->entry.valid_end));
1104    if (ent->entry.valid_end == NULL) {
1105	krb5_set_error_string(context, "malloc: out of memory");
1106	ret = ENOMEM;
1107	goto out;
1108    }
1109    ret = LDAP_get_generalized_time_value(db, msg, "krb5ValidEnd",
1110					  ent->entry.valid_end);
1111    if (ret) {
1112	/* OPTIONAL */
1113	free(ent->entry.valid_end);
1114	ent->entry.valid_end = NULL;
1115    }
1116
1117    ret = LDAP_get_integer_value(db, msg, "sambaKickoffTime", &tmp_time);
1118    if (ret == 0) {
1119 	if (ent->entry.valid_end == NULL) {
1120 	    ent->entry.valid_end = malloc(sizeof(*ent->entry.valid_end));
1121 	    if (ent->entry.valid_end == NULL) {
1122 		krb5_set_error_string(context, "malloc: out of memory");
1123 		ret = ENOMEM;
1124 		goto out;
1125 	    }
1126 	}
1127 	*ent->entry.valid_end = tmp_time;
1128    }
1129
1130    ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end));
1131    if (ent->entry.pw_end == NULL) {
1132	krb5_set_error_string(context, "malloc: out of memory");
1133	ret = ENOMEM;
1134	goto out;
1135    }
1136    ret = LDAP_get_generalized_time_value(db, msg, "krb5PasswordEnd",
1137					  ent->entry.pw_end);
1138    if (ret) {
1139	/* OPTIONAL */
1140	free(ent->entry.pw_end);
1141	ent->entry.pw_end = NULL;
1142    }
1143
1144    ret = LDAP_get_integer_value(db, msg, "sambaPwdMustChange", &tmp_time);
1145    if (ret == 0) {
1146	if (ent->entry.pw_end == NULL) {
1147	    ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end));
1148	    if (ent->entry.pw_end == NULL) {
1149		krb5_set_error_string(context, "malloc: out of memory");
1150		ret = ENOMEM;
1151		goto out;
1152	    }
1153	}
1154	*ent->entry.pw_end = tmp_time;
1155    }
1156
1157    /* OPTIONAL */
1158    ret = LDAP_get_integer_value(db, msg, "sambaPwdLastSet", &tmp_time);
1159    if (ret == 0)
1160	hdb_entry_set_pw_change_time(context, &ent->entry, tmp_time);
1161
1162    {
1163	int max_life;
1164
1165	ent->entry.max_life = malloc(sizeof(*ent->entry.max_life));
1166	if (ent->entry.max_life == NULL) {
1167	    krb5_set_error_string(context, "malloc: out of memory");
1168	    ret = ENOMEM;
1169	    goto out;
1170	}
1171	ret = LDAP_get_integer_value(db, msg, "krb5MaxLife", &max_life);
1172	if (ret) {
1173	    free(ent->entry.max_life);
1174	    ent->entry.max_life = NULL;
1175	} else
1176	    *ent->entry.max_life = max_life;
1177    }
1178
1179    {
1180	int max_renew;
1181
1182	ent->entry.max_renew = malloc(sizeof(*ent->entry.max_renew));
1183	if (ent->entry.max_renew == NULL) {
1184	    krb5_set_error_string(context, "malloc: out of memory");
1185	    ret = ENOMEM;
1186	    goto out;
1187	}
1188	ret = LDAP_get_integer_value(db, msg, "krb5MaxRenew", &max_renew);
1189	if (ret) {
1190	    free(ent->entry.max_renew);
1191	    ent->entry.max_renew = NULL;
1192	} else
1193	    *ent->entry.max_renew = max_renew;
1194    }
1195
1196    values = ldap_get_values(HDB2LDAP(db), msg, "krb5KDCFlags");
1197    if (values != NULL) {
1198	errno = 0;
1199	tmp = strtoul(values[0], (char **) NULL, 10);
1200	if (tmp == ULONG_MAX && errno == ERANGE) {
1201	    krb5_set_error_string(context, "strtoul: could not convert flag");
1202	    ret = ERANGE;
1203	    goto out;
1204	}
1205    } else {
1206	tmp = 0;
1207    }
1208
1209    ent->entry.flags = int2HDBFlags(tmp);
1210
1211    /* Try and find Samba flags to put into the mix */
1212    ret = LDAP_get_string_value(db, msg, "sambaAcctFlags", &samba_acct_flags);
1213    if (ret == 0) {
1214	/* parse the [UXW...] string:
1215
1216	   'N'    No password
1217	   'D'    Disabled
1218	   'H'    Homedir required
1219	   'T'    Temp account.
1220	   'U'    User account (normal)
1221	   'M'    MNS logon user account - what is this ?
1222	   'W'    Workstation account
1223	   'S'    Server account
1224	   'L'    Locked account
1225	   'X'    No Xpiry on password
1226	   'I'    Interdomain trust account
1227
1228	*/
1229
1230	int i;
1231	int flags_len = strlen(samba_acct_flags);
1232
1233	if (flags_len < 2)
1234	    goto out2;
1235
1236	if (samba_acct_flags[0] != '['
1237	    || samba_acct_flags[flags_len - 1] != ']')
1238	    goto out2;
1239
1240	/* Allow forwarding */
1241	if (samba_forwardable)
1242	    ent->entry.flags.forwardable = TRUE;
1243
1244	for (i=0; i < flags_len; i++) {
1245	    switch (samba_acct_flags[i]) {
1246	    case ' ':
1247	    case '[':
1248	    case ']':
1249		break;
1250	    case 'N':
1251		/* how to handle no password in kerberos? */
1252		break;
1253	    case 'D':
1254		ent->entry.flags.invalid = TRUE;
1255		break;
1256	    case 'H':
1257		break;
1258	    case 'T':
1259		/* temp duplicate */
1260		ent->entry.flags.invalid = TRUE;
1261		break;
1262	    case 'U':
1263		ent->entry.flags.client = TRUE;
1264		break;
1265	    case 'M':
1266		break;
1267	    case 'W':
1268	    case 'S':
1269		ent->entry.flags.server = TRUE;
1270		ent->entry.flags.client = TRUE;
1271		break;
1272	    case 'L':
1273		ent->entry.flags.invalid = TRUE;
1274		break;
1275	    case 'X':
1276		if (ent->entry.pw_end) {
1277		    free(ent->entry.pw_end);
1278		    ent->entry.pw_end = NULL;
1279		}
1280		break;
1281	    case 'I':
1282		ent->entry.flags.server = TRUE;
1283		ent->entry.flags.client = TRUE;
1284		break;
1285	    }
1286	}
1287    out2:
1288	free(samba_acct_flags);
1289    }
1290
1291    ret = 0;
1292
1293out:
1294    if (unparsed_name)
1295	free(unparsed_name);
1296
1297    if (ret)
1298	hdb_free_entry(context, ent);
1299
1300    return ret;
1301}
1302
1303static krb5_error_code
1304LDAP_close(krb5_context context, HDB * db)
1305{
1306    if (HDB2LDAP(db)) {
1307	ldap_unbind_ext(HDB2LDAP(db), NULL, NULL);
1308	((struct hdbldapdb *)db->hdb_db)->h_lp = NULL;
1309    }
1310
1311    return 0;
1312}
1313
1314static krb5_error_code
1315LDAP_lock(krb5_context context, HDB * db, int operation)
1316{
1317    return 0;
1318}
1319
1320static krb5_error_code
1321LDAP_unlock(krb5_context context, HDB * db)
1322{
1323    return 0;
1324}
1325
1326static krb5_error_code
1327LDAP_seq(krb5_context context, HDB * db, unsigned flags, hdb_entry_ex * entry)
1328{
1329    int msgid, rc, parserc;
1330    krb5_error_code ret;
1331    LDAPMessage *e;
1332
1333    msgid = HDB2MSGID(db);
1334    if (msgid < 0)
1335	return HDB_ERR_NOENTRY;
1336
1337    do {
1338	rc = ldap_result(HDB2LDAP(db), msgid, LDAP_MSG_ONE, NULL, &e);
1339	switch (rc) {
1340	case LDAP_RES_SEARCH_REFERENCE:
1341	    ldap_msgfree(e);
1342	    ret = 0;
1343	    break;
1344	case LDAP_RES_SEARCH_ENTRY:
1345	    /* We have an entry. Parse it. */
1346	    ret = LDAP_message2entry(context, db, e, entry);
1347	    ldap_msgfree(e);
1348	    break;
1349	case LDAP_RES_SEARCH_RESULT:
1350	    /* We're probably at the end of the results. If not, abandon. */
1351	    parserc =
1352		ldap_parse_result(HDB2LDAP(db), e, NULL, NULL, NULL,
1353				  NULL, NULL, 1);
1354	    if (parserc != LDAP_SUCCESS
1355		&& parserc != LDAP_MORE_RESULTS_TO_RETURN) {
1356	        krb5_set_error_string(context, "ldap_parse_result: %s",
1357				      ldap_err2string(parserc));
1358		ldap_abandon(HDB2LDAP(db), msgid);
1359	    }
1360	    ret = HDB_ERR_NOENTRY;
1361	    HDBSETMSGID(db, -1);
1362	    break;
1363	case LDAP_SERVER_DOWN:
1364	    ldap_msgfree(e);
1365	    LDAP_close(context, db);
1366	    HDBSETMSGID(db, -1);
1367	    ret = ENETDOWN;
1368	    break;
1369	default:
1370	    /* Some unspecified error (timeout?). Abandon. */
1371	    ldap_msgfree(e);
1372	    ldap_abandon(HDB2LDAP(db), msgid);
1373	    ret = HDB_ERR_NOENTRY;
1374	    HDBSETMSGID(db, -1);
1375	    break;
1376	}
1377    } while (rc == LDAP_RES_SEARCH_REFERENCE);
1378
1379    if (ret == 0) {
1380	if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
1381	    ret = hdb_unseal_keys(context, db, &entry->entry);
1382	    if (ret)
1383		hdb_free_entry(context, entry);
1384	}
1385    }
1386
1387    return ret;
1388}
1389
1390static krb5_error_code
1391LDAP_firstkey(krb5_context context, HDB *db, unsigned flags,
1392	      hdb_entry_ex *entry)
1393{
1394    krb5_error_code ret;
1395    int msgid;
1396
1397    ret = LDAP__connect(context, db);
1398    if (ret)
1399	return ret;
1400
1401    ret = LDAP_no_size_limit(context, HDB2LDAP(db));
1402    if (ret)
1403	return ret;
1404
1405    msgid = ldap_search(HDB2LDAP(db), HDB2BASE(db),
1406			LDAP_SCOPE_SUBTREE,
1407			"(|(objectClass=krb5Principal)(objectClass=sambaSamAccount))",
1408			krb5kdcentry_attrs, 0);
1409    if (msgid < 0)
1410	return HDB_ERR_NOENTRY;
1411
1412    HDBSETMSGID(db, msgid);
1413
1414    return LDAP_seq(context, db, flags, entry);
1415}
1416
1417static krb5_error_code
1418LDAP_nextkey(krb5_context context, HDB * db, unsigned flags,
1419	     hdb_entry_ex * entry)
1420{
1421    return LDAP_seq(context, db, flags, entry);
1422}
1423
1424static krb5_error_code
1425LDAP__connect(krb5_context context, HDB * db)
1426{
1427    int rc, version = LDAP_VERSION3;
1428    /*
1429     * Empty credentials to do a SASL bind with LDAP. Note that empty
1430     * different from NULL credentials. If you provide NULL
1431     * credentials instead of empty credentials you will get a SASL
1432     * bind in progress message.
1433     */
1434    struct berval bv = { 0, "" };
1435
1436    if (HDB2LDAP(db)) {
1437	/* connection has been opened. ping server. */
1438	struct sockaddr_un addr;
1439	socklen_t len = sizeof(addr);
1440	int sd;
1441
1442	if (ldap_get_option(HDB2LDAP(db), LDAP_OPT_DESC, &sd) == 0 &&
1443	    getpeername(sd, (struct sockaddr *) &addr, &len) < 0) {
1444	    /* the other end has died. reopen. */
1445	    LDAP_close(context, db);
1446	}
1447    }
1448
1449    if (HDB2LDAP(db) != NULL) /* server is UP */
1450	return 0;
1451
1452    rc = ldap_initialize(&((struct hdbldapdb *)db->hdb_db)->h_lp, HDB2URL(db));
1453    if (rc != LDAP_SUCCESS) {
1454	krb5_set_error_string(context, "ldap_initialize: %s",
1455			      ldap_err2string(rc));
1456	return HDB_ERR_NOENTRY;
1457    }
1458
1459    rc = ldap_set_option(HDB2LDAP(db), LDAP_OPT_PROTOCOL_VERSION,
1460			 (const void *)&version);
1461    if (rc != LDAP_SUCCESS) {
1462	krb5_set_error_string(context, "ldap_set_option: %s",
1463			      ldap_err2string(rc));
1464	LDAP_close(context, db);
1465	return HDB_ERR_BADVERSION;
1466    }
1467
1468    rc = ldap_sasl_bind_s(HDB2LDAP(db), NULL, "EXTERNAL", &bv,
1469			  NULL, NULL, NULL);
1470    if (rc != LDAP_SUCCESS) {
1471	krb5_set_error_string(context, "ldap_sasl_bind_s: %s",
1472			      ldap_err2string(rc));
1473	LDAP_close(context, db);
1474	return HDB_ERR_BADVERSION;
1475    }
1476
1477    return 0;
1478}
1479
1480static krb5_error_code
1481LDAP_open(krb5_context context, HDB * db, int flags, mode_t mode)
1482{
1483    /* Not the right place for this. */
1484#ifdef HAVE_SIGACTION
1485    struct sigaction sa;
1486
1487    sa.sa_flags = 0;
1488    sa.sa_handler = SIG_IGN;
1489    sigemptyset(&sa.sa_mask);
1490
1491    sigaction(SIGPIPE, &sa, NULL);
1492#else
1493    signal(SIGPIPE, SIG_IGN);
1494#endif /* HAVE_SIGACTION */
1495
1496    return LDAP__connect(context, db);
1497}
1498
1499static krb5_error_code
1500LDAP_fetch(krb5_context context, HDB * db, krb5_const_principal principal,
1501	   unsigned flags, hdb_entry_ex * entry)
1502{
1503    LDAPMessage *msg, *e;
1504    krb5_error_code ret;
1505
1506    ret = LDAP_principal2message(context, db, principal, &msg);
1507    if (ret)
1508	return ret;
1509
1510    e = ldap_first_entry(HDB2LDAP(db), msg);
1511    if (e == NULL) {
1512	ret = HDB_ERR_NOENTRY;
1513	goto out;
1514    }
1515
1516    ret = LDAP_message2entry(context, db, e, entry);
1517    if (ret == 0) {
1518	if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
1519	    ret = hdb_unseal_keys(context, db, &entry->entry);
1520	    if (ret)
1521		hdb_free_entry(context, entry);
1522	}
1523    }
1524
1525  out:
1526    ldap_msgfree(msg);
1527
1528    return ret;
1529}
1530
1531static krb5_error_code
1532LDAP_store(krb5_context context, HDB * db, unsigned flags,
1533	   hdb_entry_ex * entry)
1534{
1535    LDAPMod **mods = NULL;
1536    krb5_error_code ret;
1537    const char *errfn;
1538    int rc;
1539    LDAPMessage *msg = NULL, *e = NULL;
1540    char *dn = NULL, *name = NULL;
1541
1542    ret = LDAP_principal2message(context, db, entry->entry.principal, &msg);
1543    if (ret == 0)
1544	e = ldap_first_entry(HDB2LDAP(db), msg);
1545
1546    ret = krb5_unparse_name(context, entry->entry.principal, &name);
1547    if (ret) {
1548	free(name);
1549	return ret;
1550    }
1551
1552    ret = hdb_seal_keys(context, db, &entry->entry);
1553    if (ret)
1554	goto out;
1555
1556    /* turn new entry into LDAPMod array */
1557    ret = LDAP_entry2mods(context, db, entry, e, &mods);
1558    if (ret)
1559	goto out;
1560
1561    if (e == NULL) {
1562	ret = asprintf(&dn, "krb5PrincipalName=%s,%s", name, HDB2CREATE(db));
1563	if (ret < 0) {
1564	    krb5_set_error_string(context, "asprintf: out of memory");
1565	    ret = ENOMEM;
1566	    goto out;
1567	}
1568    } else if (flags & HDB_F_REPLACE) {
1569	/* Entry exists, and we're allowed to replace it. */
1570	dn = ldap_get_dn(HDB2LDAP(db), e);
1571    } else {
1572	/* Entry exists, but we're not allowed to replace it. Bail. */
1573	ret = HDB_ERR_EXISTS;
1574	goto out;
1575    }
1576
1577    /* write entry into directory */
1578    if (e == NULL) {
1579	/* didn't exist before */
1580	rc = ldap_add_s(HDB2LDAP(db), dn, mods);
1581	errfn = "ldap_add_s";
1582    } else {
1583	/* already existed, send deltas only */
1584	rc = ldap_modify_s(HDB2LDAP(db), dn, mods);
1585	errfn = "ldap_modify_s";
1586    }
1587
1588    if (check_ldap(context, db, rc)) {
1589	char *ld_error = NULL;
1590	ldap_get_option(HDB2LDAP(db), LDAP_OPT_ERROR_STRING,
1591			&ld_error);
1592	krb5_set_error_string(context, "%s: %s (DN=%s) %s: %s",
1593			      errfn, name, dn, ldap_err2string(rc), ld_error);
1594	ret = HDB_ERR_CANT_LOCK_DB;
1595    } else
1596	ret = 0;
1597
1598  out:
1599    /* free stuff */
1600    if (dn)
1601	free(dn);
1602    if (msg)
1603	ldap_msgfree(msg);
1604    if (mods)
1605	ldap_mods_free(mods, 1);
1606    if (name)
1607	free(name);
1608
1609    return ret;
1610}
1611
1612static krb5_error_code
1613LDAP_remove(krb5_context context, HDB *db, krb5_const_principal principal)
1614{
1615    krb5_error_code ret;
1616    LDAPMessage *msg, *e;
1617    char *dn = NULL;
1618    int rc, limit = LDAP_NO_LIMIT;
1619
1620    ret = LDAP_principal2message(context, db, principal, &msg);
1621    if (ret)
1622	goto out;
1623
1624    e = ldap_first_entry(HDB2LDAP(db), msg);
1625    if (e == NULL) {
1626	ret = HDB_ERR_NOENTRY;
1627	goto out;
1628    }
1629
1630    dn = ldap_get_dn(HDB2LDAP(db), e);
1631    if (dn == NULL) {
1632	ret = HDB_ERR_NOENTRY;
1633	goto out;
1634    }
1635
1636    rc = ldap_set_option(HDB2LDAP(db), LDAP_OPT_SIZELIMIT, (const void *)&limit);
1637    if (rc != LDAP_SUCCESS) {
1638	krb5_set_error_string(context, "ldap_set_option: %s",
1639			      ldap_err2string(rc));
1640	ret = HDB_ERR_BADVERSION;
1641	goto out;
1642    }
1643
1644    rc = ldap_delete_s(HDB2LDAP(db), dn);
1645    if (check_ldap(context, db, rc)) {
1646	krb5_set_error_string(context, "ldap_delete_s: %s",
1647			      ldap_err2string(rc));
1648	ret = HDB_ERR_CANT_LOCK_DB;
1649    } else
1650	ret = 0;
1651
1652  out:
1653    if (dn != NULL)
1654	free(dn);
1655    if (msg != NULL)
1656	ldap_msgfree(msg);
1657
1658    return ret;
1659}
1660
1661static krb5_error_code
1662LDAP_destroy(krb5_context context, HDB * db)
1663{
1664    krb5_error_code ret;
1665
1666    LDAP_close(context, db);
1667
1668    ret = hdb_clear_master_key(context, db);
1669    if (HDB2BASE(db))
1670	free(HDB2BASE(db));
1671    if (HDB2CREATE(db))
1672	free(HDB2CREATE(db));
1673    if (HDB2URL(db))
1674	free(HDB2URL(db));
1675    if (db->hdb_name)
1676	free(db->hdb_name);
1677    free(db->hdb_db);
1678    free(db);
1679
1680    return ret;
1681}
1682
1683krb5_error_code
1684hdb_ldap_common(krb5_context context,
1685		HDB ** db,
1686		const char *search_base,
1687		const char *url)
1688{
1689    struct hdbldapdb *h;
1690    const char *create_base = NULL;
1691
1692    if (search_base == NULL && search_base[0] == '\0') {
1693	krb5_set_error_string(context, "ldap search base not configured");
1694	return ENOMEM; /* XXX */
1695    }
1696
1697    if (structural_object == NULL) {
1698	const char *p;
1699
1700	p = krb5_config_get_string(context, NULL, "kdc",
1701				   "hdb-ldap-structural-object", NULL);
1702	if (p == NULL)
1703	    p = default_structural_object;
1704	structural_object = strdup(p);
1705	if (structural_object == NULL) {
1706	    krb5_set_error_string(context, "malloc: out of memory");
1707	    return ENOMEM;
1708	}
1709    }
1710
1711    samba_forwardable =
1712	krb5_config_get_bool_default(context, NULL, TRUE,
1713				     "kdc", "hdb-samba-forwardable", NULL);
1714
1715    *db = calloc(1, sizeof(**db));
1716    if (*db == NULL) {
1717	krb5_set_error_string(context, "malloc: out of memory");
1718	return ENOMEM;
1719    }
1720    memset(*db, 0, sizeof(**db));
1721
1722    h = calloc(1, sizeof(*h));
1723    if (h == NULL) {
1724	krb5_set_error_string(context, "malloc: out of memory");
1725	free(*db);
1726	*db = NULL;
1727	return ENOMEM;
1728    }
1729    (*db)->hdb_db = h;
1730
1731    /* XXX */
1732    if (asprintf(&(*db)->hdb_name, "ldap:%s", search_base) == -1) {
1733	LDAP_destroy(context, *db);
1734	krb5_set_error_string(context, "strdup: out of memory");
1735	*db = NULL;
1736	return ENOMEM;
1737    }
1738
1739    h->h_url = strdup(url);
1740    h->h_base = strdup(search_base);
1741    if (h->h_url == NULL || h->h_base == NULL) {
1742	LDAP_destroy(context, *db);
1743	krb5_set_error_string(context, "strdup: out of memory");
1744	*db = NULL;
1745	return ENOMEM;
1746    }
1747
1748    create_base = krb5_config_get_string(context, NULL, "kdc",
1749					 "hdb-ldap-create-base", NULL);
1750    if (create_base == NULL)
1751	create_base = h->h_base;
1752
1753    h->h_createbase = strdup(create_base);
1754    if (h->h_createbase == NULL) {
1755	LDAP_destroy(context, *db);
1756	krb5_set_error_string(context, "strdup: out of memory");
1757	*db = NULL;
1758	return ENOMEM;
1759    }
1760
1761    (*db)->hdb_master_key_set = 0;
1762    (*db)->hdb_openp = 0;
1763    (*db)->hdb_open = LDAP_open;
1764    (*db)->hdb_close = LDAP_close;
1765    (*db)->hdb_fetch = LDAP_fetch;
1766    (*db)->hdb_store = LDAP_store;
1767    (*db)->hdb_remove = LDAP_remove;
1768    (*db)->hdb_firstkey = LDAP_firstkey;
1769    (*db)->hdb_nextkey = LDAP_nextkey;
1770    (*db)->hdb_lock = LDAP_lock;
1771    (*db)->hdb_unlock = LDAP_unlock;
1772    (*db)->hdb_rename = NULL;
1773    (*db)->hdb__get = NULL;
1774    (*db)->hdb__put = NULL;
1775    (*db)->hdb__del = NULL;
1776    (*db)->hdb_destroy = LDAP_destroy;
1777
1778    return 0;
1779}
1780
1781krb5_error_code
1782hdb_ldap_create(krb5_context context, HDB ** db, const char *arg)
1783{
1784    return hdb_ldap_common(context, db, arg, "ldapi:///");
1785}
1786
1787krb5_error_code
1788hdb_ldapi_create(krb5_context context, HDB ** db, const char *arg)
1789{
1790    krb5_error_code ret;
1791    char *search_base, *p;
1792
1793    asprintf(&p, "ldapi:%s", arg);
1794    if (p == NULL) {
1795	krb5_set_error_string(context, "out of memory");
1796	*db = NULL;
1797	return ENOMEM;
1798    }
1799    search_base = strchr(p + strlen("ldapi://"), ':');
1800    if (search_base == NULL) {
1801	krb5_set_error_string(context, "search base missing");
1802	*db = NULL;
1803	return HDB_ERR_BADVERSION;
1804    }
1805    *search_base = '\0';
1806    search_base++;
1807
1808    ret = hdb_ldap_common(context, db, search_base, p);
1809    free(p);
1810    return ret;
1811}
1812
1813#ifdef OPENLDAP_MODULE
1814
1815struct hdb_so_method hdb_ldap_interface = {
1816    HDB_INTERFACE_VERSION,
1817    "ldap",
1818    hdb_ldap_create
1819};
1820
1821struct hdb_so_method hdb_ldapi_interface = {
1822    HDB_INTERFACE_VERSION,
1823    "ldapi",
1824    hdb_ldapi_create
1825};
1826
1827#endif
1828
1829#endif				/* OPENLDAP */
1830