Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/heimdal/lib/hdb/hdb-ldap.c
34878 views
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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, &quote);
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, &quote);
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
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
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
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
1424
LDAP_lock(krb5_context context, HDB * db, int operation)
1425
{
1426
return 0;
1427
}
1428
1429
static krb5_error_code
1430
LDAP_unlock(krb5_context context, HDB * db)
1431
{
1432
return 0;
1433
}
1434
1435
static krb5_error_code
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
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
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
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
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
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
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
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
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
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
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
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
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
1951