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