Move ufc to libreplace.
[ira/wip.git] / source4 / dsdb / samdb / ldb_modules / extended_dn.c
1 /* 
2    ldb database library
3
4    Copyright (C) Simo Sorce 2005-2008
5
6      ** NOTE! The following LGPL license applies to the ldb
7      ** library. This does NOT imply that all of Samba is released
8      ** under the LGPL
9    
10    This library is free software; you can redistribute it and/or
11    modify it under the terms of the GNU Lesser General Public
12    License as published by the Free Software Foundation; either
13    version 3 of the License, or (at your option) any later version.
14
15    This library is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    Lesser General Public License for more details.
19
20    You should have received a copy of the GNU Lesser General Public
21    License along with this library; if not, see <http://www.gnu.org/licenses/>.
22 */
23
24 /*
25  *  Name: ldb
26  *
27  *  Component: ldb extended dn control module
28  *
29  *  Description: this module builds a special dn
30  *
31  *  Author: Simo Sorce
32  */
33
34 #include "includes.h"
35 #include "ldb/include/ldb.h"
36 #include "ldb/include/ldb_errors.h"
37 #include "ldb/include/ldb_private.h"
38 #include "librpc/gen_ndr/ndr_misc.h"
39 #include "dsdb/samdb/samdb.h"
40 #include "libcli/security/security.h"
41
42 #include <time.h>
43
44 static bool is_attr_in_list(const char * const * attrs, const char *attr)
45 {
46         int i;
47
48         for (i = 0; attrs[i]; i++) {
49                 if (strcasecmp(attrs[i], attr) == 0)
50                         return true;
51         }
52
53         return false;
54 }
55
56 static char **copy_attrs(void *mem_ctx, const char * const * attrs)
57 {
58         char **new;
59         int i, num;
60
61         for (num = 0; attrs[num]; num++);
62
63         new = talloc_array(mem_ctx, char *, num + 1);
64         if (!new) return NULL;
65
66         for(i = 0; i < num; i++) {
67                 new[i] = talloc_strdup(new, attrs[i]);
68                 if (!new[i]) {
69                         talloc_free(new);
70                         return NULL;
71                 }
72         }
73         new[i] = NULL;
74
75         return new;
76 }
77
78 static bool add_attrs(void *mem_ctx, char ***attrs, const char *attr)
79 {
80         char **new;
81         int num;
82
83         for (num = 0; (*attrs)[num]; num++);
84
85         new = talloc_realloc(mem_ctx, *attrs, char *, num + 2);
86         if (!new) return false;
87
88         *attrs = new;
89
90         new[num] = talloc_strdup(new, attr);
91         if (!new[num]) return false;
92
93         new[num + 1] = NULL;
94
95         return true;
96 }
97
98 static int inject_extended_dn(struct ldb_message *msg,
99                                 struct ldb_context *ldb,
100                                 int type,
101                                 bool remove_guid,
102                                 bool remove_sid)
103 {
104         const struct ldb_val *val;
105         struct GUID guid;
106         struct dom_sid *sid;
107         const DATA_BLOB *guid_blob;
108         const DATA_BLOB *sid_blob;
109         char *object_guid;
110         char *object_sid;
111         char *new_dn;
112
113         guid_blob = ldb_msg_find_ldb_val(msg, "objectGUID");
114         sid_blob = ldb_msg_find_ldb_val(msg, "objectSID");
115
116         if (!guid_blob) {
117                 return LDB_ERR_OPERATIONS_ERROR;
118         }
119
120         switch (type) {
121                 case 0:
122                         /* return things in hexadecimal format */
123                         if (sid_blob) {
124                                 const char *lower_guid_hex = strlower_talloc(msg, data_blob_hex_string(msg, guid_blob));
125                                 const char *lower_sid_hex = strlower_talloc(msg, data_blob_hex_string(msg, sid_blob));
126                                 if (!lower_guid_hex || !lower_sid_hex) {
127                                         return LDB_ERR_OPERATIONS_ERROR;
128                                 }
129                                 new_dn = talloc_asprintf(msg, "<GUID=%s>;<SID=%s>;%s",
130                                                          lower_guid_hex, 
131                                                          lower_sid_hex,
132                                                          ldb_dn_get_linearized(msg->dn));
133                         } else {
134                                 const char *lower_guid_hex = strlower_talloc(msg, data_blob_hex_string(msg, guid_blob));
135                                 if (!lower_guid_hex) {
136                                         return LDB_ERR_OPERATIONS_ERROR;
137                                 }
138                                 new_dn = talloc_asprintf(msg, "<GUID=%s>;%s",
139                                                          lower_guid_hex, 
140                                                          ldb_dn_get_linearized(msg->dn));
141                         }
142
143                         break;
144                 case 1:
145                         /* retrieve object_guid */
146                         guid = samdb_result_guid(msg, "objectGUID");
147                         object_guid = GUID_string(msg, &guid);
148                         
149                         /* retrieve object_sid */
150                         object_sid = NULL;
151                         sid = samdb_result_dom_sid(msg, msg, "objectSID");
152                         if (sid) {
153                                 object_sid = dom_sid_string(msg, sid);
154                                 if (!object_sid)
155                                         return LDB_ERR_OPERATIONS_ERROR;
156
157                         }
158                         
159                         /* Normal, sane format */
160                         if (object_sid) {
161                                 new_dn = talloc_asprintf(msg, "<GUID=%s>;<SID=%s>;%s",
162                                                          object_guid, object_sid,
163                                                          ldb_dn_get_linearized(msg->dn));
164                         } else {
165                                 new_dn = talloc_asprintf(msg, "<GUID=%s>;%s",
166                                                          object_guid,
167                                                          ldb_dn_get_linearized(msg->dn));
168                         }
169                         break;
170                 default:
171                         return LDB_ERR_OPERATIONS_ERROR;
172         }
173
174         if (!new_dn) {
175                 return LDB_ERR_OPERATIONS_ERROR;
176         }
177
178         if (remove_guid) {
179                 ldb_msg_remove_attr(msg, "objectGUID");
180         }
181
182         if (sid_blob && remove_sid) {
183                 ldb_msg_remove_attr(msg, "objectSID");
184         }
185
186         msg->dn = ldb_dn_new(msg, ldb, new_dn);
187         if (! ldb_dn_validate(msg->dn))
188                 return LDB_ERR_OPERATIONS_ERROR;
189
190         val = ldb_msg_find_ldb_val(msg, "distinguishedName");
191         if (val) {
192                 ldb_msg_remove_attr(msg, "distinguishedName");
193                 if (ldb_msg_add_steal_string(msg, "distinguishedName", new_dn))
194                         return LDB_ERR_OPERATIONS_ERROR;
195         }
196
197         return LDB_SUCCESS;
198 }
199
200 /* search */
201 struct extended_context {
202
203         struct ldb_module *module;
204         struct ldb_request *req;
205         struct ldb_control *control;
206         struct ldb_dn *basedn;
207         char *wellknown_object;
208         bool inject;
209         bool remove_guid;
210         bool remove_sid;
211         int extended_type;
212         const char * const *cast_attrs;
213 };
214
215 static int extended_callback(struct ldb_request *req, struct ldb_reply *ares)
216 {
217         struct extended_context *ac;
218         int ret;
219
220         ac = talloc_get_type(req->context, struct extended_context);
221
222         if (!ares) {
223                 return ldb_module_done(ac->req, NULL, NULL,
224                                         LDB_ERR_OPERATIONS_ERROR);
225         }
226         if (ares->error != LDB_SUCCESS) {
227                 return ldb_module_done(ac->req, ares->controls,
228                                         ares->response, ares->error);
229         }
230
231         switch (ares->type) {
232         case LDB_REPLY_ENTRY:
233                 if (ac->inject) {
234                         /* for each record returned post-process to add any derived
235                            attributes that have been asked for */
236                         ret = inject_extended_dn(ares->message, ac->module->ldb,
237                                                  ac->extended_type, ac->remove_guid,
238                                                  ac->remove_sid);
239                         if (ret != LDB_SUCCESS) {
240                                 return ldb_module_done(ac->req, NULL, NULL, ret);
241                         }
242                 }
243
244                 return ldb_module_send_entry(ac->req, ares->message);
245
246         case LDB_REPLY_REFERRAL:
247                 return ldb_module_send_referral(ac->req, ares->referral);
248
249         case LDB_REPLY_DONE:
250                 return ldb_module_done(ac->req, ares->controls,
251                                         ares->response, LDB_SUCCESS);
252
253         }
254         return LDB_SUCCESS;
255 }
256
257 static int extended_base_callback(struct ldb_request *req, struct ldb_reply *ares)
258 {
259         struct extended_context *ac;
260         struct ldb_request *down_req;
261         struct ldb_control **saved_controls;
262         struct ldb_message_element *el;
263         int ret;
264         size_t i;
265         size_t wkn_len = 0;
266         char *valstr = NULL;
267         const char *found = NULL;
268
269         ac = talloc_get_type(req->context, struct extended_context);
270
271         if (!ares) {
272                 return ldb_module_done(ac->req, NULL, NULL,
273                                         LDB_ERR_OPERATIONS_ERROR);
274         }
275         if (ares->error != LDB_SUCCESS) {
276                 return ldb_module_done(ac->req, ares->controls,
277                                         ares->response, ares->error);
278         }
279
280         switch (ares->type) {
281         case LDB_REPLY_ENTRY:
282                 if (!ac->wellknown_object) {
283                         ac->basedn = ares->message->dn;
284                         break;
285                 }
286
287                 wkn_len = strlen(ac->wellknown_object);
288
289                 el = ldb_msg_find_element(ares->message, "wellKnownObjects");
290                 if (!el) {
291                         ac->basedn = NULL;
292                         break;
293                 }
294
295                 for (i=0; i < el->num_values; i++) {
296                         valstr = talloc_strndup(ac,
297                                                 (const char *)el->values[i].data,
298                                                 el->values[i].length);
299                         if (!valstr) {
300                                 ldb_oom(ac->module->ldb);
301                                 return ldb_module_done(ac->req, NULL, NULL,
302                                                        LDB_ERR_OPERATIONS_ERROR);
303                         }
304
305                         if (strncasecmp(valstr, ac->wellknown_object, wkn_len) != 0) {
306                                 talloc_free(valstr);
307                                 continue;
308                         }
309
310                         found = &valstr[wkn_len];
311                         break;
312                 }
313
314                 if (!found) {
315                         break;
316                 }
317
318                 ac->basedn = ldb_dn_new(ac, ac->module->ldb, found);
319                 talloc_free(valstr);
320                 if (!ac->basedn) {
321                         ldb_oom(ac->module->ldb);
322                         return ldb_module_done(ac->req, NULL, NULL,
323                                                LDB_ERR_OPERATIONS_ERROR);
324                 }
325
326                 break;
327
328         case LDB_REPLY_REFERRAL:
329                 break;
330
331         case LDB_REPLY_DONE:
332
333                 if (!ac->basedn) {
334                         const char *str = talloc_asprintf(req, "Base-DN '%s' not found",
335                                                           ldb_dn_get_linearized(ac->req->op.search.base));
336                         ldb_set_errstring(ac->module->ldb, str);
337                         return ldb_module_done(ac->req, NULL, NULL,
338                                                LDB_ERR_NO_SUCH_OBJECT);
339                 }
340
341                 ret = ldb_build_search_req_ex(&down_req,
342                                                 ac->module->ldb, ac,
343                                                 ac->basedn,
344                                                 ac->req->op.search.scope,
345                                                 ac->req->op.search.tree,
346                                                 ac->cast_attrs,
347                                                 ac->req->controls,
348                                                 ac, extended_callback,
349                                                 ac->req);
350                 if (ret != LDB_SUCCESS) {
351                         return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
352                 }
353
354                 if (ac->control) {
355                         /* save it locally and remove it from the list */
356                         /* we do not need to replace them later as we
357                          * are keeping the original req intact */
358                         if (!save_controls(ac->control, down_req, &saved_controls)) {
359                                 return ldb_module_done(ac->req, NULL, NULL,
360                                                         LDB_ERR_OPERATIONS_ERROR);
361                         }
362                 }
363
364                 /* perform the search */
365                 return ldb_next_request(ac->module, down_req);
366         }
367         return LDB_SUCCESS;
368 }
369
370 static int extended_search(struct ldb_module *module, struct ldb_request *req)
371 {
372         struct ldb_control *control;
373         struct ldb_extended_dn_control *extended_ctrl = NULL;
374         struct ldb_control **saved_controls;
375         struct extended_context *ac;
376         struct ldb_request *down_req;
377         char **new_attrs;
378         int ret;
379         struct ldb_dn *base_dn = NULL;
380         enum ldb_scope base_dn_scope = LDB_SCOPE_BASE;
381         const char *base_dn_filter = NULL;
382         const char * const *base_dn_attrs = NULL;
383         char *wellknown_object = NULL;
384         static const char *dnattr[] = {
385                 "distinguishedName",
386                 NULL
387         };
388         static const char *wkattr[] = {
389                 "wellKnownObjects",
390                 NULL
391         };
392
393         if (ldb_dn_is_special(req->op.search.base)) {
394                 char *dn;
395
396                 dn = ldb_dn_alloc_linearized(req, req->op.search.base);
397                 if (!dn) {
398                         ldb_oom(module->ldb);
399                         return LDB_ERR_OPERATIONS_ERROR;
400                 }
401
402                 if (strncasecmp(dn, "<SID=", 5) == 0) {
403                         char *str;
404                         char *valstr;
405                         char *p;
406
407                         p = strchr(dn, '=');
408                         if (!p) {
409                                 return LDB_ERR_INVALID_DN_SYNTAX;
410                         }
411
412                         p[0] = '\0';
413                         p++;
414
415                         str = p;
416
417                         p = strchr(str, '>');
418                         if (!p) {
419                                 return LDB_ERR_INVALID_DN_SYNTAX;
420                         }
421                         p[0] = '\0';
422
423                         if (strncasecmp(str, "S-", 2) == 0) {
424                                 valstr = str;
425                         } else {
426                                 DATA_BLOB binary;
427                                 binary = strhex_to_data_blob(str);
428                                 if (!binary.data) {
429                                         ldb_oom(module->ldb);
430                                         return LDB_ERR_OPERATIONS_ERROR;
431                                 }
432                                 valstr = ldb_binary_encode(req, binary);
433                                 data_blob_free(&binary);
434                                 if (!valstr) {
435                                         ldb_oom(module->ldb);
436                                         return LDB_ERR_OPERATIONS_ERROR;
437                                 }
438                         }
439
440                         /* TODO: do a search over all partitions */
441                         base_dn = ldb_get_default_basedn(module->ldb);
442                         base_dn_filter = talloc_asprintf(req, "(objectSid=%s)", valstr);
443                         if (!base_dn_filter) {
444                                 ldb_oom(module->ldb);
445                                 return LDB_ERR_OPERATIONS_ERROR;
446                         }
447                         base_dn_scope = LDB_SCOPE_SUBTREE;
448                         base_dn_attrs = dnattr;
449                 } else if (strncasecmp(dn, "<GUID=", 6) == 0) {
450                         char *str;
451                         char *valstr;
452                         char *p;
453
454                         p = strchr(dn, '=');
455                         if (!p) {
456                                 return LDB_ERR_INVALID_DN_SYNTAX;
457                         }
458
459                         p[0] = '\0';
460                         p++;
461
462                         str = p;
463
464                         p = strchr(str, '>');
465                         if (!p) {
466                                 return LDB_ERR_INVALID_DN_SYNTAX;
467                         }
468                         p[0] = '\0';
469
470                         if (strchr(str, '-')) {
471                                 valstr = str;
472                         } else {
473                                 DATA_BLOB binary;
474                                 binary = strhex_to_data_blob(str);
475                                 if (!binary.data) {
476                                         ldb_oom(module->ldb);
477                                         return LDB_ERR_OPERATIONS_ERROR;
478                                 }
479                                 valstr = ldb_binary_encode(req, binary);
480                                 data_blob_free(&binary);
481                                 if (!valstr) {
482                                         ldb_oom(module->ldb);
483                                         return LDB_ERR_OPERATIONS_ERROR;
484                                 }
485                         }
486
487                         /* TODO: do a search over all partitions */
488                         base_dn = ldb_get_default_basedn(module->ldb);
489                         base_dn_filter = talloc_asprintf(req, "(objectGUID=%s)", valstr);
490                         if (!base_dn_filter) {
491                                 ldb_oom(module->ldb);
492                                 return LDB_ERR_OPERATIONS_ERROR;
493                         }
494                         base_dn_scope = LDB_SCOPE_SUBTREE;
495                         base_dn_attrs = dnattr;
496                 } else if (strncasecmp(dn, "<WKGUID=", 8) == 0) {
497                         char *tail_str;
498                         char *p;
499
500                         p = strchr(dn, ',');
501                         if (!p) {
502                                 return LDB_ERR_INVALID_DN_SYNTAX;
503                         }
504
505                         p[0] = '\0';
506                         p++;
507
508                         wellknown_object = talloc_asprintf(req, "B:32:%s:", &dn[8]);
509                         if (!wellknown_object) {
510                                 ldb_oom(module->ldb);
511                                 return LDB_ERR_OPERATIONS_ERROR;
512                         }
513
514                         tail_str = p;
515                         p = strchr(tail_str, '>');
516                         if (!p) {
517                                 return LDB_ERR_INVALID_DN_SYNTAX;
518                         }
519                         p[0] = '\0';
520
521                         base_dn = ldb_dn_new(req, module->ldb, tail_str);
522                         if (!base_dn) {
523                                 ldb_oom(module->ldb);
524                                 return LDB_ERR_OPERATIONS_ERROR;
525                         }
526                         base_dn_filter = talloc_strdup(req, "(objectClass=*)");
527                         if (!base_dn_filter) {
528                                 ldb_oom(module->ldb);
529                                 return LDB_ERR_OPERATIONS_ERROR;
530                         }
531                         base_dn_scope = LDB_SCOPE_BASE;
532                         base_dn_attrs = wkattr;
533                 }
534                 talloc_free(dn);
535         }
536
537         /* check if there's an extended dn control */
538         control = ldb_request_get_control(req, LDB_CONTROL_EXTENDED_DN_OID);
539         if (control == NULL && base_dn_filter == NULL) {
540                 /* not found go on */
541                 return ldb_next_request(module, req);
542         }
543
544         if (control && control->data) {
545                 extended_ctrl = talloc_get_type(control->data, struct ldb_extended_dn_control);
546                 if (!extended_ctrl) {
547                         return LDB_ERR_PROTOCOL_ERROR;
548                 }
549         }
550
551         ac = talloc_zero(req, struct extended_context);
552         if (ac == NULL) {
553                 ldb_oom(module->ldb);
554                 return LDB_ERR_OPERATIONS_ERROR;
555         }
556
557         ac->module = module;
558         ac->req = req;
559         ac->control = control;
560         ac->basedn = NULL;
561         ac->wellknown_object = wellknown_object;
562         ac->inject = false;
563         ac->remove_guid = false;
564         ac->remove_sid = false;
565
566         if (control) {
567                 ac->inject = true;
568                 if (extended_ctrl) {
569                         ac->extended_type = extended_ctrl->type;
570                 } else {
571                         ac->extended_type = 0;
572                 }
573
574                 /* check if attrs only is specified, in that case check wether we need to modify them */
575                 if (req->op.search.attrs) {
576                         if (! is_attr_in_list(req->op.search.attrs, "objectGUID")) {
577                                 ac->remove_guid = true;
578                         }
579                         if (! is_attr_in_list(req->op.search.attrs, "objectSID")) {
580                                 ac->remove_sid = true;
581                         }
582                         if (ac->remove_guid || ac->remove_sid) {
583                                 new_attrs = copy_attrs(ac, req->op.search.attrs);
584                                 if (new_attrs == NULL) {
585                                         ldb_oom(module->ldb);
586                                         return LDB_ERR_OPERATIONS_ERROR;
587                                 }
588
589                                 if (ac->remove_guid) {
590                                         if (!add_attrs(ac, &new_attrs, "objectGUID"))
591                                                 return LDB_ERR_OPERATIONS_ERROR;
592                                 }
593                                 if (ac->remove_sid) {
594                                         if (!add_attrs(ac, &new_attrs, "objectSID"))
595                                                 return LDB_ERR_OPERATIONS_ERROR;
596                                 }
597                                 ac->cast_attrs = (const char * const *)new_attrs;
598                         } else {
599                                 ac->cast_attrs = req->op.search.attrs;
600                         }
601                 }
602         }
603
604         if (base_dn) {
605                 ret = ldb_build_search_req(&down_req,
606                                            module->ldb, ac,
607                                            base_dn,
608                                            base_dn_scope,
609                                            base_dn_filter,
610                                            base_dn_attrs,
611                                            NULL,
612                                            ac, extended_base_callback,
613                                            req);
614                 if (ret != LDB_SUCCESS) {
615                         return LDB_ERR_OPERATIONS_ERROR;
616                 }
617
618                 /* perform the search */
619                 return ldb_next_request(module, down_req);
620         }
621
622         ret = ldb_build_search_req_ex(&down_req,
623                                         module->ldb, ac,
624                                         req->op.search.base,
625                                         req->op.search.scope,
626                                         req->op.search.tree,
627                                         ac->cast_attrs,
628                                         req->controls,
629                                         ac, extended_callback,
630                                         req);
631         if (ret != LDB_SUCCESS) {
632                 return LDB_ERR_OPERATIONS_ERROR;
633         }
634
635         if (ac->control) {
636                 /* save it locally and remove it from the list */
637                 /* we do not need to replace them later as we
638                  * are keeping the original req intact */
639                 if (!save_controls(control, down_req, &saved_controls)) {
640                         return LDB_ERR_OPERATIONS_ERROR;
641                 }
642         }
643
644         /* perform the search */
645         return ldb_next_request(module, down_req);
646 }
647
648 static int extended_init(struct ldb_module *module)
649 {
650         int ret;
651
652         ret = ldb_mod_register_control(module, LDB_CONTROL_EXTENDED_DN_OID);
653         if (ret != LDB_SUCCESS) {
654                 ldb_debug(module->ldb, LDB_DEBUG_ERROR,
655                         "extended_dn: Unable to register control with rootdse!\n");
656                 return LDB_ERR_OPERATIONS_ERROR;
657         }
658
659         return ldb_next_init(module);
660 }
661
662 _PUBLIC_ const struct ldb_module_ops ldb_extended_dn_module_ops = {
663         .name              = "extended_dn",
664         .search            = extended_search,
665         .init_context      = extended_init
666 };