xref: /freebsd/crypto/heimdal/lib/hdb/hdb-ldap.c (revision 884a2a699669ec61e2366e3e358342dbc94be24a)
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 
37 RCSID("$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 
46 static krb5_error_code LDAP__connect(krb5_context context, HDB *);
47 static krb5_error_code LDAP_close(krb5_context context, HDB *);
48 
49 static krb5_error_code
50 LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,
51 		   hdb_entry_ex * ent);
52 
53 static const char *default_structural_object = "account";
54 static char *structural_object;
55 static krb5_boolean samba_forwardable;
56 
57 struct 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 
77 static 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 
104 static 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 
117 static int
118 LDAP_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 
131 static int
132 check_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 
145 static krb5_error_code
146 LDAP__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 
199 static krb5_error_code
200 LDAP_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 
238 static krb5_error_code
239 LDAP_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 
274 static krb5_error_code
275 LDAP_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 
288 static krb5_error_code
289 LDAP_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 
306 static krb5_error_code
307 LDAP_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 
330 static krb5_error_code
331 LDAP_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 
345 static krb5_error_code
346 LDAP_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 
372 static krb5_error_code
373 LDAP_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 
726 static krb5_error_code
727 LDAP_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 
772 static krb5_error_code
773 LDAP__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 
848 static krb5_error_code
849 LDAP_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  */
890 static krb5_error_code
891 LDAP_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 
1293 out:
1294     if (unparsed_name)
1295 	free(unparsed_name);
1296 
1297     if (ret)
1298 	hdb_free_entry(context, ent);
1299 
1300     return ret;
1301 }
1302 
1303 static krb5_error_code
1304 LDAP_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 
1314 static krb5_error_code
1315 LDAP_lock(krb5_context context, HDB * db, int operation)
1316 {
1317     return 0;
1318 }
1319 
1320 static krb5_error_code
1321 LDAP_unlock(krb5_context context, HDB * db)
1322 {
1323     return 0;
1324 }
1325 
1326 static krb5_error_code
1327 LDAP_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 
1390 static krb5_error_code
1391 LDAP_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 
1417 static krb5_error_code
1418 LDAP_nextkey(krb5_context context, HDB * db, unsigned flags,
1419 	     hdb_entry_ex * entry)
1420 {
1421     return LDAP_seq(context, db, flags, entry);
1422 }
1423 
1424 static krb5_error_code
1425 LDAP__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 
1480 static krb5_error_code
1481 LDAP_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 
1499 static krb5_error_code
1500 LDAP_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 
1531 static krb5_error_code
1532 LDAP_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 
1612 static krb5_error_code
1613 LDAP_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 
1661 static krb5_error_code
1662 LDAP_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 
1683 krb5_error_code
1684 hdb_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 
1781 krb5_error_code
1782 hdb_ldap_create(krb5_context context, HDB ** db, const char *arg)
1783 {
1784     return hdb_ldap_common(context, db, arg, "ldapi:///");
1785 }
1786 
1787 krb5_error_code
1788 hdb_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 
1815 struct hdb_so_method hdb_ldap_interface = {
1816     HDB_INTERFACE_VERSION,
1817     "ldap",
1818     hdb_ldap_create
1819 };
1820 
1821 struct 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