/src/samba/source3/lib/smbldap.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | LDAP protocol helper functions for SAMBA |
4 | | Copyright (C) Jean François Micouleau 1998 |
5 | | Copyright (C) Gerald Carter 2001-2003 |
6 | | Copyright (C) Shahms King 2001 |
7 | | Copyright (C) Andrew Bartlett 2002-2003 |
8 | | Copyright (C) Stefan (metze) Metzmacher 2002-2003 |
9 | | |
10 | | This program is free software; you can redistribute it and/or modify |
11 | | it under the terms of the GNU General Public License as published by |
12 | | the Free Software Foundation; either version 3 of the License, or |
13 | | (at your option) any later version. |
14 | | |
15 | | This program is distributed in the hope that it will be useful, |
16 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | GNU General Public License for more details. |
19 | | |
20 | | You should have received a copy of the GNU General Public License |
21 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
22 | | |
23 | | */ |
24 | | |
25 | | #include "includes.h" |
26 | | #include "smbldap.h" |
27 | | #include "../libcli/security/security.h" |
28 | | #include <tevent.h> |
29 | | #include "lib/param/loadparm.h" |
30 | | |
31 | | /* Try not to hit the up or down server forever */ |
32 | | |
33 | 0 | #define SMBLDAP_DONT_PING_TIME 10 /* ping only all 10 seconds */ |
34 | | #define SMBLDAP_NUM_RETRIES 8 /* retry only 8 times */ |
35 | | |
36 | 0 | #define SMBLDAP_IDLE_TIME 150 /* After 2.5 minutes disconnect */ |
37 | | |
38 | | struct smbldap_state { |
39 | | LDAP *ldap_struct; |
40 | | pid_t pid; |
41 | | time_t last_ping; /* monotonic */ |
42 | | /* retrieve-once info */ |
43 | | const char *uri; |
44 | | |
45 | | /* credentials */ |
46 | | bool anonymous; |
47 | | char *bind_dn; |
48 | | char *bind_secret; |
49 | | smbldap_bind_callback_fn bind_callback; |
50 | | void *bind_callback_data; |
51 | | |
52 | | bool paged_results; |
53 | | |
54 | | unsigned int num_failures; |
55 | | |
56 | | time_t last_use; /* monotonic */ |
57 | | struct tevent_context *tevent_context; |
58 | | struct tevent_timer *idle_event; |
59 | | |
60 | | struct timeval last_rebind; /* monotonic */ |
61 | | }; |
62 | | |
63 | | LDAP *smbldap_get_ldap(struct smbldap_state *state) |
64 | 0 | { |
65 | 0 | return state->ldap_struct; |
66 | 0 | } |
67 | | |
68 | | bool smbldap_get_paged_results(struct smbldap_state *state) |
69 | 0 | { |
70 | 0 | return state->paged_results; |
71 | 0 | } |
72 | | |
73 | | void smbldap_set_paged_results(struct smbldap_state *state, |
74 | | bool paged_results) |
75 | 0 | { |
76 | 0 | state->paged_results = paged_results; |
77 | 0 | } |
78 | | |
79 | | void smbldap_set_bind_callback(struct smbldap_state *state, |
80 | | smbldap_bind_callback_fn callback, |
81 | | void *callback_data) |
82 | 0 | { |
83 | 0 | state->bind_callback = callback; |
84 | 0 | state->bind_callback_data = callback_data; |
85 | 0 | } |
86 | | /******************************************************************* |
87 | | Search an attribute and return the first value found. |
88 | | ******************************************************************/ |
89 | | |
90 | | bool smbldap_get_single_attribute (LDAP * ldap_struct, LDAPMessage * entry, |
91 | | const char *attribute, char *value, |
92 | | int max_len) |
93 | 0 | { |
94 | 0 | char **values; |
95 | 0 | size_t size = 0; |
96 | |
|
97 | 0 | if ( !attribute ) |
98 | 0 | return False; |
99 | | |
100 | 0 | value[0] = '\0'; |
101 | |
|
102 | 0 | if ((values = ldap_get_values (ldap_struct, entry, attribute)) == NULL) { |
103 | 0 | DEBUG (10, ("smbldap_get_single_attribute: [%s] = [<does not exist>]\n", attribute)); |
104 | |
|
105 | 0 | return False; |
106 | 0 | } |
107 | | |
108 | 0 | if (!convert_string(CH_UTF8, CH_UNIX,values[0], -1, value, max_len, &size)) { |
109 | 0 | DEBUG(1, ("smbldap_get_single_attribute: string conversion of [%s] = [%s] failed!\n", |
110 | 0 | attribute, values[0])); |
111 | 0 | ldap_value_free(values); |
112 | 0 | return False; |
113 | 0 | } |
114 | | |
115 | 0 | ldap_value_free(values); |
116 | 0 | #ifdef DEBUG_PASSWORD |
117 | 0 | DEBUG (100, ("smbldap_get_single_attribute: [%s] = [%s]\n", attribute, value)); |
118 | 0 | #endif |
119 | 0 | return True; |
120 | 0 | } |
121 | | |
122 | | char * smbldap_talloc_single_attribute(LDAP *ldap_struct, LDAPMessage *entry, |
123 | | const char *attribute, |
124 | | TALLOC_CTX *mem_ctx) |
125 | 0 | { |
126 | 0 | char **values; |
127 | 0 | char *result; |
128 | 0 | size_t converted_size; |
129 | |
|
130 | 0 | if (attribute == NULL) { |
131 | 0 | return NULL; |
132 | 0 | } |
133 | | |
134 | 0 | values = ldap_get_values(ldap_struct, entry, attribute); |
135 | |
|
136 | 0 | if (values == NULL) { |
137 | 0 | DEBUG(10, ("attribute %s does not exist\n", attribute)); |
138 | 0 | return NULL; |
139 | 0 | } |
140 | | |
141 | 0 | if (ldap_count_values(values) != 1) { |
142 | 0 | DEBUG(10, ("attribute %s has %d values, expected only one\n", |
143 | 0 | attribute, ldap_count_values(values))); |
144 | 0 | ldap_value_free(values); |
145 | 0 | return NULL; |
146 | 0 | } |
147 | | |
148 | 0 | if (!pull_utf8_talloc(mem_ctx, &result, values[0], &converted_size)) { |
149 | 0 | DEBUG(10, ("pull_utf8_talloc failed\n")); |
150 | 0 | ldap_value_free(values); |
151 | 0 | return NULL; |
152 | 0 | } |
153 | | |
154 | 0 | ldap_value_free(values); |
155 | |
|
156 | 0 | #ifdef DEBUG_PASSWORD |
157 | 0 | DEBUG (100, ("smbldap_get_single_attribute: [%s] = [%s]\n", |
158 | 0 | attribute, result)); |
159 | 0 | #endif |
160 | 0 | return result; |
161 | 0 | } |
162 | | |
163 | | char * smbldap_talloc_first_attribute(LDAP *ldap_struct, LDAPMessage *entry, |
164 | | const char *attribute, |
165 | | TALLOC_CTX *mem_ctx) |
166 | 0 | { |
167 | 0 | char **values; |
168 | 0 | char *result; |
169 | 0 | size_t converted_size; |
170 | |
|
171 | 0 | if (attribute == NULL) { |
172 | 0 | return NULL; |
173 | 0 | } |
174 | | |
175 | 0 | values = ldap_get_values(ldap_struct, entry, attribute); |
176 | |
|
177 | 0 | if (values == NULL) { |
178 | 0 | DEBUG(10, ("attribute %s does not exist\n", attribute)); |
179 | 0 | return NULL; |
180 | 0 | } |
181 | | |
182 | 0 | if (!pull_utf8_talloc(mem_ctx, &result, values[0], &converted_size)) { |
183 | 0 | DEBUG(10, ("pull_utf8_talloc failed\n")); |
184 | 0 | ldap_value_free(values); |
185 | 0 | return NULL; |
186 | 0 | } |
187 | | |
188 | 0 | ldap_value_free(values); |
189 | |
|
190 | 0 | #ifdef DEBUG_PASSWORD |
191 | 0 | DEBUG (100, ("smbldap_get_first_attribute: [%s] = [%s]\n", |
192 | 0 | attribute, result)); |
193 | 0 | #endif |
194 | 0 | return result; |
195 | 0 | } |
196 | | |
197 | | char * smbldap_talloc_smallest_attribute(LDAP *ldap_struct, LDAPMessage *entry, |
198 | | const char *attribute, |
199 | | TALLOC_CTX *mem_ctx) |
200 | 0 | { |
201 | 0 | char **values; |
202 | 0 | char *result; |
203 | 0 | size_t converted_size; |
204 | 0 | int i, num_values; |
205 | |
|
206 | 0 | if (attribute == NULL) { |
207 | 0 | return NULL; |
208 | 0 | } |
209 | | |
210 | 0 | values = ldap_get_values(ldap_struct, entry, attribute); |
211 | |
|
212 | 0 | if (values == NULL) { |
213 | 0 | DEBUG(10, ("attribute %s does not exist\n", attribute)); |
214 | 0 | return NULL; |
215 | 0 | } |
216 | | |
217 | 0 | if (!pull_utf8_talloc(mem_ctx, &result, values[0], &converted_size)) { |
218 | 0 | DEBUG(10, ("pull_utf8_talloc failed\n")); |
219 | 0 | ldap_value_free(values); |
220 | 0 | return NULL; |
221 | 0 | } |
222 | | |
223 | 0 | num_values = ldap_count_values(values); |
224 | |
|
225 | 0 | for (i=1; i<num_values; i++) { |
226 | 0 | char *tmp; |
227 | |
|
228 | 0 | if (!pull_utf8_talloc(mem_ctx, &tmp, values[i], |
229 | 0 | &converted_size)) { |
230 | 0 | DEBUG(10, ("pull_utf8_talloc failed\n")); |
231 | 0 | TALLOC_FREE(result); |
232 | 0 | ldap_value_free(values); |
233 | 0 | return NULL; |
234 | 0 | } |
235 | | |
236 | 0 | if (strcasecmp_m(tmp, result) < 0) { |
237 | 0 | TALLOC_FREE(result); |
238 | 0 | result = tmp; |
239 | 0 | } else { |
240 | 0 | TALLOC_FREE(tmp); |
241 | 0 | } |
242 | 0 | } |
243 | | |
244 | 0 | ldap_value_free(values); |
245 | |
|
246 | 0 | #ifdef DEBUG_PASSWORD |
247 | 0 | DEBUG (100, ("smbldap_get_single_attribute: [%s] = [%s]\n", |
248 | 0 | attribute, result)); |
249 | 0 | #endif |
250 | 0 | return result; |
251 | 0 | } |
252 | | |
253 | | bool smbldap_talloc_single_blob(TALLOC_CTX *mem_ctx, LDAP *ld, |
254 | | LDAPMessage *msg, const char *attrib, |
255 | | DATA_BLOB *blob) |
256 | 0 | { |
257 | 0 | struct berval **values; |
258 | |
|
259 | 0 | values = ldap_get_values_len(ld, msg, attrib); |
260 | 0 | if (!values) { |
261 | 0 | return false; |
262 | 0 | } |
263 | | |
264 | 0 | if (ldap_count_values_len(values) != 1) { |
265 | 0 | DEBUG(10, ("Expected one value for %s, got %d\n", attrib, |
266 | 0 | ldap_count_values_len(values))); |
267 | 0 | return false; |
268 | 0 | } |
269 | | |
270 | 0 | *blob = data_blob_talloc(mem_ctx, values[0]->bv_val, |
271 | 0 | values[0]->bv_len); |
272 | 0 | ldap_value_free_len(values); |
273 | |
|
274 | 0 | return (blob->data != NULL); |
275 | 0 | } |
276 | | |
277 | | bool smbldap_pull_sid(LDAP *ld, LDAPMessage *msg, const char *attrib, |
278 | | struct dom_sid *sid) |
279 | 0 | { |
280 | 0 | DATA_BLOB blob; |
281 | 0 | ssize_t ret; |
282 | |
|
283 | 0 | if (!smbldap_talloc_single_blob(talloc_tos(), ld, msg, attrib, |
284 | 0 | &blob)) { |
285 | 0 | return false; |
286 | 0 | } |
287 | 0 | ret = sid_parse(blob.data, blob.length, sid); |
288 | 0 | TALLOC_FREE(blob.data); |
289 | 0 | return (ret != -1); |
290 | 0 | } |
291 | | |
292 | 0 | static int ldapmsg_destructor(LDAPMessage **result) { |
293 | 0 | ldap_msgfree(*result); |
294 | 0 | return 0; |
295 | 0 | } |
296 | | |
297 | | void smbldap_talloc_autofree_ldapmsg(TALLOC_CTX *mem_ctx, LDAPMessage *result) |
298 | 0 | { |
299 | 0 | LDAPMessage **handle; |
300 | |
|
301 | 0 | if (result == NULL) { |
302 | 0 | return; |
303 | 0 | } |
304 | | |
305 | 0 | handle = talloc(mem_ctx, LDAPMessage *); |
306 | 0 | SMB_ASSERT(handle != NULL); |
307 | | |
308 | 0 | *handle = result; |
309 | 0 | talloc_set_destructor(handle, ldapmsg_destructor); |
310 | 0 | } |
311 | | |
312 | 0 | static int ldapmod_destructor(LDAPMod ***mod) { |
313 | 0 | ldap_mods_free(*mod, True); |
314 | 0 | return 0; |
315 | 0 | } |
316 | | |
317 | | void smbldap_talloc_autofree_ldapmod(TALLOC_CTX *mem_ctx, LDAPMod **mod) |
318 | 0 | { |
319 | 0 | LDAPMod ***handle; |
320 | |
|
321 | 0 | if (mod == NULL) { |
322 | 0 | return; |
323 | 0 | } |
324 | | |
325 | 0 | handle = talloc(mem_ctx, LDAPMod **); |
326 | 0 | SMB_ASSERT(handle != NULL); |
327 | | |
328 | 0 | *handle = mod; |
329 | 0 | talloc_set_destructor(handle, ldapmod_destructor); |
330 | 0 | } |
331 | | |
332 | | /************************************************************************ |
333 | | Routine to manage the LDAPMod structure array |
334 | | manage memory used by the array, by each struct, and values |
335 | | ***********************************************************************/ |
336 | | |
337 | | static void smbldap_set_mod_internal(LDAPMod *** modlist, int modop, const char *attribute, const char *value, const DATA_BLOB *blob) |
338 | 0 | { |
339 | 0 | LDAPMod **mods; |
340 | 0 | int i; |
341 | 0 | int j; |
342 | |
|
343 | 0 | mods = *modlist; |
344 | | |
345 | | /* sanity checks on the mod values */ |
346 | |
|
347 | 0 | if (attribute == NULL || *attribute == '\0') { |
348 | 0 | return; |
349 | 0 | } |
350 | | |
351 | | #if 0 /* commented out after discussion with abartlet. Do not re-enable. |
352 | | left here so other do not re-add similar code --jerry */ |
353 | | if (value == NULL || *value == '\0') |
354 | | return; |
355 | | #endif |
356 | | |
357 | 0 | if (mods == NULL) { |
358 | 0 | mods = SMB_MALLOC_P(LDAPMod *); |
359 | 0 | if (mods == NULL) { |
360 | 0 | smb_panic("smbldap_set_mod: out of memory!"); |
361 | | /* notreached. */ |
362 | 0 | } |
363 | 0 | mods[0] = NULL; |
364 | 0 | } |
365 | | |
366 | 0 | for (i = 0; mods[i] != NULL; ++i) { |
367 | 0 | if (mods[i]->mod_op == modop && strequal(mods[i]->mod_type, attribute)) |
368 | 0 | break; |
369 | 0 | } |
370 | |
|
371 | 0 | if (mods[i] == NULL) { |
372 | 0 | mods = SMB_REALLOC_ARRAY (mods, LDAPMod *, i + 2); |
373 | 0 | if (mods == NULL) { |
374 | 0 | smb_panic("smbldap_set_mod: out of memory!"); |
375 | | /* notreached. */ |
376 | 0 | } |
377 | 0 | mods[i] = SMB_MALLOC_P(LDAPMod); |
378 | 0 | if (mods[i] == NULL) { |
379 | 0 | smb_panic("smbldap_set_mod: out of memory!"); |
380 | | /* notreached. */ |
381 | 0 | } |
382 | 0 | mods[i]->mod_op = modop; |
383 | 0 | mods[i]->mod_values = NULL; |
384 | 0 | mods[i]->mod_type = SMB_STRDUP(attribute); |
385 | 0 | mods[i + 1] = NULL; |
386 | 0 | } |
387 | | |
388 | 0 | if (blob && (modop & LDAP_MOD_BVALUES)) { |
389 | 0 | j = 0; |
390 | 0 | if (mods[i]->mod_bvalues != NULL) { |
391 | 0 | for (; mods[i]->mod_bvalues[j] != NULL; j++); |
392 | 0 | } |
393 | 0 | mods[i]->mod_bvalues = SMB_REALLOC_ARRAY(mods[i]->mod_bvalues, struct berval *, j + 2); |
394 | |
|
395 | 0 | if (mods[i]->mod_bvalues == NULL) { |
396 | 0 | smb_panic("smbldap_set_mod: out of memory!"); |
397 | | /* notreached. */ |
398 | 0 | } |
399 | | |
400 | 0 | mods[i]->mod_bvalues[j] = SMB_MALLOC_P(struct berval); |
401 | 0 | SMB_ASSERT(mods[i]->mod_bvalues[j] != NULL); |
402 | | |
403 | 0 | mods[i]->mod_bvalues[j]->bv_val = (char *)smb_memdup(blob->data, blob->length); |
404 | 0 | SMB_ASSERT(mods[i]->mod_bvalues[j]->bv_val != NULL); |
405 | 0 | mods[i]->mod_bvalues[j]->bv_len = blob->length; |
406 | |
|
407 | 0 | mods[i]->mod_bvalues[j + 1] = NULL; |
408 | 0 | } else if (value != NULL) { |
409 | 0 | char *utf8_value = NULL; |
410 | 0 | size_t converted_size; |
411 | |
|
412 | 0 | j = 0; |
413 | 0 | if (mods[i]->mod_values != NULL) { |
414 | 0 | for (; mods[i]->mod_values[j] != NULL; j++); |
415 | 0 | } |
416 | 0 | mods[i]->mod_values = SMB_REALLOC_ARRAY(mods[i]->mod_values, char *, j + 2); |
417 | |
|
418 | 0 | if (mods[i]->mod_values == NULL) { |
419 | 0 | smb_panic("smbldap_set_mod: out of memory!"); |
420 | | /* notreached. */ |
421 | 0 | } |
422 | | |
423 | 0 | if (!push_utf8_talloc(talloc_tos(), &utf8_value, value, &converted_size)) { |
424 | 0 | smb_panic("smbldap_set_mod: String conversion failure!"); |
425 | | /* notreached. */ |
426 | 0 | } |
427 | | |
428 | 0 | mods[i]->mod_values[j] = SMB_STRDUP(utf8_value); |
429 | 0 | TALLOC_FREE(utf8_value); |
430 | 0 | SMB_ASSERT(mods[i]->mod_values[j] != NULL); |
431 | | |
432 | 0 | mods[i]->mod_values[j + 1] = NULL; |
433 | 0 | } |
434 | 0 | *modlist = mods; |
435 | 0 | } |
436 | | |
437 | | void smbldap_set_mod (LDAPMod *** modlist, int modop, const char *attribute, const char *value) |
438 | 0 | { |
439 | 0 | smbldap_set_mod_internal(modlist, modop, attribute, value, NULL); |
440 | 0 | } |
441 | | |
442 | | void smbldap_set_mod_blob(LDAPMod *** modlist, int modop, const char *attribute, const DATA_BLOB *value) |
443 | 0 | { |
444 | 0 | smbldap_set_mod_internal(modlist, modop | LDAP_MOD_BVALUES, attribute, NULL, value); |
445 | 0 | } |
446 | | |
447 | | /********************************************************************** |
448 | | Set attribute to newval in LDAP, regardless of what value the |
449 | | attribute had in LDAP before. |
450 | | *********************************************************************/ |
451 | | |
452 | | static void smbldap_make_mod_internal(LDAP *ldap_struct, LDAPMessage *existing, |
453 | | LDAPMod ***mods, |
454 | | const char *attribute, int op, |
455 | | const char *newval, |
456 | | const DATA_BLOB *newblob) |
457 | 0 | { |
458 | 0 | char oldval[2048]; /* current largest allowed value is mungeddial */ |
459 | 0 | bool existed; |
460 | 0 | DATA_BLOB oldblob = data_blob_null; |
461 | |
|
462 | 0 | if (existing != NULL) { |
463 | 0 | if (op & LDAP_MOD_BVALUES) { |
464 | 0 | existed = smbldap_talloc_single_blob(talloc_tos(), ldap_struct, existing, attribute, &oldblob); |
465 | 0 | } else { |
466 | 0 | existed = smbldap_get_single_attribute(ldap_struct, existing, attribute, oldval, sizeof(oldval)); |
467 | 0 | } |
468 | 0 | } else { |
469 | 0 | existed = False; |
470 | 0 | *oldval = '\0'; |
471 | 0 | } |
472 | |
|
473 | 0 | if (existed) { |
474 | 0 | bool equal = false; |
475 | 0 | if (op & LDAP_MOD_BVALUES) { |
476 | 0 | equal = (newblob && (data_blob_cmp(&oldblob, newblob) == 0)); |
477 | 0 | } else { |
478 | | /* all of our string attributes are case insensitive */ |
479 | 0 | equal = (newval && (strcasecmp_m(oldval, newval) == 0)); |
480 | 0 | } |
481 | |
|
482 | 0 | if (equal) { |
483 | | /* Believe it or not, but LDAP will deny a delete and |
484 | | an add at the same time if the values are the |
485 | | same... */ |
486 | 0 | DEBUG(10,("smbldap_make_mod: attribute |%s| not changed.\n", attribute)); |
487 | 0 | return; |
488 | 0 | } |
489 | | |
490 | | /* There has been no value before, so don't delete it. |
491 | | * Here's a possible race: We might end up with |
492 | | * duplicate attributes */ |
493 | | /* By deleting exactly the value we found in the entry this |
494 | | * should be race-free in the sense that the LDAP-Server will |
495 | | * deny the complete operation if somebody changed the |
496 | | * attribute behind our back. */ |
497 | | /* This will also allow modifying single valued attributes |
498 | | * in Novell NDS. In NDS you have to first remove attribute and then |
499 | | * you could add new value */ |
500 | | |
501 | 0 | if (op & LDAP_MOD_BVALUES) { |
502 | 0 | DEBUG(10,("smbldap_make_mod: deleting attribute |%s| blob\n", attribute)); |
503 | 0 | smbldap_set_mod_blob(mods, LDAP_MOD_DELETE, attribute, &oldblob); |
504 | 0 | } else { |
505 | 0 | DEBUG(10,("smbldap_make_mod: deleting attribute |%s| values |%s|\n", attribute, oldval)); |
506 | 0 | smbldap_set_mod(mods, LDAP_MOD_DELETE, attribute, oldval); |
507 | 0 | } |
508 | 0 | } |
509 | | |
510 | | /* Regardless of the real operation (add or modify) |
511 | | we add the new value here. We rely on deleting |
512 | | the old value, should it exist. */ |
513 | | |
514 | 0 | if (op & LDAP_MOD_BVALUES) { |
515 | 0 | if (newblob && newblob->length) { |
516 | 0 | DEBUG(10,("smbldap_make_mod: adding attribute |%s| blob\n", attribute)); |
517 | 0 | smbldap_set_mod_blob(mods, LDAP_MOD_ADD, attribute, newblob); |
518 | 0 | } |
519 | 0 | } else { |
520 | 0 | if ((newval != NULL) && (strlen(newval) > 0)) { |
521 | 0 | DEBUG(10,("smbldap_make_mod: adding attribute |%s| value |%s|\n", attribute, newval)); |
522 | 0 | smbldap_set_mod(mods, LDAP_MOD_ADD, attribute, newval); |
523 | 0 | } |
524 | 0 | } |
525 | 0 | } |
526 | | |
527 | | void smbldap_make_mod(LDAP *ldap_struct, LDAPMessage *existing, |
528 | | LDAPMod ***mods, |
529 | | const char *attribute, const char *newval) |
530 | 0 | { |
531 | 0 | smbldap_make_mod_internal(ldap_struct, existing, mods, attribute, |
532 | 0 | 0, newval, NULL); |
533 | 0 | } |
534 | | |
535 | | void smbldap_make_mod_blob(LDAP *ldap_struct, LDAPMessage *existing, |
536 | | LDAPMod ***mods, |
537 | | const char *attribute, const DATA_BLOB *newblob) |
538 | 0 | { |
539 | 0 | smbldap_make_mod_internal(ldap_struct, existing, mods, attribute, |
540 | 0 | LDAP_MOD_BVALUES, NULL, newblob); |
541 | 0 | } |
542 | | |
543 | | /********************************************************************** |
544 | | Some variants of the LDAP rebind code do not pass in the third 'arg' |
545 | | pointer to a void*, so we try and work around it by assuming that the |
546 | | value of the 'LDAP *' pointer is the same as the one we had passed in |
547 | | **********************************************************************/ |
548 | | |
549 | | struct smbldap_state_lookup { |
550 | | LDAP *ld; |
551 | | struct smbldap_state *smbldap_state; |
552 | | struct smbldap_state_lookup *prev, *next; |
553 | | }; |
554 | | |
555 | | static struct smbldap_state_lookup *smbldap_state_lookup_list; |
556 | | |
557 | | static struct smbldap_state *smbldap_find_state(LDAP *ld) |
558 | 0 | { |
559 | 0 | struct smbldap_state_lookup *t; |
560 | |
|
561 | 0 | for (t = smbldap_state_lookup_list; t; t = t->next) { |
562 | 0 | if (t->ld == ld) { |
563 | 0 | return t->smbldap_state; |
564 | 0 | } |
565 | 0 | } |
566 | 0 | return NULL; |
567 | 0 | } |
568 | | |
569 | | static void smbldap_delete_state(struct smbldap_state *smbldap_state) |
570 | 0 | { |
571 | 0 | struct smbldap_state_lookup *t; |
572 | |
|
573 | 0 | for (t = smbldap_state_lookup_list; t; t = t->next) { |
574 | 0 | if (t->smbldap_state == smbldap_state) { |
575 | 0 | DLIST_REMOVE(smbldap_state_lookup_list, t); |
576 | 0 | SAFE_FREE(t); |
577 | 0 | return; |
578 | 0 | } |
579 | 0 | } |
580 | 0 | } |
581 | | |
582 | | static void smbldap_store_state(LDAP *ld, struct smbldap_state *smbldap_state) |
583 | 0 | { |
584 | 0 | struct smbldap_state *tmp_ldap_state; |
585 | 0 | struct smbldap_state_lookup *t; |
586 | |
|
587 | 0 | if ((tmp_ldap_state = smbldap_find_state(ld))) { |
588 | 0 | SMB_ASSERT(tmp_ldap_state == smbldap_state); |
589 | 0 | return; |
590 | 0 | } |
591 | | |
592 | 0 | t = SMB_XMALLOC_P(struct smbldap_state_lookup); |
593 | 0 | ZERO_STRUCTP(t); |
594 | |
|
595 | 0 | DLIST_ADD_END(smbldap_state_lookup_list, t); |
596 | 0 | t->ld = ld; |
597 | 0 | t->smbldap_state = smbldap_state; |
598 | 0 | } |
599 | | |
600 | | /******************************************************************** |
601 | | start TLS on an existing LDAP connection per config |
602 | | *******************************************************************/ |
603 | | |
604 | | int smbldap_start_tls(LDAP *ldap_struct, int version) |
605 | 0 | { |
606 | 0 | if (lp_ldap_ssl() != LDAP_SSL_START_TLS) { |
607 | 0 | return LDAP_SUCCESS; |
608 | 0 | } |
609 | | |
610 | 0 | return smbldap_start_tls_start(ldap_struct, version); |
611 | 0 | } |
612 | | |
613 | | /******************************************************************** |
614 | | start TLS on an existing LDAP connection unconditionally |
615 | | *******************************************************************/ |
616 | | |
617 | | int smbldap_start_tls_start(LDAP *ldap_struct, int version) |
618 | 0 | { |
619 | 0 | #ifdef LDAP_OPT_X_TLS |
620 | 0 | int rc,tls; |
621 | | |
622 | | /* check if we use ldaps already */ |
623 | 0 | ldap_get_option(ldap_struct, LDAP_OPT_X_TLS, &tls); |
624 | 0 | if (tls == LDAP_OPT_X_TLS_HARD) { |
625 | 0 | return LDAP_SUCCESS; |
626 | 0 | } |
627 | | |
628 | 0 | if (version != LDAP_VERSION3) { |
629 | 0 | DEBUG(0, ("Need LDAPv3 for Start TLS\n")); |
630 | 0 | return LDAP_OPERATIONS_ERROR; |
631 | 0 | } |
632 | | |
633 | 0 | if ((rc = ldap_start_tls_s (ldap_struct, NULL, NULL)) != LDAP_SUCCESS) { |
634 | 0 | DEBUG(0,("Failed to issue the StartTLS instruction: %s\n", |
635 | 0 | ldap_err2string(rc))); |
636 | 0 | return rc; |
637 | 0 | } |
638 | | |
639 | 0 | DEBUG (3, ("StartTLS issued: using a TLS connection\n")); |
640 | 0 | return LDAP_SUCCESS; |
641 | | #else |
642 | | DEBUG(0,("StartTLS not supported by LDAP client libraries!\n")); |
643 | | return LDAP_OPERATIONS_ERROR; |
644 | | #endif |
645 | 0 | } |
646 | | |
647 | | /******************************************************************** |
648 | | setup a connection to the LDAP server based on a uri |
649 | | *******************************************************************/ |
650 | | |
651 | | static int smb_ldap_setup_conn(LDAP **ldap_struct, const char *uri) |
652 | 0 | { |
653 | 0 | int rc; |
654 | |
|
655 | 0 | DEBUG(10, ("smb_ldap_setup_connection: %s\n", uri)); |
656 | |
|
657 | 0 | #ifdef HAVE_LDAP_INITIALIZE |
658 | |
|
659 | 0 | rc = ldap_initialize(ldap_struct, uri); |
660 | 0 | if (rc) { |
661 | 0 | DEBUG(0, ("ldap_initialize: %s\n", ldap_err2string(rc))); |
662 | 0 | return rc; |
663 | 0 | } |
664 | | |
665 | 0 | if (lp_ldap_follow_referral() != Auto) { |
666 | 0 | rc = ldap_set_option(*ldap_struct, LDAP_OPT_REFERRALS, |
667 | 0 | lp_ldap_follow_referral() ? LDAP_OPT_ON : LDAP_OPT_OFF); |
668 | 0 | if (rc != LDAP_SUCCESS) |
669 | 0 | DEBUG(0, ("Failed to set LDAP_OPT_REFERRALS: %s\n", |
670 | 0 | ldap_err2string(rc))); |
671 | 0 | } |
672 | |
|
673 | | #else |
674 | | |
675 | | /* Parse the string manually */ |
676 | | |
677 | | { |
678 | | int port = 0; |
679 | | fstring protocol; |
680 | | fstring host; |
681 | | SMB_ASSERT(sizeof(protocol)>10 && sizeof(host)>254); |
682 | | |
683 | | |
684 | | /* skip leading "URL:" (if any) */ |
685 | | if ( strnequal( uri, "URL:", 4 ) ) { |
686 | | uri += 4; |
687 | | } |
688 | | |
689 | | sscanf(uri, "%10[^:]://%254[^:/]:%d", protocol, host, &port); |
690 | | |
691 | | if (port == 0) { |
692 | | if (strequal(protocol, "ldap")) { |
693 | | port = LDAP_PORT; |
694 | | } else if (strequal(protocol, "ldaps")) { |
695 | | port = LDAPS_PORT; |
696 | | } else { |
697 | | DEBUG(0, ("unrecognised protocol (%s)!\n", protocol)); |
698 | | } |
699 | | } |
700 | | |
701 | | if ((*ldap_struct = ldap_init(host, port)) == NULL) { |
702 | | DEBUG(0, ("ldap_init failed !\n")); |
703 | | return LDAP_OPERATIONS_ERROR; |
704 | | } |
705 | | |
706 | | if (strequal(protocol, "ldaps")) { |
707 | | #ifdef LDAP_OPT_X_TLS |
708 | | int tls = LDAP_OPT_X_TLS_HARD; |
709 | | if (ldap_set_option (*ldap_struct, LDAP_OPT_X_TLS, &tls) != LDAP_SUCCESS) |
710 | | { |
711 | | DEBUG(0, ("Failed to setup a TLS session\n")); |
712 | | } |
713 | | |
714 | | DEBUG(3,("LDAPS option set...!\n")); |
715 | | #else |
716 | | DEBUG(0,("smbldap_open_connection: Secure connection not supported by LDAP client libraries!\n")); |
717 | | return LDAP_OPERATIONS_ERROR; |
718 | | #endif /* LDAP_OPT_X_TLS */ |
719 | | } |
720 | | } |
721 | | |
722 | | /* now set connection timeout */ |
723 | | #ifdef LDAP_X_OPT_CONNECT_TIMEOUT /* Netscape */ |
724 | | { |
725 | | int ct = lp_ldap_connection_timeout()*1000; |
726 | | rc = ldap_set_option(*ldap_struct, LDAP_X_OPT_CONNECT_TIMEOUT, &ct); |
727 | | if (rc != LDAP_SUCCESS) { |
728 | | DEBUG(0,("Failed to setup an ldap connection timeout %d: %s\n", |
729 | | ct, ldap_err2string(rc))); |
730 | | } |
731 | | } |
732 | | #elif defined (LDAP_OPT_NETWORK_TIMEOUT) /* OpenLDAP */ |
733 | | { |
734 | | struct timeval ct; |
735 | | ct.tv_usec = 0; |
736 | | ct.tv_sec = lp_ldap_connection_timeout(); |
737 | | rc = ldap_set_option(*ldap_struct, LDAP_OPT_NETWORK_TIMEOUT, &ct); |
738 | | if (rc != LDAP_SUCCESS) { |
739 | | DEBUG(0,("Failed to setup an ldap connection timeout %d: %s\n", |
740 | | (int)ct.tv_sec, ldap_err2string(rc))); |
741 | | } |
742 | | } |
743 | | #endif |
744 | | |
745 | | #endif /* HAVE_LDAP_INITIALIZE */ |
746 | 0 | return LDAP_SUCCESS; |
747 | 0 | } |
748 | | |
749 | | /******************************************************************** |
750 | | try to upgrade to Version 3 LDAP if not already, in either case return current |
751 | | version |
752 | | *******************************************************************/ |
753 | | |
754 | | static int smb_ldap_upgrade_conn(LDAP *ldap_struct, int *new_version) |
755 | 0 | { |
756 | 0 | int version; |
757 | 0 | int rc; |
758 | | |
759 | | /* assume the worst */ |
760 | 0 | *new_version = LDAP_VERSION2; |
761 | |
|
762 | 0 | rc = ldap_get_option(ldap_struct, LDAP_OPT_PROTOCOL_VERSION, &version); |
763 | 0 | if (rc) { |
764 | 0 | return rc; |
765 | 0 | } |
766 | | |
767 | 0 | if (version == LDAP_VERSION3) { |
768 | 0 | *new_version = LDAP_VERSION3; |
769 | 0 | return LDAP_SUCCESS; |
770 | 0 | } |
771 | | |
772 | | /* try upgrade */ |
773 | 0 | version = LDAP_VERSION3; |
774 | 0 | rc = ldap_set_option (ldap_struct, LDAP_OPT_PROTOCOL_VERSION, &version); |
775 | 0 | if (rc) { |
776 | 0 | return rc; |
777 | 0 | } |
778 | | |
779 | 0 | *new_version = LDAP_VERSION3; |
780 | 0 | return LDAP_SUCCESS; |
781 | 0 | } |
782 | | |
783 | | /******************************************************************* |
784 | | open a connection to the ldap server (just until the bind) |
785 | | ******************************************************************/ |
786 | | |
787 | | int smbldap_setup_full_conn(LDAP **ldap_struct, const char *uri) |
788 | 0 | { |
789 | 0 | int rc, version; |
790 | |
|
791 | 0 | rc = smb_ldap_setup_conn(ldap_struct, uri); |
792 | 0 | if (rc) { |
793 | 0 | return rc; |
794 | 0 | } |
795 | | |
796 | 0 | rc = smb_ldap_upgrade_conn(*ldap_struct, &version); |
797 | 0 | if (rc) { |
798 | 0 | return rc; |
799 | 0 | } |
800 | | |
801 | 0 | rc = smbldap_start_tls(*ldap_struct, version); |
802 | 0 | if (rc) { |
803 | 0 | return rc; |
804 | 0 | } |
805 | | |
806 | 0 | return LDAP_SUCCESS; |
807 | 0 | } |
808 | | |
809 | | /******************************************************************* |
810 | | open a connection to the ldap server. |
811 | | ******************************************************************/ |
812 | | static int smbldap_open_connection (struct smbldap_state *ldap_state) |
813 | | |
814 | 0 | { |
815 | 0 | int rc = LDAP_SUCCESS; |
816 | 0 | int version; |
817 | 0 | int deref; |
818 | 0 | LDAP **ldap_struct = &ldap_state->ldap_struct; |
819 | |
|
820 | 0 | rc = smb_ldap_setup_conn(ldap_struct, ldap_state->uri); |
821 | 0 | if (rc) { |
822 | 0 | return rc; |
823 | 0 | } |
824 | | |
825 | | /* Store the LDAP pointer in a lookup list */ |
826 | | |
827 | 0 | smbldap_store_state(*ldap_struct, ldap_state); |
828 | | |
829 | | /* Upgrade to LDAPv3 if possible */ |
830 | |
|
831 | 0 | rc = smb_ldap_upgrade_conn(*ldap_struct, &version); |
832 | 0 | if (rc) { |
833 | 0 | return rc; |
834 | 0 | } |
835 | | |
836 | | /* Start TLS if required */ |
837 | | |
838 | 0 | rc = smbldap_start_tls(*ldap_struct, version); |
839 | 0 | if (rc) { |
840 | 0 | return rc; |
841 | 0 | } |
842 | | |
843 | | /* Set alias dereferencing method */ |
844 | 0 | deref = lp_ldap_deref(); |
845 | 0 | if (deref != -1) { |
846 | 0 | if (ldap_set_option (*ldap_struct, LDAP_OPT_DEREF, &deref) != LDAP_OPT_SUCCESS) { |
847 | 0 | DEBUG(1,("smbldap_open_connection: Failed to set dereferencing method: %d\n", deref)); |
848 | 0 | } else { |
849 | 0 | DEBUG(5,("Set dereferencing method: %d\n", deref)); |
850 | 0 | } |
851 | 0 | } |
852 | |
|
853 | 0 | DEBUG(2, ("smbldap_open_connection: connection opened\n")); |
854 | 0 | return rc; |
855 | 0 | } |
856 | | |
857 | | /******************************************************************* |
858 | | a rebind function for authenticated referrals |
859 | | This version takes a void* that we can shove useful stuff in :-) |
860 | | ******************************************************************/ |
861 | | #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) |
862 | | #else |
863 | | static int rebindproc_with_state (LDAP * ld, char **whop, char **credp, |
864 | | int *methodp, int freeit, void *arg) |
865 | | { |
866 | | struct smbldap_state *ldap_state = arg; |
867 | | struct timespec ts; |
868 | | |
869 | | /** @TODO Should we be doing something to check what servers we rebind to? |
870 | | Could we get a referral to a machine that we don't want to give our |
871 | | username and password to? */ |
872 | | |
873 | | if (freeit) { |
874 | | SAFE_FREE(*whop); |
875 | | if (*credp) { |
876 | | memset(*credp, '\0', strlen(*credp)); |
877 | | } |
878 | | SAFE_FREE(*credp); |
879 | | } else { |
880 | | DEBUG(5,("rebind_proc_with_state: Rebinding as \"%s\"\n", |
881 | | ldap_state->bind_dn?ldap_state->bind_dn:"[Anonymous bind]")); |
882 | | |
883 | | if (ldap_state->anonymous) { |
884 | | *whop = NULL; |
885 | | *credp = NULL; |
886 | | } else { |
887 | | *whop = SMB_STRDUP(ldap_state->bind_dn); |
888 | | if (!*whop) { |
889 | | return LDAP_NO_MEMORY; |
890 | | } |
891 | | *credp = SMB_STRDUP(ldap_state->bind_secret); |
892 | | if (!*credp) { |
893 | | SAFE_FREE(*whop); |
894 | | return LDAP_NO_MEMORY; |
895 | | } |
896 | | } |
897 | | *methodp = LDAP_AUTH_SIMPLE; |
898 | | } |
899 | | |
900 | | clock_gettime_mono(&ts); |
901 | | ldap_state->last_rebind = convert_timespec_to_timeval(ts); |
902 | | |
903 | | return 0; |
904 | | } |
905 | | #endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/ |
906 | | |
907 | | /******************************************************************* |
908 | | a rebind function for authenticated referrals |
909 | | This version takes a void* that we can shove useful stuff in :-) |
910 | | and actually does the connection. |
911 | | ******************************************************************/ |
912 | | #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) |
913 | | static int rebindproc_connect_with_state (LDAP *ldap_struct, |
914 | | LDAP_CONST char *url, |
915 | | ber_tag_t request, |
916 | | ber_int_t msgid, void *arg) |
917 | 0 | { |
918 | 0 | struct smbldap_state *ldap_state = |
919 | 0 | (struct smbldap_state *)arg; |
920 | 0 | int rc; |
921 | 0 | struct timespec ts; |
922 | 0 | int version; |
923 | |
|
924 | 0 | DEBUG(5,("rebindproc_connect_with_state: Rebinding to %s as \"%s\"\n", |
925 | 0 | url, ldap_state->bind_dn?ldap_state->bind_dn:"[Anonymous bind]")); |
926 | | |
927 | | /* call START_TLS again (ldaps:// is handled by the OpenLDAP library |
928 | | * itself) before rebinding to another LDAP server to avoid to expose |
929 | | * our credentials. At least *try* to secure the connection - Guenther */ |
930 | |
|
931 | 0 | smb_ldap_upgrade_conn(ldap_struct, &version); |
932 | 0 | smbldap_start_tls(ldap_struct, version); |
933 | | |
934 | | /** @TODO Should we be doing something to check what servers we rebind to? |
935 | | Could we get a referral to a machine that we don't want to give our |
936 | | username and password to? */ |
937 | |
|
938 | 0 | rc = ldap_simple_bind_s(ldap_struct, ldap_state->bind_dn, ldap_state->bind_secret); |
939 | | |
940 | | /* only set the last rebind timestamp when we did rebind after a |
941 | | * non-read LDAP operation. That way we avoid the replication sleep |
942 | | * after a simple redirected search operation - Guenther */ |
943 | |
|
944 | 0 | switch (request) { |
945 | | |
946 | 0 | case LDAP_REQ_MODIFY: |
947 | 0 | case LDAP_REQ_ADD: |
948 | 0 | case LDAP_REQ_DELETE: |
949 | 0 | case LDAP_REQ_MODDN: |
950 | 0 | case LDAP_REQ_EXTENDED: |
951 | 0 | DEBUG(10,("rebindproc_connect_with_state: " |
952 | 0 | "setting last_rebind timestamp " |
953 | 0 | "(req: 0x%02x)\n", (unsigned int)request)); |
954 | 0 | clock_gettime_mono(&ts); |
955 | 0 | ldap_state->last_rebind = convert_timespec_to_timeval(ts); |
956 | 0 | break; |
957 | 0 | default: |
958 | 0 | ZERO_STRUCT(ldap_state->last_rebind); |
959 | 0 | break; |
960 | 0 | } |
961 | | |
962 | 0 | return rc; |
963 | 0 | } |
964 | | #endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/ |
965 | | |
966 | | /******************************************************************* |
967 | | Add a rebind function for authenticated referrals |
968 | | ******************************************************************/ |
969 | | #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) |
970 | | #else |
971 | | # if LDAP_SET_REBIND_PROC_ARGS == 2 |
972 | | static int rebindproc (LDAP *ldap_struct, char **whop, char **credp, |
973 | | int *method, int freeit ) |
974 | | { |
975 | | struct smbldap_state *ldap_state = smbldap_find_state(ldap_struct); |
976 | | |
977 | | return rebindproc_with_state(ldap_struct, whop, credp, |
978 | | method, freeit, ldap_state); |
979 | | } |
980 | | # endif /*LDAP_SET_REBIND_PROC_ARGS == 2*/ |
981 | | #endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/ |
982 | | |
983 | | /******************************************************************* |
984 | | a rebind function for authenticated referrals |
985 | | this also does the connection, but no void*. |
986 | | ******************************************************************/ |
987 | | #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) |
988 | | # if LDAP_SET_REBIND_PROC_ARGS == 2 |
989 | | static int rebindproc_connect (LDAP * ld, LDAP_CONST char *url, int request, |
990 | | ber_int_t msgid) |
991 | | { |
992 | | struct smbldap_state *ldap_state = smbldap_find_state(ld); |
993 | | |
994 | | return rebindproc_connect_with_state(ld, url, (ber_tag_t)request, msgid, |
995 | | ldap_state); |
996 | | } |
997 | | # endif /*LDAP_SET_REBIND_PROC_ARGS == 2*/ |
998 | | #endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/ |
999 | | |
1000 | | /******************************************************************* |
1001 | | connect to the ldap server under system privilege. |
1002 | | ******************************************************************/ |
1003 | | static int smbldap_connect_system(struct smbldap_state *ldap_state) |
1004 | 0 | { |
1005 | 0 | LDAP *ldap_struct = smbldap_get_ldap(ldap_state); |
1006 | 0 | int rc; |
1007 | 0 | int version; |
1008 | | |
1009 | | /* removed the sasl_bind_s "EXTERNAL" stuff, as my testsuite |
1010 | | (OpenLDAP) doesn't seem to support it */ |
1011 | |
|
1012 | 0 | DEBUG(10,("ldap_connect_system: Binding to ldap server %s as \"%s\"\n", |
1013 | 0 | ldap_state->uri, ldap_state->bind_dn)); |
1014 | |
|
1015 | 0 | #ifdef HAVE_LDAP_SET_REBIND_PROC |
1016 | 0 | #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) |
1017 | | # if LDAP_SET_REBIND_PROC_ARGS == 2 |
1018 | | ldap_set_rebind_proc(ldap_struct, &rebindproc_connect); |
1019 | | # endif |
1020 | 0 | # if LDAP_SET_REBIND_PROC_ARGS == 3 |
1021 | 0 | ldap_set_rebind_proc(ldap_struct, &rebindproc_connect_with_state, (void *)ldap_state); |
1022 | 0 | # endif |
1023 | | #else /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/ |
1024 | | # if LDAP_SET_REBIND_PROC_ARGS == 2 |
1025 | | ldap_set_rebind_proc(ldap_struct, &rebindproc); |
1026 | | # endif |
1027 | | # if LDAP_SET_REBIND_PROC_ARGS == 3 |
1028 | | ldap_set_rebind_proc(ldap_struct, &rebindproc_with_state, (void *)ldap_state); |
1029 | | # endif |
1030 | | #endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/ |
1031 | 0 | #endif |
1032 | | |
1033 | | /* When there is an alternative bind callback is set, |
1034 | | attempt to use it to perform the bind */ |
1035 | 0 | if (ldap_state->bind_callback != NULL) { |
1036 | | /* We have to allow bind callback to be run under become_root/unbecome_root |
1037 | | to make sure within smbd the callback has proper write access to its resources, |
1038 | | like credential cache. This is similar to passdb case where this callback is supposed |
1039 | | to be used. When used outside smbd, become_root()/unbecome_root() are no-op. |
1040 | | */ |
1041 | 0 | become_root(); |
1042 | 0 | rc = ldap_state->bind_callback(ldap_struct, ldap_state, ldap_state->bind_callback_data); |
1043 | 0 | unbecome_root(); |
1044 | 0 | } else { |
1045 | 0 | rc = ldap_simple_bind_s(ldap_struct, ldap_state->bind_dn, ldap_state->bind_secret); |
1046 | 0 | } |
1047 | |
|
1048 | 0 | if (rc != LDAP_SUCCESS) { |
1049 | 0 | char *ld_error = NULL; |
1050 | 0 | ldap_get_option(smbldap_get_ldap(ldap_state), |
1051 | 0 | LDAP_OPT_ERROR_STRING, |
1052 | 0 | &ld_error); |
1053 | 0 | DEBUG(ldap_state->num_failures ? 2 : 0, |
1054 | 0 | ("failed to bind to server %s with dn=\"%s\" Error: %s\n\t%s\n", |
1055 | 0 | ldap_state->uri, |
1056 | 0 | ldap_state->bind_dn ? ldap_state->bind_dn : "[Anonymous bind]", |
1057 | 0 | ldap_err2string(rc), |
1058 | 0 | ld_error ? ld_error : "(unknown)")); |
1059 | 0 | SAFE_FREE(ld_error); |
1060 | 0 | ldap_state->num_failures++; |
1061 | 0 | goto done; |
1062 | 0 | } |
1063 | | |
1064 | 0 | ldap_state->num_failures = 0; |
1065 | 0 | ldap_state->paged_results = False; |
1066 | |
|
1067 | 0 | ldap_get_option(smbldap_get_ldap(ldap_state), |
1068 | 0 | LDAP_OPT_PROTOCOL_VERSION, &version); |
1069 | |
|
1070 | 0 | if (smbldap_has_control(smbldap_get_ldap(ldap_state), ADS_PAGE_CTL_OID) |
1071 | 0 | && version == 3) { |
1072 | 0 | ldap_state->paged_results = True; |
1073 | 0 | } |
1074 | |
|
1075 | 0 | DEBUG(3, ("ldap_connect_system: successful connection to the LDAP server\n")); |
1076 | 0 | DEBUGADD(10, ("ldap_connect_system: LDAP server %s support paged results\n", |
1077 | 0 | ldap_state->paged_results ? "does" : "does not")); |
1078 | 0 | done: |
1079 | 0 | if (rc != 0) { |
1080 | 0 | ldap_unbind(ldap_struct); |
1081 | 0 | ldap_state->ldap_struct = NULL; |
1082 | 0 | } |
1083 | 0 | return rc; |
1084 | 0 | } |
1085 | | |
1086 | | static void smbldap_idle_fn(struct tevent_context *tevent_ctx, |
1087 | | struct tevent_timer *te, |
1088 | | struct timeval now_abs, |
1089 | | void *private_data); |
1090 | | |
1091 | | /********************************************************************** |
1092 | | Connect to LDAP server (called before every ldap operation) |
1093 | | *********************************************************************/ |
1094 | | static int smbldap_open(struct smbldap_state *ldap_state) |
1095 | 0 | { |
1096 | 0 | int rc, opt_rc; |
1097 | 0 | bool reopen = False; |
1098 | 0 | SMB_ASSERT(ldap_state); |
1099 | | |
1100 | 0 | if ((smbldap_get_ldap(ldap_state) != NULL) && |
1101 | 0 | ((ldap_state->last_ping + SMBLDAP_DONT_PING_TIME) < |
1102 | 0 | time_mono(NULL))) { |
1103 | |
|
1104 | 0 | #ifdef HAVE_UNIXSOCKET |
1105 | 0 | struct sockaddr_un addr; |
1106 | | #else |
1107 | | struct sockaddr_storage addr; |
1108 | | #endif |
1109 | 0 | socklen_t len = sizeof(addr); |
1110 | 0 | int sd; |
1111 | |
|
1112 | 0 | opt_rc = ldap_get_option(smbldap_get_ldap(ldap_state), |
1113 | 0 | LDAP_OPT_DESC, &sd); |
1114 | 0 | if (opt_rc == 0 && (getpeername(sd, (struct sockaddr *) &addr, &len)) < 0 ) |
1115 | 0 | reopen = True; |
1116 | |
|
1117 | 0 | #ifdef HAVE_UNIXSOCKET |
1118 | 0 | if (opt_rc == 0 && addr.sun_family == AF_UNIX) |
1119 | 0 | reopen = True; |
1120 | 0 | #endif |
1121 | 0 | if (reopen) { |
1122 | | /* the other end has died. reopen. */ |
1123 | 0 | ldap_unbind(smbldap_get_ldap(ldap_state)); |
1124 | 0 | ldap_state->ldap_struct = NULL; |
1125 | 0 | ldap_state->last_ping = (time_t)0; |
1126 | 0 | } else { |
1127 | 0 | ldap_state->last_ping = time_mono(NULL); |
1128 | 0 | } |
1129 | 0 | } |
1130 | |
|
1131 | 0 | if (smbldap_get_ldap(ldap_state) != NULL) { |
1132 | 0 | DEBUG(11,("smbldap_open: already connected to the LDAP server\n")); |
1133 | 0 | return LDAP_SUCCESS; |
1134 | 0 | } |
1135 | | |
1136 | 0 | if ((rc = smbldap_open_connection(ldap_state))) { |
1137 | 0 | return rc; |
1138 | 0 | } |
1139 | | |
1140 | 0 | if ((rc = smbldap_connect_system(ldap_state))) { |
1141 | 0 | return rc; |
1142 | 0 | } |
1143 | | |
1144 | | |
1145 | 0 | ldap_state->last_ping = time_mono(NULL); |
1146 | 0 | ldap_state->pid = getpid(); |
1147 | |
|
1148 | 0 | TALLOC_FREE(ldap_state->idle_event); |
1149 | |
|
1150 | 0 | if (ldap_state->tevent_context != NULL) { |
1151 | 0 | ldap_state->idle_event = tevent_add_timer( |
1152 | 0 | ldap_state->tevent_context, ldap_state, |
1153 | 0 | timeval_current_ofs(SMBLDAP_IDLE_TIME, 0), |
1154 | 0 | smbldap_idle_fn, ldap_state); |
1155 | 0 | } |
1156 | |
|
1157 | 0 | DEBUG(4,("The LDAP server is successfully connected\n")); |
1158 | |
|
1159 | 0 | return LDAP_SUCCESS; |
1160 | 0 | } |
1161 | | |
1162 | | /********************************************************************** |
1163 | | Disconnect from LDAP server |
1164 | | *********************************************************************/ |
1165 | | static NTSTATUS smbldap_close(struct smbldap_state *ldap_state) |
1166 | 0 | { |
1167 | 0 | if (!ldap_state) |
1168 | 0 | return NT_STATUS_INVALID_PARAMETER; |
1169 | | |
1170 | 0 | if (smbldap_get_ldap(ldap_state) != NULL) { |
1171 | 0 | ldap_unbind(smbldap_get_ldap(ldap_state)); |
1172 | 0 | ldap_state->ldap_struct = NULL; |
1173 | 0 | } |
1174 | |
|
1175 | 0 | smbldap_delete_state(ldap_state); |
1176 | |
|
1177 | 0 | TALLOC_FREE(ldap_state->idle_event); |
1178 | |
|
1179 | 0 | DEBUG(5,("The connection to the LDAP server was closed\n")); |
1180 | | /* maybe free the results here --metze */ |
1181 | |
|
1182 | 0 | return NT_STATUS_OK; |
1183 | 0 | } |
1184 | | |
1185 | | static SIG_ATOMIC_T got_alarm; |
1186 | | |
1187 | | static void gotalarm_sig(int dummy) |
1188 | 0 | { |
1189 | 0 | got_alarm = 1; |
1190 | 0 | } |
1191 | | |
1192 | | static time_t calc_ldap_abs_endtime(int ldap_to) |
1193 | 0 | { |
1194 | 0 | if (ldap_to == 0) { |
1195 | | /* No timeout - don't |
1196 | | return a value for |
1197 | | the alarm. */ |
1198 | 0 | return (time_t)0; |
1199 | 0 | } |
1200 | | |
1201 | | /* Make the alarm time one second beyond |
1202 | | the timeout we're setting for the |
1203 | | remote search timeout, to allow that |
1204 | | to fire in preference. */ |
1205 | | |
1206 | 0 | return time_mono(NULL)+ldap_to+1; |
1207 | 0 | } |
1208 | | |
1209 | | static int end_ldap_local_alarm(time_t absolute_endtime, int rc) |
1210 | 0 | { |
1211 | 0 | if (absolute_endtime) { |
1212 | 0 | alarm(0); |
1213 | 0 | CatchSignal(SIGALRM, SIG_IGN); |
1214 | 0 | if (got_alarm) { |
1215 | | /* Client timeout error code. */ |
1216 | 0 | got_alarm = 0; |
1217 | 0 | return LDAP_TIMEOUT; |
1218 | 0 | } |
1219 | 0 | } |
1220 | 0 | return rc; |
1221 | 0 | } |
1222 | | |
1223 | | static void setup_ldap_local_alarm(struct smbldap_state *ldap_state, time_t absolute_endtime) |
1224 | 0 | { |
1225 | 0 | time_t now = time_mono(NULL); |
1226 | |
|
1227 | 0 | if (absolute_endtime) { |
1228 | 0 | got_alarm = 0; |
1229 | 0 | CatchSignal(SIGALRM, gotalarm_sig); |
1230 | 0 | alarm(absolute_endtime - now); |
1231 | 0 | } |
1232 | |
|
1233 | 0 | if (ldap_state->pid != getpid()) { |
1234 | 0 | smbldap_close(ldap_state); |
1235 | 0 | } |
1236 | 0 | } |
1237 | | |
1238 | | static void get_ldap_errs(struct smbldap_state *ldap_state, char **pp_ld_error, int *p_ld_errno) |
1239 | 0 | { |
1240 | 0 | ldap_get_option(smbldap_get_ldap(ldap_state), |
1241 | 0 | LDAP_OPT_ERROR_NUMBER, p_ld_errno); |
1242 | |
|
1243 | 0 | ldap_get_option(smbldap_get_ldap(ldap_state), |
1244 | 0 | LDAP_OPT_ERROR_STRING, pp_ld_error); |
1245 | 0 | } |
1246 | | |
1247 | | static int get_cached_ldap_connect(struct smbldap_state *ldap_state, time_t abs_endtime) |
1248 | 0 | { |
1249 | 0 | int attempts = 0; |
1250 | |
|
1251 | 0 | while (1) { |
1252 | 0 | int rc; |
1253 | 0 | time_t now; |
1254 | |
|
1255 | 0 | now = time_mono(NULL); |
1256 | 0 | ldap_state->last_use = now; |
1257 | |
|
1258 | 0 | if (abs_endtime && now > abs_endtime) { |
1259 | 0 | smbldap_close(ldap_state); |
1260 | 0 | return LDAP_TIMEOUT; |
1261 | 0 | } |
1262 | | |
1263 | 0 | rc = smbldap_open(ldap_state); |
1264 | |
|
1265 | 0 | if (rc == LDAP_SUCCESS) { |
1266 | 0 | return LDAP_SUCCESS; |
1267 | 0 | } |
1268 | | |
1269 | 0 | attempts++; |
1270 | 0 | DEBUG(1, ("Connection to LDAP server failed for the " |
1271 | 0 | "%d try!\n", attempts)); |
1272 | |
|
1273 | 0 | if (rc == LDAP_INSUFFICIENT_ACCESS) { |
1274 | | /* The fact that we are non-root or any other |
1275 | | * access-denied condition will not change in the next |
1276 | | * round of trying */ |
1277 | 0 | return rc; |
1278 | 0 | } |
1279 | | |
1280 | 0 | if (got_alarm) { |
1281 | 0 | smbldap_close(ldap_state); |
1282 | 0 | return LDAP_TIMEOUT; |
1283 | 0 | } |
1284 | | |
1285 | 0 | smb_msleep(1000); |
1286 | |
|
1287 | 0 | if (got_alarm) { |
1288 | 0 | smbldap_close(ldap_state); |
1289 | 0 | return LDAP_TIMEOUT; |
1290 | 0 | } |
1291 | 0 | } |
1292 | 0 | } |
1293 | | |
1294 | | /********************************************************************* |
1295 | | ********************************************************************/ |
1296 | | |
1297 | | static int smbldap_search_ext(struct smbldap_state *ldap_state, |
1298 | | const char *base, int scope, const char *filter, |
1299 | | const char *attrs[], int attrsonly, |
1300 | | LDAPControl **sctrls, LDAPControl **cctrls, |
1301 | | int sizelimit, LDAPMessage **res) |
1302 | 0 | { |
1303 | 0 | int rc = LDAP_SERVER_DOWN; |
1304 | 0 | char *utf8_filter; |
1305 | 0 | int to = lp_ldap_timeout(); |
1306 | 0 | time_t abs_endtime = calc_ldap_abs_endtime(to); |
1307 | 0 | struct timeval timeout; |
1308 | 0 | struct timeval *timeout_ptr = NULL; |
1309 | 0 | size_t converted_size; |
1310 | |
|
1311 | 0 | SMB_ASSERT(ldap_state); |
1312 | | |
1313 | 0 | DEBUG(5,("smbldap_search_ext: base => [%s], filter => [%s], " |
1314 | 0 | "scope => [%d]\n", base, filter, scope)); |
1315 | |
|
1316 | 0 | if (ldap_state->last_rebind.tv_sec > 0) { |
1317 | 0 | struct timeval tval; |
1318 | 0 | struct timespec ts; |
1319 | 0 | int64_t tdiff = 0; |
1320 | 0 | int sleep_time = 0; |
1321 | |
|
1322 | 0 | clock_gettime_mono(&ts); |
1323 | 0 | tval = convert_timespec_to_timeval(ts); |
1324 | |
|
1325 | 0 | tdiff = usec_time_diff(&tval, &ldap_state->last_rebind); |
1326 | 0 | tdiff /= 1000; /* Convert to milliseconds. */ |
1327 | |
|
1328 | 0 | sleep_time = lp_ldap_replication_sleep()-(int)tdiff; |
1329 | 0 | sleep_time = MIN(sleep_time, MAX_LDAP_REPLICATION_SLEEP_TIME); |
1330 | |
|
1331 | 0 | if (sleep_time > 0) { |
1332 | | /* we wait for the LDAP replication */ |
1333 | 0 | DEBUG(5,("smbldap_search_ext: waiting %d milliseconds " |
1334 | 0 | "for LDAP replication.\n",sleep_time)); |
1335 | 0 | smb_msleep(sleep_time); |
1336 | 0 | DEBUG(5,("smbldap_search_ext: go on!\n")); |
1337 | 0 | } |
1338 | 0 | ZERO_STRUCT(ldap_state->last_rebind); |
1339 | 0 | } |
1340 | |
|
1341 | 0 | if (!push_utf8_talloc(talloc_tos(), &utf8_filter, filter, &converted_size)) { |
1342 | 0 | return LDAP_NO_MEMORY; |
1343 | 0 | } |
1344 | | |
1345 | | /* Setup remote timeout for the ldap_search_ext_s call. */ |
1346 | 0 | if (to) { |
1347 | 0 | timeout.tv_sec = to; |
1348 | 0 | timeout.tv_usec = 0; |
1349 | 0 | timeout_ptr = &timeout; |
1350 | 0 | } |
1351 | |
|
1352 | 0 | setup_ldap_local_alarm(ldap_state, abs_endtime); |
1353 | |
|
1354 | 0 | while (1) { |
1355 | 0 | char *ld_error = NULL; |
1356 | 0 | int ld_errno; |
1357 | |
|
1358 | 0 | rc = get_cached_ldap_connect(ldap_state, abs_endtime); |
1359 | 0 | if (rc != LDAP_SUCCESS) { |
1360 | 0 | break; |
1361 | 0 | } |
1362 | | |
1363 | 0 | rc = ldap_search_ext_s(smbldap_get_ldap(ldap_state), |
1364 | 0 | base, scope, |
1365 | 0 | utf8_filter, |
1366 | 0 | discard_const_p(char *, attrs), |
1367 | 0 | attrsonly, sctrls, cctrls, timeout_ptr, |
1368 | 0 | sizelimit, res); |
1369 | 0 | if (rc == LDAP_SUCCESS) { |
1370 | 0 | break; |
1371 | 0 | } |
1372 | | |
1373 | 0 | get_ldap_errs(ldap_state, &ld_error, &ld_errno); |
1374 | |
|
1375 | 0 | DEBUG(10, ("Failed search for base: %s, error: %d (%s) " |
1376 | 0 | "(%s)\n", base, ld_errno, |
1377 | 0 | ldap_err2string(rc), |
1378 | 0 | ld_error ? ld_error : "unknown")); |
1379 | 0 | SAFE_FREE(ld_error); |
1380 | |
|
1381 | 0 | if (ld_errno != LDAP_SERVER_DOWN) { |
1382 | 0 | break; |
1383 | 0 | } |
1384 | 0 | ldap_unbind(smbldap_get_ldap(ldap_state)); |
1385 | 0 | ldap_state->ldap_struct = NULL; |
1386 | 0 | } |
1387 | |
|
1388 | 0 | TALLOC_FREE(utf8_filter); |
1389 | 0 | return end_ldap_local_alarm(abs_endtime, rc); |
1390 | 0 | } |
1391 | | |
1392 | | int smbldap_search(struct smbldap_state *ldap_state, |
1393 | | const char *base, int scope, const char *filter, |
1394 | | const char *attrs[], int attrsonly, |
1395 | | LDAPMessage **res) |
1396 | 0 | { |
1397 | 0 | return smbldap_search_ext(ldap_state, base, scope, filter, attrs, |
1398 | 0 | attrsonly, NULL, NULL, LDAP_NO_LIMIT, res); |
1399 | 0 | } |
1400 | | |
1401 | | int smbldap_search_paged(struct smbldap_state *ldap_state, |
1402 | | const char *base, int scope, const char *filter, |
1403 | | const char **attrs, int attrsonly, int pagesize, |
1404 | | LDAPMessage **res, void **cookie) |
1405 | 0 | { |
1406 | 0 | LDAPControl pr; |
1407 | 0 | LDAPControl **rcontrols; |
1408 | 0 | LDAPControl *controls[2] = { NULL, NULL}; |
1409 | 0 | BerElement *cookie_be = NULL; |
1410 | 0 | struct berval *cookie_bv = NULL; |
1411 | 0 | int tmp = 0, i, rc; |
1412 | 0 | bool critical = True; |
1413 | |
|
1414 | 0 | *res = NULL; |
1415 | |
|
1416 | 0 | DEBUG(3,("smbldap_search_paged: base => [%s], filter => [%s]," |
1417 | 0 | "scope => [%d], pagesize => [%d]\n", |
1418 | 0 | base, filter, scope, pagesize)); |
1419 | |
|
1420 | 0 | cookie_be = ber_alloc_t(LBER_USE_DER); |
1421 | 0 | if (cookie_be == NULL) { |
1422 | 0 | DEBUG(0,("smbldap_create_page_control: ber_alloc_t returns " |
1423 | 0 | "NULL\n")); |
1424 | 0 | return LDAP_NO_MEMORY; |
1425 | 0 | } |
1426 | | |
1427 | | /* construct cookie */ |
1428 | 0 | if (*cookie != NULL) { |
1429 | 0 | ber_printf(cookie_be, "{iO}", (ber_int_t) pagesize, *cookie); |
1430 | 0 | ber_bvfree((struct berval *)*cookie); /* don't need it from last time */ |
1431 | 0 | *cookie = NULL; |
1432 | 0 | } else { |
1433 | 0 | ber_printf(cookie_be, "{io}", (ber_int_t) pagesize, "", 0); |
1434 | 0 | } |
1435 | 0 | ber_flatten(cookie_be, &cookie_bv); |
1436 | |
|
1437 | 0 | pr.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID); |
1438 | 0 | pr.ldctl_iscritical = (char) critical; |
1439 | 0 | pr.ldctl_value.bv_len = cookie_bv->bv_len; |
1440 | 0 | pr.ldctl_value.bv_val = cookie_bv->bv_val; |
1441 | |
|
1442 | 0 | controls[0] = ≺ |
1443 | 0 | controls[1] = NULL; |
1444 | |
|
1445 | 0 | rc = smbldap_search_ext(ldap_state, base, scope, filter, attrs, |
1446 | 0 | 0, controls, NULL, LDAP_NO_LIMIT, res); |
1447 | |
|
1448 | 0 | ber_free(cookie_be, 1); |
1449 | 0 | ber_bvfree(cookie_bv); |
1450 | |
|
1451 | 0 | if (rc != 0) { |
1452 | 0 | DEBUG(3,("smbldap_search_paged: smbldap_search_ext(%s) " |
1453 | 0 | "failed with [%s]\n", filter, ldap_err2string(rc))); |
1454 | 0 | goto done; |
1455 | 0 | } |
1456 | | |
1457 | 0 | DEBUG(3,("smbldap_search_paged: search was successful\n")); |
1458 | |
|
1459 | 0 | rc = ldap_parse_result(smbldap_get_ldap(ldap_state), *res, NULL, NULL, |
1460 | 0 | NULL, NULL, &rcontrols, 0); |
1461 | 0 | if (rc != 0) { |
1462 | 0 | DEBUG(3,("smbldap_search_paged: ldap_parse_result failed " \ |
1463 | 0 | "with [%s]\n", ldap_err2string(rc))); |
1464 | 0 | goto done; |
1465 | 0 | } |
1466 | | |
1467 | 0 | if (rcontrols == NULL) |
1468 | 0 | goto done; |
1469 | | |
1470 | 0 | for (i=0; rcontrols[i]; i++) { |
1471 | |
|
1472 | 0 | if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) != 0) |
1473 | 0 | continue; |
1474 | | |
1475 | 0 | cookie_be = ber_init(&rcontrols[i]->ldctl_value); |
1476 | 0 | ber_scanf(cookie_be,"{iO}", &tmp, &cookie_bv); |
1477 | | /* the berval is the cookie, but must be freed when it is all |
1478 | | done */ |
1479 | 0 | if (cookie_bv->bv_len) |
1480 | 0 | *cookie=ber_bvdup(cookie_bv); |
1481 | 0 | else |
1482 | 0 | *cookie=NULL; |
1483 | 0 | ber_bvfree(cookie_bv); |
1484 | 0 | ber_free(cookie_be, 1); |
1485 | 0 | break; |
1486 | 0 | } |
1487 | 0 | ldap_controls_free(rcontrols); |
1488 | 0 | done: |
1489 | 0 | return rc; |
1490 | 0 | } |
1491 | | |
1492 | | int smbldap_modify(struct smbldap_state *ldap_state, const char *dn, LDAPMod *attrs[]) |
1493 | 0 | { |
1494 | 0 | int rc = LDAP_SERVER_DOWN; |
1495 | 0 | char *utf8_dn; |
1496 | 0 | time_t abs_endtime = calc_ldap_abs_endtime(lp_ldap_timeout()); |
1497 | 0 | size_t converted_size; |
1498 | |
|
1499 | 0 | SMB_ASSERT(ldap_state); |
1500 | | |
1501 | 0 | DEBUG(5,("smbldap_modify: dn => [%s]\n", dn )); |
1502 | |
|
1503 | 0 | if (!push_utf8_talloc(talloc_tos(), &utf8_dn, dn, &converted_size)) { |
1504 | 0 | return LDAP_NO_MEMORY; |
1505 | 0 | } |
1506 | | |
1507 | 0 | setup_ldap_local_alarm(ldap_state, abs_endtime); |
1508 | |
|
1509 | 0 | while (1) { |
1510 | 0 | char *ld_error = NULL; |
1511 | 0 | int ld_errno; |
1512 | |
|
1513 | 0 | rc = get_cached_ldap_connect(ldap_state, abs_endtime); |
1514 | 0 | if (rc != LDAP_SUCCESS) { |
1515 | 0 | break; |
1516 | 0 | } |
1517 | | |
1518 | 0 | rc = ldap_modify_s(smbldap_get_ldap(ldap_state), utf8_dn, |
1519 | 0 | attrs); |
1520 | 0 | if (rc == LDAP_SUCCESS) { |
1521 | 0 | break; |
1522 | 0 | } |
1523 | | |
1524 | 0 | get_ldap_errs(ldap_state, &ld_error, &ld_errno); |
1525 | |
|
1526 | 0 | DEBUG(10, ("Failed to modify dn: %s, error: %d (%s) " |
1527 | 0 | "(%s)\n", dn, ld_errno, |
1528 | 0 | ldap_err2string(rc), |
1529 | 0 | ld_error ? ld_error : "unknown")); |
1530 | 0 | SAFE_FREE(ld_error); |
1531 | |
|
1532 | 0 | if (ld_errno != LDAP_SERVER_DOWN) { |
1533 | 0 | break; |
1534 | 0 | } |
1535 | 0 | ldap_unbind(smbldap_get_ldap(ldap_state)); |
1536 | 0 | ldap_state->ldap_struct = NULL; |
1537 | 0 | } |
1538 | |
|
1539 | 0 | TALLOC_FREE(utf8_dn); |
1540 | 0 | return end_ldap_local_alarm(abs_endtime, rc); |
1541 | 0 | } |
1542 | | |
1543 | | int smbldap_add(struct smbldap_state *ldap_state, const char *dn, LDAPMod *attrs[]) |
1544 | 0 | { |
1545 | 0 | int rc = LDAP_SERVER_DOWN; |
1546 | 0 | char *utf8_dn; |
1547 | 0 | time_t abs_endtime = calc_ldap_abs_endtime(lp_ldap_timeout()); |
1548 | 0 | size_t converted_size; |
1549 | |
|
1550 | 0 | SMB_ASSERT(ldap_state); |
1551 | | |
1552 | 0 | DEBUG(5,("smbldap_add: dn => [%s]\n", dn )); |
1553 | |
|
1554 | 0 | if (!push_utf8_talloc(talloc_tos(), &utf8_dn, dn, &converted_size)) { |
1555 | 0 | return LDAP_NO_MEMORY; |
1556 | 0 | } |
1557 | | |
1558 | 0 | setup_ldap_local_alarm(ldap_state, abs_endtime); |
1559 | |
|
1560 | 0 | while (1) { |
1561 | 0 | char *ld_error = NULL; |
1562 | 0 | int ld_errno; |
1563 | |
|
1564 | 0 | rc = get_cached_ldap_connect(ldap_state, abs_endtime); |
1565 | 0 | if (rc != LDAP_SUCCESS) { |
1566 | 0 | break; |
1567 | 0 | } |
1568 | | |
1569 | 0 | rc = ldap_add_s(smbldap_get_ldap(ldap_state), utf8_dn, attrs); |
1570 | 0 | if (rc == LDAP_SUCCESS) { |
1571 | 0 | break; |
1572 | 0 | } |
1573 | | |
1574 | 0 | get_ldap_errs(ldap_state, &ld_error, &ld_errno); |
1575 | |
|
1576 | 0 | DEBUG(10, ("Failed to add dn: %s, error: %d (%s) " |
1577 | 0 | "(%s)\n", dn, ld_errno, |
1578 | 0 | ldap_err2string(rc), |
1579 | 0 | ld_error ? ld_error : "unknown")); |
1580 | 0 | SAFE_FREE(ld_error); |
1581 | |
|
1582 | 0 | if (ld_errno != LDAP_SERVER_DOWN) { |
1583 | 0 | break; |
1584 | 0 | } |
1585 | 0 | ldap_unbind(smbldap_get_ldap(ldap_state)); |
1586 | 0 | ldap_state->ldap_struct = NULL; |
1587 | 0 | } |
1588 | |
|
1589 | 0 | TALLOC_FREE(utf8_dn); |
1590 | 0 | return end_ldap_local_alarm(abs_endtime, rc); |
1591 | 0 | } |
1592 | | |
1593 | | int smbldap_delete(struct smbldap_state *ldap_state, const char *dn) |
1594 | 0 | { |
1595 | 0 | int rc = LDAP_SERVER_DOWN; |
1596 | 0 | char *utf8_dn; |
1597 | 0 | time_t abs_endtime = calc_ldap_abs_endtime(lp_ldap_timeout()); |
1598 | 0 | size_t converted_size; |
1599 | |
|
1600 | 0 | SMB_ASSERT(ldap_state); |
1601 | | |
1602 | 0 | DEBUG(5,("smbldap_delete: dn => [%s]\n", dn )); |
1603 | |
|
1604 | 0 | if (!push_utf8_talloc(talloc_tos(), &utf8_dn, dn, &converted_size)) { |
1605 | 0 | return LDAP_NO_MEMORY; |
1606 | 0 | } |
1607 | | |
1608 | 0 | setup_ldap_local_alarm(ldap_state, abs_endtime); |
1609 | |
|
1610 | 0 | while (1) { |
1611 | 0 | char *ld_error = NULL; |
1612 | 0 | int ld_errno; |
1613 | |
|
1614 | 0 | rc = get_cached_ldap_connect(ldap_state, abs_endtime); |
1615 | 0 | if (rc != LDAP_SUCCESS) { |
1616 | 0 | break; |
1617 | 0 | } |
1618 | | |
1619 | 0 | rc = ldap_delete_s(smbldap_get_ldap(ldap_state), utf8_dn); |
1620 | 0 | if (rc == LDAP_SUCCESS) { |
1621 | 0 | break; |
1622 | 0 | } |
1623 | | |
1624 | 0 | get_ldap_errs(ldap_state, &ld_error, &ld_errno); |
1625 | |
|
1626 | 0 | DEBUG(10, ("Failed to delete dn: %s, error: %d (%s) " |
1627 | 0 | "(%s)\n", dn, ld_errno, |
1628 | 0 | ldap_err2string(rc), |
1629 | 0 | ld_error ? ld_error : "unknown")); |
1630 | 0 | SAFE_FREE(ld_error); |
1631 | |
|
1632 | 0 | if (ld_errno != LDAP_SERVER_DOWN) { |
1633 | 0 | break; |
1634 | 0 | } |
1635 | 0 | ldap_unbind(smbldap_get_ldap(ldap_state)); |
1636 | 0 | ldap_state->ldap_struct = NULL; |
1637 | 0 | } |
1638 | |
|
1639 | 0 | TALLOC_FREE(utf8_dn); |
1640 | 0 | return end_ldap_local_alarm(abs_endtime, rc); |
1641 | 0 | } |
1642 | | |
1643 | | int smbldap_extended_operation(struct smbldap_state *ldap_state, |
1644 | | LDAP_CONST char *reqoid, struct berval *reqdata, |
1645 | | LDAPControl **serverctrls, LDAPControl **clientctrls, |
1646 | | char **retoidp, struct berval **retdatap) |
1647 | 0 | { |
1648 | 0 | int rc = LDAP_SERVER_DOWN; |
1649 | 0 | time_t abs_endtime = calc_ldap_abs_endtime(lp_ldap_timeout()); |
1650 | |
|
1651 | 0 | if (!ldap_state) |
1652 | 0 | return (-1); |
1653 | | |
1654 | 0 | setup_ldap_local_alarm(ldap_state, abs_endtime); |
1655 | |
|
1656 | 0 | while (1) { |
1657 | 0 | char *ld_error = NULL; |
1658 | 0 | int ld_errno; |
1659 | |
|
1660 | 0 | rc = get_cached_ldap_connect(ldap_state, abs_endtime); |
1661 | 0 | if (rc != LDAP_SUCCESS) { |
1662 | 0 | break; |
1663 | 0 | } |
1664 | | |
1665 | 0 | rc = ldap_extended_operation_s(smbldap_get_ldap(ldap_state), |
1666 | 0 | reqoid, |
1667 | 0 | reqdata, serverctrls, |
1668 | 0 | clientctrls, retoidp, retdatap); |
1669 | 0 | if (rc == LDAP_SUCCESS) { |
1670 | 0 | break; |
1671 | 0 | } |
1672 | | |
1673 | 0 | get_ldap_errs(ldap_state, &ld_error, &ld_errno); |
1674 | |
|
1675 | 0 | DEBUG(10, ("Extended operation failed with error: " |
1676 | 0 | "%d (%s) (%s)\n", ld_errno, |
1677 | 0 | ldap_err2string(rc), |
1678 | 0 | ld_error ? ld_error : "unknown")); |
1679 | 0 | SAFE_FREE(ld_error); |
1680 | |
|
1681 | 0 | if (ld_errno != LDAP_SERVER_DOWN) { |
1682 | 0 | break; |
1683 | 0 | } |
1684 | 0 | ldap_unbind(smbldap_get_ldap(ldap_state)); |
1685 | 0 | ldap_state->ldap_struct = NULL; |
1686 | 0 | } |
1687 | |
|
1688 | 0 | return end_ldap_local_alarm(abs_endtime, rc); |
1689 | 0 | } |
1690 | | |
1691 | | /******************************************************************* |
1692 | | run the search by name. |
1693 | | ******************************************************************/ |
1694 | | int smbldap_search_suffix (struct smbldap_state *ldap_state, |
1695 | | const char *filter, const char **search_attr, |
1696 | | LDAPMessage ** result) |
1697 | 0 | { |
1698 | 0 | return smbldap_search(ldap_state, lp_ldap_suffix(), |
1699 | 0 | LDAP_SCOPE_SUBTREE, |
1700 | 0 | filter, search_attr, 0, result); |
1701 | 0 | } |
1702 | | |
1703 | | static void smbldap_idle_fn(struct tevent_context *tevent_ctx, |
1704 | | struct tevent_timer *te, |
1705 | | struct timeval now_abs, |
1706 | | void *private_data) |
1707 | 0 | { |
1708 | 0 | struct smbldap_state *state = (struct smbldap_state *)private_data; |
1709 | |
|
1710 | 0 | TALLOC_FREE(state->idle_event); |
1711 | |
|
1712 | 0 | if (smbldap_get_ldap(state) == NULL) { |
1713 | 0 | DEBUG(10,("ldap connection not connected...\n")); |
1714 | 0 | return; |
1715 | 0 | } |
1716 | | |
1717 | 0 | if ((state->last_use+SMBLDAP_IDLE_TIME) > time_mono(NULL)) { |
1718 | 0 | DEBUG(10,("ldap connection not idle...\n")); |
1719 | | |
1720 | | /* this needs to be made monotonic clock aware inside tevent: */ |
1721 | 0 | state->idle_event = tevent_add_timer( |
1722 | 0 | tevent_ctx, state, |
1723 | 0 | timeval_add(&now_abs, SMBLDAP_IDLE_TIME, 0), |
1724 | 0 | smbldap_idle_fn, |
1725 | 0 | private_data); |
1726 | 0 | return; |
1727 | 0 | } |
1728 | | |
1729 | 0 | DEBUG(7,("ldap connection idle...closing connection\n")); |
1730 | 0 | smbldap_close(state); |
1731 | 0 | } |
1732 | | |
1733 | | /********************************************************************** |
1734 | | Housekeeping |
1735 | | *********************************************************************/ |
1736 | | |
1737 | | void smbldap_free_struct(struct smbldap_state **ldap_state) |
1738 | 0 | { |
1739 | 0 | smbldap_close(*ldap_state); |
1740 | |
|
1741 | 0 | if ((*ldap_state)->bind_secret) { |
1742 | 0 | memset((*ldap_state)->bind_secret, '\0', strlen((*ldap_state)->bind_secret)); |
1743 | 0 | } |
1744 | |
|
1745 | 0 | SAFE_FREE((*ldap_state)->bind_dn); |
1746 | 0 | BURN_FREE_STR((*ldap_state)->bind_secret); |
1747 | 0 | smbldap_set_bind_callback(*ldap_state, NULL, NULL); |
1748 | |
|
1749 | 0 | TALLOC_FREE(*ldap_state); |
1750 | | |
1751 | | /* No need to free any further, as it is talloc()ed */ |
1752 | 0 | } |
1753 | | |
1754 | | static int smbldap_state_destructor(struct smbldap_state *state) |
1755 | 0 | { |
1756 | 0 | smbldap_free_struct(&state); |
1757 | 0 | return 0; |
1758 | 0 | } |
1759 | | |
1760 | | |
1761 | | /********************************************************************** |
1762 | | Initialise the 'general' ldap structures, on which ldap operations may be conducted |
1763 | | *********************************************************************/ |
1764 | | |
1765 | | NTSTATUS smbldap_init(TALLOC_CTX *mem_ctx, struct tevent_context *tevent_ctx, |
1766 | | const char *location, |
1767 | | bool anon, |
1768 | | const char *bind_dn, |
1769 | | const char *bind_secret, |
1770 | | struct smbldap_state **smbldap_state) |
1771 | 0 | { |
1772 | 0 | *smbldap_state = talloc_zero(mem_ctx, struct smbldap_state); |
1773 | 0 | if (!*smbldap_state) { |
1774 | 0 | DEBUG(0, ("talloc() failed for ldapsam private_data!\n")); |
1775 | 0 | return NT_STATUS_NO_MEMORY; |
1776 | 0 | } |
1777 | | |
1778 | 0 | if (location) { |
1779 | 0 | (*smbldap_state)->uri = talloc_strdup(mem_ctx, location); |
1780 | 0 | } else { |
1781 | 0 | (*smbldap_state)->uri = "ldap://localhost"; |
1782 | 0 | } |
1783 | |
|
1784 | 0 | (*smbldap_state)->tevent_context = tevent_ctx; |
1785 | |
|
1786 | 0 | if (bind_dn && bind_secret) { |
1787 | 0 | smbldap_set_creds(*smbldap_state, anon, bind_dn, bind_secret); |
1788 | 0 | } |
1789 | |
|
1790 | 0 | talloc_set_destructor(*smbldap_state, smbldap_state_destructor); |
1791 | 0 | return NT_STATUS_OK; |
1792 | 0 | } |
1793 | | |
1794 | | char *smbldap_talloc_dn(TALLOC_CTX *mem_ctx, LDAP *ld, |
1795 | | LDAPMessage *entry) |
1796 | 0 | { |
1797 | 0 | char *utf8_dn, *unix_dn; |
1798 | 0 | size_t converted_size; |
1799 | |
|
1800 | 0 | utf8_dn = ldap_get_dn(ld, entry); |
1801 | 0 | if (!utf8_dn) { |
1802 | 0 | DEBUG (5, ("smbldap_talloc_dn: ldap_get_dn failed\n")); |
1803 | 0 | return NULL; |
1804 | 0 | } |
1805 | 0 | if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) { |
1806 | 0 | DEBUG (0, ("smbldap_talloc_dn: String conversion failure utf8 " |
1807 | 0 | "[%s]\n", utf8_dn)); |
1808 | 0 | return NULL; |
1809 | 0 | } |
1810 | 0 | ldap_memfree(utf8_dn); |
1811 | 0 | return unix_dn; |
1812 | 0 | } |
1813 | | |
1814 | | /******************************************************************* |
1815 | | Check if root-dse has a certain Control or Extension |
1816 | | ********************************************************************/ |
1817 | | |
1818 | | static bool smbldap_check_root_dse(LDAP *ld, const char **attrs, const char *value) |
1819 | 0 | { |
1820 | 0 | LDAPMessage *msg = NULL; |
1821 | 0 | LDAPMessage *entry = NULL; |
1822 | 0 | char **values = NULL; |
1823 | 0 | int rc, num_result, num_values, i; |
1824 | 0 | bool result = False; |
1825 | |
|
1826 | 0 | if (!attrs[0]) { |
1827 | 0 | DEBUG(3,("smbldap_check_root_dse: nothing to look for\n")); |
1828 | 0 | return False; |
1829 | 0 | } |
1830 | | |
1831 | 0 | if (!strequal(attrs[0], "supportedExtension") && |
1832 | 0 | !strequal(attrs[0], "supportedControl") && |
1833 | 0 | !strequal(attrs[0], "namingContexts")) { |
1834 | 0 | DEBUG(3,("smbldap_check_root_dse: no idea what to query root-dse for: %s ?\n", attrs[0])); |
1835 | 0 | return False; |
1836 | 0 | } |
1837 | | |
1838 | 0 | rc = ldap_search_s(ld, "", LDAP_SCOPE_BASE, |
1839 | 0 | "(objectclass=*)", discard_const_p(char *, attrs), 0 , &msg); |
1840 | |
|
1841 | 0 | if (rc != LDAP_SUCCESS) { |
1842 | 0 | DEBUG(3,("smbldap_check_root_dse: Could not search rootDSE\n")); |
1843 | 0 | return False; |
1844 | 0 | } |
1845 | | |
1846 | 0 | num_result = ldap_count_entries(ld, msg); |
1847 | |
|
1848 | 0 | if (num_result != 1) { |
1849 | 0 | DEBUG(3,("smbldap_check_root_dse: Expected one rootDSE, got %d\n", num_result)); |
1850 | 0 | goto done; |
1851 | 0 | } |
1852 | | |
1853 | 0 | entry = ldap_first_entry(ld, msg); |
1854 | |
|
1855 | 0 | if (entry == NULL) { |
1856 | 0 | DEBUG(3,("smbldap_check_root_dse: Could not retrieve rootDSE\n")); |
1857 | 0 | goto done; |
1858 | 0 | } |
1859 | | |
1860 | 0 | values = ldap_get_values(ld, entry, attrs[0]); |
1861 | |
|
1862 | 0 | if (values == NULL) { |
1863 | 0 | DEBUG(5,("smbldap_check_root_dse: LDAP Server does not support any %s\n", attrs[0])); |
1864 | 0 | goto done; |
1865 | 0 | } |
1866 | | |
1867 | 0 | num_values = ldap_count_values(values); |
1868 | |
|
1869 | 0 | if (num_values == 0) { |
1870 | 0 | DEBUG(5,("smbldap_check_root_dse: LDAP Server does not have any %s\n", attrs[0])); |
1871 | 0 | goto done; |
1872 | 0 | } |
1873 | | |
1874 | 0 | for (i=0; i<num_values; i++) { |
1875 | 0 | if (strcmp(values[i], value) == 0) |
1876 | 0 | result = True; |
1877 | 0 | } |
1878 | | |
1879 | |
|
1880 | 0 | done: |
1881 | 0 | if (values != NULL) |
1882 | 0 | ldap_value_free(values); |
1883 | 0 | if (msg != NULL) |
1884 | 0 | ldap_msgfree(msg); |
1885 | |
|
1886 | 0 | return result; |
1887 | |
|
1888 | 0 | } |
1889 | | |
1890 | | /******************************************************************* |
1891 | | Check if LDAP-Server supports a certain Control (OID in string format) |
1892 | | ********************************************************************/ |
1893 | | |
1894 | | bool smbldap_has_control(LDAP *ld, const char *control) |
1895 | 0 | { |
1896 | 0 | const char *attrs[] = { "supportedControl", NULL }; |
1897 | 0 | return smbldap_check_root_dse(ld, attrs, control); |
1898 | 0 | } |
1899 | | |
1900 | | /******************************************************************* |
1901 | | Check if LDAP-Server supports a certain Extension (OID in string format) |
1902 | | ********************************************************************/ |
1903 | | |
1904 | | bool smbldap_has_extension(LDAP *ld, const char *extension) |
1905 | 0 | { |
1906 | 0 | const char *attrs[] = { "supportedExtension", NULL }; |
1907 | 0 | return smbldap_check_root_dse(ld, attrs, extension); |
1908 | 0 | } |
1909 | | |
1910 | | /******************************************************************* |
1911 | | Check if LDAP-Server holds a given namingContext |
1912 | | ********************************************************************/ |
1913 | | |
1914 | | bool smbldap_has_naming_context(LDAP *ld, const char *naming_context) |
1915 | 0 | { |
1916 | 0 | const char *attrs[] = { "namingContexts", NULL }; |
1917 | 0 | return smbldap_check_root_dse(ld, attrs, naming_context); |
1918 | 0 | } |
1919 | | |
1920 | | bool smbldap_set_creds(struct smbldap_state *ldap_state, bool anon, const char *dn, const char *secret) |
1921 | 0 | { |
1922 | 0 | ldap_state->anonymous = anon; |
1923 | | |
1924 | | /* free any previously set credential */ |
1925 | |
|
1926 | 0 | SAFE_FREE(ldap_state->bind_dn); |
1927 | 0 | smbldap_set_bind_callback(ldap_state, NULL, NULL); |
1928 | |
|
1929 | 0 | if (ldap_state->bind_secret) { |
1930 | | /* make sure secrets are zeroed out of memory */ |
1931 | 0 | memset(ldap_state->bind_secret, '\0', strlen(ldap_state->bind_secret)); |
1932 | 0 | SAFE_FREE(ldap_state->bind_secret); |
1933 | 0 | } |
1934 | |
|
1935 | 0 | if ( ! anon) { |
1936 | 0 | ldap_state->bind_dn = SMB_STRDUP(dn); |
1937 | 0 | ldap_state->bind_secret = SMB_STRDUP(secret); |
1938 | 0 | } |
1939 | |
|
1940 | | return True; |
1941 | 0 | } |