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_errors.h"
26 #include "lib/ldb/include/ldb_private.h"
27 #include "system/time.h"
28 #include "dsdb/samdb/samdb.h"
35 struct ldb_dn
**partitions
;
39 return 1 if a specific attribute has been requested
41 static int do_attribute(const char * const *attrs
, const char *name
)
43 return attrs
== NULL
||
44 ldb_attr_in_list(attrs
, name
) ||
45 ldb_attr_in_list(attrs
, "*");
48 static int do_attribute_explicit(const char * const *attrs
, const char *name
)
50 return attrs
!= NULL
&& ldb_attr_in_list(attrs
, name
);
55 add dynamically generated attributes to rootDSE result
57 static int rootdse_add_dynamic(struct ldb_module
*module
, struct ldb_message
*msg
, const char * const *attrs
)
59 struct private_data
*priv
= talloc_get_type(module
->private_data
, struct private_data
);
61 const struct dsdb_schema
*schema
;
63 schema
= dsdb_get_schema(module
->ldb
);
65 msg
->dn
= ldb_dn_new(msg
, module
->ldb
, NULL
);
67 /* don't return the distinduishedName, cn and name attributes */
68 ldb_msg_remove_attr(msg
, "distinguishedName");
69 ldb_msg_remove_attr(msg
, "cn");
70 ldb_msg_remove_attr(msg
, "name");
72 if (do_attribute(attrs
, "currentTime")) {
73 if (ldb_msg_add_steal_string(msg
, "currentTime",
74 ldb_timestring(msg
, time(NULL
))) != 0) {
79 if (do_attribute(attrs
, "supportedControl")) {
81 for (i
= 0; i
< priv
->num_controls
; i
++) {
82 char *control
= talloc_strdup(msg
, priv
->controls
[i
]);
86 if (ldb_msg_add_steal_string(msg
, "supportedControl",
93 if (do_attribute(attrs
, "namingContexts")) {
95 for (i
= 0; i
< priv
->num_partitions
; i
++) {
96 struct ldb_dn
*dn
= priv
->partitions
[i
];
97 if (ldb_msg_add_steal_string(msg
, "namingContexts",
98 ldb_dn_alloc_linearized(msg
, dn
)) != 0) {
104 server_sasl
= talloc_get_type(ldb_get_opaque(module
->ldb
, "supportedSASLMechanims"),
106 if (server_sasl
&& do_attribute(attrs
, "supportedSASLMechanisms")) {
108 for (i
= 0; server_sasl
&& server_sasl
[i
]; i
++) {
109 char *sasl_name
= talloc_strdup(msg
, server_sasl
[i
]);
113 if (ldb_msg_add_steal_string(msg
, "supportedSASLMechanisms",
120 if (do_attribute(attrs
, "highestCommittedUSN")) {
122 int ret
= ldb_sequence_number(module
->ldb
, LDB_SEQ_HIGHEST_SEQ
, &seq_num
);
123 if (ret
== LDB_SUCCESS
) {
124 if (ldb_msg_add_fmt(msg
, "highestCommittedUSN",
125 "%llu", (unsigned long long)seq_num
) != 0) {
131 if (schema
&& do_attribute_explicit(attrs
, "dsSchemaAttrCount")) {
132 struct dsdb_attribute
*cur
;
135 for (cur
= schema
->attributes
; cur
; cur
= cur
->next
) {
139 if (ldb_msg_add_fmt(msg
, "dsSchemaAttrCount",
145 if (schema
&& do_attribute_explicit(attrs
, "dsSchemaClassCount")) {
146 struct dsdb_class
*cur
;
149 for (cur
= schema
->classes
; cur
; cur
= cur
->next
) {
153 if (ldb_msg_add_fmt(msg
, "dsSchemaClassCount",
159 if (schema
&& do_attribute_explicit(attrs
, "dsSchemaPrefixCount")) {
160 if (ldb_msg_add_fmt(msg
, "dsSchemaPrefixCount",
161 "%u", schema
->num_prefixes
) != 0) {
166 if (do_attribute_explicit(attrs
, "validFSMOs")) {
167 const struct dsdb_naming_fsmo
*naming_fsmo
;
168 const struct dsdb_pdc_fsmo
*pdc_fsmo
;
171 if (schema
&& schema
->fsmo
.we_are_master
) {
172 dn_str
= ldb_dn_get_linearized(samdb_schema_dn(module
->ldb
));
173 if (dn_str
&& dn_str
[0]) {
174 if (ldb_msg_add_fmt(msg
, "validFSMOs", "%s", dn_str
) != 0) {
180 naming_fsmo
= talloc_get_type(ldb_get_opaque(module
->ldb
, "dsdb_naming_fsmo"),
181 struct dsdb_naming_fsmo
);
182 if (naming_fsmo
&& naming_fsmo
->we_are_master
) {
183 dn_str
= ldb_dn_get_linearized(samdb_partitions_dn(module
->ldb
, msg
));
184 if (dn_str
&& dn_str
[0]) {
185 if (ldb_msg_add_fmt(msg
, "validFSMOs", "%s", dn_str
) != 0) {
191 pdc_fsmo
= talloc_get_type(ldb_get_opaque(module
->ldb
, "dsdb_pdc_fsmo"),
192 struct dsdb_pdc_fsmo
);
193 if (pdc_fsmo
&& pdc_fsmo
->we_are_master
) {
194 dn_str
= ldb_dn_get_linearized(samdb_base_dn(module
->ldb
));
195 if (dn_str
&& dn_str
[0]) {
196 if (ldb_msg_add_fmt(msg
, "validFSMOs", "%s", dn_str
) != 0) {
203 if (schema
&& do_attribute_explicit(attrs
, "vendorVersion")) {
204 if (ldb_msg_add_fmt(msg
, "vendorVersion",
205 "%s", SAMBA_VERSION_STRING
) != 0) {
210 /* TODO: lots more dynamic attributes should be added here */
215 return LDB_ERR_OPERATIONS_ERROR
;
219 handle search requests
222 struct rootdse_context
{
223 struct ldb_module
*module
;
224 struct ldb_request
*req
;
227 static struct rootdse_context
*rootdse_init_context(struct ldb_module
*module
,
228 struct ldb_request
*req
)
230 struct rootdse_context
*ac
;
232 ac
= talloc_zero(req
, struct rootdse_context
);
234 ldb_set_errstring(module
->ldb
, "Out of Memory");
244 static int rootdse_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
246 struct rootdse_context
*ac
;
249 ac
= talloc_get_type(req
->context
, struct rootdse_context
);
252 return ldb_module_done(ac
->req
, NULL
, NULL
,
253 LDB_ERR_OPERATIONS_ERROR
);
255 if (ares
->error
!= LDB_SUCCESS
) {
256 return ldb_module_done(ac
->req
, ares
->controls
,
257 ares
->response
, ares
->error
);
260 switch (ares
->type
) {
261 case LDB_REPLY_ENTRY
:
263 * if the client explicit asks for the 'netlogon' attribute
264 * the reply_entry needs to be skipped
266 if (ac
->req
->op
.search
.attrs
&&
267 ldb_attr_in_list(ac
->req
->op
.search
.attrs
, "netlogon")) {
272 /* for each record returned post-process to add any dynamic
273 attributes that have been asked for */
274 ret
= rootdse_add_dynamic(ac
->module
, ares
->message
,
275 ac
->req
->op
.search
.attrs
);
276 if (ret
!= LDB_SUCCESS
) {
278 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
281 return ldb_module_send_entry(ac
->req
, ares
->message
, ares
->controls
);
283 case LDB_REPLY_REFERRAL
:
284 /* should we allow the backend to return referrals in this case
289 return ldb_module_done(ac
->req
, ares
->controls
,
290 ares
->response
, ares
->error
);
297 static int rootdse_search(struct ldb_module
*module
, struct ldb_request
*req
)
299 struct rootdse_context
*ac
;
300 struct ldb_request
*down_req
;
303 /* see if its for the rootDSE - only a base search on the "" DN qualifies */
304 if (req
->op
.search
.scope
!= LDB_SCOPE_BASE
||
305 ( ! ldb_dn_is_null(req
->op
.search
.base
))) {
306 /* Otherwise, pass down to the rest of the stack */
307 return ldb_next_request(module
, req
);
310 ac
= rootdse_init_context(module
, req
);
312 return LDB_ERR_OPERATIONS_ERROR
;
315 /* in our db we store the rootDSE with a DN of @ROOTDSE */
316 ret
= ldb_build_search_req(&down_req
, module
->ldb
, ac
,
317 ldb_dn_new(ac
, module
->ldb
, "@ROOTDSE"),
320 req
->op
.search
.attrs
,
321 NULL
,/* for now skip the controls from the client */
322 ac
, rootdse_callback
,
324 if (ret
!= LDB_SUCCESS
) {
328 return ldb_next_request(module
, down_req
);
331 static int rootdse_register_control(struct ldb_module
*module
, struct ldb_request
*req
)
333 struct private_data
*priv
= talloc_get_type(module
->private_data
, struct private_data
);
336 list
= talloc_realloc(priv
, priv
->controls
, char *, priv
->num_controls
+ 1);
338 return LDB_ERR_OPERATIONS_ERROR
;
341 list
[priv
->num_controls
] = talloc_strdup(list
, req
->op
.reg_control
.oid
);
342 if (!list
[priv
->num_controls
]) {
343 return LDB_ERR_OPERATIONS_ERROR
;
346 priv
->num_controls
+= 1;
347 priv
->controls
= list
;
349 return ldb_module_done(req
, NULL
, NULL
, LDB_SUCCESS
);
352 static int rootdse_register_partition(struct ldb_module
*module
, struct ldb_request
*req
)
354 struct private_data
*priv
= talloc_get_type(module
->private_data
, struct private_data
);
355 struct ldb_dn
**list
;
357 list
= talloc_realloc(priv
, priv
->partitions
, struct ldb_dn
*, priv
->num_partitions
+ 1);
359 return LDB_ERR_OPERATIONS_ERROR
;
362 list
[priv
->num_partitions
] = ldb_dn_copy(list
, req
->op
.reg_partition
.dn
);
363 if (!list
[priv
->num_partitions
]) {
364 return LDB_ERR_OPERATIONS_ERROR
;
367 priv
->num_partitions
+= 1;
368 priv
->partitions
= list
;
370 return ldb_module_done(req
, NULL
, NULL
, LDB_SUCCESS
);
374 static int rootdse_request(struct ldb_module
*module
, struct ldb_request
*req
)
376 switch (req
->operation
) {
378 case LDB_REQ_REGISTER_CONTROL
:
379 return rootdse_register_control(module
, req
);
380 case LDB_REQ_REGISTER_PARTITION
:
381 return rootdse_register_partition(module
, req
);
386 return ldb_next_request(module
, req
);
389 static int rootdse_init(struct ldb_module
*module
)
391 struct private_data
*data
;
393 data
= talloc(module
, struct private_data
);
398 data
->num_controls
= 0;
399 data
->controls
= NULL
;
400 data
->num_partitions
= 0;
401 data
->partitions
= NULL
;
402 module
->private_data
= data
;
404 ldb_set_default_dns(module
->ldb
);
406 return ldb_next_init(module
);
409 static int rootdse_modify(struct ldb_module
*module
, struct ldb_request
*req
)
411 struct ldb_result
*ext_res
;
413 struct ldb_dn
*schema_dn
;
414 struct ldb_message_element
*schemaUpdateNowAttr
;
417 If dn is not "" we should let it pass through
419 if (!ldb_dn_is_null(req
->op
.mod
.message
->dn
)) {
420 return ldb_next_request(module
, req
);
424 dn is empty so check for schemaUpdateNow attribute
425 "The type of modification and values specified in the LDAP modify operation do not matter." MSDN
427 schemaUpdateNowAttr
= ldb_msg_find_element(req
->op
.mod
.message
, "schemaUpdateNow");
428 if (!schemaUpdateNowAttr
) {
429 return LDB_ERR_OPERATIONS_ERROR
;
432 schema_dn
= samdb_schema_dn(module
->ldb
);
434 ldb_reset_err_string(module
->ldb
);
435 ldb_debug(module
->ldb
, LDB_DEBUG_WARNING
,
436 "rootdse_modify: no schema dn present: (skip ldb_extended call)\n");
437 return ldb_next_request(module
, req
);
440 ret
= ldb_extended(module
->ldb
, DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID
, schema_dn
, &ext_res
);
441 if (ret
!= LDB_SUCCESS
) {
442 return LDB_ERR_OPERATIONS_ERROR
;
445 talloc_free(ext_res
);
449 _PUBLIC_
const struct ldb_module_ops ldb_rootdse_module_ops
= {
451 .init_context
= rootdse_init
,
452 .search
= rootdse_search
,
453 .request
= rootdse_request
,
454 .modify
= rootdse_modify