3234f6f269d0665e5e06156aab38b1bc223e7bd6
[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-2008
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
46 #include <time.h>
47
48 struct extended_dn_replace_list {
49         struct extended_dn_replace_list *next;
50         struct ldb_dn *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_request *req;
62         struct ldb_request *new_req;
63
64         struct extended_dn_replace_list *ops;
65         struct extended_dn_replace_list *cur;
66 };
67
68
69 static struct extended_dn_context *extended_dn_context_init(struct ldb_module *module,
70                                                             struct ldb_request *req)
71 {
72         struct extended_dn_context *ac;
73
74         ac = talloc_zero(req, struct extended_dn_context);
75         if (ac == NULL) {
76                 ldb_oom(ldb_module_get_ctx(module));
77                 return NULL;
78         }
79
80         ac->schema = dsdb_get_schema(ldb_module_get_ctx(module));
81         ac->module = module;
82         ac->req = req;
83
84         return ac;
85 }
86
87 /* An extra layer of indirection because LDB does not allow the original request to be altered */
88
89 static int extended_final_callback(struct ldb_request *req, struct ldb_reply *ares)
90 {
91         int ret = LDB_ERR_OPERATIONS_ERROR;
92         struct extended_dn_context *ac;
93         ac = talloc_get_type(req->context, struct extended_dn_context);
94
95         if (ares->error != LDB_SUCCESS) {
96                 ret = ldb_module_done(ac->req, ares->controls,
97                                       ares->response, ares->error);
98         } else {
99                 switch (ares->type) {
100                 case LDB_REPLY_ENTRY:
101                         
102                         ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
103                         break;
104                 case LDB_REPLY_REFERRAL:
105                         
106                         ret = ldb_module_send_referral(ac->req, ares->referral);
107                         break;
108                 case LDB_REPLY_DONE:
109                         
110                         ret = ldb_module_done(ac->req, ares->controls,
111                                               ares->response, ares->error);
112                         break;
113                 }
114         }
115         return ret;
116 }
117
118 static int extended_replace_dn(struct ldb_request *req, struct ldb_reply *ares)
119 {
120         struct extended_dn_replace_list *os = talloc_get_type(req->context, 
121                                                            struct extended_dn_replace_list);
122
123         if (!ares) {
124                 return ldb_module_done(os->ac->req, NULL, NULL,
125                                         LDB_ERR_OPERATIONS_ERROR);
126         }
127         if (ares->error == LDB_ERR_NO_SUCH_OBJECT) {
128                 /* Don't worry too much about dangling references */
129
130                 ldb_reset_err_string(ldb_module_get_ctx(os->ac->module));
131                 if (os->next) {
132                         struct extended_dn_replace_list *next;
133
134                         next = os->next;
135
136                         talloc_free(os);
137
138                         os = next;
139                         return ldb_next_request(os->ac->module, next->search_req);
140                 } else {
141                         /* Otherwise, we are done - let's run the
142                          * request now we have swapped the DNs for the
143                          * full versions */
144                         return ldb_next_request(os->ac->module, os->ac->req);
145                 }
146         }
147         if (ares->error != LDB_SUCCESS) {
148                 return ldb_module_done(os->ac->req, ares->controls,
149                                         ares->response, ares->error);
150         }
151
152         /* Only entries are interesting, and we only want the olddn */
153         switch (ares->type) {
154         case LDB_REPLY_ENTRY:
155         {
156                 /* This *must* be the right DN, as this is a base
157                  * search.  We can't check, as it could be an extended
158                  * DN, so a module below will resolve it */
159                 struct ldb_dn *dn = ares->message->dn;
160
161                 /* Replace the DN with the extended version of the DN
162                  * (ie, add SID and GUID) */
163                 *os->replace_dn = data_blob_string_const(
164                         ldb_dn_get_extended_linearized(os->mem_ctx, 
165                                                        dn, 1));
166                 if (os->replace_dn->data == NULL) {
167                         return ldb_module_done(os->ac->req, NULL, NULL,
168                                                 LDB_ERR_OPERATIONS_ERROR);
169                 }
170                 break;
171         }
172         case LDB_REPLY_REFERRAL:
173                 /* ignore */
174                 break;
175
176         case LDB_REPLY_DONE:
177
178                 talloc_free(ares);
179                 
180                 /* Run the next search */
181
182                 if (os->next) {
183                         struct extended_dn_replace_list *next;
184
185                         next = os->next;
186
187                         talloc_free(os);
188
189                         os = next;
190                         return ldb_next_request(os->ac->module, next->search_req);
191                 } else {
192                         /* Otherwise, we are done - let's run the
193                          * request now we have swapped the DNs for the
194                          * full versions */
195                         return ldb_next_request(os->ac->module, os->ac->new_req);
196                 }
197         }
198
199         talloc_free(ares);
200         return LDB_SUCCESS;
201 }
202
203 /* We have a 'normal' DN in the inbound request.  We need to find out
204  * what the GUID and SID are on the DN it points to, so we can
205  * construct an extended DN for storage.
206  *
207  * This creates a list of DNs to look up, and the plain DN to replace
208  */
209
210 static int extended_store_replace(struct extended_dn_context *ac,
211                                   TALLOC_CTX *callback_mem_ctx,
212                                   struct ldb_val *plain_dn)
213 {
214         int ret;
215         struct extended_dn_replace_list *os;
216         static const char *attrs[] = {
217                 "objectSid",
218                 "objectGUID",
219                 NULL
220         };
221
222         os = talloc_zero(ac, struct extended_dn_replace_list);
223         if (!os) {
224                 return LDB_ERR_OPERATIONS_ERROR;
225         }
226
227         os->ac = ac;
228         
229         os->mem_ctx = callback_mem_ctx;
230
231         os->dn = ldb_dn_from_ldb_val(os, ldb_module_get_ctx(ac->module), plain_dn);
232         if (!os->dn || !ldb_dn_validate(os->dn)) {
233                 talloc_free(os);
234                 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module), 
235                                        "could not parse %.*s as a DN", (int)plain_dn->length, plain_dn->data);
236                 return LDB_ERR_INVALID_DN_SYNTAX;
237         }
238
239         os->replace_dn = plain_dn;
240
241         /* The search request here might happen to be for an
242          * 'extended' style DN, such as <GUID=abced...>.  The next
243          * module in the stack will convert this into a normal DN for
244          * processing */
245         ret = ldb_build_search_req(&os->search_req,
246                                    ldb_module_get_ctx(ac->module), os, os->dn, LDB_SCOPE_BASE, NULL, 
247                                    attrs, NULL, os, extended_replace_dn,
248                                    ac->req);
249
250         if (ret != LDB_SUCCESS) {
251                 talloc_free(os);
252                 return ret;
253         }
254
255         ret = ldb_request_add_control(os->search_req,
256                                       DSDB_CONTROL_DN_STORAGE_FORMAT_OID,
257                                       true, NULL);
258         if (ret != LDB_SUCCESS) {
259                 talloc_free(os);
260                 return ret;
261         }
262
263         if (ac->ops) {
264                 ac->cur->next = os;
265         } else {
266                 ac->ops = os;
267         }
268         ac->cur = os;
269
270         return LDB_SUCCESS;
271 }
272
273
274 /* add */
275 static int extended_dn_add(struct ldb_module *module, struct ldb_request *req)
276 {
277         struct extended_dn_context *ac;
278         int ret;
279         int i, j;
280
281         if (ldb_dn_is_special(req->op.add.message->dn)) {
282                 /* do not manipulate our control entries */
283                 return ldb_next_request(module, req);
284         }
285
286         ac = extended_dn_context_init(module, req);
287         if (!ac) {
288                 return LDB_ERR_OPERATIONS_ERROR;
289         }
290
291         if (!ac->schema) {
292                 /* without schema, this doesn't make any sense */
293                 talloc_free(ac);
294                 return ldb_next_request(module, req);
295         }
296
297         for (i=0; i < req->op.add.message->num_elements; i++) {
298                 const struct ldb_message_element *el = &req->op.add.message->elements[i];
299                 const struct dsdb_attribute *schema_attr
300                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
301                 if (!schema_attr) {
302                         continue;
303                 }
304
305                 /* We only setup an extended DN GUID on these particular DN objects */
306                 if (strcmp(schema_attr->attributeSyntax_oid, "2.5.5.1") != 0) {
307                         continue;
308                 }
309
310                 /* Before we setup a procedure to modify the incoming message, we must copy it */
311                 if (!ac->new_req) {
312                         struct ldb_message *msg = ldb_msg_copy(ac, req->op.add.message);
313                         if (!msg) {
314                                 ldb_oom(ldb_module_get_ctx(module));
315                                 return LDB_ERR_OPERATIONS_ERROR;
316                         }
317                    
318                         ret = ldb_build_add_req(&ac->new_req, ldb_module_get_ctx(module), ac, msg, req->controls, ac, extended_final_callback, req);
319                         if (ret != LDB_SUCCESS) {
320                                 return ret;
321                         }
322                 }
323                 /* Re-calculate el */
324                 el = &ac->new_req->op.add.message->elements[i];
325                 for (j = 0; j < el->num_values; j++) {
326                         ret = extended_store_replace(ac, ac->new_req->op.add.message->elements, &el->values[j]);
327                         if (ret != LDB_SUCCESS) {
328                                 return ret;
329                         }
330                 }
331         }
332
333         /* if DNs were set continue */
334         if (ac->ops == NULL) {
335                 talloc_free(ac);
336                 return ldb_next_request(module, req);
337         }
338
339         /* start with the searches */
340         return ldb_next_request(module, ac->ops->search_req);
341 }
342
343 /* modify */
344 static int extended_dn_modify(struct ldb_module *module, struct ldb_request *req)
345 {
346         /* Look over list of modifications */
347         /* Find if any are for linked attributes */
348         /* Determine the effect of the modification */
349         /* Apply the modify to the linked entry */
350
351         int i, j;
352         struct extended_dn_context *ac;
353         int ret;
354
355         if (ldb_dn_is_special(req->op.mod.message->dn)) {
356                 /* do not manipulate our control entries */
357                 return ldb_next_request(module, req);
358         }
359
360         ac = extended_dn_context_init(module, req);
361         if (!ac) {
362                 return LDB_ERR_OPERATIONS_ERROR;
363         }
364
365         if (!ac->schema) {
366                 /* without schema, this doesn't make any sense */
367                 return ldb_next_request(module, req);
368         }
369
370         for (i=0; i < req->op.mod.message->num_elements; i++) {
371                 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
372                 const struct dsdb_attribute *schema_attr
373                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
374                 if (!schema_attr) {
375                         continue;
376                 }
377
378                 /* We only setup an extended DN GUID on these particular DN objects */
379                 if (strcmp(schema_attr->attributeSyntax_oid, "2.5.5.1") != 0) {
380                         continue;
381                 }
382                 
383                 /* Before we setup a procedure to modify the incoming message, we must copy it */
384                 if (!ac->new_req) {
385                         struct ldb_message *msg = ldb_msg_copy(ac, req->op.mod.message);
386                         if (!msg) {
387                                 ldb_oom(ldb_module_get_ctx(module));
388                                 return LDB_ERR_OPERATIONS_ERROR;
389                         }
390                    
391                         ret = ldb_build_mod_req(&ac->new_req, ldb_module_get_ctx(module), ac, msg, req->controls, ac, extended_final_callback, req);
392                         if (ret != LDB_SUCCESS) {
393                                 return ret;
394                         }
395                 }
396                 /* Re-calculate el */
397                 el = &ac->new_req->op.mod.message->elements[i];
398                 /* For each value being added, we need to setup the lookups to fill in the extended DN */
399                 for (j = 0; j < el->num_values; j++) {
400                         struct ldb_dn *dn = ldb_dn_from_ldb_val(ac, ldb_module_get_ctx(module), &el->values[j]);
401                         if (!dn || !ldb_dn_validate(dn)) {
402                                 ldb_asprintf_errstring(ldb_module_get_ctx(module), 
403                                                        "could not parse attribute %s as a DN", el->name);
404                                 return LDB_ERR_INVALID_DN_SYNTAX;
405                         }
406                         if (((el->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_DELETE) && !ldb_dn_has_extended(dn)) {
407                                 /* NO need to figure this DN out, it's going to be deleted anyway */
408                                 continue;
409                         }
410                         ret = extended_store_replace(ac, req->op.mod.message->elements, &el->values[j]);
411                         if (ret != LDB_SUCCESS) {
412                                 return ret;
413                         }
414                 }
415         }
416
417         /* if DNs were set continue */
418         if (ac->ops == NULL) {
419                 talloc_free(ac);
420                 return ldb_next_request(module, req);
421         }
422
423         /* start with the searches */
424         return ldb_next_request(module, ac->ops->search_req);
425 }
426
427 _PUBLIC_ const struct ldb_module_ops ldb_extended_dn_store_module_ops = {
428         .name              = "extended_dn_store",
429         .add               = extended_dn_add,
430         .modify            = extended_dn_modify,
431 };