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 };