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