xref: /freebsd/crypto/heimdal/lib/hdb/hdb-ldap.c (revision 17d6c636720d00f77e5d098daf4c278f89d84f7b)
1 /*
2  * Copyright (c) 1999 - 2001, PADL Software Pty Ltd.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of PADL Software  nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include "hdb_locl.h"
34 
35 RCSID("$Id: hdb-ldap.c,v 1.8 2001/03/26 00:59:37 assar Exp $");
36 
37 #ifdef OPENLDAP
38 
39 #include <lber.h>
40 #include <ldap.h>
41 #include <ctype.h>
42 #include <sys/un.h>
43 
44 static krb5_error_code LDAP__connect(krb5_context context, HDB * db);
45 
46 static krb5_error_code
47 LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,
48 		   hdb_entry * ent);
49 
50 static char *krb5kdcentry_attrs[] =
51     { "krb5PrincipalName", "cn", "krb5PrincipalRealm",
52     "krb5KeyVersionNumber", "krb5Key",
53     "krb5ValidStart", "krb5ValidEnd", "krb5PasswordEnd",
54     "krb5MaxLife", "krb5MaxRenew", "krb5KDCFlags", "krb5EncryptionType",
55     "modifiersName", "modifyTimestamp", "creatorsName", "createTimestamp",
56     NULL
57 };
58 
59 static char *krb5principal_attrs[] =
60     { "krb5PrincipalName", "cn", "krb5PrincipalRealm",
61     "modifiersName", "modifyTimestamp", "creatorsName", "createTimestamp",
62     NULL
63 };
64 
65 /* based on samba: source/passdb/ldap.c */
66 static krb5_error_code
67 LDAP_addmod_len(LDAPMod *** modlist, int modop, const char *attribute,
68 		unsigned char *value, size_t len)
69 {
70     LDAPMod **mods = *modlist;
71     int i, j;
72 
73     if (mods == NULL) {
74 	mods = (LDAPMod **) calloc(1, sizeof(LDAPMod *));
75 	if (mods == NULL) {
76 	    return ENOMEM;
77 	}
78 	mods[0] = NULL;
79     }
80 
81     for (i = 0; mods[i] != NULL; ++i) {
82 	if ((mods[i]->mod_op & (~LDAP_MOD_BVALUES)) == modop
83 	    && (!strcasecmp(mods[i]->mod_type, attribute))) {
84 	    break;
85 	}
86     }
87 
88     if (mods[i] == NULL) {
89 	mods = (LDAPMod **) realloc(mods, (i + 2) * sizeof(LDAPMod *));
90 	if (mods == NULL) {
91 	    return ENOMEM;
92 	}
93 	mods[i] = (LDAPMod *) malloc(sizeof(LDAPMod));
94 	if (mods[i] == NULL) {
95 	    return ENOMEM;
96 	}
97 	mods[i]->mod_op = modop | LDAP_MOD_BVALUES;
98 	mods[i]->mod_bvalues = NULL;
99 	mods[i]->mod_type = strdup(attribute);
100 	if (mods[i]->mod_type == NULL) {
101 	    return ENOMEM;
102 	}
103 	mods[i + 1] = NULL;
104     }
105 
106     if (value != NULL) {
107 	j = 0;
108 	if (mods[i]->mod_bvalues != NULL) {
109 	    for (; mods[i]->mod_bvalues[j] != NULL; j++);
110 	}
111 	mods[i]->mod_bvalues =
112 	    (struct berval **) realloc(mods[i]->mod_bvalues,
113 				       (j + 2) * sizeof(struct berval *));
114 	if (mods[i]->mod_bvalues == NULL) {
115 	    return ENOMEM;
116 	}
117 	/* Caller allocates memory on our behalf, unlike LDAP_addmod. */
118 	mods[i]->mod_bvalues[j] =
119 	    (struct berval *) malloc(sizeof(struct berval));
120 	if (mods[i]->mod_bvalues[j] == NULL) {
121 	    return ENOMEM;
122 	}
123 	mods[i]->mod_bvalues[j]->bv_val = value;
124 	mods[i]->mod_bvalues[j]->bv_len = len;
125 	mods[i]->mod_bvalues[j + 1] = NULL;
126     }
127     *modlist = mods;
128     return 0;
129 }
130 
131 static krb5_error_code
132 LDAP_addmod(LDAPMod *** modlist, int modop, const char *attribute,
133 	    const char *value)
134 {
135     LDAPMod **mods = *modlist;
136     int i, j;
137 
138     if (mods == NULL) {
139 	mods = (LDAPMod **) calloc(1, sizeof(LDAPMod *));
140 	if (mods == NULL) {
141 	    return ENOMEM;
142 	}
143 	mods[0] = NULL;
144     }
145 
146     for (i = 0; mods[i] != NULL; ++i) {
147 	if (mods[i]->mod_op == modop
148 	    && (!strcasecmp(mods[i]->mod_type, attribute))) {
149 	    break;
150 	}
151     }
152 
153     if (mods[i] == NULL) {
154 	mods = (LDAPMod **) realloc(mods, (i + 2) * sizeof(LDAPMod *));
155 	if (mods == NULL) {
156 	    return ENOMEM;
157 	}
158 	mods[i] = (LDAPMod *) malloc(sizeof(LDAPMod));
159 	if (mods[i] == NULL) {
160 	    return ENOMEM;
161 	}
162 	mods[i]->mod_op = modop;
163 	mods[i]->mod_values = NULL;
164 	mods[i]->mod_type = strdup(attribute);
165 	if (mods[i]->mod_type == NULL) {
166 	    return ENOMEM;
167 	}
168 	mods[i + 1] = NULL;
169     }
170 
171     if (value != NULL) {
172 	j = 0;
173 	if (mods[i]->mod_values != NULL) {
174 	    for (; mods[i]->mod_values[j] != NULL; j++);
175 	}
176 	mods[i]->mod_values = (char **) realloc(mods[i]->mod_values,
177 						(j + 2) * sizeof(char *));
178 	if (mods[i]->mod_values == NULL) {
179 	    return ENOMEM;
180 	}
181 	mods[i]->mod_values[j] = strdup(value);
182 	if (mods[i]->mod_values[j] == NULL) {
183 	    return ENOMEM;
184 	}
185 	mods[i]->mod_values[j + 1] = NULL;
186     }
187     *modlist = mods;
188     return 0;
189 }
190 
191 static krb5_error_code
192 LDAP_addmod_generalized_time(LDAPMod *** mods, int modop,
193 			     const char *attribute, KerberosTime * time)
194 {
195     char buf[22];
196     struct tm *tm;
197 
198     /* XXX not threadsafe */
199     tm = gmtime(time);
200     strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", tm);
201 
202     return LDAP_addmod(mods, modop, attribute, buf);
203 }
204 
205 static krb5_error_code
206 LDAP_get_string_value(HDB * db, LDAPMessage * entry,
207 		      const char *attribute, char **ptr)
208 {
209     char **vals;
210     int ret;
211 
212     vals = ldap_get_values((LDAP *) db->db, entry, (char *) attribute);
213     if (vals == NULL) {
214 	return HDB_ERR_NOENTRY;
215     }
216     *ptr = strdup(vals[0]);
217     if (*ptr == NULL) {
218 	ret = ENOMEM;
219     } else {
220 	ret = 0;
221     }
222 
223     ldap_value_free(vals);
224 
225     return ret;
226 }
227 
228 static krb5_error_code
229 LDAP_get_integer_value(HDB * db, LDAPMessage * entry,
230 		       const char *attribute, int *ptr)
231 {
232     char **vals;
233 
234     vals = ldap_get_values((LDAP *) db->db, entry, (char *) attribute);
235     if (vals == NULL) {
236 	return HDB_ERR_NOENTRY;
237     }
238     *ptr = atoi(vals[0]);
239     ldap_value_free(vals);
240     return 0;
241 }
242 
243 static krb5_error_code
244 LDAP_get_generalized_time_value(HDB * db, LDAPMessage * entry,
245 				const char *attribute, KerberosTime * kt)
246 {
247     char *tmp, *gentime;
248     struct tm tm;
249     int ret;
250 
251     *kt = 0;
252 
253     ret = LDAP_get_string_value(db, entry, attribute, &gentime);
254     if (ret != 0) {
255 	return ret;
256     }
257 
258     tmp = strptime(gentime, "%Y%m%d%H%M%SZ", &tm);
259     if (tmp == NULL) {
260 	free(gentime);
261 	return HDB_ERR_NOENTRY;
262     }
263 
264     free(gentime);
265 
266     *kt = timegm(&tm);
267 
268     return 0;
269 }
270 
271 static krb5_error_code
272 LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry * ent,
273 		LDAPMessage * msg, LDAPMod *** pmods)
274 {
275     krb5_error_code ret;
276     krb5_boolean is_new_entry;
277     int rc, i;
278     char *tmp = NULL;
279     LDAPMod **mods = NULL;
280     hdb_entry orig;
281     unsigned long oflags, nflags;
282 
283     if (msg != NULL) {
284 	ret = LDAP_message2entry(context, db, msg, &orig);
285 	if (ret != 0) {
286 	    goto out;
287 	}
288 	is_new_entry = FALSE;
289     } else {
290 	/* to make it perfectly obvious we're depending on
291 	 * orig being intiialized to zero */
292 	memset(&orig, 0, sizeof(orig));
293 	is_new_entry = TRUE;
294     }
295 
296     if (is_new_entry) {
297 	ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "top");
298 	if (ret != 0) {
299 	    goto out;
300 	}
301 	/* person is the structural object class */
302 	ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "person");
303 	if (ret != 0) {
304 	    goto out;
305 	}
306 	ret =
307 	    LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass",
308 			"krb5Principal");
309 	if (ret != 0) {
310 	    goto out;
311 	}
312 	ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass",
313 			  "krb5KDCEntry");
314 	if (ret != 0) {
315 	    goto out;
316 	}
317     }
318 
319     if (is_new_entry ||
320 	krb5_principal_compare(context, ent->principal, orig.principal) ==
321 	FALSE) {
322 	ret = krb5_unparse_name(context, ent->principal, &tmp);
323 	if (ret != 0) {
324 	    goto out;
325 	}
326 	ret =
327 	    LDAP_addmod(&mods, LDAP_MOD_REPLACE, "krb5PrincipalName", tmp);
328 	if (ret != 0) {
329 	    free(tmp);
330 	    goto out;
331 	}
332 	free(tmp);
333     }
334 
335     if (ent->kvno != orig.kvno) {
336 	rc = asprintf(&tmp, "%d", ent->kvno);
337 	if (rc < 0) {
338 	    ret = ENOMEM;
339 	    goto out;
340 	}
341 	ret =
342 	    LDAP_addmod(&mods, LDAP_MOD_REPLACE, "krb5KeyVersionNumber",
343 			tmp);
344 	free(tmp);
345 	if (ret != 0) {
346 	    goto out;
347 	}
348     }
349 
350     if (ent->valid_start) {
351 	if (orig.valid_end == NULL
352 	    || (*(ent->valid_start) != *(orig.valid_start))) {
353 	    ret =
354 		LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,
355 					     "krb5ValidStart",
356 					     ent->valid_start);
357 	    if (ret != 0) {
358 		goto out;
359 	    }
360 	}
361     }
362 
363     if (ent->valid_end) {
364 	if (orig.valid_end == NULL
365 	    || (*(ent->valid_end) != *(orig.valid_end))) {
366 	    ret =
367 		LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,
368 					     "krb5ValidEnd",
369 					     ent->valid_end);
370 	    if (ret != 0) {
371 		goto out;
372 	    }
373 	}
374     }
375 
376     if (ent->pw_end) {
377 	if (orig.pw_end == NULL || (*(ent->pw_end) != *(orig.pw_end))) {
378 	    ret =
379 		LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,
380 					     "krb5PasswordEnd",
381 					     ent->pw_end);
382 	    if (ret != 0) {
383 		goto out;
384 	    }
385 	}
386     }
387 
388     if (ent->max_life) {
389 	if (orig.max_life == NULL
390 	    || (*(ent->max_life) != *(orig.max_life))) {
391 	    rc = asprintf(&tmp, "%d", *(ent->max_life));
392 	    if (rc < 0) {
393 		ret = ENOMEM;
394 		goto out;
395 	    }
396 	    ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "krb5MaxLife", tmp);
397 	    free(tmp);
398 	    if (ret != 0) {
399 		goto out;
400 	    }
401 	}
402     }
403 
404     if (ent->max_renew) {
405 	if (orig.max_renew == NULL
406 	    || (*(ent->max_renew) != *(orig.max_renew))) {
407 	    rc = asprintf(&tmp, "%d", *(ent->max_renew));
408 	    if (rc < 0) {
409 		ret = ENOMEM;
410 		goto out;
411 	    }
412 	    ret =
413 		LDAP_addmod(&mods, LDAP_MOD_REPLACE, "krb5MaxRenew", tmp);
414 	    free(tmp);
415 	    if (ret != 0) {
416 		goto out;
417 	    }
418 	}
419     }
420 
421     memset(&oflags, 0, sizeof(oflags));
422     memcpy(&oflags, &orig.flags, sizeof(HDBFlags));
423     memset(&nflags, 0, sizeof(nflags));
424     memcpy(&nflags, &ent->flags, sizeof(HDBFlags));
425 
426     if (memcmp(&oflags, &nflags, sizeof(HDBFlags))) {
427 	rc = asprintf(&tmp, "%lu", nflags);
428 	if (rc < 0) {
429 	    ret = ENOMEM;
430 	    goto out;
431 	}
432 	ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "krb5KDCFlags", tmp);
433 	free(tmp);
434 	if (ret != 0) {
435 	    goto out;
436 	}
437     }
438 
439     if (is_new_entry == FALSE && orig.keys.len > 0) {
440 	/* for the moment, clobber and replace keys. */
441 	ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5Key", NULL);
442 	if (ret != 0) {
443 	    goto out;
444 	}
445     }
446 
447     for (i = 0; i < ent->keys.len; i++) {
448 	unsigned char *buf;
449 	size_t len;
450 	Key new;
451 
452 	ret = copy_Key(&ent->keys.val[i], &new);
453 	if (ret != 0) {
454 	    goto out;
455 	}
456 
457 	len = length_Key(&new);
458 	buf = malloc(len);
459 	if (buf == NULL) {
460 	    ret = ENOMEM;
461 	    free_Key(&new);
462 	    goto out;
463 	}
464 
465 	ret = encode_Key(buf + len - 1, len, &new, &len);
466 	if (ret != 0) {
467 	    free(buf);
468 	    free_Key(&new);
469 	    goto out;
470 	}
471 	free_Key(&new);
472 
473 	/* addmod_len _owns_ the key, doesn't need to copy it */
474 	ret = LDAP_addmod_len(&mods, LDAP_MOD_ADD, "krb5Key", buf, len);
475 	if (ret != 0) {
476 	    goto out;
477 	}
478     }
479 
480     if (ent->etypes) {
481 	/* clobber and replace encryption types. */
482 	if (is_new_entry == FALSE) {
483 	    ret =
484 		LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5EncryptionType",
485 			    NULL);
486 	}
487 	for (i = 0; i < ent->etypes->len; i++) {
488 	    rc = asprintf(&tmp, "%d", ent->etypes->val[i]);
489 	    if (rc < 0) {
490 		ret = ENOMEM;
491 		goto out;
492 	    }
493 	    free(tmp);
494 	    ret =
495 		LDAP_addmod(&mods, LDAP_MOD_ADD, "krb5EncryptionType",
496 			    tmp);
497 	    if (ret != 0) {
498 		goto out;
499 	    }
500 	}
501     }
502 
503     /* for clarity */
504     ret = 0;
505 
506   out:
507 
508     if (ret == 0) {
509 	*pmods = mods;
510     } else if (mods != NULL) {
511 	ldap_mods_free(mods, 1);
512 	*pmods = NULL;
513     }
514 
515     if (msg != NULL) {
516 	hdb_free_entry(context, &orig);
517     }
518 
519     return ret;
520 }
521 
522 static krb5_error_code
523 LDAP_dn2principal(krb5_context context, HDB * db, const char *dn,
524 		  krb5_principal * principal)
525 {
526     krb5_error_code ret;
527     int rc;
528     char **values;
529     LDAPMessage *res = NULL, *e;
530 
531     rc = 1;
532     (void) ldap_set_option((LDAP *) db->db, LDAP_OPT_SIZELIMIT, &rc);
533     rc = ldap_search_s((LDAP *) db->db, db->name, LDAP_SCOPE_BASE,
534 		       "(objectclass=krb5Principal)", krb5principal_attrs,
535 		       0, &res);
536 
537     if (rc != LDAP_SUCCESS) {
538 	ret = HDB_ERR_NOENTRY;
539 	goto out;
540     }
541 
542     e = ldap_first_entry((LDAP *) db->db, res);
543     if (e == NULL) {
544 	ret = HDB_ERR_NOENTRY;
545 	goto out;
546     }
547 
548     values = ldap_get_values((LDAP *) db->db, e, "krb5PrincipalName");
549     if (values == NULL) {
550 	ret = HDB_ERR_NOENTRY;
551 	goto out;
552     }
553 
554     ret = krb5_parse_name(context, values[0], principal);
555     ldap_value_free(values);
556 
557   out:
558     if (res != NULL) {
559 	ldap_msgfree(res);
560     }
561     return ret;
562 }
563 
564 static krb5_error_code
565 LDAP__lookup_princ(krb5_context context, HDB * db, const char *princname,
566 		   LDAPMessage ** msg)
567 {
568     krb5_error_code ret;
569     int rc;
570     char *filter = NULL;
571 
572     (void) LDAP__connect(context, db);
573 
574     rc =
575 	asprintf(&filter,
576 		 "(&(objectclass=krb5KDCEntry)(krb5PrincipalName=%s))",
577 		 princname);
578     if (rc < 0) {
579 	ret = ENOMEM;
580 	goto out;
581     }
582 
583     rc = 1;
584     (void) ldap_set_option((LDAP *) db->db, LDAP_OPT_SIZELIMIT, (void *) &rc);
585 
586     rc = ldap_search_s((LDAP *) db->db, db->name, LDAP_SCOPE_ONELEVEL, filter,
587 		       krb5kdcentry_attrs, 0, msg);
588     if (rc != LDAP_SUCCESS) {
589 	ret = HDB_ERR_NOENTRY;
590 	goto out;
591     }
592 
593     ret = 0;
594 
595   out:
596     if (filter != NULL) {
597 	free(filter);
598     }
599     return ret;
600 }
601 
602 static krb5_error_code
603 LDAP_principal2message(krb5_context context, HDB * db,
604 		       krb5_principal princ, LDAPMessage ** msg)
605 {
606     char *princname = NULL;
607     krb5_error_code ret;
608 
609     ret = krb5_unparse_name(context, princ, &princname);
610     if (ret != 0) {
611 	return ret;
612     }
613 
614     ret = LDAP__lookup_princ(context, db, princname, msg);
615     free(princname);
616 
617     return ret;
618 }
619 
620 /*
621  * Construct an hdb_entry from a directory entry.
622  */
623 static krb5_error_code
624 LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,
625 		   hdb_entry * ent)
626 {
627     char *unparsed_name = NULL, *dn = NULL;
628     int ret;
629     unsigned long tmp;
630     struct berval **keys;
631     char **values;
632 
633     memset(ent, 0, sizeof(*ent));
634     memset(&ent->flags, 0, sizeof(HDBFlags));
635 
636     ret =
637 	LDAP_get_string_value(db, msg, "krb5PrincipalName",
638 			      &unparsed_name);
639     if (ret != 0) {
640 	return ret;
641     }
642 
643     ret = krb5_parse_name(context, unparsed_name, &ent->principal);
644     if (ret != 0) {
645 	goto out;
646     }
647 
648     ret =
649 	LDAP_get_integer_value(db, msg, "krb5KeyVersionNumber",
650 			       &ent->kvno);
651     if (ret != 0) {
652 	ent->kvno = 0;
653     }
654 
655     keys = ldap_get_values_len((LDAP *) db->db, msg, "krb5Key");
656     if (keys != NULL) {
657 	int i;
658 	size_t l;
659 
660 	ent->keys.len = ldap_count_values_len(keys);
661 	ent->keys.val = (Key *) calloc(ent->keys.len, sizeof(Key));
662 	for (i = 0; i < ent->keys.len; i++) {
663 	    decode_Key((unsigned char *) keys[i]->bv_val,
664 		       (size_t) keys[i]->bv_len, &ent->keys.val[i], &l);
665 	}
666 	ber_bvecfree(keys);
667     } else {
668 #if 1
669 	/*
670 	 * This violates the ASN1 but it allows a principal to
671 	 * be related to a general directory entry without creating
672 	 * the keys. Hopefully it's OK.
673 	 */
674 	ent->keys.len = 0;
675 	ent->keys.val = NULL;
676 #else
677 	ret = HDB_ERR_NOENTRY;
678 	goto out;
679 #endif
680     }
681 
682     ret =
683 	LDAP_get_generalized_time_value(db, msg, "createTimestamp",
684 					&ent->created_by.time);
685     if (ret != 0) {
686 	ent->created_by.time = time(NULL);
687     }
688 
689     ent->created_by.principal = NULL;
690 
691     ret = LDAP_get_string_value(db, msg, "creatorsName", &dn);
692     if (ret == 0) {
693 	if (LDAP_dn2principal(context, db, dn, &ent->created_by.principal)
694 	    != 0) {
695 	    ent->created_by.principal = NULL;
696 	}
697 	free(dn);
698     }
699 
700     ent->modified_by = (Event *) malloc(sizeof(Event));
701     if (ent->modified_by == NULL) {
702 	ret = ENOMEM;
703 	goto out;
704     }
705     ret =
706 	LDAP_get_generalized_time_value(db, msg, "modifyTimestamp",
707 					&ent->modified_by->time);
708     if (ret == 0) {
709 	ret = LDAP_get_string_value(db, msg, "modifiersName", &dn);
710 	if (LDAP_dn2principal
711 	    (context, db, dn, &ent->modified_by->principal) != 0) {
712 	    ent->modified_by->principal = NULL;
713 	}
714 	free(dn);
715     } else {
716 	free(ent->modified_by);
717 	ent->modified_by = NULL;
718     }
719 
720     if ((ent->valid_start = (KerberosTime *) malloc(sizeof(KerberosTime)))
721 	== NULL) {
722 	ret = ENOMEM;
723 	goto out;
724     }
725     ret =
726 	LDAP_get_generalized_time_value(db, msg, "krb5ValidStart",
727 					ent->valid_start);
728     if (ret != 0) {
729 	/* OPTIONAL */
730 	free(ent->valid_start);
731 	ent->valid_start = NULL;
732     }
733 
734     if ((ent->valid_end = (KerberosTime *) malloc(sizeof(KerberosTime))) ==
735 	NULL) {ret = ENOMEM;
736 	goto out;
737     }
738     ret =
739 	LDAP_get_generalized_time_value(db, msg, "krb5ValidEnd",
740 					ent->valid_end);
741     if (ret != 0) {
742 	/* OPTIONAL */
743 	free(ent->valid_end);
744 	ent->valid_end = NULL;
745     }
746 
747     if ((ent->pw_end = (KerberosTime *) malloc(sizeof(KerberosTime))) ==
748 	NULL) {ret = ENOMEM;
749 	goto out;
750     }
751     ret =
752 	LDAP_get_generalized_time_value(db, msg, "krb5PasswordEnd",
753 					ent->pw_end);
754     if (ret != 0) {
755 	/* OPTIONAL */
756 	free(ent->pw_end);
757 	ent->pw_end = NULL;
758     }
759 
760     ent->max_life = (int *) malloc(sizeof(int));
761     if (ent->max_life == NULL) {
762 	ret = ENOMEM;
763 	goto out;
764     }
765     ret = LDAP_get_integer_value(db, msg, "krb5MaxLife", ent->max_life);
766     if (ret != 0) {
767 	free(ent->max_life);
768 	ent->max_life = NULL;
769     }
770 
771     ent->max_renew = (int *) malloc(sizeof(int));
772     if (ent->max_renew == NULL) {
773 	ret = ENOMEM;
774 	goto out;
775     }
776     ret = LDAP_get_integer_value(db, msg, "krb5MaxRenew", ent->max_renew);
777     if (ret != 0) {
778 	free(ent->max_renew);
779 	ent->max_renew = NULL;
780     }
781 
782     values = ldap_get_values((LDAP *) db->db, msg, "krb5KDCFlags");
783     if (values != NULL) {
784 	tmp = strtoul(values[0], (char **) NULL, 10);
785 	if (tmp == ULONG_MAX && errno == ERANGE) {
786 	    ret = ERANGE;
787 	    goto out;
788 	}
789     } else {
790 	tmp = 0;
791     }
792     memcpy(&ent->flags, &tmp, sizeof(HDBFlags));
793 
794     values = ldap_get_values((LDAP *) db->db, msg, "krb5EncryptionType");
795     if (values != NULL) {
796 	int i;
797 
798 	ent->etypes = malloc(sizeof(*(ent->etypes)));
799 	if (ent->etypes == NULL) {
800 	    ret = ENOMEM;
801 	    goto out;
802 	}
803 	ent->etypes->len = ldap_count_values(values);
804 	ent->etypes->val = calloc(ent->etypes->len, sizeof(int));
805 	for (i = 0; i < ent->etypes->len; i++) {
806 	    ent->etypes->val[i] = atoi(values[i]);
807 	}
808 	ldap_value_free(values);
809     }
810 
811     ret = 0;
812 
813   out:
814     if (unparsed_name != NULL) {
815 	free(unparsed_name);
816     }
817 
818     if (ret != 0) {
819 	/* I don't think this frees ent itself. */
820 	hdb_free_entry(context, ent);
821     }
822 
823     return ret;
824 }
825 
826 static krb5_error_code LDAP_close(krb5_context context, HDB * db)
827 {
828     LDAP *ld = (LDAP *) db->db;
829 
830     ldap_unbind(ld);
831     db->db = NULL;
832     return 0;
833 }
834 
835 static krb5_error_code
836 LDAP_lock(krb5_context context, HDB * db, int operation)
837 {
838     return 0;
839 }
840 
841 static krb5_error_code LDAP_unlock(krb5_context context, HDB * db)
842 {
843     return 0;
844 }
845 
846 static krb5_error_code
847 LDAP_seq(krb5_context context, HDB * db, unsigned flags, hdb_entry * entry)
848 {
849     int msgid, rc, parserc;
850     krb5_error_code ret;
851     LDAPMessage *e;
852 
853     msgid = db->openp;		/* BOGUS OVERLOADING */
854     if (msgid < 0) {
855 	return HDB_ERR_NOENTRY;
856     }
857 
858     do {
859 	rc = ldap_result((LDAP *) db->db, msgid, LDAP_MSG_ONE, NULL, &e);
860 	switch (rc) {
861 	case LDAP_RES_SEARCH_ENTRY:
862 	    /* We have an entry. Parse it. */
863 	    ret = LDAP_message2entry(context, db, e, entry);
864 	    ldap_msgfree(e);
865 	    break;
866 	case LDAP_RES_SEARCH_RESULT:
867 	    /* We're probably at the end of the results. If not, abandon. */
868 	    parserc =
869 		ldap_parse_result((LDAP *) db->db, e, NULL, NULL, NULL,
870 				  NULL, NULL, 1);
871 	    if (parserc != LDAP_SUCCESS
872 		&& parserc != LDAP_MORE_RESULTS_TO_RETURN) {
873 		ldap_abandon((LDAP *) db->db, msgid);
874 	    }
875 	    ret = HDB_ERR_NOENTRY;
876 	    db->openp = -1;
877 	    break;
878 	case 0:
879 	case -1:
880 	default:
881 	    /* Some unspecified error (timeout?). Abandon. */
882 	    ldap_msgfree(e);
883 	    ldap_abandon((LDAP *) db->db, msgid);
884 	    ret = HDB_ERR_NOENTRY;
885 	    db->openp = -1;
886 	    break;
887 	}
888     } while (rc == LDAP_RES_SEARCH_REFERENCE);
889 
890     if (ret == 0) {
891 	if (db->master_key_set && (flags & HDB_F_DECRYPT)) {
892 	    ret = hdb_unseal_keys(context, db, entry);
893 	    if (ret)
894 		hdb_free_entry(context,entry);
895 	}
896     }
897 
898     return ret;
899 }
900 
901 static krb5_error_code
902 LDAP_firstkey(krb5_context context, HDB * db, unsigned flags,
903 	      hdb_entry * entry)
904 {
905     int msgid;
906 
907     (void) LDAP__connect(context, db);
908 
909     msgid = LDAP_NO_LIMIT;
910     (void) ldap_set_option((LDAP *) db->db, LDAP_OPT_SIZELIMIT, &msgid);
911 
912     msgid = ldap_search((LDAP *) db->db, db->name,
913 			LDAP_SCOPE_ONELEVEL, "(objectclass=krb5KDCEntry)",
914 			krb5kdcentry_attrs, 0);
915     if (msgid < 0) {
916 	return HDB_ERR_NOENTRY;
917     }
918 
919     db->openp = msgid;
920 
921     return LDAP_seq(context, db, flags, entry);
922 }
923 
924 static krb5_error_code
925 LDAP_nextkey(krb5_context context, HDB * db, unsigned flags,
926 	     hdb_entry * entry)
927 {
928     return LDAP_seq(context, db, flags, entry);
929 }
930 
931 static krb5_error_code
932 LDAP_rename(krb5_context context, HDB * db, const char *new_name)
933 {
934     return HDB_ERR_DB_INUSE;
935 }
936 
937 static krb5_boolean LDAP__is_user_namingcontext(const char *ctx,
938 						char *const *subschema)
939 {
940     char *const *p;
941 
942     if (!strcasecmp(ctx, "CN=MONITOR")
943 	|| !strcasecmp(ctx, "CN=CONFIG")) {
944 	return FALSE;
945     }
946 
947     if (subschema != NULL) {
948 	for (p = subschema; *p != NULL; p++) {
949 	    if (!strcasecmp(ctx, *p)) {
950 		return FALSE;
951 	    }
952 	}
953     }
954 
955     return TRUE;
956 }
957 
958 static krb5_error_code LDAP__connect(krb5_context context, HDB * db)
959 {
960     int rc;
961     krb5_error_code ret;
962     char *attrs[] = { "namingContexts", "subschemaSubentry", NULL };
963     LDAPMessage *res = NULL, *e;
964 
965     if (db->db != NULL) {
966 	/* connection has been opened. ping server. */
967 	struct sockaddr_un addr;
968 	socklen_t len;
969 	int sd;
970 
971 	if (ldap_get_option((LDAP *) db->db, LDAP_OPT_DESC, &sd) == 0 &&
972 	    getpeername(sd, (struct sockaddr *) &addr, &len) < 0) {
973 	    /* the other end has died. reopen. */
974 	    LDAP_close(context, db);
975 	}
976     }
977 
978     if (db->db != NULL) {
979 	/* server is UP */
980 	return 0;
981     }
982 
983     rc = ldap_initialize((LDAP **) & db->db, "ldapi:///");
984     if (rc != LDAP_SUCCESS) {
985 	return HDB_ERR_NOENTRY;
986     }
987 
988     rc = LDAP_VERSION3;
989     (void) ldap_set_option((LDAP *) db->db, LDAP_OPT_PROTOCOL_VERSION, &rc);
990 
991     /* XXX set db->name to the search base */
992     rc = ldap_search_s((LDAP *) db->db, "", LDAP_SCOPE_BASE,
993 		       "(objectclass=*)", attrs, 0, &res);
994     if (rc != LDAP_SUCCESS) {
995 	ret = HDB_ERR_BADVERSION;
996 	goto out;
997     }
998 
999     e = ldap_first_entry((LDAP *) db->db, res);
1000     if (e == NULL) {
1001 	ret = HDB_ERR_NOENTRY;
1002 	goto out;
1003     }
1004 
1005     if (db->name == NULL) {
1006 	char **contexts = NULL, **schema_contexts, **p;
1007 
1008 	contexts = ldap_get_values((LDAP *) db->db, e, "namingContexts");
1009 	if (contexts == NULL) {
1010 	    ret = HDB_ERR_NOENTRY;
1011 	    goto out;
1012 	}
1013 
1014 	schema_contexts =
1015 	    ldap_get_values((LDAP *) db->db, e, "subschemaSubentry");
1016 
1017 	if (db->name != NULL) {
1018 	    free(db->name);
1019 	    db->name = NULL;
1020 	}
1021 
1022 	for (p = contexts; *p != NULL; p++) {
1023 	    if (LDAP__is_user_namingcontext(*p, schema_contexts)) {
1024 		break;
1025 	    }
1026 	}
1027 
1028 	db->name = strdup(*p);
1029 	if (db->name == NULL) {
1030 	    ldap_value_free(contexts);
1031 	    ret = ENOMEM;
1032 	    goto out;
1033 	}
1034 
1035 	ldap_value_free(contexts);
1036 	if (schema_contexts != NULL) {
1037 	    ldap_value_free(schema_contexts);
1038 	}
1039     }
1040 
1041     ret = 0;
1042 
1043   out:
1044 
1045     if (res != NULL) {
1046 	ldap_msgfree(res);
1047     }
1048 
1049     if (ret != 0) {
1050 	if (db->db != NULL) {
1051 	    ldap_unbind((LDAP *) db->db);
1052 	    db->db = NULL;
1053 	}
1054     }
1055 
1056     return ret;
1057 }
1058 
1059 static krb5_error_code
1060 LDAP_open(krb5_context context, HDB * db, int flags, mode_t mode)
1061 {
1062     krb5_error_code ret;
1063 
1064     /* Not the right place for this. */
1065 #ifdef HAVE_SIGACTION
1066     {
1067 	struct sigaction sa;
1068 
1069 	sa.sa_flags = 0;
1070 	sa.sa_handler = SIG_IGN;
1071 	sigemptyset(&sa.sa_mask);
1072 
1073 	sigaction(SIGPIPE, &sa, NULL);
1074     }
1075 #else
1076     signal(SIGPIPE, SIG_IGN);
1077 #endif
1078 
1079     if (db->name != NULL) {
1080 	free(db->name);
1081 	db->name = NULL;
1082     }
1083 
1084     ret = LDAP__connect(context, db);
1085     if (ret != 0) {
1086 	return ret;
1087     }
1088 
1089     return ret;
1090 }
1091 
1092 static krb5_error_code
1093 LDAP_fetch(krb5_context context, HDB * db, unsigned flags,
1094 	   hdb_entry * entry)
1095 {
1096     LDAPMessage *msg, *e;
1097     krb5_error_code ret;
1098 
1099     ret = LDAP_principal2message(context, db, entry->principal, &msg);
1100     if (ret != 0) {
1101 	return ret;
1102     }
1103 
1104     e = ldap_first_entry((LDAP *) db->db, msg);
1105     if (e == NULL) {
1106 	ret = HDB_ERR_NOENTRY;
1107 	goto out;
1108     }
1109 
1110     ret = LDAP_message2entry(context, db, e, entry);
1111     if (ret == 0) {
1112 	if (db->master_key_set && (flags & HDB_F_DECRYPT)) {
1113 	    ret = hdb_unseal_keys(context, db, entry);
1114 	    if (ret)
1115 		hdb_free_entry(context,entry);
1116 	}
1117     }
1118 
1119   out:
1120     ldap_msgfree(msg);
1121 
1122     return ret;
1123 }
1124 
1125 static krb5_error_code
1126 LDAP_store(krb5_context context, HDB * db, unsigned flags,
1127 	   hdb_entry * entry)
1128 {
1129     LDAPMod **mods = NULL;
1130     krb5_error_code ret;
1131     LDAPMessage *msg = NULL, *e = NULL;
1132     char *dn = NULL, *name = NULL;
1133 
1134     ret = krb5_unparse_name(context, entry->principal, &name);
1135     if (ret != 0) {
1136 	goto out;
1137     }
1138 
1139     ret = LDAP__lookup_princ(context, db, name, &msg);
1140     if (ret == 0) {
1141 	e = ldap_first_entry((LDAP *) db->db, msg);
1142     }
1143 
1144     ret = hdb_seal_keys(context, db, entry);
1145     if (ret)
1146 	goto out;
1147 
1148     /* turn new entry into LDAPMod array */
1149     ret = LDAP_entry2mods(context, db, entry, e, &mods);
1150     if (ret != 0) {
1151 	goto out;
1152     }
1153 
1154     if (e == NULL) {
1155 	/* Doesn't exist yet. */
1156 	char *p;
1157 
1158 	e = NULL;
1159 
1160 	/* normalize the naming attribute */
1161 	for (p = name; *p != '\0'; p++) {
1162 	    *p = (char) tolower((int) *p);
1163 	}
1164 
1165 	/*
1166 	 * We could do getpwnam() on the local component of
1167 	 * the principal to find cn/sn but that's probably
1168 	 * bad thing to do from inside a KDC. Better leave
1169 	 * it to management tools.
1170 	 */
1171 	ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "cn", name);
1172 	if (ret < 0) {
1173 	    goto out;
1174 	}
1175 
1176 	ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "sn", name);
1177 	if (ret < 0) {
1178 	    goto out;
1179 	}
1180 
1181 	ret = asprintf(&dn, "cn=%s,%s", name, db->name);
1182 	if (ret < 0) {
1183 	    ret = ENOMEM;
1184 	    goto out;
1185 	}
1186     } else if (flags & HDB_F_REPLACE) {
1187 	/* Entry exists, and we're allowed to replace it. */
1188 	dn = ldap_get_dn((LDAP *) db->db, e);
1189     } else {
1190 	/* Entry exists, but we're not allowed to replace it. Bail. */
1191 	ret = HDB_ERR_EXISTS;
1192 	goto out;
1193     }
1194 
1195     /* write entry into directory */
1196     if (e == NULL) {
1197 	/* didn't exist before */
1198 	ret = ldap_add_s((LDAP *) db->db, dn, mods);
1199     } else {
1200 	/* already existed, send deltas only */
1201 	ret = ldap_modify_s((LDAP *) db->db, dn, mods);
1202     }
1203 
1204     if (ret == LDAP_SUCCESS) {
1205 	ret = 0;
1206     } else {
1207 	ret = HDB_ERR_CANT_LOCK_DB;
1208     }
1209 
1210   out:
1211     /* free stuff */
1212     if (dn != NULL) {
1213 	free(dn);
1214     }
1215 
1216     if (msg != NULL) {
1217 	ldap_msgfree(msg);
1218     }
1219 
1220     if (mods != NULL) {
1221 	ldap_mods_free(mods, 1);
1222     }
1223 
1224     if (name != NULL) {
1225 	free(name);
1226     }
1227 
1228     return ret;
1229 }
1230 
1231 static krb5_error_code
1232 LDAP_remove(krb5_context context, HDB * db, hdb_entry * entry)
1233 {
1234     krb5_error_code ret;
1235     LDAPMessage *msg, *e;
1236     char *dn = NULL;
1237 
1238     ret = LDAP_principal2message(context, db, entry->principal, &msg);
1239     if (ret != 0) {
1240 	goto out;
1241     }
1242 
1243     e = ldap_first_entry((LDAP *) db->db, msg);
1244     if (e == NULL) {
1245 	ret = HDB_ERR_NOENTRY;
1246 	goto out;
1247     }
1248 
1249     dn = ldap_get_dn((LDAP *) db->db, e);
1250     if (dn == NULL) {
1251 	ret = HDB_ERR_NOENTRY;
1252 	goto out;
1253     }
1254 
1255     ret = LDAP_NO_LIMIT;
1256     (void) ldap_set_option((LDAP *) db->db, LDAP_OPT_SIZELIMIT, &ret);
1257 
1258     ret = ldap_delete_s((LDAP *) db->db, dn);
1259     if (ret == LDAP_SUCCESS) {
1260 	ret = 0;
1261     } else {
1262 	ret = HDB_ERR_CANT_LOCK_DB;
1263     }
1264 
1265   out:
1266     if (dn != NULL) {
1267 	free(dn);
1268     }
1269 
1270     if (msg != NULL) {
1271 	ldap_msgfree(msg);
1272     }
1273 
1274     return ret;
1275 }
1276 
1277 static krb5_error_code
1278 LDAP__get(krb5_context context, HDB * db, krb5_data key, krb5_data * reply)
1279 {
1280     fprintf(stderr, "LDAP__get not implemented\n");
1281     abort();
1282     return 0;
1283 }
1284 
1285 static krb5_error_code
1286 LDAP__put(krb5_context context, HDB * db, int replace,
1287 	  krb5_data key, krb5_data value)
1288 {
1289     fprintf(stderr, "LDAP__put not implemented\n");
1290     abort();
1291     return 0;
1292 }
1293 
1294 static krb5_error_code
1295 LDAP__del(krb5_context context, HDB * db, krb5_data key)
1296 {
1297     fprintf(stderr, "LDAP__del not implemented\n");
1298     abort();
1299     return 0;
1300 }
1301 
1302 static krb5_error_code LDAP_destroy(krb5_context context, HDB * db)
1303 {
1304     krb5_error_code ret;
1305 
1306     ret = hdb_clear_master_key(context, db);
1307     free(db->name);
1308     free(db);
1309 
1310     return ret;
1311 }
1312 
1313 krb5_error_code
1314 hdb_ldap_create(krb5_context context, HDB ** db, const char *filename)
1315 {
1316     *db = malloc(sizeof(**db));
1317     if (*db == NULL)
1318 	return ENOMEM;
1319 
1320     (*db)->db = NULL;
1321 /*    (*db)->name = strdup(filename); */
1322     (*db)->name = NULL;
1323     (*db)->master_key_set = 0;
1324     (*db)->openp = 0;
1325     (*db)->open = LDAP_open;
1326     (*db)->close = LDAP_close;
1327     (*db)->fetch = LDAP_fetch;
1328     (*db)->store = LDAP_store;
1329     (*db)->remove = LDAP_remove;
1330     (*db)->firstkey = LDAP_firstkey;
1331     (*db)->nextkey = LDAP_nextkey;
1332     (*db)->lock = LDAP_lock;
1333     (*db)->unlock = LDAP_unlock;
1334     (*db)->rename = LDAP_rename;
1335     /* can we ditch these? */
1336     (*db)->_get = LDAP__get;
1337     (*db)->_put = LDAP__put;
1338     (*db)->_del = LDAP__del;
1339     (*db)->destroy = LDAP_destroy;
1340 
1341     return 0;
1342 }
1343 
1344 #endif				/* OPENLDAP */
1345