s4-dsdb: Explicitly mark some internal ldb requests as trusted
[nivanova/samba.git] / source4 / lib / ldb / modules / asq.c
1 /* 
2    ldb database library
3
4    Copyright (C) Simo Sorce  2005-2008
5
6      ** NOTE! The following LGPL license applies to the ldb
7      ** library. This does NOT imply that all of Samba is released
8      ** under the LGPL
9    
10    This library is free software; you can redistribute it and/or
11    modify it under the terms of the GNU Lesser General Public
12    License as published by the Free Software Foundation; either
13    version 3 of the License, or (at your option) any later version.
14
15    This library 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 GNU
18    Lesser General Public License for more details.
19
20    You should have received a copy of the GNU Lesser General Public
21    License along with this library; if not, see <http://www.gnu.org/licenses/>.
22 */
23
24 /*
25  *  Name: ldb
26  *
27  *  Component: ldb attribute scoped query control module
28  *
29  *  Description: this module searches all the objects pointed by
30  *               the DNs contained in the references attribute
31  *
32  *  Author: Simo Sorce
33  */
34
35 #include "replace.h"
36 #include "system/filesys.h"
37 #include "system/time.h"
38 #include "ldb_module.h"
39
40 struct asq_context {
41
42         enum {ASQ_SEARCH_BASE, ASQ_SEARCH_MULTI} step;
43
44         struct ldb_module *module;
45         struct ldb_request *req;
46
47         struct ldb_asq_control *asq_ctrl;
48
49         const char * const *req_attrs;
50         char *req_attribute;
51         enum {
52                 ASQ_CTRL_SUCCESS                        = 0,
53                 ASQ_CTRL_INVALID_ATTRIBUTE_SYNTAX       = 21,
54                 ASQ_CTRL_UNWILLING_TO_PERFORM           = 53,
55                 ASQ_CTRL_AFFECTS_MULTIPLE_DSA           = 71
56         } asq_ret;
57
58         struct ldb_reply *base_res;
59
60         struct ldb_request **reqs;
61         unsigned int num_reqs;
62         unsigned int cur_req;
63
64         struct ldb_control **controls;
65 };
66
67 static struct asq_context *asq_context_init(struct ldb_module *module, struct ldb_request *req)
68 {
69         struct ldb_context *ldb;
70         struct asq_context *ac;
71
72         ldb = ldb_module_get_ctx(module);
73
74         ac = talloc_zero(req, struct asq_context);
75         if (ac == NULL) {
76                 ldb_oom(ldb);
77                 return NULL;
78         }
79
80         ac->module = module;
81         ac->req = req;
82
83         return ac;
84 }
85
86 static int asq_search_continue(struct asq_context *ac);
87
88 static int asq_search_terminate(struct asq_context *ac)
89 {
90         struct ldb_asq_control *asq;
91         unsigned int i;
92
93         if (ac->controls) {
94                 for (i = 0; ac->controls[i]; i++) /* count em */ ;
95         } else {
96                 i = 0;
97         }
98
99         ac->controls = talloc_realloc(ac, ac->controls, struct ldb_control *, i + 2);
100
101         if (ac->controls == NULL) {
102                 return LDB_ERR_OPERATIONS_ERROR;
103         }
104
105         ac->controls[i] = talloc(ac->controls, struct ldb_control);
106         if (ac->controls[i] == NULL) {
107                 return LDB_ERR_OPERATIONS_ERROR;
108         }
109
110         ac->controls[i]->oid = LDB_CONTROL_ASQ_OID;
111         ac->controls[i]->critical = 0;
112
113         asq = talloc_zero(ac->controls[i], struct ldb_asq_control);
114         if (asq == NULL)
115                 return LDB_ERR_OPERATIONS_ERROR;
116
117         asq->result = ac->asq_ret;
118
119         ac->controls[i]->data = asq;
120
121         ac->controls[i + 1] = NULL;
122
123         return ldb_module_done(ac->req, ac->controls, NULL, LDB_SUCCESS);
124 }
125
126 static int asq_base_callback(struct ldb_request *req, struct ldb_reply *ares)
127 {
128         struct asq_context *ac;
129         int ret;
130
131         ac = talloc_get_type(req->context, struct asq_context);
132
133         if (!ares) {
134                 return ldb_module_done(ac->req, NULL, NULL,
135                                         LDB_ERR_OPERATIONS_ERROR);
136         }
137         if (ares->error != LDB_SUCCESS) {
138                 return ldb_module_done(ac->req, ares->controls,
139                                         ares->response, ares->error);
140         }
141
142         switch (ares->type) {
143         case LDB_REPLY_ENTRY:
144                 ac->base_res = talloc_move(ac, &ares);
145                 break;
146
147         case LDB_REPLY_REFERRAL:
148                 /* ignore referrals */
149                 talloc_free(ares);
150                 break;
151
152         case LDB_REPLY_DONE:
153
154                 talloc_free(ares);
155
156                 /* next step */
157                 ret = asq_search_continue(ac);
158                 if (ret != LDB_SUCCESS) {
159                         return ldb_module_done(ac->req, NULL, NULL, ret);
160                 }
161                 break;
162
163         }
164         return LDB_SUCCESS;
165 }
166
167 static int asq_reqs_callback(struct ldb_request *req, struct ldb_reply *ares)
168 {
169         struct asq_context *ac;
170         int ret;
171
172         ac = talloc_get_type(req->context, struct asq_context);
173
174         if (!ares) {
175                 return ldb_module_done(ac->req, NULL, NULL,
176                                         LDB_ERR_OPERATIONS_ERROR);
177         }
178         if (ares->error != LDB_SUCCESS) {
179                 return ldb_module_done(ac->req, ares->controls,
180                                         ares->response, ares->error);
181         }
182
183         switch (ares->type) {
184         case LDB_REPLY_ENTRY:
185                 /* pass the message up to the original callback as we
186                  * do not have to elaborate on it any further */
187                 ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
188                 if (ret != LDB_SUCCESS) {
189                         return ldb_module_done(ac->req, NULL, NULL, ret);
190                 }
191                 talloc_free(ares);
192                 break;
193
194         case LDB_REPLY_REFERRAL:
195                 /* ignore referrals */
196                 talloc_free(ares);
197                 break;
198
199         case LDB_REPLY_DONE:
200
201                 talloc_free(ares);
202
203                 ret = asq_search_continue(ac);
204                 if (ret != LDB_SUCCESS) {
205                         return ldb_module_done(ac->req, NULL, NULL, ret);
206                 }
207                 break;
208         }
209
210         return LDB_SUCCESS;
211 }
212
213 static int asq_build_first_request(struct asq_context *ac, struct ldb_request **base_req)
214 {
215         struct ldb_context *ldb;
216         const char **base_attrs;
217         int ret;
218
219         ldb = ldb_module_get_ctx(ac->module);
220
221         ac->req_attrs = ac->req->op.search.attrs;
222         ac->req_attribute = talloc_strdup(ac, ac->asq_ctrl->source_attribute);
223         if (ac->req_attribute == NULL)
224                 return LDB_ERR_OPERATIONS_ERROR;
225
226         base_attrs = talloc_array(ac, const char *, 2);
227         if (base_attrs == NULL) return LDB_ERR_OPERATIONS_ERROR;
228
229         base_attrs[0] = talloc_strdup(base_attrs, ac->asq_ctrl->source_attribute);
230         if (base_attrs[0] == NULL) return LDB_ERR_OPERATIONS_ERROR;
231
232         base_attrs[1] = NULL;
233
234         ret = ldb_build_search_req(base_req, ldb, ac,
235                                         ac->req->op.search.base,
236                                         LDB_SCOPE_BASE,
237                                         NULL,
238                                         (const char * const *)base_attrs,
239                                         NULL,
240                                         ac, asq_base_callback,
241                                         ac->req);
242         ldb_req_mark_trusted(*base_req);
243         if (ret != LDB_SUCCESS) {
244                 return ret;
245         }
246
247         return LDB_SUCCESS;
248 }
249
250 static int asq_build_multiple_requests(struct asq_context *ac, bool *terminated)
251 {
252         struct ldb_context *ldb;
253         struct ldb_control **saved_controls;
254         struct ldb_control *control;
255         struct ldb_dn *dn;
256         struct ldb_message_element *el;
257         unsigned int i;
258         int ret;
259
260         if (ac->base_res == NULL) {
261                 return LDB_ERR_NO_SUCH_OBJECT;
262         }
263
264         ldb = ldb_module_get_ctx(ac->module);
265
266         el = ldb_msg_find_element(ac->base_res->message, ac->req_attribute);
267         /* no values found */
268         if (el == NULL) {
269                 ac->asq_ret = ASQ_CTRL_SUCCESS;
270                 *terminated = true;
271                 return asq_search_terminate(ac);
272         }
273
274         ac->num_reqs = el->num_values;
275         ac->cur_req = 0;
276         ac->reqs = talloc_array(ac, struct ldb_request *, ac->num_reqs);
277         if (ac->reqs == NULL) {
278                 return LDB_ERR_OPERATIONS_ERROR;
279         }
280
281         for (i = 0; i < el->num_values; i++) {
282
283                 dn = ldb_dn_new(ac, ldb,
284                                 (const char *)el->values[i].data);
285                 if ( ! ldb_dn_validate(dn)) {
286                         ac->asq_ret = ASQ_CTRL_INVALID_ATTRIBUTE_SYNTAX;
287                         *terminated = true;
288                         return asq_search_terminate(ac);
289                 }
290
291                 ret = ldb_build_search_req_ex(&ac->reqs[i],
292                                                 ldb, ac,
293                                                 dn, LDB_SCOPE_BASE,
294                                                 ac->req->op.search.tree,
295                                                 ac->req_attrs,
296                                                 ac->req->controls,
297                                                 ac, asq_reqs_callback,
298                                                 ac->req);
299                 if (ret != LDB_SUCCESS) {
300                         return ret;
301                 }
302
303                 /* remove the ASQ control itself */
304                 control = ldb_request_get_control(ac->req, LDB_CONTROL_ASQ_OID);
305                 if (!ldb_save_controls(control, ac->reqs[i], &saved_controls)) {
306                         return LDB_ERR_OPERATIONS_ERROR;
307                 }
308         }
309
310         return LDB_SUCCESS;
311 }
312
313 static int asq_search_continue(struct asq_context *ac)
314 {
315         struct ldb_context *ldb;
316         bool terminated = false;
317         int ret;
318
319         ldb = ldb_module_get_ctx(ac->module);
320
321         switch (ac->step) {
322         case ASQ_SEARCH_BASE:
323
324                 /* build up the requests call chain */
325                 ret = asq_build_multiple_requests(ac, &terminated);
326                 if (ret != LDB_SUCCESS || terminated) {
327                         return ret;
328                 }
329
330                 ac->step = ASQ_SEARCH_MULTI;
331
332                 return ldb_request(ldb, ac->reqs[ac->cur_req]);
333
334         case ASQ_SEARCH_MULTI:
335
336                 ac->cur_req++;
337
338                 if (ac->cur_req == ac->num_reqs) {
339                         /* done */
340                         return asq_search_terminate(ac);
341                 }
342
343                 return ldb_request(ldb, ac->reqs[ac->cur_req]);
344         }
345
346         return LDB_ERR_OPERATIONS_ERROR;
347 }
348
349 static int asq_search(struct ldb_module *module, struct ldb_request *req)
350 {
351         struct ldb_context *ldb;
352         struct ldb_request *base_req;
353         struct ldb_control *control;
354         struct asq_context *ac;
355         int ret;
356
357         ldb = ldb_module_get_ctx(module);
358
359         /* check if there's an ASQ control */
360         control = ldb_request_get_control(req, LDB_CONTROL_ASQ_OID);
361         if (control == NULL) {
362                 /* not found go on */
363                 return ldb_next_request(module, req);
364         }
365
366         ac = asq_context_init(module, req);
367         if (!ac) {
368                 return LDB_ERR_OPERATIONS_ERROR;
369         }
370
371         /* check the search is well formed */
372         if (req->op.search.scope != LDB_SCOPE_BASE) {
373                 ac->asq_ret = ASQ_CTRL_UNWILLING_TO_PERFORM;
374                 return asq_search_terminate(ac);
375         }
376
377         ac->asq_ctrl = talloc_get_type(control->data, struct ldb_asq_control);
378         if (!ac->asq_ctrl) {
379                 return LDB_ERR_PROTOCOL_ERROR;
380         }
381
382         ret = asq_build_first_request(ac, &base_req);
383         if (ret != LDB_SUCCESS) {
384                 return ret;
385         }
386
387         ac->step = ASQ_SEARCH_BASE;
388
389         return ldb_request(ldb, base_req);
390 }
391
392 static int asq_init(struct ldb_module *module)
393 {
394         struct ldb_context *ldb;
395         int ret;
396
397         ldb = ldb_module_get_ctx(module);
398
399         ret = ldb_mod_register_control(module, LDB_CONTROL_ASQ_OID);
400         if (ret != LDB_SUCCESS) {
401                 ldb_debug(ldb, LDB_DEBUG_WARNING, "asq: Unable to register control with rootdse!");
402         }
403
404         return ldb_next_init(module);
405 }
406
407 static const struct ldb_module_ops ldb_asq_module_ops = {
408         .name              = "asq",
409         .search            = asq_search,
410         .init_context      = asq_init
411 };
412
413 int ldb_asq_init(const char *version)
414 {
415         LDB_MODULE_CHECK_VERSION(version);
416         return ldb_register_module(&ldb_asq_module_ops);
417 }