• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/samba-3.5.8/source4/dsdb/samdb/ldb_modules/
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
30struct private_data {
31	int num_controls;
32	char **controls;
33	int num_partitions;
34	struct ldb_dn **partitions;
35};
36
37/*
38  return 1 if a specific attribute has been requested
39*/
40static int do_attribute(const char * const *attrs, const char *name)
41{
42	return attrs == NULL ||
43		ldb_attr_in_list(attrs, name) ||
44		ldb_attr_in_list(attrs, "*");
45}
46
47static int do_attribute_explicit(const char * const *attrs, const char *name)
48{
49	return attrs != NULL && ldb_attr_in_list(attrs, name);
50}
51
52
53/*
54  expand a DN attribute to include extended DN information if requested
55 */
56static 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)
59{
60	struct ldb_dn *dn, *dn2;
61	struct ldb_val *v;
62	int ret;
63	struct ldb_request *req2;
64	char *dn_string;
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;
70	int edn_type = 0;
71
72	ldb = ldb_module_get_ctx(module);
73
74	edn = talloc_get_type(edn_control->data, struct ldb_extended_dn_control);
75	if (edn) {
76		edn_type = edn->type;
77	}
78
79	v = discard_const_p(struct ldb_val, ldb_msg_find_ldb_val(msg, attrname));
80	if (v == NULL) {
81		talloc_free(tmp_ctx);
82		return 0;
83	}
84
85	dn_string = talloc_strndup(tmp_ctx, (const char *)v->data, v->length);
86	if (dn_string == NULL) {
87		talloc_free(tmp_ctx);
88		return LDB_ERR_OPERATIONS_ERROR;
89	}
90
91	res = talloc_zero(tmp_ctx, struct ldb_result);
92	if (res == NULL) {
93		talloc_free(tmp_ctx);
94		return LDB_ERR_OPERATIONS_ERROR;
95	}
96
97	dn = ldb_dn_new(tmp_ctx, ldb, dn_string);
98	if (!ldb_dn_validate(dn)) {
99		talloc_free(tmp_ctx);
100		return LDB_ERR_OPERATIONS_ERROR;
101	}
102
103	ret = ldb_build_search_req(&req2, ldb, tmp_ctx,
104				   dn,
105				   LDB_SCOPE_BASE,
106				   NULL,
107				   no_attrs,
108				   NULL,
109				   res, ldb_search_default_callback,
110				   req);
111	if (ret != LDB_SUCCESS) {
112		talloc_free(tmp_ctx);
113		return ret;
114	}
115
116
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);
122		return ret;
123	}
124
125	ret = ldb_next_request(module, req2);
126	if (ret == LDB_SUCCESS) {
127		ret = ldb_wait(req2->handle, LDB_WAIT_ALL);
128	}
129	if (ret != LDB_SUCCESS) {
130		talloc_free(tmp_ctx);
131		return ret;
132	}
133
134	if (!res || res->count != 1) {
135		talloc_free(tmp_ctx);
136		return LDB_ERR_OPERATIONS_ERROR;
137	}
138
139	dn2 = res->msgs[0]->dn;
140
141	v->data = (uint8_t *)ldb_dn_get_extended_linearized(msg->elements, dn2, edn_type);
142	v->length = strlen((char *)v->data);
143
144	if (v->data == NULL) {
145		talloc_free(tmp_ctx);
146		return LDB_ERR_OPERATIONS_ERROR;
147	}
148
149	talloc_free(tmp_ctx);
150
151	return 0;
152}
153
154
155/*
156  add dynamically generated attributes to rootDSE result
157*/
158static int rootdse_add_dynamic(struct ldb_module *module, struct ldb_message *msg,
159			       const char * const *attrs, struct ldb_request *req)
160{
161	struct ldb_context *ldb;
162	struct private_data *priv = talloc_get_type(ldb_module_get_private(module), struct private_data);
163	char **server_sasl;
164	const struct dsdb_schema *schema;
165	int *val;
166	struct ldb_control *edn_control;
167	const char *dn_attrs[] = {
168		"configurationNamingContext",
169		"defaultNamingContext",
170		"dsServiceName",
171		"rootDomainNamingContext",
172		"schemaNamingContext",
173		"serverName",
174		NULL
175	};
176
177	ldb = ldb_module_get_ctx(module);
178	schema = dsdb_get_schema(ldb);
179
180	msg->dn = ldb_dn_new(msg, ldb, NULL);
181
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");
186
187	if (do_attribute(attrs, "currentTime")) {
188		if (ldb_msg_add_steal_string(msg, "currentTime",
189					     ldb_timestring(msg, time(NULL))) != 0) {
190			goto failed;
191		}
192	}
193
194	if (priv && do_attribute(attrs, "supportedControl")) {
195 		int i;
196		for (i = 0; i < priv->num_controls; i++) {
197			char *control = talloc_strdup(msg, priv->controls[i]);
198			if (!control) {
199				goto failed;
200			}
201			if (ldb_msg_add_steal_string(msg, "supportedControl",
202						     control) != 0) {
203				goto failed;
204 			}
205 		}
206 	}
207
208	if (priv && do_attribute(attrs, "namingContexts")) {
209		int i;
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) {
214				goto failed;
215 			}
216 		}
217	}
218
219	server_sasl = talloc_get_type(ldb_get_opaque(ldb, "supportedSASLMechanims"),
220				       char *);
221	if (server_sasl && do_attribute(attrs, "supportedSASLMechanisms")) {
222		int i;
223		for (i = 0; server_sasl && server_sasl[i]; i++) {
224			char *sasl_name = talloc_strdup(msg, server_sasl[i]);
225			if (!sasl_name) {
226				goto failed;
227			}
228			if (ldb_msg_add_steal_string(msg, "supportedSASLMechanisms",
229						     sasl_name) != 0) {
230				goto failed;
231			}
232		}
233	}
234
235	if (do_attribute(attrs, "highestCommittedUSN")) {
236		uint64_t seq_num;
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) {
241				goto failed;
242			}
243		}
244	}
245
246	if (schema && do_attribute_explicit(attrs, "dsSchemaAttrCount")) {
247		struct dsdb_attribute *cur;
248		uint32_t n = 0;
249
250		for (cur = schema->attributes; cur; cur = cur->next) {
251			n++;
252		}
253
254		if (ldb_msg_add_fmt(msg, "dsSchemaAttrCount",
255				    "%u", n) != 0) {
256			goto failed;
257		}
258	}
259
260	if (schema && do_attribute_explicit(attrs, "dsSchemaClassCount")) {
261		struct dsdb_class *cur;
262		uint32_t n = 0;
263
264		for (cur = schema->classes; cur; cur = cur->next) {
265			n++;
266		}
267
268		if (ldb_msg_add_fmt(msg, "dsSchemaClassCount",
269				    "%u", n) != 0) {
270			goto failed;
271		}
272	}
273
274	if (schema && do_attribute_explicit(attrs, "dsSchemaPrefixCount")) {
275		if (ldb_msg_add_fmt(msg, "dsSchemaPrefixCount",
276				    "%u", schema->num_prefixes) != 0) {
277			goto failed;
278		}
279	}
280
281	if (do_attribute_explicit(attrs, "validFSMOs")) {
282		const struct dsdb_naming_fsmo *naming_fsmo;
283		const struct dsdb_pdc_fsmo *pdc_fsmo;
284		const char *dn_str;
285
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) {
290					goto failed;
291				}
292			}
293		}
294
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) {
301					goto failed;
302				}
303			}
304		}
305
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) {
312					goto failed;
313				}
314			}
315		}
316	}
317
318	if (do_attribute_explicit(attrs, "vendorVersion")) {
319		if (ldb_msg_add_fmt(msg, "vendorVersion",
320				    "%s", SAMBA_VERSION_STRING) != 0) {
321			goto failed;
322		}
323	}
324
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",
328				    "%d", *val) != 0) {
329			goto failed;
330		}
331	}
332
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",
336				    "%d", *val) != 0) {
337			goto failed;
338		}
339	}
340
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",
344				    "%d", *val) != 0) {
345			goto failed;
346		}
347	}
348
349	edn_control = ldb_request_get_control(req, LDB_CONTROL_EXTENDED_DN_OID);
350
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
353	   this */
354	if (edn_control) {
355		int i, ret;
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],
359						   edn_control, req);
360			if (ret != LDB_SUCCESS) {
361				DEBUG(0,(__location__ ": Failed to expand DN in rootDSE for %s\n",
362					 dn_attrs[i]));
363				goto failed;
364			}
365		}
366	}
367
368
369	/* TODO: lots more dynamic attributes should be added here */
370
371	return LDB_SUCCESS;
372
373failed:
374	return LDB_ERR_OPERATIONS_ERROR;
375}
376
377/*
378  handle search requests
379*/
380
381struct rootdse_context {
382	struct ldb_module *module;
383	struct ldb_request *req;
384};
385
386static struct rootdse_context *rootdse_init_context(struct ldb_module *module,
387						    struct ldb_request *req)
388{
389	struct ldb_context *ldb;
390	struct rootdse_context *ac;
391
392	ldb = ldb_module_get_ctx(module);
393
394	ac = talloc_zero(req, struct rootdse_context);
395	if (ac == NULL) {
396		ldb_set_errstring(ldb, "Out of Memory");
397		return NULL;
398	}
399
400	ac->module = module;
401	ac->req = req;
402
403	return ac;
404}
405
406static int rootdse_callback(struct ldb_request *req, struct ldb_reply *ares)
407{
408	struct rootdse_context *ac;
409	int ret;
410
411	ac = talloc_get_type(req->context, struct rootdse_context);
412
413	if (!ares) {
414		return ldb_module_done(ac->req, NULL, NULL,
415					LDB_ERR_OPERATIONS_ERROR);
416	}
417	if (ares->error != LDB_SUCCESS) {
418		return ldb_module_done(ac->req, ares->controls,
419					ares->response, ares->error);
420	}
421
422	switch (ares->type) {
423	case LDB_REPLY_ENTRY:
424		/*
425		 * if the client explicit asks for the 'netlogon' attribute
426		 * the reply_entry needs to be skipped
427		 */
428		if (ac->req->op.search.attrs &&
429		    ldb_attr_in_list(ac->req->op.search.attrs, "netlogon")) {
430			talloc_free(ares);
431			return LDB_SUCCESS;
432		}
433
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) {
439			talloc_free(ares);
440			return ldb_module_done(ac->req, NULL, NULL, ret);
441		}
442
443		return ldb_module_send_entry(ac->req, ares->message, ares->controls);
444
445	case LDB_REPLY_REFERRAL:
446		/* should we allow the backend to return referrals in this case
447		 * ?? */
448		break;
449
450	case LDB_REPLY_DONE:
451		return ldb_module_done(ac->req, ares->controls,
452					ares->response, ares->error);
453	}
454
455	talloc_free(ares);
456	return LDB_SUCCESS;
457}
458
459static int rootdse_search(struct ldb_module *module, struct ldb_request *req)
460{
461	struct ldb_context *ldb;
462	struct rootdse_context *ac;
463	struct ldb_request *down_req;
464	int ret;
465
466	ldb = ldb_module_get_ctx(module);
467
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);
472	}
473
474	ac = rootdse_init_context(module, req);
475	if (ac == NULL) {
476		return LDB_ERR_OPERATIONS_ERROR;
477	}
478
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"),
482					LDB_SCOPE_BASE,
483					NULL,
484					req->op.search.attrs,
485					NULL,/* for now skip the controls from the client */
486					ac, rootdse_callback,
487					req);
488	if (ret != LDB_SUCCESS) {
489		return ret;
490	}
491
492	return ldb_next_request(module, down_req);
493}
494
495static int rootdse_register_control(struct ldb_module *module, struct ldb_request *req)
496{
497	struct private_data *priv = talloc_get_type(ldb_module_get_private(module), struct private_data);
498	char **list;
499
500	list = talloc_realloc(priv, priv->controls, char *, priv->num_controls + 1);
501	if (!list) {
502		return LDB_ERR_OPERATIONS_ERROR;
503	}
504
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;
508	}
509
510	priv->num_controls += 1;
511	priv->controls = list;
512
513	return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
514}
515
516static int rootdse_register_partition(struct ldb_module *module, struct ldb_request *req)
517{
518	struct private_data *priv = talloc_get_type(ldb_module_get_private(module), struct private_data);
519	struct ldb_dn **list;
520
521	list = talloc_realloc(priv, priv->partitions, struct ldb_dn *, priv->num_partitions + 1);
522	if (!list) {
523		return LDB_ERR_OPERATIONS_ERROR;
524	}
525
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;
529	}
530
531	priv->num_partitions += 1;
532	priv->partitions = list;
533
534	return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
535}
536
537
538static int rootdse_request(struct ldb_module *module, struct ldb_request *req)
539{
540	switch (req->operation) {
541
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);
546
547	default:
548		break;
549	}
550	return ldb_next_request(module, req);
551}
552
553static int rootdse_init(struct ldb_module *module)
554{
555	int ret;
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 };
561	TALLOC_CTX *mem_ctx;
562
563	ldb = ldb_module_get_ctx(module);
564
565	data = talloc_zero(module, struct private_data);
566	if (data == NULL) {
567		return -1;
568	}
569
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);
575
576	ldb_set_default_dns(ldb);
577
578	ret = ldb_next_init(module);
579
580	if (ret) {
581		return ret;
582	}
583
584	mem_ctx = talloc_new(data);
585	if (!mem_ctx) {
586		ldb_oom(ldb);
587		return LDB_ERR_OPERATIONS_ERROR;
588	}
589
590	/* Now that the partitions are set up, do a search for:
591	   - domainControllerFunctionality
592	   - domainFunctionality
593	   - forestFunctionality
594
595	   Then stuff these values into an opaque
596	*/
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);
606			if (!val) {
607				ldb_oom(ldb);
608				talloc_free(mem_ctx);
609				return LDB_ERR_OPERATIONS_ERROR;
610			}
611			*val = domain_behaviour_version;
612			ret = ldb_set_opaque(ldb, "domainFunctionality", val);
613			if (ret != LDB_SUCCESS) {
614				talloc_free(mem_ctx);
615				return ret;
616			}
617		}
618	}
619
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);
629			if (!val) {
630				ldb_oom(ldb);
631				talloc_free(mem_ctx);
632				return LDB_ERR_OPERATIONS_ERROR;
633			}
634			*val = forest_behaviour_version;
635			ret = ldb_set_opaque(ldb, "forestFunctionality", val);
636			if (ret != LDB_SUCCESS) {
637				talloc_free(mem_ctx);
638				return ret;
639			}
640		}
641	}
642
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) {
647		struct ldb_dn *ds_dn
648			= ldb_msg_find_attr_as_dn(ldb, mem_ctx, res->msgs[0],
649						  "dsServiceName");
650		if (ds_dn) {
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);
659					if (!val) {
660						ldb_oom(ldb);
661						talloc_free(mem_ctx);
662					return LDB_ERR_OPERATIONS_ERROR;
663					}
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);
669						return ret;
670					}
671				}
672			}
673		}
674	}
675
676	talloc_free(mem_ctx);
677
678	return LDB_SUCCESS;
679}
680
681static int rootdse_modify(struct ldb_module *module, struct ldb_request *req)
682{
683	struct ldb_context *ldb;
684	struct ldb_result *ext_res;
685	int ret;
686	struct ldb_dn *schema_dn;
687	struct ldb_message_element *schemaUpdateNowAttr;
688
689	/*
690		If dn is not "" we should let it pass through
691	*/
692	if (!ldb_dn_is_null(req->op.mod.message->dn)) {
693		return ldb_next_request(module, req);
694	}
695
696	ldb = ldb_module_get_ctx(module);
697
698	/*
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
701	*/
702	schemaUpdateNowAttr = ldb_msg_find_element(req->op.mod.message, "schemaUpdateNow");
703	if (!schemaUpdateNowAttr) {
704		return LDB_ERR_OPERATIONS_ERROR;
705	}
706
707	schema_dn = samdb_schema_dn(ldb);
708	if (!schema_dn) {
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);
713	}
714
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;
718	}
719
720	talloc_free(ext_res);
721	return ldb_request_done(req, ret);
722}
723
724_PUBLIC_ const struct ldb_module_ops ldb_rootdse_module_ops = {
725	.name			= "rootdse",
726	.init_context   = rootdse_init,
727	.search         = rootdse_search,
728	.request		= rootdse_request,
729	.modify         = rootdse_modify
730};
731