2 Unix SMB/CIFS mplementation.
5 Copyright (C) Stefan Metzmacher <metze@samba.org> 2006-2007
6 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006-2008
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "lib/util/dlinklist.h"
25 #include "dsdb/samdb/samdb.h"
26 #include "lib/ldb/include/ldb_module.h"
27 #include "param/param.h"
28 #include "librpc/ndr/libndr.h"
29 #include "librpc/gen_ndr/ndr_misc.h"
30 #include "lib/util/tsort.h"
33 override the name to attribute handler function
35 const struct ldb_schema_attribute *dsdb_attribute_handler_override(struct ldb_context *ldb,
39 struct dsdb_schema *schema = talloc_get_type_abort(private_data, struct dsdb_schema);
40 const struct dsdb_attribute *a = dsdb_attribute_by_lDAPDisplayName(schema, name);
42 /* this will fall back to ldb internal handling */
45 return a->ldb_schema_attribute;
48 static int dsdb_schema_set_attributes(struct ldb_context *ldb, struct dsdb_schema *schema, bool write_attributes)
50 int ret = LDB_SUCCESS;
51 struct ldb_result *res;
52 struct ldb_result *res_idx;
53 struct dsdb_attribute *attr;
54 struct ldb_message *mod_msg;
56 struct ldb_message *msg;
57 struct ldb_message *msg_idx;
59 /* setup our own attribute name to schema handler */
60 ldb_schema_attribute_set_override_handler(ldb, dsdb_attribute_handler_override, schema);
62 if (!write_attributes) {
66 mem_ctx = talloc_new(ldb);
71 msg = ldb_msg_new(mem_ctx);
76 msg_idx = ldb_msg_new(mem_ctx);
81 msg->dn = ldb_dn_new(msg, ldb, "@ATTRIBUTES");
86 msg_idx->dn = ldb_dn_new(msg_idx, ldb, "@INDEXLIST");
92 ret = ldb_msg_add_string(msg_idx, "@IDXONE", "1");
93 if (ret != LDB_SUCCESS) {
97 for (attr = schema->attributes; attr; attr = attr->next) {
98 const char *syntax = attr->syntax->ldb_syntax;
101 syntax = attr->syntax->ldap_oid;
104 /* Write out a rough approximation of the schema as an @ATTRIBUTES value, for bootstrapping */
105 if (strcmp(syntax, LDB_SYNTAX_INTEGER) == 0) {
106 ret = ldb_msg_add_string(msg, attr->lDAPDisplayName, "INTEGER");
107 } else if (strcmp(syntax, LDB_SYNTAX_DIRECTORY_STRING) == 0) {
108 ret = ldb_msg_add_string(msg, attr->lDAPDisplayName, "CASE_INSENSITIVE");
110 if (ret != LDB_SUCCESS) {
114 if (attr->searchFlags & SEARCH_FLAG_ATTINDEX) {
115 ret = ldb_msg_add_string(msg_idx, "@IDXATTR", attr->lDAPDisplayName);
116 if (ret != LDB_SUCCESS) {
122 if (ret != LDB_SUCCESS) {
123 talloc_free(mem_ctx);
127 /* Try to avoid churning the attributes too much - we only want to do this if they have changed */
128 ret = ldb_search(ldb, mem_ctx, &res, msg->dn, LDB_SCOPE_BASE, NULL,
130 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
131 ret = ldb_add(ldb, msg);
132 } else if (ret != LDB_SUCCESS) {
133 } else if (res->count != 1) {
134 ret = ldb_add(ldb, msg);
137 /* Annoyingly added to our search results */
138 ldb_msg_remove_attr(res->msgs[0], "distinguishedName");
140 ret = ldb_msg_difference(ldb, mem_ctx,
141 res->msgs[0], msg, &mod_msg);
142 if (ret != LDB_SUCCESS) {
145 if (mod_msg->num_elements > 0) {
146 ret = dsdb_replace(ldb, mod_msg, 0);
148 talloc_free(mod_msg);
151 if (ret == LDB_ERR_OPERATIONS_ERROR || ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS || ret == LDB_ERR_INVALID_DN_SYNTAX) {
152 /* We might be on a read-only DB or LDAP */
155 if (ret != LDB_SUCCESS) {
156 talloc_free(mem_ctx);
160 /* Now write out the indexs, as found in the schema (if they have changed) */
162 ret = ldb_search(ldb, mem_ctx, &res_idx, msg_idx->dn, LDB_SCOPE_BASE,
164 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
165 ret = ldb_add(ldb, msg_idx);
166 } else if (ret != LDB_SUCCESS) {
167 } else if (res_idx->count != 1) {
168 ret = ldb_add(ldb, msg_idx);
171 /* Annoyingly added to our search results */
172 ldb_msg_remove_attr(res_idx->msgs[0], "distinguishedName");
174 ret = ldb_msg_difference(ldb, mem_ctx,
175 res_idx->msgs[0], msg_idx, &mod_msg);
176 if (ret != LDB_SUCCESS) {
179 if (mod_msg->num_elements > 0) {
180 ret = dsdb_replace(ldb, mod_msg, 0);
182 talloc_free(mod_msg);
184 if (ret == LDB_ERR_OPERATIONS_ERROR || ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS || ret == LDB_ERR_INVALID_DN_SYNTAX) {
185 /* We might be on a read-only DB */
188 talloc_free(mem_ctx);
192 talloc_free(mem_ctx);
193 return ldb_operr(ldb);
196 static int uint32_cmp(uint32_t c1, uint32_t c2)
198 if (c1 == c2) return 0;
199 return c1 > c2 ? 1 : -1;
202 static int dsdb_compare_class_by_lDAPDisplayName(struct dsdb_class **c1, struct dsdb_class **c2)
204 return strcasecmp((*c1)->lDAPDisplayName, (*c2)->lDAPDisplayName);
206 static int dsdb_compare_class_by_governsID_id(struct dsdb_class **c1, struct dsdb_class **c2)
208 return uint32_cmp((*c1)->governsID_id, (*c2)->governsID_id);
210 static int dsdb_compare_class_by_governsID_oid(struct dsdb_class **c1, struct dsdb_class **c2)
212 return strcasecmp((*c1)->governsID_oid, (*c2)->governsID_oid);
214 static int dsdb_compare_class_by_cn(struct dsdb_class **c1, struct dsdb_class **c2)
216 return strcasecmp((*c1)->cn, (*c2)->cn);
219 static int dsdb_compare_attribute_by_lDAPDisplayName(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
221 return strcasecmp((*a1)->lDAPDisplayName, (*a2)->lDAPDisplayName);
223 static int dsdb_compare_attribute_by_attributeID_id(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
225 return uint32_cmp((*a1)->attributeID_id, (*a2)->attributeID_id);
227 static int dsdb_compare_attribute_by_attributeID_oid(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
229 return strcasecmp((*a1)->attributeID_oid, (*a2)->attributeID_oid);
231 static int dsdb_compare_attribute_by_linkID(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
233 return uint32_cmp((*a1)->linkID, (*a2)->linkID);
237 * Clean up Classes and Attributes accessor arrays
239 static void dsdb_sorted_accessors_free(struct dsdb_schema *schema)
241 /* free classes accessors */
242 TALLOC_FREE(schema->classes_by_lDAPDisplayName);
243 TALLOC_FREE(schema->classes_by_governsID_id);
244 TALLOC_FREE(schema->classes_by_governsID_oid);
245 TALLOC_FREE(schema->classes_by_cn);
246 /* free attribute accessors */
247 TALLOC_FREE(schema->attributes_by_lDAPDisplayName);
248 TALLOC_FREE(schema->attributes_by_attributeID_id);
249 TALLOC_FREE(schema->attributes_by_msDS_IntId);
250 TALLOC_FREE(schema->attributes_by_attributeID_oid);
251 TALLOC_FREE(schema->attributes_by_linkID);
255 create the sorted accessor arrays for the schema
257 static int dsdb_setup_sorted_accessors(struct ldb_context *ldb,
258 struct dsdb_schema *schema)
260 struct dsdb_class *cur;
261 struct dsdb_attribute *a;
263 unsigned int num_int_id;
265 /* free all caches */
266 dsdb_sorted_accessors_free(schema);
268 /* count the classes */
269 for (i=0, cur=schema->classes; cur; i++, cur=cur->next) /* noop */ ;
270 schema->num_classes = i;
272 /* setup classes_by_* */
273 schema->classes_by_lDAPDisplayName = talloc_array(schema, struct dsdb_class *, i);
274 schema->classes_by_governsID_id = talloc_array(schema, struct dsdb_class *, i);
275 schema->classes_by_governsID_oid = talloc_array(schema, struct dsdb_class *, i);
276 schema->classes_by_cn = talloc_array(schema, struct dsdb_class *, i);
277 if (schema->classes_by_lDAPDisplayName == NULL ||
278 schema->classes_by_governsID_id == NULL ||
279 schema->classes_by_governsID_oid == NULL ||
280 schema->classes_by_cn == NULL) {
284 for (i=0, cur=schema->classes; cur; i++, cur=cur->next) {
285 schema->classes_by_lDAPDisplayName[i] = cur;
286 schema->classes_by_governsID_id[i] = cur;
287 schema->classes_by_governsID_oid[i] = cur;
288 schema->classes_by_cn[i] = cur;
291 /* sort the arrays */
292 TYPESAFE_QSORT(schema->classes_by_lDAPDisplayName, schema->num_classes, dsdb_compare_class_by_lDAPDisplayName);
293 TYPESAFE_QSORT(schema->classes_by_governsID_id, schema->num_classes, dsdb_compare_class_by_governsID_id);
294 TYPESAFE_QSORT(schema->classes_by_governsID_oid, schema->num_classes, dsdb_compare_class_by_governsID_oid);
295 TYPESAFE_QSORT(schema->classes_by_cn, schema->num_classes, dsdb_compare_class_by_cn);
297 /* now build the attribute accessor arrays */
299 /* count the attributes
300 * and attributes with msDS-IntId set */
302 for (i=0, a=schema->attributes; a; i++, a=a->next) {
303 if (a->msDS_IntId != 0) {
307 schema->num_attributes = i;
308 schema->num_int_id_attr = num_int_id;
310 /* setup attributes_by_* */
311 schema->attributes_by_lDAPDisplayName = talloc_array(schema, struct dsdb_attribute *, i);
312 schema->attributes_by_attributeID_id = talloc_array(schema, struct dsdb_attribute *, i);
313 schema->attributes_by_msDS_IntId = talloc_array(schema,
314 struct dsdb_attribute *, num_int_id);
315 schema->attributes_by_attributeID_oid = talloc_array(schema, struct dsdb_attribute *, i);
316 schema->attributes_by_linkID = talloc_array(schema, struct dsdb_attribute *, i);
317 if (schema->attributes_by_lDAPDisplayName == NULL ||
318 schema->attributes_by_attributeID_id == NULL ||
319 schema->attributes_by_msDS_IntId == NULL ||
320 schema->attributes_by_attributeID_oid == NULL ||
321 schema->attributes_by_linkID == NULL) {
326 for (i=0, a=schema->attributes; a; i++, a=a->next) {
327 schema->attributes_by_lDAPDisplayName[i] = a;
328 schema->attributes_by_attributeID_id[i] = a;
329 schema->attributes_by_attributeID_oid[i] = a;
330 schema->attributes_by_linkID[i] = a;
331 /* append attr-by-msDS-IntId values */
332 if (a->msDS_IntId != 0) {
333 schema->attributes_by_msDS_IntId[num_int_id] = a;
337 SMB_ASSERT(num_int_id == schema->num_int_id_attr);
339 /* sort the arrays */
340 TYPESAFE_QSORT(schema->attributes_by_lDAPDisplayName, schema->num_attributes, dsdb_compare_attribute_by_lDAPDisplayName);
341 TYPESAFE_QSORT(schema->attributes_by_attributeID_id, schema->num_attributes, dsdb_compare_attribute_by_attributeID_id);
342 TYPESAFE_QSORT(schema->attributes_by_msDS_IntId, schema->num_int_id_attr, dsdb_compare_attribute_by_attributeID_id);
343 TYPESAFE_QSORT(schema->attributes_by_attributeID_oid, schema->num_attributes, dsdb_compare_attribute_by_attributeID_oid);
344 TYPESAFE_QSORT(schema->attributes_by_linkID, schema->num_attributes, dsdb_compare_attribute_by_linkID);
349 dsdb_sorted_accessors_free(schema);
353 int dsdb_setup_schema_inversion(struct ldb_context *ldb, struct dsdb_schema *schema)
355 /* Walk the list of schema classes */
357 /* For each subClassOf, add us to subclasses of the parent */
359 /* collect these subclasses into a recursive list of total subclasses, preserving order */
361 /* For each subclass under 'top', write the index from it's
362 * order as an integer in the dsdb_class (for sorting
363 * objectClass lists efficiently) */
365 /* Walk the list of scheam classes */
367 /* Create a 'total possible superiors' on each class */
372 * Attach the schema to an opaque pointer on the ldb, so ldb modules
376 int dsdb_set_schema(struct ldb_context *ldb, struct dsdb_schema *schema)
378 struct dsdb_schema *old_schema;
381 ret = dsdb_setup_sorted_accessors(ldb, schema);
382 if (ret != LDB_SUCCESS) {
386 ret = schema_fill_constructed(schema);
387 if (ret != LDB_SUCCESS) {
391 old_schema = ldb_get_opaque(ldb, "dsdb_schema");
393 ret = ldb_set_opaque(ldb, "dsdb_schema", schema);
394 if (ret != LDB_SUCCESS) {
398 /* Remove the reference to the schema we just overwrote - if there was
399 * none, NULL is harmless here */
400 if (old_schema != schema) {
401 talloc_unlink(ldb, old_schema);
402 talloc_steal(ldb, schema);
405 ret = ldb_set_opaque(ldb, "dsdb_use_global_schema", NULL);
406 if (ret != LDB_SUCCESS) {
410 /* Set the new attributes based on the new schema */
411 ret = dsdb_schema_set_attributes(ldb, schema, true);
412 if (ret != LDB_SUCCESS) {
420 * Global variable to hold one copy of the schema, used to avoid memory bloat
422 static struct dsdb_schema *global_schema;
425 * Make this ldb use a specified schema, already fully calculated and belonging to another ldb
427 int dsdb_reference_schema(struct ldb_context *ldb, struct dsdb_schema *schema,
428 bool write_attributes)
431 struct dsdb_schema *old_schema;
432 old_schema = ldb_get_opaque(ldb, "dsdb_schema");
433 ret = ldb_set_opaque(ldb, "dsdb_schema", schema);
434 if (ret != LDB_SUCCESS) {
438 /* Remove the reference to the schema we just overwrote - if there was
439 * none, NULL is harmless here */
440 talloc_unlink(ldb, old_schema);
442 if (talloc_reference(ldb, schema) == NULL) {
446 ret = dsdb_schema_set_attributes(ldb, schema, write_attributes);
447 if (ret != LDB_SUCCESS) {
455 * Make this ldb use the 'global' schema, setup to avoid having multiple copies in this process
457 int dsdb_set_global_schema(struct ldb_context *ldb)
460 void *use_global_schema = (void *)1;
461 if (!global_schema) {
464 ret = ldb_set_opaque(ldb, "dsdb_use_global_schema", use_global_schema);
465 if (ret != LDB_SUCCESS) {
469 /* Set the new attributes based on the new schema */
470 ret = dsdb_schema_set_attributes(ldb, global_schema, false /* Don't write attributes, it's expensive */);
471 if (ret == LDB_SUCCESS) {
472 /* Keep a reference to this schema, just incase the original copy is replaced */
473 if (talloc_reference(ldb, global_schema) == NULL) {
482 * Find the schema object for this ldb
484 * If reference_ctx is not NULL, then talloc_reference onto that context
487 struct dsdb_schema *dsdb_get_schema(struct ldb_context *ldb, TALLOC_CTX *reference_ctx)
490 struct dsdb_schema *schema_out;
491 struct dsdb_schema *schema_in;
492 bool use_global_schema;
493 TALLOC_CTX *tmp_ctx = talloc_new(reference_ctx);
498 /* see if we have a cached copy */
499 use_global_schema = (ldb_get_opaque(ldb, "dsdb_use_global_schema") != NULL);
500 if (use_global_schema) {
501 schema_in = global_schema;
503 p = ldb_get_opaque(ldb, "dsdb_schema");
505 schema_in = talloc_get_type(p, struct dsdb_schema);
507 talloc_free(tmp_ctx);
512 if (schema_in->refresh_fn && !schema_in->refresh_in_progress) {
513 if (!talloc_reference(tmp_ctx, schema_in)) {
514 /* ensure that the schema_in->refresh_in_progress remains valid for the right amount of time */
515 talloc_free(tmp_ctx);
518 schema_in->refresh_in_progress = true;
519 /* This may change schema, if it needs to reload it from disk */
520 schema_out = schema_in->refresh_fn(schema_in->loaded_from_module,
523 schema_in->refresh_in_progress = false;
525 schema_out = schema_in;
528 /* This removes the extra reference above */
529 talloc_free(tmp_ctx);
530 if (!reference_ctx) {
533 return talloc_reference(reference_ctx, schema_out);
538 * Make the schema found on this ldb the 'global' schema
541 void dsdb_make_schema_global(struct ldb_context *ldb, struct dsdb_schema *schema)
548 talloc_unlink(talloc_autofree_context(), global_schema);
551 /* we want the schema to be around permanently */
552 talloc_reparent(ldb, talloc_autofree_context(), schema);
553 global_schema = schema;
555 /* This calls the talloc_reference() of the global schema back onto the ldb */
556 dsdb_set_global_schema(ldb);
559 /* When loading the schema from LDIF files, we don't get the extended DNs.
561 We need to set these up, so that from the moment we start the provision, the defaultObjectCategory links are set up correctly.
563 int dsdb_schema_fill_extended_dn(struct ldb_context *ldb, struct dsdb_schema *schema)
565 struct dsdb_class *cur;
566 const struct dsdb_class *target_class;
567 for (cur = schema->classes; cur; cur = cur->next) {
568 const struct ldb_val *rdn;
571 struct ldb_dn *dn = ldb_dn_new(NULL, ldb, cur->defaultObjectCategory);
574 return LDB_ERR_INVALID_DN_SYNTAX;
576 rdn = ldb_dn_get_component_val(dn, 0);
579 return LDB_ERR_INVALID_DN_SYNTAX;
581 target_class = dsdb_class_by_cn_ldb_val(schema, rdn);
584 return LDB_ERR_CONSTRAINT_VIOLATION;
587 status = GUID_to_ndr_blob(&target_class->objectGUID, dn, &guid);
588 if (!NT_STATUS_IS_OK(status)) {
590 return ldb_operr(ldb);
592 ldb_dn_set_extended_component(dn, "GUID", &guid);
594 cur->defaultObjectCategory = ldb_dn_get_extended_linearized(cur, dn, 1);
601 * Add an element to the schema (attribute or class) from an LDB message
603 WERROR dsdb_schema_set_el_from_ldb_msg(struct ldb_context *ldb, struct dsdb_schema *schema,
604 struct ldb_message *msg)
606 if (samdb_find_attribute(ldb, msg,
607 "objectclass", "attributeSchema") != NULL) {
608 return dsdb_attribute_from_ldb(ldb, schema, msg);
609 } else if (samdb_find_attribute(ldb, msg,
610 "objectclass", "classSchema") != NULL) {
611 return dsdb_class_from_ldb(schema, msg);
614 /* Don't fail on things not classes or attributes */
619 * Rather than read a schema from the LDB itself, read it from an ldif
620 * file. This allows schema to be loaded and used while adding the
621 * schema itself to the directory.
624 WERROR dsdb_set_schema_from_ldif(struct ldb_context *ldb, const char *pf, const char *df)
626 struct ldb_ldif *ldif;
627 struct ldb_message *msg;
631 struct dsdb_schema *schema;
632 const struct ldb_val *prefix_val;
633 const struct ldb_val *info_val;
634 struct ldb_val info_val_default;
637 mem_ctx = talloc_new(ldb);
642 schema = dsdb_new_schema(mem_ctx);
644 schema->fsmo.we_are_master = true;
645 schema->fsmo.master_dn = ldb_dn_new_fmt(schema, ldb, "@PROVISION_SCHEMA_MASTER");
646 if (!schema->fsmo.master_dn) {
651 * load the prefixMap attribute from pf
653 ldif = ldb_ldif_read_string(ldb, &pf);
655 status = WERR_INVALID_PARAM;
658 talloc_steal(mem_ctx, ldif);
660 msg = ldb_msg_canonicalize(ldb, ldif->msg);
664 talloc_steal(mem_ctx, msg);
667 prefix_val = ldb_msg_find_ldb_val(msg, "prefixMap");
669 status = WERR_INVALID_PARAM;
673 info_val = ldb_msg_find_ldb_val(msg, "schemaInfo");
675 status = dsdb_schema_info_blob_new(mem_ctx, &info_val_default);
676 W_ERROR_NOT_OK_GOTO(status, failed);
677 info_val = &info_val_default;
680 status = dsdb_load_oid_mappings_ldb(schema, prefix_val, info_val);
681 if (!W_ERROR_IS_OK(status)) {
682 DEBUG(0,("ERROR: dsdb_load_oid_mappings_ldb() failed with %s\n", win_errstr(status)));
687 * load the attribute and class definitions outof df
689 while ((ldif = ldb_ldif_read_string(ldb, &df))) {
690 talloc_steal(mem_ctx, ldif);
692 msg = ldb_msg_canonicalize(ldb, ldif->msg);
697 status = dsdb_schema_set_el_from_ldb_msg(ldb, schema, msg);
699 if (!W_ERROR_IS_OK(status)) {
704 ret = dsdb_set_schema(ldb, schema);
705 if (ret != LDB_SUCCESS) {
706 status = WERR_FOOBAR;
710 ret = dsdb_schema_fill_extended_dn(ldb, schema);
711 if (ret != LDB_SUCCESS) {
712 status = WERR_FOOBAR;
722 talloc_free(mem_ctx);