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