s3: piddir creation fix part 2.
[ira/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->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_SET_LOCATION(os->search_req);
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_RECYCLED|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         unsigned 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 (schema_attr->dn_format == 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                         LDB_REQ_SET_LOCATION(ac->new_req);
341                         if (ret != LDB_SUCCESS) {
342                                 return ret;
343                         }
344                 }
345                 /* Re-calculate el */
346                 el = &ac->new_req->op.add.message->elements[i];
347                 for (j = 0; j < el->num_values; j++) {
348                         ret = extended_store_replace(ac, ac->new_req, &el->values[j],
349                                                      false, schema_attr->syntax->ldap_oid);
350                         if (ret != LDB_SUCCESS) {
351                                 return ret;
352                         }
353                 }
354         }
355
356         /* if no DNs were set continue */
357         if (ac->ops == NULL) {
358                 talloc_free(ac);
359                 return ldb_next_request(module, req);
360         }
361
362         /* start with the searches */
363         return ldb_next_request(module, ac->ops->search_req);
364 }
365
366 /* modify */
367 static int extended_dn_modify(struct ldb_module *module, struct ldb_request *req)
368 {
369         /* Look over list of modifications */
370         /* Find if any are for linked attributes */
371         /* Determine the effect of the modification */
372         /* Apply the modify to the linked entry */
373
374         unsigned int i, j;
375         struct extended_dn_context *ac;
376         int ret;
377
378         if (ldb_dn_is_special(req->op.mod.message->dn)) {
379                 /* do not manipulate our control entries */
380                 return ldb_next_request(module, req);
381         }
382
383         ac = extended_dn_context_init(module, req);
384         if (!ac) {
385                 return ldb_operr(ldb_module_get_ctx(module));
386         }
387
388         if (!ac->schema) {
389                 talloc_free(ac);
390                 /* without schema, this doesn't make any sense */
391                 return ldb_next_request(module, req);
392         }
393
394         for (i=0; i < req->op.mod.message->num_elements; i++) {
395                 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
396                 const struct dsdb_attribute *schema_attr
397                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
398                 if (!schema_attr) {
399                         continue;
400                 }
401
402                 /* We only setup an extended DN GUID on these particular DN objects */
403                 if (schema_attr->dn_format == DSDB_INVALID_DN) {
404                         continue;
405                 }
406
407                 /* Before we setup a procedure to modify the incoming message, we must copy it */
408                 if (!ac->new_req) {
409                         struct ldb_message *msg = ldb_msg_copy(ac, req->op.mod.message);
410                         if (!msg) {
411                                 talloc_free(ac);
412                                 return ldb_oom(ac->ldb);
413                         }
414                    
415                         ret = ldb_build_mod_req(&ac->new_req, ac->ldb, ac, msg, req->controls, ac, extended_final_callback, req);
416                         LDB_REQ_SET_LOCATION(ac->new_req);
417                         if (ret != LDB_SUCCESS) {
418                                 talloc_free(ac);
419                                 return ret;
420                         }
421                 }
422                 /* Re-calculate el */
423                 el = &ac->new_req->op.mod.message->elements[i];
424                 /* For each value being added, we need to setup the lookups to fill in the extended DN */
425                 for (j = 0; j < el->num_values; j++) {
426                         /* If we are just going to delete this
427                          * element, only do a lookup if
428                          * extended_store_replace determines it's an
429                          * input of an extended DN */
430                         bool is_delete = (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE);
431
432                         ret = extended_store_replace(ac, ac->new_req, &el->values[j],
433                                                      is_delete, schema_attr->syntax->ldap_oid);
434                         if (ret != LDB_SUCCESS) {
435                                 talloc_free(ac);
436                                 return ret;
437                         }
438                 }
439         }
440
441         /* if DNs were set continue */
442         if (ac->ops == NULL) {
443                 talloc_free(ac);
444                 return ldb_next_request(module, req);
445         }
446
447         /* start with the searches */
448         return ldb_next_request(module, ac->ops->search_req);
449 }
450
451 static const struct ldb_module_ops ldb_extended_dn_store_module_ops = {
452         .name              = "extended_dn_store",
453         .add               = extended_dn_add,
454         .modify            = extended_dn_modify,
455 };
456
457 int ldb_extended_dn_store_module_init(const char *version)
458 {
459         LDB_MODULE_CHECK_VERSION(version);
460         return ldb_register_module(&ldb_extended_dn_store_module_ops);
461 }