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