172445Sassar/*
2178825Sdfr * Copyright (c) 1999-2001, 2003, PADL Software Pty Ltd.
3178825Sdfr * Copyright (c) 2004, Andrew Bartlett.
4233294Sstas * Copyright (c) 2003 - 2008, Kungliga Tekniska H��gskolan.
572445Sassar * All rights reserved.
672445Sassar *
772445Sassar * Redistribution and use in source and binary forms, with or without
872445Sassar * modification, are permitted provided that the following conditions
972445Sassar * are met:
1072445Sassar *
1172445Sassar * 1. Redistributions of source code must retain the above copyright
1272445Sassar *    notice, this list of conditions and the following disclaimer.
1372445Sassar *
1472445Sassar * 2. Redistributions in binary form must reproduce the above copyright
1572445Sassar *    notice, this list of conditions and the following disclaimer in the
1672445Sassar *    documentation and/or other materials provided with the distribution.
1772445Sassar *
1872445Sassar * 3. Neither the name of PADL Software  nor the names of its contributors
1972445Sassar *    may be used to endorse or promote products derived from this software
2072445Sassar *    without specific prior written permission.
2172445Sassar *
2272445Sassar * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
2372445Sassar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2472445Sassar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2572445Sassar * ARE DISCLAIMED.  IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
2672445Sassar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2772445Sassar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2872445Sassar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2972445Sassar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3072445Sassar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3172445Sassar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3272445Sassar * SUCH DAMAGE.
3372445Sassar */
3472445Sassar
3572445Sassar#include "hdb_locl.h"
3672445Sassar
3772445Sassar#ifdef OPENLDAP
3872445Sassar
3978527Sassar#include <lber.h>
4072445Sassar#include <ldap.h>
4172445Sassar#include <sys/un.h>
42178825Sdfr#include <hex.h>
4372445Sassar
44178825Sdfrstatic krb5_error_code LDAP__connect(krb5_context context, HDB *);
45178825Sdfrstatic krb5_error_code LDAP_close(krb5_context context, HDB *);
4672445Sassar
4772445Sassarstatic krb5_error_code
4872445SassarLDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,
49233294Sstas		   int flags, hdb_entry_ex * ent);
5072445Sassar
51178825Sdfrstatic const char *default_structural_object = "account";
52178825Sdfrstatic char *structural_object;
53178825Sdfrstatic krb5_boolean samba_forwardable;
54178825Sdfr
55178825Sdfrstruct hdbldapdb {
56178825Sdfr    LDAP *h_lp;
57178825Sdfr    int   h_msgid;
58178825Sdfr    char *h_base;
59178825Sdfr    char *h_url;
60178825Sdfr    char *h_createbase;
61178825Sdfr};
62178825Sdfr
63178825Sdfr#define HDB2LDAP(db) (((struct hdbldapdb *)(db)->hdb_db)->h_lp)
64178825Sdfr#define HDB2MSGID(db) (((struct hdbldapdb *)(db)->hdb_db)->h_msgid)
65178825Sdfr#define HDBSETMSGID(db,msgid) \
66178825Sdfr	do { ((struct hdbldapdb *)(db)->hdb_db)->h_msgid = msgid; } while(0)
67178825Sdfr#define HDB2BASE(dn) (((struct hdbldapdb *)(db)->hdb_db)->h_base)
68178825Sdfr#define HDB2URL(dn) (((struct hdbldapdb *)(db)->hdb_db)->h_url)
69178825Sdfr#define HDB2CREATE(db) (((struct hdbldapdb *)(db)->hdb_db)->h_createbase)
70178825Sdfr
71178825Sdfr/*
72178825Sdfr *
73178825Sdfr */
74178825Sdfr
75233294Sstasstatic char * krb5kdcentry_attrs[] = {
76178825Sdfr    "cn",
77178825Sdfr    "createTimestamp",
78178825Sdfr    "creatorsName",
79178825Sdfr    "krb5EncryptionType",
80178825Sdfr    "krb5KDCFlags",
81178825Sdfr    "krb5Key",
82178825Sdfr    "krb5KeyVersionNumber",
83178825Sdfr    "krb5MaxLife",
84178825Sdfr    "krb5MaxRenew",
85178825Sdfr    "krb5PasswordEnd",
86178825Sdfr    "krb5PrincipalName",
87178825Sdfr    "krb5PrincipalRealm",
88178825Sdfr    "krb5ValidEnd",
89178825Sdfr    "krb5ValidStart",
90178825Sdfr    "modifiersName",
91178825Sdfr    "modifyTimestamp",
92178825Sdfr    "objectClass",
93178825Sdfr    "sambaAcctFlags",
94178825Sdfr    "sambaKickoffTime",
95178825Sdfr    "sambaNTPassword",
96178825Sdfr    "sambaPwdLastSet",
97178825Sdfr    "sambaPwdMustChange",
98178825Sdfr    "uid",
9972445Sassar    NULL
10072445Sassar};
10172445Sassar
102178825Sdfrstatic char *krb5principal_attrs[] = {
103178825Sdfr    "cn",
104178825Sdfr    "createTimestamp",
105178825Sdfr    "creatorsName",
106178825Sdfr    "krb5PrincipalName",
107178825Sdfr    "krb5PrincipalRealm",
108178825Sdfr    "modifiersName",
109178825Sdfr    "modifyTimestamp",
110178825Sdfr    "objectClass",
111178825Sdfr    "uid",
11272445Sassar    NULL
11372445Sassar};
11472445Sassar
115178825Sdfrstatic int
116178825SdfrLDAP_no_size_limit(krb5_context context, LDAP *lp)
117178825Sdfr{
118178825Sdfr    int ret, limit = LDAP_NO_LIMIT;
119178825Sdfr
120178825Sdfr    ret = ldap_set_option(lp, LDAP_OPT_SIZELIMIT, (const void *)&limit);
121178825Sdfr    if (ret != LDAP_SUCCESS) {
122233294Sstas	krb5_set_error_message(context, HDB_ERR_BADVERSION,
123233294Sstas			       "ldap_set_option: %s",
124233294Sstas			       ldap_err2string(ret));
125178825Sdfr	return HDB_ERR_BADVERSION;
126178825Sdfr    }
127178825Sdfr    return 0;
128178825Sdfr}
129178825Sdfr
130178825Sdfrstatic int
131178825Sdfrcheck_ldap(krb5_context context, HDB *db, int ret)
132178825Sdfr{
133178825Sdfr    switch (ret) {
134178825Sdfr    case LDAP_SUCCESS:
135178825Sdfr	return 0;
136178825Sdfr    case LDAP_SERVER_DOWN:
137178825Sdfr	LDAP_close(context, db);
138178825Sdfr	return 1;
139178825Sdfr    default:
140178825Sdfr	return 1;
141178825Sdfr    }
142178825Sdfr}
143178825Sdfr
14472445Sassarstatic krb5_error_code
145127808SnectarLDAP__setmod(LDAPMod *** modlist, int modop, const char *attribute,
146178825Sdfr	     int *pIndex)
14772445Sassar{
148127808Snectar    int cMods;
14972445Sassar
150127808Snectar    if (*modlist == NULL) {
151127808Snectar	*modlist = (LDAPMod **)ber_memcalloc(1, sizeof(LDAPMod *));
152178825Sdfr	if (*modlist == NULL)
15372445Sassar	    return ENOMEM;
15472445Sassar    }
15572445Sassar
156127808Snectar    for (cMods = 0; (*modlist)[cMods] != NULL; cMods++) {
157127808Snectar	if ((*modlist)[cMods]->mod_op == modop &&
158127808Snectar	    strcasecmp((*modlist)[cMods]->mod_type, attribute) == 0) {
15972445Sassar	    break;
16072445Sassar	}
16172445Sassar    }
16272445Sassar
163127808Snectar    *pIndex = cMods;
164127808Snectar
165127808Snectar    if ((*modlist)[cMods] == NULL) {
166127808Snectar	LDAPMod *mod;
167127808Snectar
168127808Snectar	*modlist = (LDAPMod **)ber_memrealloc(*modlist,
169127808Snectar					      (cMods + 2) * sizeof(LDAPMod *));
170178825Sdfr	if (*modlist == NULL)
17172445Sassar	    return ENOMEM;
172178825Sdfr
173127808Snectar	(*modlist)[cMods] = (LDAPMod *)ber_memalloc(sizeof(LDAPMod));
174178825Sdfr	if ((*modlist)[cMods] == NULL)
17572445Sassar	    return ENOMEM;
176127808Snectar
177127808Snectar	mod = (*modlist)[cMods];
178127808Snectar	mod->mod_op = modop;
179127808Snectar	mod->mod_type = ber_strdup(attribute);
180127808Snectar	if (mod->mod_type == NULL) {
181127808Snectar	    ber_memfree(mod);
182127808Snectar	    (*modlist)[cMods] = NULL;
18372445Sassar	    return ENOMEM;
18472445Sassar	}
185127808Snectar
186127808Snectar	if (modop & LDAP_MOD_BVALUES) {
187127808Snectar	    mod->mod_bvalues = NULL;
188127808Snectar	} else {
189127808Snectar	    mod->mod_values = NULL;
190127808Snectar	}
191127808Snectar
192127808Snectar	(*modlist)[cMods + 1] = NULL;
19372445Sassar    }
19472445Sassar
195127808Snectar    return 0;
196127808Snectar}
197127808Snectar
198127808Snectarstatic krb5_error_code
199127808SnectarLDAP_addmod_len(LDAPMod *** modlist, int modop, const char *attribute,
200127808Snectar		unsigned char *value, size_t len)
201127808Snectar{
202127808Snectar    krb5_error_code ret;
203178825Sdfr    int cMods, i = 0;
204127808Snectar
205127808Snectar    ret = LDAP__setmod(modlist, modop | LDAP_MOD_BVALUES, attribute, &cMods);
206178825Sdfr    if (ret)
207127808Snectar	return ret;
208127808Snectar
20972445Sassar    if (value != NULL) {
210178825Sdfr	struct berval **bv;
211127808Snectar
212178825Sdfr	bv = (*modlist)[cMods]->mod_bvalues;
213178825Sdfr	if (bv != NULL) {
214178825Sdfr	    for (i = 0; bv[i] != NULL; i++)
215127808Snectar		;
216178825Sdfr	    bv = ber_memrealloc(bv, (i + 2) * sizeof(*bv));
217178825Sdfr	} else
218178825Sdfr	    bv = ber_memalloc(2 * sizeof(*bv));
219178825Sdfr	if (bv == NULL)
22072445Sassar	    return ENOMEM;
221178825Sdfr
222178825Sdfr	(*modlist)[cMods]->mod_bvalues = bv;
223178825Sdfr
224233294Sstas	bv[i] = ber_memalloc(sizeof(**bv));;
225178825Sdfr	if (bv[i] == NULL)
22672445Sassar	    return ENOMEM;
227127808Snectar
228178825Sdfr	bv[i]->bv_val = (void *)value;
229178825Sdfr	bv[i]->bv_len = len;
230127808Snectar
231178825Sdfr	bv[i + 1] = NULL;
23272445Sassar    }
233127808Snectar
23472445Sassar    return 0;
23572445Sassar}
23672445Sassar
23772445Sassarstatic krb5_error_code
23872445SassarLDAP_addmod(LDAPMod *** modlist, int modop, const char *attribute,
23972445Sassar	    const char *value)
24072445Sassar{
241178825Sdfr    int cMods, i = 0;
242127808Snectar    krb5_error_code ret;
24372445Sassar
244127808Snectar    ret = LDAP__setmod(modlist, modop, attribute, &cMods);
245178825Sdfr    if (ret)
246127808Snectar	return ret;
24772445Sassar
248127808Snectar    if (value != NULL) {
249178825Sdfr	char **bv;
25072445Sassar
251178825Sdfr	bv = (*modlist)[cMods]->mod_values;
252178825Sdfr	if (bv != NULL) {
253178825Sdfr	    for (i = 0; bv[i] != NULL; i++)
254127808Snectar		;
255178825Sdfr	    bv = ber_memrealloc(bv, (i + 2) * sizeof(*bv));
256178825Sdfr	} else
257178825Sdfr	    bv = ber_memalloc(2 * sizeof(*bv));
258178825Sdfr	if (bv == NULL)
25972445Sassar	    return ENOMEM;
260178825Sdfr
261178825Sdfr	(*modlist)[cMods]->mod_values = bv;
262178825Sdfr
263178825Sdfr	bv[i] = ber_strdup(value);
264178825Sdfr	if (bv[i] == NULL)
26572445Sassar	    return ENOMEM;
266178825Sdfr
267178825Sdfr	bv[i + 1] = NULL;
26872445Sassar    }
26972445Sassar
27072445Sassar    return 0;
27172445Sassar}
27272445Sassar
27372445Sassarstatic krb5_error_code
27472445SassarLDAP_addmod_generalized_time(LDAPMod *** mods, int modop,
27572445Sassar			     const char *attribute, KerberosTime * time)
27672445Sassar{
27772445Sassar    char buf[22];
27872445Sassar    struct tm *tm;
27972445Sassar
28072445Sassar    /* XXX not threadsafe */
28172445Sassar    tm = gmtime(time);
28272445Sassar    strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", tm);
28372445Sassar
28472445Sassar    return LDAP_addmod(mods, modop, attribute, buf);
28572445Sassar}
28672445Sassar
28772445Sassarstatic krb5_error_code
288178825SdfrLDAP_addmod_integer(krb5_context context,
289178825Sdfr		    LDAPMod *** mods, int modop,
290178825Sdfr		    const char *attribute, unsigned long l)
291178825Sdfr{
292178825Sdfr    krb5_error_code ret;
293178825Sdfr    char *buf;
294178825Sdfr
295178825Sdfr    ret = asprintf(&buf, "%ld", l);
296178825Sdfr    if (ret < 0) {
297233294Sstas	krb5_set_error_message(context, ENOMEM,
298233294Sstas			       "asprintf: out of memory:");
299233294Sstas	return ENOMEM;
300178825Sdfr    }
301178825Sdfr    ret = LDAP_addmod(mods, modop, attribute, buf);
302178825Sdfr    free (buf);
303178825Sdfr    return ret;
304178825Sdfr}
305178825Sdfr
306178825Sdfrstatic krb5_error_code
30772445SassarLDAP_get_string_value(HDB * db, LDAPMessage * entry,
30872445Sassar		      const char *attribute, char **ptr)
30972445Sassar{
310233294Sstas    struct berval **vals;
31172445Sassar
312233294Sstas    vals = ldap_get_values_len(HDB2LDAP(db), entry, attribute);
313233294Sstas    if (vals == NULL || vals[0] == NULL) {
314178825Sdfr	*ptr = NULL;
31572445Sassar	return HDB_ERR_NOENTRY;
31672445Sassar    }
317178825Sdfr
318233294Sstas    *ptr = malloc(vals[0]->bv_len + 1);
319233294Sstas    if (*ptr == NULL) {
320233294Sstas	ldap_value_free_len(vals);
321233294Sstas	return ENOMEM;
322233294Sstas    }
32372445Sassar
324233294Sstas    memcpy(*ptr, vals[0]->bv_val, vals[0]->bv_len);
325233294Sstas    (*ptr)[vals[0]->bv_len] = 0;
32672445Sassar
327233294Sstas    ldap_value_free_len(vals);
328233294Sstas
329233294Sstas    return 0;
33072445Sassar}
33172445Sassar
33272445Sassarstatic krb5_error_code
33372445SassarLDAP_get_integer_value(HDB * db, LDAPMessage * entry,
33472445Sassar		       const char *attribute, int *ptr)
33572445Sassar{
336233294Sstas    krb5_error_code ret;
337233294Sstas    char *val;
33872445Sassar
339233294Sstas    ret = LDAP_get_string_value(db, entry, attribute, &val);
340233294Sstas    if (ret)
341233294Sstas	return ret;
342233294Sstas    *ptr = atoi(val);
343233294Sstas    free(val);
34472445Sassar    return 0;
34572445Sassar}
34672445Sassar
34772445Sassarstatic krb5_error_code
34872445SassarLDAP_get_generalized_time_value(HDB * db, LDAPMessage * entry,
34972445Sassar				const char *attribute, KerberosTime * kt)
35072445Sassar{
35172445Sassar    char *tmp, *gentime;
35272445Sassar    struct tm tm;
35372445Sassar    int ret;
35472445Sassar
35572445Sassar    *kt = 0;
35672445Sassar
35772445Sassar    ret = LDAP_get_string_value(db, entry, attribute, &gentime);
358178825Sdfr    if (ret)
35972445Sassar	return ret;
36072445Sassar
36172445Sassar    tmp = strptime(gentime, "%Y%m%d%H%M%SZ", &tm);
36272445Sassar    if (tmp == NULL) {
36372445Sassar	free(gentime);
36472445Sassar	return HDB_ERR_NOENTRY;
36572445Sassar    }
36672445Sassar
36772445Sassar    free(gentime);
36872445Sassar
36972445Sassar    *kt = timegm(&tm);
37072445Sassar
37172445Sassar    return 0;
37272445Sassar}
37372445Sassar
374233294Sstasstatic int
375233294Sstasbervalstrcmp(struct berval *v, const char *str)
376233294Sstas{
377233294Sstas    size_t len = strlen(str);
378233294Sstas    return (v->bv_len == len) && strncasecmp(str, (char *)v->bv_val, len) == 0;
379233294Sstas}
380233294Sstas
381233294Sstas
38272445Sassarstatic krb5_error_code
383178825SdfrLDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent,
38472445Sassar		LDAPMessage * msg, LDAPMod *** pmods)
38572445Sassar{
38672445Sassar    krb5_error_code ret;
38772445Sassar    krb5_boolean is_new_entry;
38872445Sassar    char *tmp = NULL;
38972445Sassar    LDAPMod **mods = NULL;
390178825Sdfr    hdb_entry_ex orig;
39172445Sassar    unsigned long oflags, nflags;
392178825Sdfr    int i;
39372445Sassar
394178825Sdfr    krb5_boolean is_samba_account = FALSE;
395178825Sdfr    krb5_boolean is_account = FALSE;
396178825Sdfr    krb5_boolean is_heimdal_entry = FALSE;
397178825Sdfr    krb5_boolean is_heimdal_principal = FALSE;
398178825Sdfr
399233294Sstas    struct berval **vals;
400178825Sdfr
401178825Sdfr    *pmods = NULL;
402178825Sdfr
40372445Sassar    if (msg != NULL) {
404178825Sdfr
405233294Sstas	ret = LDAP_message2entry(context, db, msg, 0, &orig);
406178825Sdfr	if (ret)
40772445Sassar	    goto out;
408178825Sdfr
409178825Sdfr	is_new_entry = FALSE;
410233294Sstas
411233294Sstas	vals = ldap_get_values_len(HDB2LDAP(db), msg, "objectClass");
412233294Sstas	if (vals) {
413233294Sstas	    int num_objectclasses = ldap_count_values_len(vals);
414178825Sdfr	    for (i=0; i < num_objectclasses; i++) {
415233294Sstas		if (bervalstrcmp(vals[i], "sambaSamAccount"))
416178825Sdfr		    is_samba_account = TRUE;
417233294Sstas		else if (bervalstrcmp(vals[i], structural_object))
418178825Sdfr		    is_account = TRUE;
419233294Sstas		else if (bervalstrcmp(vals[i], "krb5Principal"))
420178825Sdfr		    is_heimdal_principal = TRUE;
421233294Sstas		else if (bervalstrcmp(vals[i], "krb5KDCEntry"))
422178825Sdfr		    is_heimdal_entry = TRUE;
423178825Sdfr	    }
424233294Sstas	    ldap_value_free_len(vals);
42572445Sassar	}
426178825Sdfr
427178825Sdfr	/*
428178825Sdfr	 * If this is just a "account" entry and no other objectclass
429178825Sdfr	 * is hanging on this entry, it's really a new entry.
430178825Sdfr	 */
431233294Sstas	if (is_samba_account == FALSE && is_heimdal_principal == FALSE &&
432178825Sdfr	    is_heimdal_entry == FALSE) {
433178825Sdfr	    if (is_account == TRUE) {
434178825Sdfr		is_new_entry = TRUE;
435178825Sdfr	    } else {
436178825Sdfr		ret = HDB_ERR_NOENTRY;
437178825Sdfr		goto out;
438178825Sdfr	    }
439178825Sdfr	}
440178825Sdfr    } else
441178825Sdfr	is_new_entry = TRUE;
442178825Sdfr
443178825Sdfr    if (is_new_entry) {
444178825Sdfr
44572445Sassar	/* to make it perfectly obvious we're depending on
44672445Sassar	 * orig being intiialized to zero */
44772445Sassar	memset(&orig, 0, sizeof(orig));
44872445Sassar
44972445Sassar	ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "top");
450178825Sdfr	if (ret)
45172445Sassar	    goto out;
452233294Sstas
453178825Sdfr	/* account is the structural object class */
454178825Sdfr	if (is_account == FALSE) {
455233294Sstas	    ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass",
456178825Sdfr			      structural_object);
457178825Sdfr	    is_account = TRUE;
458178825Sdfr	    if (ret)
459178825Sdfr		goto out;
46072445Sassar	}
461178825Sdfr
462178825Sdfr	ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "krb5Principal");
463178825Sdfr	is_heimdal_principal = TRUE;
464178825Sdfr	if (ret)
46572445Sassar	    goto out;
466178825Sdfr
467178825Sdfr	ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "krb5KDCEntry");
468178825Sdfr	is_heimdal_entry = TRUE;
469178825Sdfr	if (ret)
47072445Sassar	    goto out;
47172445Sassar    }
47272445Sassar
473233294Sstas    if (is_new_entry ||
474178825Sdfr	krb5_principal_compare(context, ent->entry.principal, orig.entry.principal)
475178825Sdfr	== FALSE)
476178825Sdfr    {
477178825Sdfr	if (is_heimdal_principal || is_heimdal_entry) {
478178825Sdfr
479178825Sdfr	    ret = krb5_unparse_name(context, ent->entry.principal, &tmp);
480178825Sdfr	    if (ret)
481178825Sdfr		goto out;
482178825Sdfr
483178825Sdfr	    ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE,
484178825Sdfr			      "krb5PrincipalName", tmp);
485178825Sdfr	    if (ret) {
486178825Sdfr		free(tmp);
487178825Sdfr		goto out;
488178825Sdfr	    }
489178825Sdfr	    free(tmp);
49072445Sassar	}
491178825Sdfr
492178825Sdfr	if (is_account || is_samba_account) {
493178825Sdfr	    ret = krb5_unparse_name_short(context, ent->entry.principal, &tmp);
494178825Sdfr	    if (ret)
495178825Sdfr		goto out;
496178825Sdfr	    ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "uid", tmp);
497178825Sdfr	    if (ret) {
498178825Sdfr		free(tmp);
499178825Sdfr		goto out;
500178825Sdfr	    }
50172445Sassar	    free(tmp);
50272445Sassar	}
50372445Sassar    }
50472445Sassar
505178825Sdfr    if (is_heimdal_entry && (ent->entry.kvno != orig.entry.kvno || is_new_entry)) {
506178825Sdfr	ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
507233294Sstas			    "krb5KeyVersionNumber",
508178825Sdfr			    ent->entry.kvno);
509178825Sdfr	if (ret)
51072445Sassar	    goto out;
51172445Sassar    }
51272445Sassar
513178825Sdfr    if (is_heimdal_entry && ent->entry.valid_start) {
514178825Sdfr	if (orig.entry.valid_end == NULL
515178825Sdfr	    || (*(ent->entry.valid_start) != *(orig.entry.valid_start))) {
516178825Sdfr	    ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,
517178825Sdfr					       "krb5ValidStart",
518178825Sdfr					       ent->entry.valid_start);
519178825Sdfr	    if (ret)
52072445Sassar		goto out;
52172445Sassar	}
52272445Sassar    }
52372445Sassar
524178825Sdfr    if (ent->entry.valid_end) {
525178825Sdfr 	if (orig.entry.valid_end == NULL || (*(ent->entry.valid_end) != *(orig.entry.valid_end))) {
526233294Sstas	    if (is_heimdal_entry) {
527178825Sdfr		ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,
528178825Sdfr						   "krb5ValidEnd",
529178825Sdfr						   ent->entry.valid_end);
530178825Sdfr		if (ret)
531178825Sdfr		    goto out;
532178825Sdfr            }
533178825Sdfr	    if (is_samba_account) {
534178825Sdfr		ret = LDAP_addmod_integer(context, &mods,  LDAP_MOD_REPLACE,
535233294Sstas					  "sambaKickoffTime",
536178825Sdfr					  *(ent->entry.valid_end));
537178825Sdfr		if (ret)
538178825Sdfr		    goto out;
53972445Sassar	    }
540178825Sdfr   	}
54172445Sassar    }
54272445Sassar
543178825Sdfr    if (ent->entry.pw_end) {
544178825Sdfr	if (orig.entry.pw_end == NULL || (*(ent->entry.pw_end) != *(orig.entry.pw_end))) {
545178825Sdfr	    if (is_heimdal_entry) {
546178825Sdfr		ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,
547178825Sdfr						   "krb5PasswordEnd",
548178825Sdfr						   ent->entry.pw_end);
549178825Sdfr		if (ret)
550178825Sdfr		    goto out;
55172445Sassar	    }
552178825Sdfr
553178825Sdfr	    if (is_samba_account) {
554178825Sdfr		ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
555233294Sstas					  "sambaPwdMustChange",
556178825Sdfr					  *(ent->entry.pw_end));
557178825Sdfr		if (ret)
558178825Sdfr		    goto out;
559178825Sdfr	    }
56072445Sassar	}
56172445Sassar    }
56272445Sassar
563178825Sdfr
564178825Sdfr#if 0 /* we we have last_pw_change */
565178825Sdfr    if (is_samba_account && ent->entry.last_pw_change) {
566178825Sdfr	if (orig.entry.last_pw_change == NULL || (*(ent->entry.last_pw_change) != *(orig.entry.last_pw_change))) {
567178825Sdfr	    ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
568233294Sstas				      "sambaPwdLastSet",
569178825Sdfr				      *(ent->entry.last_pw_change));
570178825Sdfr	    if (ret)
57172445Sassar		goto out;
57272445Sassar	}
57372445Sassar    }
574178825Sdfr#endif
57572445Sassar
576178825Sdfr    if (is_heimdal_entry && ent->entry.max_life) {
577178825Sdfr	if (orig.entry.max_life == NULL
578178825Sdfr	    || (*(ent->entry.max_life) != *(orig.entry.max_life))) {
579178825Sdfr
580178825Sdfr	    ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
581233294Sstas				      "krb5MaxLife",
582178825Sdfr				      *(ent->entry.max_life));
583178825Sdfr	    if (ret)
58472445Sassar		goto out;
58572445Sassar	}
58672445Sassar    }
58772445Sassar
588178825Sdfr    if (is_heimdal_entry && ent->entry.max_renew) {
589178825Sdfr	if (orig.entry.max_renew == NULL
590178825Sdfr	    || (*(ent->entry.max_renew) != *(orig.entry.max_renew))) {
59172445Sassar
592178825Sdfr	    ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
593178825Sdfr				      "krb5MaxRenew",
594178825Sdfr				      *(ent->entry.max_renew));
595178825Sdfr	    if (ret)
596178825Sdfr		goto out;
59772445Sassar	}
59872445Sassar    }
59972445Sassar
600178825Sdfr    oflags = HDBFlags2int(orig.entry.flags);
601178825Sdfr    nflags = HDBFlags2int(ent->entry.flags);
602178825Sdfr
603178825Sdfr    if (is_heimdal_entry && oflags != nflags) {
604178825Sdfr
605178825Sdfr	ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
606178825Sdfr				  "krb5KDCFlags",
607178825Sdfr				  nflags);
608178825Sdfr	if (ret)
60972445Sassar	    goto out;
61072445Sassar    }
61172445Sassar
612178825Sdfr    /* Remove keys if they exists, and then replace keys. */
613178825Sdfr    if (!is_new_entry && orig.entry.keys.len > 0) {
614233294Sstas	vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5Key");
615233294Sstas	if (vals) {
616233294Sstas	    ldap_value_free_len(vals);
61772445Sassar
618178825Sdfr	    ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5Key", NULL);
619178825Sdfr	    if (ret)
620178825Sdfr		goto out;
62172445Sassar	}
62272445Sassar    }
62372445Sassar
624178825Sdfr    for (i = 0; i < ent->entry.keys.len; i++) {
625178825Sdfr
626178825Sdfr	if (is_samba_account
627178825Sdfr	    && ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) {
628178825Sdfr	    char *ntHexPassword;
629178825Sdfr	    char *nt;
630233294Sstas	    time_t now = time(NULL);
631233294Sstas
632178825Sdfr	    /* the key might have been 'sealed', but samba passwords
633178825Sdfr	       are clear in the directory */
634178825Sdfr	    ret = hdb_unseal_key(context, db, &ent->entry.keys.val[i]);
635178825Sdfr	    if (ret)
636178825Sdfr		goto out;
637233294Sstas
638178825Sdfr	    nt = ent->entry.keys.val[i].key.keyvalue.data;
639178825Sdfr	    /* store in ntPassword, not krb5key */
640178825Sdfr	    ret = hex_encode(nt, 16, &ntHexPassword);
641178825Sdfr	    if (ret < 0) {
642233294Sstas		ret = ENOMEM;
643233294Sstas		krb5_set_error_message(context, ret, "hdb-ldap: failed to "
644178825Sdfr				      "hex encode key");
64572445Sassar		goto out;
64672445Sassar	    }
647233294Sstas	    ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "sambaNTPassword",
648178825Sdfr			      ntHexPassword);
649178825Sdfr	    free(ntHexPassword);
650178825Sdfr	    if (ret)
65172445Sassar		goto out;
652233294Sstas	    ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
653233294Sstas				      "sambaPwdLastSet", now);
654233294Sstas	    if (ret)
655233294Sstas		goto out;
656233294Sstas
657178825Sdfr	    /* have to kill the LM passwod if it exists */
658233294Sstas	    vals = ldap_get_values_len(HDB2LDAP(db), msg, "sambaLMPassword");
659233294Sstas	    if (vals) {
660233294Sstas		ldap_value_free_len(vals);
661178825Sdfr		ret = LDAP_addmod(&mods, LDAP_MOD_DELETE,
662178825Sdfr				  "sambaLMPassword", NULL);
663178825Sdfr		if (ret)
664178825Sdfr		    goto out;
66572445Sassar	    }
666233294Sstas
667178825Sdfr	} else if (is_heimdal_entry) {
668178825Sdfr	    unsigned char *buf;
669178825Sdfr	    size_t len, buf_size;
670178825Sdfr
671178825Sdfr	    ASN1_MALLOC_ENCODE(Key, buf, buf_size, &ent->entry.keys.val[i], &len, ret);
672178825Sdfr	    if (ret)
673178825Sdfr		goto out;
674178825Sdfr	    if(buf_size != len)
675178825Sdfr		krb5_abortx(context, "internal error in ASN.1 encoder");
676178825Sdfr
677178825Sdfr	    /* addmod_len _owns_ the key, doesn't need to copy it */
678178825Sdfr	    ret = LDAP_addmod_len(&mods, LDAP_MOD_ADD, "krb5Key", buf, len);
679178825Sdfr	    if (ret)
680178825Sdfr		goto out;
68172445Sassar	}
68272445Sassar    }
68372445Sassar
684178825Sdfr    if (ent->entry.etypes) {
685178825Sdfr	int add_krb5EncryptionType = 0;
686178825Sdfr
687233294Sstas	/*
688178825Sdfr	 * Only add/modify krb5EncryptionType if it's a new heimdal
689178825Sdfr	 * entry or krb5EncryptionType already exists on the entry.
690178825Sdfr	 */
691178825Sdfr
692178825Sdfr	if (!is_new_entry) {
693233294Sstas	    vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5EncryptionType");
694233294Sstas	    if (vals) {
695233294Sstas		ldap_value_free_len(vals);
696178825Sdfr		ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5EncryptionType",
697178825Sdfr				  NULL);
698178825Sdfr		if (ret)
699178825Sdfr		    goto out;
700178825Sdfr		add_krb5EncryptionType = 1;
701178825Sdfr	    }
702178825Sdfr	} else if (is_heimdal_entry)
703178825Sdfr	    add_krb5EncryptionType = 1;
704178825Sdfr
705178825Sdfr	if (add_krb5EncryptionType) {
706178825Sdfr	    for (i = 0; i < ent->entry.etypes->len; i++) {
707233294Sstas		if (is_samba_account &&
708178825Sdfr		    ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5)
709178825Sdfr		{
710178825Sdfr		    ;
711178825Sdfr		} else if (is_heimdal_entry) {
712178825Sdfr		    ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_ADD,
713178825Sdfr					      "krb5EncryptionType",
714178825Sdfr					      ent->entry.etypes->val[i]);
715178825Sdfr		    if (ret)
716178825Sdfr			goto out;
717178825Sdfr		}
718178825Sdfr	    }
719178825Sdfr	}
720178825Sdfr    }
721178825Sdfr
72272445Sassar    /* for clarity */
72372445Sassar    ret = 0;
72472445Sassar
725178825Sdfr out:
72672445Sassar
727178825Sdfr    if (ret == 0)
72872445Sassar	*pmods = mods;
729178825Sdfr    else if (mods != NULL) {
73072445Sassar	ldap_mods_free(mods, 1);
73172445Sassar	*pmods = NULL;
73272445Sassar    }
73372445Sassar
734178825Sdfr    if (msg)
73572445Sassar	hdb_free_entry(context, &orig);
73672445Sassar
73772445Sassar    return ret;
73872445Sassar}
73972445Sassar
74072445Sassarstatic krb5_error_code
74172445SassarLDAP_dn2principal(krb5_context context, HDB * db, const char *dn,
74272445Sassar		  krb5_principal * principal)
74372445Sassar{
74472445Sassar    krb5_error_code ret;
745178825Sdfr    int rc;
746178825Sdfr    const char *filter = "(objectClass=krb5Principal)";
74772445Sassar    LDAPMessage *res = NULL, *e;
748233294Sstas    char *p;
74972445Sassar
750178825Sdfr    ret = LDAP_no_size_limit(context, HDB2LDAP(db));
751178825Sdfr    if (ret)
75290926Snectar	goto out;
75390926Snectar
754233294Sstas    rc = ldap_search_ext_s(HDB2LDAP(db), dn, LDAP_SCOPE_SUBTREE,
755233294Sstas			   filter, krb5principal_attrs, 0,
756233294Sstas			   NULL, NULL, NULL,
757233294Sstas			   0, &res);
758178825Sdfr    if (check_ldap(context, db, rc)) {
75972445Sassar	ret = HDB_ERR_NOENTRY;
760233294Sstas	krb5_set_error_message(context, ret, "ldap_search_ext_s: "
761233294Sstas			       "filter: %s error: %s",
762233294Sstas			       filter, ldap_err2string(rc));
76372445Sassar	goto out;
76472445Sassar    }
76572445Sassar
766178825Sdfr    e = ldap_first_entry(HDB2LDAP(db), res);
76772445Sassar    if (e == NULL) {
76872445Sassar	ret = HDB_ERR_NOENTRY;
76972445Sassar	goto out;
77072445Sassar    }
77172445Sassar
772233294Sstas    ret = LDAP_get_string_value(db, e, "krb5PrincipalName", &p);
773233294Sstas    if (ret) {
77472445Sassar	ret = HDB_ERR_NOENTRY;
77572445Sassar	goto out;
77672445Sassar    }
77772445Sassar
778233294Sstas    ret = krb5_parse_name(context, p, principal);
779233294Sstas    free(p);
78072445Sassar
78172445Sassar  out:
782178825Sdfr    if (res)
78372445Sassar	ldap_msgfree(res);
784178825Sdfr
78572445Sassar    return ret;
78672445Sassar}
78772445Sassar
788233294Sstasstatic int
789233294Sstasneed_quote(unsigned char c)
790233294Sstas{
791233294Sstas    return (c & 0x80) ||
792233294Sstas	(c < 32) ||
793233294Sstas	(c == '(') ||
794233294Sstas	(c == ')') ||
795233294Sstas	(c == '*') ||
796233294Sstas	(c == '\\') ||
797233294Sstas	(c == 0x7f);
798233294Sstas}
799233294Sstas
800233294Sstasconst static char hexchar[] = "0123456789ABCDEF";
801233294Sstas
80272445Sassarstatic krb5_error_code
803233294Sstasescape_value(krb5_context context, const unsigned char *unquoted, char **quoted)
804233294Sstas{
805233294Sstas    size_t i, len;
806233294Sstas
807233294Sstas    for (i = 0, len = 0; unquoted[i] != '\0'; i++, len++) {
808233294Sstas	if (need_quote((unsigned char)unquoted[i]))
809233294Sstas	    len += 2;
810233294Sstas    }
811233294Sstas
812233294Sstas    *quoted = malloc(len + 1);
813233294Sstas    if (*quoted == NULL) {
814233294Sstas	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
815233294Sstas	return ENOMEM;
816233294Sstas    }
817233294Sstas
818233294Sstas    for (i = 0; unquoted[0] ; unquoted++) {
819233294Sstas	if (need_quote((unsigned char *)unquoted[0])) {
820233294Sstas	    (*quoted)[i++] = '\\';
821233294Sstas	    (*quoted)[i++] = hexchar[(unquoted[0] >> 4) & 0xf];
822233294Sstas	    (*quoted)[i++] = hexchar[(unquoted[0]     ) & 0xf];
823233294Sstas	} else
824233294Sstas	    (*quoted)[i++] = (char)unquoted[0];
825233294Sstas    }
826233294Sstas    (*quoted)[i] = '\0';
827233294Sstas    return 0;
828233294Sstas}
829233294Sstas
830233294Sstas
831233294Sstasstatic krb5_error_code
832178825SdfrLDAP__lookup_princ(krb5_context context,
833178825Sdfr		   HDB *db,
834178825Sdfr		   const char *princname,
835178825Sdfr		   const char *userid,
836178825Sdfr		   LDAPMessage **msg)
83772445Sassar{
83872445Sassar    krb5_error_code ret;
839178825Sdfr    int rc;
840233294Sstas    char *quote, *filter = NULL;
84172445Sassar
842178825Sdfr    ret = LDAP__connect(context, db);
843178825Sdfr    if (ret)
844178825Sdfr	return ret;
84572445Sassar
846233294Sstas    /*
847233294Sstas     * Quote searches that contain filter language, this quote
848233294Sstas     * searches for *@REALM, which takes very long time.
849233294Sstas     */
850233294Sstas
851233294Sstas    ret = escape_value(context, princname, &quote);
852233294Sstas    if (ret)
853233294Sstas	goto out;
854233294Sstas
855178825Sdfr    rc = asprintf(&filter,
856178825Sdfr		  "(&(objectClass=krb5Principal)(krb5PrincipalName=%s))",
857233294Sstas		  quote);
858233294Sstas    free(quote);
859233294Sstas
86072445Sassar    if (rc < 0) {
86172445Sassar	ret = ENOMEM;
862233294Sstas	krb5_set_error_message(context, ret, "malloc: out of memory");
86372445Sassar	goto out;
86472445Sassar    }
86572445Sassar
866178825Sdfr    ret = LDAP_no_size_limit(context, HDB2LDAP(db));
867178825Sdfr    if (ret)
86890926Snectar	goto out;
86972445Sassar
870233294Sstas    rc = ldap_search_ext_s(HDB2LDAP(db), HDB2BASE(db),
871233294Sstas			   LDAP_SCOPE_SUBTREE, filter,
872233294Sstas			   krb5kdcentry_attrs, 0,
873233294Sstas			   NULL, NULL, NULL,
874233294Sstas			   0, msg);
875178825Sdfr    if (check_ldap(context, db, rc)) {
876233294Sstas	ret = HDB_ERR_NOENTRY;
877233294Sstas	krb5_set_error_message(context, ret, "ldap_search_ext_s: "
878233294Sstas			      "filter: %s - error: %s",
879178825Sdfr			      filter, ldap_err2string(rc));
88072445Sassar	goto out;
88172445Sassar    }
88272445Sassar
883178825Sdfr    if (userid && ldap_count_entries(HDB2LDAP(db), *msg) == 0) {
884178825Sdfr	free(filter);
885178825Sdfr	filter = NULL;
886178825Sdfr	ldap_msgfree(*msg);
887178825Sdfr	*msg = NULL;
888233294Sstas
889233294Sstas	ret = escape_value(context, userid, &quote);
890233294Sstas	if (ret)
891233294Sstas	    goto out;
892233294Sstas
893178825Sdfr	rc = asprintf(&filter,
894178825Sdfr	    "(&(|(objectClass=sambaSamAccount)(objectClass=%s))(uid=%s))",
895233294Sstas		      structural_object, quote);
896233294Sstas	free(quote);
897178825Sdfr	if (rc < 0) {
898178825Sdfr	    ret = ENOMEM;
899233294Sstas	    krb5_set_error_message(context, ret, "asprintf: out of memory");
900178825Sdfr	    goto out;
901178825Sdfr	}
902233294Sstas
903178825Sdfr	ret = LDAP_no_size_limit(context, HDB2LDAP(db));
904178825Sdfr	if (ret)
905178825Sdfr	    goto out;
906178825Sdfr
907233294Sstas	rc = ldap_search_ext_s(HDB2LDAP(db), HDB2BASE(db), LDAP_SCOPE_SUBTREE,
908233294Sstas			       filter, krb5kdcentry_attrs, 0,
909233294Sstas			       NULL, NULL, NULL,
910233294Sstas			       0, msg);
911178825Sdfr	if (check_ldap(context, db, rc)) {
912178825Sdfr	    ret = HDB_ERR_NOENTRY;
913233294Sstas	    krb5_set_error_message(context, ret,
914233294Sstas				   "ldap_search_ext_s: filter: %s error: %s",
915233294Sstas				   filter, ldap_err2string(rc));
916178825Sdfr	    goto out;
917178825Sdfr	}
918178825Sdfr    }
919178825Sdfr
92072445Sassar    ret = 0;
92172445Sassar
92272445Sassar  out:
923178825Sdfr    if (filter)
92472445Sassar	free(filter);
925178825Sdfr
92672445Sassar    return ret;
92772445Sassar}
92872445Sassar
92972445Sassarstatic krb5_error_code
93072445SassarLDAP_principal2message(krb5_context context, HDB * db,
931178825Sdfr		       krb5_const_principal princ, LDAPMessage ** msg)
93272445Sassar{
933178825Sdfr    char *name, *name_short = NULL;
93472445Sassar    krb5_error_code ret;
935178825Sdfr    krb5_realm *r, *r0;
93672445Sassar
937178825Sdfr    *msg = NULL;
938178825Sdfr
939178825Sdfr    ret = krb5_unparse_name(context, princ, &name);
940178825Sdfr    if (ret)
94172445Sassar	return ret;
942178825Sdfr
943178825Sdfr    ret = krb5_get_default_realms(context, &r0);
944178825Sdfr    if(ret) {
945178825Sdfr	free(name);
946178825Sdfr	return ret;
94772445Sassar    }
948178825Sdfr    for (r = r0; *r != NULL; r++) {
949178825Sdfr	if(strcmp(krb5_principal_get_realm(context, princ), *r) == 0) {
950178825Sdfr	    ret = krb5_unparse_name_short(context, princ, &name_short);
951178825Sdfr	    if (ret) {
952178825Sdfr		krb5_free_host_realm(context, r0);
953178825Sdfr		free(name);
954178825Sdfr		return ret;
955178825Sdfr	    }
956178825Sdfr	    break;
957178825Sdfr	}
958178825Sdfr    }
959178825Sdfr    krb5_free_host_realm(context, r0);
96072445Sassar
961178825Sdfr    ret = LDAP__lookup_princ(context, db, name, name_short, msg);
962178825Sdfr    free(name);
963178825Sdfr    free(name_short);
96472445Sassar
96572445Sassar    return ret;
96672445Sassar}
96772445Sassar
96872445Sassar/*
96972445Sassar * Construct an hdb_entry from a directory entry.
97072445Sassar */
97172445Sassarstatic krb5_error_code
97272445SassarLDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,
973233294Sstas		   int flags, hdb_entry_ex * ent)
97472445Sassar{
975178825Sdfr    char *unparsed_name = NULL, *dn = NULL, *ntPasswordIN = NULL;
976178825Sdfr    char *samba_acct_flags = NULL;
97772445Sassar    struct berval **keys;
978233294Sstas    struct berval **vals;
979233294Sstas    int tmp, tmp_time, i, ret, have_arcfour = 0;
98072445Sassar
98172445Sassar    memset(ent, 0, sizeof(*ent));
982178825Sdfr    ent->entry.flags = int2HDBFlags(0);
98372445Sassar
984178825Sdfr    ret = LDAP_get_string_value(db, msg, "krb5PrincipalName", &unparsed_name);
985178825Sdfr    if (ret == 0) {
986178825Sdfr	ret = krb5_parse_name(context, unparsed_name, &ent->entry.principal);
987178825Sdfr	if (ret)
988178825Sdfr	    goto out;
989178825Sdfr    } else {
990178825Sdfr	ret = LDAP_get_string_value(db, msg, "uid",
991178825Sdfr				    &unparsed_name);
992178825Sdfr	if (ret == 0) {
993178825Sdfr	    ret = krb5_parse_name(context, unparsed_name, &ent->entry.principal);
994178825Sdfr	    if (ret)
995178825Sdfr		goto out;
996178825Sdfr	} else {
997233294Sstas	    krb5_set_error_message(context, HDB_ERR_NOENTRY,
998233294Sstas				   "hdb-ldap: ldap entry missing"
999178825Sdfr				  "principal name");
1000178825Sdfr	    return HDB_ERR_NOENTRY;
1001178825Sdfr	}
100272445Sassar    }
100372445Sassar
1004178825Sdfr    {
1005178825Sdfr	int integer;
1006178825Sdfr	ret = LDAP_get_integer_value(db, msg, "krb5KeyVersionNumber",
1007178825Sdfr				     &integer);
1008178825Sdfr	if (ret)
1009178825Sdfr	    ent->entry.kvno = 0;
1010178825Sdfr	else
1011178825Sdfr	    ent->entry.kvno = integer;
101272445Sassar    }
101372445Sassar
1014178825Sdfr    keys = ldap_get_values_len(HDB2LDAP(db), msg, "krb5Key");
101572445Sassar    if (keys != NULL) {
101672445Sassar	int i;
101772445Sassar	size_t l;
101872445Sassar
1019178825Sdfr	ent->entry.keys.len = ldap_count_values_len(keys);
1020178825Sdfr	ent->entry.keys.val = (Key *) calloc(ent->entry.keys.len, sizeof(Key));
1021178825Sdfr	if (ent->entry.keys.val == NULL) {
102290926Snectar	    ret = ENOMEM;
1023233294Sstas	    krb5_set_error_message(context, ret, "calloc: out of memory");
102490926Snectar	    goto out;
102590926Snectar	}
1026178825Sdfr	for (i = 0; i < ent->entry.keys.len; i++) {
102772445Sassar	    decode_Key((unsigned char *) keys[i]->bv_val,
1028178825Sdfr		       (size_t) keys[i]->bv_len, &ent->entry.keys.val[i], &l);
102972445Sassar	}
103072445Sassar	ber_bvecfree(keys);
103172445Sassar    } else {
103272445Sassar#if 1
103372445Sassar	/*
103472445Sassar	 * This violates the ASN1 but it allows a principal to
103572445Sassar	 * be related to a general directory entry without creating
103672445Sassar	 * the keys. Hopefully it's OK.
103772445Sassar	 */
1038178825Sdfr	ent->entry.keys.len = 0;
1039178825Sdfr	ent->entry.keys.val = NULL;
104072445Sassar#else
104172445Sassar	ret = HDB_ERR_NOENTRY;
104272445Sassar	goto out;
104372445Sassar#endif
104472445Sassar    }
104572445Sassar
1046233294Sstas    vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5EncryptionType");
1047233294Sstas    if (vals != NULL) {
1048178825Sdfr	int i;
1049178825Sdfr
1050178825Sdfr	ent->entry.etypes = malloc(sizeof(*(ent->entry.etypes)));
1051178825Sdfr	if (ent->entry.etypes == NULL) {
1052178825Sdfr	    ret = ENOMEM;
1053233294Sstas	    krb5_set_error_message(context, ret,"malloc: out of memory");
1054178825Sdfr	    goto out;
1055178825Sdfr	}
1056233294Sstas	ent->entry.etypes->len = ldap_count_values_len(vals);
1057178825Sdfr	ent->entry.etypes->val = calloc(ent->entry.etypes->len, sizeof(int));
1058178825Sdfr	if (ent->entry.etypes->val == NULL) {
1059178825Sdfr	    ret = ENOMEM;
1060233294Sstas	    krb5_set_error_message(context, ret, "malloc: out of memory");
1061233294Sstas	    ent->entry.etypes->len = 0;
1062178825Sdfr	    goto out;
1063178825Sdfr	}
1064178825Sdfr	for (i = 0; i < ent->entry.etypes->len; i++) {
1065233294Sstas	    char *buf;
1066233294Sstas
1067233294Sstas	    buf = malloc(vals[i]->bv_len + 1);
1068233294Sstas	    if (buf == NULL) {
1069233294Sstas		ret = ENOMEM;
1070233294Sstas		krb5_set_error_message(context, ret, "malloc: out of memory");
1071233294Sstas		goto out;
1072233294Sstas	    }
1073233294Sstas	    memcpy(buf, vals[i]->bv_val, vals[i]->bv_len);
1074233294Sstas	    buf[vals[i]->bv_len] = '\0';
1075233294Sstas	    ent->entry.etypes->val[i] = atoi(buf);
1076233294Sstas	    free(buf);
1077178825Sdfr	}
1078233294Sstas	ldap_value_free_len(vals);
107972445Sassar    }
108072445Sassar
1081178825Sdfr    for (i = 0; i < ent->entry.keys.len; i++) {
1082178825Sdfr	if (ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) {
1083178825Sdfr	    have_arcfour = 1;
1084178825Sdfr	    break;
1085178825Sdfr	}
1086178825Sdfr    }
108772445Sassar
1088178825Sdfr    /* manually construct the NT (type 23) key */
1089178825Sdfr    ret = LDAP_get_string_value(db, msg, "sambaNTPassword", &ntPasswordIN);
1090178825Sdfr    if (ret == 0 && have_arcfour == 0) {
1091178825Sdfr	unsigned *etypes;
1092178825Sdfr	Key *keys;
1093178825Sdfr	int i;
1094178825Sdfr
1095178825Sdfr	keys = realloc(ent->entry.keys.val,
1096178825Sdfr		       (ent->entry.keys.len + 1) * sizeof(ent->entry.keys.val[0]));
1097178825Sdfr	if (keys == NULL) {
1098178825Sdfr	    free(ntPasswordIN);
1099178825Sdfr	    ret = ENOMEM;
1100233294Sstas	    krb5_set_error_message(context, ret, "malloc: out of memory");
1101178825Sdfr	    goto out;
1102178825Sdfr	}
1103178825Sdfr	ent->entry.keys.val = keys;
1104178825Sdfr	memset(&ent->entry.keys.val[ent->entry.keys.len], 0, sizeof(Key));
1105178825Sdfr	ent->entry.keys.val[ent->entry.keys.len].key.keytype = ETYPE_ARCFOUR_HMAC_MD5;
1106178825Sdfr	ret = krb5_data_alloc (&ent->entry.keys.val[ent->entry.keys.len].key.keyvalue, 16);
1107178825Sdfr	if (ret) {
1108233294Sstas	    krb5_set_error_message(context, ret, "malloc: out of memory");
1109178825Sdfr	    free(ntPasswordIN);
1110178825Sdfr	    ret = ENOMEM;
1111178825Sdfr	    goto out;
1112178825Sdfr	}
1113178825Sdfr	ret = hex_decode(ntPasswordIN,
1114178825Sdfr			 ent->entry.keys.val[ent->entry.keys.len].key.keyvalue.data, 16);
1115178825Sdfr	ent->entry.keys.len++;
1116178825Sdfr
1117178825Sdfr	if (ent->entry.etypes == NULL) {
1118178825Sdfr	    ent->entry.etypes = malloc(sizeof(*(ent->entry.etypes)));
1119178825Sdfr	    if (ent->entry.etypes == NULL) {
1120178825Sdfr		ret = ENOMEM;
1121233294Sstas		krb5_set_error_message(context, ret, "malloc: out of memory");
1122178825Sdfr		goto out;
1123178825Sdfr	    }
1124178825Sdfr	    ent->entry.etypes->val = NULL;
1125178825Sdfr	    ent->entry.etypes->len = 0;
1126178825Sdfr	}
1127178825Sdfr
1128178825Sdfr	for (i = 0; i < ent->entry.etypes->len; i++)
1129178825Sdfr	    if (ent->entry.etypes->val[i] == ETYPE_ARCFOUR_HMAC_MD5)
1130178825Sdfr		break;
1131178825Sdfr	/* If there is no ARCFOUR enctype, add one */
1132178825Sdfr	if (i == ent->entry.etypes->len) {
1133233294Sstas	    etypes = realloc(ent->entry.etypes->val,
1134233294Sstas			     (ent->entry.etypes->len + 1) *
1135178825Sdfr			     sizeof(ent->entry.etypes->val[0]));
1136178825Sdfr	    if (etypes == NULL) {
1137178825Sdfr		ret = ENOMEM;
1138233294Sstas		krb5_set_error_message(context, ret, "malloc: out of memory");
1139233294Sstas		goto out;
1140178825Sdfr	    }
1141178825Sdfr	    ent->entry.etypes->val = etypes;
1142233294Sstas	    ent->entry.etypes->val[ent->entry.etypes->len] =
1143178825Sdfr		ETYPE_ARCFOUR_HMAC_MD5;
1144178825Sdfr	    ent->entry.etypes->len++;
1145178825Sdfr	}
1146178825Sdfr    }
1147178825Sdfr
1148178825Sdfr    ret = LDAP_get_generalized_time_value(db, msg, "createTimestamp",
1149178825Sdfr					  &ent->entry.created_by.time);
1150178825Sdfr    if (ret)
1151178825Sdfr	ent->entry.created_by.time = time(NULL);
1152178825Sdfr
1153178825Sdfr    ent->entry.created_by.principal = NULL;
1154178825Sdfr
1155233294Sstas    if (flags & HDB_F_ADMIN_DATA) {
1156233294Sstas	ret = LDAP_get_string_value(db, msg, "creatorsName", &dn);
1157233294Sstas	if (ret == 0) {
1158233294Sstas	    LDAP_dn2principal(context, db, dn, &ent->entry.created_by.principal);
1159233294Sstas	    free(dn);
116072445Sassar	}
116172445Sassar
1162233294Sstas	ent->entry.modified_by = calloc(1, sizeof(*ent->entry.modified_by));
1163233294Sstas	if (ent->entry.modified_by == NULL) {
1164233294Sstas	    ret = ENOMEM;
1165233294Sstas	    krb5_set_error_message(context, ret, "malloc: out of memory");
1166233294Sstas	    goto out;
1167233294Sstas	}
1168233294Sstas
1169233294Sstas	ret = LDAP_get_generalized_time_value(db, msg, "modifyTimestamp",
1170233294Sstas					      &ent->entry.modified_by->time);
1171233294Sstas	if (ret == 0) {
1172233294Sstas	    ret = LDAP_get_string_value(db, msg, "modifiersName", &dn);
1173233294Sstas	    if (ret == 0) {
1174233294Sstas		LDAP_dn2principal(context, db, dn, &ent->entry.modified_by->principal);
1175233294Sstas		free(dn);
1176233294Sstas	    } else {
1177233294Sstas		free(ent->entry.modified_by);
1178233294Sstas		ent->entry.modified_by = NULL;
1179233294Sstas	    }
1180233294Sstas	}
118172445Sassar    }
118272445Sassar
1183178825Sdfr    ent->entry.valid_start = malloc(sizeof(*ent->entry.valid_start));
1184178825Sdfr    if (ent->entry.valid_start == NULL) {
118572445Sassar	ret = ENOMEM;
1186233294Sstas	krb5_set_error_message(context, ret, "malloc: out of memory");
118772445Sassar	goto out;
118872445Sassar    }
1189178825Sdfr    ret = LDAP_get_generalized_time_value(db, msg, "krb5ValidStart",
1190178825Sdfr					  ent->entry.valid_start);
1191178825Sdfr    if (ret) {
119272445Sassar	/* OPTIONAL */
1193178825Sdfr	free(ent->entry.valid_start);
1194178825Sdfr	ent->entry.valid_start = NULL;
119572445Sassar    }
1196233294Sstas
1197178825Sdfr    ent->entry.valid_end = malloc(sizeof(*ent->entry.valid_end));
1198178825Sdfr    if (ent->entry.valid_end == NULL) {
119990926Snectar	ret = ENOMEM;
1200233294Sstas	krb5_set_error_message(context, ret, "malloc: out of memory");
120172445Sassar	goto out;
120272445Sassar    }
1203178825Sdfr    ret = LDAP_get_generalized_time_value(db, msg, "krb5ValidEnd",
1204178825Sdfr					  ent->entry.valid_end);
1205178825Sdfr    if (ret) {
120672445Sassar	/* OPTIONAL */
1207178825Sdfr	free(ent->entry.valid_end);
1208178825Sdfr	ent->entry.valid_end = NULL;
120972445Sassar    }
121072445Sassar
1211178825Sdfr    ret = LDAP_get_integer_value(db, msg, "sambaKickoffTime", &tmp_time);
1212178825Sdfr    if (ret == 0) {
1213178825Sdfr 	if (ent->entry.valid_end == NULL) {
1214178825Sdfr 	    ent->entry.valid_end = malloc(sizeof(*ent->entry.valid_end));
1215178825Sdfr 	    if (ent->entry.valid_end == NULL) {
1216178825Sdfr 		ret = ENOMEM;
1217233294Sstas 		krb5_set_error_message(context, ret, "malloc: out of memory");
1218178825Sdfr 		goto out;
1219178825Sdfr 	    }
1220178825Sdfr 	}
1221178825Sdfr 	*ent->entry.valid_end = tmp_time;
1222178825Sdfr    }
1223178825Sdfr
1224178825Sdfr    ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end));
1225178825Sdfr    if (ent->entry.pw_end == NULL) {
122690926Snectar	ret = ENOMEM;
1227233294Sstas	krb5_set_error_message(context, ret, "malloc: out of memory");
122872445Sassar	goto out;
122972445Sassar    }
1230178825Sdfr    ret = LDAP_get_generalized_time_value(db, msg, "krb5PasswordEnd",
1231178825Sdfr					  ent->entry.pw_end);
1232178825Sdfr    if (ret) {
123372445Sassar	/* OPTIONAL */
1234178825Sdfr	free(ent->entry.pw_end);
1235178825Sdfr	ent->entry.pw_end = NULL;
123672445Sassar    }
123772445Sassar
1238233294Sstas    ret = LDAP_get_integer_value(db, msg, "sambaPwdLastSet", &tmp_time);
1239233294Sstas    if (ret == 0) {
1240233294Sstas	time_t delta;
1241233294Sstas
1242233294Sstas	if (ent->entry.pw_end == NULL) {
1243233294Sstas            ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end));
1244233294Sstas            if (ent->entry.pw_end == NULL) {
1245233294Sstas                ret = ENOMEM;
1246233294Sstas                krb5_set_error_message(context, ret, "malloc: out of memory");
1247233294Sstas                goto out;
1248233294Sstas            }
1249233294Sstas        }
1250233294Sstas
1251233294Sstas	delta = krb5_config_get_time_default(context, NULL,
1252233294Sstas					     365 * 24 * 60 * 60,
1253233294Sstas					     "kadmin",
1254233294Sstas					     "password_lifetime",
1255233294Sstas					     NULL);
1256233294Sstas        *ent->entry.pw_end = tmp_time + delta;
1257233294Sstas    }
1258233294Sstas
1259178825Sdfr    ret = LDAP_get_integer_value(db, msg, "sambaPwdMustChange", &tmp_time);
1260178825Sdfr    if (ret == 0) {
1261178825Sdfr	if (ent->entry.pw_end == NULL) {
1262178825Sdfr	    ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end));
1263178825Sdfr	    if (ent->entry.pw_end == NULL) {
1264178825Sdfr		ret = ENOMEM;
1265233294Sstas		krb5_set_error_message(context, ret, "malloc: out of memory");
1266178825Sdfr		goto out;
1267178825Sdfr	    }
1268178825Sdfr	}
1269178825Sdfr	*ent->entry.pw_end = tmp_time;
127072445Sassar    }
1271178825Sdfr
1272178825Sdfr    /* OPTIONAL */
1273178825Sdfr    ret = LDAP_get_integer_value(db, msg, "sambaPwdLastSet", &tmp_time);
1274178825Sdfr    if (ret == 0)
1275178825Sdfr	hdb_entry_set_pw_change_time(context, &ent->entry, tmp_time);
1276178825Sdfr
1277178825Sdfr    {
1278178825Sdfr	int max_life;
1279178825Sdfr
1280178825Sdfr	ent->entry.max_life = malloc(sizeof(*ent->entry.max_life));
1281178825Sdfr	if (ent->entry.max_life == NULL) {
1282178825Sdfr	    ret = ENOMEM;
1283233294Sstas	    krb5_set_error_message(context, ret, "malloc: out of memory");
1284178825Sdfr	    goto out;
1285178825Sdfr	}
1286178825Sdfr	ret = LDAP_get_integer_value(db, msg, "krb5MaxLife", &max_life);
1287178825Sdfr	if (ret) {
1288178825Sdfr	    free(ent->entry.max_life);
1289178825Sdfr	    ent->entry.max_life = NULL;
1290178825Sdfr	} else
1291178825Sdfr	    *ent->entry.max_life = max_life;
129272445Sassar    }
129372445Sassar
1294178825Sdfr    {
1295178825Sdfr	int max_renew;
1296178825Sdfr
1297178825Sdfr	ent->entry.max_renew = malloc(sizeof(*ent->entry.max_renew));
1298178825Sdfr	if (ent->entry.max_renew == NULL) {
1299178825Sdfr	    ret = ENOMEM;
1300233294Sstas	    krb5_set_error_message(context, ret, "malloc: out of memory");
1301178825Sdfr	    goto out;
1302178825Sdfr	}
1303178825Sdfr	ret = LDAP_get_integer_value(db, msg, "krb5MaxRenew", &max_renew);
1304178825Sdfr	if (ret) {
1305178825Sdfr	    free(ent->entry.max_renew);
1306178825Sdfr	    ent->entry.max_renew = NULL;
1307178825Sdfr	} else
1308178825Sdfr	    *ent->entry.max_renew = max_renew;
130972445Sassar    }
131072445Sassar
1311233294Sstas    ret = LDAP_get_integer_value(db, msg, "krb5KDCFlags", &tmp);
1312233294Sstas    if (ret)
131372445Sassar	tmp = 0;
131472445Sassar
1315178825Sdfr    ent->entry.flags = int2HDBFlags(tmp);
1316178825Sdfr
1317178825Sdfr    /* Try and find Samba flags to put into the mix */
1318178825Sdfr    ret = LDAP_get_string_value(db, msg, "sambaAcctFlags", &samba_acct_flags);
1319178825Sdfr    if (ret == 0) {
1320178825Sdfr	/* parse the [UXW...] string:
1321233294Sstas
1322233294Sstas	   'N'    No password
1323233294Sstas	   'D'    Disabled
1324233294Sstas	   'H'    Homedir required
1325233294Sstas	   'T'    Temp account.
1326233294Sstas	   'U'    User account (normal)
1327233294Sstas	   'M'    MNS logon user account - what is this ?
1328233294Sstas	   'W'    Workstation account
1329233294Sstas	   'S'    Server account
1330233294Sstas	   'L'    Locked account
1331233294Sstas	   'X'    No Xpiry on password
1332233294Sstas	   'I'    Interdomain trust account
1333233294Sstas
1334233294Sstas	*/
1335233294Sstas
133672445Sassar	int i;
1337178825Sdfr	int flags_len = strlen(samba_acct_flags);
133872445Sassar
1339178825Sdfr	if (flags_len < 2)
1340178825Sdfr	    goto out2;
1341178825Sdfr
1342233294Sstas	if (samba_acct_flags[0] != '['
1343233294Sstas	    || samba_acct_flags[flags_len - 1] != ']')
1344178825Sdfr	    goto out2;
1345178825Sdfr
1346178825Sdfr	/* Allow forwarding */
1347178825Sdfr	if (samba_forwardable)
1348178825Sdfr	    ent->entry.flags.forwardable = TRUE;
1349178825Sdfr
1350178825Sdfr	for (i=0; i < flags_len; i++) {
1351178825Sdfr	    switch (samba_acct_flags[i]) {
1352178825Sdfr	    case ' ':
1353178825Sdfr	    case '[':
1354178825Sdfr	    case ']':
1355178825Sdfr		break;
1356178825Sdfr	    case 'N':
1357178825Sdfr		/* how to handle no password in kerberos? */
1358178825Sdfr		break;
1359178825Sdfr	    case 'D':
1360178825Sdfr		ent->entry.flags.invalid = TRUE;
1361178825Sdfr		break;
1362178825Sdfr	    case 'H':
1363178825Sdfr		break;
1364178825Sdfr	    case 'T':
1365178825Sdfr		/* temp duplicate */
1366178825Sdfr		ent->entry.flags.invalid = TRUE;
1367178825Sdfr		break;
1368178825Sdfr	    case 'U':
1369178825Sdfr		ent->entry.flags.client = TRUE;
1370178825Sdfr		break;
1371178825Sdfr	    case 'M':
1372178825Sdfr		break;
1373178825Sdfr	    case 'W':
1374178825Sdfr	    case 'S':
1375178825Sdfr		ent->entry.flags.server = TRUE;
1376178825Sdfr		ent->entry.flags.client = TRUE;
1377178825Sdfr		break;
1378178825Sdfr	    case 'L':
1379178825Sdfr		ent->entry.flags.invalid = TRUE;
1380178825Sdfr		break;
1381178825Sdfr	    case 'X':
1382178825Sdfr		if (ent->entry.pw_end) {
1383178825Sdfr		    free(ent->entry.pw_end);
1384178825Sdfr		    ent->entry.pw_end = NULL;
1385178825Sdfr		}
1386178825Sdfr		break;
1387178825Sdfr	    case 'I':
1388178825Sdfr		ent->entry.flags.server = TRUE;
1389178825Sdfr		ent->entry.flags.client = TRUE;
1390178825Sdfr		break;
1391178825Sdfr	    }
139272445Sassar	}
1393178825Sdfr    out2:
1394178825Sdfr	free(samba_acct_flags);
139572445Sassar    }
139672445Sassar
139772445Sassar    ret = 0;
139872445Sassar
1399178825Sdfrout:
1400178825Sdfr    if (unparsed_name)
140172445Sassar	free(unparsed_name);
140272445Sassar
1403178825Sdfr    if (ret)
140472445Sassar	hdb_free_entry(context, ent);
140572445Sassar
140672445Sassar    return ret;
140772445Sassar}
140872445Sassar
1409178825Sdfrstatic krb5_error_code
1410178825SdfrLDAP_close(krb5_context context, HDB * db)
141172445Sassar{
1412178825Sdfr    if (HDB2LDAP(db)) {
1413178825Sdfr	ldap_unbind_ext(HDB2LDAP(db), NULL, NULL);
1414178825Sdfr	((struct hdbldapdb *)db->hdb_db)->h_lp = NULL;
1415178825Sdfr    }
1416233294Sstas
141772445Sassar    return 0;
141872445Sassar}
141972445Sassar
142072445Sassarstatic krb5_error_code
142172445SassarLDAP_lock(krb5_context context, HDB * db, int operation)
142272445Sassar{
142372445Sassar    return 0;
142472445Sassar}
142572445Sassar
1426178825Sdfrstatic krb5_error_code
1427178825SdfrLDAP_unlock(krb5_context context, HDB * db)
142872445Sassar{
142972445Sassar    return 0;
143072445Sassar}
143172445Sassar
143272445Sassarstatic krb5_error_code
1433178825SdfrLDAP_seq(krb5_context context, HDB * db, unsigned flags, hdb_entry_ex * entry)
143472445Sassar{
143572445Sassar    int msgid, rc, parserc;
143672445Sassar    krb5_error_code ret;
143772445Sassar    LDAPMessage *e;
143872445Sassar
1439178825Sdfr    msgid = HDB2MSGID(db);
1440178825Sdfr    if (msgid < 0)
144172445Sassar	return HDB_ERR_NOENTRY;
144272445Sassar
144372445Sassar    do {
1444178825Sdfr	rc = ldap_result(HDB2LDAP(db), msgid, LDAP_MSG_ONE, NULL, &e);
144572445Sassar	switch (rc) {
1446178825Sdfr	case LDAP_RES_SEARCH_REFERENCE:
1447178825Sdfr	    ldap_msgfree(e);
1448178825Sdfr	    ret = 0;
1449178825Sdfr	    break;
145072445Sassar	case LDAP_RES_SEARCH_ENTRY:
145172445Sassar	    /* We have an entry. Parse it. */
1452233294Sstas	    ret = LDAP_message2entry(context, db, e, flags, entry);
145372445Sassar	    ldap_msgfree(e);
145472445Sassar	    break;
145572445Sassar	case LDAP_RES_SEARCH_RESULT:
145672445Sassar	    /* We're probably at the end of the results. If not, abandon. */
145772445Sassar	    parserc =
1458178825Sdfr		ldap_parse_result(HDB2LDAP(db), e, NULL, NULL, NULL,
145972445Sassar				  NULL, NULL, 1);
1460233294Sstas	    ret = HDB_ERR_NOENTRY;
146172445Sassar	    if (parserc != LDAP_SUCCESS
146272445Sassar		&& parserc != LDAP_MORE_RESULTS_TO_RETURN) {
1463233294Sstas	        krb5_set_error_message(context, ret, "ldap_parse_result: %s",
1464233294Sstas				       ldap_err2string(parserc));
1465233294Sstas		ldap_abandon_ext(HDB2LDAP(db), msgid, NULL, NULL);
146672445Sassar	    }
1467178825Sdfr	    HDBSETMSGID(db, -1);
146872445Sassar	    break;
1469178825Sdfr	case LDAP_SERVER_DOWN:
1470178825Sdfr	    ldap_msgfree(e);
1471178825Sdfr	    LDAP_close(context, db);
1472178825Sdfr	    HDBSETMSGID(db, -1);
1473178825Sdfr	    ret = ENETDOWN;
1474178825Sdfr	    break;
147572445Sassar	default:
147672445Sassar	    /* Some unspecified error (timeout?). Abandon. */
147772445Sassar	    ldap_msgfree(e);
1478233294Sstas	    ldap_abandon_ext(HDB2LDAP(db), msgid, NULL, NULL);
147972445Sassar	    ret = HDB_ERR_NOENTRY;
1480178825Sdfr	    HDBSETMSGID(db, -1);
148172445Sassar	    break;
148272445Sassar	}
148372445Sassar    } while (rc == LDAP_RES_SEARCH_REFERENCE);
148472445Sassar
148572445Sassar    if (ret == 0) {
1486178825Sdfr	if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
1487178825Sdfr	    ret = hdb_unseal_keys(context, db, &entry->entry);
148872445Sassar	    if (ret)
1489178825Sdfr		hdb_free_entry(context, entry);
149072445Sassar	}
149172445Sassar    }
149272445Sassar
149372445Sassar    return ret;
149472445Sassar}
149572445Sassar
149672445Sassarstatic krb5_error_code
1497178825SdfrLDAP_firstkey(krb5_context context, HDB *db, unsigned flags,
1498178825Sdfr	      hdb_entry_ex *entry)
149972445Sassar{
1500178825Sdfr    krb5_error_code ret;
1501178825Sdfr    int msgid;
150272445Sassar
1503178825Sdfr    ret = LDAP__connect(context, db);
1504178825Sdfr    if (ret)
1505178825Sdfr	return ret;
150672445Sassar
1507178825Sdfr    ret = LDAP_no_size_limit(context, HDB2LDAP(db));
1508178825Sdfr    if (ret)
1509178825Sdfr	return ret;
151072445Sassar
1511233294Sstas    ret = ldap_search_ext(HDB2LDAP(db), HDB2BASE(db),
1512178825Sdfr			LDAP_SCOPE_SUBTREE,
1513178825Sdfr			"(|(objectClass=krb5Principal)(objectClass=sambaSamAccount))",
1514233294Sstas			krb5kdcentry_attrs, 0,
1515233294Sstas			NULL, NULL, NULL, 0, &msgid);
1516178825Sdfr    if (msgid < 0)
151772445Sassar	return HDB_ERR_NOENTRY;
151872445Sassar
1519178825Sdfr    HDBSETMSGID(db, msgid);
152072445Sassar
152172445Sassar    return LDAP_seq(context, db, flags, entry);
152272445Sassar}
152372445Sassar
152472445Sassarstatic krb5_error_code
152572445SassarLDAP_nextkey(krb5_context context, HDB * db, unsigned flags,
1526178825Sdfr	     hdb_entry_ex * entry)
152772445Sassar{
152872445Sassar    return LDAP_seq(context, db, flags, entry);
152972445Sassar}
153072445Sassar
153172445Sassarstatic krb5_error_code
1532178825SdfrLDAP__connect(krb5_context context, HDB * db)
153372445Sassar{
153490926Snectar    int rc, version = LDAP_VERSION3;
1535127808Snectar    /*
1536127808Snectar     * Empty credentials to do a SASL bind with LDAP. Note that empty
1537127808Snectar     * different from NULL credentials. If you provide NULL
1538127808Snectar     * credentials instead of empty credentials you will get a SASL
1539127808Snectar     * bind in progress message.
1540127808Snectar     */
1541127808Snectar    struct berval bv = { 0, "" };
154272445Sassar
1543178825Sdfr    if (HDB2LDAP(db)) {
154472445Sassar	/* connection has been opened. ping server. */
154572445Sassar	struct sockaddr_un addr;
1546178825Sdfr	socklen_t len = sizeof(addr);
154772445Sassar	int sd;
154872445Sassar
1549178825Sdfr	if (ldap_get_option(HDB2LDAP(db), LDAP_OPT_DESC, &sd) == 0 &&
155072445Sassar	    getpeername(sd, (struct sockaddr *) &addr, &len) < 0) {
155172445Sassar	    /* the other end has died. reopen. */
155272445Sassar	    LDAP_close(context, db);
155372445Sassar	}
155472445Sassar    }
155572445Sassar
1556178825Sdfr    if (HDB2LDAP(db) != NULL) /* server is UP */
155772445Sassar	return 0;
155872445Sassar
1559178825Sdfr    rc = ldap_initialize(&((struct hdbldapdb *)db->hdb_db)->h_lp, HDB2URL(db));
156072445Sassar    if (rc != LDAP_SUCCESS) {
1561233294Sstas	krb5_set_error_message(context, HDB_ERR_NOENTRY, "ldap_initialize: %s",
1562233294Sstas			       ldap_err2string(rc));
156372445Sassar	return HDB_ERR_NOENTRY;
156472445Sassar    }
156572445Sassar
1566178825Sdfr    rc = ldap_set_option(HDB2LDAP(db), LDAP_OPT_PROTOCOL_VERSION,
1567178825Sdfr			 (const void *)&version);
156872445Sassar    if (rc != LDAP_SUCCESS) {
1569233294Sstas	krb5_set_error_message(context, HDB_ERR_BADVERSION,
1570233294Sstas			       "ldap_set_option: %s", ldap_err2string(rc));
1571178825Sdfr	LDAP_close(context, db);
157290926Snectar	return HDB_ERR_BADVERSION;
157372445Sassar    }
157472445Sassar
1575178825Sdfr    rc = ldap_sasl_bind_s(HDB2LDAP(db), NULL, "EXTERNAL", &bv,
1576178825Sdfr			  NULL, NULL, NULL);
1577127808Snectar    if (rc != LDAP_SUCCESS) {
1578233294Sstas	krb5_set_error_message(context, HDB_ERR_BADVERSION,
1579233294Sstas			      "ldap_sasl_bind_s: %s", ldap_err2string(rc));
1580178825Sdfr	LDAP_close(context, db);
1581127808Snectar	return HDB_ERR_BADVERSION;
1582127808Snectar    }
1583127808Snectar
158490926Snectar    return 0;
158572445Sassar}
158672445Sassar
158772445Sassarstatic krb5_error_code
158872445SassarLDAP_open(krb5_context context, HDB * db, int flags, mode_t mode)
158972445Sassar{
159072445Sassar    /* Not the right place for this. */
159172445Sassar#ifdef HAVE_SIGACTION
159290926Snectar    struct sigaction sa;
159372445Sassar
159490926Snectar    sa.sa_flags = 0;
159590926Snectar    sa.sa_handler = SIG_IGN;
159690926Snectar    sigemptyset(&sa.sa_mask);
159772445Sassar
159890926Snectar    sigaction(SIGPIPE, &sa, NULL);
159972445Sassar#else
160072445Sassar    signal(SIGPIPE, SIG_IGN);
160190926Snectar#endif /* HAVE_SIGACTION */
160272445Sassar
160390926Snectar    return LDAP__connect(context, db);
160472445Sassar}
160572445Sassar
160672445Sassarstatic krb5_error_code
1607233294SstasLDAP_fetch_kvno(krb5_context context, HDB * db, krb5_const_principal principal,
1608233294Sstas		unsigned flags, krb5_kvno kvno, hdb_entry_ex * entry)
160972445Sassar{
161072445Sassar    LDAPMessage *msg, *e;
161172445Sassar    krb5_error_code ret;
161272445Sassar
1613178825Sdfr    ret = LDAP_principal2message(context, db, principal, &msg);
1614178825Sdfr    if (ret)
161572445Sassar	return ret;
161672445Sassar
1617178825Sdfr    e = ldap_first_entry(HDB2LDAP(db), msg);
161872445Sassar    if (e == NULL) {
161972445Sassar	ret = HDB_ERR_NOENTRY;
162072445Sassar	goto out;
162172445Sassar    }
162272445Sassar
1623233294Sstas    ret = LDAP_message2entry(context, db, e, flags, entry);
162472445Sassar    if (ret == 0) {
1625178825Sdfr	if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
1626178825Sdfr	    ret = hdb_unseal_keys(context, db, &entry->entry);
162772445Sassar	    if (ret)
1628178825Sdfr		hdb_free_entry(context, entry);
162972445Sassar	}
163072445Sassar    }
163172445Sassar
163272445Sassar  out:
163372445Sassar    ldap_msgfree(msg);
163472445Sassar
163572445Sassar    return ret;
163672445Sassar}
163772445Sassar
163872445Sassarstatic krb5_error_code
1639233294SstasLDAP_fetch(krb5_context context, HDB * db, krb5_const_principal principal,
1640233294Sstas	   unsigned flags, hdb_entry_ex * entry)
1641233294Sstas{
1642233294Sstas    return LDAP_fetch_kvno(context, db, principal,
1643233294Sstas			   flags & (~HDB_F_KVNO_SPECIFIED), 0, entry);
1644233294Sstas}
1645233294Sstas
1646233294Sstasstatic krb5_error_code
164772445SassarLDAP_store(krb5_context context, HDB * db, unsigned flags,
1648178825Sdfr	   hdb_entry_ex * entry)
164972445Sassar{
165072445Sassar    LDAPMod **mods = NULL;
165172445Sassar    krb5_error_code ret;
165290926Snectar    const char *errfn;
165390926Snectar    int rc;
165472445Sassar    LDAPMessage *msg = NULL, *e = NULL;
165572445Sassar    char *dn = NULL, *name = NULL;
165672445Sassar
1657178825Sdfr    ret = LDAP_principal2message(context, db, entry->entry.principal, &msg);
1658178825Sdfr    if (ret == 0)
1659178825Sdfr	e = ldap_first_entry(HDB2LDAP(db), msg);
166072445Sassar
1661178825Sdfr    ret = krb5_unparse_name(context, entry->entry.principal, &name);
1662178825Sdfr    if (ret) {
1663178825Sdfr	free(name);
1664178825Sdfr	return ret;
166572445Sassar    }
166672445Sassar
1667178825Sdfr    ret = hdb_seal_keys(context, db, &entry->entry);
1668178825Sdfr    if (ret)
166972445Sassar	goto out;
167072445Sassar
167172445Sassar    /* turn new entry into LDAPMod array */
167272445Sassar    ret = LDAP_entry2mods(context, db, entry, e, &mods);
1673178825Sdfr    if (ret)
167472445Sassar	goto out;
167572445Sassar
167672445Sassar    if (e == NULL) {
1677178825Sdfr	ret = asprintf(&dn, "krb5PrincipalName=%s,%s", name, HDB2CREATE(db));
167872445Sassar	if (ret < 0) {
167972445Sassar	    ret = ENOMEM;
1680233294Sstas	    krb5_set_error_message(context, ret, "asprintf: out of memory");
168172445Sassar	    goto out;
168272445Sassar	}
168372445Sassar    } else if (flags & HDB_F_REPLACE) {
168472445Sassar	/* Entry exists, and we're allowed to replace it. */
1685178825Sdfr	dn = ldap_get_dn(HDB2LDAP(db), e);
168672445Sassar    } else {
168772445Sassar	/* Entry exists, but we're not allowed to replace it. Bail. */
168872445Sassar	ret = HDB_ERR_EXISTS;
168972445Sassar	goto out;
169072445Sassar    }
169172445Sassar
169272445Sassar    /* write entry into directory */
169372445Sassar    if (e == NULL) {
169472445Sassar	/* didn't exist before */
1695233294Sstas	rc = ldap_add_ext_s(HDB2LDAP(db), dn, mods, NULL, NULL );
1696233294Sstas	errfn = "ldap_add_ext_s";
169772445Sassar    } else {
169872445Sassar	/* already existed, send deltas only */
1699233294Sstas	rc = ldap_modify_ext_s(HDB2LDAP(db), dn, mods, NULL, NULL );
1700233294Sstas	errfn = "ldap_modify_ext_s";
170172445Sassar    }
170272445Sassar
1703178825Sdfr    if (check_ldap(context, db, rc)) {
1704178825Sdfr	char *ld_error = NULL;
1705178825Sdfr	ldap_get_option(HDB2LDAP(db), LDAP_OPT_ERROR_STRING,
1706178825Sdfr			&ld_error);
1707233294Sstas	ret = HDB_ERR_CANT_LOCK_DB;
1708233294Sstas	krb5_set_error_message(context, ret, "%s: %s (DN=%s) %s: %s",
1709178825Sdfr			      errfn, name, dn, ldap_err2string(rc), ld_error);
1710178825Sdfr    } else
171172445Sassar	ret = 0;
171272445Sassar
171372445Sassar  out:
171472445Sassar    /* free stuff */
1715178825Sdfr    if (dn)
171672445Sassar	free(dn);
1717178825Sdfr    if (msg)
171872445Sassar	ldap_msgfree(msg);
1719178825Sdfr    if (mods)
172072445Sassar	ldap_mods_free(mods, 1);
1721178825Sdfr    if (name)
172272445Sassar	free(name);
172372445Sassar
172472445Sassar    return ret;
172572445Sassar}
172672445Sassar
172772445Sassarstatic krb5_error_code
1728178825SdfrLDAP_remove(krb5_context context, HDB *db, krb5_const_principal principal)
172972445Sassar{
173072445Sassar    krb5_error_code ret;
173172445Sassar    LDAPMessage *msg, *e;
173272445Sassar    char *dn = NULL;
173390926Snectar    int rc, limit = LDAP_NO_LIMIT;
173472445Sassar
1735178825Sdfr    ret = LDAP_principal2message(context, db, principal, &msg);
1736178825Sdfr    if (ret)
173772445Sassar	goto out;
173872445Sassar
1739178825Sdfr    e = ldap_first_entry(HDB2LDAP(db), msg);
174072445Sassar    if (e == NULL) {
174172445Sassar	ret = HDB_ERR_NOENTRY;
174272445Sassar	goto out;
174372445Sassar    }
174472445Sassar
1745178825Sdfr    dn = ldap_get_dn(HDB2LDAP(db), e);
174672445Sassar    if (dn == NULL) {
174772445Sassar	ret = HDB_ERR_NOENTRY;
174872445Sassar	goto out;
174972445Sassar    }
175072445Sassar
1751178825Sdfr    rc = ldap_set_option(HDB2LDAP(db), LDAP_OPT_SIZELIMIT, (const void *)&limit);
175290926Snectar    if (rc != LDAP_SUCCESS) {
1753233294Sstas	ret = HDB_ERR_BADVERSION;
1754233294Sstas	krb5_set_error_message(context, ret, "ldap_set_option: %s",
1755178825Sdfr			      ldap_err2string(rc));
175690926Snectar	goto out;
175790926Snectar    }
175872445Sassar
1759233294Sstas    rc = ldap_delete_ext_s(HDB2LDAP(db), dn, NULL, NULL );
1760178825Sdfr    if (check_ldap(context, db, rc)) {
1761178825Sdfr	ret = HDB_ERR_CANT_LOCK_DB;
1762233294Sstas	krb5_set_error_message(context, ret, "ldap_delete_ext_s: %s",
1763233294Sstas			       ldap_err2string(rc));
1764178825Sdfr    } else
176572445Sassar	ret = 0;
176672445Sassar
176772445Sassar  out:
1768178825Sdfr    if (dn != NULL)
176972445Sassar	free(dn);
1770178825Sdfr    if (msg != NULL)
177172445Sassar	ldap_msgfree(msg);
177272445Sassar
177372445Sassar    return ret;
177472445Sassar}
177572445Sassar
177672445Sassarstatic krb5_error_code
1777178825SdfrLDAP_destroy(krb5_context context, HDB * db)
177872445Sassar{
1779178825Sdfr    krb5_error_code ret;
178072445Sassar
1781178825Sdfr    LDAP_close(context, db);
178272445Sassar
178372445Sassar    ret = hdb_clear_master_key(context, db);
1784178825Sdfr    if (HDB2BASE(db))
1785178825Sdfr	free(HDB2BASE(db));
1786178825Sdfr    if (HDB2CREATE(db))
1787178825Sdfr	free(HDB2CREATE(db));
1788178825Sdfr    if (HDB2URL(db))
1789178825Sdfr	free(HDB2URL(db));
1790178825Sdfr    if (db->hdb_name)
1791178825Sdfr	free(db->hdb_name);
1792178825Sdfr    free(db->hdb_db);
179372445Sassar    free(db);
179472445Sassar
179572445Sassar    return ret;
179672445Sassar}
179772445Sassar
1798233294Sstasstatic krb5_error_code
1799178825Sdfrhdb_ldap_common(krb5_context context,
1800178825Sdfr		HDB ** db,
1801178825Sdfr		const char *search_base,
1802178825Sdfr		const char *url)
180372445Sassar{
1804178825Sdfr    struct hdbldapdb *h;
1805178825Sdfr    const char *create_base = NULL;
1806178825Sdfr
1807178825Sdfr    if (search_base == NULL && search_base[0] == '\0') {
1808233294Sstas	krb5_set_error_message(context, ENOMEM, "ldap search base not configured");
1809178825Sdfr	return ENOMEM; /* XXX */
1810178825Sdfr    }
1811178825Sdfr
1812178825Sdfr    if (structural_object == NULL) {
1813178825Sdfr	const char *p;
1814178825Sdfr
1815233294Sstas	p = krb5_config_get_string(context, NULL, "kdc",
1816178825Sdfr				   "hdb-ldap-structural-object", NULL);
1817178825Sdfr	if (p == NULL)
1818178825Sdfr	    p = default_structural_object;
1819178825Sdfr	structural_object = strdup(p);
1820178825Sdfr	if (structural_object == NULL) {
1821233294Sstas	    krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
1822178825Sdfr	    return ENOMEM;
1823178825Sdfr	}
1824178825Sdfr    }
1825178825Sdfr
1826233294Sstas    samba_forwardable =
1827178825Sdfr	krb5_config_get_bool_default(context, NULL, TRUE,
1828178825Sdfr				     "kdc", "hdb-samba-forwardable", NULL);
1829178825Sdfr
1830178825Sdfr    *db = calloc(1, sizeof(**db));
183190926Snectar    if (*db == NULL) {
1832233294Sstas	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
183372445Sassar	return ENOMEM;
183490926Snectar    }
1835178825Sdfr    memset(*db, 0, sizeof(**db));
183672445Sassar
1837178825Sdfr    h = calloc(1, sizeof(*h));
1838178825Sdfr    if (h == NULL) {
1839178825Sdfr	free(*db);
1840178825Sdfr	*db = NULL;
1841233294Sstas	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
1842178825Sdfr	return ENOMEM;
1843178825Sdfr    }
1844178825Sdfr    (*db)->hdb_db = h;
184590926Snectar
1846178825Sdfr    /* XXX */
1847178825Sdfr    if (asprintf(&(*db)->hdb_name, "ldap:%s", search_base) == -1) {
1848178825Sdfr	LDAP_destroy(context, *db);
1849178825Sdfr	*db = NULL;
1850233294Sstas	krb5_set_error_message(context, ENOMEM, "strdup: out of memory");
1851178825Sdfr	return ENOMEM;
185290926Snectar    }
185390926Snectar
1854178825Sdfr    h->h_url = strdup(url);
1855178825Sdfr    h->h_base = strdup(search_base);
1856178825Sdfr    if (h->h_url == NULL || h->h_base == NULL) {
1857178825Sdfr	LDAP_destroy(context, *db);
1858178825Sdfr	*db = NULL;
1859233294Sstas	krb5_set_error_message(context, ENOMEM, "strdup: out of memory");
1860178825Sdfr	return ENOMEM;
1861178825Sdfr    }
186272445Sassar
1863233294Sstas    create_base = krb5_config_get_string(context, NULL, "kdc",
1864178825Sdfr					 "hdb-ldap-create-base", NULL);
1865178825Sdfr    if (create_base == NULL)
1866178825Sdfr	create_base = h->h_base;
1867178825Sdfr
1868178825Sdfr    h->h_createbase = strdup(create_base);
1869178825Sdfr    if (h->h_createbase == NULL) {
1870178825Sdfr	LDAP_destroy(context, *db);
1871178825Sdfr	*db = NULL;
1872233294Sstas	krb5_set_error_message(context, ENOMEM, "strdup: out of memory");
1873178825Sdfr	return ENOMEM;
1874178825Sdfr    }
1875178825Sdfr
1876178825Sdfr    (*db)->hdb_master_key_set = 0;
1877178825Sdfr    (*db)->hdb_openp = 0;
1878233294Sstas    (*db)->hdb_capability_flags = 0;
1879178825Sdfr    (*db)->hdb_open = LDAP_open;
1880178825Sdfr    (*db)->hdb_close = LDAP_close;
1881233294Sstas    (*db)->hdb_fetch_kvno = LDAP_fetch_kvno;
1882178825Sdfr    (*db)->hdb_store = LDAP_store;
1883178825Sdfr    (*db)->hdb_remove = LDAP_remove;
1884178825Sdfr    (*db)->hdb_firstkey = LDAP_firstkey;
1885178825Sdfr    (*db)->hdb_nextkey = LDAP_nextkey;
1886178825Sdfr    (*db)->hdb_lock = LDAP_lock;
1887178825Sdfr    (*db)->hdb_unlock = LDAP_unlock;
1888178825Sdfr    (*db)->hdb_rename = NULL;
1889178825Sdfr    (*db)->hdb__get = NULL;
1890178825Sdfr    (*db)->hdb__put = NULL;
1891178825Sdfr    (*db)->hdb__del = NULL;
1892178825Sdfr    (*db)->hdb_destroy = LDAP_destroy;
1893178825Sdfr
189472445Sassar    return 0;
189572445Sassar}
189672445Sassar
1897178825Sdfrkrb5_error_code
1898178825Sdfrhdb_ldap_create(krb5_context context, HDB ** db, const char *arg)
1899178825Sdfr{
1900178825Sdfr    return hdb_ldap_common(context, db, arg, "ldapi:///");
1901178825Sdfr}
1902178825Sdfr
1903178825Sdfrkrb5_error_code
1904178825Sdfrhdb_ldapi_create(krb5_context context, HDB ** db, const char *arg)
1905178825Sdfr{
1906178825Sdfr    krb5_error_code ret;
1907178825Sdfr    char *search_base, *p;
1908178825Sdfr
1909178825Sdfr    asprintf(&p, "ldapi:%s", arg);
1910178825Sdfr    if (p == NULL) {
1911178825Sdfr	*db = NULL;
1912233294Sstas	krb5_set_error_message(context, ENOMEM, "out of memory");
1913178825Sdfr	return ENOMEM;
1914178825Sdfr    }
1915178825Sdfr    search_base = strchr(p + strlen("ldapi://"), ':');
1916178825Sdfr    if (search_base == NULL) {
1917178825Sdfr	*db = NULL;
1918233294Sstas	krb5_set_error_message(context, HDB_ERR_BADVERSION,
1919233294Sstas			       "search base missing");
1920178825Sdfr	return HDB_ERR_BADVERSION;
1921178825Sdfr    }
1922178825Sdfr    *search_base = '\0';
1923178825Sdfr    search_base++;
1924178825Sdfr
1925178825Sdfr    ret = hdb_ldap_common(context, db, search_base, p);
1926178825Sdfr    free(p);
1927178825Sdfr    return ret;
1928178825Sdfr}
1929178825Sdfr
1930178825Sdfr#ifdef OPENLDAP_MODULE
1931178825Sdfr
1932178825Sdfrstruct hdb_so_method hdb_ldap_interface = {
1933178825Sdfr    HDB_INTERFACE_VERSION,
1934178825Sdfr    "ldap",
1935178825Sdfr    hdb_ldap_create
1936178825Sdfr};
1937178825Sdfr
1938178825Sdfrstruct hdb_so_method hdb_ldapi_interface = {
1939178825Sdfr    HDB_INTERFACE_VERSION,
1940178825Sdfr    "ldapi",
1941178825Sdfr    hdb_ldapi_create
1942178825Sdfr};
1943178825Sdfr
1944178825Sdfr#endif
1945178825Sdfr
194672445Sassar#endif				/* OPENLDAP */
1947