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);
68 return LDB_ERR_OPERATIONS_ERROR;
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 mod_msg = ldb_msg_diff(ldb, res->msgs[0], msg);
141 if (mod_msg->num_elements > 0) {
142 ret = dsdb_replace(ldb, mod_msg, 0);
144 talloc_free(mod_msg);
147 if (ret == LDB_ERR_OPERATIONS_ERROR || ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS || ret == LDB_ERR_INVALID_DN_SYNTAX) {
148 /* We might be on a read-only DB or LDAP */
151 if (ret != LDB_SUCCESS) {
152 talloc_free(mem_ctx);
156 /* Now write out the indexs, as found in the schema (if they have changed) */
158 ret = ldb_search(ldb, mem_ctx, &res_idx, msg_idx->dn, LDB_SCOPE_BASE,
160 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
161 ret = ldb_add(ldb, msg_idx);
162 } else if (ret != LDB_SUCCESS) {
163 } else if (res_idx->count != 1) {
164 ret = ldb_add(ldb, msg_idx);
167 /* Annoyingly added to our search results */
168 ldb_msg_remove_attr(res_idx->msgs[0], "distinguishedName");
170 mod_msg = ldb_msg_diff(ldb, res_idx->msgs[0], msg_idx);
171 if (mod_msg->num_elements > 0) {
172 ret = dsdb_replace(ldb, mod_msg, 0);
174 talloc_free(mod_msg);
176 if (ret == LDB_ERR_OPERATIONS_ERROR || ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS || ret == LDB_ERR_INVALID_DN_SYNTAX) {
177 /* We might be on a read-only DB */
180 talloc_free(mem_ctx);
184 talloc_free(mem_ctx);
185 return LDB_ERR_OPERATIONS_ERROR;
188 static int uint32_cmp(uint32_t c1, uint32_t c2)
190 if (c1 == c2) return 0;
191 return c1 > c2 ? 1 : -1;
194 static int dsdb_compare_class_by_lDAPDisplayName(struct dsdb_class **c1, struct dsdb_class **c2)
196 return strcasecmp((*c1)->lDAPDisplayName, (*c2)->lDAPDisplayName);
198 static int dsdb_compare_class_by_governsID_id(struct dsdb_class **c1, struct dsdb_class **c2)
200 return uint32_cmp((*c1)->governsID_id, (*c2)->governsID_id);
202 static int dsdb_compare_class_by_governsID_oid(struct dsdb_class **c1, struct dsdb_class **c2)
204 return strcasecmp((*c1)->governsID_oid, (*c2)->governsID_oid);
206 static int dsdb_compare_class_by_cn(struct dsdb_class **c1, struct dsdb_class **c2)
208 return strcasecmp((*c1)->cn, (*c2)->cn);
211 static int dsdb_compare_attribute_by_lDAPDisplayName(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
213 return strcasecmp((*a1)->lDAPDisplayName, (*a2)->lDAPDisplayName);
215 static int dsdb_compare_attribute_by_attributeID_id(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
217 return uint32_cmp((*a1)->attributeID_id, (*a2)->attributeID_id);
219 static int dsdb_compare_attribute_by_attributeID_oid(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
221 return strcasecmp((*a1)->attributeID_oid, (*a2)->attributeID_oid);
223 static int dsdb_compare_attribute_by_linkID(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
225 return uint32_cmp((*a1)->linkID, (*a2)->linkID);
229 * Clean up Classes and Attributes accessor arrays
231 static void dsdb_sorted_accessors_free(struct dsdb_schema *schema)
233 /* free classes accessors */
234 TALLOC_FREE(schema->classes_by_lDAPDisplayName);
235 TALLOC_FREE(schema->classes_by_governsID_id);
236 TALLOC_FREE(schema->classes_by_governsID_oid);
237 TALLOC_FREE(schema->classes_by_cn);
238 /* free attribute accessors */
239 TALLOC_FREE(schema->attributes_by_lDAPDisplayName);
240 TALLOC_FREE(schema->attributes_by_attributeID_id);
241 TALLOC_FREE(schema->attributes_by_msDS_IntId);
242 TALLOC_FREE(schema->attributes_by_attributeID_oid);
243 TALLOC_FREE(schema->attributes_by_linkID);
247 create the sorted accessor arrays for the schema
249 static int dsdb_setup_sorted_accessors(struct ldb_context *ldb,
250 struct dsdb_schema *schema)
252 struct dsdb_class *cur;
253 struct dsdb_attribute *a;
255 unsigned int num_int_id;
257 /* free all caches */
258 dsdb_sorted_accessors_free(schema);
260 /* count the classes */
261 for (i=0, cur=schema->classes; cur; i++, cur=cur->next) /* noop */ ;
262 schema->num_classes = i;
264 /* setup classes_by_* */
265 schema->classes_by_lDAPDisplayName = talloc_array(schema, struct dsdb_class *, i);
266 schema->classes_by_governsID_id = talloc_array(schema, struct dsdb_class *, i);
267 schema->classes_by_governsID_oid = talloc_array(schema, struct dsdb_class *, i);
268 schema->classes_by_cn = talloc_array(schema, struct dsdb_class *, i);
269 if (schema->classes_by_lDAPDisplayName == NULL ||
270 schema->classes_by_governsID_id == NULL ||
271 schema->classes_by_governsID_oid == NULL ||
272 schema->classes_by_cn == NULL) {
276 for (i=0, cur=schema->classes; cur; i++, cur=cur->next) {
277 schema->classes_by_lDAPDisplayName[i] = cur;
278 schema->classes_by_governsID_id[i] = cur;
279 schema->classes_by_governsID_oid[i] = cur;
280 schema->classes_by_cn[i] = cur;
283 /* sort the arrays */
284 TYPESAFE_QSORT(schema->classes_by_lDAPDisplayName, schema->num_classes, dsdb_compare_class_by_lDAPDisplayName);
285 TYPESAFE_QSORT(schema->classes_by_governsID_id, schema->num_classes, dsdb_compare_class_by_governsID_id);
286 TYPESAFE_QSORT(schema->classes_by_governsID_oid, schema->num_classes, dsdb_compare_class_by_governsID_oid);
287 TYPESAFE_QSORT(schema->classes_by_cn, schema->num_classes, dsdb_compare_class_by_cn);
289 /* now build the attribute accessor arrays */
291 /* count the attributes
292 * and attributes with msDS-IntId set */
294 for (i=0, a=schema->attributes; a; i++, a=a->next) {
295 if (a->msDS_IntId != 0) {
299 schema->num_attributes = i;
300 schema->num_int_id_attr = num_int_id;
302 /* setup attributes_by_* */
303 schema->attributes_by_lDAPDisplayName = talloc_array(schema, struct dsdb_attribute *, i);
304 schema->attributes_by_attributeID_id = talloc_array(schema, struct dsdb_attribute *, i);
305 schema->attributes_by_msDS_IntId = talloc_array(schema,
306 struct dsdb_attribute *, num_int_id);
307 schema->attributes_by_attributeID_oid = talloc_array(schema, struct dsdb_attribute *, i);
308 schema->attributes_by_linkID = talloc_array(schema, struct dsdb_attribute *, i);
309 if (schema->attributes_by_lDAPDisplayName == NULL ||
310 schema->attributes_by_attributeID_id == NULL ||
311 schema->attributes_by_msDS_IntId == NULL ||
312 schema->attributes_by_attributeID_oid == NULL ||
313 schema->attributes_by_linkID == NULL) {
318 for (i=0, a=schema->attributes; a; i++, a=a->next) {
319 schema->attributes_by_lDAPDisplayName[i] = a;
320 schema->attributes_by_attributeID_id[i] = a;
321 schema->attributes_by_attributeID_oid[i] = a;
322 schema->attributes_by_linkID[i] = a;
323 /* append attr-by-msDS-IntId values */
324 if (a->msDS_IntId != 0) {
325 schema->attributes_by_msDS_IntId[num_int_id] = a;
329 SMB_ASSERT(num_int_id == schema->num_int_id_attr);
331 /* sort the arrays */
332 TYPESAFE_QSORT(schema->attributes_by_lDAPDisplayName, schema->num_attributes, dsdb_compare_attribute_by_lDAPDisplayName);
333 TYPESAFE_QSORT(schema->attributes_by_attributeID_id, schema->num_attributes, dsdb_compare_attribute_by_attributeID_id);
334 TYPESAFE_QSORT(schema->attributes_by_msDS_IntId, schema->num_int_id_attr, dsdb_compare_attribute_by_attributeID_id);
335 TYPESAFE_QSORT(schema->attributes_by_attributeID_oid, schema->num_attributes, dsdb_compare_attribute_by_attributeID_oid);
336 TYPESAFE_QSORT(schema->attributes_by_linkID, schema->num_attributes, dsdb_compare_attribute_by_linkID);
341 dsdb_sorted_accessors_free(schema);
343 return LDB_ERR_OPERATIONS_ERROR;
346 int dsdb_setup_schema_inversion(struct ldb_context *ldb, struct dsdb_schema *schema)
348 /* Walk the list of schema classes */
350 /* For each subClassOf, add us to subclasses of the parent */
352 /* collect these subclasses into a recursive list of total subclasses, preserving order */
354 /* For each subclass under 'top', write the index from it's
355 * order as an integer in the dsdb_class (for sorting
356 * objectClass lists efficiently) */
358 /* Walk the list of scheam classes */
360 /* Create a 'total possible superiors' on each class */
365 * Attach the schema to an opaque pointer on the ldb, so ldb modules
369 int dsdb_set_schema(struct ldb_context *ldb, struct dsdb_schema *schema)
371 struct dsdb_schema *old_schema;
374 ret = dsdb_setup_sorted_accessors(ldb, schema);
375 if (ret != LDB_SUCCESS) {
379 ret = schema_fill_constructed(schema);
380 if (ret != LDB_SUCCESS) {
384 old_schema = ldb_get_opaque(ldb, "dsdb_schema");
386 ret = ldb_set_opaque(ldb, "dsdb_schema", schema);
387 if (ret != LDB_SUCCESS) {
391 /* Remove the reference to the schema we just overwrote - if there was
392 * none, NULL is harmless here */
393 if (old_schema != schema) {
394 talloc_unlink(ldb, old_schema);
395 talloc_steal(ldb, schema);
398 ret = ldb_set_opaque(ldb, "dsdb_use_global_schema", NULL);
399 if (ret != LDB_SUCCESS) {
403 /* Set the new attributes based on the new schema */
404 ret = dsdb_schema_set_attributes(ldb, schema, true);
405 if (ret != LDB_SUCCESS) {
413 * Global variable to hold one copy of the schema, used to avoid memory bloat
415 static struct dsdb_schema *global_schema;
418 * Make this ldb use a specified schema, already fully calculated and belonging to another ldb
420 int dsdb_reference_schema(struct ldb_context *ldb, struct dsdb_schema *schema,
421 bool write_attributes)
424 struct dsdb_schema *old_schema;
425 old_schema = ldb_get_opaque(ldb, "dsdb_schema");
426 ret = ldb_set_opaque(ldb, "dsdb_schema", schema);
427 if (ret != LDB_SUCCESS) {
431 /* Remove the refernece to the schema we just overwrote - if there was none, NULL is harmless here */
432 talloc_unlink(ldb, old_schema);
434 if (talloc_reference(ldb, schema) == NULL) {
435 return LDB_ERR_OPERATIONS_ERROR;
438 ret = dsdb_schema_set_attributes(ldb, schema, write_attributes);
439 if (ret != LDB_SUCCESS) {
447 * Make this ldb use the 'global' schema, setup to avoid having multiple copies in this process
449 int dsdb_set_global_schema(struct ldb_context *ldb)
452 void *use_global_schema = (void *)1;
453 if (!global_schema) {
456 ret = ldb_set_opaque(ldb, "dsdb_use_global_schema", use_global_schema);
457 if (ret != LDB_SUCCESS) {
461 /* Set the new attributes based on the new schema */
462 ret = dsdb_schema_set_attributes(ldb, global_schema, false /* Don't write attributes, it's expensive */);
463 if (ret == LDB_SUCCESS) {
464 /* Keep a reference to this schema, just incase the original copy is replaced */
465 if (talloc_reference(ldb, global_schema) == NULL) {
466 return LDB_ERR_OPERATIONS_ERROR;
474 * Find the schema object for this ldb
476 * If reference_ctx is not NULL, then talloc_reference onto that context
479 struct dsdb_schema *dsdb_get_schema(struct ldb_context *ldb, TALLOC_CTX *reference_ctx)
482 struct dsdb_schema *schema_out;
483 struct dsdb_schema *schema_in;
484 bool use_global_schema;
485 TALLOC_CTX *tmp_ctx = talloc_new(reference_ctx);
490 /* see if we have a cached copy */
491 use_global_schema = (ldb_get_opaque(ldb, "dsdb_use_global_schema") != NULL);
492 if (use_global_schema) {
493 schema_in = global_schema;
495 p = ldb_get_opaque(ldb, "dsdb_schema");
497 schema_in = talloc_get_type(p, struct dsdb_schema);
499 talloc_free(tmp_ctx);
504 if (schema_in->refresh_fn && !schema_in->refresh_in_progress) {
505 if (!talloc_reference(tmp_ctx, schema_in)) {
506 /* ensure that the schema_in->refresh_in_progress remains valid for the right amount of time */
507 talloc_free(tmp_ctx);
510 schema_in->refresh_in_progress = true;
511 /* This may change schema, if it needs to reload it from disk */
512 schema_out = schema_in->refresh_fn(schema_in->loaded_from_module,
515 schema_in->refresh_in_progress = false;
517 schema_out = schema_in;
520 /* This removes the extra reference above */
521 talloc_free(tmp_ctx);
522 if (!reference_ctx) {
525 return talloc_reference(reference_ctx, schema_out);
530 * Make the schema found on this ldb the 'global' schema
533 void dsdb_make_schema_global(struct ldb_context *ldb, struct dsdb_schema *schema)
540 talloc_unlink(talloc_autofree_context(), global_schema);
543 /* we want the schema to be around permanently */
544 talloc_reparent(ldb, talloc_autofree_context(), schema);
545 global_schema = schema;
547 /* This calls the talloc_reference() of the global schema back onto the ldb */
548 dsdb_set_global_schema(ldb);
551 /* When loading the schema from LDIF files, we don't get the extended DNs.
553 We need to set these up, so that from the moment we start the provision, the defaultObjectCategory links are set up correctly.
555 int dsdb_schema_fill_extended_dn(struct ldb_context *ldb, struct dsdb_schema *schema)
557 struct dsdb_class *cur;
558 const struct dsdb_class *target_class;
559 for (cur = schema->classes; cur; cur = cur->next) {
560 const struct ldb_val *rdn;
563 struct ldb_dn *dn = ldb_dn_new(NULL, ldb, cur->defaultObjectCategory);
566 return LDB_ERR_INVALID_DN_SYNTAX;
568 rdn = ldb_dn_get_component_val(dn, 0);
571 return LDB_ERR_INVALID_DN_SYNTAX;
573 target_class = dsdb_class_by_cn_ldb_val(schema, rdn);
576 return LDB_ERR_CONSTRAINT_VIOLATION;
579 status = GUID_to_ndr_blob(&target_class->objectGUID, dn, &guid);
580 if (!NT_STATUS_IS_OK(status)) {
582 return LDB_ERR_OPERATIONS_ERROR;
584 ldb_dn_set_extended_component(dn, "GUID", &guid);
586 cur->defaultObjectCategory = ldb_dn_get_extended_linearized(cur, dn, 1);
593 * Add an element to the schema (attribute or class) from an LDB message
595 WERROR dsdb_schema_set_el_from_ldb_msg(struct ldb_context *ldb, struct dsdb_schema *schema,
596 struct ldb_message *msg)
598 if (samdb_find_attribute(ldb, msg,
599 "objectclass", "attributeSchema") != NULL) {
600 return dsdb_attribute_from_ldb(ldb, schema, msg);
601 } else if (samdb_find_attribute(ldb, msg,
602 "objectclass", "classSchema") != NULL) {
603 return dsdb_class_from_ldb(schema, msg);
606 /* Don't fail on things not classes or attributes */
611 * Rather than read a schema from the LDB itself, read it from an ldif
612 * file. This allows schema to be loaded and used while adding the
613 * schema itself to the directory.
616 WERROR dsdb_set_schema_from_ldif(struct ldb_context *ldb, const char *pf, const char *df)
618 struct ldb_ldif *ldif;
619 struct ldb_message *msg;
623 struct dsdb_schema *schema;
624 const struct ldb_val *prefix_val;
625 const struct ldb_val *info_val;
626 struct ldb_val info_val_default;
629 mem_ctx = talloc_new(ldb);
634 schema = dsdb_new_schema(mem_ctx);
636 schema->fsmo.we_are_master = true;
637 schema->fsmo.master_dn = ldb_dn_new_fmt(schema, ldb, "@PROVISION_SCHEMA_MASTER");
638 if (!schema->fsmo.master_dn) {
643 * load the prefixMap attribute from pf
645 ldif = ldb_ldif_read_string(ldb, &pf);
647 status = WERR_INVALID_PARAM;
650 talloc_steal(mem_ctx, ldif);
652 msg = ldb_msg_canonicalize(ldb, ldif->msg);
656 talloc_steal(mem_ctx, msg);
659 prefix_val = ldb_msg_find_ldb_val(msg, "prefixMap");
661 status = WERR_INVALID_PARAM;
665 info_val = ldb_msg_find_ldb_val(msg, "schemaInfo");
667 status = dsdb_schema_info_blob_new(mem_ctx, &info_val_default);
668 W_ERROR_NOT_OK_GOTO(status, failed);
669 info_val = &info_val_default;
672 status = dsdb_load_oid_mappings_ldb(schema, prefix_val, info_val);
673 if (!W_ERROR_IS_OK(status)) {
674 DEBUG(0,("ERROR: dsdb_load_oid_mappings_ldb() failed with %s\n", win_errstr(status)));
679 * load the attribute and class definitions outof df
681 while ((ldif = ldb_ldif_read_string(ldb, &df))) {
682 talloc_steal(mem_ctx, ldif);
684 msg = ldb_msg_canonicalize(ldb, ldif->msg);
689 status = dsdb_schema_set_el_from_ldb_msg(ldb, schema, msg);
691 if (!W_ERROR_IS_OK(status)) {
696 ret = dsdb_set_schema(ldb, schema);
697 if (ret != LDB_SUCCESS) {
698 status = WERR_FOOBAR;
702 ret = dsdb_schema_fill_extended_dn(ldb, schema);
703 if (ret != LDB_SUCCESS) {
704 status = WERR_FOOBAR;
714 talloc_free(mem_ctx);