s4-dsdb: prioritise GUID in extended_dn_in
[obnox/samba/samba-obnox.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.h>
34 #include <ldb_errors.h>
35 #include <ldb_module.h>
36
37 /*
38   TODO: if relax is not set then we need to reject the fancy RMD_* and
39   DELETED extended DN codes
40  */
41
42 /* search */
43 struct extended_search_context {
44         struct ldb_module *module;
45         struct ldb_request *req;
46         struct ldb_dn *basedn;
47         char *wellknown_object;
48         int extended_type;
49 };
50
51 /* An extra layer of indirection because LDB does not allow the original request to be altered */
52
53 static int extended_final_callback(struct ldb_request *req, struct ldb_reply *ares)
54 {
55         int ret = LDB_ERR_OPERATIONS_ERROR;
56         struct extended_search_context *ac;
57         ac = talloc_get_type(req->context, struct extended_search_context);
58
59         if (ares->error != LDB_SUCCESS) {
60                 ret = ldb_module_done(ac->req, ares->controls,
61                                       ares->response, ares->error);
62         } else {
63                 switch (ares->type) {
64                 case LDB_REPLY_ENTRY:
65                         
66                         ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
67                         break;
68                 case LDB_REPLY_REFERRAL:
69                         
70                         ret = ldb_module_send_referral(ac->req, ares->referral);
71                         break;
72                 case LDB_REPLY_DONE:
73                         
74                         ret = ldb_module_done(ac->req, ares->controls,
75                                               ares->response, ares->error);
76                         break;
77                 }
78         }
79         return ret;
80 }
81
82 static int extended_base_callback(struct ldb_request *req, struct ldb_reply *ares)
83 {
84         struct extended_search_context *ac;
85         struct ldb_request *down_req;
86         struct ldb_message_element *el;
87         int ret;
88         unsigned int i;
89         size_t wkn_len = 0;
90         char *valstr = NULL;
91         const char *found = NULL;
92
93         ac = talloc_get_type(req->context, struct extended_search_context);
94
95         if (!ares) {
96                 return ldb_module_done(ac->req, NULL, NULL,
97                                         LDB_ERR_OPERATIONS_ERROR);
98         }
99         if (ares->error != LDB_SUCCESS) {
100                 return ldb_module_done(ac->req, ares->controls,
101                                         ares->response, ares->error);
102         }
103
104         switch (ares->type) {
105         case LDB_REPLY_ENTRY:
106                 if (ac->basedn) {
107                         /* we have more than one match! This can
108                            happen as S-1-5-17 appears twice in a
109                            normal provision. We need to return
110                            NO_SUCH_OBJECT */
111                         const char *str = talloc_asprintf(req, "Duplicate base-DN matches found for '%s'",
112                                                           ldb_dn_get_extended_linearized(req, ac->req->op.search.base, 1));
113                         ldb_set_errstring(ldb_module_get_ctx(ac->module), str);
114                         return ldb_module_done(ac->req, NULL, NULL,
115                                                LDB_ERR_NO_SUCH_OBJECT);
116                 }
117
118                 if (!ac->wellknown_object) {
119                         ac->basedn = talloc_steal(ac, ares->message->dn);
120                         break;
121                 }
122
123                 wkn_len = strlen(ac->wellknown_object);
124
125                 el = ldb_msg_find_element(ares->message, "wellKnownObjects");
126                 if (!el) {
127                         ac->basedn = NULL;
128                         break;
129                 }
130
131                 for (i=0; i < el->num_values; i++) {
132                         valstr = talloc_strndup(ac,
133                                                 (const char *)el->values[i].data,
134                                                 el->values[i].length);
135                         if (!valstr) {
136                                 ldb_oom(ldb_module_get_ctx(ac->module));
137                                 return ldb_module_done(ac->req, NULL, NULL,
138                                                        LDB_ERR_OPERATIONS_ERROR);
139                         }
140
141                         if (strncasecmp(valstr, ac->wellknown_object, wkn_len) != 0) {
142                                 talloc_free(valstr);
143                                 continue;
144                         }
145
146                         found = &valstr[wkn_len];
147                         break;
148                 }
149
150                 if (!found) {
151                         break;
152                 }
153
154                 ac->basedn = ldb_dn_new(ac, ldb_module_get_ctx(ac->module), found);
155                 talloc_free(valstr);
156                 if (!ac->basedn) {
157                         ldb_oom(ldb_module_get_ctx(ac->module));
158                         return ldb_module_done(ac->req, NULL, NULL,
159                                                LDB_ERR_OPERATIONS_ERROR);
160                 }
161
162                 break;
163
164         case LDB_REPLY_REFERRAL:
165                 break;
166
167         case LDB_REPLY_DONE:
168
169                 if (!ac->basedn) {
170                         const char *str = talloc_asprintf(req, "Base-DN '%s' not found",
171                                                           ldb_dn_get_extended_linearized(req, ac->req->op.search.base, 1));
172                         ldb_set_errstring(ldb_module_get_ctx(ac->module), str);
173                         return ldb_module_done(ac->req, NULL, NULL,
174                                                LDB_ERR_NO_SUCH_OBJECT);
175                 }
176
177                 switch (ac->req->operation) {
178                 case LDB_SEARCH:
179                         ret = ldb_build_search_req_ex(&down_req,
180                                                       ldb_module_get_ctx(ac->module), ac->req,
181                                                       ac->basedn,
182                                                       ac->req->op.search.scope,
183                                                       ac->req->op.search.tree,
184                                                       ac->req->op.search.attrs,
185                                                       ac->req->controls,
186                                                       ac, extended_final_callback, 
187                                                       ac->req);
188                         LDB_REQ_SET_LOCATION(down_req);
189                         break;
190                 case LDB_ADD:
191                 {
192                         struct ldb_message *add_msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
193                         if (!add_msg) {
194                                 ldb_oom(ldb_module_get_ctx(ac->module));
195                                 return ldb_module_done(ac->req, NULL, NULL,
196                                                        LDB_ERR_OPERATIONS_ERROR);
197                         }
198                         
199                         add_msg->dn = ac->basedn;
200
201                         ret = ldb_build_add_req(&down_req,
202                                                 ldb_module_get_ctx(ac->module), ac->req,
203                                                 add_msg, 
204                                                 ac->req->controls,
205                                                 ac, extended_final_callback, 
206                                                 ac->req);
207                         LDB_REQ_SET_LOCATION(down_req);
208                         break;
209                 }
210                 case LDB_MODIFY:
211                 {
212                         struct ldb_message *mod_msg = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
213                         if (!mod_msg) {
214                                 ldb_oom(ldb_module_get_ctx(ac->module));
215                                 return ldb_module_done(ac->req, NULL, NULL,
216                                                        LDB_ERR_OPERATIONS_ERROR);
217                         }
218                         
219                         mod_msg->dn = ac->basedn;
220
221                         ret = ldb_build_mod_req(&down_req,
222                                                 ldb_module_get_ctx(ac->module), ac->req,
223                                                 mod_msg, 
224                                                 ac->req->controls,
225                                                 ac, extended_final_callback, 
226                                                 ac->req);
227                         LDB_REQ_SET_LOCATION(down_req);
228                         break;
229                 }
230                 case LDB_DELETE:
231                         ret = ldb_build_del_req(&down_req,
232                                                 ldb_module_get_ctx(ac->module), ac->req,
233                                                 ac->basedn, 
234                                                 ac->req->controls,
235                                                 ac, extended_final_callback, 
236                                                 ac->req);
237                         LDB_REQ_SET_LOCATION(down_req);
238                         break;
239                 case LDB_RENAME:
240                         ret = ldb_build_rename_req(&down_req,
241                                                    ldb_module_get_ctx(ac->module), ac->req,
242                                                    ac->basedn, 
243                                                    ac->req->op.rename.newdn,
244                                                    ac->req->controls,
245                                                    ac, extended_final_callback, 
246                                                    ac->req);
247                         LDB_REQ_SET_LOCATION(down_req);
248                         break;
249                 default:
250                         return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
251                 }
252                 
253                 if (ret != LDB_SUCCESS) {
254                         return ldb_module_done(ac->req, NULL, NULL, ret);
255                 }
256
257                 return ldb_next_request(ac->module, down_req);
258         }
259         talloc_free(ares);
260         return LDB_SUCCESS;
261 }
262
263 static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req, struct ldb_dn *dn)
264 {
265         struct extended_search_context *ac;
266         struct ldb_request *down_req;
267         int ret;
268         struct ldb_dn *base_dn = NULL;
269         enum ldb_scope base_dn_scope = LDB_SCOPE_BASE;
270         const char *base_dn_filter = NULL;
271         const char * const *base_dn_attrs = NULL;
272         char *wellknown_object = NULL;
273         static const char *no_attr[] = {
274                 NULL
275         };
276         static const char *wkattr[] = {
277                 "wellKnownObjects",
278                 NULL
279         };
280         bool all_partitions = false;
281
282         if (!ldb_dn_has_extended(dn)) {
283                 /* Move along there isn't anything to see here */
284                 return ldb_next_request(module, req);
285         } else {
286                 /* It looks like we need to map the DN */
287                 const struct ldb_val *sid_val, *guid_val, *wkguid_val;
288                 int num_components = ldb_dn_get_comp_num(dn);
289                 int num_ex_components = ldb_dn_get_extended_comp_num(dn);
290
291                 /*
292                   windows ldap searchs don't allow a baseDN with more
293                   than one extended component, or an extended
294                   component and a string DN
295
296                   We only enforce this over ldap, not for internal
297                   use, as there are just too many places where we
298                   internally want to use a DN that has come from a
299                   search with extended DN enabled, or comes from a DRS
300                   naming context.
301
302                   Enforcing this would also make debugging samba much
303                   harder, as we'd need to use ldb_dn_minimise() in a
304                   lot of places, and that would lose the DN string
305                   which is so useful for working out what a request is
306                   for
307                  */
308                 if ((num_components != 0 || num_ex_components != 1) &&
309                     ldb_req_is_untrusted(req)) {
310                         return ldb_error(ldb_module_get_ctx(module),
311                                          LDB_ERR_INVALID_DN_SYNTAX, "invalid number of DN components");
312                 }
313
314                 sid_val = ldb_dn_get_extended_component(dn, "SID");
315                 guid_val = ldb_dn_get_extended_component(dn, "GUID");
316                 wkguid_val = ldb_dn_get_extended_component(dn, "WKGUID");
317
318                 /*
319                   prioritise the GUID - we have had instances of
320                   duplicate SIDs in the database in the
321                   ForeignSecurityPrinciples due to provision errors
322                  */
323                 if (guid_val) {
324                         all_partitions = true;
325                         base_dn = ldb_get_default_basedn(ldb_module_get_ctx(module));
326                         base_dn_filter = talloc_asprintf(req, "(objectGUID=%s)",
327                                                          ldb_binary_encode(req, *guid_val));
328                         if (!base_dn_filter) {
329                                 return ldb_oom(ldb_module_get_ctx(module));
330                         }
331                         base_dn_scope = LDB_SCOPE_SUBTREE;
332                         base_dn_attrs = no_attr;
333
334                 } else if (sid_val) {
335                         all_partitions = true;
336                         base_dn = ldb_get_default_basedn(ldb_module_get_ctx(module));
337                         base_dn_filter = talloc_asprintf(req, "(objectSid=%s)",
338                                                          ldb_binary_encode(req, *sid_val));
339                         if (!base_dn_filter) {
340                                 return ldb_oom(ldb_module_get_ctx(module));
341                         }
342                         base_dn_scope = LDB_SCOPE_SUBTREE;
343                         base_dn_attrs = no_attr;
344
345                 } else if (wkguid_val) {
346                         char *wkguid_dup;
347                         char *tail_str;
348                         char *p;
349
350                         wkguid_dup = talloc_strndup(req, (char *)wkguid_val->data, wkguid_val->length);
351
352                         p = strchr(wkguid_dup, ',');
353                         if (!p) {
354                                 return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
355                                                  "Invalid WKGUID format");
356                         }
357
358                         p[0] = '\0';
359                         p++;
360
361                         wellknown_object = talloc_asprintf(req, "B:32:%s:", wkguid_dup);
362                         if (!wellknown_object) {
363                                 return ldb_oom(ldb_module_get_ctx(module));
364                         }
365
366                         tail_str = p;
367
368                         base_dn = ldb_dn_new(req, ldb_module_get_ctx(module), tail_str);
369                         talloc_free(wkguid_dup);
370                         if (!base_dn) {
371                                 return ldb_oom(ldb_module_get_ctx(module));
372                         }
373                         base_dn_filter = talloc_strdup(req, "(objectClass=*)");
374                         if (!base_dn_filter) {
375                                 return ldb_oom(ldb_module_get_ctx(module));
376                         }
377                         base_dn_scope = LDB_SCOPE_BASE;
378                         base_dn_attrs = wkattr;
379                 } else {
380                         return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
381                                          "Invalid extended DN component");
382                 }
383
384                 ac = talloc_zero(req, struct extended_search_context);
385                 if (ac == NULL) {
386                         return ldb_oom(ldb_module_get_ctx(module));
387                 }
388                 
389                 ac->module = module;
390                 ac->req = req;
391                 ac->basedn = NULL;  /* Filled in if the search finds the DN by SID/GUID etc */
392                 ac->wellknown_object = wellknown_object;
393                 
394                 /* If the base DN was an extended DN (perhaps a well known
395                  * GUID) then search for that, so we can proceed with the original operation */
396
397                 ret = ldb_build_search_req(&down_req,
398                                            ldb_module_get_ctx(module), ac,
399                                            base_dn,
400                                            base_dn_scope,
401                                            base_dn_filter,
402                                            base_dn_attrs,
403                                            req->controls,
404                                            ac, extended_base_callback,
405                                            req);
406                 LDB_REQ_SET_LOCATION(down_req);
407                 if (ret != LDB_SUCCESS) {
408                         return ldb_operr(ldb_module_get_ctx(module));
409                 }
410
411                 if (all_partitions) {
412                         struct ldb_search_options_control *control;
413                         control = talloc(down_req, struct ldb_search_options_control);
414                         control->search_options = 2;
415                         ret = ldb_request_replace_control(down_req,
416                                                       LDB_CONTROL_SEARCH_OPTIONS_OID,
417                                                       true, control);
418                         if (ret != LDB_SUCCESS) {
419                                 ldb_oom(ldb_module_get_ctx(module));
420                                 return ret;
421                         }
422                 }
423
424                 /* perform the search */
425                 return ldb_next_request(module, down_req);
426         }
427 }
428
429 static int extended_dn_in_search(struct ldb_module *module, struct ldb_request *req)
430 {
431         return extended_dn_in_fix(module, req, req->op.search.base);
432 }
433
434 static int extended_dn_in_modify(struct ldb_module *module, struct ldb_request *req)
435 {
436         return extended_dn_in_fix(module, req, req->op.mod.message->dn);
437 }
438
439 static int extended_dn_in_del(struct ldb_module *module, struct ldb_request *req)
440 {
441         return extended_dn_in_fix(module, req, req->op.del.dn);
442 }
443
444 static int extended_dn_in_rename(struct ldb_module *module, struct ldb_request *req)
445 {
446         return extended_dn_in_fix(module, req, req->op.rename.olddn);
447 }
448
449 static const struct ldb_module_ops ldb_extended_dn_in_module_ops = {
450         .name              = "extended_dn_in",
451         .search            = extended_dn_in_search,
452         .modify            = extended_dn_in_modify,
453         .del               = extended_dn_in_del,
454         .rename            = extended_dn_in_rename,
455 };
456
457 int ldb_extended_dn_in_module_init(const char *version)
458 {
459         LDB_MODULE_CHECK_VERSION(version);
460         return ldb_register_module(&ldb_extended_dn_in_module_ops);
461 }