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