s4-dsdb: use ldb_operr() in the dsdb code
[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/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
273         if (ret != LDB_SUCCESS) {
274                 talloc_free(os);
275                 return ret;
276         }
277
278         ret = dsdb_request_add_controls(os->search_req,
279                                         DSDB_SEARCH_SHOW_DELETED|DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
280         if (ret != LDB_SUCCESS) {
281                 talloc_free(os);
282                 return ret;
283         }
284
285         if (ac->ops) {
286                 ac->cur->next = os;
287         } else {
288                 ac->ops = os;
289         }
290         ac->cur = os;
291
292         return LDB_SUCCESS;
293 }
294
295
296 /* add */
297 static int extended_dn_add(struct ldb_module *module, struct ldb_request *req)
298 {
299         struct extended_dn_context *ac;
300         int ret;
301         int i, j;
302
303         if (ldb_dn_is_special(req->op.add.message->dn)) {
304                 /* do not manipulate our control entries */
305                 return ldb_next_request(module, req);
306         }
307
308         ac = extended_dn_context_init(module, req);
309         if (!ac) {
310                 return ldb_operr(ldb_module_get_ctx(module));
311         }
312
313         if (!ac->schema) {
314                 /* without schema, this doesn't make any sense */
315                 talloc_free(ac);
316                 return ldb_next_request(module, req);
317         }
318
319         for (i=0; i < req->op.add.message->num_elements; i++) {
320                 const struct ldb_message_element *el = &req->op.add.message->elements[i];
321                 const struct dsdb_attribute *schema_attr
322                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
323                 if (!schema_attr) {
324                         continue;
325                 }
326
327                 /* We only setup an extended DN GUID on DN elements */
328                 if (dsdb_dn_oid_to_format(schema_attr->syntax->ldap_oid) == DSDB_INVALID_DN) {
329                         continue;
330                 }
331
332                 /* Before we setup a procedure to modify the incoming message, we must copy it */
333                 if (!ac->new_req) {
334                         struct ldb_message *msg = ldb_msg_copy(ac, req->op.add.message);
335                         if (!msg) {
336                                 return ldb_oom(ldb_module_get_ctx(module));
337                         }
338                    
339                         ret = ldb_build_add_req(&ac->new_req, ac->ldb, ac, msg, req->controls, ac, extended_final_callback, req);
340                         if (ret != LDB_SUCCESS) {
341                                 return ret;
342                         }
343                 }
344                 /* Re-calculate el */
345                 el = &ac->new_req->op.add.message->elements[i];
346                 for (j = 0; j < el->num_values; j++) {
347                         ret = extended_store_replace(ac, ac->new_req->op.add.message->elements, &el->values[j],
348                                                      false, schema_attr->syntax->ldap_oid);
349                         if (ret != LDB_SUCCESS) {
350                                 return ret;
351                         }
352                 }
353         }
354
355         /* if no DNs were set continue */
356         if (ac->ops == NULL) {
357                 talloc_free(ac);
358                 return ldb_next_request(module, req);
359         }
360
361         /* start with the searches */
362         return ldb_next_request(module, ac->ops->search_req);
363 }
364
365 /* modify */
366 static int extended_dn_modify(struct ldb_module *module, struct ldb_request *req)
367 {
368         /* Look over list of modifications */
369         /* Find if any are for linked attributes */
370         /* Determine the effect of the modification */
371         /* Apply the modify to the linked entry */
372
373         int i, j;
374         struct extended_dn_context *ac;
375         int ret;
376
377         if (ldb_dn_is_special(req->op.mod.message->dn)) {
378                 /* do not manipulate our control entries */
379                 return ldb_next_request(module, req);
380         }
381
382         ac = extended_dn_context_init(module, req);
383         if (!ac) {
384                 return ldb_operr(ldb_module_get_ctx(module));
385         }
386
387         if (!ac->schema) {
388                 talloc_free(ac);
389                 /* without schema, this doesn't make any sense */
390                 return ldb_next_request(module, req);
391         }
392
393         for (i=0; i < req->op.mod.message->num_elements; i++) {
394                 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
395                 const struct dsdb_attribute *schema_attr
396                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
397                 if (!schema_attr) {
398                         continue;
399                 }
400
401                 /* We only setup an extended DN GUID on these particular DN objects */
402                 if (dsdb_dn_oid_to_format(schema_attr->syntax->ldap_oid) == DSDB_INVALID_DN) {
403                         continue;
404                 }
405
406                 /* Before we setup a procedure to modify the incoming message, we must copy it */
407                 if (!ac->new_req) {
408                         struct ldb_message *msg = ldb_msg_copy(ac, req->op.mod.message);
409                         if (!msg) {
410                                 talloc_free(ac);
411                                 return ldb_oom(ac->ldb);
412                         }
413                    
414                         ret = ldb_build_mod_req(&ac->new_req, ac->ldb, ac, msg, req->controls, ac, extended_final_callback, req);
415                         if (ret != LDB_SUCCESS) {
416                                 talloc_free(ac);
417                                 return ret;
418                         }
419                 }
420                 /* Re-calculate el */
421                 el = &ac->new_req->op.mod.message->elements[i];
422                 /* For each value being added, we need to setup the lookups to fill in the extended DN */
423                 for (j = 0; j < el->num_values; j++) {
424                         /* If we are just going to delete this
425                          * element, only do a lookup if
426                          * extended_store_replace determines it's an
427                          * input of an extended DN */
428                         bool is_delete = ((el->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_DELETE);
429
430                         ret = extended_store_replace(ac, req->op.mod.message->elements, &el->values[j],
431                                                      is_delete, schema_attr->syntax->ldap_oid);
432                         if (ret != LDB_SUCCESS) {
433                                 talloc_free(ac);
434                                 return ret;
435                         }
436                 }
437         }
438
439         /* if DNs were set continue */
440         if (ac->ops == NULL) {
441                 talloc_free(ac);
442                 return ldb_next_request(module, req);
443         }
444
445         /* start with the searches */
446         return ldb_next_request(module, ac->ops->search_req);
447 }
448
449 _PUBLIC_ const struct ldb_module_ops ldb_extended_dn_store_module_ops = {
450         .name              = "extended_dn_store",
451         .add               = extended_dn_add,
452         .modify            = extended_dn_modify,
453 };