2 Unix SMB/CIFS implementation.
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/>.
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"
34 struct ldb_dn
**partitions
;
38 return 1 if a specific attribute has been requested
40 static int do_attribute(const char * const *attrs
, const char *name
)
42 return attrs
== NULL
||
43 ldb_attr_in_list(attrs
, name
) ||
44 ldb_attr_in_list(attrs
, "*");
47 static int do_attribute_explicit(const char * const *attrs
, const char *name
)
49 return attrs
!= NULL
&& ldb_attr_in_list(attrs
, name
);
54 expand a DN attribute to include extended DN information if requested
56 static int expand_dn_in_message(struct ldb_module
*module
, struct ldb_message
*msg
,
57 const char *attrname
, struct ldb_control
*edn_control
,
58 struct ldb_request
*req
)
60 struct ldb_dn
*dn
, *dn2
;
63 struct ldb_request
*req2
;
65 const char *no_attrs
[] = { NULL
};
66 struct ldb_result
*res
;
67 struct ldb_extended_dn_control
*edn
;
68 TALLOC_CTX
*tmp_ctx
= talloc_new(req
);
69 struct ldb_context
*ldb
;
72 ldb
= ldb_module_get_ctx(module
);
74 edn
= talloc_get_type(edn_control
->data
, struct ldb_extended_dn_control
);
79 v
= discard_const_p(struct ldb_val
, ldb_msg_find_ldb_val(msg
, attrname
));
85 dn_string
= talloc_strndup(tmp_ctx
, (const char *)v
->data
, v
->length
);
86 if (dn_string
== NULL
) {
88 return LDB_ERR_OPERATIONS_ERROR
;
91 res
= talloc_zero(tmp_ctx
, struct ldb_result
);
94 return LDB_ERR_OPERATIONS_ERROR
;
97 dn
= ldb_dn_new(tmp_ctx
, ldb
, dn_string
);
98 if (!ldb_dn_validate(dn
)) {
100 return LDB_ERR_OPERATIONS_ERROR
;
103 ret
= ldb_build_search_req(&req2
, ldb
, tmp_ctx
,
109 res
, ldb_search_default_callback
,
111 if (ret
!= LDB_SUCCESS
) {
112 talloc_free(tmp_ctx
);
117 ret
= ldb_request_add_control(req2
,
118 LDB_CONTROL_EXTENDED_DN_OID
,
119 edn_control
->critical
, edn
);
120 if (ret
!= LDB_SUCCESS
) {
121 talloc_free(tmp_ctx
);
125 ret
= ldb_next_request(module
, req2
);
126 if (ret
== LDB_SUCCESS
) {
127 ret
= ldb_wait(req2
->handle
, LDB_WAIT_ALL
);
129 if (ret
!= LDB_SUCCESS
) {
130 talloc_free(tmp_ctx
);
134 if (!res
|| res
->count
!= 1) {
135 talloc_free(tmp_ctx
);
136 return LDB_ERR_OPERATIONS_ERROR
;
139 dn2
= res
->msgs
[0]->dn
;
141 v
->data
= (uint8_t *)ldb_dn_get_extended_linearized(msg
->elements
, dn2
, edn_type
);
142 v
->length
= strlen((char *)v
->data
);
144 if (v
->data
== NULL
) {
145 talloc_free(tmp_ctx
);
146 return LDB_ERR_OPERATIONS_ERROR
;
149 talloc_free(tmp_ctx
);
156 add dynamically generated attributes to rootDSE result
158 static int rootdse_add_dynamic(struct ldb_module
*module
, struct ldb_message
*msg
,
159 const char * const *attrs
, struct ldb_request
*req
)
161 struct ldb_context
*ldb
;
162 struct private_data
*priv
= talloc_get_type(ldb_module_get_private(module
), struct private_data
);
164 const struct dsdb_schema
*schema
;
166 struct ldb_control
*edn_control
;
167 const char *dn_attrs
[] = {
168 "configurationNamingContext",
169 "defaultNamingContext",
171 "rootDomainNamingContext",
172 "schemaNamingContext",
177 ldb
= ldb_module_get_ctx(module
);
178 schema
= dsdb_get_schema(ldb
);
180 msg
->dn
= ldb_dn_new(msg
, ldb
, NULL
);
182 /* don't return the distinduishedName, cn and name attributes */
183 ldb_msg_remove_attr(msg
, "distinguishedName");
184 ldb_msg_remove_attr(msg
, "cn");
185 ldb_msg_remove_attr(msg
, "name");
187 if (do_attribute(attrs
, "currentTime")) {
188 if (ldb_msg_add_steal_string(msg
, "currentTime",
189 ldb_timestring(msg
, time(NULL
))) != 0) {
194 if (priv
&& do_attribute(attrs
, "supportedControl")) {
196 for (i
= 0; i
< priv
->num_controls
; i
++) {
197 char *control
= talloc_strdup(msg
, priv
->controls
[i
]);
201 if (ldb_msg_add_steal_string(msg
, "supportedControl",
208 if (priv
&& do_attribute(attrs
, "namingContexts")) {
210 for (i
= 0; i
< priv
->num_partitions
; i
++) {
211 struct ldb_dn
*dn
= priv
->partitions
[i
];
212 if (ldb_msg_add_steal_string(msg
, "namingContexts",
213 ldb_dn_alloc_linearized(msg
, dn
)) != 0) {
219 server_sasl
= talloc_get_type(ldb_get_opaque(ldb
, "supportedSASLMechanims"),
221 if (server_sasl
&& do_attribute(attrs
, "supportedSASLMechanisms")) {
223 for (i
= 0; server_sasl
&& server_sasl
[i
]; i
++) {
224 char *sasl_name
= talloc_strdup(msg
, server_sasl
[i
]);
228 if (ldb_msg_add_steal_string(msg
, "supportedSASLMechanisms",
235 if (do_attribute(attrs
, "highestCommittedUSN")) {
237 int ret
= ldb_sequence_number(ldb
, LDB_SEQ_HIGHEST_SEQ
, &seq_num
);
238 if (ret
== LDB_SUCCESS
) {
239 if (ldb_msg_add_fmt(msg
, "highestCommittedUSN",
240 "%llu", (unsigned long long)seq_num
) != 0) {
246 if (schema
&& do_attribute_explicit(attrs
, "dsSchemaAttrCount")) {
247 struct dsdb_attribute
*cur
;
250 for (cur
= schema
->attributes
; cur
; cur
= cur
->next
) {
254 if (ldb_msg_add_fmt(msg
, "dsSchemaAttrCount",
260 if (schema
&& do_attribute_explicit(attrs
, "dsSchemaClassCount")) {
261 struct dsdb_class
*cur
;
264 for (cur
= schema
->classes
; cur
; cur
= cur
->next
) {
268 if (ldb_msg_add_fmt(msg
, "dsSchemaClassCount",
274 if (schema
&& do_attribute_explicit(attrs
, "dsSchemaPrefixCount")) {
275 if (ldb_msg_add_fmt(msg
, "dsSchemaPrefixCount",
276 "%u", schema
->num_prefixes
) != 0) {
281 if (do_attribute_explicit(attrs
, "validFSMOs")) {
282 const struct dsdb_naming_fsmo
*naming_fsmo
;
283 const struct dsdb_pdc_fsmo
*pdc_fsmo
;
286 if (schema
&& schema
->fsmo
.we_are_master
) {
287 dn_str
= ldb_dn_get_linearized(samdb_schema_dn(ldb
));
288 if (dn_str
&& dn_str
[0]) {
289 if (ldb_msg_add_fmt(msg
, "validFSMOs", "%s", dn_str
) != 0) {
295 naming_fsmo
= talloc_get_type(ldb_get_opaque(ldb
, "dsdb_naming_fsmo"),
296 struct dsdb_naming_fsmo
);
297 if (naming_fsmo
&& naming_fsmo
->we_are_master
) {
298 dn_str
= ldb_dn_get_linearized(samdb_partitions_dn(ldb
, msg
));
299 if (dn_str
&& dn_str
[0]) {
300 if (ldb_msg_add_fmt(msg
, "validFSMOs", "%s", dn_str
) != 0) {
306 pdc_fsmo
= talloc_get_type(ldb_get_opaque(ldb
, "dsdb_pdc_fsmo"),
307 struct dsdb_pdc_fsmo
);
308 if (pdc_fsmo
&& pdc_fsmo
->we_are_master
) {
309 dn_str
= ldb_dn_get_linearized(samdb_base_dn(ldb
));
310 if (dn_str
&& dn_str
[0]) {
311 if (ldb_msg_add_fmt(msg
, "validFSMOs", "%s", dn_str
) != 0) {
318 if (do_attribute_explicit(attrs
, "vendorVersion")) {
319 if (ldb_msg_add_fmt(msg
, "vendorVersion",
320 "%s", SAMBA_VERSION_STRING
) != 0) {
325 if (priv
&& do_attribute(attrs
, "domainFunctionality")
326 && (val
= talloc_get_type(ldb_get_opaque(ldb
, "domainFunctionality"), int))) {
327 if (ldb_msg_add_fmt(msg
, "domainFunctionality",
333 if (priv
&& do_attribute(attrs
, "forestFunctionality")
334 && (val
= talloc_get_type(ldb_get_opaque(ldb
, "forestFunctionality"), int))) {
335 if (ldb_msg_add_fmt(msg
, "forestFunctionality",
341 if (priv
&& do_attribute(attrs
, "domainControllerFunctionality")
342 && (val
= talloc_get_type(ldb_get_opaque(ldb
, "domainControllerFunctionality"), int))) {
343 if (ldb_msg_add_fmt(msg
, "domainControllerFunctionality",
349 edn_control
= ldb_request_get_control(req
, LDB_CONTROL_EXTENDED_DN_OID
);
351 /* if the client sent us the EXTENDED_DN control then we need
352 to expand the DNs to have GUID and SID. W2K8 join relies on
356 for (i
=0; dn_attrs
[i
]; i
++) {
357 if (!do_attribute(attrs
, dn_attrs
[i
])) continue;
358 ret
= expand_dn_in_message(module
, msg
, dn_attrs
[i
],
360 if (ret
!= LDB_SUCCESS
) {
361 DEBUG(0,(__location__
": Failed to expand DN in rootDSE for %s\n",
369 /* TODO: lots more dynamic attributes should be added here */
374 return LDB_ERR_OPERATIONS_ERROR
;
378 handle search requests
381 struct rootdse_context
{
382 struct ldb_module
*module
;
383 struct ldb_request
*req
;
386 static struct rootdse_context
*rootdse_init_context(struct ldb_module
*module
,
387 struct ldb_request
*req
)
389 struct ldb_context
*ldb
;
390 struct rootdse_context
*ac
;
392 ldb
= ldb_module_get_ctx(module
);
394 ac
= talloc_zero(req
, struct rootdse_context
);
396 ldb_set_errstring(ldb
, "Out of Memory");
406 static int rootdse_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
408 struct rootdse_context
*ac
;
411 ac
= talloc_get_type(req
->context
, struct rootdse_context
);
414 return ldb_module_done(ac
->req
, NULL
, NULL
,
415 LDB_ERR_OPERATIONS_ERROR
);
417 if (ares
->error
!= LDB_SUCCESS
) {
418 return ldb_module_done(ac
->req
, ares
->controls
,
419 ares
->response
, ares
->error
);
422 switch (ares
->type
) {
423 case LDB_REPLY_ENTRY
:
425 * if the client explicit asks for the 'netlogon' attribute
426 * the reply_entry needs to be skipped
428 if (ac
->req
->op
.search
.attrs
&&
429 ldb_attr_in_list(ac
->req
->op
.search
.attrs
, "netlogon")) {
434 /* for each record returned post-process to add any dynamic
435 attributes that have been asked for */
436 ret
= rootdse_add_dynamic(ac
->module
, ares
->message
,
437 ac
->req
->op
.search
.attrs
, ac
->req
);
438 if (ret
!= LDB_SUCCESS
) {
440 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
443 return ldb_module_send_entry(ac
->req
, ares
->message
, ares
->controls
);
445 case LDB_REPLY_REFERRAL
:
446 /* should we allow the backend to return referrals in this case
451 return ldb_module_done(ac
->req
, ares
->controls
,
452 ares
->response
, ares
->error
);
459 static int rootdse_search(struct ldb_module
*module
, struct ldb_request
*req
)
461 struct ldb_context
*ldb
;
462 struct rootdse_context
*ac
;
463 struct ldb_request
*down_req
;
466 ldb
= ldb_module_get_ctx(module
);
468 /* see if its for the rootDSE - only a base search on the "" DN qualifies */
469 if (!(req
->op
.search
.scope
== LDB_SCOPE_BASE
&& ldb_dn_is_null(req
->op
.search
.base
))) {
470 /* Otherwise, pass down to the rest of the stack */
471 return ldb_next_request(module
, req
);
474 ac
= rootdse_init_context(module
, req
);
476 return LDB_ERR_OPERATIONS_ERROR
;
479 /* in our db we store the rootDSE with a DN of @ROOTDSE */
480 ret
= ldb_build_search_req(&down_req
, ldb
, ac
,
481 ldb_dn_new(ac
, ldb
, "@ROOTDSE"),
484 req
->op
.search
.attrs
,
485 NULL
,/* for now skip the controls from the client */
486 ac
, rootdse_callback
,
488 if (ret
!= LDB_SUCCESS
) {
492 return ldb_next_request(module
, down_req
);
495 static int rootdse_register_control(struct ldb_module
*module
, struct ldb_request
*req
)
497 struct private_data
*priv
= talloc_get_type(ldb_module_get_private(module
), struct private_data
);
500 list
= talloc_realloc(priv
, priv
->controls
, char *, priv
->num_controls
+ 1);
502 return LDB_ERR_OPERATIONS_ERROR
;
505 list
[priv
->num_controls
] = talloc_strdup(list
, req
->op
.reg_control
.oid
);
506 if (!list
[priv
->num_controls
]) {
507 return LDB_ERR_OPERATIONS_ERROR
;
510 priv
->num_controls
+= 1;
511 priv
->controls
= list
;
513 return ldb_module_done(req
, NULL
, NULL
, LDB_SUCCESS
);
516 static int rootdse_register_partition(struct ldb_module
*module
, struct ldb_request
*req
)
518 struct private_data
*priv
= talloc_get_type(ldb_module_get_private(module
), struct private_data
);
519 struct ldb_dn
**list
;
521 list
= talloc_realloc(priv
, priv
->partitions
, struct ldb_dn
*, priv
->num_partitions
+ 1);
523 return LDB_ERR_OPERATIONS_ERROR
;
526 list
[priv
->num_partitions
] = ldb_dn_copy(list
, req
->op
.reg_partition
.dn
);
527 if (!list
[priv
->num_partitions
]) {
528 return LDB_ERR_OPERATIONS_ERROR
;
531 priv
->num_partitions
+= 1;
532 priv
->partitions
= list
;
534 return ldb_module_done(req
, NULL
, NULL
, LDB_SUCCESS
);
538 static int rootdse_request(struct ldb_module
*module
, struct ldb_request
*req
)
540 switch (req
->operation
) {
542 case LDB_REQ_REGISTER_CONTROL
:
543 return rootdse_register_control(module
, req
);
544 case LDB_REQ_REGISTER_PARTITION
:
545 return rootdse_register_partition(module
, req
);
550 return ldb_next_request(module
, req
);
553 static int rootdse_init(struct ldb_module
*module
)
556 struct ldb_context
*ldb
;
557 struct ldb_result
*res
;
558 struct private_data
*data
;
559 const char *attrs
[] = { "msDS-Behavior-Version", NULL
};
560 const char *ds_attrs
[] = { "dsServiceName", NULL
};
563 ldb
= ldb_module_get_ctx(module
);
565 data
= talloc_zero(module
, struct private_data
);
570 data
->num_controls
= 0;
571 data
->controls
= NULL
;
572 data
->num_partitions
= 0;
573 data
->partitions
= NULL
;
574 ldb_module_set_private(module
, data
);
576 ldb_set_default_dns(ldb
);
578 ret
= ldb_next_init(module
);
584 mem_ctx
= talloc_new(data
);
587 return LDB_ERR_OPERATIONS_ERROR
;
590 /* Now that the partitions are set up, do a search for:
591 - domainControllerFunctionality
592 - domainFunctionality
593 - forestFunctionality
595 Then stuff these values into an opaque
597 ret
= ldb_search(ldb
, mem_ctx
, &res
,
598 ldb_get_default_basedn(ldb
),
599 LDB_SCOPE_BASE
, attrs
, NULL
);
600 if (ret
== LDB_SUCCESS
&& res
->count
== 1) {
601 int domain_behaviour_version
602 = ldb_msg_find_attr_as_int(res
->msgs
[0],
603 "msDS-Behavior-Version", -1);
604 if (domain_behaviour_version
!= -1) {
605 int *val
= talloc(ldb
, int);
608 talloc_free(mem_ctx
);
609 return LDB_ERR_OPERATIONS_ERROR
;
611 *val
= domain_behaviour_version
;
612 ret
= ldb_set_opaque(ldb
, "domainFunctionality", val
);
613 if (ret
!= LDB_SUCCESS
) {
614 talloc_free(mem_ctx
);
620 ret
= ldb_search(ldb
, mem_ctx
, &res
,
621 samdb_partitions_dn(ldb
, mem_ctx
),
622 LDB_SCOPE_BASE
, attrs
, NULL
);
623 if (ret
== LDB_SUCCESS
&& res
->count
== 1) {
624 int forest_behaviour_version
625 = ldb_msg_find_attr_as_int(res
->msgs
[0],
626 "msDS-Behavior-Version", -1);
627 if (forest_behaviour_version
!= -1) {
628 int *val
= talloc(ldb
, int);
631 talloc_free(mem_ctx
);
632 return LDB_ERR_OPERATIONS_ERROR
;
634 *val
= forest_behaviour_version
;
635 ret
= ldb_set_opaque(ldb
, "forestFunctionality", val
);
636 if (ret
!= LDB_SUCCESS
) {
637 talloc_free(mem_ctx
);
643 ret
= ldb_search(ldb
, mem_ctx
, &res
,
644 ldb_dn_new(mem_ctx
, ldb
, ""),
645 LDB_SCOPE_BASE
, ds_attrs
, NULL
);
646 if (ret
== LDB_SUCCESS
&& res
->count
== 1) {
648 = ldb_msg_find_attr_as_dn(ldb
, mem_ctx
, res
->msgs
[0],
651 ret
= ldb_search(ldb
, mem_ctx
, &res
, ds_dn
,
652 LDB_SCOPE_BASE
, attrs
, NULL
);
653 if (ret
== LDB_SUCCESS
&& res
->count
== 1) {
654 int domain_controller_behaviour_version
655 = ldb_msg_find_attr_as_int(res
->msgs
[0],
656 "msDS-Behavior-Version", -1);
657 if (domain_controller_behaviour_version
!= -1) {
658 int *val
= talloc(ldb
, int);
661 talloc_free(mem_ctx
);
662 return LDB_ERR_OPERATIONS_ERROR
;
664 *val
= domain_controller_behaviour_version
;
665 ret
= ldb_set_opaque(ldb
,
666 "domainControllerFunctionality", val
);
667 if (ret
!= LDB_SUCCESS
) {
668 talloc_free(mem_ctx
);
676 talloc_free(mem_ctx
);
681 static int rootdse_modify(struct ldb_module
*module
, struct ldb_request
*req
)
683 struct ldb_context
*ldb
;
684 struct ldb_result
*ext_res
;
686 struct ldb_dn
*schema_dn
;
687 struct ldb_message_element
*schemaUpdateNowAttr
;
690 If dn is not "" we should let it pass through
692 if (!ldb_dn_is_null(req
->op
.mod
.message
->dn
)) {
693 return ldb_next_request(module
, req
);
696 ldb
= ldb_module_get_ctx(module
);
699 dn is empty so check for schemaUpdateNow attribute
700 "The type of modification and values specified in the LDAP modify operation do not matter." MSDN
702 schemaUpdateNowAttr
= ldb_msg_find_element(req
->op
.mod
.message
, "schemaUpdateNow");
703 if (!schemaUpdateNowAttr
) {
704 return LDB_ERR_OPERATIONS_ERROR
;
707 schema_dn
= samdb_schema_dn(ldb
);
709 ldb_reset_err_string(ldb
);
710 ldb_debug(ldb
, LDB_DEBUG_WARNING
,
711 "rootdse_modify: no schema dn present: (skip ldb_extended call)\n");
712 return ldb_next_request(module
, req
);
715 ret
= ldb_extended(ldb
, DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID
, schema_dn
, &ext_res
);
716 if (ret
!= LDB_SUCCESS
) {
717 return LDB_ERR_OPERATIONS_ERROR
;
720 talloc_free(ext_res
);
721 return ldb_request_done(req
, ret
);
724 _PUBLIC_
const struct ldb_module_ops ldb_rootdse_module_ops
= {
726 .init_context
= rootdse_init
,
727 .search
= rootdse_search
,
728 .request
= rootdse_request
,
729 .modify
= rootdse_modify