Mercurial > dovecot > core-2.2
comparison src/lib-dict-extra/dict-ldap.c @ 20066:f215c409c86d
lib-dict: Moved dict-ldap to lib-dict-extra
This also allows moving lib-ldap away from LIBDOVECOT_SUBDIRS in
src/Makefile.am, which was wrong because it's not really part of
libdovecot.la.
author | Timo Sirainen <timo.sirainen@dovecot.fi> |
---|---|
date | Thu, 21 Apr 2016 18:58:10 +0300 |
parents | src/lib-dict/dict-ldap.c@0ef427d75bab |
children | af1d619d2f55 |
comparison
equal
deleted
inserted
replaced
20065:50d86fbcfd28 | 20066:f215c409c86d |
---|---|
1 /* Copyright (c) 2016 Dovecot authors, see the included COPYING memcached */ | |
2 | |
3 #include "lib.h" | |
4 #include "array.h" | |
5 #include "module-dir.h" | |
6 #include "str.h" | |
7 #include "istream.h" | |
8 #include "ostream.h" | |
9 #include "var-expand.h" | |
10 #include "connection.h" | |
11 #include "llist.h" | |
12 #include "ldap-client.h" | |
13 #include "dict.h" | |
14 #include "dict-private.h" | |
15 #include "dict-ldap-settings.h" | |
16 | |
17 struct ldap_dict; | |
18 | |
19 struct dict_ldap_op { | |
20 struct ldap_dict *dict; | |
21 const struct dict_ldap_map *map; | |
22 pool_t pool; | |
23 unsigned long txid; | |
24 struct dict_lookup_result res; | |
25 dict_lookup_callback_t *callback; | |
26 void *callback_ctx; | |
27 }; | |
28 | |
29 struct ldap_dict { | |
30 struct dict dict; | |
31 struct dict_ldap_settings *set; | |
32 | |
33 const char *uri; | |
34 const char *username; | |
35 const char *base_dn; | |
36 enum ldap_scope scope; | |
37 | |
38 pool_t pool; | |
39 | |
40 struct ldap_client *client; | |
41 struct ioloop *ioloop, *prev_ioloop; | |
42 | |
43 unsigned long last_txid; | |
44 unsigned int pending; | |
45 | |
46 struct ldap_dict *prev,*next; | |
47 }; | |
48 | |
49 static | |
50 struct ldap_dict *ldap_dict_list; | |
51 | |
52 static | |
53 void ldap_dict_lookup_async(struct dict *dict, const char *key, | |
54 dict_lookup_callback_t *callback, void *context); | |
55 | |
56 | |
57 static bool | |
58 dict_ldap_map_match(const struct dict_ldap_map *map, const char *path, | |
59 ARRAY_TYPE(const_string) *values, unsigned int *pat_len_r, | |
60 unsigned int *path_len_r, bool partial_ok, bool recurse) | |
61 { | |
62 const char *path_start = path; | |
63 const char *pat, *attribute, *p; | |
64 unsigned int len; | |
65 | |
66 array_clear(values); | |
67 pat = map->pattern; | |
68 while (*pat != '\0' && *path != '\0') { | |
69 if (*pat == '$') { | |
70 /* variable */ | |
71 pat++; | |
72 if (*pat == '\0') { | |
73 /* pattern ended with this variable, | |
74 it'll match the rest of the path */ | |
75 len = strlen(path); | |
76 if (partial_ok) { | |
77 /* iterating - the last field never | |
78 matches fully. if there's a trailing | |
79 '/', drop it. */ | |
80 pat--; | |
81 if (path[len-1] == '/') { | |
82 attribute = t_strndup(path, len-1); | |
83 array_append(values, &attribute, 1); | |
84 } else { | |
85 array_append(values, &path, 1); | |
86 } | |
87 } else { | |
88 array_append(values, &path, 1); | |
89 path += len; | |
90 } | |
91 *path_len_r = path - path_start; | |
92 *pat_len_r = pat - map->pattern; | |
93 return TRUE; | |
94 } | |
95 /* pattern matches until the next '/' in path */ | |
96 p = strchr(path, '/'); | |
97 if (p != NULL) { | |
98 attribute = t_strdup_until(path, p); | |
99 array_append(values, &attribute, 1); | |
100 path = p; | |
101 } else { | |
102 /* no '/' anymore, but it'll still match a | |
103 partial */ | |
104 array_append(values, &path, 1); | |
105 path += strlen(path); | |
106 pat++; | |
107 } | |
108 } else if (*pat == *path) { | |
109 pat++; | |
110 path++; | |
111 } else { | |
112 return FALSE; | |
113 } | |
114 } | |
115 | |
116 *path_len_r = path - path_start; | |
117 *pat_len_r = pat - map->pattern; | |
118 | |
119 if (*pat == '\0') | |
120 return *path == '\0'; | |
121 else if (!partial_ok) | |
122 return FALSE; | |
123 else { | |
124 /* partial matches must end with '/'. */ | |
125 if (pat != map->pattern && pat[-1] != '/') | |
126 return FALSE; | |
127 /* if we're not recursing, there should be only one $variable | |
128 left. */ | |
129 if (recurse) | |
130 return TRUE; | |
131 return pat[0] == '$' && strchr(pat, '/') == NULL; | |
132 } | |
133 } | |
134 | |
135 static const struct dict_ldap_map * | |
136 ldap_dict_find_map(struct ldap_dict *dict, const char *path, | |
137 ARRAY_TYPE(const_string) *values) | |
138 { | |
139 const struct dict_ldap_map *maps; | |
140 unsigned int i, count, len; | |
141 | |
142 t_array_init(values, dict->set->max_attribute_count); | |
143 maps = array_get(&dict->set->maps, &count); | |
144 for (i = 0; i < count; i++) { | |
145 if (dict_ldap_map_match(&maps[i], path, values, | |
146 &len, &len, FALSE, FALSE)) | |
147 return &maps[i]; | |
148 } | |
149 return NULL; | |
150 } | |
151 | |
152 static | |
153 int dict_ldap_connect(struct ldap_dict *dict, const char **error_r) | |
154 { | |
155 struct ldap_client_settings set; | |
156 memset(&set, 0, sizeof(set)); | |
157 set.uri = dict->set->uri; | |
158 set.bind_dn = dict->set->bind_dn; | |
159 set.password = dict->set->password; | |
160 set.timeout_secs = dict->set->timeout; | |
161 set.max_idle_time_secs = dict->set->max_idle_time; | |
162 set.debug = dict->set->debug; | |
163 set.require_ssl = dict->set->require_ssl; | |
164 set.start_tls = dict->set->start_tls; | |
165 return ldap_client_init(&set, &dict->client, error_r); | |
166 } | |
167 | |
168 static | |
169 const char* ldap_dict_build_query(struct ldap_dict *dict, const struct dict_ldap_map *map, ARRAY_TYPE(const_string) *values, bool priv) | |
170 { | |
171 const char *template; | |
172 ARRAY(struct var_expand_table) exp; | |
173 struct var_expand_table entry; | |
174 string_t *query = t_str_new(64); | |
175 | |
176 t_array_init(&exp, 8); | |
177 entry.key = '\0'; | |
178 entry.value = dict->username; | |
179 entry.long_key = "username"; | |
180 array_append(&exp, &entry, 1); | |
181 | |
182 if (priv) { | |
183 template = t_strdup_printf("(&(%s=%s)%s)", map->username_attribute, "%{username}", map->filter); | |
184 } else { | |
185 template = map->filter; | |
186 } | |
187 | |
188 for(size_t i = 0; i < array_count(values) && i < array_count(&(map->ldap_attributes)); i++) { | |
189 struct var_expand_table entry; | |
190 entry.value = *array_idx(values, i); | |
191 entry.long_key = *array_idx(&(map->ldap_attributes), i); | |
192 array_append(&exp, &entry, 1); | |
193 } | |
194 | |
195 array_append_zero(&exp); | |
196 | |
197 var_expand(query, template, array_idx(&exp, 0)); | |
198 | |
199 return str_c(query); | |
200 } | |
201 | |
202 static | |
203 int ldap_dict_create(struct dict *dict_driver, const char *uri, | |
204 const struct dict_settings *set, | |
205 struct dict **dict_r, const char **error_r) | |
206 { | |
207 pool_t pool = pool_alloconly_create("ldap dict", 2048); | |
208 struct ldap_dict *dict = p_new(pool, struct ldap_dict, 1); | |
209 dict->pool = pool; | |
210 dict->dict = *dict_driver; | |
211 dict->username = p_strdup(pool, set->username); | |
212 dict->uri = p_strdup(pool, uri); | |
213 dict->set = dict_ldap_settings_read(pool, uri, error_r); | |
214 | |
215 if (dict->set == NULL) { | |
216 pool_unref(&pool); | |
217 return -1; | |
218 } | |
219 | |
220 if (dict_ldap_connect(dict, error_r) < 0) { | |
221 pool_unref(&pool); | |
222 return -1; | |
223 } | |
224 | |
225 *dict_r = (struct dict*)dict; | |
226 *error_r = NULL; | |
227 | |
228 DLLIST_PREPEND(&ldap_dict_list, dict); | |
229 | |
230 return 0; | |
231 } | |
232 | |
233 static | |
234 int ldap_dict_init(struct dict *dict_driver, const char *uri, | |
235 const struct dict_settings *set, | |
236 struct dict **dict_r, const char **error_r) | |
237 { | |
238 /* reuse possible existing entry */ | |
239 for(struct ldap_dict *ptr = ldap_dict_list; | |
240 ptr != NULL; | |
241 ptr = ptr->next) { | |
242 if (strcmp(ptr->uri, uri) == 0) { | |
243 *dict_r = (struct dict*)ptr; | |
244 return 0; | |
245 } | |
246 } | |
247 return ldap_dict_create(dict_driver, uri, set, dict_r, error_r); | |
248 } | |
249 | |
250 static | |
251 void ldap_dict_deinit(struct dict *dict ATTR_UNUSED) { | |
252 } | |
253 | |
254 static | |
255 int ldap_dict_wait(struct dict *dict) { | |
256 struct ldap_dict *ctx = (struct ldap_dict *)dict; | |
257 | |
258 i_assert(ctx->ioloop == NULL); | |
259 | |
260 ctx->prev_ioloop = current_ioloop; | |
261 ctx->ioloop = io_loop_create(); | |
262 ldap_client_switch_ioloop(ctx->client); | |
263 | |
264 do { | |
265 io_loop_run(current_ioloop); | |
266 } while (ctx->pending > 0); | |
267 | |
268 io_loop_set_current(ctx->prev_ioloop); | |
269 ldap_client_switch_ioloop(ctx->client); | |
270 io_loop_set_current(ctx->ioloop); | |
271 io_loop_destroy(&ctx->ioloop); | |
272 ctx->prev_ioloop = NULL; | |
273 | |
274 return 0; | |
275 } | |
276 | |
277 static | |
278 void ldap_dict_lookup_done(const struct dict_lookup_result *result, void *ctx) | |
279 { | |
280 struct dict_lookup_result *res = ctx; | |
281 *res = *result; | |
282 } | |
283 | |
284 static void | |
285 ldap_dict_lookup_callback(struct ldap_result *result, struct dict_ldap_op *op) | |
286 { | |
287 pool_t pool = op->pool; | |
288 struct ldap_search_iterator *iter; | |
289 const struct ldap_entry *entry; | |
290 | |
291 op->dict->pending--; | |
292 | |
293 if (ldap_result_has_failed(result)) { | |
294 op->res.ret = -1; | |
295 op->res.error = ldap_result_get_error(result); | |
296 } else { | |
297 iter = ldap_search_iterator_init(result); | |
298 entry = ldap_search_iterator_next(iter); | |
299 if (entry != NULL) { | |
300 if (op->dict->set->debug > 0) | |
301 i_debug("ldap_dict_lookup_callback got dn %s", ldap_entry_dn(entry)); | |
302 /* try extract value */ | |
303 const char *const *values = ldap_entry_get_attribute(entry, op->map->value_attribute); | |
304 if (values != NULL) { | |
305 if (op->dict->set->debug > 0) | |
306 i_debug("ldap_dict_lookup_callback got attribute %s", op->map->value_attribute); | |
307 op->res.ret = 1; | |
308 op->res.value = p_strdup(op->pool, values[0]); | |
309 } else { | |
310 if (op->dict->set->debug > 0) | |
311 i_debug("ldap_dict_lookup_callback dit not get attribute %s", op->map->value_attribute); | |
312 op->res.value = NULL; | |
313 } | |
314 } | |
315 ldap_search_iterator_deinit(&iter); | |
316 } | |
317 op->callback(&(op->res), op->callback_ctx); | |
318 pool_unref(&pool); | |
319 } | |
320 | |
321 static | |
322 int ldap_dict_lookup(struct dict *dict, pool_t pool, | |
323 const char *key, const char **value_r) | |
324 { | |
325 struct dict_lookup_result res; | |
326 pool_t orig_pool = pool; | |
327 int ret; | |
328 | |
329 T_BEGIN { | |
330 ldap_dict_lookup_async(dict, key, ldap_dict_lookup_done, &res); | |
331 | |
332 if ((ret = ldap_dict_wait(dict)) == 0) { | |
333 if (res.ret == 0) { | |
334 *value_r = p_strdup(orig_pool, res.value); | |
335 } else ret = res.ret; | |
336 } | |
337 } T_END; | |
338 return ret; | |
339 } | |
340 | |
341 /* | |
342 static | |
343 struct dict_iterate_context *ldap_dict_iterate_init(struct dict *dict, | |
344 const char *const *paths, | |
345 enum dict_iterate_flags flags) | |
346 { | |
347 return NULL; | |
348 } | |
349 | |
350 static | |
351 bool ldap_dict_iterate(struct dict_iterate_context *ctx, | |
352 const char **key_r, const char **value_r) | |
353 { | |
354 return FALSE; | |
355 } | |
356 | |
357 static | |
358 int ldap_dict_iterate_deinit(struct dict_iterate_context *ctx) | |
359 { | |
360 return -1; | |
361 } | |
362 | |
363 static | |
364 struct dict_transaction_context ldap_dict_transaction_init(struct dict *dict); | |
365 | |
366 static | |
367 int ldap_dict_transaction_commit(struct dict_transaction_context *ctx, | |
368 bool async, | |
369 dict_transaction_commit_callback_t *callback, | |
370 void *context); | |
371 static | |
372 void ldap_dict_transaction_rollback(struct dict_transaction_context *ctx); | |
373 | |
374 static | |
375 void ldap_dict_set(struct dict_transaction_context *ctx, | |
376 const char *key, const char *value); | |
377 static | |
378 void ldap_dict_unset(struct dict_transaction_context *ctx, | |
379 const char *key); | |
380 static | |
381 void ldap_dict_append(struct dict_transaction_context *ctx, | |
382 const char *key, const char *value); | |
383 static | |
384 void ldap_dict_atomic_inc(struct dict_transaction_context *ctx, | |
385 const char *key, long long diff); | |
386 */ | |
387 | |
388 static | |
389 void ldap_dict_lookup_async(struct dict *dict, const char *key, | |
390 dict_lookup_callback_t *callback, void *context) | |
391 { | |
392 struct ldap_search_input input; | |
393 struct ldap_dict *ctx = (struct ldap_dict*)dict; | |
394 struct dict_ldap_op *op; | |
395 pool_t oppool = pool_alloconly_create("ldap dict lookup", 64); | |
396 op = p_new(oppool, struct dict_ldap_op, 1); | |
397 op->pool = oppool; | |
398 op->dict = ctx; | |
399 op->callback = callback; | |
400 op->callback_ctx = context; | |
401 op->txid = ctx->last_txid++; | |
402 | |
403 /* key needs to be transformed into something else */ | |
404 ARRAY_TYPE(const_string) values; | |
405 T_BEGIN { | |
406 const char *attributes[2] = {0, 0}; | |
407 t_array_init(&values, 8); | |
408 const struct dict_ldap_map *map = ldap_dict_find_map(ctx, key, &values); | |
409 | |
410 if (map != NULL) { | |
411 op->map = map; | |
412 attributes[0] = map->value_attribute; | |
413 /* build lookup */ | |
414 memset(&input, 0, sizeof(input)); | |
415 input.base_dn = map->base_dn; | |
416 input.scope = map->scope_val; | |
417 input.filter = ldap_dict_build_query(ctx, map, &values, strncmp(key, DICT_PATH_PRIVATE, strlen(DICT_PATH_PRIVATE))==0); | |
418 input.attributes = attributes; | |
419 input.timeout_secs = ctx->set->timeout; | |
420 ctx->pending++; | |
421 ldap_search_start(ctx->client, &input, ldap_dict_lookup_callback, op); | |
422 } else { | |
423 op->res.error = "no such key"; | |
424 callback(&(op->res), context); | |
425 pool_unref(&oppool); | |
426 } | |
427 } T_END; | |
428 } | |
429 | |
430 struct dict dict_driver_ldap = { | |
431 .name = "ldap", | |
432 { | |
433 ldap_dict_init, | |
434 ldap_dict_deinit, | |
435 ldap_dict_wait, | |
436 ldap_dict_lookup, | |
437 NULL, /*ldap_dict_iterate_init,*/ | |
438 NULL, /*ldap_dict_iterate,*/ | |
439 NULL, /*ldap_dict_iterate_deinit,*/ | |
440 NULL, /*ldap_transaction_init,*/ | |
441 NULL, /*ldap_transaction_commit,*/ | |
442 NULL, /*ldap_transaction_rollback,*/ | |
443 NULL, /*ldap_set,*/ | |
444 NULL, /*ldap_unset,*/ | |
445 NULL, /*ldap_append,*/ | |
446 NULL, /*ldap_atomic_inc,*/ | |
447 ldap_dict_lookup_async | |
448 } | |
449 }; | |
450 | |
451 void dict_ldap_init(struct module *module ATTR_UNUSED); | |
452 void dict_ldap_deinit(void); | |
453 | |
454 void dict_ldap_init(struct module *module ATTR_UNUSED) | |
455 { | |
456 dict_driver_register(&dict_driver_ldap); | |
457 ldap_dict_list = NULL; | |
458 } | |
459 | |
460 void dict_ldap_deinit(void) | |
461 { | |
462 dict_driver_unregister(&dict_driver_ldap); | |
463 /* destroy all server connections */ | |
464 struct ldap_dict *ptr = ldap_dict_list; | |
465 ldap_dict_list = NULL; | |
466 | |
467 while(ptr != NULL) { | |
468 ldap_client_deinit(&(ptr->client)); | |
469 pool_t pool = ptr->pool; | |
470 ptr = ptr->next; | |
471 pool_unref(&pool); | |
472 } | |
473 } | |
474 | |
475 const char *dict_ldap_plugin_dependencies[] = { NULL }; |