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 add dynamically generated attributes to rootDSE result
56 static int rootdse_add_dynamic(struct ldb_module
*module
, struct ldb_message
*msg
, const char * const *attrs
)
58 struct ldb_context
*ldb
;
59 struct private_data
*priv
= talloc_get_type(ldb_module_get_private(module
), struct private_data
);
61 const struct dsdb_schema
*schema
;
63 ldb
= ldb_module_get_ctx(module
);
64 schema
= dsdb_get_schema(ldb
);
66 msg
->dn
= ldb_dn_new(msg
, ldb
, NULL
);
68 /* don't return the distinduishedName, cn and name attributes */
69 ldb_msg_remove_attr(msg
, "distinguishedName");
70 ldb_msg_remove_attr(msg
, "cn");
71 ldb_msg_remove_attr(msg
, "name");
73 if (do_attribute(attrs
, "currentTime")) {
74 if (ldb_msg_add_steal_string(msg
, "currentTime",
75 ldb_timestring(msg
, time(NULL
))) != 0) {
80 if (do_attribute(attrs
, "supportedControl")) {
82 for (i
= 0; i
< priv
->num_controls
; i
++) {
83 char *control
= talloc_strdup(msg
, priv
->controls
[i
]);
87 if (ldb_msg_add_steal_string(msg
, "supportedControl",
94 if (do_attribute(attrs
, "namingContexts")) {
96 for (i
= 0; i
< priv
->num_partitions
; i
++) {
97 struct ldb_dn
*dn
= priv
->partitions
[i
];
98 if (ldb_msg_add_steal_string(msg
, "namingContexts",
99 ldb_dn_alloc_linearized(msg
, dn
)) != 0) {
105 server_sasl
= talloc_get_type(ldb_get_opaque(ldb
, "supportedSASLMechanims"),
107 if (server_sasl
&& do_attribute(attrs
, "supportedSASLMechanisms")) {
109 for (i
= 0; server_sasl
&& server_sasl
[i
]; i
++) {
110 char *sasl_name
= talloc_strdup(msg
, server_sasl
[i
]);
114 if (ldb_msg_add_steal_string(msg
, "supportedSASLMechanisms",
121 if (do_attribute(attrs
, "highestCommittedUSN")) {
123 int ret
= ldb_sequence_number(ldb
, LDB_SEQ_HIGHEST_SEQ
, &seq_num
);
124 if (ret
== LDB_SUCCESS
) {
125 if (ldb_msg_add_fmt(msg
, "highestCommittedUSN",
126 "%llu", (unsigned long long)seq_num
) != 0) {
132 if (schema
&& do_attribute_explicit(attrs
, "dsSchemaAttrCount")) {
133 struct dsdb_attribute
*cur
;
136 for (cur
= schema
->attributes
; cur
; cur
= cur
->next
) {
140 if (ldb_msg_add_fmt(msg
, "dsSchemaAttrCount",
146 if (schema
&& do_attribute_explicit(attrs
, "dsSchemaClassCount")) {
147 struct dsdb_class
*cur
;
150 for (cur
= schema
->classes
; cur
; cur
= cur
->next
) {
154 if (ldb_msg_add_fmt(msg
, "dsSchemaClassCount",
160 if (schema
&& do_attribute_explicit(attrs
, "dsSchemaPrefixCount")) {
161 if (ldb_msg_add_fmt(msg
, "dsSchemaPrefixCount",
162 "%u", schema
->num_prefixes
) != 0) {
167 if (do_attribute_explicit(attrs
, "validFSMOs")) {
168 const struct dsdb_naming_fsmo
*naming_fsmo
;
169 const struct dsdb_pdc_fsmo
*pdc_fsmo
;
172 if (schema
&& schema
->fsmo
.we_are_master
) {
173 dn_str
= ldb_dn_get_linearized(samdb_schema_dn(ldb
));
174 if (dn_str
&& dn_str
[0]) {
175 if (ldb_msg_add_fmt(msg
, "validFSMOs", "%s", dn_str
) != 0) {
181 naming_fsmo
= talloc_get_type(ldb_get_opaque(ldb
, "dsdb_naming_fsmo"),
182 struct dsdb_naming_fsmo
);
183 if (naming_fsmo
&& naming_fsmo
->we_are_master
) {
184 dn_str
= ldb_dn_get_linearized(samdb_partitions_dn(ldb
, msg
));
185 if (dn_str
&& dn_str
[0]) {
186 if (ldb_msg_add_fmt(msg
, "validFSMOs", "%s", dn_str
) != 0) {
192 pdc_fsmo
= talloc_get_type(ldb_get_opaque(ldb
, "dsdb_pdc_fsmo"),
193 struct dsdb_pdc_fsmo
);
194 if (pdc_fsmo
&& pdc_fsmo
->we_are_master
) {
195 dn_str
= ldb_dn_get_linearized(samdb_base_dn(ldb
));
196 if (dn_str
&& dn_str
[0]) {
197 if (ldb_msg_add_fmt(msg
, "validFSMOs", "%s", dn_str
) != 0) {
204 if (schema
&& do_attribute_explicit(attrs
, "vendorVersion")) {
205 if (ldb_msg_add_fmt(msg
, "vendorVersion",
206 "%s", SAMBA_VERSION_STRING
) != 0) {
211 /* TODO: lots more dynamic attributes should be added here */
216 return LDB_ERR_OPERATIONS_ERROR
;
220 handle search requests
223 struct rootdse_context
{
224 struct ldb_module
*module
;
225 struct ldb_request
*req
;
228 static struct rootdse_context
*rootdse_init_context(struct ldb_module
*module
,
229 struct ldb_request
*req
)
231 struct ldb_context
*ldb
;
232 struct rootdse_context
*ac
;
234 ldb
= ldb_module_get_ctx(module
);
236 ac
= talloc_zero(req
, struct rootdse_context
);
238 ldb_set_errstring(ldb
, "Out of Memory");
248 static int rootdse_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
250 struct rootdse_context
*ac
;
253 ac
= talloc_get_type(req
->context
, struct rootdse_context
);
256 return ldb_module_done(ac
->req
, NULL
, NULL
,
257 LDB_ERR_OPERATIONS_ERROR
);
259 if (ares
->error
!= LDB_SUCCESS
) {
260 return ldb_module_done(ac
->req
, ares
->controls
,
261 ares
->response
, ares
->error
);
264 switch (ares
->type
) {
265 case LDB_REPLY_ENTRY
:
267 * if the client explicit asks for the 'netlogon' attribute
268 * the reply_entry needs to be skipped
270 if (ac
->req
->op
.search
.attrs
&&
271 ldb_attr_in_list(ac
->req
->op
.search
.attrs
, "netlogon")) {
276 /* for each record returned post-process to add any dynamic
277 attributes that have been asked for */
278 ret
= rootdse_add_dynamic(ac
->module
, ares
->message
,
279 ac
->req
->op
.search
.attrs
);
280 if (ret
!= LDB_SUCCESS
) {
282 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
285 return ldb_module_send_entry(ac
->req
, ares
->message
, ares
->controls
);
287 case LDB_REPLY_REFERRAL
:
288 /* should we allow the backend to return referrals in this case
293 return ldb_module_done(ac
->req
, ares
->controls
,
294 ares
->response
, ares
->error
);
301 static int rootdse_search(struct ldb_module
*module
, struct ldb_request
*req
)
303 struct ldb_context
*ldb
;
304 struct rootdse_context
*ac
;
305 struct ldb_request
*down_req
;
308 ldb
= ldb_module_get_ctx(module
);
310 /* see if its for the rootDSE - only a base search on the "" DN qualifies */
311 if (!(req
->op
.search
.scope
== LDB_SCOPE_BASE
&& ldb_dn_is_null(req
->op
.search
.base
))) {
312 /* Otherwise, pass down to the rest of the stack */
313 return ldb_next_request(module
, req
);
316 ac
= rootdse_init_context(module
, req
);
318 return LDB_ERR_OPERATIONS_ERROR
;
321 /* in our db we store the rootDSE with a DN of @ROOTDSE */
322 ret
= ldb_build_search_req(&down_req
, ldb
, ac
,
323 ldb_dn_new(ac
, ldb
, "@ROOTDSE"),
326 req
->op
.search
.attrs
,
327 NULL
,/* for now skip the controls from the client */
328 ac
, rootdse_callback
,
330 if (ret
!= LDB_SUCCESS
) {
334 return ldb_next_request(module
, down_req
);
337 static int rootdse_register_control(struct ldb_module
*module
, struct ldb_request
*req
)
339 struct private_data
*priv
= talloc_get_type(ldb_module_get_private(module
), struct private_data
);
342 list
= talloc_realloc(priv
, priv
->controls
, char *, priv
->num_controls
+ 1);
344 return LDB_ERR_OPERATIONS_ERROR
;
347 list
[priv
->num_controls
] = talloc_strdup(list
, req
->op
.reg_control
.oid
);
348 if (!list
[priv
->num_controls
]) {
349 return LDB_ERR_OPERATIONS_ERROR
;
352 priv
->num_controls
+= 1;
353 priv
->controls
= list
;
355 return ldb_module_done(req
, NULL
, NULL
, LDB_SUCCESS
);
358 static int rootdse_register_partition(struct ldb_module
*module
, struct ldb_request
*req
)
360 struct private_data
*priv
= talloc_get_type(ldb_module_get_private(module
), struct private_data
);
361 struct ldb_dn
**list
;
363 list
= talloc_realloc(priv
, priv
->partitions
, struct ldb_dn
*, priv
->num_partitions
+ 1);
365 return LDB_ERR_OPERATIONS_ERROR
;
368 list
[priv
->num_partitions
] = ldb_dn_copy(list
, req
->op
.reg_partition
.dn
);
369 if (!list
[priv
->num_partitions
]) {
370 return LDB_ERR_OPERATIONS_ERROR
;
373 priv
->num_partitions
+= 1;
374 priv
->partitions
= list
;
376 return ldb_module_done(req
, NULL
, NULL
, LDB_SUCCESS
);
380 static int rootdse_request(struct ldb_module
*module
, struct ldb_request
*req
)
382 switch (req
->operation
) {
384 case LDB_REQ_REGISTER_CONTROL
:
385 return rootdse_register_control(module
, req
);
386 case LDB_REQ_REGISTER_PARTITION
:
387 return rootdse_register_partition(module
, req
);
392 return ldb_next_request(module
, req
);
395 static int rootdse_init(struct ldb_module
*module
)
397 struct ldb_context
*ldb
;
398 struct private_data
*data
;
400 ldb
= ldb_module_get_ctx(module
);
402 data
= talloc(module
, struct private_data
);
407 data
->num_controls
= 0;
408 data
->controls
= NULL
;
409 data
->num_partitions
= 0;
410 data
->partitions
= NULL
;
411 ldb_module_set_private(module
, data
);
413 ldb_set_default_dns(ldb
);
415 return ldb_next_init(module
);
418 static int rootdse_modify(struct ldb_module
*module
, struct ldb_request
*req
)
420 struct ldb_context
*ldb
;
421 struct ldb_result
*ext_res
;
423 struct ldb_dn
*schema_dn
;
424 struct ldb_message_element
*schemaUpdateNowAttr
;
427 If dn is not "" we should let it pass through
429 if (!ldb_dn_is_null(req
->op
.mod
.message
->dn
)) {
430 return ldb_next_request(module
, req
);
433 ldb
= ldb_module_get_ctx(module
);
436 dn is empty so check for schemaUpdateNow attribute
437 "The type of modification and values specified in the LDAP modify operation do not matter." MSDN
439 schemaUpdateNowAttr
= ldb_msg_find_element(req
->op
.mod
.message
, "schemaUpdateNow");
440 if (!schemaUpdateNowAttr
) {
441 return LDB_ERR_OPERATIONS_ERROR
;
444 schema_dn
= samdb_schema_dn(ldb
);
446 ldb_reset_err_string(ldb
);
447 ldb_debug(ldb
, LDB_DEBUG_WARNING
,
448 "rootdse_modify: no schema dn present: (skip ldb_extended call)\n");
449 return ldb_next_request(module
, req
);
452 ret
= ldb_extended(ldb
, DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID
, schema_dn
, &ext_res
);
453 if (ret
!= LDB_SUCCESS
) {
454 return LDB_ERR_OPERATIONS_ERROR
;
457 talloc_free(ext_res
);
461 _PUBLIC_
const struct ldb_module_ops ldb_rootdse_module_ops
= {
463 .init_context
= rootdse_init
,
464 .search
= rootdse_search
,
465 .request
= rootdse_request
,
466 .modify
= rootdse_modify