s4-dsdb: Explicitly mark some internal ldb requests as trusted
[nivanova/samba.git] / source4 / dsdb / samdb / ldb_modules / extended_dn_store.c
1 /* 
2    ldb database library
3
4    Copyright (C) Simo Sorce 2005-2008
5    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007-2009
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 /*
22  *  Name: ldb
23  *
24  *  Component: ldb extended dn control module
25  *
26  *  Description: this module builds a special dn for returned search
27  *  results nad creates the special DN in the backend store for new
28  *  values.
29  *
30  *  This also has the curious result that we convert <SID=S-1-2-345>
31  *  in an attribute value into a normal DN for the rest of the stack
32  *  to process
33  *
34  *  Authors: Simo Sorce
35  *           Andrew Bartlett
36  */
37
38 #include "includes.h"
39 #include "ldb/include/ldb.h"
40 #include "ldb/include/ldb_errors.h"
41 #include "ldb/include/ldb_module.h"
42 #include "librpc/gen_ndr/ndr_misc.h"
43 #include "dsdb/samdb/samdb.h"
44 #include "libcli/security/security.h"
45 #include "dsdb/samdb/ldb_modules/util.h"
46 #include <time.h>
47
48 struct extended_dn_replace_list {
49         struct extended_dn_replace_list *next;
50         struct dsdb_dn *dsdb_dn;
51         TALLOC_CTX *mem_ctx;
52         struct ldb_val *replace_dn;
53         struct extended_dn_context *ac;
54         struct ldb_request *search_req;
55 };
56
57
58 struct extended_dn_context {
59         const struct dsdb_schema *schema;
60         struct ldb_module *module;
61         struct ldb_context *ldb;
62         struct ldb_request *req;
63         struct ldb_request *new_req;
64
65         struct extended_dn_replace_list *ops;
66         struct extended_dn_replace_list *cur;
67 };
68
69
70 static struct extended_dn_context *extended_dn_context_init(struct ldb_module *module,
71                                                             struct ldb_request *req)
72 {
73         struct extended_dn_context *ac;
74         struct ldb_context *ldb = ldb_module_get_ctx(module);
75         ac = talloc_zero(req, struct extended_dn_context);
76         if (ac == NULL) {
77                 ldb_oom(ldb);
78                 return NULL;
79         }
80
81         ac->schema = dsdb_get_schema(ldb_module_get_ctx(module), ac);
82         ac->module = module;
83         ac->ldb = ldb;
84         ac->req = req;
85
86         return ac;
87 }
88
89 /* An extra layer of indirection because LDB does not allow the original request to be altered */
90
91 static int extended_final_callback(struct ldb_request *req, struct ldb_reply *ares)
92 {
93         int ret = LDB_ERR_OPERATIONS_ERROR;
94         struct extended_dn_context *ac;
95         ac = talloc_get_type(req->context, struct extended_dn_context);
96
97         if (ares->error != LDB_SUCCESS) {
98                 ret = ldb_module_done(ac->req, ares->controls,
99                                       ares->response, ares->error);
100         } else {
101                 switch (ares->type) {
102                 case LDB_REPLY_ENTRY:
103                         
104                         ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
105                         break;
106                 case LDB_REPLY_REFERRAL:
107                         
108                         ret = ldb_module_send_referral(ac->req, ares->referral);
109                         break;
110                 case LDB_REPLY_DONE:
111                         
112                         ret = ldb_module_done(ac->req, ares->controls,
113                                               ares->response, ares->error);
114                         break;
115                 }
116         }
117         return ret;
118 }
119
120 static int extended_replace_dn(struct ldb_request *req, struct ldb_reply *ares)
121 {
122         struct extended_dn_replace_list *os = talloc_get_type(req->context, 
123                                                            struct extended_dn_replace_list);
124
125         if (!ares) {
126                 return ldb_module_done(os->ac->req, NULL, NULL,
127                                         LDB_ERR_OPERATIONS_ERROR);
128         }
129         if (ares->error == LDB_ERR_NO_SUCH_OBJECT) {
130                 /* Don't worry too much about dangling references */
131
132                 ldb_reset_err_string(os->ac->ldb);
133                 if (os->next) {
134                         struct extended_dn_replace_list *next;
135
136                         next = os->next;
137
138                         talloc_free(os);
139
140                         os = next;
141                         return ldb_next_request(os->ac->module, next->search_req);
142                 } else {
143                         /* Otherwise, we are done - let's run the
144                          * request now we have swapped the DNs for the
145                          * full versions */
146                         return ldb_next_request(os->ac->module, os->ac->req);
147                 }
148         }
149         if (ares->error != LDB_SUCCESS) {
150                 return ldb_module_done(os->ac->req, ares->controls,
151                                         ares->response, ares->error);
152         }
153
154         /* Only entries are interesting, and we only want the olddn */
155         switch (ares->type) {
156         case LDB_REPLY_ENTRY:
157         {
158                 /* This *must* be the right DN, as this is a base
159                  * search.  We can't check, as it could be an extended
160                  * DN, so a module below will resolve it */
161                 struct ldb_dn *dn = ares->message->dn;
162                 
163                 /* Rebuild with the string or binary 'extra part' the
164                  * DN may have had as a prefix */
165                 struct dsdb_dn *dsdb_dn = dsdb_dn_construct(ares, dn, 
166                                                             os->dsdb_dn->extra_part,
167                                                             os->dsdb_dn->oid);
168                 if (dsdb_dn) {
169                         /* Replace the DN with the extended version of the DN
170                          * (ie, add SID and GUID) */
171                         *os->replace_dn = data_blob_string_const(
172                                 dsdb_dn_get_extended_linearized(os->mem_ctx, 
173                                                                 dsdb_dn, 1));
174                         talloc_free(dsdb_dn);
175                 }
176                 if (os->replace_dn->data == NULL) {
177                         return ldb_module_done(os->ac->req, NULL, NULL,
178                                                 LDB_ERR_OPERATIONS_ERROR);
179                 }
180                 break;
181         }
182         case LDB_REPLY_REFERRAL:
183                 /* ignore */
184                 break;
185
186         case LDB_REPLY_DONE:
187
188                 talloc_free(ares);
189                 
190                 /* Run the next search */
191
192                 if (os->next) {
193                         struct extended_dn_replace_list *next;
194
195                         next = os->next;
196
197                         talloc_free(os);
198
199                         os = next;
200                         return ldb_next_request(os->ac->module, next->search_req);
201                 } else {
202                         /* Otherwise, we are done - let's run the
203                          * request now we have swapped the DNs for the
204                          * full versions */
205                         return ldb_next_request(os->ac->module, os->ac->new_req);
206                 }
207         }
208
209         talloc_free(ares);
210         return LDB_SUCCESS;
211 }
212
213 /* We have a 'normal' DN in the inbound request.  We need to find out
214  * what the GUID and SID are on the DN it points to, so we can
215  * construct an extended DN for storage.
216  *
217  * This creates a list of DNs to look up, and the plain DN to replace
218  */
219
220 static int extended_store_replace(struct extended_dn_context *ac,
221                                   TALLOC_CTX *callback_mem_ctx,
222                                   struct ldb_val *plain_dn,
223                                   bool is_delete, 
224                                   const char *oid)
225 {
226         int ret;
227         struct extended_dn_replace_list *os;
228         static const char *attrs[] = {
229                 "objectSid",
230                 "objectGUID",
231                 NULL
232         };
233
234         os = talloc_zero(ac, struct extended_dn_replace_list);
235         if (!os) {
236                 return ldb_oom(ac->ldb);
237         }
238
239         os->ac = ac;
240         
241         os->mem_ctx = callback_mem_ctx;
242
243         os->dsdb_dn = dsdb_dn_parse(os, ac->ldb, plain_dn, oid);
244         if (!os->dsdb_dn || !ldb_dn_validate(os->dsdb_dn->dn)) {
245                 talloc_free(os);
246                 ldb_asprintf_errstring(ac->ldb,
247                                        "could not parse %.*s as a %s DN", (int)plain_dn->length, plain_dn->data,
248                                        oid);
249                 return LDB_ERR_INVALID_DN_SYNTAX;
250         }
251
252         if (is_delete && !ldb_dn_has_extended(os->dsdb_dn->dn)) {
253                 /* NO need to figure this DN out, this element is
254                  * going to be deleted anyway, and becuase it's not
255                  * extended, we have enough information to do the
256                  * delete */
257                 talloc_free(os);
258                 return LDB_SUCCESS;
259         }
260         
261                 
262         os->replace_dn = plain_dn;
263
264         /* The search request here might happen to be for an
265          * 'extended' style DN, such as <GUID=abced...>.  The next
266          * module in the stack will convert this into a normal DN for
267          * processing */
268         ret = ldb_build_search_req(&os->search_req,
269                                    ac->ldb, os, os->dsdb_dn->dn, LDB_SCOPE_BASE, NULL, 
270                                    attrs, NULL, os, extended_replace_dn,
271                                    ac->req);
272         ldb_req_mark_trusted(os->search_req);
273         LDB_REQ_SET_LOCATION(os->search_req);
274         if (ret != LDB_SUCCESS) {
275                 talloc_free(os);
276                 return ret;
277         }
278
279         ret = dsdb_request_add_controls(os->search_req,
280                                         DSDB_SEARCH_SHOW_RECYCLED|DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
281         if (ret != LDB_SUCCESS) {
282                 talloc_free(os);
283                 return ret;
284         }
285
286         if (ac->ops) {
287                 ac->cur->next = os;
288         } else {
289                 ac->ops = os;
290         }
291         ac->cur = os;
292
293         return LDB_SUCCESS;
294 }
295
296
297 /* add */
298 static int extended_dn_add(struct ldb_module *module, struct ldb_request *req)
299 {
300         struct extended_dn_context *ac;
301         int ret;
302         unsigned int i, j;
303
304         if (ldb_dn_is_special(req->op.add.message->dn)) {
305                 /* do not manipulate our control entries */
306                 return ldb_next_request(module, req);
307         }
308
309         ac = extended_dn_context_init(module, req);
310         if (!ac) {
311                 return ldb_operr(ldb_module_get_ctx(module));
312         }
313
314         if (!ac->schema) {
315                 /* without schema, this doesn't make any sense */
316                 talloc_free(ac);
317                 return ldb_next_request(module, req);
318         }
319
320         for (i=0; i < req->op.add.message->num_elements; i++) {
321                 const struct ldb_message_element *el = &req->op.add.message->elements[i];
322                 const struct dsdb_attribute *schema_attr
323                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
324                 if (!schema_attr) {
325                         continue;
326                 }
327
328                 /* We only setup an extended DN GUID on DN elements */
329                 if (dsdb_dn_oid_to_format(schema_attr->syntax->ldap_oid) == DSDB_INVALID_DN) {
330                         continue;
331                 }
332
333                 /* Before we setup a procedure to modify the incoming message, we must copy it */
334                 if (!ac->new_req) {
335                         struct ldb_message *msg = ldb_msg_copy(ac, req->op.add.message);
336                         if (!msg) {
337                                 return ldb_oom(ldb_module_get_ctx(module));
338                         }
339                    
340                         ret = ldb_build_add_req(&ac->new_req, ac->ldb, ac, msg, req->controls, ac, extended_final_callback, req);
341                         LDB_REQ_SET_LOCATION(ac->new_req);
342                         if (ret != LDB_SUCCESS) {
343                                 return ret;
344                         }
345                 }
346                 /* Re-calculate el */
347                 el = &ac->new_req->op.add.message->elements[i];
348                 for (j = 0; j < el->num_values; j++) {
349                         ret = extended_store_replace(ac, ac->new_req->op.add.message->elements, &el->values[j],
350                                                      false, schema_attr->syntax->ldap_oid);
351                         if (ret != LDB_SUCCESS) {
352                                 return ret;
353                         }
354                 }
355         }
356
357         /* if no DNs were set continue */
358         if (ac->ops == NULL) {
359                 talloc_free(ac);
360                 return ldb_next_request(module, req);
361         }
362
363         /* start with the searches */
364         return ldb_next_request(module, ac->ops->search_req);
365 }
366
367 /* modify */
368 static int extended_dn_modify(struct ldb_module *module, struct ldb_request *req)
369 {
370         /* Look over list of modifications */
371         /* Find if any are for linked attributes */
372         /* Determine the effect of the modification */
373         /* Apply the modify to the linked entry */
374
375         unsigned int i, j;
376         struct extended_dn_context *ac;
377         int ret;
378
379         if (ldb_dn_is_special(req->op.mod.message->dn)) {
380                 /* do not manipulate our control entries */
381                 return ldb_next_request(module, req);
382         }
383
384         ac = extended_dn_context_init(module, req);
385         if (!ac) {
386                 return ldb_operr(ldb_module_get_ctx(module));
387         }
388
389         if (!ac->schema) {
390                 talloc_free(ac);
391                 /* without schema, this doesn't make any sense */
392                 return ldb_next_request(module, req);
393         }
394
395         for (i=0; i < req->op.mod.message->num_elements; i++) {
396                 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
397                 const struct dsdb_attribute *schema_attr
398                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
399                 if (!schema_attr) {
400                         continue;
401                 }
402
403                 /* We only setup an extended DN GUID on these particular DN objects */
404                 if (dsdb_dn_oid_to_format(schema_attr->syntax->ldap_oid) == DSDB_INVALID_DN) {
405                         continue;
406                 }
407
408                 /* Before we setup a procedure to modify the incoming message, we must copy it */
409                 if (!ac->new_req) {
410                         struct ldb_message *msg = ldb_msg_copy(ac, req->op.mod.message);
411                         if (!msg) {
412                                 talloc_free(ac);
413                                 return ldb_oom(ac->ldb);
414                         }
415                    
416                         ret = ldb_build_mod_req(&ac->new_req, ac->ldb, ac, msg, req->controls, ac, extended_final_callback, req);
417                         LDB_REQ_SET_LOCATION(ac->new_req);
418                         if (ret != LDB_SUCCESS) {
419                                 talloc_free(ac);
420                                 return ret;
421                         }
422                 }
423                 /* Re-calculate el */
424                 el = &ac->new_req->op.mod.message->elements[i];
425                 /* For each value being added, we need to setup the lookups to fill in the extended DN */
426                 for (j = 0; j < el->num_values; j++) {
427                         /* If we are just going to delete this
428                          * element, only do a lookup if
429                          * extended_store_replace determines it's an
430                          * input of an extended DN */
431                         bool is_delete = (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE);
432
433                         ret = extended_store_replace(ac, req->op.mod.message->elements, &el->values[j],
434                                                      is_delete, schema_attr->syntax->ldap_oid);
435                         if (ret != LDB_SUCCESS) {
436                                 talloc_free(ac);
437                                 return ret;
438                         }
439                 }
440         }
441
442         /* if DNs were set continue */
443         if (ac->ops == NULL) {
444                 talloc_free(ac);
445                 return ldb_next_request(module, req);
446         }
447
448         /* start with the searches */
449         return ldb_next_request(module, ac->ops->search_req);
450 }
451
452 static const struct ldb_module_ops ldb_extended_dn_store_module_ops = {
453         .name              = "extended_dn_store",
454         .add               = extended_dn_add,
455         .modify            = extended_dn_modify,
456 };
457
458 int ldb_extended_dn_store_module_init(const char *version)
459 {
460         LDB_MODULE_CHECK_VERSION(version);
461         return ldb_register_module(&ldb_extended_dn_store_module_ops);
462 }