119b217f5f64bdc9a668900602609302ec579e18
[metze/samba/wip.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.h>
40 #include <ldb_errors.h>
41 #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->new_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 struct dsdb_attribute *schema_attr)
225 {
226         const char *oid = schema_attr->syntax->ldap_oid;
227         int ret;
228         struct extended_dn_replace_list *os;
229         static const char *attrs[] = {
230                 "objectSid",
231                 "objectGUID",
232                 NULL
233         };
234
235         os = talloc_zero(ac, struct extended_dn_replace_list);
236         if (!os) {
237                 return ldb_oom(ac->ldb);
238         }
239
240         os->ac = ac;
241         
242         os->mem_ctx = callback_mem_ctx;
243
244         os->dsdb_dn = dsdb_dn_parse(os, ac->ldb, plain_dn, oid);
245         if (!os->dsdb_dn || !ldb_dn_validate(os->dsdb_dn->dn)) {
246                 talloc_free(os);
247                 ldb_asprintf_errstring(ac->ldb,
248                                        "could not parse %.*s as a %s DN", (int)plain_dn->length, plain_dn->data,
249                                        oid);
250                 return LDB_ERR_INVALID_DN_SYNTAX;
251         }
252
253         if (is_delete && !ldb_dn_has_extended(os->dsdb_dn->dn)) {
254                 /* NO need to figure this DN out, this element is
255                  * going to be deleted anyway, and becuase it's not
256                  * extended, we have enough information to do the
257                  * delete */
258                 talloc_free(os);
259                 return LDB_SUCCESS;
260         }
261         
262                 
263         os->replace_dn = plain_dn;
264
265         /* The search request here might happen to be for an
266          * 'extended' style DN, such as <GUID=abced...>.  The next
267          * module in the stack will convert this into a normal DN for
268          * processing */
269         ret = ldb_build_search_req(&os->search_req,
270                                    ac->ldb, os, os->dsdb_dn->dn, LDB_SCOPE_BASE, NULL, 
271                                    attrs, NULL, os, extended_replace_dn,
272                                    ac->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_FLAG_AS_SYSTEM |
281                                         DSDB_SEARCH_SHOW_RECYCLED |
282                                         DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
283         if (ret != LDB_SUCCESS) {
284                 talloc_free(os);
285                 return ret;
286         }
287
288         if (ac->ops) {
289                 ac->cur->next = os;
290         } else {
291                 ac->ops = os;
292         }
293         ac->cur = os;
294
295         return LDB_SUCCESS;
296 }
297
298
299 /* add */
300 static int extended_dn_add(struct ldb_module *module, struct ldb_request *req)
301 {
302         struct extended_dn_context *ac;
303         int ret;
304         unsigned int i, j;
305
306         if (ldb_dn_is_special(req->op.add.message->dn)) {
307                 /* do not manipulate our control entries */
308                 return ldb_next_request(module, req);
309         }
310
311         ac = extended_dn_context_init(module, req);
312         if (!ac) {
313                 return ldb_operr(ldb_module_get_ctx(module));
314         }
315
316         if (!ac->schema) {
317                 /* without schema, this doesn't make any sense */
318                 talloc_free(ac);
319                 return ldb_next_request(module, req);
320         }
321
322         for (i=0; i < req->op.add.message->num_elements; i++) {
323                 const struct ldb_message_element *el = &req->op.add.message->elements[i];
324                 const struct dsdb_attribute *schema_attr
325                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
326                 if (!schema_attr) {
327                         continue;
328                 }
329
330                 /* We only setup an extended DN GUID on DN elements */
331                 if (schema_attr->dn_format == DSDB_INVALID_DN) {
332                         continue;
333                 }
334
335                 if (schema_attr->attributeID_id == DRSUAPI_ATTID_distinguishedName) {
336                         /* distinguishedName values are ignored */
337                         continue;
338                 }
339
340                 /* Before we setup a procedure to modify the incoming message, we must copy it */
341                 if (!ac->new_req) {
342                         struct ldb_message *msg = ldb_msg_copy(ac, req->op.add.message);
343                         if (!msg) {
344                                 return ldb_oom(ldb_module_get_ctx(module));
345                         }
346                    
347                         ret = ldb_build_add_req(&ac->new_req, ac->ldb, ac, msg, req->controls, ac, extended_final_callback, req);
348                         LDB_REQ_SET_LOCATION(ac->new_req);
349                         if (ret != LDB_SUCCESS) {
350                                 return ret;
351                         }
352                 }
353                 /* Re-calculate el */
354                 el = &ac->new_req->op.add.message->elements[i];
355                 for (j = 0; j < el->num_values; j++) {
356                         ret = extended_store_replace(ac, ac->new_req, &el->values[j],
357                                                      false, schema_attr);
358                         if (ret != LDB_SUCCESS) {
359                                 return ret;
360                         }
361                 }
362         }
363
364         /* if no DNs were set continue */
365         if (ac->ops == NULL) {
366                 talloc_free(ac);
367                 return ldb_next_request(module, req);
368         }
369
370         /* start with the searches */
371         return ldb_next_request(module, ac->ops->search_req);
372 }
373
374 /* modify */
375 static int extended_dn_modify(struct ldb_module *module, struct ldb_request *req)
376 {
377         /* Look over list of modifications */
378         /* Find if any are for linked attributes */
379         /* Determine the effect of the modification */
380         /* Apply the modify to the linked entry */
381
382         unsigned int i, j;
383         struct extended_dn_context *ac;
384         struct ldb_control *fix_links_control = NULL;
385         int ret;
386
387         if (ldb_dn_is_special(req->op.mod.message->dn)) {
388                 /* do not manipulate our control entries */
389                 return ldb_next_request(module, req);
390         }
391
392         ac = extended_dn_context_init(module, req);
393         if (!ac) {
394                 return ldb_operr(ldb_module_get_ctx(module));
395         }
396
397         if (!ac->schema) {
398                 talloc_free(ac);
399                 /* without schema, this doesn't make any sense */
400                 return ldb_next_request(module, req);
401         }
402
403         fix_links_control = ldb_request_get_control(req,
404                                         DSDB_CONTROL_DBCHECK_FIX_DUPLICATE_LINKS);
405         if (fix_links_control != NULL) {
406                 return ldb_next_request(module, req);
407         }
408
409         for (i=0; i < req->op.mod.message->num_elements; i++) {
410                 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
411                 const struct dsdb_attribute *schema_attr
412                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
413                 if (!schema_attr) {
414                         continue;
415                 }
416
417                 /* We only setup an extended DN GUID on these particular DN objects */
418                 if (schema_attr->dn_format == DSDB_INVALID_DN) {
419                         continue;
420                 }
421
422                 if (schema_attr->attributeID_id == DRSUAPI_ATTID_distinguishedName) {
423                         /* distinguishedName values are ignored */
424                         continue;
425                 }
426
427                 /* Before we setup a procedure to modify the incoming message, we must copy it */
428                 if (!ac->new_req) {
429                         struct ldb_message *msg = ldb_msg_copy(ac, req->op.mod.message);
430                         if (!msg) {
431                                 talloc_free(ac);
432                                 return ldb_oom(ac->ldb);
433                         }
434                    
435                         ret = ldb_build_mod_req(&ac->new_req, ac->ldb, ac, msg, req->controls, ac, extended_final_callback, req);
436                         LDB_REQ_SET_LOCATION(ac->new_req);
437                         if (ret != LDB_SUCCESS) {
438                                 talloc_free(ac);
439                                 return ret;
440                         }
441                 }
442                 /* Re-calculate el */
443                 el = &ac->new_req->op.mod.message->elements[i];
444                 /* For each value being added, we need to setup the lookups to fill in the extended DN */
445                 for (j = 0; j < el->num_values; j++) {
446                         /* If we are just going to delete this
447                          * element, only do a lookup if
448                          * extended_store_replace determines it's an
449                          * input of an extended DN */
450                         bool is_delete = (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE);
451
452                         ret = extended_store_replace(ac, ac->new_req, &el->values[j],
453                                                      is_delete, schema_attr);
454                         if (ret != LDB_SUCCESS) {
455                                 talloc_free(ac);
456                                 return ret;
457                         }
458                 }
459         }
460
461         /* if DNs were set continue */
462         if (ac->ops == NULL) {
463                 talloc_free(ac);
464                 return ldb_next_request(module, req);
465         }
466
467         /* start with the searches */
468         return ldb_next_request(module, ac->ops->search_req);
469 }
470
471 static const struct ldb_module_ops ldb_extended_dn_store_module_ops = {
472         .name              = "extended_dn_store",
473         .add               = extended_dn_add,
474         .modify            = extended_dn_modify,
475 };
476
477 int ldb_extended_dn_store_module_init(const char *version)
478 {
479         LDB_MODULE_CHECK_VERSION(version);
480         return ldb_register_module(&ldb_extended_dn_store_module_ops);
481 }