xref: /freebsd/crypto/heimdal/lib/hdb/hdb-ldap.c (revision 4b2eaea43fec8e8792be611dea204071a10b655a)
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 2002/09/04 18:42:22 joda 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 	    krb5_set_error_string(context, "asprintf: out of memory");
339 	    ret = ENOMEM;
340 	    goto out;
341 	}
342 	ret =
343 	    LDAP_addmod(&mods, LDAP_MOD_REPLACE, "krb5KeyVersionNumber",
344 			tmp);
345 	free(tmp);
346 	if (ret != 0) {
347 	    goto out;
348 	}
349     }
350 
351     if (ent->valid_start) {
352 	if (orig.valid_end == NULL
353 	    || (*(ent->valid_start) != *(orig.valid_start))) {
354 	    ret =
355 		LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,
356 					     "krb5ValidStart",
357 					     ent->valid_start);
358 	    if (ret != 0) {
359 		goto out;
360 	    }
361 	}
362     }
363 
364     if (ent->valid_end) {
365 	if (orig.valid_end == NULL
366 	    || (*(ent->valid_end) != *(orig.valid_end))) {
367 	    ret =
368 		LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,
369 					     "krb5ValidEnd",
370 					     ent->valid_end);
371 	    if (ret != 0) {
372 		goto out;
373 	    }
374 	}
375     }
376 
377     if (ent->pw_end) {
378 	if (orig.pw_end == NULL || (*(ent->pw_end) != *(orig.pw_end))) {
379 	    ret =
380 		LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,
381 					     "krb5PasswordEnd",
382 					     ent->pw_end);
383 	    if (ret != 0) {
384 		goto out;
385 	    }
386 	}
387     }
388 
389     if (ent->max_life) {
390 	if (orig.max_life == NULL
391 	    || (*(ent->max_life) != *(orig.max_life))) {
392 	    rc = asprintf(&tmp, "%d", *(ent->max_life));
393 	    if (rc < 0) {
394 		krb5_set_error_string(context, "asprintf: out of memory");
395 		ret = ENOMEM;
396 		goto out;
397 	    }
398 	    ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "krb5MaxLife", tmp);
399 	    free(tmp);
400 	    if (ret != 0) {
401 		goto out;
402 	    }
403 	}
404     }
405 
406     if (ent->max_renew) {
407 	if (orig.max_renew == NULL
408 	    || (*(ent->max_renew) != *(orig.max_renew))) {
409 	    rc = asprintf(&tmp, "%d", *(ent->max_renew));
410 	    if (rc < 0) {
411 		krb5_set_error_string(context, "asprintf: out of memory");
412 		ret = ENOMEM;
413 		goto out;
414 	    }
415 	    ret =
416 		LDAP_addmod(&mods, LDAP_MOD_REPLACE, "krb5MaxRenew", tmp);
417 	    free(tmp);
418 	    if (ret != 0) {
419 		goto out;
420 	    }
421 	}
422     }
423 
424     memset(&oflags, 0, sizeof(oflags));
425     memcpy(&oflags, &orig.flags, sizeof(HDBFlags));
426     memset(&nflags, 0, sizeof(nflags));
427     memcpy(&nflags, &ent->flags, sizeof(HDBFlags));
428 
429     if (memcmp(&oflags, &nflags, sizeof(HDBFlags))) {
430 	rc = asprintf(&tmp, "%lu", nflags);
431 	if (rc < 0) {
432 	    krb5_set_error_string(context, "asprintf: out of memory");
433 	    ret = ENOMEM;
434 	    goto out;
435 	}
436 	ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "krb5KDCFlags", tmp);
437 	free(tmp);
438 	if (ret != 0) {
439 	    goto out;
440 	}
441     }
442 
443     if (is_new_entry == FALSE && orig.keys.len > 0) {
444 	/* for the moment, clobber and replace keys. */
445 	ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5Key", NULL);
446 	if (ret != 0) {
447 	    goto out;
448 	}
449     }
450 
451     for (i = 0; i < ent->keys.len; i++) {
452 	unsigned char *buf;
453 	size_t len;
454 
455 	ASN1_MALLOC_ENCODE(Key, buf, len, &ent->keys.val[i], &len, ret);
456 	if (ret != 0)
457 	    goto out;
458 
459 	/* addmod_len _owns_ the key, doesn't need to copy it */
460 	ret = LDAP_addmod_len(&mods, LDAP_MOD_ADD, "krb5Key", buf, len);
461 	if (ret != 0) {
462 	    goto out;
463 	}
464     }
465 
466     if (ent->etypes) {
467 	/* clobber and replace encryption types. */
468 	if (is_new_entry == FALSE) {
469 	    ret =
470 		LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5EncryptionType",
471 			    NULL);
472 	}
473 	for (i = 0; i < ent->etypes->len; i++) {
474 	    rc = asprintf(&tmp, "%d", ent->etypes->val[i]);
475 	    if (rc < 0) {
476 		krb5_set_error_string(context, "asprintf: out of memory");
477 		ret = ENOMEM;
478 		goto out;
479 	    }
480 	    free(tmp);
481 	    ret =
482 		LDAP_addmod(&mods, LDAP_MOD_ADD, "krb5EncryptionType",
483 			    tmp);
484 	    if (ret != 0) {
485 		goto out;
486 	    }
487 	}
488     }
489 
490     /* for clarity */
491     ret = 0;
492 
493   out:
494 
495     if (ret == 0) {
496 	*pmods = mods;
497     } else if (mods != NULL) {
498 	ldap_mods_free(mods, 1);
499 	*pmods = NULL;
500     }
501 
502     if (msg != NULL) {
503 	hdb_free_entry(context, &orig);
504     }
505 
506     return ret;
507 }
508 
509 static krb5_error_code
510 LDAP_dn2principal(krb5_context context, HDB * db, const char *dn,
511 		  krb5_principal * principal)
512 {
513     krb5_error_code ret;
514     int rc, limit = 1;
515     char **values;
516     LDAPMessage *res = NULL, *e;
517 
518     rc = ldap_set_option((LDAP *) db->db, LDAP_OPT_SIZELIMIT, (const void *)&limit);
519     if (rc != LDAP_SUCCESS) {
520 	krb5_set_error_string(context, "ldap_set_option: %s", ldap_err2string(rc));
521 	ret = HDB_ERR_BADVERSION;
522 	goto out;
523      }
524 
525     rc = ldap_search_s((LDAP *) db->db, dn, LDAP_SCOPE_BASE,
526 		       "(objectclass=krb5Principal)", krb5principal_attrs,
527 		       0, &res);
528     if (rc != LDAP_SUCCESS) {
529 	krb5_set_error_string(context, "ldap_search_s: %s", ldap_err2string(rc));
530 	ret = HDB_ERR_NOENTRY;
531 	goto out;
532     }
533 
534     e = ldap_first_entry((LDAP *) db->db, res);
535     if (e == NULL) {
536 	ret = HDB_ERR_NOENTRY;
537 	goto out;
538     }
539 
540     values = ldap_get_values((LDAP *) db->db, e, "krb5PrincipalName");
541     if (values == NULL) {
542 	ret = HDB_ERR_NOENTRY;
543 	goto out;
544     }
545 
546     ret = krb5_parse_name(context, values[0], principal);
547     ldap_value_free(values);
548 
549   out:
550     if (res != NULL) {
551 	ldap_msgfree(res);
552     }
553     return ret;
554 }
555 
556 static krb5_error_code
557 LDAP__lookup_princ(krb5_context context, HDB * db, const char *princname,
558 		   LDAPMessage ** msg)
559 {
560     krb5_error_code ret;
561     int rc, limit = 1;
562     char *filter = NULL;
563 
564     (void) LDAP__connect(context, db);
565 
566     rc =
567 	asprintf(&filter,
568 		 "(&(objectclass=krb5KDCEntry)(krb5PrincipalName=%s))",
569 		 princname);
570     if (rc < 0) {
571 	krb5_set_error_string(context, "asprintf: out of memory");
572 	ret = ENOMEM;
573 	goto out;
574     }
575 
576     rc = ldap_set_option((LDAP *) db->db, LDAP_OPT_SIZELIMIT, (const void *)&limit);
577     if (rc != LDAP_SUCCESS) {
578 	krb5_set_error_string(context, "ldap_set_option: %s", ldap_err2string(rc));
579 	ret = HDB_ERR_BADVERSION;
580 	goto out;
581     }
582 
583     rc = ldap_search_s((LDAP *) db->db, db->name, LDAP_SCOPE_ONELEVEL, filter,
584 		       krb5kdcentry_attrs, 0, msg);
585     if (rc != LDAP_SUCCESS) {
586 	krb5_set_error_string(context, "ldap_search_s: %s", ldap_err2string(rc));
587 	ret = HDB_ERR_NOENTRY;
588 	goto out;
589     }
590 
591     ret = 0;
592 
593   out:
594     if (filter != NULL) {
595 	free(filter);
596     }
597     return ret;
598 }
599 
600 static krb5_error_code
601 LDAP_principal2message(krb5_context context, HDB * db,
602 		       krb5_principal princ, LDAPMessage ** msg)
603 {
604     char *princname = NULL;
605     krb5_error_code ret;
606 
607     ret = krb5_unparse_name(context, princ, &princname);
608     if (ret != 0) {
609 	return ret;
610     }
611 
612     ret = LDAP__lookup_princ(context, db, princname, msg);
613     free(princname);
614 
615     return ret;
616 }
617 
618 /*
619  * Construct an hdb_entry from a directory entry.
620  */
621 static krb5_error_code
622 LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,
623 		   hdb_entry * ent)
624 {
625     char *unparsed_name = NULL, *dn = NULL;
626     int ret;
627     unsigned long tmp;
628     struct berval **keys;
629     char **values;
630 
631     memset(ent, 0, sizeof(*ent));
632     memset(&ent->flags, 0, sizeof(HDBFlags));
633 
634     ret =
635 	LDAP_get_string_value(db, msg, "krb5PrincipalName",
636 			      &unparsed_name);
637     if (ret != 0) {
638 	return ret;
639     }
640 
641     ret = krb5_parse_name(context, unparsed_name, &ent->principal);
642     if (ret != 0) {
643 	goto out;
644     }
645 
646     ret =
647 	LDAP_get_integer_value(db, msg, "krb5KeyVersionNumber",
648 			       &ent->kvno);
649     if (ret != 0) {
650 	ent->kvno = 0;
651     }
652 
653     keys = ldap_get_values_len((LDAP *) db->db, msg, "krb5Key");
654     if (keys != NULL) {
655 	int i;
656 	size_t l;
657 
658 	ent->keys.len = ldap_count_values_len(keys);
659 	ent->keys.val = (Key *) calloc(ent->keys.len, sizeof(Key));
660 	if (ent->keys.val == NULL) {
661 	    krb5_set_error_string(context, "calloc: out of memory");
662 	    ret = ENOMEM;
663 	    goto out;
664 	}
665 	for (i = 0; i < ent->keys.len; i++) {
666 	    decode_Key((unsigned char *) keys[i]->bv_val,
667 		       (size_t) keys[i]->bv_len, &ent->keys.val[i], &l);
668 	}
669 	ber_bvecfree(keys);
670     } else {
671 #if 1
672 	/*
673 	 * This violates the ASN1 but it allows a principal to
674 	 * be related to a general directory entry without creating
675 	 * the keys. Hopefully it's OK.
676 	 */
677 	ent->keys.len = 0;
678 	ent->keys.val = NULL;
679 #else
680 	ret = HDB_ERR_NOENTRY;
681 	goto out;
682 #endif
683     }
684 
685     ret =
686 	LDAP_get_generalized_time_value(db, msg, "createTimestamp",
687 					&ent->created_by.time);
688     if (ret != 0) {
689 	ent->created_by.time = time(NULL);
690     }
691 
692     ent->created_by.principal = NULL;
693 
694     ret = LDAP_get_string_value(db, msg, "creatorsName", &dn);
695     if (ret == 0) {
696 	if (LDAP_dn2principal(context, db, dn, &ent->created_by.principal)
697 	    != 0) {
698 	    ent->created_by.principal = NULL;
699 	}
700 	free(dn);
701     }
702 
703     ent->modified_by = (Event *) malloc(sizeof(Event));
704     if (ent->modified_by == NULL) {
705 	krb5_set_error_string(context, "malloc: out of memory");
706 	ret = ENOMEM;
707 	goto out;
708     }
709     ret =
710 	LDAP_get_generalized_time_value(db, msg, "modifyTimestamp",
711 					&ent->modified_by->time);
712     if (ret == 0) {
713 	ret = LDAP_get_string_value(db, msg, "modifiersName", &dn);
714 	if (LDAP_dn2principal
715 	    (context, db, dn, &ent->modified_by->principal) != 0) {
716 	    ent->modified_by->principal = NULL;
717 	}
718 	free(dn);
719     } else {
720 	free(ent->modified_by);
721 	ent->modified_by = NULL;
722     }
723 
724     if ((ent->valid_start = (KerberosTime *) malloc(sizeof(KerberosTime)))
725 	== NULL) {
726 	krb5_set_error_string(context, "malloc: out of memory");
727 	ret = ENOMEM;
728 	goto out;
729     }
730     ret =
731 	LDAP_get_generalized_time_value(db, msg, "krb5ValidStart",
732 					ent->valid_start);
733     if (ret != 0) {
734 	/* OPTIONAL */
735 	free(ent->valid_start);
736 	ent->valid_start = NULL;
737     }
738 
739     if ((ent->valid_end = (KerberosTime *) malloc(sizeof(KerberosTime))) ==
740 	NULL) {
741 	krb5_set_error_string(context, "malloc: out of memory");
742 	ret = ENOMEM;
743 	goto out;
744     }
745     ret =
746 	LDAP_get_generalized_time_value(db, msg, "krb5ValidEnd",
747 					ent->valid_end);
748     if (ret != 0) {
749 	/* OPTIONAL */
750 	free(ent->valid_end);
751 	ent->valid_end = NULL;
752     }
753 
754     if ((ent->pw_end = (KerberosTime *) malloc(sizeof(KerberosTime))) ==
755 	NULL) {
756 	krb5_set_error_string(context, "malloc: out of memory");
757 	ret = ENOMEM;
758 	goto out;
759     }
760     ret =
761 	LDAP_get_generalized_time_value(db, msg, "krb5PasswordEnd",
762 					ent->pw_end);
763     if (ret != 0) {
764 	/* OPTIONAL */
765 	free(ent->pw_end);
766 	ent->pw_end = NULL;
767     }
768 
769     ent->max_life = (int *) malloc(sizeof(int));
770     if (ent->max_life == NULL) {
771 	krb5_set_error_string(context, "malloc: out of memory");
772 	ret = ENOMEM;
773 	goto out;
774     }
775     ret = LDAP_get_integer_value(db, msg, "krb5MaxLife", ent->max_life);
776     if (ret != 0) {
777 	free(ent->max_life);
778 	ent->max_life = NULL;
779     }
780 
781     ent->max_renew = (int *) malloc(sizeof(int));
782     if (ent->max_renew == NULL) {
783 	krb5_set_error_string(context, "malloc: out of memory");
784 	ret = ENOMEM;
785 	goto out;
786     }
787     ret = LDAP_get_integer_value(db, msg, "krb5MaxRenew", ent->max_renew);
788     if (ret != 0) {
789 	free(ent->max_renew);
790 	ent->max_renew = NULL;
791     }
792 
793     values = ldap_get_values((LDAP *) db->db, msg, "krb5KDCFlags");
794     if (values != NULL) {
795 	tmp = strtoul(values[0], (char **) NULL, 10);
796 	if (tmp == ULONG_MAX && errno == ERANGE) {
797 	    krb5_set_error_string(context, "strtoul: could not convert flag");
798 	    ret = ERANGE;
799 	    goto out;
800 	}
801     } else {
802 	tmp = 0;
803     }
804     memcpy(&ent->flags, &tmp, sizeof(HDBFlags));
805 
806     values = ldap_get_values((LDAP *) db->db, msg, "krb5EncryptionType");
807     if (values != NULL) {
808 	int i;
809 
810 	ent->etypes = malloc(sizeof(*(ent->etypes)));
811 	if (ent->etypes == NULL) {
812 	    krb5_set_error_string(context, "malloc: out of memory");
813 	    ret = ENOMEM;
814 	    goto out;
815 	}
816 	ent->etypes->len = ldap_count_values(values);
817 	ent->etypes->val = calloc(ent->etypes->len, sizeof(int));
818 	for (i = 0; i < ent->etypes->len; i++) {
819 	    ent->etypes->val[i] = atoi(values[i]);
820 	}
821 	ldap_value_free(values);
822     }
823 
824     ret = 0;
825 
826   out:
827     if (unparsed_name != NULL) {
828 	free(unparsed_name);
829     }
830 
831     if (ret != 0) {
832 	/* I don't think this frees ent itself. */
833 	hdb_free_entry(context, ent);
834     }
835 
836     return ret;
837 }
838 
839 static krb5_error_code LDAP_close(krb5_context context, HDB * db)
840 {
841     ldap_unbind_ext((LDAP *) db->db, NULL, NULL);
842     db->db = NULL;
843 
844     return 0;
845 }
846 
847 static krb5_error_code
848 LDAP_lock(krb5_context context, HDB * db, int operation)
849 {
850     return 0;
851 }
852 
853 static krb5_error_code LDAP_unlock(krb5_context context, HDB * db)
854 {
855     return 0;
856 }
857 
858 static krb5_error_code
859 LDAP_seq(krb5_context context, HDB * db, unsigned flags, hdb_entry * entry)
860 {
861     int msgid, rc, parserc;
862     krb5_error_code ret;
863     LDAPMessage *e;
864 
865     msgid = db->openp;		/* BOGUS OVERLOADING */
866     if (msgid < 0) {
867 	return HDB_ERR_NOENTRY;
868     }
869 
870     do {
871 	rc = ldap_result((LDAP *) db->db, msgid, LDAP_MSG_ONE, NULL, &e);
872 	switch (rc) {
873 	case LDAP_RES_SEARCH_ENTRY:
874 	    /* We have an entry. Parse it. */
875 	    ret = LDAP_message2entry(context, db, e, entry);
876 	    ldap_msgfree(e);
877 	    break;
878 	case LDAP_RES_SEARCH_RESULT:
879 	    /* We're probably at the end of the results. If not, abandon. */
880 	    parserc =
881 		ldap_parse_result((LDAP *) db->db, e, NULL, NULL, NULL,
882 				  NULL, NULL, 1);
883 	    if (parserc != LDAP_SUCCESS
884 		&& parserc != LDAP_MORE_RESULTS_TO_RETURN) {
885 	        krb5_set_error_string(context, "ldap_parse_result: %s", ldap_err2string(parserc));
886 		ldap_abandon((LDAP *) db->db, msgid);
887 	    }
888 	    ret = HDB_ERR_NOENTRY;
889 	    db->openp = -1;
890 	    break;
891 	case 0:
892 	case -1:
893 	default:
894 	    /* Some unspecified error (timeout?). Abandon. */
895 	    ldap_msgfree(e);
896 	    ldap_abandon((LDAP *) db->db, msgid);
897 	    ret = HDB_ERR_NOENTRY;
898 	    db->openp = -1;
899 	    break;
900 	}
901     } while (rc == LDAP_RES_SEARCH_REFERENCE);
902 
903     if (ret == 0) {
904 	if (db->master_key_set && (flags & HDB_F_DECRYPT)) {
905 	    ret = hdb_unseal_keys(context, db, entry);
906 	    if (ret)
907 		hdb_free_entry(context,entry);
908 	}
909     }
910 
911     return ret;
912 }
913 
914 static krb5_error_code
915 LDAP_firstkey(krb5_context context, HDB * db, unsigned flags,
916 	      hdb_entry * entry)
917 {
918     int msgid, limit = LDAP_NO_LIMIT, rc;
919 
920     (void) LDAP__connect(context, db);
921 
922     rc = ldap_set_option((LDAP *) db->db, LDAP_OPT_SIZELIMIT, (const void *)&limit);
923     if (rc != LDAP_SUCCESS) {
924 	krb5_set_error_string(context, "ldap_set_option: %s", ldap_err2string(rc));
925 	return HDB_ERR_BADVERSION;
926     }
927 
928     msgid = ldap_search((LDAP *) db->db, db->name,
929 			LDAP_SCOPE_ONELEVEL, "(objectclass=krb5KDCEntry)",
930 			krb5kdcentry_attrs, 0);
931     if (msgid < 0) {
932 	return HDB_ERR_NOENTRY;
933     }
934 
935     db->openp = msgid;
936 
937     return LDAP_seq(context, db, flags, entry);
938 }
939 
940 static krb5_error_code
941 LDAP_nextkey(krb5_context context, HDB * db, unsigned flags,
942 	     hdb_entry * entry)
943 {
944     return LDAP_seq(context, db, flags, entry);
945 }
946 
947 static krb5_error_code
948 LDAP_rename(krb5_context context, HDB * db, const char *new_name)
949 {
950     return HDB_ERR_DB_INUSE;
951 }
952 
953 static krb5_error_code LDAP__connect(krb5_context context, HDB * db)
954 {
955     int rc, version = LDAP_VERSION3;
956 
957     if (db->db != NULL) {
958 	/* connection has been opened. ping server. */
959 	struct sockaddr_un addr;
960 	socklen_t len;
961 	int sd;
962 
963 	if (ldap_get_option((LDAP *) db->db, LDAP_OPT_DESC, &sd) == 0 &&
964 	    getpeername(sd, (struct sockaddr *) &addr, &len) < 0) {
965 	    /* the other end has died. reopen. */
966 	    LDAP_close(context, db);
967 	}
968     }
969 
970     if (db->db != NULL) {
971 	/* server is UP */
972 	return 0;
973     }
974 
975     rc = ldap_initialize((LDAP **) & db->db, "ldapi:///");
976     if (rc != LDAP_SUCCESS) {
977 	krb5_set_error_string(context, "ldap_initialize: %s", ldap_err2string(rc));
978 	return HDB_ERR_NOENTRY;
979     }
980 
981     rc = ldap_set_option((LDAP *) db->db, LDAP_OPT_PROTOCOL_VERSION, (const void *)&version);
982     if (rc != LDAP_SUCCESS) {
983 	krb5_set_error_string(context, "ldap_set_option: %s", ldap_err2string(rc));
984 	ldap_unbind_ext((LDAP *) db->db, NULL, NULL);
985 	db->db = NULL;
986 	return HDB_ERR_BADVERSION;
987     }
988 
989     return 0;
990 }
991 
992 static krb5_error_code
993 LDAP_open(krb5_context context, HDB * db, int flags, mode_t mode)
994 {
995     /* Not the right place for this. */
996 #ifdef HAVE_SIGACTION
997     struct sigaction sa;
998 
999     sa.sa_flags = 0;
1000     sa.sa_handler = SIG_IGN;
1001     sigemptyset(&sa.sa_mask);
1002 
1003     sigaction(SIGPIPE, &sa, NULL);
1004 #else
1005     signal(SIGPIPE, SIG_IGN);
1006 #endif /* HAVE_SIGACTION */
1007 
1008     return LDAP__connect(context, db);
1009 }
1010 
1011 static krb5_error_code
1012 LDAP_fetch(krb5_context context, HDB * db, unsigned flags,
1013 	   hdb_entry * entry)
1014 {
1015     LDAPMessage *msg, *e;
1016     krb5_error_code ret;
1017 
1018     ret = LDAP_principal2message(context, db, entry->principal, &msg);
1019     if (ret != 0) {
1020 	return ret;
1021     }
1022 
1023     e = ldap_first_entry((LDAP *) db->db, msg);
1024     if (e == NULL) {
1025 	ret = HDB_ERR_NOENTRY;
1026 	goto out;
1027     }
1028 
1029     ret = LDAP_message2entry(context, db, e, entry);
1030     if (ret == 0) {
1031 	if (db->master_key_set && (flags & HDB_F_DECRYPT)) {
1032 	    ret = hdb_unseal_keys(context, db, entry);
1033 	    if (ret)
1034 		hdb_free_entry(context,entry);
1035 	}
1036     }
1037 
1038   out:
1039     ldap_msgfree(msg);
1040 
1041     return ret;
1042 }
1043 
1044 static krb5_error_code
1045 LDAP_store(krb5_context context, HDB * db, unsigned flags,
1046 	   hdb_entry * entry)
1047 {
1048     LDAPMod **mods = NULL;
1049     krb5_error_code ret;
1050     const char *errfn;
1051     int rc;
1052     LDAPMessage *msg = NULL, *e = NULL;
1053     char *dn = NULL, *name = NULL;
1054 
1055     ret = krb5_unparse_name(context, entry->principal, &name);
1056     if (ret != 0) {
1057 	goto out;
1058     }
1059 
1060     ret = LDAP__lookup_princ(context, db, name, &msg);
1061     if (ret == 0) {
1062 	e = ldap_first_entry((LDAP *) db->db, msg);
1063     }
1064 
1065     ret = hdb_seal_keys(context, db, entry);
1066     if (ret != 0) {
1067 	goto out;
1068     }
1069 
1070     /* turn new entry into LDAPMod array */
1071     ret = LDAP_entry2mods(context, db, entry, e, &mods);
1072     if (ret != 0) {
1073 	goto out;
1074     }
1075 
1076     if (e == NULL) {
1077 	/* Doesn't exist yet. */
1078 	char *p;
1079 
1080 	e = NULL;
1081 
1082 	/* normalize the naming attribute */
1083 	for (p = name; *p != '\0'; p++) {
1084 	    *p = (char) tolower((int) *p);
1085 	}
1086 
1087 	/*
1088 	 * We could do getpwnam() on the local component of
1089 	 * the principal to find cn/sn but that's probably
1090 	 * bad thing to do from inside a KDC. Better leave
1091 	 * it to management tools.
1092 	 */
1093 	ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "cn", name);
1094 	if (ret < 0) {
1095 	    goto out;
1096 	}
1097 
1098 	ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "sn", name);
1099 	if (ret < 0) {
1100 	    goto out;
1101 	}
1102 
1103 	if (db->name != NULL) {
1104 	    ret = asprintf(&dn, "cn=%s,%s", name, db->name);
1105 	} else {
1106 	    /* A bit bogus, but we don't have a search base */
1107 	    ret = asprintf(&dn, "cn=%s", name, db->name);
1108 	}
1109 	if (ret < 0) {
1110 	    krb5_set_error_string(context, "asprintf: out of memory");
1111 	    ret = ENOMEM;
1112 	    goto out;
1113 	}
1114     } else if (flags & HDB_F_REPLACE) {
1115 	/* Entry exists, and we're allowed to replace it. */
1116 	dn = ldap_get_dn((LDAP *) db->db, e);
1117     } else {
1118 	/* Entry exists, but we're not allowed to replace it. Bail. */
1119 	ret = HDB_ERR_EXISTS;
1120 	goto out;
1121     }
1122 
1123     /* write entry into directory */
1124     if (e == NULL) {
1125 	/* didn't exist before */
1126 	rc = ldap_add_s((LDAP *) db->db, dn, mods);
1127 	errfn = "ldap_add_s";
1128     } else {
1129 	/* already existed, send deltas only */
1130 	rc = ldap_modify_s((LDAP *) db->db, dn, mods);
1131 	errfn = "ldap_modify_s";
1132     }
1133 
1134     if (rc == LDAP_SUCCESS) {
1135 	ret = 0;
1136     } else {
1137 	krb5_set_error_string(context, "%s: %s", errfn, ldap_err2string(rc));
1138 	ret = HDB_ERR_CANT_LOCK_DB;
1139     }
1140 
1141   out:
1142     /* free stuff */
1143     if (dn != NULL) {
1144 	free(dn);
1145     }
1146 
1147     if (msg != NULL) {
1148 	ldap_msgfree(msg);
1149     }
1150 
1151     if (mods != NULL) {
1152 	ldap_mods_free(mods, 1);
1153     }
1154 
1155     if (name != NULL) {
1156 	free(name);
1157     }
1158 
1159     return ret;
1160 }
1161 
1162 static krb5_error_code
1163 LDAP_remove(krb5_context context, HDB * db, hdb_entry * entry)
1164 {
1165     krb5_error_code ret;
1166     LDAPMessage *msg, *e;
1167     char *dn = NULL;
1168     int rc, limit = LDAP_NO_LIMIT;
1169 
1170     ret = LDAP_principal2message(context, db, entry->principal, &msg);
1171     if (ret != 0) {
1172 	goto out;
1173     }
1174 
1175     e = ldap_first_entry((LDAP *) db->db, msg);
1176     if (e == NULL) {
1177 	ret = HDB_ERR_NOENTRY;
1178 	goto out;
1179     }
1180 
1181     dn = ldap_get_dn((LDAP *) db->db, e);
1182     if (dn == NULL) {
1183 	ret = HDB_ERR_NOENTRY;
1184 	goto out;
1185     }
1186 
1187     rc = ldap_set_option((LDAP *) db->db, LDAP_OPT_SIZELIMIT, (const void *)&limit);
1188     if (rc != LDAP_SUCCESS) {
1189 	krb5_set_error_string(context, "ldap_set_option: %s", ldap_err2string(rc));
1190 	ret = HDB_ERR_BADVERSION;
1191 	goto out;
1192     }
1193 
1194     rc = ldap_delete_s((LDAP *) db->db, dn);
1195     if (rc == LDAP_SUCCESS) {
1196 	ret = 0;
1197     } else {
1198 	krb5_set_error_string(context, "ldap_delete_s: %s", ldap_err2string(rc));
1199 	ret = HDB_ERR_CANT_LOCK_DB;
1200     }
1201 
1202   out:
1203     if (dn != NULL) {
1204 	free(dn);
1205     }
1206 
1207     if (msg != NULL) {
1208 	ldap_msgfree(msg);
1209     }
1210 
1211     return ret;
1212 }
1213 
1214 static krb5_error_code
1215 LDAP__get(krb5_context context, HDB * db, krb5_data key, krb5_data * reply)
1216 {
1217     fprintf(stderr, "LDAP__get not implemented\n");
1218     abort();
1219     return 0;
1220 }
1221 
1222 static krb5_error_code
1223 LDAP__put(krb5_context context, HDB * db, int replace,
1224 	  krb5_data key, krb5_data value)
1225 {
1226     fprintf(stderr, "LDAP__put not implemented\n");
1227     abort();
1228     return 0;
1229 }
1230 
1231 static krb5_error_code
1232 LDAP__del(krb5_context context, HDB * db, krb5_data key)
1233 {
1234     fprintf(stderr, "LDAP__del not implemented\n");
1235     abort();
1236     return 0;
1237 }
1238 
1239 static krb5_error_code LDAP_destroy(krb5_context context, HDB * db)
1240 {
1241     krb5_error_code ret;
1242 
1243     ret = hdb_clear_master_key(context, db);
1244     if (db->name != NULL) {
1245 	free(db->name);
1246     }
1247     free(db);
1248 
1249     return ret;
1250 }
1251 
1252 krb5_error_code
1253 hdb_ldap_create(krb5_context context, HDB ** db, const char *arg)
1254 {
1255     *db = malloc(sizeof(**db));
1256     if (*db == NULL) {
1257 	krb5_set_error_string(context, "malloc: out of memory");
1258 	return ENOMEM;
1259     }
1260 
1261     (*db)->db = NULL;
1262 
1263     if (arg == NULL || arg[0] == '\0') {
1264 	/*
1265 	 * if no argument specified in the configuration file
1266 	 * then use NULL, which tells OpenLDAP to look in
1267 	 * the ldap.conf file. This doesn't work for
1268 	 * writing entries because we don't know where to
1269 	 * put new principals.
1270 	 */
1271 	(*db)->name = NULL;
1272     } else {
1273 	(*db)->name = strdup(arg);
1274 	if ((*db)->name == NULL) {
1275 	    krb5_set_error_string(context, "strdup: out of memory");
1276 	    free(*db);
1277 	    *db = NULL;
1278 	    return ENOMEM;
1279 	}
1280     }
1281 
1282     (*db)->master_key_set = 0;
1283     (*db)->openp = 0;
1284     (*db)->open = LDAP_open;
1285     (*db)->close = LDAP_close;
1286     (*db)->fetch = LDAP_fetch;
1287     (*db)->store = LDAP_store;
1288     (*db)->remove = LDAP_remove;
1289     (*db)->firstkey = LDAP_firstkey;
1290     (*db)->nextkey = LDAP_nextkey;
1291     (*db)->lock = LDAP_lock;
1292     (*db)->unlock = LDAP_unlock;
1293     (*db)->rename = LDAP_rename;
1294     /* can we ditch these? */
1295     (*db)->_get = LDAP__get;
1296     (*db)->_put = LDAP__put;
1297     (*db)->_del = LDAP__del;
1298     (*db)->destroy = LDAP_destroy;
1299 
1300     return 0;
1301 }
1302 
1303 #endif				/* OPENLDAP */
1304