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