s4-dsdb: Fixed incorrect LDAP return code when anonymous login is used.
[Samba.git] / source4 / dsdb / samdb / ldb_modules / rootdse.c
blob2571bc3c8b150b682366d3650d4ed1c63d2e8ffd
1 /*
2 Unix SMB/CIFS implementation.
4 rootDSE ldb module
6 Copyright (C) Andrew Tridgell 2005
7 Copyright (C) Simo Sorce 2005-2008
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.
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.
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/>.
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"
38 struct private_data {
39 unsigned int num_controls;
40 char **controls;
41 unsigned int num_partitions;
42 struct ldb_dn **partitions;
43 bool block_anonymous;
47 return 1 if a specific attribute has been requested
49 static int do_attribute(const char * const *attrs, const char *name)
51 return attrs == NULL ||
52 ldb_attr_in_list(attrs, name) ||
53 ldb_attr_in_list(attrs, "*");
56 static int do_attribute_explicit(const char * const *attrs, const char *name)
58 return attrs != NULL && ldb_attr_in_list(attrs, name);
63 expand a DN attribute to include extended DN information if requested
65 static int expand_dn_in_message(struct ldb_module *module, struct ldb_message *msg,
66 const char *attrname, struct ldb_control *edn_control,
67 struct ldb_request *req)
69 struct ldb_dn *dn, *dn2;
70 struct ldb_val *v;
71 int ret;
72 struct ldb_request *req2;
73 char *dn_string;
74 const char *no_attrs[] = { NULL };
75 struct ldb_result *res;
76 struct ldb_extended_dn_control *edn;
77 TALLOC_CTX *tmp_ctx = talloc_new(req);
78 struct ldb_context *ldb;
79 int edn_type = 0;
81 ldb = ldb_module_get_ctx(module);
83 edn = talloc_get_type(edn_control->data, struct ldb_extended_dn_control);
84 if (edn) {
85 edn_type = edn->type;
88 v = discard_const_p(struct ldb_val, ldb_msg_find_ldb_val(msg, attrname));
89 if (v == NULL) {
90 talloc_free(tmp_ctx);
91 return LDB_SUCCESS;
94 dn_string = talloc_strndup(tmp_ctx, (const char *)v->data, v->length);
95 if (dn_string == NULL) {
96 talloc_free(tmp_ctx);
97 return ldb_operr(ldb);
100 res = talloc_zero(tmp_ctx, struct ldb_result);
101 if (res == NULL) {
102 talloc_free(tmp_ctx);
103 return ldb_operr(ldb);
106 dn = ldb_dn_new(tmp_ctx, ldb, dn_string);
107 if (!ldb_dn_validate(dn)) {
108 talloc_free(tmp_ctx);
109 return ldb_operr(ldb);
112 ret = ldb_build_search_req(&req2, ldb, tmp_ctx,
114 LDB_SCOPE_BASE,
115 NULL,
116 no_attrs,
117 NULL,
118 res, ldb_search_default_callback,
119 req);
120 LDB_REQ_SET_LOCATION(req2);
121 if (ret != LDB_SUCCESS) {
122 talloc_free(tmp_ctx);
123 return ret;
127 ret = ldb_request_add_control(req2,
128 LDB_CONTROL_EXTENDED_DN_OID,
129 edn_control->critical, edn);
130 if (ret != LDB_SUCCESS) {
131 talloc_free(tmp_ctx);
132 return ret;
135 ret = ldb_next_request(module, req2);
136 if (ret == LDB_SUCCESS) {
137 ret = ldb_wait(req2->handle, LDB_WAIT_ALL);
139 if (ret != LDB_SUCCESS) {
140 talloc_free(tmp_ctx);
141 return ret;
144 if (!res || res->count != 1) {
145 talloc_free(tmp_ctx);
146 return ldb_operr(ldb);
149 dn2 = res->msgs[0]->dn;
151 v->data = (uint8_t *)ldb_dn_get_extended_linearized(msg->elements, dn2, edn_type);
152 if (v->data == NULL) {
153 talloc_free(tmp_ctx);
154 return ldb_operr(ldb);
156 v->length = strlen((char *)v->data);
159 talloc_free(tmp_ctx);
161 return LDB_SUCCESS;
166 add dynamically generated attributes to rootDSE result
168 static int rootdse_add_dynamic(struct ldb_module *module, struct ldb_message *msg,
169 const char * const *attrs, struct ldb_request *req)
171 struct ldb_context *ldb;
172 struct private_data *priv = talloc_get_type(ldb_module_get_private(module), struct private_data);
173 char **server_sasl;
174 const struct dsdb_schema *schema;
175 int *val;
176 struct ldb_control *edn_control;
177 const char *dn_attrs[] = {
178 "configurationNamingContext",
179 "defaultNamingContext",
180 "dsServiceName",
181 "rootDomainNamingContext",
182 "schemaNamingContext",
183 "serverName",
184 NULL
187 ldb = ldb_module_get_ctx(module);
188 schema = dsdb_get_schema(ldb, NULL);
190 msg->dn = ldb_dn_new(msg, ldb, NULL);
192 /* don't return the distinguishedName, cn and name attributes */
193 ldb_msg_remove_attr(msg, "distinguishedName");
194 ldb_msg_remove_attr(msg, "cn");
195 ldb_msg_remove_attr(msg, "name");
197 if (do_attribute(attrs, "serverName")) {
198 if (ldb_msg_add_linearized_dn(msg, "serverName",
199 samdb_server_dn(ldb, msg)) != LDB_SUCCESS) {
200 goto failed;
204 if (do_attribute(attrs, "dnsHostName")) {
205 if (ldb_msg_add_string(msg, "dnsHostName",
206 samdb_search_string(ldb, msg, samdb_server_dn(ldb, msg),
207 "dNSHostName", NULL)) != LDB_SUCCESS) {
208 goto failed;
212 if (do_attribute(attrs, "ldapServiceName")) {
213 struct loadparm_context *lp_ctx
214 = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
215 struct loadparm_context);
216 char *ldap_service_name, *hostname;
218 hostname = talloc_strdup(msg, lpcfg_netbios_name(lp_ctx));
219 if (hostname == NULL) {
220 goto failed;
222 strlower_m(hostname);
224 ldap_service_name = talloc_asprintf(msg, "%s:%s$@%s",
225 samdb_forest_name(ldb, msg),
226 hostname, lpcfg_realm(lp_ctx));
227 if (ldap_service_name == NULL) {
228 goto failed;
231 if (ldb_msg_add_string(msg, "ldapServiceName",
232 ldap_service_name) != LDB_SUCCESS) {
233 goto failed;
237 if (do_attribute(attrs, "currentTime")) {
238 if (ldb_msg_add_steal_string(msg, "currentTime",
239 ldb_timestring(msg, time(NULL))) != LDB_SUCCESS) {
240 goto failed;
244 if (priv && do_attribute(attrs, "supportedControl")) {
245 unsigned int i;
246 for (i = 0; i < priv->num_controls; i++) {
247 char *control = talloc_strdup(msg, priv->controls[i]);
248 if (!control) {
249 goto failed;
251 if (ldb_msg_add_steal_string(msg, "supportedControl",
252 control) != LDB_SUCCESS) {
253 goto failed;
258 if (priv && do_attribute(attrs, "namingContexts")) {
259 unsigned int i;
260 for (i = 0; i < priv->num_partitions; i++) {
261 struct ldb_dn *dn = priv->partitions[i];
262 if (ldb_msg_add_steal_string(msg, "namingContexts",
263 ldb_dn_alloc_linearized(msg, dn)) != LDB_SUCCESS) {
264 goto failed;
269 server_sasl = talloc_get_type(ldb_get_opaque(ldb, "supportedSASLMechanisms"),
270 char *);
271 if (server_sasl && do_attribute(attrs, "supportedSASLMechanisms")) {
272 unsigned int i;
273 for (i = 0; server_sasl && server_sasl[i]; i++) {
274 char *sasl_name = talloc_strdup(msg, server_sasl[i]);
275 if (!sasl_name) {
276 goto failed;
278 if (ldb_msg_add_steal_string(msg, "supportedSASLMechanisms",
279 sasl_name) != LDB_SUCCESS) {
280 goto failed;
285 if (do_attribute(attrs, "highestCommittedUSN")) {
286 uint64_t seq_num;
287 int ret = ldb_sequence_number(ldb, LDB_SEQ_HIGHEST_SEQ, &seq_num);
288 if (ret == LDB_SUCCESS) {
289 if (samdb_msg_add_uint64(ldb, msg, msg,
290 "highestCommittedUSN",
291 seq_num) != LDB_SUCCESS) {
292 goto failed;
297 if (schema && do_attribute_explicit(attrs, "dsSchemaAttrCount")) {
298 struct dsdb_attribute *cur;
299 unsigned int n = 0;
301 for (cur = schema->attributes; cur; cur = cur->next) {
302 n++;
305 if (samdb_msg_add_uint(ldb, msg, msg, "dsSchemaAttrCount",
306 n) != LDB_SUCCESS) {
307 goto failed;
311 if (schema && do_attribute_explicit(attrs, "dsSchemaClassCount")) {
312 struct dsdb_class *cur;
313 unsigned int n = 0;
315 for (cur = schema->classes; cur; cur = cur->next) {
316 n++;
319 if (samdb_msg_add_uint(ldb, msg, msg, "dsSchemaClassCount",
320 n) != LDB_SUCCESS) {
321 goto failed;
325 if (schema && do_attribute_explicit(attrs, "dsSchemaPrefixCount")) {
326 if (samdb_msg_add_uint(ldb, msg, msg, "dsSchemaPrefixCount",
327 schema->prefixmap->length) != LDB_SUCCESS) {
328 goto failed;
332 if (do_attribute_explicit(attrs, "validFSMOs")) {
333 const struct dsdb_naming_fsmo *naming_fsmo;
334 const struct dsdb_pdc_fsmo *pdc_fsmo;
335 const char *dn_str;
337 if (schema && schema->fsmo.we_are_master) {
338 dn_str = ldb_dn_get_linearized(ldb_get_schema_basedn(ldb));
339 if (dn_str && dn_str[0]) {
340 if (ldb_msg_add_fmt(msg, "validFSMOs", "%s", dn_str) != LDB_SUCCESS) {
341 goto failed;
346 naming_fsmo = talloc_get_type(ldb_get_opaque(ldb, "dsdb_naming_fsmo"),
347 struct dsdb_naming_fsmo);
348 if (naming_fsmo && naming_fsmo->we_are_master) {
349 dn_str = ldb_dn_get_linearized(samdb_partitions_dn(ldb, msg));
350 if (dn_str && dn_str[0]) {
351 if (ldb_msg_add_fmt(msg, "validFSMOs", "%s", dn_str) != LDB_SUCCESS) {
352 goto failed;
357 pdc_fsmo = talloc_get_type(ldb_get_opaque(ldb, "dsdb_pdc_fsmo"),
358 struct dsdb_pdc_fsmo);
359 if (pdc_fsmo && pdc_fsmo->we_are_master) {
360 dn_str = ldb_dn_get_linearized(ldb_get_default_basedn(ldb));
361 if (dn_str && dn_str[0]) {
362 if (ldb_msg_add_fmt(msg, "validFSMOs", "%s", dn_str) != LDB_SUCCESS) {
363 goto failed;
369 if (do_attribute_explicit(attrs, "vendorVersion")) {
370 if (ldb_msg_add_fmt(msg, "vendorVersion",
371 "%s", SAMBA_VERSION_STRING) != LDB_SUCCESS) {
372 goto failed;
376 if (do_attribute(attrs, "domainFunctionality")) {
377 if (samdb_msg_add_int(ldb, msg, msg, "domainFunctionality",
378 dsdb_functional_level(ldb)) != LDB_SUCCESS) {
379 goto failed;
383 if (do_attribute(attrs, "forestFunctionality")) {
384 if (samdb_msg_add_int(ldb, msg, msg, "forestFunctionality",
385 dsdb_forest_functional_level(ldb)) != LDB_SUCCESS) {
386 goto failed;
390 if (do_attribute(attrs, "domainControllerFunctionality")
391 && (val = talloc_get_type(ldb_get_opaque(ldb, "domainControllerFunctionality"), int))) {
392 if (samdb_msg_add_int(ldb, msg, msg,
393 "domainControllerFunctionality",
394 *val) != LDB_SUCCESS) {
395 goto failed;
399 if (do_attribute(attrs, "isGlobalCatalogReady")) {
400 /* MS-ADTS 3.1.1.3.2.10
401 Note, we should only return true here is we have
402 completed at least one synchronisation. As both
403 provision and vampire do a full sync, this means we
404 can return true is the gc bit is set in the NTDSDSA
405 options */
406 if (ldb_msg_add_fmt(msg, "isGlobalCatalogReady",
407 "%s", samdb_is_gc(ldb)?"TRUE":"FALSE") != LDB_SUCCESS) {
408 goto failed;
412 if (do_attribute_explicit(attrs, "tokenGroups")) {
413 unsigned int i;
414 /* Obtain the user's session_info */
415 struct auth_session_info *session_info
416 = (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
417 if (session_info && session_info->security_token) {
418 /* The list of groups this user is in */
419 for (i = 0; i < session_info->security_token->num_sids; i++) {
420 if (samdb_msg_add_dom_sid(ldb, msg, msg,
421 "tokenGroups",
422 &session_info->security_token->sids[i]) != LDB_SUCCESS) {
423 goto failed;
429 /* TODO: lots more dynamic attributes should be added here */
431 edn_control = ldb_request_get_control(req, LDB_CONTROL_EXTENDED_DN_OID);
433 /* if the client sent us the EXTENDED_DN control then we need
434 to expand the DNs to have GUID and SID. W2K8 join relies on
435 this */
436 if (edn_control) {
437 unsigned int i;
438 int ret;
439 for (i=0; dn_attrs[i]; i++) {
440 if (!do_attribute(attrs, dn_attrs[i])) continue;
441 ret = expand_dn_in_message(module, msg, dn_attrs[i],
442 edn_control, req);
443 if (ret != LDB_SUCCESS) {
444 DEBUG(0,(__location__ ": Failed to expand DN in rootDSE for %s\n",
445 dn_attrs[i]));
446 goto failed;
451 return LDB_SUCCESS;
453 failed:
454 return ldb_operr(ldb);
458 handle search requests
461 struct rootdse_context {
462 struct ldb_module *module;
463 struct ldb_request *req;
466 static struct rootdse_context *rootdse_init_context(struct ldb_module *module,
467 struct ldb_request *req)
469 struct ldb_context *ldb;
470 struct rootdse_context *ac;
472 ldb = ldb_module_get_ctx(module);
474 ac = talloc_zero(req, struct rootdse_context);
475 if (ac == NULL) {
476 ldb_set_errstring(ldb, "Out of Memory");
477 return NULL;
480 ac->module = module;
481 ac->req = req;
483 return ac;
486 static int rootdse_callback(struct ldb_request *req, struct ldb_reply *ares)
488 struct rootdse_context *ac;
489 int ret;
491 ac = talloc_get_type(req->context, struct rootdse_context);
493 if (!ares) {
494 return ldb_module_done(ac->req, NULL, NULL,
495 LDB_ERR_OPERATIONS_ERROR);
497 if (ares->error != LDB_SUCCESS) {
498 return ldb_module_done(ac->req, ares->controls,
499 ares->response, ares->error);
502 switch (ares->type) {
503 case LDB_REPLY_ENTRY:
505 * if the client explicit asks for the 'netlogon' attribute
506 * the reply_entry needs to be skipped
508 if (ac->req->op.search.attrs &&
509 ldb_attr_in_list(ac->req->op.search.attrs, "netlogon")) {
510 talloc_free(ares);
511 return LDB_SUCCESS;
514 /* for each record returned post-process to add any dynamic
515 attributes that have been asked for */
516 ret = rootdse_add_dynamic(ac->module, ares->message,
517 ac->req->op.search.attrs, ac->req);
518 if (ret != LDB_SUCCESS) {
519 talloc_free(ares);
520 return ldb_module_done(ac->req, NULL, NULL, ret);
523 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
525 case LDB_REPLY_REFERRAL:
526 /* should we allow the backend to return referrals in this case
527 * ?? */
528 break;
530 case LDB_REPLY_DONE:
531 return ldb_module_done(ac->req, ares->controls,
532 ares->response, ares->error);
535 talloc_free(ares);
536 return LDB_SUCCESS;
540 filter from controls from clients in several ways
542 1) mark our registered controls as non-critical in the request
544 This is needed as clients may mark controls as critical even if
545 they are not needed at all in a request. For example, the centrify
546 client sets the SD_FLAGS control as critical on ldap modify
547 requests which are setting the dNSHostName attribute on the
548 machine account. That request doesn't need SD_FLAGS at all, but
549 centrify adds it on all ldap requests.
551 2) if this request is untrusted then remove any non-registered
552 controls that are non-critical
554 This is used on ldap:// connections to prevent remote users from
555 setting an internal control that may be dangerous
557 3) if this request is untrusted then fail any request that includes
558 a critical non-registered control
560 static int rootdse_filter_controls(struct ldb_module *module, struct ldb_request *req)
562 unsigned int i, j;
563 struct private_data *priv = talloc_get_type(ldb_module_get_private(module), struct private_data);
564 bool is_untrusted;
566 if (!req->controls) {
567 return LDB_SUCCESS;
570 is_untrusted = ldb_req_is_untrusted(req);
572 for (i=0; req->controls[i]; i++) {
573 bool is_registered = false;
574 bool is_critical = (req->controls[i]->critical != 0);
576 if (req->controls[i]->oid == NULL) {
577 continue;
580 if (is_untrusted || is_critical) {
581 for (j=0; j<priv->num_controls; j++) {
582 if (strcasecmp(priv->controls[j], req->controls[i]->oid) == 0) {
583 is_registered = true;
584 break;
589 if (is_untrusted && !is_registered) {
590 if (!is_critical) {
591 /* remove it by marking the oid NULL */
592 req->controls[i]->oid = NULL;
593 req->controls[i]->data = NULL;
594 req->controls[i]->critical = 0;
595 continue;
597 /* its a critical unregistered control - give
598 an error */
599 ldb_asprintf_errstring(ldb_module_get_ctx(module),
600 "Attempt to use critical non-registered control '%s'",
601 req->controls[i]->oid);
602 return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
605 if (!is_critical) {
606 continue;
609 if (is_registered) {
610 req->controls[i]->critical = 0;
614 return LDB_SUCCESS;
617 /* Ensure that anonymous users are not allowed to make anything other than rootDSE search operations */
619 static int rootdse_filter_operations(struct ldb_module *module, struct ldb_request *req)
621 struct auth_session_info *session_info;
622 struct private_data *priv = talloc_get_type(ldb_module_get_private(module), struct private_data);
623 bool is_untrusted = ldb_req_is_untrusted(req);
624 bool is_anonymous = true;
625 if (is_untrusted == false) {
626 return LDB_SUCCESS;
629 session_info = (struct auth_session_info *)ldb_get_opaque(ldb_module_get_ctx(module), "sessionInfo");
630 if (session_info) {
631 is_anonymous = security_token_is_anonymous(session_info->security_token);
634 if (is_anonymous == false || (priv && priv->block_anonymous == false)) {
635 return LDB_SUCCESS;
638 if (req->operation == LDB_SEARCH) {
639 if (req->op.search.scope == LDB_SCOPE_BASE && ldb_dn_is_null(req->op.search.base)) {
640 return LDB_SUCCESS;
643 ldb_set_errstring(ldb_module_get_ctx(module), "Operation unavailable without authentication");
644 return LDB_ERR_OPERATIONS_ERROR;
647 static int rootdse_search(struct ldb_module *module, struct ldb_request *req)
649 struct ldb_context *ldb;
650 struct rootdse_context *ac;
651 struct ldb_request *down_req;
652 int ret;
654 ret = rootdse_filter_operations(module, req);
655 if (ret != LDB_SUCCESS) {
656 return ret;
659 ret = rootdse_filter_controls(module, req);
660 if (ret != LDB_SUCCESS) {
661 return ret;
664 ldb = ldb_module_get_ctx(module);
666 /* see if its for the rootDSE - only a base search on the "" DN qualifies */
667 if (!(req->op.search.scope == LDB_SCOPE_BASE && ldb_dn_is_null(req->op.search.base))) {
668 /* Otherwise, pass down to the rest of the stack */
669 return ldb_next_request(module, req);
672 ac = rootdse_init_context(module, req);
673 if (ac == NULL) {
674 return ldb_operr(ldb);
677 /* in our db we store the rootDSE with a DN of @ROOTDSE */
678 ret = ldb_build_search_req(&down_req, ldb, ac,
679 ldb_dn_new(ac, ldb, "@ROOTDSE"),
680 LDB_SCOPE_BASE,
681 NULL,
682 req->op.search.attrs,
683 NULL,/* for now skip the controls from the client */
684 ac, rootdse_callback,
685 req);
686 LDB_REQ_SET_LOCATION(down_req);
687 if (ret != LDB_SUCCESS) {
688 return ret;
691 return ldb_next_request(module, down_req);
694 static int rootdse_register_control(struct ldb_module *module, struct ldb_request *req)
696 struct private_data *priv = talloc_get_type(ldb_module_get_private(module), struct private_data);
697 char **list;
699 list = talloc_realloc(priv, priv->controls, char *, priv->num_controls + 1);
700 if (!list) {
701 return ldb_oom(ldb_module_get_ctx(module));
704 list[priv->num_controls] = talloc_strdup(list, req->op.reg_control.oid);
705 if (!list[priv->num_controls]) {
706 return ldb_oom(ldb_module_get_ctx(module));
709 priv->num_controls += 1;
710 priv->controls = list;
712 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
715 static int rootdse_register_partition(struct ldb_module *module, struct ldb_request *req)
717 struct private_data *priv = talloc_get_type(ldb_module_get_private(module), struct private_data);
718 struct ldb_dn **list;
720 list = talloc_realloc(priv, priv->partitions, struct ldb_dn *, priv->num_partitions + 1);
721 if (!list) {
722 return ldb_oom(ldb_module_get_ctx(module));
725 list[priv->num_partitions] = ldb_dn_copy(list, req->op.reg_partition.dn);
726 if (!list[priv->num_partitions]) {
727 return ldb_operr(ldb_module_get_ctx(module));
730 priv->num_partitions += 1;
731 priv->partitions = list;
733 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
737 static int rootdse_request(struct ldb_module *module, struct ldb_request *req)
739 switch (req->operation) {
741 case LDB_REQ_REGISTER_CONTROL:
742 return rootdse_register_control(module, req);
743 case LDB_REQ_REGISTER_PARTITION:
744 return rootdse_register_partition(module, req);
746 default:
747 break;
749 return ldb_next_request(module, req);
752 static int rootdse_init(struct ldb_module *module)
754 int ret;
755 struct ldb_context *ldb;
756 struct ldb_result *res;
757 struct private_data *data;
758 const char *attrs[] = { "msDS-Behavior-Version", NULL };
759 const char *ds_attrs[] = { "dsServiceName", NULL };
760 TALLOC_CTX *mem_ctx;
762 ldb = ldb_module_get_ctx(module);
764 data = talloc_zero(module, struct private_data);
765 if (data == NULL) {
766 return ldb_oom(ldb);
769 data->num_controls = 0;
770 data->controls = NULL;
771 data->num_partitions = 0;
772 data->partitions = NULL;
773 data->block_anonymous = true;
775 ldb_module_set_private(module, data);
777 ldb_set_default_dns(ldb);
779 ret = ldb_next_init(module);
781 if (ret != LDB_SUCCESS) {
782 return ret;
785 mem_ctx = talloc_new(data);
786 if (!mem_ctx) {
787 return ldb_oom(ldb);
790 /* Now that the partitions are set up, do a search for:
791 - domainControllerFunctionality
792 - domainFunctionality
793 - forestFunctionality
795 Then stuff these values into an opaque
797 ret = ldb_search(ldb, mem_ctx, &res,
798 ldb_get_default_basedn(ldb),
799 LDB_SCOPE_BASE, attrs, NULL);
800 if (ret == LDB_SUCCESS && res->count == 1) {
801 int domain_behaviour_version
802 = ldb_msg_find_attr_as_int(res->msgs[0],
803 "msDS-Behavior-Version", -1);
804 if (domain_behaviour_version != -1) {
805 int *val = talloc(ldb, int);
806 if (!val) {
807 talloc_free(mem_ctx);
808 return ldb_oom(ldb);
810 *val = domain_behaviour_version;
811 ret = ldb_set_opaque(ldb, "domainFunctionality", val);
812 if (ret != LDB_SUCCESS) {
813 talloc_free(mem_ctx);
814 return ret;
819 ret = ldb_search(ldb, mem_ctx, &res,
820 samdb_partitions_dn(ldb, mem_ctx),
821 LDB_SCOPE_BASE, attrs, NULL);
822 if (ret == LDB_SUCCESS && res->count == 1) {
823 int forest_behaviour_version
824 = ldb_msg_find_attr_as_int(res->msgs[0],
825 "msDS-Behavior-Version", -1);
826 if (forest_behaviour_version != -1) {
827 int *val = talloc(ldb, int);
828 if (!val) {
829 talloc_free(mem_ctx);
830 return ldb_oom(ldb);
832 *val = forest_behaviour_version;
833 ret = ldb_set_opaque(ldb, "forestFunctionality", val);
834 if (ret != LDB_SUCCESS) {
835 talloc_free(mem_ctx);
836 return ret;
841 ret = ldb_search(ldb, mem_ctx, &res,
842 ldb_dn_new(mem_ctx, ldb, ""),
843 LDB_SCOPE_BASE, ds_attrs, NULL);
844 if (ret == LDB_SUCCESS && res->count == 1) {
845 struct ldb_dn *ds_dn
846 = ldb_msg_find_attr_as_dn(ldb, mem_ctx, res->msgs[0],
847 "dsServiceName");
848 if (ds_dn) {
849 ret = ldb_search(ldb, mem_ctx, &res, ds_dn,
850 LDB_SCOPE_BASE, attrs, NULL);
851 if (ret == LDB_SUCCESS && res->count == 1) {
852 int domain_controller_behaviour_version
853 = ldb_msg_find_attr_as_int(res->msgs[0],
854 "msDS-Behavior-Version", -1);
855 if (domain_controller_behaviour_version != -1) {
856 int *val = talloc(ldb, int);
857 if (!val) {
858 talloc_free(mem_ctx);
859 return ldb_oom(ldb);
861 *val = domain_controller_behaviour_version;
862 ret = ldb_set_opaque(ldb,
863 "domainControllerFunctionality", val);
864 if (ret != LDB_SUCCESS) {
865 talloc_free(mem_ctx);
866 return ret;
873 data->block_anonymous = dsdb_block_anonymous_ops(module);
875 talloc_free(mem_ctx);
877 return LDB_SUCCESS;
881 * This function gets the string SCOPE_DN:OPTIONAL_FEATURE_GUID and parse it
882 * to a DN and a GUID object
884 static int get_optional_feature_dn_guid(struct ldb_request *req, struct ldb_context *ldb,
885 TALLOC_CTX *mem_ctx,
886 struct ldb_dn **op_feature_scope_dn,
887 struct GUID *op_feature_guid)
889 const struct ldb_message *msg = req->op.mod.message;
890 const char *ldb_val_str;
891 char *dn, *guid;
892 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
893 NTSTATUS status;
895 ldb_val_str = ldb_msg_find_attr_as_string(msg, "enableOptionalFeature", NULL);
896 if (!ldb_val_str) {
897 ldb_set_errstring(ldb,
898 "rootdse: unable to find 'enableOptionalFeature'!");
899 return LDB_ERR_UNWILLING_TO_PERFORM;
902 guid = strchr(ldb_val_str, ':');
903 if (!guid) {
904 ldb_set_errstring(ldb,
905 "rootdse: unable to find GUID in 'enableOptionalFeature'!");
906 return LDB_ERR_UNWILLING_TO_PERFORM;
908 status = GUID_from_string(guid+1, op_feature_guid);
909 if (!NT_STATUS_IS_OK(status)) {
910 ldb_set_errstring(ldb,
911 "rootdse: bad GUID in 'enableOptionalFeature'!");
912 return LDB_ERR_UNWILLING_TO_PERFORM;
915 dn = talloc_strndup(tmp_ctx, ldb_val_str, guid-ldb_val_str);
916 if (!dn) {
917 ldb_set_errstring(ldb,
918 "rootdse: bad DN in 'enableOptionalFeature'!");
919 return LDB_ERR_UNWILLING_TO_PERFORM;
922 *op_feature_scope_dn = ldb_dn_new(mem_ctx, ldb, dn);
924 talloc_free(tmp_ctx);
925 return LDB_SUCCESS;
929 * This function gets the OPTIONAL_FEATURE_GUID and looks for the optional feature
930 * ldb_message object.
932 static int dsdb_find_optional_feature(struct ldb_module *module, struct ldb_context *ldb,
933 TALLOC_CTX *mem_ctx, struct GUID op_feature_guid, struct ldb_message **msg)
935 struct ldb_result *res;
936 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
937 int ret;
939 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
940 NULL,
941 DSDB_FLAG_NEXT_MODULE |
942 DSDB_SEARCH_SEARCH_ALL_PARTITIONS,
943 "(&(objectClass=msDS-OptionalFeature)"
944 "(msDS-OptionalFeatureGUID=%s))",GUID_string(tmp_ctx, &op_feature_guid));
946 if (ret != LDB_SUCCESS) {
947 talloc_free(tmp_ctx);
948 return ret;
950 if (res->count == 0) {
951 talloc_free(tmp_ctx);
952 return LDB_ERR_NO_SUCH_OBJECT;
954 if (res->count != 1) {
955 ldb_asprintf_errstring(ldb,
956 "More than one object found matching optional feature GUID %s\n",
957 GUID_string(tmp_ctx, &op_feature_guid));
958 talloc_free(tmp_ctx);
959 return LDB_ERR_OPERATIONS_ERROR;
962 *msg = talloc_steal(mem_ctx, res->msgs[0]);
964 talloc_free(tmp_ctx);
965 return LDB_SUCCESS;
968 static int rootdse_enable_recycle_bin(struct ldb_module *module,struct ldb_context *ldb,
969 TALLOC_CTX *mem_ctx, struct ldb_dn *op_feature_scope_dn,
970 struct ldb_message *op_feature_msg)
972 int ret;
973 const int domain_func_level = dsdb_functional_level(ldb);
974 struct ldb_dn *ntds_settings_dn;
975 TALLOC_CTX *tmp_ctx;
976 unsigned int el_count = 0;
977 struct ldb_message *msg;
979 ret = ldb_msg_find_attr_as_int(op_feature_msg, "msDS-RequiredForestBehaviorVersion", 0);
980 if (domain_func_level < ret){
981 ldb_asprintf_errstring(ldb,
982 "rootdse_enable_recycle_bin: Domain functional level must be at least %d\n",
983 ret);
984 return LDB_ERR_UNWILLING_TO_PERFORM;
987 tmp_ctx = talloc_new(mem_ctx);
988 ntds_settings_dn = samdb_ntds_settings_dn(ldb);
989 if (!ntds_settings_dn) {
990 DEBUG(0, (__location__ ": Failed to find NTDS settings DN\n"));
991 ret = LDB_ERR_OPERATIONS_ERROR;
992 talloc_free(tmp_ctx);
993 return ret;
996 ntds_settings_dn = ldb_dn_copy(tmp_ctx, ntds_settings_dn);
997 if (!ntds_settings_dn) {
998 DEBUG(0, (__location__ ": Failed to copy NTDS settings DN\n"));
999 ret = LDB_ERR_OPERATIONS_ERROR;
1000 talloc_free(tmp_ctx);
1001 return ret;
1004 msg = ldb_msg_new(tmp_ctx);
1005 msg->dn = ntds_settings_dn;
1007 ldb_msg_add_linearized_dn(msg, "msDS-EnabledFeature", op_feature_msg->dn);
1008 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
1010 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
1011 if (ret != LDB_SUCCESS) {
1012 ldb_asprintf_errstring(ldb,
1013 "rootdse_enable_recycle_bin: Failed to modify object %s - %s",
1014 ldb_dn_get_linearized(ntds_settings_dn),
1015 ldb_errstring(ldb));
1016 talloc_free(tmp_ctx);
1017 return ret;
1020 msg->dn = op_feature_scope_dn;
1021 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
1022 if (ret != LDB_SUCCESS) {
1023 ldb_asprintf_errstring(ldb,
1024 "rootdse_enable_recycle_bin: Failed to modify object %s - %s",
1025 ldb_dn_get_linearized(op_feature_scope_dn),
1026 ldb_errstring(ldb));
1027 talloc_free(tmp_ctx);
1028 return ret;
1031 return LDB_SUCCESS;
1034 static int rootdse_enableoptionalfeature(struct ldb_module *module, struct ldb_request *req)
1037 steps:
1038 - check for system (only system can enable features)
1039 - extract GUID from the request
1040 - find the feature object
1041 - check functional level, must be at least msDS-RequiredForestBehaviorVersion
1042 - check if it is already enabled (if enabled return LDAP_ATTRIBUTE_OR_VALUE_EXISTS) - probably not needed, just return error from the add/modify
1043 - add/modify objects (see ntdsconnection code for an example)
1046 struct ldb_context *ldb = ldb_module_get_ctx(module);
1047 struct GUID op_feature_guid;
1048 struct ldb_dn *op_feature_scope_dn;
1049 struct ldb_message *op_feature_msg;
1050 struct auth_session_info *session_info =
1051 (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
1052 TALLOC_CTX *tmp_ctx = talloc_new(ldb);
1053 int ret;
1054 const char *guid_string;
1056 if (security_session_user_level(session_info, NULL) != SECURITY_SYSTEM) {
1057 ldb_set_errstring(ldb, "rootdse: Insufficient rights for enableoptionalfeature");
1058 return LDB_ERR_UNWILLING_TO_PERFORM;
1061 ret = get_optional_feature_dn_guid(req, ldb, tmp_ctx, &op_feature_scope_dn, &op_feature_guid);
1062 if (ret != LDB_SUCCESS) {
1063 talloc_free(tmp_ctx);
1064 return ret;
1067 guid_string = GUID_string(tmp_ctx, &op_feature_guid);
1068 if (!guid_string) {
1069 ldb_set_errstring(ldb, "rootdse: bad optional feature GUID");
1070 return LDB_ERR_UNWILLING_TO_PERFORM;
1073 ret = dsdb_find_optional_feature(module, ldb, tmp_ctx, op_feature_guid, &op_feature_msg);
1074 if (ret != LDB_SUCCESS) {
1075 ldb_asprintf_errstring(ldb,
1076 "rootdse: unable to find optional feature for %s - %s",
1077 guid_string, ldb_errstring(ldb));
1078 talloc_free(tmp_ctx);
1079 return ret;
1082 if (strcasecmp(DS_GUID_FEATURE_RECYCLE_BIN, guid_string) == 0) {
1083 ret = rootdse_enable_recycle_bin(module, ldb,
1084 tmp_ctx, op_feature_scope_dn,
1085 op_feature_msg);
1086 } else {
1087 ldb_asprintf_errstring(ldb,
1088 "rootdse: unknown optional feature %s",
1089 guid_string);
1090 talloc_free(tmp_ctx);
1091 return LDB_ERR_UNWILLING_TO_PERFORM;
1093 if (ret != LDB_SUCCESS) {
1094 ldb_asprintf_errstring(ldb,
1095 "rootdse: failed to set optional feature for %s - %s",
1096 guid_string, ldb_errstring(ldb));
1097 talloc_free(tmp_ctx);
1098 return ret;
1101 talloc_free(tmp_ctx);
1102 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);;
1105 static int rootdse_schemaupdatenow(struct ldb_module *module, struct ldb_request *req)
1107 struct ldb_context *ldb = ldb_module_get_ctx(module);
1108 struct ldb_result *ext_res;
1109 int ret;
1110 struct ldb_dn *schema_dn;
1112 schema_dn = ldb_get_schema_basedn(ldb);
1113 if (!schema_dn) {
1114 ldb_reset_err_string(ldb);
1115 ldb_debug(ldb, LDB_DEBUG_WARNING,
1116 "rootdse_modify: no schema dn present: (skip ldb_extended call)\n");
1117 return ldb_next_request(module, req);
1120 ret = ldb_extended(ldb, DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID, schema_dn, &ext_res);
1121 if (ret != LDB_SUCCESS) {
1122 return ldb_operr(ldb);
1125 talloc_free(ext_res);
1126 return ldb_module_done(req, NULL, NULL, ret);
1129 static int rootdse_add(struct ldb_module *module, struct ldb_request *req)
1131 struct ldb_context *ldb = ldb_module_get_ctx(module);
1132 int ret;
1134 ret = rootdse_filter_operations(module, req);
1135 if (ret != LDB_SUCCESS) {
1136 return ret;
1139 ret = rootdse_filter_controls(module, req);
1140 if (ret != LDB_SUCCESS) {
1141 return ret;
1145 If dn is not "" we should let it pass through
1147 if (!ldb_dn_is_null(req->op.add.message->dn)) {
1148 return ldb_next_request(module, req);
1151 ldb_set_errstring(ldb, "rootdse_add: you cannot add a new rootdse entry!");
1152 return LDB_ERR_NAMING_VIOLATION;
1155 static int rootdse_become_master(struct ldb_module *module,
1156 struct ldb_request *req,
1157 enum drepl_role_master role)
1159 struct drepl_takeFSMORole r;
1160 struct messaging_context *msg;
1161 struct ldb_context *ldb = ldb_module_get_ctx(module);
1162 TALLOC_CTX *tmp_ctx = talloc_new(req);
1163 struct loadparm_context *lp_ctx = ldb_get_opaque(ldb, "loadparm");
1164 NTSTATUS status_call;
1165 WERROR status_fn;
1166 bool am_rodc;
1167 struct dcerpc_binding_handle *irpc_handle;
1168 int ret;
1170 ret = samdb_rodc(ldb, &am_rodc);
1171 if (ret != LDB_SUCCESS) {
1172 return ldb_error(ldb, ret, "Could not determine if server is RODC.");
1175 if (am_rodc) {
1176 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1177 "RODC cannot become a role master.");
1180 msg = messaging_client_init(tmp_ctx, lpcfg_messaging_path(tmp_ctx, lp_ctx),
1181 ldb_get_event_context(ldb));
1182 if (!msg) {
1183 ldb_asprintf_errstring(ldb, "Failed to generate client messaging context in %s", lpcfg_messaging_path(tmp_ctx, lp_ctx));
1184 return LDB_ERR_OPERATIONS_ERROR;
1186 irpc_handle = irpc_binding_handle_by_name(tmp_ctx, msg,
1187 "dreplsrv",
1188 &ndr_table_irpc);
1189 if (irpc_handle == NULL) {
1190 return ldb_oom(ldb);
1192 r.in.role = role;
1194 status_call = dcerpc_drepl_takeFSMORole_r(irpc_handle, tmp_ctx, &r);
1195 if (!NT_STATUS_IS_OK(status_call)) {
1196 return LDB_ERR_OPERATIONS_ERROR;
1198 status_fn = r.out.result;
1199 if (!W_ERROR_IS_OK(status_fn)) {
1200 return LDB_ERR_OPERATIONS_ERROR;
1202 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
1205 static int rootdse_modify(struct ldb_module *module, struct ldb_request *req)
1207 struct ldb_context *ldb = ldb_module_get_ctx(module);
1208 int ret;
1210 ret = rootdse_filter_operations(module, req);
1211 if (ret != LDB_SUCCESS) {
1212 return ret;
1215 ret = rootdse_filter_controls(module, req);
1216 if (ret != LDB_SUCCESS) {
1217 return ret;
1221 If dn is not "" we should let it pass through
1223 if (!ldb_dn_is_null(req->op.mod.message->dn)) {
1224 return ldb_next_request(module, req);
1228 dn is empty so check for schemaUpdateNow attribute
1229 "The type of modification and values specified in the LDAP modify operation do not matter." MSDN
1231 if (ldb_msg_find_element(req->op.mod.message, "schemaUpdateNow")) {
1232 return rootdse_schemaupdatenow(module, req);
1234 if (ldb_msg_find_element(req->op.mod.message, "becomeDomainMaster")) {
1235 return rootdse_become_master(module, req, DREPL_NAMING_MASTER);
1237 if (ldb_msg_find_element(req->op.mod.message, "becomeInfrastructureMaster")) {
1238 return rootdse_become_master(module, req, DREPL_INFRASTRUCTURE_MASTER);
1240 if (ldb_msg_find_element(req->op.mod.message, "becomeRidMaster")) {
1241 return rootdse_become_master(module, req, DREPL_RID_MASTER);
1243 if (ldb_msg_find_element(req->op.mod.message, "becomeSchemaMaster")) {
1244 return rootdse_become_master(module, req, DREPL_SCHEMA_MASTER);
1246 if (ldb_msg_find_element(req->op.mod.message, "becomePdc")) {
1247 return rootdse_become_master(module, req, DREPL_PDC_MASTER);
1249 if (ldb_msg_find_element(req->op.mod.message, "enableOptionalFeature")) {
1250 return rootdse_enableoptionalfeature(module, req);
1253 ldb_set_errstring(ldb, "rootdse_modify: unknown attribute to change!");
1254 return LDB_ERR_UNWILLING_TO_PERFORM;
1257 static int rootdse_rename(struct ldb_module *module, struct ldb_request *req)
1259 struct ldb_context *ldb = ldb_module_get_ctx(module);
1260 int ret;
1262 ret = rootdse_filter_operations(module, req);
1263 if (ret != LDB_SUCCESS) {
1264 return ret;
1267 ret = rootdse_filter_controls(module, req);
1268 if (ret != LDB_SUCCESS) {
1269 return ret;
1273 If dn is not "" we should let it pass through
1275 if (!ldb_dn_is_null(req->op.rename.olddn)) {
1276 return ldb_next_request(module, req);
1279 ldb_set_errstring(ldb, "rootdse_remove: you cannot rename the rootdse entry!");
1280 return LDB_ERR_NO_SUCH_OBJECT;
1283 static int rootdse_delete(struct ldb_module *module, struct ldb_request *req)
1285 struct ldb_context *ldb = ldb_module_get_ctx(module);
1286 int ret;
1288 ret = rootdse_filter_operations(module, req);
1289 if (ret != LDB_SUCCESS) {
1290 return ret;
1293 ret = rootdse_filter_controls(module, req);
1294 if (ret != LDB_SUCCESS) {
1295 return ret;
1299 If dn is not "" we should let it pass through
1301 if (!ldb_dn_is_null(req->op.del.dn)) {
1302 return ldb_next_request(module, req);
1305 ldb_set_errstring(ldb, "rootdse_remove: you cannot delete the rootdse entry!");
1306 return LDB_ERR_NO_SUCH_OBJECT;
1309 static int rootdse_extended(struct ldb_module *module, struct ldb_request *req)
1311 int ret;
1313 ret = rootdse_filter_operations(module, req);
1314 if (ret != LDB_SUCCESS) {
1315 return ret;
1318 ret = rootdse_filter_controls(module, req);
1319 if (ret != LDB_SUCCESS) {
1320 return ret;
1323 return ldb_next_request(module, req);
1326 static const struct ldb_module_ops ldb_rootdse_module_ops = {
1327 .name = "rootdse",
1328 .init_context = rootdse_init,
1329 .search = rootdse_search,
1330 .request = rootdse_request,
1331 .add = rootdse_add,
1332 .modify = rootdse_modify,
1333 .rename = rootdse_rename,
1334 .extended = rootdse_extended,
1335 .del = rootdse_delete
1338 int ldb_rootdse_module_init(const char *version)
1340 LDB_MODULE_CHECK_VERSION(version);
1341 return ldb_register_module(&ldb_rootdse_module_ops);