1 /* $NetBSD: hdb-ldap.c,v 1.3 2019/12/15 22:50:49 christos Exp $ */
2
3 /*
4 * Copyright (c) 1999-2001, 2003, PADL Software Pty Ltd.
5 * Copyright (c) 2004, Andrew Bartlett.
6 * Copyright (c) 2003 - 2008, Kungliga Tekniska Högskolan.
7 * Copyright (c) 2015, Timothy Pearson.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 *
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 *
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * 3. Neither the name of PADL Software nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37
38 #include "hdb_locl.h"
39
40 #ifdef OPENLDAP
41
42 #include <lber.h>
43 #include <ldap.h>
44 #include <sys/un.h>
45 #include <krb5/hex.h>
46
47 static krb5_error_code LDAP__connect(krb5_context context, HDB *);
48 static krb5_error_code LDAP_close(krb5_context context, HDB *);
49
50 static krb5_error_code
51 LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,
52 int flags, hdb_entry_ex * ent);
53
54 static const char *default_structural_object = "account";
55 static char *structural_object;
56 static const char *default_ldap_url = "ldapi:///";
57 static krb5_boolean samba_forwardable;
58
59 struct hdbldapdb {
60 LDAP *h_lp;
61 int h_msgid;
62 char *h_base;
63 char *h_url;
64 char *h_bind_dn;
65 char *h_bind_password;
66 krb5_boolean h_start_tls;
67 char *h_createbase;
68 };
69
70 #define HDB2LDAP(db) (((struct hdbldapdb *)(db)->hdb_db)->h_lp)
71 #define HDB2MSGID(db) (((struct hdbldapdb *)(db)->hdb_db)->h_msgid)
72 #define HDBSETMSGID(db,msgid) \
73 do { ((struct hdbldapdb *)(db)->hdb_db)->h_msgid = msgid; } while(0)
74 #define HDB2BASE(dn) (((struct hdbldapdb *)(db)->hdb_db)->h_base)
75 #define HDB2URL(dn) (((struct hdbldapdb *)(db)->hdb_db)->h_url)
76 #define HDB2BINDDN(db) (((struct hdbldapdb *)(db)->hdb_db)->h_bind_dn)
77 #define HDB2BINDPW(db) (((struct hdbldapdb *)(db)->hdb_db)->h_bind_password)
78 #define HDB2CREATE(db) (((struct hdbldapdb *)(db)->hdb_db)->h_createbase)
79
80 /*
81 *
82 */
83
84 static char * krb5kdcentry_attrs[] = {
85 "cn",
86 "createTimestamp",
87 "creatorsName",
88 "krb5EncryptionType",
89 "krb5KDCFlags",
90 "krb5Key",
91 "krb5KeyVersionNumber",
92 "krb5MaxLife",
93 "krb5MaxRenew",
94 "krb5PasswordEnd",
95 "krb5PrincipalName",
96 "krb5PrincipalRealm",
97 "krb5ExtendedAttributes",
98 "krb5ValidEnd",
99 "krb5ValidStart",
100 "modifiersName",
101 "modifyTimestamp",
102 "objectClass",
103 "sambaAcctFlags",
104 "sambaKickoffTime",
105 "sambaNTPassword",
106 "sambaPwdLastSet",
107 "sambaPwdMustChange",
108 "uid",
109 NULL
110 };
111
112 static char *krb5principal_attrs[] = {
113 "cn",
114 "createTimestamp",
115 "creatorsName",
116 "krb5PrincipalName",
117 "krb5PrincipalRealm",
118 "modifiersName",
119 "modifyTimestamp",
120 "objectClass",
121 "uid",
122 NULL
123 };
124
125 static int
LDAP_no_size_limit(krb5_context context,LDAP * lp)126 LDAP_no_size_limit(krb5_context context, LDAP *lp)
127 {
128 int ret, limit = LDAP_NO_LIMIT;
129
130 ret = ldap_set_option(lp, LDAP_OPT_SIZELIMIT, (const void *)&limit);
131 if (ret != LDAP_SUCCESS) {
132 krb5_set_error_message(context, HDB_ERR_BADVERSION,
133 "ldap_set_option: %s",
134 ldap_err2string(ret));
135 return HDB_ERR_BADVERSION;
136 }
137 return 0;
138 }
139
140 static int
check_ldap(krb5_context context,HDB * db,int ret)141 check_ldap(krb5_context context, HDB *db, int ret)
142 {
143 switch (ret) {
144 case LDAP_SUCCESS:
145 return 0;
146 case LDAP_SERVER_DOWN:
147 LDAP_close(context, db);
148 return 1;
149 default:
150 return 1;
151 }
152 }
153
154 static krb5_error_code
LDAP__setmod(LDAPMod *** modlist,int modop,const char * attribute,int * pIndex)155 LDAP__setmod(LDAPMod *** modlist, int modop, const char *attribute,
156 int *pIndex)
157 {
158 int cMods;
159
160 if (*modlist == NULL) {
161 *modlist = (LDAPMod **)ber_memcalloc(1, sizeof(LDAPMod *));
162 if (*modlist == NULL)
163 return ENOMEM;
164 }
165
166 for (cMods = 0; (*modlist)[cMods] != NULL; cMods++) {
167 if ((*modlist)[cMods]->mod_op == modop &&
168 strcasecmp((*modlist)[cMods]->mod_type, attribute) == 0) {
169 break;
170 }
171 }
172
173 *pIndex = cMods;
174
175 if ((*modlist)[cMods] == NULL) {
176 LDAPMod *mod;
177
178 *modlist = (LDAPMod **)ber_memrealloc(*modlist,
179 (cMods + 2) * sizeof(LDAPMod *));
180 if (*modlist == NULL)
181 return ENOMEM;
182
183 (*modlist)[cMods] = (LDAPMod *)ber_memalloc(sizeof(LDAPMod));
184 if ((*modlist)[cMods] == NULL)
185 return ENOMEM;
186
187 mod = (*modlist)[cMods];
188 mod->mod_op = modop;
189 mod->mod_type = ber_strdup(attribute);
190 if (mod->mod_type == NULL) {
191 ber_memfree(mod);
192 (*modlist)[cMods] = NULL;
193 return ENOMEM;
194 }
195
196 if (modop & LDAP_MOD_BVALUES) {
197 mod->mod_bvalues = NULL;
198 } else {
199 mod->mod_values = NULL;
200 }
201
202 (*modlist)[cMods + 1] = NULL;
203 }
204
205 return 0;
206 }
207
208 static krb5_error_code
LDAP_addmod_len(LDAPMod *** modlist,int modop,const char * attribute,unsigned char * value,size_t len)209 LDAP_addmod_len(LDAPMod *** modlist, int modop, const char *attribute,
210 unsigned char *value, size_t len)
211 {
212 krb5_error_code ret;
213 int cMods, i = 0;
214
215 ret = LDAP__setmod(modlist, modop | LDAP_MOD_BVALUES, attribute, &cMods);
216 if (ret)
217 return ret;
218
219 if (value != NULL) {
220 struct berval **bv;
221
222 bv = (*modlist)[cMods]->mod_bvalues;
223 if (bv != NULL) {
224 for (i = 0; bv[i] != NULL; i++)
225 ;
226 bv = ber_memrealloc(bv, (i + 2) * sizeof(*bv));
227 } else
228 bv = ber_memalloc(2 * sizeof(*bv));
229 if (bv == NULL)
230 return ENOMEM;
231
232 (*modlist)[cMods]->mod_bvalues = bv;
233
234 bv[i] = ber_memalloc(sizeof(**bv));;
235 if (bv[i] == NULL)
236 return ENOMEM;
237
238 bv[i]->bv_val = (void *)value;
239 bv[i]->bv_len = len;
240
241 bv[i + 1] = NULL;
242 }
243
244 return 0;
245 }
246
247 static krb5_error_code
LDAP_addmod(LDAPMod *** modlist,int modop,const char * attribute,const char * value)248 LDAP_addmod(LDAPMod *** modlist, int modop, const char *attribute,
249 const char *value)
250 {
251 int cMods, i = 0;
252 krb5_error_code ret;
253
254 ret = LDAP__setmod(modlist, modop, attribute, &cMods);
255 if (ret)
256 return ret;
257
258 if (value != NULL) {
259 char **bv;
260
261 bv = (*modlist)[cMods]->mod_values;
262 if (bv != NULL) {
263 for (i = 0; bv[i] != NULL; i++)
264 ;
265 bv = ber_memrealloc(bv, (i + 2) * sizeof(*bv));
266 } else
267 bv = ber_memalloc(2 * sizeof(*bv));
268 if (bv == NULL)
269 return ENOMEM;
270
271 (*modlist)[cMods]->mod_values = bv;
272
273 bv[i] = ber_strdup(value);
274 if (bv[i] == NULL)
275 return ENOMEM;
276
277 bv[i + 1] = NULL;
278 }
279
280 return 0;
281 }
282
283 static krb5_error_code
LDAP_addmod_generalized_time(LDAPMod *** mods,int modop,const char * attribute,KerberosTime * time)284 LDAP_addmod_generalized_time(LDAPMod *** mods, int modop,
285 const char *attribute, KerberosTime * time)
286 {
287 char buf[22];
288 struct tm *tm;
289
290 /* XXX not threadsafe */
291 tm = gmtime(time);
292 strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", tm);
293
294 return LDAP_addmod(mods, modop, attribute, buf);
295 }
296
297 static krb5_error_code
LDAP_addmod_integer(krb5_context context,LDAPMod *** mods,int modop,const char * attribute,unsigned long l)298 LDAP_addmod_integer(krb5_context context,
299 LDAPMod *** mods, int modop,
300 const char *attribute, unsigned long l)
301 {
302 krb5_error_code ret;
303 char *buf;
304
305 ret = asprintf(&buf, "%ld", l);
306 if (ret < 0) {
307 krb5_set_error_message(context, ENOMEM,
308 "asprintf: out of memory:");
309 return ENOMEM;
310 }
311 ret = LDAP_addmod(mods, modop, attribute, buf);
312 free (buf);
313 return ret;
314 }
315
316 static krb5_error_code
LDAP_get_string_value(HDB * db,LDAPMessage * entry,const char * attribute,char ** ptr)317 LDAP_get_string_value(HDB * db, LDAPMessage * entry,
318 const char *attribute, char **ptr)
319 {
320 struct berval **vals;
321
322 vals = ldap_get_values_len(HDB2LDAP(db), entry, attribute);
323 if (vals == NULL || vals[0] == NULL) {
324 *ptr = NULL;
325 return HDB_ERR_NOENTRY;
326 }
327
328 *ptr = malloc(vals[0]->bv_len + 1);
329 if (*ptr == NULL) {
330 ldap_value_free_len(vals);
331 return ENOMEM;
332 }
333
334 memcpy(*ptr, vals[0]->bv_val, vals[0]->bv_len);
335 (*ptr)[vals[0]->bv_len] = 0;
336
337 ldap_value_free_len(vals);
338
339 return 0;
340 }
341
342 static krb5_error_code
LDAP_get_integer_value(HDB * db,LDAPMessage * entry,const char * attribute,int * ptr)343 LDAP_get_integer_value(HDB * db, LDAPMessage * entry,
344 const char *attribute, int *ptr)
345 {
346 krb5_error_code ret;
347 char *val;
348
349 ret = LDAP_get_string_value(db, entry, attribute, &val);
350 if (ret)
351 return ret;
352 *ptr = atoi(val);
353 free(val);
354 return 0;
355 }
356
357 static krb5_error_code
LDAP_get_generalized_time_value(HDB * db,LDAPMessage * entry,const char * attribute,KerberosTime * kt)358 LDAP_get_generalized_time_value(HDB * db, LDAPMessage * entry,
359 const char *attribute, KerberosTime * kt)
360 {
361 char *tmp, *gentime;
362 struct tm tm;
363 int ret;
364
365 *kt = 0;
366
367 ret = LDAP_get_string_value(db, entry, attribute, &gentime);
368 if (ret)
369 return ret;
370
371 tmp = strptime(gentime, "%Y%m%d%H%M%SZ", &tm);
372 if (tmp == NULL) {
373 free(gentime);
374 return HDB_ERR_NOENTRY;
375 }
376
377 free(gentime);
378
379 *kt = timegm(&tm);
380
381 return 0;
382 }
383
384 static int
bervalstrcmp(struct berval * v,const char * str)385 bervalstrcmp(struct berval *v, const char *str)
386 {
387 size_t len = strlen(str);
388 return (v->bv_len == len) && strncasecmp(str, (char *)v->bv_val, len) == 0;
389 }
390
391
392 static krb5_error_code
LDAP_entry2mods(krb5_context context,HDB * db,hdb_entry_ex * ent,LDAPMessage * msg,LDAPMod *** pmods)393 LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent,
394 LDAPMessage * msg, LDAPMod *** pmods)
395 {
396 krb5_error_code ret;
397 krb5_boolean is_new_entry;
398 char *tmp = NULL;
399 LDAPMod **mods = NULL;
400 hdb_entry_ex orig;
401 unsigned long oflags, nflags;
402 int i;
403
404 krb5_boolean is_samba_account = FALSE;
405 krb5_boolean is_account = FALSE;
406 krb5_boolean is_heimdal_entry = FALSE;
407 krb5_boolean is_heimdal_principal = FALSE;
408
409 struct berval **vals;
410
411 *pmods = NULL;
412
413 if (msg != NULL) {
414
415 ret = LDAP_message2entry(context, db, msg, 0, &orig);
416 if (ret)
417 goto out;
418
419 is_new_entry = FALSE;
420
421 vals = ldap_get_values_len(HDB2LDAP(db), msg, "objectClass");
422 if (vals) {
423 int num_objectclasses = ldap_count_values_len(vals);
424 for (i=0; i < num_objectclasses; i++) {
425 if (bervalstrcmp(vals[i], "sambaSamAccount"))
426 is_samba_account = TRUE;
427 else if (bervalstrcmp(vals[i], structural_object))
428 is_account = TRUE;
429 else if (bervalstrcmp(vals[i], "krb5Principal"))
430 is_heimdal_principal = TRUE;
431 else if (bervalstrcmp(vals[i], "krb5KDCEntry"))
432 is_heimdal_entry = TRUE;
433 }
434 ldap_value_free_len(vals);
435 }
436
437 /*
438 * If this is just a "account" entry and no other objectclass
439 * is hanging on this entry, it's really a new entry.
440 */
441 if (is_samba_account == FALSE && is_heimdal_principal == FALSE &&
442 is_heimdal_entry == FALSE) {
443 if (is_account == TRUE) {
444 is_new_entry = TRUE;
445 } else {
446 ret = HDB_ERR_NOENTRY;
447 goto out;
448 }
449 }
450 } else
451 is_new_entry = TRUE;
452
453 if (is_new_entry) {
454
455 /* to make it perfectly obvious we're depending on
456 * orig being intiialized to zero */
457 memset(&orig, 0, sizeof(orig));
458
459 ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "top");
460 if (ret)
461 goto out;
462
463 /* account is the structural object class */
464 if (is_account == FALSE) {
465 ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass",
466 structural_object);
467 is_account = TRUE;
468 if (ret)
469 goto out;
470 }
471
472 ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "krb5Principal");
473 is_heimdal_principal = TRUE;
474 if (ret)
475 goto out;
476
477 ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "krb5KDCEntry");
478 is_heimdal_entry = TRUE;
479 if (ret)
480 goto out;
481 }
482
483 if (is_new_entry ||
484 krb5_principal_compare(context, ent->entry.principal, orig.entry.principal)
485 == FALSE)
486 {
487 if (is_heimdal_principal || is_heimdal_entry) {
488
489 ret = krb5_unparse_name(context, ent->entry.principal, &tmp);
490 if (ret)
491 goto out;
492
493 ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE,
494 "krb5PrincipalName", tmp);
495 if (ret) {
496 free(tmp);
497 goto out;
498 }
499 free(tmp);
500 }
501
502 if (is_account || is_samba_account) {
503 ret = krb5_unparse_name_short(context, ent->entry.principal, &tmp);
504 if (ret)
505 goto out;
506 ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "uid", tmp);
507 if (ret) {
508 free(tmp);
509 goto out;
510 }
511 free(tmp);
512 }
513 }
514
515 if (is_heimdal_entry && (ent->entry.kvno != orig.entry.kvno || is_new_entry)) {
516 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
517 "krb5KeyVersionNumber",
518 ent->entry.kvno);
519 if (ret)
520 goto out;
521 }
522
523 if (is_heimdal_entry && ent->entry.extensions) {
524 if (!is_new_entry) {
525 vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5ExtendedAttributes");
526 if (vals) {
527 ldap_value_free_len(vals);
528 ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5ExtendedAttributes", NULL);
529 if (ret)
530 goto out;
531 }
532 }
533
534 for (i = 0; i < ent->entry.extensions->len; i++) {
535 unsigned char *buf;
536 size_t size, sz = 0;
537
538 ASN1_MALLOC_ENCODE(HDB_extension, buf, size, &ent->entry.extensions->val[i], &sz, ret);
539 if (ret)
540 goto out;
541 if (size != sz)
542 krb5_abortx(context, "internal error in ASN.1 encoder");
543
544 ret = LDAP_addmod_len(&mods, LDAP_MOD_ADD, "krb5ExtendedAttributes", buf, sz);
545 if (ret)
546 goto out;
547 }
548 }
549
550 if (is_heimdal_entry && ent->entry.valid_start) {
551 if (orig.entry.valid_end == NULL
552 || (*(ent->entry.valid_start) != *(orig.entry.valid_start))) {
553 ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,
554 "krb5ValidStart",
555 ent->entry.valid_start);
556 if (ret)
557 goto out;
558 }
559 }
560
561 if (ent->entry.valid_end) {
562 if (orig.entry.valid_end == NULL || (*(ent->entry.valid_end) != *(orig.entry.valid_end))) {
563 if (is_heimdal_entry) {
564 ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,
565 "krb5ValidEnd",
566 ent->entry.valid_end);
567 if (ret)
568 goto out;
569 }
570 if (is_samba_account) {
571 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
572 "sambaKickoffTime",
573 *(ent->entry.valid_end));
574 if (ret)
575 goto out;
576 }
577 }
578 }
579
580 if (ent->entry.pw_end) {
581 if (orig.entry.pw_end == NULL || (*(ent->entry.pw_end) != *(orig.entry.pw_end))) {
582 if (is_heimdal_entry) {
583 ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,
584 "krb5PasswordEnd",
585 ent->entry.pw_end);
586 if (ret)
587 goto out;
588 }
589
590 if (is_samba_account) {
591 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
592 "sambaPwdMustChange",
593 *(ent->entry.pw_end));
594 if (ret)
595 goto out;
596 }
597 }
598 }
599
600
601 #if 0 /* we we have last_pw_change */
602 if (is_samba_account && ent->entry.last_pw_change) {
603 if (orig.entry.last_pw_change == NULL || (*(ent->entry.last_pw_change) != *(orig.entry.last_pw_change))) {
604 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
605 "sambaPwdLastSet",
606 *(ent->entry.last_pw_change));
607 if (ret)
608 goto out;
609 }
610 }
611 #endif
612
613 if (is_heimdal_entry && ent->entry.max_life) {
614 if (orig.entry.max_life == NULL
615 || (*(ent->entry.max_life) != *(orig.entry.max_life))) {
616
617 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
618 "krb5MaxLife",
619 *(ent->entry.max_life));
620 if (ret)
621 goto out;
622 }
623 }
624
625 if (is_heimdal_entry && ent->entry.max_renew) {
626 if (orig.entry.max_renew == NULL
627 || (*(ent->entry.max_renew) != *(orig.entry.max_renew))) {
628
629 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
630 "krb5MaxRenew",
631 *(ent->entry.max_renew));
632 if (ret)
633 goto out;
634 }
635 }
636
637 oflags = HDBFlags2int(orig.entry.flags);
638 nflags = HDBFlags2int(ent->entry.flags);
639
640 if (is_heimdal_entry && oflags != nflags) {
641
642 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
643 "krb5KDCFlags",
644 nflags);
645 if (ret)
646 goto out;
647 }
648
649 /* Remove keys if they exists, and then replace keys. */
650 if (!is_new_entry && orig.entry.keys.len > 0) {
651 vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5Key");
652 if (vals) {
653 ldap_value_free_len(vals);
654
655 ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5Key", NULL);
656 if (ret)
657 goto out;
658 }
659 }
660
661 for (i = 0; i < ent->entry.keys.len; i++) {
662
663 if (is_samba_account
664 && ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) {
665 char *ntHexPassword;
666 char *nt;
667 time_t now = time(NULL);
668
669 /* the key might have been 'sealed', but samba passwords
670 are clear in the directory */
671 ret = hdb_unseal_key(context, db, &ent->entry.keys.val[i]);
672 if (ret)
673 goto out;
674
675 nt = ent->entry.keys.val[i].key.keyvalue.data;
676 /* store in ntPassword, not krb5key */
677 ret = hex_encode(nt, 16, &ntHexPassword);
678 if (ret < 0) {
679 ret = ENOMEM;
680 krb5_set_error_message(context, ret, "hdb-ldap: failed to "
681 "hex encode key");
682 goto out;
683 }
684 ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "sambaNTPassword",
685 ntHexPassword);
686 free(ntHexPassword);
687 if (ret)
688 goto out;
689 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
690 "sambaPwdLastSet", now);
691 if (ret)
692 goto out;
693
694 /* have to kill the LM passwod if it exists */
695 vals = ldap_get_values_len(HDB2LDAP(db), msg, "sambaLMPassword");
696 if (vals) {
697 ldap_value_free_len(vals);
698 ret = LDAP_addmod(&mods, LDAP_MOD_DELETE,
699 "sambaLMPassword", NULL);
700 if (ret)
701 goto out;
702 }
703
704 } else if (is_heimdal_entry) {
705 unsigned char *buf;
706 size_t len, buf_size;
707
708 ASN1_MALLOC_ENCODE(Key, buf, buf_size, &ent->entry.keys.val[i], &len, ret);
709 if (ret)
710 goto out;
711 if(buf_size != len)
712 krb5_abortx(context, "internal error in ASN.1 encoder");
713
714 /* addmod_len _owns_ the key, doesn't need to copy it */
715 ret = LDAP_addmod_len(&mods, LDAP_MOD_ADD, "krb5Key", buf, len);
716 if (ret)
717 goto out;
718 }
719 }
720
721 if (ent->entry.etypes) {
722 int add_krb5EncryptionType = 0;
723
724 /*
725 * Only add/modify krb5EncryptionType if it's a new heimdal
726 * entry or krb5EncryptionType already exists on the entry.
727 */
728
729 if (!is_new_entry) {
730 vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5EncryptionType");
731 if (vals) {
732 ldap_value_free_len(vals);
733 ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5EncryptionType",
734 NULL);
735 if (ret)
736 goto out;
737 add_krb5EncryptionType = 1;
738 }
739 } else if (is_heimdal_entry)
740 add_krb5EncryptionType = 1;
741
742 if (add_krb5EncryptionType) {
743 for (i = 0; i < ent->entry.etypes->len; i++) {
744 if (is_samba_account &&
745 ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5)
746 {
747 ;
748 } else if (is_heimdal_entry) {
749 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_ADD,
750 "krb5EncryptionType",
751 ent->entry.etypes->val[i]);
752 if (ret)
753 goto out;
754 }
755 }
756 }
757 }
758
759 /* for clarity */
760 ret = 0;
761
762 out:
763
764 if (ret == 0)
765 *pmods = mods;
766 else if (mods != NULL) {
767 ldap_mods_free(mods, 1);
768 *pmods = NULL;
769 }
770
771 if (msg)
772 hdb_free_entry(context, &orig);
773
774 return ret;
775 }
776
777 static krb5_error_code
LDAP_dn2principal(krb5_context context,HDB * db,const char * dn,krb5_principal * principal)778 LDAP_dn2principal(krb5_context context, HDB * db, const char *dn,
779 krb5_principal * principal)
780 {
781 krb5_error_code ret;
782 int rc;
783 const char *filter = "(objectClass=krb5Principal)";
784 LDAPMessage *res = NULL, *e;
785 char *p;
786
787 ret = LDAP_no_size_limit(context, HDB2LDAP(db));
788 if (ret)
789 goto out;
790
791 rc = ldap_search_ext_s(HDB2LDAP(db), dn, LDAP_SCOPE_SUBTREE,
792 filter, krb5principal_attrs, 0,
793 NULL, NULL, NULL,
794 0, &res);
795 if (check_ldap(context, db, rc)) {
796 ret = HDB_ERR_NOENTRY;
797 krb5_set_error_message(context, ret, "ldap_search_ext_s: "
798 "filter: %s error: %s",
799 filter, ldap_err2string(rc));
800 goto out;
801 }
802
803 e = ldap_first_entry(HDB2LDAP(db), res);
804 if (e == NULL) {
805 ret = HDB_ERR_NOENTRY;
806 goto out;
807 }
808
809 ret = LDAP_get_string_value(db, e, "krb5PrincipalName", &p);
810 if (ret) {
811 ret = HDB_ERR_NOENTRY;
812 goto out;
813 }
814
815 ret = krb5_parse_name(context, p, principal);
816 free(p);
817
818 out:
819 if (res)
820 ldap_msgfree(res);
821
822 return ret;
823 }
824
825 static int
need_quote(unsigned char c)826 need_quote(unsigned char c)
827 {
828 return (c & 0x80) ||
829 (c < 32) ||
830 (c == '(') ||
831 (c == ')') ||
832 (c == '*') ||
833 (c == '\\') ||
834 (c == 0x7f);
835 }
836
837 static const char hexchar[] = "0123456789ABCDEF";
838
839 static krb5_error_code
escape_value(krb5_context context,const char * unquoted,char ** quoted)840 escape_value(krb5_context context, const char *unquoted, char **quoted)
841 {
842 size_t i, len;
843
844 for (i = 0, len = 0; unquoted[i] != '\0'; i++, len++) {
845 if (need_quote((unsigned char)unquoted[i]))
846 len += 2;
847 }
848
849 *quoted = malloc(len + 1);
850 if (*quoted == NULL) {
851 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
852 return ENOMEM;
853 }
854
855 for (i = 0; unquoted[0] ; unquoted++) {
856 if (need_quote((unsigned char)unquoted[0])) {
857 (*quoted)[i++] = '\\';
858 (*quoted)[i++] = hexchar[(unquoted[0] >> 4) & 0xf];
859 (*quoted)[i++] = hexchar[(unquoted[0] ) & 0xf];
860 } else
861 (*quoted)[i++] = (char)unquoted[0];
862 }
863 (*quoted)[i] = '\0';
864 return 0;
865 }
866
867
868 static krb5_error_code
LDAP__lookup_princ(krb5_context context,HDB * db,const char * princname,const char * userid,LDAPMessage ** msg)869 LDAP__lookup_princ(krb5_context context,
870 HDB *db,
871 const char *princname,
872 const char *userid,
873 LDAPMessage **msg)
874 {
875 krb5_error_code ret;
876 int rc;
877 char *quote, *filter = NULL;
878
879 ret = LDAP__connect(context, db);
880 if (ret)
881 return ret;
882
883 /*
884 * Quote searches that contain filter language, this quote
885 * searches for *@REALM, which takes very long time.
886 */
887
888 ret = escape_value(context, princname, "e);
889 if (ret)
890 goto out;
891
892 rc = asprintf(&filter,
893 "(&(objectClass=krb5Principal)(krb5PrincipalName=%s))",
894 quote);
895 free(quote);
896
897 if (rc < 0) {
898 ret = ENOMEM;
899 krb5_set_error_message(context, ret, "malloc: out of memory");
900 goto out;
901 }
902
903 ret = LDAP_no_size_limit(context, HDB2LDAP(db));
904 if (ret)
905 goto out;
906
907 rc = ldap_search_ext_s(HDB2LDAP(db), HDB2BASE(db),
908 LDAP_SCOPE_SUBTREE, filter,
909 krb5kdcentry_attrs, 0,
910 NULL, NULL, NULL,
911 0, msg);
912 if (check_ldap(context, db, rc)) {
913 ret = HDB_ERR_NOENTRY;
914 krb5_set_error_message(context, ret, "ldap_search_ext_s: "
915 "filter: %s - error: %s",
916 filter, ldap_err2string(rc));
917 goto out;
918 }
919
920 if (userid && ldap_count_entries(HDB2LDAP(db), *msg) == 0) {
921 free(filter);
922 filter = NULL;
923 ldap_msgfree(*msg);
924 *msg = NULL;
925
926 ret = escape_value(context, userid, "e);
927 if (ret)
928 goto out;
929
930 rc = asprintf(&filter,
931 "(&(|(objectClass=sambaSamAccount)(objectClass=%s))(uid=%s))",
932 structural_object, quote);
933 free(quote);
934 if (rc < 0) {
935 ret = ENOMEM;
936 krb5_set_error_message(context, ret, "asprintf: out of memory");
937 goto out;
938 }
939
940 ret = LDAP_no_size_limit(context, HDB2LDAP(db));
941 if (ret)
942 goto out;
943
944 rc = ldap_search_ext_s(HDB2LDAP(db), HDB2BASE(db), LDAP_SCOPE_SUBTREE,
945 filter, krb5kdcentry_attrs, 0,
946 NULL, NULL, NULL,
947 0, msg);
948 if (check_ldap(context, db, rc)) {
949 ret = HDB_ERR_NOENTRY;
950 krb5_set_error_message(context, ret,
951 "ldap_search_ext_s: filter: %s error: %s",
952 filter, ldap_err2string(rc));
953 goto out;
954 }
955 }
956
957 ret = 0;
958
959 out:
960 if (filter)
961 free(filter);
962
963 return ret;
964 }
965
966 static krb5_error_code
LDAP_principal2message(krb5_context context,HDB * db,krb5_const_principal princ,LDAPMessage ** msg)967 LDAP_principal2message(krb5_context context, HDB * db,
968 krb5_const_principal princ, LDAPMessage ** msg)
969 {
970 char *name, *name_short = NULL;
971 krb5_error_code ret;
972 krb5_realm *r, *r0;
973
974 *msg = NULL;
975
976 ret = krb5_unparse_name(context, princ, &name);
977 if (ret)
978 return ret;
979
980 ret = krb5_get_default_realms(context, &r0);
981 if(ret) {
982 free(name);
983 return ret;
984 }
985 for (r = r0; *r != NULL; r++) {
986 if(strcmp(krb5_principal_get_realm(context, princ), *r) == 0) {
987 ret = krb5_unparse_name_short(context, princ, &name_short);
988 if (ret) {
989 krb5_free_host_realm(context, r0);
990 free(name);
991 return ret;
992 }
993 break;
994 }
995 }
996 krb5_free_host_realm(context, r0);
997
998 ret = LDAP__lookup_princ(context, db, name, name_short, msg);
999 free(name);
1000 free(name_short);
1001
1002 return ret;
1003 }
1004
1005 /*
1006 * Construct an hdb_entry from a directory entry.
1007 */
1008 static krb5_error_code
LDAP_message2entry(krb5_context context,HDB * db,LDAPMessage * msg,int flags,hdb_entry_ex * ent)1009 LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,
1010 int flags, hdb_entry_ex * ent)
1011 {
1012 char *unparsed_name = NULL, *dn = NULL, *ntPasswordIN = NULL;
1013 char *samba_acct_flags = NULL;
1014 struct berval **keys;
1015 struct berval **extensions;
1016 struct berval **vals;
1017 int tmp, tmp_time, i, ret, have_arcfour = 0;
1018
1019 memset(ent, 0, sizeof(*ent));
1020 ent->entry.flags = int2HDBFlags(0);
1021
1022 ret = LDAP_get_string_value(db, msg, "krb5PrincipalName", &unparsed_name);
1023 if (ret == 0) {
1024 ret = krb5_parse_name(context, unparsed_name, &ent->entry.principal);
1025 if (ret)
1026 goto out;
1027 } else {
1028 ret = LDAP_get_string_value(db, msg, "uid",
1029 &unparsed_name);
1030 if (ret == 0) {
1031 ret = krb5_parse_name(context, unparsed_name, &ent->entry.principal);
1032 if (ret)
1033 goto out;
1034 } else {
1035 krb5_set_error_message(context, HDB_ERR_NOENTRY,
1036 "hdb-ldap: ldap entry missing"
1037 "principal name");
1038 return HDB_ERR_NOENTRY;
1039 }
1040 }
1041
1042 {
1043 int integer;
1044 ret = LDAP_get_integer_value(db, msg, "krb5KeyVersionNumber",
1045 &integer);
1046 if (ret)
1047 ent->entry.kvno = 0;
1048 else
1049 ent->entry.kvno = integer;
1050 }
1051
1052 keys = ldap_get_values_len(HDB2LDAP(db), msg, "krb5Key");
1053 if (keys != NULL) {
1054 size_t l;
1055
1056 ent->entry.keys.len = ldap_count_values_len(keys);
1057 ent->entry.keys.val = (Key *) calloc(ent->entry.keys.len, sizeof(Key));
1058 if (ent->entry.keys.val == NULL) {
1059 ret = ENOMEM;
1060 krb5_set_error_message(context, ret, "calloc: out of memory");
1061 goto out;
1062 }
1063 for (i = 0; i < ent->entry.keys.len; i++) {
1064 decode_Key((unsigned char *) keys[i]->bv_val,
1065 (size_t) keys[i]->bv_len, &ent->entry.keys.val[i], &l);
1066 }
1067 ber_bvecfree(keys);
1068 } else {
1069 #if 1
1070 /*
1071 * This violates the ASN1 but it allows a principal to
1072 * be related to a general directory entry without creating
1073 * the keys. Hopefully it's OK.
1074 */
1075 ent->entry.keys.len = 0;
1076 ent->entry.keys.val = NULL;
1077 #else
1078 ret = HDB_ERR_NOENTRY;
1079 goto out;
1080 #endif
1081 }
1082
1083 extensions = ldap_get_values_len(HDB2LDAP(db), msg, "krb5ExtendedAttributes");
1084 if (extensions != NULL) {
1085 size_t l;
1086
1087 ent->entry.extensions = calloc(1, sizeof(*(ent->entry.extensions)));
1088 if (ent->entry.extensions == NULL) {
1089 ret = krb5_enomem(context);
1090 goto out;
1091 }
1092 ent->entry.extensions->len = ldap_count_values_len(extensions);
1093 ent->entry.extensions->val = (HDB_extension *) calloc(ent->entry.extensions->len, sizeof(HDB_extension));
1094 if (ent->entry.extensions->val == NULL) {
1095 ent->entry.extensions->len = 0;
1096 ret = krb5_enomem(context);
1097 goto out;
1098 }
1099 for (i = 0; i < ent->entry.extensions->len; i++) {
1100 ret = decode_HDB_extension((unsigned char *) extensions[i]->bv_val,
1101 (size_t) extensions[i]->bv_len, &ent->entry.extensions->val[i], &l);
1102 if (ret)
1103 krb5_set_error_message(context, ret, "decode_HDB_extension failed");
1104 }
1105 ber_bvecfree(extensions);
1106 } else {
1107 ent->entry.extensions = NULL;
1108 }
1109
1110 vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5EncryptionType");
1111 if (vals != NULL) {
1112 ent->entry.etypes = malloc(sizeof(*(ent->entry.etypes)));
1113 if (ent->entry.etypes == NULL) {
1114 ret = ENOMEM;
1115 krb5_set_error_message(context, ret,"malloc: out of memory");
1116 goto out;
1117 }
1118 ent->entry.etypes->len = ldap_count_values_len(vals);
1119 ent->entry.etypes->val = calloc(ent->entry.etypes->len,
1120 sizeof(ent->entry.etypes->val[0]));
1121 if (ent->entry.etypes->val == NULL) {
1122 ret = ENOMEM;
1123 krb5_set_error_message(context, ret, "malloc: out of memory");
1124 ent->entry.etypes->len = 0;
1125 goto out;
1126 }
1127 for (i = 0; i < ent->entry.etypes->len; i++) {
1128 char *buf;
1129
1130 buf = malloc(vals[i]->bv_len + 1);
1131 if (buf == NULL) {
1132 ret = ENOMEM;
1133 krb5_set_error_message(context, ret, "malloc: out of memory");
1134 goto out;
1135 }
1136 memcpy(buf, vals[i]->bv_val, vals[i]->bv_len);
1137 buf[vals[i]->bv_len] = '\0';
1138 ent->entry.etypes->val[i] = atoi(buf);
1139 free(buf);
1140 }
1141 ldap_value_free_len(vals);
1142 }
1143
1144 for (i = 0; i < ent->entry.keys.len; i++) {
1145 if (ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) {
1146 have_arcfour = 1;
1147 break;
1148 }
1149 }
1150
1151 /* manually construct the NT (type 23) key */
1152 ret = LDAP_get_string_value(db, msg, "sambaNTPassword", &ntPasswordIN);
1153 if (ret == 0 && have_arcfour == 0) {
1154 unsigned *etypes;
1155 Key *ks;
1156
1157 ks = realloc(ent->entry.keys.val,
1158 (ent->entry.keys.len + 1) *
1159 sizeof(ent->entry.keys.val[0]));
1160 if (ks == NULL) {
1161 ret = ENOMEM;
1162 krb5_set_error_message(context, ret, "malloc: out of memory");
1163 goto out;
1164 }
1165 ent->entry.keys.val = ks;
1166 memset(&ent->entry.keys.val[ent->entry.keys.len], 0, sizeof(Key));
1167 ent->entry.keys.val[ent->entry.keys.len].key.keytype = ETYPE_ARCFOUR_HMAC_MD5;
1168 ret = krb5_data_alloc (&ent->entry.keys.val[ent->entry.keys.len].key.keyvalue, 16);
1169 if (ret) {
1170 krb5_set_error_message(context, ret, "malloc: out of memory");
1171 ret = ENOMEM;
1172 goto out;
1173 }
1174 ret = hex_decode(ntPasswordIN,
1175 ent->entry.keys.val[ent->entry.keys.len].key.keyvalue.data, 16);
1176 ent->entry.keys.len++;
1177
1178 if (ent->entry.etypes == NULL) {
1179 ent->entry.etypes = malloc(sizeof(*(ent->entry.etypes)));
1180 if (ent->entry.etypes == NULL) {
1181 ret = ENOMEM;
1182 krb5_set_error_message(context, ret, "malloc: out of memory");
1183 goto out;
1184 }
1185 ent->entry.etypes->val = NULL;
1186 ent->entry.etypes->len = 0;
1187 }
1188
1189 for (i = 0; i < ent->entry.etypes->len; i++)
1190 if (ent->entry.etypes->val[i] == ETYPE_ARCFOUR_HMAC_MD5)
1191 break;
1192 /* If there is no ARCFOUR enctype, add one */
1193 if (i == ent->entry.etypes->len) {
1194 etypes = realloc(ent->entry.etypes->val,
1195 (ent->entry.etypes->len + 1) *
1196 sizeof(ent->entry.etypes->val[0]));
1197 if (etypes == NULL) {
1198 ret = ENOMEM;
1199 krb5_set_error_message(context, ret, "malloc: out of memory");
1200 goto out;
1201 }
1202 ent->entry.etypes->val = etypes;
1203 ent->entry.etypes->val[ent->entry.etypes->len] =
1204 ETYPE_ARCFOUR_HMAC_MD5;
1205 ent->entry.etypes->len++;
1206 }
1207 }
1208
1209 ret = LDAP_get_generalized_time_value(db, msg, "createTimestamp",
1210 &ent->entry.created_by.time);
1211 if (ret)
1212 ent->entry.created_by.time = time(NULL);
1213
1214 ent->entry.created_by.principal = NULL;
1215
1216 if (flags & HDB_F_ADMIN_DATA) {
1217 ret = LDAP_get_string_value(db, msg, "creatorsName", &dn);
1218 if (ret == 0) {
1219 LDAP_dn2principal(context, db, dn, &ent->entry.created_by.principal);
1220 free(dn);
1221 }
1222
1223 ent->entry.modified_by = calloc(1, sizeof(*ent->entry.modified_by));
1224 if (ent->entry.modified_by == NULL) {
1225 ret = ENOMEM;
1226 krb5_set_error_message(context, ret, "malloc: out of memory");
1227 goto out;
1228 }
1229
1230 ret = LDAP_get_generalized_time_value(db, msg, "modifyTimestamp",
1231 &ent->entry.modified_by->time);
1232 if (ret == 0) {
1233 ret = LDAP_get_string_value(db, msg, "modifiersName", &dn);
1234 if (ret == 0) {
1235 LDAP_dn2principal(context, db, dn, &ent->entry.modified_by->principal);
1236 free(dn);
1237 } else {
1238 free(ent->entry.modified_by);
1239 ent->entry.modified_by = NULL;
1240 }
1241 }
1242 }
1243
1244 ent->entry.valid_start = malloc(sizeof(*ent->entry.valid_start));
1245 if (ent->entry.valid_start == NULL) {
1246 ret = ENOMEM;
1247 krb5_set_error_message(context, ret, "malloc: out of memory");
1248 goto out;
1249 }
1250 ret = LDAP_get_generalized_time_value(db, msg, "krb5ValidStart",
1251 ent->entry.valid_start);
1252 if (ret) {
1253 /* OPTIONAL */
1254 free(ent->entry.valid_start);
1255 ent->entry.valid_start = NULL;
1256 }
1257
1258 ent->entry.valid_end = malloc(sizeof(*ent->entry.valid_end));
1259 if (ent->entry.valid_end == NULL) {
1260 ret = ENOMEM;
1261 krb5_set_error_message(context, ret, "malloc: out of memory");
1262 goto out;
1263 }
1264 ret = LDAP_get_generalized_time_value(db, msg, "krb5ValidEnd",
1265 ent->entry.valid_end);
1266 if (ret) {
1267 /* OPTIONAL */
1268 free(ent->entry.valid_end);
1269 ent->entry.valid_end = NULL;
1270 }
1271
1272 ret = LDAP_get_integer_value(db, msg, "sambaKickoffTime", &tmp_time);
1273 if (ret == 0) {
1274 if (ent->entry.valid_end == NULL) {
1275 ent->entry.valid_end = malloc(sizeof(*ent->entry.valid_end));
1276 if (ent->entry.valid_end == NULL) {
1277 ret = ENOMEM;
1278 krb5_set_error_message(context, ret, "malloc: out of memory");
1279 goto out;
1280 }
1281 }
1282 *ent->entry.valid_end = tmp_time;
1283 }
1284
1285 ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end));
1286 if (ent->entry.pw_end == NULL) {
1287 ret = ENOMEM;
1288 krb5_set_error_message(context, ret, "malloc: out of memory");
1289 goto out;
1290 }
1291 ret = LDAP_get_generalized_time_value(db, msg, "krb5PasswordEnd",
1292 ent->entry.pw_end);
1293 if (ret) {
1294 /* OPTIONAL */
1295 free(ent->entry.pw_end);
1296 ent->entry.pw_end = NULL;
1297 }
1298
1299 ret = LDAP_get_integer_value(db, msg, "sambaPwdLastSet", &tmp_time);
1300 if (ret == 0) {
1301 time_t delta;
1302
1303 delta = krb5_config_get_time_default(context, NULL,
1304 0,
1305 "kadmin",
1306 "password_lifetime",
1307 NULL);
1308
1309 if (delta) {
1310 if (ent->entry.pw_end == NULL) {
1311 ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end));
1312 if (ent->entry.pw_end == NULL) {
1313 ret = ENOMEM;
1314 krb5_set_error_message(context, ret, "malloc: out of memory");
1315 goto out;
1316 }
1317 }
1318
1319 *ent->entry.pw_end = tmp_time + delta;
1320 }
1321 }
1322
1323 ret = LDAP_get_integer_value(db, msg, "sambaPwdMustChange", &tmp_time);
1324 if (ret == 0) {
1325 if (ent->entry.pw_end == NULL) {
1326 ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end));
1327 if (ent->entry.pw_end == NULL) {
1328 ret = ENOMEM;
1329 krb5_set_error_message(context, ret, "malloc: out of memory");
1330 goto out;
1331 }
1332 }
1333 *ent->entry.pw_end = tmp_time;
1334 }
1335
1336 /* OPTIONAL */
1337 ret = LDAP_get_integer_value(db, msg, "sambaPwdLastSet", &tmp_time);
1338 if (ret == 0)
1339 hdb_entry_set_pw_change_time(context, &ent->entry, tmp_time);
1340
1341 {
1342 int max_life;
1343
1344 ent->entry.max_life = malloc(sizeof(*ent->entry.max_life));
1345 if (ent->entry.max_life == NULL) {
1346 ret = ENOMEM;
1347 krb5_set_error_message(context, ret, "malloc: out of memory");
1348 goto out;
1349 }
1350 ret = LDAP_get_integer_value(db, msg, "krb5MaxLife", &max_life);
1351 if (ret) {
1352 free(ent->entry.max_life);
1353 ent->entry.max_life = NULL;
1354 } else
1355 *ent->entry.max_life = max_life;
1356 }
1357
1358 {
1359 int max_renew;
1360
1361 ent->entry.max_renew = malloc(sizeof(*ent->entry.max_renew));
1362 if (ent->entry.max_renew == NULL) {
1363 ret = ENOMEM;
1364 krb5_set_error_message(context, ret, "malloc: out of memory");
1365 goto out;
1366 }
1367 ret = LDAP_get_integer_value(db, msg, "krb5MaxRenew", &max_renew);
1368 if (ret) {
1369 free(ent->entry.max_renew);
1370 ent->entry.max_renew = NULL;
1371 } else
1372 *ent->entry.max_renew = max_renew;
1373 }
1374
1375 ret = LDAP_get_integer_value(db, msg, "krb5KDCFlags", &tmp);
1376 if (ret)
1377 tmp = 0;
1378
1379 ent->entry.flags = int2HDBFlags(tmp);
1380
1381 /* Try and find Samba flags to put into the mix */
1382 ret = LDAP_get_string_value(db, msg, "sambaAcctFlags", &samba_acct_flags);
1383 if (ret == 0) {
1384 /* parse the [UXW...] string:
1385
1386 'N' No password
1387 'D' Disabled
1388 'H' Homedir required
1389 'T' Temp account.
1390 'U' User account (normal)
1391 'M' MNS logon user account - what is this ?
1392 'W' Workstation account
1393 'S' Server account
1394 'L' Locked account
1395 'X' No Xpiry on password
1396 'I' Interdomain trust account
1397
1398 */
1399
1400 int flags_len = strlen(samba_acct_flags);
1401
1402 if (flags_len < 2)
1403 goto out2;
1404
1405 if (samba_acct_flags[0] != '['
1406 || samba_acct_flags[flags_len - 1] != ']')
1407 goto out2;
1408
1409 /* Allow forwarding */
1410 if (samba_forwardable)
1411 ent->entry.flags.forwardable = TRUE;
1412
1413 for (i=0; i < flags_len; i++) {
1414 switch (samba_acct_flags[i]) {
1415 case ' ':
1416 case '[':
1417 case ']':
1418 break;
1419 case 'N':
1420 /* how to handle no password in kerberos? */
1421 break;
1422 case 'D':
1423 ent->entry.flags.invalid = TRUE;
1424 break;
1425 case 'H':
1426 break;
1427 case 'T':
1428 /* temp duplicate */
1429 ent->entry.flags.invalid = TRUE;
1430 break;
1431 case 'U':
1432 ent->entry.flags.client = TRUE;
1433 break;
1434 case 'M':
1435 break;
1436 case 'W':
1437 case 'S':
1438 ent->entry.flags.server = TRUE;
1439 ent->entry.flags.client = TRUE;
1440 break;
1441 case 'L':
1442 ent->entry.flags.invalid = TRUE;
1443 break;
1444 case 'X':
1445 if (ent->entry.pw_end) {
1446 free(ent->entry.pw_end);
1447 ent->entry.pw_end = NULL;
1448 }
1449 break;
1450 case 'I':
1451 ent->entry.flags.server = TRUE;
1452 ent->entry.flags.client = TRUE;
1453 break;
1454 }
1455 }
1456 out2:
1457 free(samba_acct_flags);
1458 }
1459
1460 ret = 0;
1461
1462 out:
1463 free(unparsed_name);
1464 free(ntPasswordIN);
1465
1466 if (ret)
1467 hdb_free_entry(context, ent);
1468
1469 return ret;
1470 }
1471
1472 static krb5_error_code
LDAP_close(krb5_context context,HDB * db)1473 LDAP_close(krb5_context context, HDB * db)
1474 {
1475 if (HDB2LDAP(db)) {
1476 ldap_unbind_ext(HDB2LDAP(db), NULL, NULL);
1477 ((struct hdbldapdb *)db->hdb_db)->h_lp = NULL;
1478 }
1479
1480 return 0;
1481 }
1482
1483 static krb5_error_code
LDAP_lock(krb5_context context,HDB * db,int operation)1484 LDAP_lock(krb5_context context, HDB * db, int operation)
1485 {
1486 return 0;
1487 }
1488
1489 static krb5_error_code
LDAP_unlock(krb5_context context,HDB * db)1490 LDAP_unlock(krb5_context context, HDB * db)
1491 {
1492 return 0;
1493 }
1494
1495 static krb5_error_code
LDAP_seq(krb5_context context,HDB * db,unsigned flags,hdb_entry_ex * entry)1496 LDAP_seq(krb5_context context, HDB * db, unsigned flags, hdb_entry_ex * entry)
1497 {
1498 int msgid, rc, parserc;
1499 krb5_error_code ret;
1500 LDAPMessage *e;
1501
1502 msgid = HDB2MSGID(db);
1503 if (msgid < 0)
1504 return HDB_ERR_NOENTRY;
1505
1506 do {
1507 rc = ldap_result(HDB2LDAP(db), msgid, LDAP_MSG_ONE, NULL, &e);
1508 switch (rc) {
1509 case LDAP_RES_SEARCH_REFERENCE:
1510 ldap_msgfree(e);
1511 ret = 0;
1512 break;
1513 case LDAP_RES_SEARCH_ENTRY:
1514 /* We have an entry. Parse it. */
1515 ret = LDAP_message2entry(context, db, e, flags, entry);
1516 ldap_msgfree(e);
1517 break;
1518 case LDAP_RES_SEARCH_RESULT:
1519 /* We're probably at the end of the results. If not, abandon. */
1520 parserc =
1521 ldap_parse_result(HDB2LDAP(db), e, NULL, NULL, NULL,
1522 NULL, NULL, 1);
1523 ret = HDB_ERR_NOENTRY;
1524 if (parserc != LDAP_SUCCESS
1525 && parserc != LDAP_MORE_RESULTS_TO_RETURN) {
1526 krb5_set_error_message(context, ret, "ldap_parse_result: %s",
1527 ldap_err2string(parserc));
1528 ldap_abandon_ext(HDB2LDAP(db), msgid, NULL, NULL);
1529 }
1530 HDBSETMSGID(db, -1);
1531 break;
1532 case LDAP_SERVER_DOWN:
1533 ldap_msgfree(e);
1534 LDAP_close(context, db);
1535 HDBSETMSGID(db, -1);
1536 ret = ENETDOWN;
1537 break;
1538 default:
1539 /* Some unspecified error (timeout?). Abandon. */
1540 ldap_msgfree(e);
1541 ldap_abandon_ext(HDB2LDAP(db), msgid, NULL, NULL);
1542 ret = HDB_ERR_NOENTRY;
1543 HDBSETMSGID(db, -1);
1544 break;
1545 }
1546 } while (rc == LDAP_RES_SEARCH_REFERENCE);
1547
1548 if (ret == 0) {
1549 if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
1550 ret = hdb_unseal_keys(context, db, &entry->entry);
1551 if (ret)
1552 hdb_free_entry(context, entry);
1553 }
1554 }
1555
1556 return ret;
1557 }
1558
1559 static krb5_error_code
LDAP_firstkey(krb5_context context,HDB * db,unsigned flags,hdb_entry_ex * entry)1560 LDAP_firstkey(krb5_context context, HDB *db, unsigned flags,
1561 hdb_entry_ex *entry)
1562 {
1563 krb5_error_code ret;
1564 int msgid;
1565
1566 ret = LDAP__connect(context, db);
1567 if (ret)
1568 return ret;
1569
1570 ret = LDAP_no_size_limit(context, HDB2LDAP(db));
1571 if (ret)
1572 return ret;
1573
1574 ret = ldap_search_ext(HDB2LDAP(db), HDB2BASE(db),
1575 LDAP_SCOPE_SUBTREE,
1576 "(|(objectClass=krb5Principal)(objectClass=sambaSamAccount))",
1577 krb5kdcentry_attrs, 0,
1578 NULL, NULL, NULL, 0, &msgid);
1579 if (ret != LDAP_SUCCESS || msgid < 0)
1580 return HDB_ERR_NOENTRY;
1581
1582 HDBSETMSGID(db, msgid);
1583
1584 return LDAP_seq(context, db, flags, entry);
1585 }
1586
1587 static krb5_error_code
LDAP_nextkey(krb5_context context,HDB * db,unsigned flags,hdb_entry_ex * entry)1588 LDAP_nextkey(krb5_context context, HDB * db, unsigned flags,
1589 hdb_entry_ex * entry)
1590 {
1591 return LDAP_seq(context, db, flags, entry);
1592 }
1593
1594 static krb5_error_code
LDAP__connect(krb5_context context,HDB * db)1595 LDAP__connect(krb5_context context, HDB * db)
1596 {
1597 int rc, version = LDAP_VERSION3;
1598 /*
1599 * Empty credentials to do a SASL bind with LDAP. Note that empty
1600 * different from NULL credentials. If you provide NULL
1601 * credentials instead of empty credentials you will get a SASL
1602 * bind in progress message.
1603 */
1604 struct berval bv = { 0, "" };
1605 const char *sasl_method = "EXTERNAL";
1606 const char *bind_dn = NULL;
1607
1608 if (HDB2BINDDN(db) != NULL && HDB2BINDPW(db) != NULL) {
1609 /* A bind DN was specified; use SASL SIMPLE */
1610 bind_dn = HDB2BINDDN(db);
1611 sasl_method = LDAP_SASL_SIMPLE;
1612 bv.bv_val = HDB2BINDPW(db);
1613 bv.bv_len = strlen(bv.bv_val);
1614 }
1615
1616 if (HDB2LDAP(db)) {
1617 /* connection has been opened. ping server. */
1618 struct sockaddr_un addr;
1619 socklen_t len = sizeof(addr);
1620 int sd;
1621
1622 if (ldap_get_option(HDB2LDAP(db), LDAP_OPT_DESC, &sd) == 0 &&
1623 getpeername(sd, (struct sockaddr *) &addr, &len) < 0) {
1624 /* the other end has died. reopen. */
1625 LDAP_close(context, db);
1626 }
1627 }
1628
1629 if (HDB2LDAP(db) != NULL) /* server is UP */
1630 return 0;
1631
1632 rc = ldap_initialize(&((struct hdbldapdb *)db->hdb_db)->h_lp, HDB2URL(db));
1633 if (rc != LDAP_SUCCESS) {
1634 krb5_set_error_message(context, HDB_ERR_NOENTRY, "ldap_initialize: %s",
1635 ldap_err2string(rc));
1636 return HDB_ERR_NOENTRY;
1637 }
1638
1639 rc = ldap_set_option(HDB2LDAP(db), LDAP_OPT_PROTOCOL_VERSION,
1640 (const void *)&version);
1641 if (rc != LDAP_SUCCESS) {
1642 krb5_set_error_message(context, HDB_ERR_BADVERSION,
1643 "ldap_set_option: %s", ldap_err2string(rc));
1644 LDAP_close(context, db);
1645 return HDB_ERR_BADVERSION;
1646 }
1647
1648 if (((struct hdbldapdb *)db->hdb_db)->h_start_tls) {
1649 rc = ldap_start_tls_s(HDB2LDAP(db), NULL, NULL);
1650
1651 if (rc != LDAP_SUCCESS) {
1652 krb5_set_error_message(context, HDB_ERR_BADVERSION,
1653 "ldap_start_tls_s: %s", ldap_err2string(rc));
1654 LDAP_close(context, db);
1655 return HDB_ERR_BADVERSION;
1656 }
1657 }
1658
1659 rc = ldap_sasl_bind_s(HDB2LDAP(db), bind_dn, sasl_method, &bv,
1660 NULL, NULL, NULL);
1661 if (rc != LDAP_SUCCESS) {
1662 krb5_set_error_message(context, HDB_ERR_BADVERSION,
1663 "ldap_sasl_bind_s: %s", ldap_err2string(rc));
1664 LDAP_close(context, db);
1665 return HDB_ERR_BADVERSION;
1666 }
1667
1668 return 0;
1669 }
1670
1671 static krb5_error_code
LDAP_open(krb5_context context,HDB * db,int flags,mode_t mode)1672 LDAP_open(krb5_context context, HDB * db, int flags, mode_t mode)
1673 {
1674 /* Not the right place for this. */
1675 #ifdef HAVE_SIGACTION
1676 struct sigaction sa;
1677
1678 sa.sa_flags = 0;
1679 sa.sa_handler = SIG_IGN;
1680 sigemptyset(&sa.sa_mask);
1681
1682 sigaction(SIGPIPE, &sa, NULL);
1683 #else
1684 signal(SIGPIPE, SIG_IGN);
1685 #endif /* HAVE_SIGACTION */
1686
1687 return LDAP__connect(context, db);
1688 }
1689
1690 static krb5_error_code
LDAP_fetch_kvno(krb5_context context,HDB * db,krb5_const_principal principal,unsigned flags,krb5_kvno kvno,hdb_entry_ex * entry)1691 LDAP_fetch_kvno(krb5_context context, HDB * db, krb5_const_principal principal,
1692 unsigned flags, krb5_kvno kvno, hdb_entry_ex * entry)
1693 {
1694 LDAPMessage *msg, *e;
1695 krb5_error_code ret;
1696
1697 ret = LDAP_principal2message(context, db, principal, &msg);
1698 if (ret)
1699 return ret;
1700
1701 e = ldap_first_entry(HDB2LDAP(db), msg);
1702 if (e == NULL) {
1703 ret = HDB_ERR_NOENTRY;
1704 goto out;
1705 }
1706
1707 ret = LDAP_message2entry(context, db, e, flags, entry);
1708 if (ret == 0) {
1709 if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
1710 ret = hdb_unseal_keys(context, db, &entry->entry);
1711 if (ret)
1712 hdb_free_entry(context, entry);
1713 }
1714 }
1715
1716 out:
1717 ldap_msgfree(msg);
1718
1719 return ret;
1720 }
1721
1722 #if 0
1723 static krb5_error_code
1724 LDAP_fetch(krb5_context context, HDB * db, krb5_const_principal principal,
1725 unsigned flags, hdb_entry_ex * entry)
1726 {
1727 return LDAP_fetch_kvno(context, db, principal,
1728 flags & (~HDB_F_KVNO_SPECIFIED), 0, entry);
1729 }
1730 #endif
1731
1732 static krb5_error_code
LDAP_store(krb5_context context,HDB * db,unsigned flags,hdb_entry_ex * entry)1733 LDAP_store(krb5_context context, HDB * db, unsigned flags,
1734 hdb_entry_ex * entry)
1735 {
1736 LDAPMod **mods = NULL;
1737 krb5_error_code ret;
1738 const char *errfn;
1739 int rc;
1740 LDAPMessage *msg = NULL, *e = NULL;
1741 char *dn = NULL, *name = NULL;
1742
1743 if ((flags & HDB_F_PRECHECK))
1744 return 0; /* we can't guarantee whether we'll be able to perform it */
1745
1746 ret = LDAP_principal2message(context, db, entry->entry.principal, &msg);
1747 if (ret == 0)
1748 e = ldap_first_entry(HDB2LDAP(db), msg);
1749
1750 ret = krb5_unparse_name(context, entry->entry.principal, &name);
1751 if (ret) {
1752 free(name);
1753 return ret;
1754 }
1755
1756 ret = hdb_seal_keys(context, db, &entry->entry);
1757 if (ret)
1758 goto out;
1759
1760 /* turn new entry into LDAPMod array */
1761 ret = LDAP_entry2mods(context, db, entry, e, &mods);
1762 if (ret)
1763 goto out;
1764
1765 if (e == NULL) {
1766 ret = asprintf(&dn, "krb5PrincipalName=%s,%s", name, HDB2CREATE(db));
1767 if (ret < 0) {
1768 ret = ENOMEM;
1769 krb5_set_error_message(context, ret, "asprintf: out of memory");
1770 goto out;
1771 }
1772 } else if (flags & HDB_F_REPLACE) {
1773 /* Entry exists, and we're allowed to replace it. */
1774 dn = ldap_get_dn(HDB2LDAP(db), e);
1775 } else {
1776 /* Entry exists, but we're not allowed to replace it. Bail. */
1777 ret = HDB_ERR_EXISTS;
1778 goto out;
1779 }
1780
1781 /* write entry into directory */
1782 if (e == NULL) {
1783 /* didn't exist before */
1784 rc = ldap_add_ext_s(HDB2LDAP(db), dn, mods, NULL, NULL );
1785 errfn = "ldap_add_ext_s";
1786 } else {
1787 /* already existed, send deltas only */
1788 rc = ldap_modify_ext_s(HDB2LDAP(db), dn, mods, NULL, NULL );
1789 errfn = "ldap_modify_ext_s";
1790 }
1791
1792 if (check_ldap(context, db, rc)) {
1793 char *ld_error = NULL;
1794 ldap_get_option(HDB2LDAP(db), LDAP_OPT_ERROR_STRING,
1795 &ld_error);
1796 ret = HDB_ERR_CANT_LOCK_DB;
1797 krb5_set_error_message(context, ret, "%s: %s (DN=%s) %s: %s",
1798 errfn, name, dn, ldap_err2string(rc), ld_error);
1799 } else
1800 ret = 0;
1801
1802 out:
1803 /* free stuff */
1804 if (dn)
1805 free(dn);
1806 if (msg)
1807 ldap_msgfree(msg);
1808 if (mods)
1809 ldap_mods_free(mods, 1);
1810 if (name)
1811 free(name);
1812
1813 return ret;
1814 }
1815
1816 static krb5_error_code
LDAP_remove(krb5_context context,HDB * db,unsigned flags,krb5_const_principal principal)1817 LDAP_remove(krb5_context context, HDB *db,
1818 unsigned flags, krb5_const_principal principal)
1819 {
1820 krb5_error_code ret;
1821 LDAPMessage *msg, *e;
1822 char *dn = NULL;
1823 int rc, limit = LDAP_NO_LIMIT;
1824
1825 if ((flags & HDB_F_PRECHECK))
1826 return 0; /* we can't guarantee whether we'll be able to perform it */
1827
1828 ret = LDAP_principal2message(context, db, principal, &msg);
1829 if (ret)
1830 goto out;
1831
1832 e = ldap_first_entry(HDB2LDAP(db), msg);
1833 if (e == NULL) {
1834 ret = HDB_ERR_NOENTRY;
1835 goto out;
1836 }
1837
1838 dn = ldap_get_dn(HDB2LDAP(db), e);
1839 if (dn == NULL) {
1840 ret = HDB_ERR_NOENTRY;
1841 goto out;
1842 }
1843
1844 rc = ldap_set_option(HDB2LDAP(db), LDAP_OPT_SIZELIMIT, (const void *)&limit);
1845 if (rc != LDAP_SUCCESS) {
1846 ret = HDB_ERR_BADVERSION;
1847 krb5_set_error_message(context, ret, "ldap_set_option: %s",
1848 ldap_err2string(rc));
1849 goto out;
1850 }
1851
1852 rc = ldap_delete_ext_s(HDB2LDAP(db), dn, NULL, NULL );
1853 if (check_ldap(context, db, rc)) {
1854 ret = HDB_ERR_CANT_LOCK_DB;
1855 krb5_set_error_message(context, ret, "ldap_delete_ext_s: %s",
1856 ldap_err2string(rc));
1857 } else
1858 ret = 0;
1859
1860 out:
1861 if (dn != NULL)
1862 free(dn);
1863 if (msg != NULL)
1864 ldap_msgfree(msg);
1865
1866 return ret;
1867 }
1868
1869 static krb5_error_code
LDAP_destroy(krb5_context context,HDB * db)1870 LDAP_destroy(krb5_context context, HDB * db)
1871 {
1872 krb5_error_code ret;
1873
1874 LDAP_close(context, db);
1875
1876 ret = hdb_clear_master_key(context, db);
1877 if (HDB2BASE(db))
1878 free(HDB2BASE(db));
1879 if (HDB2CREATE(db))
1880 free(HDB2CREATE(db));
1881 if (HDB2URL(db))
1882 free(HDB2URL(db));
1883 if (db->hdb_name)
1884 free(db->hdb_name);
1885 free(db->hdb_db);
1886 free(db);
1887
1888 return ret;
1889 }
1890
1891 static krb5_error_code
LDAP_set_sync(krb5_context context,HDB * db,int on)1892 LDAP_set_sync(krb5_context context, HDB * db, int on)
1893 {
1894 (void)on;
1895 return 0;
1896 }
1897
1898 static krb5_error_code
hdb_ldap_common(krb5_context context,HDB ** db,const char * search_base,const char * url)1899 hdb_ldap_common(krb5_context context,
1900 HDB ** db,
1901 const char *search_base,
1902 const char *url)
1903 {
1904 struct hdbldapdb *h;
1905 const char *create_base = NULL;
1906 const char *ldap_secret_file = NULL;
1907
1908 if (url == NULL || url[0] == '\0') {
1909 const char *p;
1910 p = krb5_config_get_string(context, NULL, "kdc",
1911 "hdb-ldap-url", NULL);
1912 if (p == NULL)
1913 p = default_ldap_url;
1914
1915 url = p;
1916 }
1917
1918 if (search_base == NULL || search_base[0] == '\0') {
1919 krb5_set_error_message(context, ENOMEM, "ldap search base not configured");
1920 return ENOMEM; /* XXX */
1921 }
1922
1923 if (structural_object == NULL) {
1924 const char *p;
1925
1926 p = krb5_config_get_string(context, NULL, "kdc",
1927 "hdb-ldap-structural-object", NULL);
1928 if (p == NULL)
1929 p = default_structural_object;
1930 structural_object = strdup(p);
1931 if (structural_object == NULL) {
1932 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
1933 return ENOMEM;
1934 }
1935 }
1936
1937 samba_forwardable =
1938 krb5_config_get_bool_default(context, NULL, TRUE,
1939 "kdc", "hdb-samba-forwardable", NULL);
1940
1941 *db = calloc(1, sizeof(**db));
1942 if (*db == NULL) {
1943 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
1944 return ENOMEM;
1945 }
1946 memset(*db, 0, sizeof(**db));
1947
1948 h = calloc(1, sizeof(*h));
1949 if (h == NULL) {
1950 free(*db);
1951 *db = NULL;
1952 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
1953 return ENOMEM;
1954 }
1955 (*db)->hdb_db = h;
1956
1957 /* XXX */
1958 if (asprintf(&(*db)->hdb_name, "ldap:%s", search_base) == -1) {
1959 LDAP_destroy(context, *db);
1960 *db = NULL;
1961 krb5_set_error_message(context, ENOMEM, "strdup: out of memory");
1962 return ENOMEM;
1963 }
1964
1965 h->h_url = strdup(url);
1966 h->h_base = strdup(search_base);
1967 if (h->h_url == NULL || h->h_base == NULL) {
1968 LDAP_destroy(context, *db);
1969 *db = NULL;
1970 krb5_set_error_message(context, ENOMEM, "strdup: out of memory");
1971 return ENOMEM;
1972 }
1973
1974 ldap_secret_file = krb5_config_get_string(context, NULL, "kdc",
1975 "hdb-ldap-secret-file", NULL);
1976 if (ldap_secret_file != NULL) {
1977 krb5_config_binding *tmp;
1978 krb5_error_code ret;
1979 const char *p;
1980
1981 ret = krb5_config_parse_file(context, ldap_secret_file, &tmp);
1982 if (ret)
1983 return ret;
1984
1985 p = krb5_config_get_string(context, tmp, "kdc",
1986 "hdb-ldap-bind-dn", NULL);
1987 if (p != NULL)
1988 h->h_bind_dn = strdup(p);
1989
1990 p = krb5_config_get_string(context, tmp, "kdc",
1991 "hdb-ldap-bind-password", NULL);
1992 if (p != NULL)
1993 h->h_bind_password = strdup(p);
1994
1995 krb5_config_file_free(context, tmp);
1996 }
1997
1998 h->h_start_tls =
1999 krb5_config_get_bool_default(context, NULL, FALSE,
2000 "kdc", "hdb-ldap-start-tls", NULL);
2001
2002 create_base = krb5_config_get_string(context, NULL, "kdc",
2003 "hdb-ldap-create-base", NULL);
2004 if (create_base == NULL)
2005 create_base = h->h_base;
2006
2007 h->h_createbase = strdup(create_base);
2008 if (h->h_createbase == NULL) {
2009 LDAP_destroy(context, *db);
2010 *db = NULL;
2011 krb5_set_error_message(context, ENOMEM, "strdup: out of memory");
2012 return ENOMEM;
2013 }
2014
2015 (*db)->hdb_master_key_set = 0;
2016 (*db)->hdb_openp = 0;
2017 (*db)->hdb_capability_flags = HDB_CAP_F_SHARED_DIRECTORY;
2018 (*db)->hdb_open = LDAP_open;
2019 (*db)->hdb_close = LDAP_close;
2020 (*db)->hdb_fetch_kvno = LDAP_fetch_kvno;
2021 (*db)->hdb_store = LDAP_store;
2022 (*db)->hdb_remove = LDAP_remove;
2023 (*db)->hdb_firstkey = LDAP_firstkey;
2024 (*db)->hdb_nextkey = LDAP_nextkey;
2025 (*db)->hdb_lock = LDAP_lock;
2026 (*db)->hdb_unlock = LDAP_unlock;
2027 (*db)->hdb_rename = NULL;
2028 (*db)->hdb__get = NULL;
2029 (*db)->hdb__put = NULL;
2030 (*db)->hdb__del = NULL;
2031 (*db)->hdb_destroy = LDAP_destroy;
2032 (*db)->hdb_set_sync = LDAP_set_sync;
2033
2034 return 0;
2035 }
2036
2037 #ifdef OPENLDAP_MODULE
2038 static
2039 #endif
2040
2041 krb5_error_code
hdb_ldap_create(krb5_context context,HDB ** db,const char * arg)2042 hdb_ldap_create(krb5_context context, HDB ** db, const char *arg)
2043 {
2044 return hdb_ldap_common(context, db, arg, NULL);
2045 }
2046
2047 #ifdef OPENLDAP_MODULE
2048 static
2049 #endif
2050
2051 krb5_error_code
hdb_ldapi_create(krb5_context context,HDB ** db,const char * arg)2052 hdb_ldapi_create(krb5_context context, HDB ** db, const char *arg)
2053 {
2054 krb5_error_code ret;
2055 char *search_base, *p;
2056
2057 if (asprintf(&p, "ldapi:%s", arg) == -1 || p == NULL) {
2058 *db = NULL;
2059 krb5_set_error_message(context, ENOMEM, "out of memory");
2060 return ENOMEM;
2061 }
2062 search_base = strchr(p + strlen("ldapi://"), ':');
2063 if (search_base == NULL) {
2064 *db = NULL;
2065 krb5_set_error_message(context, HDB_ERR_BADVERSION,
2066 "search base missing");
2067 return HDB_ERR_BADVERSION;
2068 }
2069 *search_base = '\0';
2070 search_base++;
2071
2072 ret = hdb_ldap_common(context, db, search_base, p);
2073 free(p);
2074 return ret;
2075 }
2076
2077 #ifdef OPENLDAP_MODULE
2078 static krb5_error_code
init(krb5_context context,void ** ctx)2079 init(krb5_context context, void **ctx)
2080 {
2081 *ctx = NULL;
2082 return 0;
2083 }
2084
2085 static void
fini(void * ctx)2086 fini(void *ctx)
2087 {
2088 }
2089
2090 struct hdb_method hdb_ldap_interface = {
2091 HDB_INTERFACE_VERSION,
2092 init,
2093 fini,
2094 "ldap",
2095 hdb_ldap_create
2096 };
2097
2098 struct hdb_method hdb_ldapi_interface = {
2099 HDB_INTERFACE_VERSION,
2100 init,
2101 fini,
2102 "ldapi",
2103 hdb_ldapi_create
2104 };
2105 #endif
2106
2107 #endif /* OPENLDAP */
2108