hdb: hdb_ldap_common NULL dereference
[heimdal.git] / lib / hdb / hdb-ldap.c
blob3add67bf37f73d15209f85e30f16ec9385687dd1
1 /*
2 * Copyright (c) 1999-2001, 2003, PADL Software Pty Ltd.
3 * Copyright (c) 2004, Andrew Bartlett.
4 * Copyright (c) 2003 - 2008, Kungliga Tekniska Högskolan.
5 * Copyright (c) 2015, Timothy Pearson.
6 * All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of PADL Software nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
36 #include "hdb_locl.h"
38 #ifdef OPENLDAP
40 #include <lber.h>
41 #include <ldap.h>
42 #include <sys/un.h>
43 #include <hex.h>
45 static krb5_error_code LDAP__connect(krb5_context context, HDB *);
46 static krb5_error_code LDAP_close(krb5_context context, HDB *);
48 static krb5_error_code
49 LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,
50 int flags, hdb_entry_ex * ent);
52 static const char *default_structural_object = "account";
53 static char *structural_object;
54 static const char *default_ldap_url = "ldapi:///";
55 static krb5_boolean samba_forwardable;
57 struct hdbldapdb {
58 LDAP *h_lp;
59 int h_msgid;
60 char *h_base;
61 char *h_url;
62 char *h_bind_dn;
63 char *h_bind_password;
64 krb5_boolean h_start_tls;
65 char *h_createbase;
68 #define HDB2LDAP(db) (((struct hdbldapdb *)(db)->hdb_db)->h_lp)
69 #define HDB2MSGID(db) (((struct hdbldapdb *)(db)->hdb_db)->h_msgid)
70 #define HDBSETMSGID(db,msgid) \
71 do { ((struct hdbldapdb *)(db)->hdb_db)->h_msgid = msgid; } while(0)
72 #define HDB2BASE(dn) (((struct hdbldapdb *)(db)->hdb_db)->h_base)
73 #define HDB2URL(dn) (((struct hdbldapdb *)(db)->hdb_db)->h_url)
74 #define HDB2BINDDN(db) (((struct hdbldapdb *)(db)->hdb_db)->h_bind_dn)
75 #define HDB2BINDPW(db) (((struct hdbldapdb *)(db)->hdb_db)->h_bind_password)
76 #define HDB2CREATE(db) (((struct hdbldapdb *)(db)->hdb_db)->h_createbase)
82 static char * krb5kdcentry_attrs[] = {
83 "cn",
84 "createTimestamp",
85 "creatorsName",
86 "krb5EncryptionType",
87 "krb5KDCFlags",
88 "krb5Key",
89 "krb5KeyVersionNumber",
90 "krb5MaxLife",
91 "krb5MaxRenew",
92 "krb5PasswordEnd",
93 "krb5PrincipalName",
94 "krb5PrincipalRealm",
95 "krb5ExtendedAttributes",
96 "krb5ValidEnd",
97 "krb5ValidStart",
98 "modifiersName",
99 "modifyTimestamp",
100 "objectClass",
101 "sambaAcctFlags",
102 "sambaKickoffTime",
103 "sambaNTPassword",
104 "sambaPwdLastSet",
105 "sambaPwdMustChange",
106 "uid",
107 NULL
110 static char *krb5principal_attrs[] = {
111 "cn",
112 "createTimestamp",
113 "creatorsName",
114 "krb5PrincipalName",
115 "krb5PrincipalRealm",
116 "modifiersName",
117 "modifyTimestamp",
118 "objectClass",
119 "uid",
120 NULL
123 static int
124 LDAP_no_size_limit(krb5_context context, LDAP *lp)
126 int ret, limit = LDAP_NO_LIMIT;
128 ret = ldap_set_option(lp, LDAP_OPT_SIZELIMIT, (const void *)&limit);
129 if (ret != LDAP_SUCCESS) {
130 krb5_set_error_message(context, HDB_ERR_BADVERSION,
131 "ldap_set_option: %s",
132 ldap_err2string(ret));
133 return HDB_ERR_BADVERSION;
135 return 0;
138 static int
139 check_ldap(krb5_context context, HDB *db, int ret)
141 switch (ret) {
142 case LDAP_SUCCESS:
143 return 0;
144 case LDAP_SERVER_DOWN:
145 LDAP_close(context, db);
146 return 1;
147 default:
148 return 1;
152 static krb5_error_code
153 LDAP__setmod(LDAPMod *** modlist, int modop, const char *attribute,
154 int *pIndex)
156 int cMods;
158 if (*modlist == NULL) {
159 *modlist = (LDAPMod **)ber_memcalloc(1, sizeof(LDAPMod *));
160 if (*modlist == NULL)
161 return ENOMEM;
164 for (cMods = 0; (*modlist)[cMods] != NULL; cMods++) {
165 if ((*modlist)[cMods]->mod_op == modop &&
166 strcasecmp((*modlist)[cMods]->mod_type, attribute) == 0) {
167 break;
171 *pIndex = cMods;
173 if ((*modlist)[cMods] == NULL) {
174 LDAPMod *mod;
176 *modlist = (LDAPMod **)ber_memrealloc(*modlist,
177 (cMods + 2) * sizeof(LDAPMod *));
178 if (*modlist == NULL)
179 return ENOMEM;
181 (*modlist)[cMods] = (LDAPMod *)ber_memalloc(sizeof(LDAPMod));
182 if ((*modlist)[cMods] == NULL)
183 return ENOMEM;
185 mod = (*modlist)[cMods];
186 mod->mod_op = modop;
187 mod->mod_type = ber_strdup(attribute);
188 if (mod->mod_type == NULL) {
189 ber_memfree(mod);
190 (*modlist)[cMods] = NULL;
191 return ENOMEM;
194 if (modop & LDAP_MOD_BVALUES) {
195 mod->mod_bvalues = NULL;
196 } else {
197 mod->mod_values = NULL;
200 (*modlist)[cMods + 1] = NULL;
203 return 0;
206 static krb5_error_code
207 LDAP_addmod_len(LDAPMod *** modlist, int modop, const char *attribute,
208 unsigned char *value, size_t len)
210 krb5_error_code ret;
211 int cMods, i = 0;
213 ret = LDAP__setmod(modlist, modop | LDAP_MOD_BVALUES, attribute, &cMods);
214 if (ret)
215 return ret;
217 if (value != NULL) {
218 struct berval **bv;
220 bv = (*modlist)[cMods]->mod_bvalues;
221 if (bv != NULL) {
222 for (i = 0; bv[i] != NULL; i++)
224 bv = ber_memrealloc(bv, (i + 2) * sizeof(*bv));
225 } else
226 bv = ber_memalloc(2 * sizeof(*bv));
227 if (bv == NULL)
228 return ENOMEM;
230 (*modlist)[cMods]->mod_bvalues = bv;
232 bv[i] = ber_memalloc(sizeof(**bv));;
233 if (bv[i] == NULL)
234 return ENOMEM;
236 bv[i]->bv_val = (void *)value;
237 bv[i]->bv_len = len;
239 bv[i + 1] = NULL;
242 return 0;
245 static krb5_error_code
246 LDAP_addmod(LDAPMod *** modlist, int modop, const char *attribute,
247 const char *value)
249 int cMods, i = 0;
250 krb5_error_code ret;
252 ret = LDAP__setmod(modlist, modop, attribute, &cMods);
253 if (ret)
254 return ret;
256 if (value != NULL) {
257 char **bv;
259 bv = (*modlist)[cMods]->mod_values;
260 if (bv != NULL) {
261 for (i = 0; bv[i] != NULL; i++)
263 bv = ber_memrealloc(bv, (i + 2) * sizeof(*bv));
264 } else
265 bv = ber_memalloc(2 * sizeof(*bv));
266 if (bv == NULL)
267 return ENOMEM;
269 (*modlist)[cMods]->mod_values = bv;
271 bv[i] = ber_strdup(value);
272 if (bv[i] == NULL)
273 return ENOMEM;
275 bv[i + 1] = NULL;
278 return 0;
281 static krb5_error_code
282 LDAP_addmod_generalized_time(LDAPMod *** mods, int modop,
283 const char *attribute, KerberosTime * time)
285 char buf[22];
286 struct tm *tm;
288 /* XXX not threadsafe */
289 tm = gmtime(time);
290 strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", tm);
292 return LDAP_addmod(mods, modop, attribute, buf);
295 static krb5_error_code
296 LDAP_addmod_integer(krb5_context context,
297 LDAPMod *** mods, int modop,
298 const char *attribute, unsigned long l)
300 krb5_error_code ret;
301 char *buf;
303 ret = asprintf(&buf, "%ld", l);
304 if (ret < 0) {
305 krb5_set_error_message(context, ENOMEM,
306 "asprintf: out of memory:");
307 return ENOMEM;
309 ret = LDAP_addmod(mods, modop, attribute, buf);
310 free (buf);
311 return ret;
314 static krb5_error_code
315 LDAP_get_string_value(HDB * db, LDAPMessage * entry,
316 const char *attribute, char **ptr)
318 struct berval **vals;
320 vals = ldap_get_values_len(HDB2LDAP(db), entry, attribute);
321 if (vals == NULL || vals[0] == NULL) {
322 *ptr = NULL;
323 return HDB_ERR_NOENTRY;
326 *ptr = malloc(vals[0]->bv_len + 1);
327 if (*ptr == NULL) {
328 ldap_value_free_len(vals);
329 return ENOMEM;
332 memcpy(*ptr, vals[0]->bv_val, vals[0]->bv_len);
333 (*ptr)[vals[0]->bv_len] = 0;
335 ldap_value_free_len(vals);
337 return 0;
340 static krb5_error_code
341 LDAP_get_integer_value(HDB * db, LDAPMessage * entry,
342 const char *attribute, int *ptr)
344 krb5_error_code ret;
345 char *val;
347 ret = LDAP_get_string_value(db, entry, attribute, &val);
348 if (ret)
349 return ret;
350 *ptr = atoi(val);
351 free(val);
352 return 0;
355 static krb5_error_code
356 LDAP_get_generalized_time_value(HDB * db, LDAPMessage * entry,
357 const char *attribute, KerberosTime * kt)
359 char *tmp, *gentime;
360 struct tm tm;
361 int ret;
363 *kt = 0;
365 ret = LDAP_get_string_value(db, entry, attribute, &gentime);
366 if (ret)
367 return ret;
369 tmp = strptime(gentime, "%Y%m%d%H%M%SZ", &tm);
370 if (tmp == NULL) {
371 free(gentime);
372 return HDB_ERR_NOENTRY;
375 free(gentime);
377 *kt = timegm(&tm);
379 return 0;
382 static int
383 bervalstrcmp(struct berval *v, const char *str)
385 size_t len = strlen(str);
386 return (v->bv_len == len) && strncasecmp(str, (char *)v->bv_val, len) == 0;
390 static krb5_error_code
391 LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent,
392 LDAPMessage * msg, LDAPMod *** pmods)
394 krb5_error_code ret;
395 krb5_boolean is_new_entry;
396 char *tmp = NULL;
397 LDAPMod **mods = NULL;
398 hdb_entry_ex orig;
399 unsigned long oflags, nflags;
400 int i;
402 krb5_boolean is_samba_account = FALSE;
403 krb5_boolean is_account = FALSE;
404 krb5_boolean is_heimdal_entry = FALSE;
405 krb5_boolean is_heimdal_principal = FALSE;
407 struct berval **vals;
409 *pmods = NULL;
411 if (msg != NULL) {
413 ret = LDAP_message2entry(context, db, msg, 0, &orig);
414 if (ret)
415 goto out;
417 is_new_entry = FALSE;
419 vals = ldap_get_values_len(HDB2LDAP(db), msg, "objectClass");
420 if (vals) {
421 int num_objectclasses = ldap_count_values_len(vals);
422 for (i=0; i < num_objectclasses; i++) {
423 if (bervalstrcmp(vals[i], "sambaSamAccount"))
424 is_samba_account = TRUE;
425 else if (bervalstrcmp(vals[i], structural_object))
426 is_account = TRUE;
427 else if (bervalstrcmp(vals[i], "krb5Principal"))
428 is_heimdal_principal = TRUE;
429 else if (bervalstrcmp(vals[i], "krb5KDCEntry"))
430 is_heimdal_entry = TRUE;
432 ldap_value_free_len(vals);
436 * If this is just a "account" entry and no other objectclass
437 * is hanging on this entry, it's really a new entry.
439 if (is_samba_account == FALSE && is_heimdal_principal == FALSE &&
440 is_heimdal_entry == FALSE) {
441 if (is_account == TRUE) {
442 is_new_entry = TRUE;
443 } else {
444 ret = HDB_ERR_NOENTRY;
445 goto out;
448 } else
449 is_new_entry = TRUE;
451 if (is_new_entry) {
453 /* to make it perfectly obvious we're depending on
454 * orig being intiialized to zero */
455 memset(&orig, 0, sizeof(orig));
457 ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "top");
458 if (ret)
459 goto out;
461 /* account is the structural object class */
462 if (is_account == FALSE) {
463 ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass",
464 structural_object);
465 is_account = TRUE;
466 if (ret)
467 goto out;
470 ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "krb5Principal");
471 is_heimdal_principal = TRUE;
472 if (ret)
473 goto out;
475 ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "krb5KDCEntry");
476 is_heimdal_entry = TRUE;
477 if (ret)
478 goto out;
481 if (is_new_entry ||
482 krb5_principal_compare(context, ent->entry.principal, orig.entry.principal)
483 == FALSE)
485 if (is_heimdal_principal || is_heimdal_entry) {
487 ret = krb5_unparse_name(context, ent->entry.principal, &tmp);
488 if (ret)
489 goto out;
491 ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE,
492 "krb5PrincipalName", tmp);
493 if (ret) {
494 free(tmp);
495 goto out;
497 free(tmp);
500 if (is_account || is_samba_account) {
501 ret = krb5_unparse_name_short(context, ent->entry.principal, &tmp);
502 if (ret)
503 goto out;
504 ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "uid", tmp);
505 if (ret) {
506 free(tmp);
507 goto out;
509 free(tmp);
513 if (is_heimdal_entry && (ent->entry.kvno != orig.entry.kvno || is_new_entry)) {
514 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
515 "krb5KeyVersionNumber",
516 ent->entry.kvno);
517 if (ret)
518 goto out;
521 if (is_heimdal_entry && ent->entry.extensions) {
522 if (!is_new_entry) {
523 vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5ExtendedAttributes");
524 if (vals) {
525 ldap_value_free_len(vals);
526 ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5ExtendedAttributes", NULL);
527 if (ret)
528 goto out;
532 for (i = 0; i < ent->entry.extensions->len; i++) {
533 unsigned char *buf;
534 size_t size, sz = 0;
536 ASN1_MALLOC_ENCODE(HDB_extension, buf, size, &ent->entry.extensions->val[i], &sz, ret);
537 if (ret)
538 goto out;
539 if (size != sz)
540 krb5_abortx(context, "internal error in ASN.1 encoder");
542 ret = LDAP_addmod_len(&mods, LDAP_MOD_ADD, "krb5ExtendedAttributes", buf, sz);
543 if (ret)
544 goto out;
548 if (is_heimdal_entry && ent->entry.valid_start) {
549 if (orig.entry.valid_end == NULL
550 || (*(ent->entry.valid_start) != *(orig.entry.valid_start))) {
551 ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,
552 "krb5ValidStart",
553 ent->entry.valid_start);
554 if (ret)
555 goto out;
559 if (ent->entry.valid_end) {
560 if (orig.entry.valid_end == NULL || (*(ent->entry.valid_end) != *(orig.entry.valid_end))) {
561 if (is_heimdal_entry) {
562 ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,
563 "krb5ValidEnd",
564 ent->entry.valid_end);
565 if (ret)
566 goto out;
568 if (is_samba_account) {
569 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
570 "sambaKickoffTime",
571 *(ent->entry.valid_end));
572 if (ret)
573 goto out;
578 if (ent->entry.pw_end) {
579 if (orig.entry.pw_end == NULL || (*(ent->entry.pw_end) != *(orig.entry.pw_end))) {
580 if (is_heimdal_entry) {
581 ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,
582 "krb5PasswordEnd",
583 ent->entry.pw_end);
584 if (ret)
585 goto out;
588 if (is_samba_account) {
589 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
590 "sambaPwdMustChange",
591 *(ent->entry.pw_end));
592 if (ret)
593 goto out;
599 #if 0 /* we we have last_pw_change */
600 if (is_samba_account && ent->entry.last_pw_change) {
601 if (orig.entry.last_pw_change == NULL || (*(ent->entry.last_pw_change) != *(orig.entry.last_pw_change))) {
602 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
603 "sambaPwdLastSet",
604 *(ent->entry.last_pw_change));
605 if (ret)
606 goto out;
609 #endif
611 if (is_heimdal_entry && ent->entry.max_life) {
612 if (orig.entry.max_life == NULL
613 || (*(ent->entry.max_life) != *(orig.entry.max_life))) {
615 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
616 "krb5MaxLife",
617 *(ent->entry.max_life));
618 if (ret)
619 goto out;
623 if (is_heimdal_entry && ent->entry.max_renew) {
624 if (orig.entry.max_renew == NULL
625 || (*(ent->entry.max_renew) != *(orig.entry.max_renew))) {
627 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
628 "krb5MaxRenew",
629 *(ent->entry.max_renew));
630 if (ret)
631 goto out;
635 oflags = HDBFlags2int(orig.entry.flags);
636 nflags = HDBFlags2int(ent->entry.flags);
638 if (is_heimdal_entry && oflags != nflags) {
640 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
641 "krb5KDCFlags",
642 nflags);
643 if (ret)
644 goto out;
647 /* Remove keys if they exists, and then replace keys. */
648 if (!is_new_entry && orig.entry.keys.len > 0) {
649 vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5Key");
650 if (vals) {
651 ldap_value_free_len(vals);
653 ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5Key", NULL);
654 if (ret)
655 goto out;
659 for (i = 0; i < ent->entry.keys.len; i++) {
661 if (is_samba_account
662 && ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) {
663 char *ntHexPassword;
664 char *nt;
665 time_t now = time(NULL);
667 /* the key might have been 'sealed', but samba passwords
668 are clear in the directory */
669 ret = hdb_unseal_key(context, db, &ent->entry.keys.val[i]);
670 if (ret)
671 goto out;
673 nt = ent->entry.keys.val[i].key.keyvalue.data;
674 /* store in ntPassword, not krb5key */
675 ret = hex_encode(nt, 16, &ntHexPassword);
676 if (ret < 0) {
677 ret = ENOMEM;
678 krb5_set_error_message(context, ret, "hdb-ldap: failed to "
679 "hex encode key");
680 goto out;
682 ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "sambaNTPassword",
683 ntHexPassword);
684 free(ntHexPassword);
685 if (ret)
686 goto out;
687 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
688 "sambaPwdLastSet", now);
689 if (ret)
690 goto out;
692 /* have to kill the LM passwod if it exists */
693 vals = ldap_get_values_len(HDB2LDAP(db), msg, "sambaLMPassword");
694 if (vals) {
695 ldap_value_free_len(vals);
696 ret = LDAP_addmod(&mods, LDAP_MOD_DELETE,
697 "sambaLMPassword", NULL);
698 if (ret)
699 goto out;
702 } else if (is_heimdal_entry) {
703 unsigned char *buf;
704 size_t len, buf_size;
706 ASN1_MALLOC_ENCODE(Key, buf, buf_size, &ent->entry.keys.val[i], &len, ret);
707 if (ret)
708 goto out;
709 if(buf_size != len)
710 krb5_abortx(context, "internal error in ASN.1 encoder");
712 /* addmod_len _owns_ the key, doesn't need to copy it */
713 ret = LDAP_addmod_len(&mods, LDAP_MOD_ADD, "krb5Key", buf, len);
714 if (ret)
715 goto out;
719 if (ent->entry.etypes) {
720 int add_krb5EncryptionType = 0;
723 * Only add/modify krb5EncryptionType if it's a new heimdal
724 * entry or krb5EncryptionType already exists on the entry.
727 if (!is_new_entry) {
728 vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5EncryptionType");
729 if (vals) {
730 ldap_value_free_len(vals);
731 ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5EncryptionType",
732 NULL);
733 if (ret)
734 goto out;
735 add_krb5EncryptionType = 1;
737 } else if (is_heimdal_entry)
738 add_krb5EncryptionType = 1;
740 if (add_krb5EncryptionType) {
741 for (i = 0; i < ent->entry.etypes->len; i++) {
742 if (is_samba_account &&
743 ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5)
746 } else if (is_heimdal_entry) {
747 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_ADD,
748 "krb5EncryptionType",
749 ent->entry.etypes->val[i]);
750 if (ret)
751 goto out;
757 /* for clarity */
758 ret = 0;
760 out:
762 if (ret == 0)
763 *pmods = mods;
764 else if (mods != NULL) {
765 ldap_mods_free(mods, 1);
766 *pmods = NULL;
769 if (msg)
770 hdb_free_entry(context, &orig);
772 return ret;
775 static krb5_error_code
776 LDAP_dn2principal(krb5_context context, HDB * db, const char *dn,
777 krb5_principal * principal)
779 krb5_error_code ret;
780 int rc;
781 const char *filter = "(objectClass=krb5Principal)";
782 LDAPMessage *res = NULL, *e;
783 char *p;
785 ret = LDAP_no_size_limit(context, HDB2LDAP(db));
786 if (ret)
787 goto out;
789 rc = ldap_search_ext_s(HDB2LDAP(db), dn, LDAP_SCOPE_SUBTREE,
790 filter, krb5principal_attrs, 0,
791 NULL, NULL, NULL,
792 0, &res);
793 if (check_ldap(context, db, rc)) {
794 ret = HDB_ERR_NOENTRY;
795 krb5_set_error_message(context, ret, "ldap_search_ext_s: "
796 "filter: %s error: %s",
797 filter, ldap_err2string(rc));
798 goto out;
801 e = ldap_first_entry(HDB2LDAP(db), res);
802 if (e == NULL) {
803 ret = HDB_ERR_NOENTRY;
804 goto out;
807 ret = LDAP_get_string_value(db, e, "krb5PrincipalName", &p);
808 if (ret) {
809 ret = HDB_ERR_NOENTRY;
810 goto out;
813 ret = krb5_parse_name(context, p, principal);
814 free(p);
816 out:
817 if (res)
818 ldap_msgfree(res);
820 return ret;
823 static int
824 need_quote(unsigned char c)
826 return (c & 0x80) ||
827 (c < 32) ||
828 (c == '(') ||
829 (c == ')') ||
830 (c == '*') ||
831 (c == '\\') ||
832 (c == 0x7f);
835 static const char hexchar[] = "0123456789ABCDEF";
837 static krb5_error_code
838 escape_value(krb5_context context, const char *unquoted, char **quoted)
840 size_t i, len;
842 for (i = 0, len = 0; unquoted[i] != '\0'; i++, len++) {
843 if (need_quote((unsigned char)unquoted[i]))
844 len += 2;
847 *quoted = malloc(len + 1);
848 if (*quoted == NULL) {
849 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
850 return ENOMEM;
853 for (i = 0; unquoted[0] ; unquoted++) {
854 if (need_quote((unsigned char)unquoted[0])) {
855 (*quoted)[i++] = '\\';
856 (*quoted)[i++] = hexchar[(unquoted[0] >> 4) & 0xf];
857 (*quoted)[i++] = hexchar[(unquoted[0] ) & 0xf];
858 } else
859 (*quoted)[i++] = (char)unquoted[0];
861 (*quoted)[i] = '\0';
862 return 0;
866 static krb5_error_code
867 LDAP__lookup_princ(krb5_context context,
868 HDB *db,
869 const char *princname,
870 const char *userid,
871 LDAPMessage **msg)
873 krb5_error_code ret;
874 int rc;
875 char *quote, *filter = NULL;
877 ret = LDAP__connect(context, db);
878 if (ret)
879 return ret;
882 * Quote searches that contain filter language, this quote
883 * searches for *@REALM, which takes very long time.
886 ret = escape_value(context, princname, &quote);
887 if (ret)
888 goto out;
890 rc = asprintf(&filter,
891 "(&(objectClass=krb5Principal)(krb5PrincipalName=%s))",
892 quote);
893 free(quote);
895 if (rc < 0) {
896 ret = ENOMEM;
897 krb5_set_error_message(context, ret, "malloc: out of memory");
898 goto out;
901 ret = LDAP_no_size_limit(context, HDB2LDAP(db));
902 if (ret)
903 goto out;
905 rc = ldap_search_ext_s(HDB2LDAP(db), HDB2BASE(db),
906 LDAP_SCOPE_SUBTREE, filter,
907 krb5kdcentry_attrs, 0,
908 NULL, NULL, NULL,
909 0, msg);
910 if (check_ldap(context, db, rc)) {
911 ret = HDB_ERR_NOENTRY;
912 krb5_set_error_message(context, ret, "ldap_search_ext_s: "
913 "filter: %s - error: %s",
914 filter, ldap_err2string(rc));
915 goto out;
918 if (userid && ldap_count_entries(HDB2LDAP(db), *msg) == 0) {
919 free(filter);
920 filter = NULL;
921 ldap_msgfree(*msg);
922 *msg = NULL;
924 ret = escape_value(context, userid, &quote);
925 if (ret)
926 goto out;
928 rc = asprintf(&filter,
929 "(&(|(objectClass=sambaSamAccount)(objectClass=%s))(uid=%s))",
930 structural_object, quote);
931 free(quote);
932 if (rc < 0) {
933 ret = ENOMEM;
934 krb5_set_error_message(context, ret, "asprintf: out of memory");
935 goto out;
938 ret = LDAP_no_size_limit(context, HDB2LDAP(db));
939 if (ret)
940 goto out;
942 rc = ldap_search_ext_s(HDB2LDAP(db), HDB2BASE(db), LDAP_SCOPE_SUBTREE,
943 filter, krb5kdcentry_attrs, 0,
944 NULL, NULL, NULL,
945 0, msg);
946 if (check_ldap(context, db, rc)) {
947 ret = HDB_ERR_NOENTRY;
948 krb5_set_error_message(context, ret,
949 "ldap_search_ext_s: filter: %s error: %s",
950 filter, ldap_err2string(rc));
951 goto out;
955 ret = 0;
957 out:
958 if (filter)
959 free(filter);
961 return ret;
964 static krb5_error_code
965 LDAP_principal2message(krb5_context context, HDB * db,
966 krb5_const_principal princ, LDAPMessage ** msg)
968 char *name, *name_short = NULL;
969 krb5_error_code ret;
970 krb5_realm *r, *r0;
972 *msg = NULL;
974 ret = krb5_unparse_name(context, princ, &name);
975 if (ret)
976 return ret;
978 ret = krb5_get_default_realms(context, &r0);
979 if(ret) {
980 free(name);
981 return ret;
983 for (r = r0; *r != NULL; r++) {
984 if(strcmp(krb5_principal_get_realm(context, princ), *r) == 0) {
985 ret = krb5_unparse_name_short(context, princ, &name_short);
986 if (ret) {
987 krb5_free_host_realm(context, r0);
988 free(name);
989 return ret;
991 break;
994 krb5_free_host_realm(context, r0);
996 ret = LDAP__lookup_princ(context, db, name, name_short, msg);
997 free(name);
998 free(name_short);
1000 return ret;
1004 * Construct an hdb_entry from a directory entry.
1006 static krb5_error_code
1007 LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,
1008 int flags, hdb_entry_ex * ent)
1010 char *unparsed_name = NULL, *dn = NULL, *ntPasswordIN = NULL;
1011 char *samba_acct_flags = NULL;
1012 struct berval **keys;
1013 struct berval **extensions;
1014 struct berval **vals;
1015 int tmp, tmp_time, i, ret, have_arcfour = 0;
1017 memset(ent, 0, sizeof(*ent));
1018 ent->entry.flags = int2HDBFlags(0);
1020 ret = LDAP_get_string_value(db, msg, "krb5PrincipalName", &unparsed_name);
1021 if (ret == 0) {
1022 ret = krb5_parse_name(context, unparsed_name, &ent->entry.principal);
1023 if (ret)
1024 goto out;
1025 } else {
1026 ret = LDAP_get_string_value(db, msg, "uid",
1027 &unparsed_name);
1028 if (ret == 0) {
1029 ret = krb5_parse_name(context, unparsed_name, &ent->entry.principal);
1030 if (ret)
1031 goto out;
1032 } else {
1033 krb5_set_error_message(context, HDB_ERR_NOENTRY,
1034 "hdb-ldap: ldap entry missing"
1035 "principal name");
1036 return HDB_ERR_NOENTRY;
1041 int integer;
1042 ret = LDAP_get_integer_value(db, msg, "krb5KeyVersionNumber",
1043 &integer);
1044 if (ret)
1045 ent->entry.kvno = 0;
1046 else
1047 ent->entry.kvno = integer;
1050 keys = ldap_get_values_len(HDB2LDAP(db), msg, "krb5Key");
1051 if (keys != NULL) {
1052 size_t l;
1054 ent->entry.keys.len = ldap_count_values_len(keys);
1055 ent->entry.keys.val = (Key *) calloc(ent->entry.keys.len, sizeof(Key));
1056 if (ent->entry.keys.val == NULL) {
1057 ret = ENOMEM;
1058 krb5_set_error_message(context, ret, "calloc: out of memory");
1059 goto out;
1061 for (i = 0; i < ent->entry.keys.len; i++) {
1062 decode_Key((unsigned char *) keys[i]->bv_val,
1063 (size_t) keys[i]->bv_len, &ent->entry.keys.val[i], &l);
1065 ber_bvecfree(keys);
1066 } else {
1067 #if 1
1069 * This violates the ASN1 but it allows a principal to
1070 * be related to a general directory entry without creating
1071 * the keys. Hopefully it's OK.
1073 ent->entry.keys.len = 0;
1074 ent->entry.keys.val = NULL;
1075 #else
1076 ret = HDB_ERR_NOENTRY;
1077 goto out;
1078 #endif
1081 extensions = ldap_get_values_len(HDB2LDAP(db), msg, "krb5ExtendedAttributes");
1082 if (extensions != NULL) {
1083 size_t l;
1085 ent->entry.extensions = calloc(1, sizeof(*(ent->entry.extensions)));
1086 if (ent->entry.extensions == NULL) {
1087 ret = krb5_enomem(context);
1088 goto out;
1090 ent->entry.extensions->len = ldap_count_values_len(extensions);
1091 ent->entry.extensions->val = (HDB_extension *) calloc(ent->entry.extensions->len, sizeof(HDB_extension));
1092 if (ent->entry.extensions->val == NULL) {
1093 ent->entry.extensions->len = 0;
1094 ret = krb5_enomem(context);
1095 goto out;
1097 for (i = 0; i < ent->entry.extensions->len; i++) {
1098 ret = decode_HDB_extension((unsigned char *) extensions[i]->bv_val,
1099 (size_t) extensions[i]->bv_len, &ent->entry.extensions->val[i], &l);
1100 if (ret)
1101 krb5_set_error_message(context, ret, "decode_HDB_extension failed");
1103 ber_bvecfree(extensions);
1104 } else {
1105 ent->entry.extensions = NULL;
1108 vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5EncryptionType");
1109 if (vals != NULL) {
1110 ent->entry.etypes = malloc(sizeof(*(ent->entry.etypes)));
1111 if (ent->entry.etypes == NULL) {
1112 ret = ENOMEM;
1113 krb5_set_error_message(context, ret,"malloc: out of memory");
1114 goto out;
1116 ent->entry.etypes->len = ldap_count_values_len(vals);
1117 ent->entry.etypes->val = calloc(ent->entry.etypes->len, sizeof(int));
1118 if (ent->entry.etypes->val == NULL) {
1119 ret = ENOMEM;
1120 krb5_set_error_message(context, ret, "malloc: out of memory");
1121 ent->entry.etypes->len = 0;
1122 goto out;
1124 for (i = 0; i < ent->entry.etypes->len; i++) {
1125 char *buf;
1127 buf = malloc(vals[i]->bv_len + 1);
1128 if (buf == NULL) {
1129 ret = ENOMEM;
1130 krb5_set_error_message(context, ret, "malloc: out of memory");
1131 goto out;
1133 memcpy(buf, vals[i]->bv_val, vals[i]->bv_len);
1134 buf[vals[i]->bv_len] = '\0';
1135 ent->entry.etypes->val[i] = atoi(buf);
1136 free(buf);
1138 ldap_value_free_len(vals);
1141 for (i = 0; i < ent->entry.keys.len; i++) {
1142 if (ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) {
1143 have_arcfour = 1;
1144 break;
1148 /* manually construct the NT (type 23) key */
1149 ret = LDAP_get_string_value(db, msg, "sambaNTPassword", &ntPasswordIN);
1150 if (ret == 0 && have_arcfour == 0) {
1151 unsigned *etypes;
1152 Key *ks;
1154 ks = realloc(ent->entry.keys.val,
1155 (ent->entry.keys.len + 1) *
1156 sizeof(ent->entry.keys.val[0]));
1157 if (ks == NULL) {
1158 ret = ENOMEM;
1159 krb5_set_error_message(context, ret, "malloc: out of memory");
1160 goto out;
1162 ent->entry.keys.val = ks;
1163 memset(&ent->entry.keys.val[ent->entry.keys.len], 0, sizeof(Key));
1164 ent->entry.keys.val[ent->entry.keys.len].key.keytype = ETYPE_ARCFOUR_HMAC_MD5;
1165 ret = krb5_data_alloc (&ent->entry.keys.val[ent->entry.keys.len].key.keyvalue, 16);
1166 if (ret) {
1167 krb5_set_error_message(context, ret, "malloc: out of memory");
1168 ret = ENOMEM;
1169 goto out;
1171 ret = hex_decode(ntPasswordIN,
1172 ent->entry.keys.val[ent->entry.keys.len].key.keyvalue.data, 16);
1173 ent->entry.keys.len++;
1175 if (ent->entry.etypes == NULL) {
1176 ent->entry.etypes = malloc(sizeof(*(ent->entry.etypes)));
1177 if (ent->entry.etypes == NULL) {
1178 ret = ENOMEM;
1179 krb5_set_error_message(context, ret, "malloc: out of memory");
1180 goto out;
1182 ent->entry.etypes->val = NULL;
1183 ent->entry.etypes->len = 0;
1186 for (i = 0; i < ent->entry.etypes->len; i++)
1187 if (ent->entry.etypes->val[i] == ETYPE_ARCFOUR_HMAC_MD5)
1188 break;
1189 /* If there is no ARCFOUR enctype, add one */
1190 if (i == ent->entry.etypes->len) {
1191 etypes = realloc(ent->entry.etypes->val,
1192 (ent->entry.etypes->len + 1) *
1193 sizeof(ent->entry.etypes->val[0]));
1194 if (etypes == NULL) {
1195 ret = ENOMEM;
1196 krb5_set_error_message(context, ret, "malloc: out of memory");
1197 goto out;
1199 ent->entry.etypes->val = etypes;
1200 ent->entry.etypes->val[ent->entry.etypes->len] =
1201 ETYPE_ARCFOUR_HMAC_MD5;
1202 ent->entry.etypes->len++;
1206 ret = LDAP_get_generalized_time_value(db, msg, "createTimestamp",
1207 &ent->entry.created_by.time);
1208 if (ret)
1209 ent->entry.created_by.time = time(NULL);
1211 ent->entry.created_by.principal = NULL;
1213 if (flags & HDB_F_ADMIN_DATA) {
1214 ret = LDAP_get_string_value(db, msg, "creatorsName", &dn);
1215 if (ret == 0) {
1216 LDAP_dn2principal(context, db, dn, &ent->entry.created_by.principal);
1217 free(dn);
1220 ent->entry.modified_by = calloc(1, sizeof(*ent->entry.modified_by));
1221 if (ent->entry.modified_by == NULL) {
1222 ret = ENOMEM;
1223 krb5_set_error_message(context, ret, "malloc: out of memory");
1224 goto out;
1227 ret = LDAP_get_generalized_time_value(db, msg, "modifyTimestamp",
1228 &ent->entry.modified_by->time);
1229 if (ret == 0) {
1230 ret = LDAP_get_string_value(db, msg, "modifiersName", &dn);
1231 if (ret == 0) {
1232 LDAP_dn2principal(context, db, dn, &ent->entry.modified_by->principal);
1233 free(dn);
1234 } else {
1235 free(ent->entry.modified_by);
1236 ent->entry.modified_by = NULL;
1241 ent->entry.valid_start = malloc(sizeof(*ent->entry.valid_start));
1242 if (ent->entry.valid_start == NULL) {
1243 ret = ENOMEM;
1244 krb5_set_error_message(context, ret, "malloc: out of memory");
1245 goto out;
1247 ret = LDAP_get_generalized_time_value(db, msg, "krb5ValidStart",
1248 ent->entry.valid_start);
1249 if (ret) {
1250 /* OPTIONAL */
1251 free(ent->entry.valid_start);
1252 ent->entry.valid_start = NULL;
1255 ent->entry.valid_end = malloc(sizeof(*ent->entry.valid_end));
1256 if (ent->entry.valid_end == NULL) {
1257 ret = ENOMEM;
1258 krb5_set_error_message(context, ret, "malloc: out of memory");
1259 goto out;
1261 ret = LDAP_get_generalized_time_value(db, msg, "krb5ValidEnd",
1262 ent->entry.valid_end);
1263 if (ret) {
1264 /* OPTIONAL */
1265 free(ent->entry.valid_end);
1266 ent->entry.valid_end = NULL;
1269 ret = LDAP_get_integer_value(db, msg, "sambaKickoffTime", &tmp_time);
1270 if (ret == 0) {
1271 if (ent->entry.valid_end == NULL) {
1272 ent->entry.valid_end = malloc(sizeof(*ent->entry.valid_end));
1273 if (ent->entry.valid_end == NULL) {
1274 ret = ENOMEM;
1275 krb5_set_error_message(context, ret, "malloc: out of memory");
1276 goto out;
1279 *ent->entry.valid_end = tmp_time;
1282 ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end));
1283 if (ent->entry.pw_end == NULL) {
1284 ret = ENOMEM;
1285 krb5_set_error_message(context, ret, "malloc: out of memory");
1286 goto out;
1288 ret = LDAP_get_generalized_time_value(db, msg, "krb5PasswordEnd",
1289 ent->entry.pw_end);
1290 if (ret) {
1291 /* OPTIONAL */
1292 free(ent->entry.pw_end);
1293 ent->entry.pw_end = NULL;
1296 ret = LDAP_get_integer_value(db, msg, "sambaPwdLastSet", &tmp_time);
1297 if (ret == 0) {
1298 time_t delta;
1300 delta = krb5_config_get_time_default(context, NULL,
1302 "kadmin",
1303 "password_lifetime",
1304 NULL);
1306 if (delta) {
1307 if (ent->entry.pw_end == NULL) {
1308 ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end));
1309 if (ent->entry.pw_end == NULL) {
1310 ret = ENOMEM;
1311 krb5_set_error_message(context, ret, "malloc: out of memory");
1312 goto out;
1316 *ent->entry.pw_end = tmp_time + delta;
1320 ret = LDAP_get_integer_value(db, msg, "sambaPwdMustChange", &tmp_time);
1321 if (ret == 0) {
1322 if (ent->entry.pw_end == NULL) {
1323 ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end));
1324 if (ent->entry.pw_end == NULL) {
1325 ret = ENOMEM;
1326 krb5_set_error_message(context, ret, "malloc: out of memory");
1327 goto out;
1330 *ent->entry.pw_end = tmp_time;
1333 /* OPTIONAL */
1334 ret = LDAP_get_integer_value(db, msg, "sambaPwdLastSet", &tmp_time);
1335 if (ret == 0)
1336 hdb_entry_set_pw_change_time(context, &ent->entry, tmp_time);
1339 int max_life;
1341 ent->entry.max_life = malloc(sizeof(*ent->entry.max_life));
1342 if (ent->entry.max_life == NULL) {
1343 ret = ENOMEM;
1344 krb5_set_error_message(context, ret, "malloc: out of memory");
1345 goto out;
1347 ret = LDAP_get_integer_value(db, msg, "krb5MaxLife", &max_life);
1348 if (ret) {
1349 free(ent->entry.max_life);
1350 ent->entry.max_life = NULL;
1351 } else
1352 *ent->entry.max_life = max_life;
1356 int max_renew;
1358 ent->entry.max_renew = malloc(sizeof(*ent->entry.max_renew));
1359 if (ent->entry.max_renew == NULL) {
1360 ret = ENOMEM;
1361 krb5_set_error_message(context, ret, "malloc: out of memory");
1362 goto out;
1364 ret = LDAP_get_integer_value(db, msg, "krb5MaxRenew", &max_renew);
1365 if (ret) {
1366 free(ent->entry.max_renew);
1367 ent->entry.max_renew = NULL;
1368 } else
1369 *ent->entry.max_renew = max_renew;
1372 ret = LDAP_get_integer_value(db, msg, "krb5KDCFlags", &tmp);
1373 if (ret)
1374 tmp = 0;
1376 ent->entry.flags = int2HDBFlags(tmp);
1378 /* Try and find Samba flags to put into the mix */
1379 ret = LDAP_get_string_value(db, msg, "sambaAcctFlags", &samba_acct_flags);
1380 if (ret == 0) {
1381 /* parse the [UXW...] string:
1383 'N' No password
1384 'D' Disabled
1385 'H' Homedir required
1386 'T' Temp account.
1387 'U' User account (normal)
1388 'M' MNS logon user account - what is this ?
1389 'W' Workstation account
1390 'S' Server account
1391 'L' Locked account
1392 'X' No Xpiry on password
1393 'I' Interdomain trust account
1397 int flags_len = strlen(samba_acct_flags);
1399 if (flags_len < 2)
1400 goto out2;
1402 if (samba_acct_flags[0] != '['
1403 || samba_acct_flags[flags_len - 1] != ']')
1404 goto out2;
1406 /* Allow forwarding */
1407 if (samba_forwardable)
1408 ent->entry.flags.forwardable = TRUE;
1410 for (i=0; i < flags_len; i++) {
1411 switch (samba_acct_flags[i]) {
1412 case ' ':
1413 case '[':
1414 case ']':
1415 break;
1416 case 'N':
1417 /* how to handle no password in kerberos? */
1418 break;
1419 case 'D':
1420 ent->entry.flags.invalid = TRUE;
1421 break;
1422 case 'H':
1423 break;
1424 case 'T':
1425 /* temp duplicate */
1426 ent->entry.flags.invalid = TRUE;
1427 break;
1428 case 'U':
1429 ent->entry.flags.client = TRUE;
1430 break;
1431 case 'M':
1432 break;
1433 case 'W':
1434 case 'S':
1435 ent->entry.flags.server = TRUE;
1436 ent->entry.flags.client = TRUE;
1437 break;
1438 case 'L':
1439 ent->entry.flags.invalid = TRUE;
1440 break;
1441 case 'X':
1442 if (ent->entry.pw_end) {
1443 free(ent->entry.pw_end);
1444 ent->entry.pw_end = NULL;
1446 break;
1447 case 'I':
1448 ent->entry.flags.server = TRUE;
1449 ent->entry.flags.client = TRUE;
1450 break;
1453 out2:
1454 free(samba_acct_flags);
1457 ret = 0;
1459 out:
1460 free(unparsed_name);
1461 free(ntPasswordIN);
1463 if (ret)
1464 hdb_free_entry(context, ent);
1466 return ret;
1469 static krb5_error_code
1470 LDAP_close(krb5_context context, HDB * db)
1472 if (HDB2LDAP(db)) {
1473 ldap_unbind_ext(HDB2LDAP(db), NULL, NULL);
1474 ((struct hdbldapdb *)db->hdb_db)->h_lp = NULL;
1477 return 0;
1480 static krb5_error_code
1481 LDAP_lock(krb5_context context, HDB * db, int operation)
1483 return 0;
1486 static krb5_error_code
1487 LDAP_unlock(krb5_context context, HDB * db)
1489 return 0;
1492 static krb5_error_code
1493 LDAP_seq(krb5_context context, HDB * db, unsigned flags, hdb_entry_ex * entry)
1495 int msgid, rc, parserc;
1496 krb5_error_code ret;
1497 LDAPMessage *e;
1499 msgid = HDB2MSGID(db);
1500 if (msgid < 0)
1501 return HDB_ERR_NOENTRY;
1503 do {
1504 rc = ldap_result(HDB2LDAP(db), msgid, LDAP_MSG_ONE, NULL, &e);
1505 switch (rc) {
1506 case LDAP_RES_SEARCH_REFERENCE:
1507 ldap_msgfree(e);
1508 ret = 0;
1509 break;
1510 case LDAP_RES_SEARCH_ENTRY:
1511 /* We have an entry. Parse it. */
1512 ret = LDAP_message2entry(context, db, e, flags, entry);
1513 ldap_msgfree(e);
1514 break;
1515 case LDAP_RES_SEARCH_RESULT:
1516 /* We're probably at the end of the results. If not, abandon. */
1517 parserc =
1518 ldap_parse_result(HDB2LDAP(db), e, NULL, NULL, NULL,
1519 NULL, NULL, 1);
1520 ret = HDB_ERR_NOENTRY;
1521 if (parserc != LDAP_SUCCESS
1522 && parserc != LDAP_MORE_RESULTS_TO_RETURN) {
1523 krb5_set_error_message(context, ret, "ldap_parse_result: %s",
1524 ldap_err2string(parserc));
1525 ldap_abandon_ext(HDB2LDAP(db), msgid, NULL, NULL);
1527 HDBSETMSGID(db, -1);
1528 break;
1529 case LDAP_SERVER_DOWN:
1530 ldap_msgfree(e);
1531 LDAP_close(context, db);
1532 HDBSETMSGID(db, -1);
1533 ret = ENETDOWN;
1534 break;
1535 default:
1536 /* Some unspecified error (timeout?). Abandon. */
1537 ldap_msgfree(e);
1538 ldap_abandon_ext(HDB2LDAP(db), msgid, NULL, NULL);
1539 ret = HDB_ERR_NOENTRY;
1540 HDBSETMSGID(db, -1);
1541 break;
1543 } while (rc == LDAP_RES_SEARCH_REFERENCE);
1545 if (ret == 0) {
1546 if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
1547 ret = hdb_unseal_keys(context, db, &entry->entry);
1548 if (ret)
1549 hdb_free_entry(context, entry);
1553 return ret;
1556 static krb5_error_code
1557 LDAP_firstkey(krb5_context context, HDB *db, unsigned flags,
1558 hdb_entry_ex *entry)
1560 krb5_error_code ret;
1561 int msgid;
1563 ret = LDAP__connect(context, db);
1564 if (ret)
1565 return ret;
1567 ret = LDAP_no_size_limit(context, HDB2LDAP(db));
1568 if (ret)
1569 return ret;
1571 ret = ldap_search_ext(HDB2LDAP(db), HDB2BASE(db),
1572 LDAP_SCOPE_SUBTREE,
1573 "(|(objectClass=krb5Principal)(objectClass=sambaSamAccount))",
1574 krb5kdcentry_attrs, 0,
1575 NULL, NULL, NULL, 0, &msgid);
1576 if (msgid < 0)
1577 return HDB_ERR_NOENTRY;
1579 HDBSETMSGID(db, msgid);
1581 return LDAP_seq(context, db, flags, entry);
1584 static krb5_error_code
1585 LDAP_nextkey(krb5_context context, HDB * db, unsigned flags,
1586 hdb_entry_ex * entry)
1588 return LDAP_seq(context, db, flags, entry);
1591 static krb5_error_code
1592 LDAP__connect(krb5_context context, HDB * db)
1594 int rc, version = LDAP_VERSION3;
1596 * Empty credentials to do a SASL bind with LDAP. Note that empty
1597 * different from NULL credentials. If you provide NULL
1598 * credentials instead of empty credentials you will get a SASL
1599 * bind in progress message.
1601 struct berval bv = { 0, "" };
1602 const char *sasl_method = "EXTERNAL";
1603 const char *bind_dn = NULL;
1605 if (HDB2BINDDN(db) != NULL && HDB2BINDPW(db) != NULL) {
1606 /* A bind DN was specified; use SASL SIMPLE */
1607 bind_dn = HDB2BINDDN(db);
1608 sasl_method = LDAP_SASL_SIMPLE;
1609 bv.bv_val = HDB2BINDPW(db);
1610 bv.bv_len = strlen(bv.bv_val);
1613 if (HDB2LDAP(db)) {
1614 /* connection has been opened. ping server. */
1615 struct sockaddr_un addr;
1616 socklen_t len = sizeof(addr);
1617 int sd;
1619 if (ldap_get_option(HDB2LDAP(db), LDAP_OPT_DESC, &sd) == 0 &&
1620 getpeername(sd, (struct sockaddr *) &addr, &len) < 0) {
1621 /* the other end has died. reopen. */
1622 LDAP_close(context, db);
1626 if (HDB2LDAP(db) != NULL) /* server is UP */
1627 return 0;
1629 rc = ldap_initialize(&((struct hdbldapdb *)db->hdb_db)->h_lp, HDB2URL(db));
1630 if (rc != LDAP_SUCCESS) {
1631 krb5_set_error_message(context, HDB_ERR_NOENTRY, "ldap_initialize: %s",
1632 ldap_err2string(rc));
1633 return HDB_ERR_NOENTRY;
1636 rc = ldap_set_option(HDB2LDAP(db), LDAP_OPT_PROTOCOL_VERSION,
1637 (const void *)&version);
1638 if (rc != LDAP_SUCCESS) {
1639 krb5_set_error_message(context, HDB_ERR_BADVERSION,
1640 "ldap_set_option: %s", ldap_err2string(rc));
1641 LDAP_close(context, db);
1642 return HDB_ERR_BADVERSION;
1645 if (((struct hdbldapdb *)db->hdb_db)->h_start_tls) {
1646 rc = ldap_start_tls_s(HDB2LDAP(db), NULL, NULL);
1648 if (rc != LDAP_SUCCESS) {
1649 krb5_set_error_message(context, HDB_ERR_BADVERSION,
1650 "ldap_start_tls_s: %s", ldap_err2string(rc));
1651 LDAP_close(context, db);
1652 return HDB_ERR_BADVERSION;
1656 rc = ldap_sasl_bind_s(HDB2LDAP(db), bind_dn, sasl_method, &bv,
1657 NULL, NULL, NULL);
1658 if (rc != LDAP_SUCCESS) {
1659 krb5_set_error_message(context, HDB_ERR_BADVERSION,
1660 "ldap_sasl_bind_s: %s", ldap_err2string(rc));
1661 LDAP_close(context, db);
1662 return HDB_ERR_BADVERSION;
1665 return 0;
1668 static krb5_error_code
1669 LDAP_open(krb5_context context, HDB * db, int flags, mode_t mode)
1671 /* Not the right place for this. */
1672 #ifdef HAVE_SIGACTION
1673 struct sigaction sa;
1675 sa.sa_flags = 0;
1676 sa.sa_handler = SIG_IGN;
1677 sigemptyset(&sa.sa_mask);
1679 sigaction(SIGPIPE, &sa, NULL);
1680 #else
1681 signal(SIGPIPE, SIG_IGN);
1682 #endif /* HAVE_SIGACTION */
1684 return LDAP__connect(context, db);
1687 static krb5_error_code
1688 LDAP_fetch_kvno(krb5_context context, HDB * db, krb5_const_principal principal,
1689 unsigned flags, krb5_kvno kvno, hdb_entry_ex * entry)
1691 LDAPMessage *msg, *e;
1692 krb5_error_code ret;
1694 ret = LDAP_principal2message(context, db, principal, &msg);
1695 if (ret)
1696 return ret;
1698 e = ldap_first_entry(HDB2LDAP(db), msg);
1699 if (e == NULL) {
1700 ret = HDB_ERR_NOENTRY;
1701 goto out;
1704 ret = LDAP_message2entry(context, db, e, flags, entry);
1705 if (ret == 0) {
1706 if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
1707 ret = hdb_unseal_keys(context, db, &entry->entry);
1708 if (ret)
1709 hdb_free_entry(context, entry);
1713 out:
1714 ldap_msgfree(msg);
1716 return ret;
1719 #if 0
1720 static krb5_error_code
1721 LDAP_fetch(krb5_context context, HDB * db, krb5_const_principal principal,
1722 unsigned flags, hdb_entry_ex * entry)
1724 return LDAP_fetch_kvno(context, db, principal,
1725 flags & (~HDB_F_KVNO_SPECIFIED), 0, entry);
1727 #endif
1729 static krb5_error_code
1730 LDAP_store(krb5_context context, HDB * db, unsigned flags,
1731 hdb_entry_ex * entry)
1733 LDAPMod **mods = NULL;
1734 krb5_error_code ret;
1735 const char *errfn;
1736 int rc;
1737 LDAPMessage *msg = NULL, *e = NULL;
1738 char *dn = NULL, *name = NULL;
1740 if ((flags & HDB_F_PRECHECK))
1741 return 0; /* we can't guarantee whether we'll be able to perform it */
1743 ret = LDAP_principal2message(context, db, entry->entry.principal, &msg);
1744 if (ret == 0)
1745 e = ldap_first_entry(HDB2LDAP(db), msg);
1747 ret = krb5_unparse_name(context, entry->entry.principal, &name);
1748 if (ret) {
1749 free(name);
1750 return ret;
1753 ret = hdb_seal_keys(context, db, &entry->entry);
1754 if (ret)
1755 goto out;
1757 /* turn new entry into LDAPMod array */
1758 ret = LDAP_entry2mods(context, db, entry, e, &mods);
1759 if (ret)
1760 goto out;
1762 if (e == NULL) {
1763 ret = asprintf(&dn, "krb5PrincipalName=%s,%s", name, HDB2CREATE(db));
1764 if (ret < 0) {
1765 ret = ENOMEM;
1766 krb5_set_error_message(context, ret, "asprintf: out of memory");
1767 goto out;
1769 } else if (flags & HDB_F_REPLACE) {
1770 /* Entry exists, and we're allowed to replace it. */
1771 dn = ldap_get_dn(HDB2LDAP(db), e);
1772 } else {
1773 /* Entry exists, but we're not allowed to replace it. Bail. */
1774 ret = HDB_ERR_EXISTS;
1775 goto out;
1778 /* write entry into directory */
1779 if (e == NULL) {
1780 /* didn't exist before */
1781 rc = ldap_add_ext_s(HDB2LDAP(db), dn, mods, NULL, NULL );
1782 errfn = "ldap_add_ext_s";
1783 } else {
1784 /* already existed, send deltas only */
1785 rc = ldap_modify_ext_s(HDB2LDAP(db), dn, mods, NULL, NULL );
1786 errfn = "ldap_modify_ext_s";
1789 if (check_ldap(context, db, rc)) {
1790 char *ld_error = NULL;
1791 ldap_get_option(HDB2LDAP(db), LDAP_OPT_ERROR_STRING,
1792 &ld_error);
1793 ret = HDB_ERR_CANT_LOCK_DB;
1794 krb5_set_error_message(context, ret, "%s: %s (DN=%s) %s: %s",
1795 errfn, name, dn, ldap_err2string(rc), ld_error);
1796 } else
1797 ret = 0;
1799 out:
1800 /* free stuff */
1801 if (dn)
1802 free(dn);
1803 if (msg)
1804 ldap_msgfree(msg);
1805 if (mods)
1806 ldap_mods_free(mods, 1);
1807 if (name)
1808 free(name);
1810 return ret;
1813 static krb5_error_code
1814 LDAP_remove(krb5_context context, HDB *db,
1815 unsigned flags, krb5_const_principal principal)
1817 krb5_error_code ret;
1818 LDAPMessage *msg, *e;
1819 char *dn = NULL;
1820 int rc, limit = LDAP_NO_LIMIT;
1822 if ((flags & HDB_F_PRECHECK))
1823 return 0; /* we can't guarantee whether we'll be able to perform it */
1825 ret = LDAP_principal2message(context, db, principal, &msg);
1826 if (ret)
1827 goto out;
1829 e = ldap_first_entry(HDB2LDAP(db), msg);
1830 if (e == NULL) {
1831 ret = HDB_ERR_NOENTRY;
1832 goto out;
1835 dn = ldap_get_dn(HDB2LDAP(db), e);
1836 if (dn == NULL) {
1837 ret = HDB_ERR_NOENTRY;
1838 goto out;
1841 rc = ldap_set_option(HDB2LDAP(db), LDAP_OPT_SIZELIMIT, (const void *)&limit);
1842 if (rc != LDAP_SUCCESS) {
1843 ret = HDB_ERR_BADVERSION;
1844 krb5_set_error_message(context, ret, "ldap_set_option: %s",
1845 ldap_err2string(rc));
1846 goto out;
1849 rc = ldap_delete_ext_s(HDB2LDAP(db), dn, NULL, NULL );
1850 if (check_ldap(context, db, rc)) {
1851 ret = HDB_ERR_CANT_LOCK_DB;
1852 krb5_set_error_message(context, ret, "ldap_delete_ext_s: %s",
1853 ldap_err2string(rc));
1854 } else
1855 ret = 0;
1857 out:
1858 if (dn != NULL)
1859 free(dn);
1860 if (msg != NULL)
1861 ldap_msgfree(msg);
1863 return ret;
1866 static krb5_error_code
1867 LDAP_destroy(krb5_context context, HDB * db)
1869 krb5_error_code ret;
1871 LDAP_close(context, db);
1873 ret = hdb_clear_master_key(context, db);
1874 if (HDB2BASE(db))
1875 free(HDB2BASE(db));
1876 if (HDB2CREATE(db))
1877 free(HDB2CREATE(db));
1878 if (HDB2URL(db))
1879 free(HDB2URL(db));
1880 if (db->hdb_name)
1881 free(db->hdb_name);
1882 free(db->hdb_db);
1883 free(db);
1885 return ret;
1888 static krb5_error_code
1889 hdb_ldap_common(krb5_context context,
1890 HDB ** db,
1891 const char *search_base,
1892 const char *url)
1894 struct hdbldapdb *h;
1895 const char *create_base = NULL;
1896 const char *ldap_secret_file = NULL;
1898 if (url == NULL || url[0] == '\0') {
1899 const char *p;
1900 p = krb5_config_get_string(context, NULL, "kdc",
1901 "hdb-ldap-url", NULL);
1902 if (p == NULL)
1903 p = default_ldap_url;
1905 url = p;
1908 if (search_base == NULL || search_base[0] == '\0') {
1909 krb5_set_error_message(context, ENOMEM, "ldap search base not configured");
1910 return ENOMEM; /* XXX */
1913 if (structural_object == NULL) {
1914 const char *p;
1916 p = krb5_config_get_string(context, NULL, "kdc",
1917 "hdb-ldap-structural-object", NULL);
1918 if (p == NULL)
1919 p = default_structural_object;
1920 structural_object = strdup(p);
1921 if (structural_object == NULL) {
1922 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
1923 return ENOMEM;
1927 samba_forwardable =
1928 krb5_config_get_bool_default(context, NULL, TRUE,
1929 "kdc", "hdb-samba-forwardable", NULL);
1931 *db = calloc(1, sizeof(**db));
1932 if (*db == NULL) {
1933 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
1934 return ENOMEM;
1936 memset(*db, 0, sizeof(**db));
1938 h = calloc(1, sizeof(*h));
1939 if (h == NULL) {
1940 free(*db);
1941 *db = NULL;
1942 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
1943 return ENOMEM;
1945 (*db)->hdb_db = h;
1947 /* XXX */
1948 if (asprintf(&(*db)->hdb_name, "ldap:%s", search_base) == -1) {
1949 LDAP_destroy(context, *db);
1950 *db = NULL;
1951 krb5_set_error_message(context, ENOMEM, "strdup: out of memory");
1952 return ENOMEM;
1955 h->h_url = strdup(url);
1956 h->h_base = strdup(search_base);
1957 if (h->h_url == NULL || h->h_base == NULL) {
1958 LDAP_destroy(context, *db);
1959 *db = NULL;
1960 krb5_set_error_message(context, ENOMEM, "strdup: out of memory");
1961 return ENOMEM;
1964 ldap_secret_file = krb5_config_get_string(context, NULL, "kdc",
1965 "hdb-ldap-secret-file", NULL);
1966 if (ldap_secret_file != NULL) {
1967 krb5_config_binding *tmp;
1968 krb5_error_code ret;
1969 const char *p;
1971 ret = krb5_config_parse_file(context, ldap_secret_file, &tmp);
1972 if (ret)
1973 return ret;
1975 p = krb5_config_get_string(context, tmp, "kdc",
1976 "hdb-ldap-bind-dn", NULL);
1977 if (p != NULL)
1978 h->h_bind_dn = strdup(p);
1980 p = krb5_config_get_string(context, tmp, "kdc",
1981 "hdb-ldap-bind-password", NULL);
1982 if (p != NULL)
1983 h->h_bind_password = strdup(p);
1985 krb5_config_file_free(context, tmp);
1988 h->h_start_tls =
1989 krb5_config_get_bool_default(context, NULL, FALSE,
1990 "kdc", "hdb-ldap-start-tls", NULL);
1992 create_base = krb5_config_get_string(context, NULL, "kdc",
1993 "hdb-ldap-create-base", NULL);
1994 if (create_base == NULL)
1995 create_base = h->h_base;
1997 h->h_createbase = strdup(create_base);
1998 if (h->h_createbase == NULL) {
1999 LDAP_destroy(context, *db);
2000 *db = NULL;
2001 krb5_set_error_message(context, ENOMEM, "strdup: out of memory");
2002 return ENOMEM;
2005 (*db)->hdb_master_key_set = 0;
2006 (*db)->hdb_openp = 0;
2007 (*db)->hdb_capability_flags = HDB_CAP_F_SHARED_DIRECTORY;
2008 (*db)->hdb_open = LDAP_open;
2009 (*db)->hdb_close = LDAP_close;
2010 (*db)->hdb_fetch_kvno = LDAP_fetch_kvno;
2011 (*db)->hdb_store = LDAP_store;
2012 (*db)->hdb_remove = LDAP_remove;
2013 (*db)->hdb_firstkey = LDAP_firstkey;
2014 (*db)->hdb_nextkey = LDAP_nextkey;
2015 (*db)->hdb_lock = LDAP_lock;
2016 (*db)->hdb_unlock = LDAP_unlock;
2017 (*db)->hdb_rename = NULL;
2018 (*db)->hdb__get = NULL;
2019 (*db)->hdb__put = NULL;
2020 (*db)->hdb__del = NULL;
2021 (*db)->hdb_destroy = LDAP_destroy;
2023 return 0;
2026 krb5_error_code
2027 hdb_ldap_create(krb5_context context, HDB ** db, const char *arg)
2029 return hdb_ldap_common(context, db, arg, NULL);
2032 krb5_error_code
2033 hdb_ldapi_create(krb5_context context, HDB ** db, const char *arg)
2035 krb5_error_code ret;
2036 char *search_base, *p;
2038 asprintf(&p, "ldapi:%s", arg);
2039 if (p == NULL) {
2040 *db = NULL;
2041 krb5_set_error_message(context, ENOMEM, "out of memory");
2042 return ENOMEM;
2044 search_base = strchr(p + strlen("ldapi://"), ':');
2045 if (search_base == NULL) {
2046 *db = NULL;
2047 krb5_set_error_message(context, HDB_ERR_BADVERSION,
2048 "search base missing");
2049 return HDB_ERR_BADVERSION;
2051 *search_base = '\0';
2052 search_base++;
2054 ret = hdb_ldap_common(context, db, search_base, p);
2055 free(p);
2056 return ret;
2059 #ifdef OPENLDAP_MODULE
2062 static krb5_error_code
2063 init(krb5_context context, void **ctx)
2065 *ctx = NULL;
2066 return 0;
2069 static void
2070 fini(void *ctx)
2074 struct hdb_method hdb_ldap_interface = {
2075 HDB_INTERFACE_VERSION,
2076 init,
2077 fini,
2078 "ldap",
2079 hdb_ldap_create
2082 struct hdb_method hdb_ldapi_interface = {
2083 HDB_INTERFACE_VERSION,
2084 init,
2085 fini,
2086 "ldapi",
2087 hdb_ldapi_create
2090 #endif
2092 #endif /* OPENLDAP */