s4-drs: Synchronous Implementation of generated parentGUID
[ira/wip.git] / source4 / dsdb / samdb / ldb_modules / extended_dn_in.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 interprets DNs of the form <SID=S-1-2-4456> into normal DNs.
27  *
28  *  Authors: Simo Sorce
29  *           Andrew Bartlett
30  */
31
32 #include "includes.h"
33 #include "ldb/include/ldb.h"
34 #include "ldb/include/ldb_errors.h"
35 #include "ldb/include/ldb_module.h"
36
37 /* search */
38 struct extended_search_context {
39         struct ldb_module *module;
40         struct ldb_request *req;
41         struct ldb_dn *basedn;
42         char *wellknown_object;
43         int extended_type;
44 };
45
46 /* An extra layer of indirection because LDB does not allow the original request to be altered */
47
48 static int extended_final_callback(struct ldb_request *req, struct ldb_reply *ares)
49 {
50         int ret = LDB_ERR_OPERATIONS_ERROR;
51         struct extended_search_context *ac;
52         ac = talloc_get_type(req->context, struct extended_search_context);
53
54         if (ares->error != LDB_SUCCESS) {
55                 ret = ldb_module_done(ac->req, ares->controls,
56                                       ares->response, ares->error);
57         } else {
58                 switch (ares->type) {
59                 case LDB_REPLY_ENTRY:
60                         
61                         ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
62                         break;
63                 case LDB_REPLY_REFERRAL:
64                         
65                         ret = ldb_module_send_referral(ac->req, ares->referral);
66                         break;
67                 case LDB_REPLY_DONE:
68                         
69                         ret = ldb_module_done(ac->req, ares->controls,
70                                               ares->response, ares->error);
71                         break;
72                 }
73         }
74         return ret;
75 }
76
77 static int extended_base_callback(struct ldb_request *req, struct ldb_reply *ares)
78 {
79         struct extended_search_context *ac;
80         struct ldb_request *down_req;
81         struct ldb_message_element *el;
82         int ret;
83         size_t i;
84         size_t wkn_len = 0;
85         char *valstr = NULL;
86         const char *found = NULL;
87
88         ac = talloc_get_type(req->context, struct extended_search_context);
89
90         if (!ares) {
91                 return ldb_module_done(ac->req, NULL, NULL,
92                                         LDB_ERR_OPERATIONS_ERROR);
93         }
94         if (ares->error != LDB_SUCCESS) {
95                 return ldb_module_done(ac->req, ares->controls,
96                                         ares->response, ares->error);
97         }
98
99         switch (ares->type) {
100         case LDB_REPLY_ENTRY:
101                 if (!ac->wellknown_object) {
102                         ac->basedn = talloc_steal(ac, ares->message->dn);
103                         break;
104                 }
105
106                 wkn_len = strlen(ac->wellknown_object);
107
108                 el = ldb_msg_find_element(ares->message, "wellKnownObjects");
109                 if (!el) {
110                         ac->basedn = NULL;
111                         break;
112                 }
113
114                 for (i=0; i < el->num_values; i++) {
115                         valstr = talloc_strndup(ac,
116                                                 (const char *)el->values[i].data,
117                                                 el->values[i].length);
118                         if (!valstr) {
119                                 ldb_oom(ldb_module_get_ctx(ac->module));
120                                 return ldb_module_done(ac->req, NULL, NULL,
121                                                        LDB_ERR_OPERATIONS_ERROR);
122                         }
123
124                         if (strncasecmp(valstr, ac->wellknown_object, wkn_len) != 0) {
125                                 talloc_free(valstr);
126                                 continue;
127                         }
128
129                         found = &valstr[wkn_len];
130                         break;
131                 }
132
133                 if (!found) {
134                         break;
135                 }
136
137                 ac->basedn = ldb_dn_new(ac, ldb_module_get_ctx(ac->module), found);
138                 talloc_free(valstr);
139                 if (!ac->basedn) {
140                         ldb_oom(ldb_module_get_ctx(ac->module));
141                         return ldb_module_done(ac->req, NULL, NULL,
142                                                LDB_ERR_OPERATIONS_ERROR);
143                 }
144
145                 break;
146
147         case LDB_REPLY_REFERRAL:
148                 break;
149
150         case LDB_REPLY_DONE:
151
152                 if (!ac->basedn) {
153                         const char *str = talloc_asprintf(req, "Base-DN '%s' not found",
154                                                           ldb_dn_get_linearized(ac->req->op.search.base));
155                         ldb_set_errstring(ldb_module_get_ctx(ac->module), str);
156                         return ldb_module_done(ac->req, NULL, NULL,
157                                                LDB_ERR_NO_SUCH_OBJECT);
158                 }
159
160                 switch (ac->req->operation) {
161                 case LDB_SEARCH:
162                         ret = ldb_build_search_req_ex(&down_req,
163                                                       ldb_module_get_ctx(ac->module), ac->req,
164                                                       ac->basedn,
165                                                       ac->req->op.search.scope,
166                                                       ac->req->op.search.tree,
167                                                       ac->req->op.search.attrs,
168                                                       ac->req->controls,
169                                                       ac, extended_final_callback, 
170                                                       ac->req);
171                         break;
172                 case LDB_ADD:
173                 {
174                         struct ldb_message *add_msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
175                         if (!add_msg) {
176                                 ldb_oom(ldb_module_get_ctx(ac->module));
177                                 return ldb_module_done(ac->req, NULL, NULL,
178                                                        LDB_ERR_OPERATIONS_ERROR);
179                         }
180                         
181                         add_msg->dn = ac->basedn;
182
183                         ret = ldb_build_add_req(&down_req,
184                                                 ldb_module_get_ctx(ac->module), ac->req,
185                                                 add_msg, 
186                                                 ac->req->controls,
187                                                 ac, extended_final_callback, 
188                                                 ac->req);
189                         break;
190                 }
191                 case LDB_MODIFY:
192                 {
193                         struct ldb_message *mod_msg = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
194                         if (!mod_msg) {
195                                 ldb_oom(ldb_module_get_ctx(ac->module));
196                                 return ldb_module_done(ac->req, NULL, NULL,
197                                                        LDB_ERR_OPERATIONS_ERROR);
198                         }
199                         
200                         mod_msg->dn = ac->basedn;
201
202                         ret = ldb_build_mod_req(&down_req,
203                                                 ldb_module_get_ctx(ac->module), ac->req,
204                                                 mod_msg, 
205                                                 ac->req->controls,
206                                                 ac, extended_final_callback, 
207                                                 ac->req);
208                         break;
209                 }
210                 case LDB_DELETE:
211                         ret = ldb_build_del_req(&down_req,
212                                                 ldb_module_get_ctx(ac->module), ac->req,
213                                                 ac->basedn, 
214                                                 ac->req->controls,
215                                                 ac, extended_final_callback, 
216                                                 ac->req);
217                         break;
218                 case LDB_RENAME:
219                         ret = ldb_build_rename_req(&down_req,
220                                                    ldb_module_get_ctx(ac->module), ac->req,
221                                                    ac->basedn, 
222                                                    ac->req->op.rename.newdn,
223                                                    ac->req->controls,
224                                                    ac, extended_final_callback, 
225                                                    ac->req);
226                         break;
227                 default:
228                         return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
229                 }
230                 
231                 if (ret != LDB_SUCCESS) {
232                         return ldb_module_done(ac->req, NULL, NULL, ret);
233                 }
234
235                 return ldb_next_request(ac->module, down_req);
236         }
237         talloc_free(ares);
238         return LDB_SUCCESS;
239 }
240
241 static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req, struct ldb_dn *dn)
242 {
243         struct extended_search_context *ac;
244         struct ldb_request *down_req;
245         int ret;
246         struct ldb_dn *base_dn = NULL;
247         enum ldb_scope base_dn_scope = LDB_SCOPE_BASE;
248         const char *base_dn_filter = NULL;
249         const char * const *base_dn_attrs = NULL;
250         char *wellknown_object = NULL;
251         static const char *no_attr[] = {
252                 NULL
253         };
254         static const char *wkattr[] = {
255                 "wellKnownObjects",
256                 NULL
257         };
258         bool all_partitions = false;
259
260         if (!ldb_dn_has_extended(dn)) {
261                 /* Move along there isn't anything to see here */
262                 return ldb_next_request(module, req);
263         } else {
264                 /* It looks like we need to map the DN */
265                 const struct ldb_val *sid_val, *guid_val, *wkguid_val;
266
267                 sid_val = ldb_dn_get_extended_component(dn, "SID");
268                 guid_val = ldb_dn_get_extended_component(dn, "GUID");
269                 wkguid_val = ldb_dn_get_extended_component(dn, "WKGUID");
270
271                 if (sid_val) {
272                         all_partitions = true;
273                         base_dn = ldb_get_default_basedn(ldb_module_get_ctx(module));
274                         base_dn_filter = talloc_asprintf(req, "(objectSid=%s)", 
275                                                          ldb_binary_encode(req, *sid_val));
276                         if (!base_dn_filter) {
277                                 ldb_oom(ldb_module_get_ctx(module));
278                                 return LDB_ERR_OPERATIONS_ERROR;
279                         }
280                         base_dn_scope = LDB_SCOPE_SUBTREE;
281                         base_dn_attrs = no_attr;
282
283                 } else if (guid_val) {
284
285                         all_partitions = true;
286                         base_dn = ldb_get_default_basedn(ldb_module_get_ctx(module));
287                         base_dn_filter = talloc_asprintf(req, "(objectGUID=%s)", 
288                                                          ldb_binary_encode(req, *guid_val));
289                         if (!base_dn_filter) {
290                                 ldb_oom(ldb_module_get_ctx(module));
291                                 return LDB_ERR_OPERATIONS_ERROR;
292                         }
293                         base_dn_scope = LDB_SCOPE_SUBTREE;
294                         base_dn_attrs = no_attr;
295
296
297                 } else if (wkguid_val) {
298                         char *wkguid_dup;
299                         char *tail_str;
300                         char *p;
301
302                         wkguid_dup = talloc_strndup(req, (char *)wkguid_val->data, wkguid_val->length);
303
304                         p = strchr(wkguid_dup, ',');
305                         if (!p) {
306                                 return LDB_ERR_INVALID_DN_SYNTAX;
307                         }
308
309                         p[0] = '\0';
310                         p++;
311
312                         wellknown_object = talloc_asprintf(req, "B:32:%s:", wkguid_dup);
313                         if (!wellknown_object) {
314                                 ldb_oom(ldb_module_get_ctx(module));
315                                 return LDB_ERR_OPERATIONS_ERROR;
316                         }
317
318                         tail_str = p;
319
320                         base_dn = ldb_dn_new(req, ldb_module_get_ctx(module), tail_str);
321                         talloc_free(wkguid_dup);
322                         if (!base_dn) {
323                                 ldb_oom(ldb_module_get_ctx(module));
324                                 return LDB_ERR_OPERATIONS_ERROR;
325                         }
326                         base_dn_filter = talloc_strdup(req, "(objectClass=*)");
327                         if (!base_dn_filter) {
328                                 ldb_oom(ldb_module_get_ctx(module));
329                                 return LDB_ERR_OPERATIONS_ERROR;
330                         }
331                         base_dn_scope = LDB_SCOPE_BASE;
332                         base_dn_attrs = wkattr;
333                 } else {
334                         return LDB_ERR_INVALID_DN_SYNTAX;
335                 }
336
337                 ac = talloc_zero(req, struct extended_search_context);
338                 if (ac == NULL) {
339                         ldb_oom(ldb_module_get_ctx(module));
340                         return LDB_ERR_OPERATIONS_ERROR;
341                 }
342                 
343                 ac->module = module;
344                 ac->req = req;
345                 ac->basedn = NULL;  /* Filled in if the search finds the DN by SID/GUID etc */
346                 ac->wellknown_object = wellknown_object;
347                 
348                 /* If the base DN was an extended DN (perhaps a well known
349                  * GUID) then search for that, so we can proceed with the original operation */
350
351                 ret = ldb_build_search_req(&down_req,
352                                            ldb_module_get_ctx(module), ac,
353                                            base_dn,
354                                            base_dn_scope,
355                                            base_dn_filter,
356                                            base_dn_attrs,
357                                            NULL,
358                                            ac, extended_base_callback,
359                                            req);
360                 if (ret != LDB_SUCCESS) {
361                         return LDB_ERR_OPERATIONS_ERROR;
362                 }
363
364                 if (all_partitions) {
365                         struct ldb_search_options_control *control;
366                         control = talloc(down_req, struct ldb_search_options_control);
367                         control->search_options = 2;
368                         ret = ldb_request_add_control(down_req,
369                                                       LDB_CONTROL_SEARCH_OPTIONS_OID,
370                                                       true, control);
371                         if (ret != LDB_SUCCESS) {
372                                 ldb_oom(ldb_module_get_ctx(module));
373                                 return ret;
374                         }
375                 }
376
377                 /* perform the search */
378                 return ldb_next_request(module, down_req);
379         }
380 }
381
382 static int extended_dn_in_search(struct ldb_module *module, struct ldb_request *req)
383 {
384         return extended_dn_in_fix(module, req, req->op.search.base);
385 }
386
387 static int extended_dn_in_modify(struct ldb_module *module, struct ldb_request *req)
388 {
389         return extended_dn_in_fix(module, req, req->op.mod.message->dn);
390 }
391
392 static int extended_dn_in_del(struct ldb_module *module, struct ldb_request *req)
393 {
394         return extended_dn_in_fix(module, req, req->op.del.dn);
395 }
396
397 static int extended_dn_in_rename(struct ldb_module *module, struct ldb_request *req)
398 {
399         return extended_dn_in_fix(module, req, req->op.rename.olddn);
400 }
401
402 _PUBLIC_ const struct ldb_module_ops ldb_extended_dn_in_module_ops = {
403         .name              = "extended_dn_in",
404         .search            = extended_dn_in_search,
405         .modify            = extended_dn_in_modify,
406         .del               = extended_dn_in_del,
407         .rename            = extended_dn_in_rename,
408 };