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