s4:rootdse LDB module - better that the "edn" control handling is done last
[samba.git] / source4 / dsdb / samdb / ldb_modules / rootdse.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    rootDSE ldb module
5
6    Copyright (C) Andrew Tridgell 2005
7    Copyright (C) Simo Sorce 2005-2008
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "lib/ldb/include/ldb.h"
25 #include "lib/ldb/include/ldb_module.h"
26 #include "system/time.h"
27 #include "dsdb/samdb/samdb.h"
28 #include "version.h"
29 #include "dsdb/samdb/ldb_modules/util.h"
30 #include "libcli/security/security.h"
31 #include "librpc/ndr/libndr.h"
32 #include "auth/auth.h"
33 #include "param/param.h"
34 #include "lib/messaging/irpc.h"
35 #include "librpc/gen_ndr/ndr_irpc_c.h"
36
37 struct private_data {
38         unsigned int num_controls;
39         char **controls;
40         unsigned int num_partitions;
41         struct ldb_dn **partitions;
42 };
43
44 /*
45   return 1 if a specific attribute has been requested
46 */
47 static int do_attribute(const char * const *attrs, const char *name)
48 {
49         return attrs == NULL ||
50                 ldb_attr_in_list(attrs, name) ||
51                 ldb_attr_in_list(attrs, "*");
52 }
53
54 static int do_attribute_explicit(const char * const *attrs, const char *name)
55 {
56         return attrs != NULL && ldb_attr_in_list(attrs, name);
57 }
58
59
60 /*
61   expand a DN attribute to include extended DN information if requested
62  */
63 static int expand_dn_in_message(struct ldb_module *module, struct ldb_message *msg,
64                                 const char *attrname, struct ldb_control *edn_control,
65                                 struct ldb_request *req)
66 {
67         struct ldb_dn *dn, *dn2;
68         struct ldb_val *v;
69         int ret;
70         struct ldb_request *req2;
71         char *dn_string;
72         const char *no_attrs[] = { NULL };
73         struct ldb_result *res;
74         struct ldb_extended_dn_control *edn;
75         TALLOC_CTX *tmp_ctx = talloc_new(req);
76         struct ldb_context *ldb;
77         int edn_type = 0;
78
79         ldb = ldb_module_get_ctx(module);
80
81         edn = talloc_get_type(edn_control->data, struct ldb_extended_dn_control);
82         if (edn) {
83                 edn_type = edn->type;
84         }
85
86         v = discard_const_p(struct ldb_val, ldb_msg_find_ldb_val(msg, attrname));
87         if (v == NULL) {
88                 talloc_free(tmp_ctx);
89                 return LDB_SUCCESS;
90         }
91
92         dn_string = talloc_strndup(tmp_ctx, (const char *)v->data, v->length);
93         if (dn_string == NULL) {
94                 talloc_free(tmp_ctx);
95                 return ldb_operr(ldb);
96         }
97
98         res = talloc_zero(tmp_ctx, struct ldb_result);
99         if (res == NULL) {
100                 talloc_free(tmp_ctx);
101                 return ldb_operr(ldb);
102         }
103
104         dn = ldb_dn_new(tmp_ctx, ldb, dn_string);
105         if (!ldb_dn_validate(dn)) {
106                 talloc_free(tmp_ctx);
107                 return ldb_operr(ldb);
108         }
109
110         ret = ldb_build_search_req(&req2, ldb, tmp_ctx,
111                                    dn,
112                                    LDB_SCOPE_BASE,
113                                    NULL,
114                                    no_attrs,
115                                    NULL,
116                                    res, ldb_search_default_callback,
117                                    req);
118         if (ret != LDB_SUCCESS) {
119                 talloc_free(tmp_ctx);
120                 return ret;
121         }
122
123
124         ret = ldb_request_add_control(req2,
125                                       LDB_CONTROL_EXTENDED_DN_OID,
126                                       edn_control->critical, edn);
127         if (ret != LDB_SUCCESS) {
128                 talloc_free(tmp_ctx);
129                 return ret;
130         }
131
132         ret = ldb_next_request(module, req2);
133         if (ret == LDB_SUCCESS) {
134                 ret = ldb_wait(req2->handle, LDB_WAIT_ALL);
135         }
136         if (ret != LDB_SUCCESS) {
137                 talloc_free(tmp_ctx);
138                 return ret;
139         }
140
141         if (!res || res->count != 1) {
142                 talloc_free(tmp_ctx);
143                 return ldb_operr(ldb);
144         }
145
146         dn2 = res->msgs[0]->dn;
147
148         v->data = (uint8_t *)ldb_dn_get_extended_linearized(msg->elements, dn2, edn_type);
149         if (v->data == NULL) {
150                 talloc_free(tmp_ctx);
151                 return ldb_operr(ldb);
152         }
153         v->length = strlen((char *)v->data);
154
155
156         talloc_free(tmp_ctx);
157
158         return LDB_SUCCESS;
159 }
160
161
162 /*
163   add dynamically generated attributes to rootDSE result
164 */
165 static int rootdse_add_dynamic(struct ldb_module *module, struct ldb_message *msg,
166                                const char * const *attrs, struct ldb_request *req)
167 {
168         struct ldb_context *ldb;
169         struct private_data *priv = talloc_get_type(ldb_module_get_private(module), struct private_data);
170         char **server_sasl;
171         const struct dsdb_schema *schema;
172         int *val;
173         struct ldb_control *edn_control;
174         const char *dn_attrs[] = {
175                 "configurationNamingContext",
176                 "defaultNamingContext",
177                 "dsServiceName",
178                 "rootDomainNamingContext",
179                 "schemaNamingContext",
180                 "serverName",
181                 NULL
182         };
183
184         ldb = ldb_module_get_ctx(module);
185         schema = dsdb_get_schema(ldb, NULL);
186
187         msg->dn = ldb_dn_new(msg, ldb, NULL);
188
189         /* don't return the distinguishedName, cn and name attributes */
190         ldb_msg_remove_attr(msg, "distinguishedName");
191         ldb_msg_remove_attr(msg, "cn");
192         ldb_msg_remove_attr(msg, "name");
193
194         if (do_attribute(attrs, "currentTime")) {
195                 if (ldb_msg_add_steal_string(msg, "currentTime",
196                                              ldb_timestring(msg, time(NULL))) != LDB_SUCCESS) {
197                         goto failed;
198                 }
199         }
200
201         if (priv && do_attribute(attrs, "supportedControl")) {
202                 unsigned int i;
203                 for (i = 0; i < priv->num_controls; i++) {
204                         char *control = talloc_strdup(msg, priv->controls[i]);
205                         if (!control) {
206                                 goto failed;
207                         }
208                         if (ldb_msg_add_steal_string(msg, "supportedControl",
209                                                      control) != LDB_SUCCESS) {
210                                 goto failed;
211                         }
212                 }
213         }
214
215         if (priv && do_attribute(attrs, "namingContexts")) {
216                 unsigned int i;
217                 for (i = 0; i < priv->num_partitions; i++) {
218                         struct ldb_dn *dn = priv->partitions[i];
219                         if (ldb_msg_add_steal_string(msg, "namingContexts",
220                                                      ldb_dn_alloc_linearized(msg, dn)) != LDB_SUCCESS) {
221                                 goto failed;
222                         }
223                 }
224         }
225
226         server_sasl = talloc_get_type(ldb_get_opaque(ldb, "supportedSASLMechanisms"),
227                                        char *);
228         if (server_sasl && do_attribute(attrs, "supportedSASLMechanisms")) {
229                 unsigned int i;
230                 for (i = 0; server_sasl && server_sasl[i]; i++) {
231                         char *sasl_name = talloc_strdup(msg, server_sasl[i]);
232                         if (!sasl_name) {
233                                 goto failed;
234                         }
235                         if (ldb_msg_add_steal_string(msg, "supportedSASLMechanisms",
236                                                      sasl_name) != LDB_SUCCESS) {
237                                 goto failed;
238                         }
239                 }
240         }
241
242         if (do_attribute(attrs, "highestCommittedUSN")) {
243                 uint64_t seq_num;
244                 int ret = ldb_sequence_number(ldb, LDB_SEQ_HIGHEST_SEQ, &seq_num);
245                 if (ret == LDB_SUCCESS) {
246                         if (ldb_msg_add_fmt(msg, "highestCommittedUSN",
247                                             "%llu", (unsigned long long)seq_num) != LDB_SUCCESS) {
248                                 goto failed;
249                         }
250                 }
251         }
252
253         if (schema && do_attribute_explicit(attrs, "dsSchemaAttrCount")) {
254                 struct dsdb_attribute *cur;
255                 unsigned int n = 0;
256
257                 for (cur = schema->attributes; cur; cur = cur->next) {
258                         n++;
259                 }
260
261                 if (ldb_msg_add_fmt(msg, "dsSchemaAttrCount",
262                                     "%u", n) != LDB_SUCCESS) {
263                         goto failed;
264                 }
265         }
266
267         if (schema && do_attribute_explicit(attrs, "dsSchemaClassCount")) {
268                 struct dsdb_class *cur;
269                 unsigned int n = 0;
270
271                 for (cur = schema->classes; cur; cur = cur->next) {
272                         n++;
273                 }
274
275                 if (ldb_msg_add_fmt(msg, "dsSchemaClassCount",
276                                     "%u", n) != LDB_SUCCESS) {
277                         goto failed;
278                 }
279         }
280
281         if (schema && do_attribute_explicit(attrs, "dsSchemaPrefixCount")) {
282                 if (ldb_msg_add_fmt(msg, "dsSchemaPrefixCount",
283                                     "%u", schema->prefixmap->length) != LDB_SUCCESS) {
284                         goto failed;
285                 }
286         }
287
288         if (do_attribute_explicit(attrs, "validFSMOs")) {
289                 const struct dsdb_naming_fsmo *naming_fsmo;
290                 const struct dsdb_pdc_fsmo *pdc_fsmo;
291                 const char *dn_str;
292
293                 if (schema && schema->fsmo.we_are_master) {
294                         dn_str = ldb_dn_get_linearized(ldb_get_schema_basedn(ldb));
295                         if (dn_str && dn_str[0]) {
296                                 if (ldb_msg_add_fmt(msg, "validFSMOs", "%s", dn_str) != LDB_SUCCESS) {
297                                         goto failed;
298                                 }
299                         }
300                 }
301
302                 naming_fsmo = talloc_get_type(ldb_get_opaque(ldb, "dsdb_naming_fsmo"),
303                                               struct dsdb_naming_fsmo);
304                 if (naming_fsmo && naming_fsmo->we_are_master) {
305                         dn_str = ldb_dn_get_linearized(samdb_partitions_dn(ldb, msg));
306                         if (dn_str && dn_str[0]) {
307                                 if (ldb_msg_add_fmt(msg, "validFSMOs", "%s", dn_str) != LDB_SUCCESS) {
308                                         goto failed;
309                                 }
310                         }
311                 }
312
313                 pdc_fsmo = talloc_get_type(ldb_get_opaque(ldb, "dsdb_pdc_fsmo"),
314                                            struct dsdb_pdc_fsmo);
315                 if (pdc_fsmo && pdc_fsmo->we_are_master) {
316                         dn_str = ldb_dn_get_linearized(ldb_get_default_basedn(ldb));
317                         if (dn_str && dn_str[0]) {
318                                 if (ldb_msg_add_fmt(msg, "validFSMOs", "%s", dn_str) != LDB_SUCCESS) {
319                                         goto failed;
320                                 }
321                         }
322                 }
323         }
324
325         if (do_attribute_explicit(attrs, "vendorVersion")) {
326                 if (ldb_msg_add_fmt(msg, "vendorVersion",
327                                     "%s", SAMBA_VERSION_STRING) != LDB_SUCCESS) {
328                         goto failed;
329                 }
330         }
331
332         if (priv && do_attribute(attrs, "domainFunctionality")) {
333                 if (ldb_msg_add_fmt(msg, "domainFunctionality",
334                                     "%d", dsdb_functional_level(ldb)) != LDB_SUCCESS) {
335                         goto failed;
336                 }
337         }
338
339         if (priv && do_attribute(attrs, "forestFunctionality")
340             && (val = talloc_get_type(ldb_get_opaque(ldb, "forestFunctionality"), int))) {
341                 if (ldb_msg_add_fmt(msg, "forestFunctionality",
342                                     "%d", *val) != LDB_SUCCESS) {
343                         goto failed;
344                 }
345         }
346
347         if (priv && do_attribute(attrs, "domainControllerFunctionality")
348             && (val = talloc_get_type(ldb_get_opaque(ldb, "domainControllerFunctionality"), int))) {
349                 if (ldb_msg_add_fmt(msg, "domainControllerFunctionality",
350                                     "%d", *val) != LDB_SUCCESS) {
351                         goto failed;
352                 }
353         }
354
355         if (do_attribute(attrs, "isGlobalCatalogReady")) {
356                 /* MS-ADTS 3.1.1.3.2.10
357                    Note, we should only return true here is we have
358                    completed at least one synchronisation. As both
359                    provision and vampire do a full sync, this means we
360                    can return true is the gc bit is set in the NTDSDSA
361                    options */
362                 if (ldb_msg_add_fmt(msg, "isGlobalCatalogReady",
363                                     "%s", samdb_is_gc(ldb)?"TRUE":"FALSE") != LDB_SUCCESS) {
364                         goto failed;
365                 }
366         }
367
368         if (do_attribute_explicit(attrs, "tokenGroups")) {
369                 unsigned int i;
370                 /* Obtain the user's session_info */
371                 struct auth_session_info *session_info
372                         = (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
373                 if (session_info && session_info->security_token) {
374                         /* The list of groups this user is in */
375                         for (i = 0; i < session_info->security_token->num_sids; i++) {
376                                 if (samdb_msg_add_dom_sid(ldb, msg, msg,
377                                                           "tokenGroups",
378                                                           &session_info->security_token->sids[i]) != LDB_SUCCESS) {
379                                         goto failed;
380                                 }
381                         }
382                 }
383         }
384
385         /* TODO: lots more dynamic attributes should be added here */
386
387         edn_control = ldb_request_get_control(req, LDB_CONTROL_EXTENDED_DN_OID);
388
389         /* if the client sent us the EXTENDED_DN control then we need
390            to expand the DNs to have GUID and SID. W2K8 join relies on
391            this */
392         if (edn_control) {
393                 unsigned int i;
394                 int ret;
395                 for (i=0; dn_attrs[i]; i++) {
396                         if (!do_attribute(attrs, dn_attrs[i])) continue;
397                         ret = expand_dn_in_message(module, msg, dn_attrs[i],
398                                                    edn_control, req);
399                         if (ret != LDB_SUCCESS) {
400                                 DEBUG(0,(__location__ ": Failed to expand DN in rootDSE for %s\n",
401                                          dn_attrs[i]));
402                                 goto failed;
403                         }
404                 }
405         }
406
407         return LDB_SUCCESS;
408
409 failed:
410         return ldb_operr(ldb);
411 }
412
413 /*
414   handle search requests
415 */
416
417 struct rootdse_context {
418         struct ldb_module *module;
419         struct ldb_request *req;
420 };
421
422 static struct rootdse_context *rootdse_init_context(struct ldb_module *module,
423                                                     struct ldb_request *req)
424 {
425         struct ldb_context *ldb;
426         struct rootdse_context *ac;
427
428         ldb = ldb_module_get_ctx(module);
429
430         ac = talloc_zero(req, struct rootdse_context);
431         if (ac == NULL) {
432                 ldb_set_errstring(ldb, "Out of Memory");
433                 return NULL;
434         }
435
436         ac->module = module;
437         ac->req = req;
438
439         return ac;
440 }
441
442 static int rootdse_callback(struct ldb_request *req, struct ldb_reply *ares)
443 {
444         struct rootdse_context *ac;
445         int ret;
446
447         ac = talloc_get_type(req->context, struct rootdse_context);
448
449         if (!ares) {
450                 return ldb_module_done(ac->req, NULL, NULL,
451                                         LDB_ERR_OPERATIONS_ERROR);
452         }
453         if (ares->error != LDB_SUCCESS) {
454                 return ldb_module_done(ac->req, ares->controls,
455                                         ares->response, ares->error);
456         }
457
458         switch (ares->type) {
459         case LDB_REPLY_ENTRY:
460                 /*
461                  * if the client explicit asks for the 'netlogon' attribute
462                  * the reply_entry needs to be skipped
463                  */
464                 if (ac->req->op.search.attrs &&
465                     ldb_attr_in_list(ac->req->op.search.attrs, "netlogon")) {
466                         talloc_free(ares);
467                         return LDB_SUCCESS;
468                 }
469
470                 /* for each record returned post-process to add any dynamic
471                    attributes that have been asked for */
472                 ret = rootdse_add_dynamic(ac->module, ares->message,
473                                           ac->req->op.search.attrs, ac->req);
474                 if (ret != LDB_SUCCESS) {
475                         talloc_free(ares);
476                         return ldb_module_done(ac->req, NULL, NULL, ret);
477                 }
478
479                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
480
481         case LDB_REPLY_REFERRAL:
482                 /* should we allow the backend to return referrals in this case
483                  * ?? */
484                 break;
485
486         case LDB_REPLY_DONE:
487                 return ldb_module_done(ac->req, ares->controls,
488                                         ares->response, ares->error);
489         }
490
491         talloc_free(ares);
492         return LDB_SUCCESS;
493 }
494
495 /*
496   mark our registered controls as non-critical in the request
497
498   This is needed as clients may mark controls as critical even if they
499   are not needed at all in a request. For example, the centrify client
500   sets the SD_FLAGS control as critical on ldap modify requests which
501   are setting the dNSHostName attribute on the machine account. That
502   request doesn't need SD_FLAGS at all, but centrify adds it on all
503   ldap requests.
504  */
505 static void rootdse_mark_noncritical(struct ldb_module *module, struct ldb_control **controls)
506 {
507         unsigned int i, j;
508         struct private_data *priv = talloc_get_type(ldb_module_get_private(module), struct private_data);
509
510         if (!controls) return;
511
512         for (i=0; controls[i]; i++) {
513                 if (controls[i]->critical == 0) {
514                         continue;
515                 }
516                 for (j=0; j<priv->num_controls; j++) {
517                         if (strcasecmp(priv->controls[j], controls[i]->oid) == 0) {
518                                 controls[i]->critical = 0;
519                         }
520                 }
521         }
522 }
523
524 static int rootdse_search(struct ldb_module *module, struct ldb_request *req)
525 {
526         struct ldb_context *ldb;
527         struct rootdse_context *ac;
528         struct ldb_request *down_req;
529         int ret;
530
531         rootdse_mark_noncritical(module, req->controls);
532
533         ldb = ldb_module_get_ctx(module);
534
535         /* see if its for the rootDSE - only a base search on the "" DN qualifies */
536         if (!(req->op.search.scope == LDB_SCOPE_BASE && ldb_dn_is_null(req->op.search.base))) {
537                 /* Otherwise, pass down to the rest of the stack */
538                 return ldb_next_request(module, req);
539         }
540
541         ac = rootdse_init_context(module, req);
542         if (ac == NULL) {
543                 return ldb_operr(ldb);
544         }
545
546         /* in our db we store the rootDSE with a DN of @ROOTDSE */
547         ret = ldb_build_search_req(&down_req, ldb, ac,
548                                         ldb_dn_new(ac, ldb, "@ROOTDSE"),
549                                         LDB_SCOPE_BASE,
550                                         NULL,
551                                         req->op.search.attrs,
552                                         NULL,/* for now skip the controls from the client */
553                                         ac, rootdse_callback,
554                                         req);
555         if (ret != LDB_SUCCESS) {
556                 return ret;
557         }
558
559         return ldb_next_request(module, down_req);
560 }
561
562 static int rootdse_register_control(struct ldb_module *module, struct ldb_request *req)
563 {
564         struct private_data *priv = talloc_get_type(ldb_module_get_private(module), struct private_data);
565         char **list;
566
567         list = talloc_realloc(priv, priv->controls, char *, priv->num_controls + 1);
568         if (!list) {
569                 return ldb_oom(ldb_module_get_ctx(module));
570         }
571
572         list[priv->num_controls] = talloc_strdup(list, req->op.reg_control.oid);
573         if (!list[priv->num_controls]) {
574                 return ldb_oom(ldb_module_get_ctx(module));
575         }
576
577         priv->num_controls += 1;
578         priv->controls = list;
579
580         return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
581 }
582
583 static int rootdse_register_partition(struct ldb_module *module, struct ldb_request *req)
584 {
585         struct private_data *priv = talloc_get_type(ldb_module_get_private(module), struct private_data);
586         struct ldb_dn **list;
587
588         list = talloc_realloc(priv, priv->partitions, struct ldb_dn *, priv->num_partitions + 1);
589         if (!list) {
590                 return ldb_oom(ldb_module_get_ctx(module));
591         }
592
593         list[priv->num_partitions] = ldb_dn_copy(list, req->op.reg_partition.dn);
594         if (!list[priv->num_partitions]) {
595                 return ldb_operr(ldb_module_get_ctx(module));
596         }
597
598         priv->num_partitions += 1;
599         priv->partitions = list;
600
601         return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
602 }
603
604
605 static int rootdse_request(struct ldb_module *module, struct ldb_request *req)
606 {
607         switch (req->operation) {
608
609         case LDB_REQ_REGISTER_CONTROL:
610                 return rootdse_register_control(module, req);
611         case LDB_REQ_REGISTER_PARTITION:
612                 return rootdse_register_partition(module, req);
613
614         default:
615                 break;
616         }
617         return ldb_next_request(module, req);
618 }
619
620 static int rootdse_init(struct ldb_module *module)
621 {
622         int ret;
623         struct ldb_context *ldb;
624         struct ldb_result *res;
625         struct private_data *data;
626         const char *attrs[] = { "msDS-Behavior-Version", NULL };
627         const char *ds_attrs[] = { "dsServiceName", NULL };
628         TALLOC_CTX *mem_ctx;
629
630         ldb = ldb_module_get_ctx(module);
631
632         data = talloc_zero(module, struct private_data);
633         if (data == NULL) {
634                 return ldb_oom(ldb);
635         }
636
637         data->num_controls = 0;
638         data->controls = NULL;
639         data->num_partitions = 0;
640         data->partitions = NULL;
641         ldb_module_set_private(module, data);
642
643         ldb_set_default_dns(ldb);
644
645         ret = ldb_next_init(module);
646
647         if (ret != LDB_SUCCESS) {
648                 return ret;
649         }
650
651         mem_ctx = talloc_new(data);
652         if (!mem_ctx) {
653                 return ldb_oom(ldb);
654         }
655
656         /* Now that the partitions are set up, do a search for:
657            - domainControllerFunctionality
658            - domainFunctionality
659            - forestFunctionality
660
661            Then stuff these values into an opaque
662         */
663         ret = ldb_search(ldb, mem_ctx, &res,
664                          ldb_get_default_basedn(ldb),
665                          LDB_SCOPE_BASE, attrs, NULL);
666         if (ret == LDB_SUCCESS && res->count == 1) {
667                 int domain_behaviour_version
668                         = ldb_msg_find_attr_as_int(res->msgs[0],
669                                                    "msDS-Behavior-Version", -1);
670                 if (domain_behaviour_version != -1) {
671                         int *val = talloc(ldb, int);
672                         if (!val) {
673                                 talloc_free(mem_ctx);
674                                 return ldb_oom(ldb);
675                         }
676                         *val = domain_behaviour_version;
677                         ret = ldb_set_opaque(ldb, "domainFunctionality", val);
678                         if (ret != LDB_SUCCESS) {
679                                 talloc_free(mem_ctx);
680                                 return ret;
681                         }
682                 }
683         }
684
685         ret = ldb_search(ldb, mem_ctx, &res,
686                          samdb_partitions_dn(ldb, mem_ctx),
687                          LDB_SCOPE_BASE, attrs, NULL);
688         if (ret == LDB_SUCCESS && res->count == 1) {
689                 int forest_behaviour_version
690                         = ldb_msg_find_attr_as_int(res->msgs[0],
691                                                    "msDS-Behavior-Version", -1);
692                 if (forest_behaviour_version != -1) {
693                         int *val = talloc(ldb, int);
694                         if (!val) {
695                                 talloc_free(mem_ctx);
696                                 return ldb_oom(ldb);
697                         }
698                         *val = forest_behaviour_version;
699                         ret = ldb_set_opaque(ldb, "forestFunctionality", val);
700                         if (ret != LDB_SUCCESS) {
701                                 talloc_free(mem_ctx);
702                                 return ret;
703                         }
704                 }
705         }
706
707         ret = ldb_search(ldb, mem_ctx, &res,
708                          ldb_dn_new(mem_ctx, ldb, ""),
709                          LDB_SCOPE_BASE, ds_attrs, NULL);
710         if (ret == LDB_SUCCESS && res->count == 1) {
711                 struct ldb_dn *ds_dn
712                         = ldb_msg_find_attr_as_dn(ldb, mem_ctx, res->msgs[0],
713                                                   "dsServiceName");
714                 if (ds_dn) {
715                         ret = ldb_search(ldb, mem_ctx, &res, ds_dn,
716                                          LDB_SCOPE_BASE, attrs, NULL);
717                         if (ret == LDB_SUCCESS && res->count == 1) {
718                                 int domain_controller_behaviour_version
719                                         = ldb_msg_find_attr_as_int(res->msgs[0],
720                                                                    "msDS-Behavior-Version", -1);
721                                 if (domain_controller_behaviour_version != -1) {
722                                         int *val = talloc(ldb, int);
723                                         if (!val) {
724                                                 talloc_free(mem_ctx);
725                                                 return ldb_oom(ldb);
726                                         }
727                                         *val = domain_controller_behaviour_version;
728                                         ret = ldb_set_opaque(ldb,
729                                                              "domainControllerFunctionality", val);
730                                         if (ret != LDB_SUCCESS) {
731                                                 talloc_free(mem_ctx);
732                                                 return ret;
733                                         }
734                                 }
735                         }
736                 }
737         }
738
739         talloc_free(mem_ctx);
740
741         return LDB_SUCCESS;
742 }
743
744 /*
745  * This function gets the string SCOPE_DN:OPTIONAL_FEATURE_GUID and parse it
746  * to a DN and a GUID object
747  */
748 static int get_optional_feature_dn_guid(struct ldb_request *req, struct ldb_context *ldb,
749                                                 TALLOC_CTX *mem_ctx,
750                                                 struct ldb_dn **op_feature_scope_dn,
751                                                 struct GUID *op_feature_guid)
752 {
753         const struct ldb_message *msg = req->op.mod.message;
754         const char *ldb_val_str;
755         char *dn, *guid;
756         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
757         NTSTATUS status;
758
759         ldb_val_str = ldb_msg_find_attr_as_string(msg, "enableOptionalFeature", NULL);
760         if (!ldb_val_str) {
761                 ldb_set_errstring(ldb,
762                                   "rootdse: unable to find 'enableOptionalFeature'!");
763                 return LDB_ERR_UNWILLING_TO_PERFORM;
764         }
765
766         guid = strchr(ldb_val_str, ':');
767         if (!guid) {
768                 ldb_set_errstring(ldb,
769                                   "rootdse: unable to find GUID in 'enableOptionalFeature'!");
770                 return LDB_ERR_UNWILLING_TO_PERFORM;
771         }
772         status = GUID_from_string(guid+1, op_feature_guid);
773         if (!NT_STATUS_IS_OK(status)) {
774                 ldb_set_errstring(ldb,
775                                   "rootdse: bad GUID in 'enableOptionalFeature'!");
776                 return LDB_ERR_UNWILLING_TO_PERFORM;
777         }
778
779         dn = talloc_strndup(tmp_ctx, ldb_val_str, guid-ldb_val_str);
780         if (!dn) {
781                 ldb_set_errstring(ldb,
782                                   "rootdse: bad DN in 'enableOptionalFeature'!");
783                 return LDB_ERR_UNWILLING_TO_PERFORM;
784         }
785
786         *op_feature_scope_dn = ldb_dn_new(mem_ctx, ldb, dn);
787
788         talloc_free(tmp_ctx);
789         return LDB_SUCCESS;
790 }
791
792 /*
793  * This function gets the OPTIONAL_FEATURE_GUID and looks for the optional feature
794  * ldb_message object.
795  */
796 static int dsdb_find_optional_feature(struct ldb_module *module, struct ldb_context *ldb,
797                                 TALLOC_CTX *mem_ctx, struct GUID op_feature_guid, struct ldb_message **msg)
798 {
799         struct ldb_result *res;
800         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
801         int ret;
802
803         ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
804                                 NULL,
805                                 DSDB_FLAG_NEXT_MODULE |
806                                 DSDB_SEARCH_SEARCH_ALL_PARTITIONS,
807                                  "(&(objectClass=msDS-OptionalFeature)"
808                                  "(msDS-OptionalFeatureGUID=%s))",GUID_string(tmp_ctx, &op_feature_guid));
809
810         if (ret != LDB_SUCCESS) {
811                 talloc_free(tmp_ctx);
812                 return ret;
813         }
814         if (res->count == 0) {
815                 talloc_free(tmp_ctx);
816                 return LDB_ERR_NO_SUCH_OBJECT;
817         }
818         if (res->count != 1) {
819                 ldb_asprintf_errstring(ldb,
820                                        "More than one object found matching optional feature GUID %s\n",
821                                        GUID_string(tmp_ctx, &op_feature_guid));
822                 talloc_free(tmp_ctx);
823                 return LDB_ERR_OPERATIONS_ERROR;
824         }
825
826         *msg = talloc_steal(mem_ctx, res->msgs[0]);
827
828         talloc_free(tmp_ctx);
829         return LDB_SUCCESS;
830 }
831
832 static int rootdse_enable_recycle_bin(struct ldb_module *module,struct ldb_context *ldb,
833                         TALLOC_CTX *mem_ctx, struct ldb_dn *op_feature_scope_dn,
834                         struct ldb_message *op_feature_msg)
835 {
836         int ret;
837         const int domain_func_level = dsdb_functional_level(ldb);
838         struct ldb_dn *ntds_settings_dn;
839         TALLOC_CTX *tmp_ctx;
840         unsigned int el_count = 0;
841         struct ldb_message *msg;
842
843         ret = ldb_msg_find_attr_as_int(op_feature_msg, "msDS-RequiredForestBehaviorVersion", 0);
844         if (domain_func_level < ret){
845                 ldb_asprintf_errstring(ldb,
846                                        "rootdse_enable_recycle_bin: Domain functional level must be at least %d\n",
847                                        ret);
848                 return LDB_ERR_UNWILLING_TO_PERFORM;
849         }
850
851         tmp_ctx = talloc_new(mem_ctx);
852         ntds_settings_dn = samdb_ntds_settings_dn(ldb);
853         if (!ntds_settings_dn) {
854                 DEBUG(0, (__location__ ": Failed to find NTDS settings DN\n"));
855                 ret = LDB_ERR_OPERATIONS_ERROR;
856                 talloc_free(tmp_ctx);
857                 return ret;
858         }
859
860         ntds_settings_dn = ldb_dn_copy(tmp_ctx, ntds_settings_dn);
861         if (!ntds_settings_dn) {
862                 DEBUG(0, (__location__ ": Failed to copy NTDS settings DN\n"));
863                 ret = LDB_ERR_OPERATIONS_ERROR;
864                 talloc_free(tmp_ctx);
865                 return ret;
866         }
867
868         msg = ldb_msg_new(tmp_ctx);
869         msg->dn = ntds_settings_dn;
870
871         ldb_msg_add_linearized_dn(msg, "msDS-EnabledFeature", op_feature_msg->dn);
872         msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
873
874         ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
875         if (ret != LDB_SUCCESS) {
876                 ldb_asprintf_errstring(ldb,
877                                        "rootdse_enable_recycle_bin: Failed to modify object %s - %s",
878                                        ldb_dn_get_linearized(ntds_settings_dn),
879                                        ldb_errstring(ldb));
880                 talloc_free(tmp_ctx);
881                 return ret;
882         }
883
884         msg->dn = op_feature_scope_dn;
885         ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
886         if (ret != LDB_SUCCESS) {
887                 ldb_asprintf_errstring(ldb,
888                                        "rootdse_enable_recycle_bin: Failed to modify object %s - %s",
889                                        ldb_dn_get_linearized(op_feature_scope_dn),
890                                        ldb_errstring(ldb));
891                 talloc_free(tmp_ctx);
892                 return ret;
893         }
894
895         return LDB_SUCCESS;
896 }
897
898 static int rootdse_enableoptionalfeature(struct ldb_module *module, struct ldb_request *req)
899 {
900         /*
901           steps:
902                - check for system (only system can enable features)
903                - extract GUID from the request
904                - find the feature object
905                - check functional level, must be at least msDS-RequiredForestBehaviorVersion
906                - check if it is already enabled (if enabled return LDAP_ATTRIBUTE_OR_VALUE_EXISTS) - probably not needed, just return error from the add/modify
907                - add/modify objects (see ntdsconnection code for an example)
908          */
909
910         struct ldb_context *ldb = ldb_module_get_ctx(module);
911         struct GUID op_feature_guid;
912         struct ldb_dn *op_feature_scope_dn;
913         struct ldb_message *op_feature_msg;
914         struct auth_session_info *session_info =
915                                 (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
916         TALLOC_CTX *tmp_ctx = talloc_new(ldb);
917         int ret;
918         const char *guid_string;
919
920         if (security_session_user_level(session_info, NULL) != SECURITY_SYSTEM) {
921                 ldb_set_errstring(ldb, "rootdse: Insufficient rights for enableoptionalfeature");
922                 return LDB_ERR_UNWILLING_TO_PERFORM;
923         }
924
925         ret = get_optional_feature_dn_guid(req, ldb, tmp_ctx, &op_feature_scope_dn, &op_feature_guid);
926         if (ret != LDB_SUCCESS) {
927                 talloc_free(tmp_ctx);
928                 return ret;
929         }
930
931         guid_string = GUID_string(tmp_ctx, &op_feature_guid);
932         if (!guid_string) {
933                 ldb_set_errstring(ldb, "rootdse: bad optional feature GUID");
934                 return LDB_ERR_UNWILLING_TO_PERFORM;
935         }
936
937         ret = dsdb_find_optional_feature(module, ldb, tmp_ctx, op_feature_guid, &op_feature_msg);
938         if (ret != LDB_SUCCESS) {
939                 ldb_asprintf_errstring(ldb,
940                                        "rootdse: unable to find optional feature for %s - %s",
941                                        guid_string, ldb_errstring(ldb));
942                 talloc_free(tmp_ctx);
943                 return ret;
944         }
945
946         if (strcasecmp(DS_GUID_FEATURE_RECYCLE_BIN, guid_string) == 0) {
947                         ret = rootdse_enable_recycle_bin(module, ldb,
948                                                          tmp_ctx, op_feature_scope_dn,
949                                                          op_feature_msg);
950         } else {
951                 ldb_asprintf_errstring(ldb,
952                                        "rootdse: unknown optional feature %s",
953                                        guid_string);
954                 talloc_free(tmp_ctx);
955                 return LDB_ERR_UNWILLING_TO_PERFORM;
956         }
957         if (ret != LDB_SUCCESS) {
958                 ldb_asprintf_errstring(ldb,
959                                        "rootdse: failed to set optional feature for %s - %s",
960                                        guid_string, ldb_errstring(ldb));
961                 talloc_free(tmp_ctx);
962                 return ret;
963         }
964
965         talloc_free(tmp_ctx);
966         return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);;
967 }
968
969 static int rootdse_schemaupdatenow(struct ldb_module *module, struct ldb_request *req)
970 {
971         struct ldb_context *ldb = ldb_module_get_ctx(module);
972         struct ldb_result *ext_res;
973         int ret;
974         struct ldb_dn *schema_dn;
975
976         schema_dn = ldb_get_schema_basedn(ldb);
977         if (!schema_dn) {
978                 ldb_reset_err_string(ldb);
979                 ldb_debug(ldb, LDB_DEBUG_WARNING,
980                           "rootdse_modify: no schema dn present: (skip ldb_extended call)\n");
981                 return ldb_next_request(module, req);
982         }
983
984         ret = ldb_extended(ldb, DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID, schema_dn, &ext_res);
985         if (ret != LDB_SUCCESS) {
986                 return ldb_operr(ldb);
987         }
988
989         talloc_free(ext_res);
990         return ldb_module_done(req, NULL, NULL, ret);
991 }
992
993 static int rootdse_add(struct ldb_module *module, struct ldb_request *req)
994 {
995         struct ldb_context *ldb = ldb_module_get_ctx(module);
996
997         rootdse_mark_noncritical(module, req->controls);
998
999         /*
1000                 If dn is not "" we should let it pass through
1001         */
1002         if (!ldb_dn_is_null(req->op.add.message->dn)) {
1003                 return ldb_next_request(module, req);
1004         }
1005
1006         ldb_set_errstring(ldb, "rootdse_add: you cannot add a new rootdse entry!");
1007         return LDB_ERR_NAMING_VIOLATION;
1008 }
1009
1010 static int rootdse_become_master(struct ldb_module *module,
1011                                  struct ldb_request *req,
1012                                  uint32_t role)
1013 {
1014         struct drepl_takeFSMORole r;
1015         struct messaging_context *msg;
1016         struct ldb_context *ldb = ldb_module_get_ctx(module);
1017         TALLOC_CTX *tmp_ctx = talloc_new(req);
1018         struct loadparm_context *lp_ctx = ldb_get_opaque(ldb, "loadparm");
1019         NTSTATUS status_call;
1020         WERROR status_fn;
1021         struct dcerpc_binding_handle *irpc_handle;
1022
1023         msg = messaging_client_init(tmp_ctx, lpcfg_messaging_path(tmp_ctx, lp_ctx),
1024                                     ldb_get_event_context(ldb));
1025
1026         irpc_handle = irpc_binding_handle_by_name(tmp_ctx, msg,
1027                                                   "dreplsrv",
1028                                                   &ndr_table_irpc);
1029         if (irpc_handle == NULL) {
1030                 return ldb_oom(ldb);
1031         }
1032         r.in.role = role;
1033
1034         status_call = dcerpc_drepl_takeFSMORole_r(irpc_handle, tmp_ctx, &r);
1035         if (!NT_STATUS_IS_OK(status_call)) {
1036                 return LDB_ERR_OPERATIONS_ERROR;
1037         }
1038         status_fn = r.out.result;
1039         if (!W_ERROR_IS_OK(status_fn)) {
1040                 return LDB_ERR_OPERATIONS_ERROR;
1041         }
1042         return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
1043 }
1044
1045 static int rootdse_modify(struct ldb_module *module, struct ldb_request *req)
1046 {
1047         struct ldb_context *ldb = ldb_module_get_ctx(module);
1048
1049         rootdse_mark_noncritical(module, req->controls);
1050
1051         /*
1052                 If dn is not "" we should let it pass through
1053         */
1054         if (!ldb_dn_is_null(req->op.mod.message->dn)) {
1055                 return ldb_next_request(module, req);
1056         }
1057
1058         /*
1059                 dn is empty so check for schemaUpdateNow attribute
1060                 "The type of modification and values specified in the LDAP modify operation do not matter." MSDN
1061         */
1062         if (ldb_msg_find_element(req->op.mod.message, "schemaUpdateNow")) {
1063                 return rootdse_schemaupdatenow(module, req);
1064         }
1065         if (ldb_msg_find_element(req->op.mod.message, "becomeDomainMaster")) {
1066                 return rootdse_become_master(module, req, DREPL_NAMING_MASTER);
1067         }
1068         if (ldb_msg_find_element(req->op.mod.message, "becomeInfrastructureMaster")) {
1069                 return rootdse_become_master(module, req, DREPL_INFRASTRUCTURE_MASTER);
1070         }
1071         if (ldb_msg_find_element(req->op.mod.message, "becomeRidMaster")) {
1072                 return rootdse_become_master(module, req, DREPL_RID_MASTER);
1073         }
1074         if (ldb_msg_find_element(req->op.mod.message, "becomeSchemaMaster")) {
1075                 return rootdse_become_master(module, req, DREPL_SCHEMA_MASTER);
1076         }
1077         if (ldb_msg_find_element(req->op.mod.message, "becomePdc")) {
1078                 return rootdse_become_master(module, req, DREPL_PDC_MASTER);
1079         }
1080         if (ldb_msg_find_element(req->op.mod.message, "enableOptionalFeature")) {
1081                 return rootdse_enableoptionalfeature(module, req);
1082         }
1083
1084         ldb_set_errstring(ldb, "rootdse_modify: unknown attribute to change!");
1085         return LDB_ERR_UNWILLING_TO_PERFORM;
1086 }
1087
1088 static int rootdse_delete(struct ldb_module *module, struct ldb_request *req)
1089 {
1090         struct ldb_context *ldb = ldb_module_get_ctx(module);
1091
1092         rootdse_mark_noncritical(module, req->controls);
1093
1094         /*
1095                 If dn is not "" we should let it pass through
1096         */
1097         if (!ldb_dn_is_null(req->op.del.dn)) {
1098                 return ldb_next_request(module, req);
1099         }
1100
1101         ldb_set_errstring(ldb, "rootdse_remove: you cannot delete the rootdse entry!");
1102         return LDB_ERR_NO_SUCH_OBJECT;
1103 }
1104
1105 _PUBLIC_ const struct ldb_module_ops ldb_rootdse_module_ops = {
1106         .name           = "rootdse",
1107         .init_context   = rootdse_init,
1108         .search         = rootdse_search,
1109         .request        = rootdse_request,
1110         .add            = rootdse_add,
1111         .modify         = rootdse_modify,
1112         .del            = rootdse_delete
1113 };