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