2 Unix SMB/CIFS implementation.
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
105 * as an @ATTRIBUTES value, for bootstrapping */
106 if (strcmp(syntax, LDB_SYNTAX_INTEGER) == 0) {
107 ret = ldb_msg_add_string(msg, attr->lDAPDisplayName, "INTEGER");
108 } else if (strcmp(syntax, LDB_SYNTAX_DIRECTORY_STRING) == 0) {
109 ret = ldb_msg_add_string(msg, attr->lDAPDisplayName, "CASE_INSENSITIVE");
111 if (ret != LDB_SUCCESS) {
115 if (attr->searchFlags & SEARCH_FLAG_ATTINDEX) {
116 ret = ldb_msg_add_string(msg_idx, "@IDXATTR", attr->lDAPDisplayName);
117 if (ret != LDB_SUCCESS) {
123 if (ret != LDB_SUCCESS) {
124 talloc_free(mem_ctx);
128 /* Try to avoid churning the attributes too much,
129 * we only want to do this if they have changed */
130 ret = ldb_search(ldb, mem_ctx, &res, msg->dn, LDB_SCOPE_BASE, NULL,
132 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
133 ret = ldb_add(ldb, msg);
134 } else if (ret != LDB_SUCCESS) {
135 } else if (res->count != 1) {
136 ret = ldb_add(ldb, msg);
139 /* Annoyingly added to our search results */
140 ldb_msg_remove_attr(res->msgs[0], "distinguishedName");
142 ret = ldb_msg_diff_ex(ldb, res->msgs[0], msg, mem_ctx, &mod_msg);
143 if (ret != LDB_SUCCESS) {
146 if (mod_msg->num_elements > 0) {
147 ret = dsdb_replace(ldb, mod_msg, 0);
149 talloc_free(mod_msg);
152 if (ret == LDB_ERR_OPERATIONS_ERROR || ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS || ret == LDB_ERR_INVALID_DN_SYNTAX) {
153 /* We might be on a read-only DB or LDAP */
156 if (ret != LDB_SUCCESS) {
157 talloc_free(mem_ctx);
161 /* Now write out the indexes, as found in the schema (if they have changed) */
163 ret = ldb_search(ldb, mem_ctx, &res_idx, msg_idx->dn, LDB_SCOPE_BASE,
165 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
166 ret = ldb_add(ldb, msg_idx);
167 } else if (ret != LDB_SUCCESS) {
168 } else if (res_idx->count != 1) {
169 ret = ldb_add(ldb, msg_idx);
172 /* Annoyingly added to our search results */
173 ldb_msg_remove_attr(res_idx->msgs[0], "distinguishedName");
175 ret = ldb_msg_diff_ex(ldb, res_idx->msgs[0], msg_idx,
177 if (ret != LDB_SUCCESS) {
180 if (mod_msg->num_elements > 0) {
181 ret = dsdb_replace(ldb, mod_msg, 0);
183 talloc_free(mod_msg);
185 if (ret == LDB_ERR_OPERATIONS_ERROR || ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS || ret == LDB_ERR_INVALID_DN_SYNTAX) {
186 /* We might be on a read-only DB */
189 talloc_free(mem_ctx);
193 talloc_free(mem_ctx);
194 return ldb_operr(ldb);
197 static int uint32_cmp(uint32_t c1, uint32_t c2)
199 if (c1 == c2) return 0;
200 return c1 > c2 ? 1 : -1;
203 static int dsdb_compare_class_by_lDAPDisplayName(struct dsdb_class **c1, struct dsdb_class **c2)
205 return strcasecmp((*c1)->lDAPDisplayName, (*c2)->lDAPDisplayName);
207 static int dsdb_compare_class_by_governsID_id(struct dsdb_class **c1, struct dsdb_class **c2)
209 return uint32_cmp((*c1)->governsID_id, (*c2)->governsID_id);
211 static int dsdb_compare_class_by_governsID_oid(struct dsdb_class **c1, struct dsdb_class **c2)
213 return strcasecmp((*c1)->governsID_oid, (*c2)->governsID_oid);
215 static int dsdb_compare_class_by_cn(struct dsdb_class **c1, struct dsdb_class **c2)
217 return strcasecmp((*c1)->cn, (*c2)->cn);
220 static int dsdb_compare_attribute_by_lDAPDisplayName(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
222 return strcasecmp((*a1)->lDAPDisplayName, (*a2)->lDAPDisplayName);
224 static int dsdb_compare_attribute_by_attributeID_id(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
226 return uint32_cmp((*a1)->attributeID_id, (*a2)->attributeID_id);
228 static int dsdb_compare_attribute_by_attributeID_oid(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
230 return strcasecmp((*a1)->attributeID_oid, (*a2)->attributeID_oid);
232 static int dsdb_compare_attribute_by_linkID(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
234 return uint32_cmp((*a1)->linkID, (*a2)->linkID);
238 * Clean up Classes and Attributes accessor arrays
240 static void dsdb_sorted_accessors_free(struct dsdb_schema *schema)
242 /* free classes accessors */
243 TALLOC_FREE(schema->classes_by_lDAPDisplayName);
244 TALLOC_FREE(schema->classes_by_governsID_id);
245 TALLOC_FREE(schema->classes_by_governsID_oid);
246 TALLOC_FREE(schema->classes_by_cn);
247 /* free attribute accessors */
248 TALLOC_FREE(schema->attributes_by_lDAPDisplayName);
249 TALLOC_FREE(schema->attributes_by_attributeID_id);
250 TALLOC_FREE(schema->attributes_by_msDS_IntId);
251 TALLOC_FREE(schema->attributes_by_attributeID_oid);
252 TALLOC_FREE(schema->attributes_by_linkID);
256 create the sorted accessor arrays for the schema
258 static int dsdb_setup_sorted_accessors(struct ldb_context *ldb,
259 struct dsdb_schema *schema)
261 struct dsdb_class *cur;
262 struct dsdb_attribute *a;
264 unsigned int num_int_id;
266 /* free all caches */
267 dsdb_sorted_accessors_free(schema);
269 /* count the classes */
270 for (i=0, cur=schema->classes; cur; i++, cur=cur->next) /* noop */ ;
271 schema->num_classes = i;
273 /* setup classes_by_* */
274 schema->classes_by_lDAPDisplayName = talloc_array(schema, struct dsdb_class *, i);
275 schema->classes_by_governsID_id = talloc_array(schema, struct dsdb_class *, i);
276 schema->classes_by_governsID_oid = talloc_array(schema, struct dsdb_class *, i);
277 schema->classes_by_cn = talloc_array(schema, struct dsdb_class *, i);
278 if (schema->classes_by_lDAPDisplayName == NULL ||
279 schema->classes_by_governsID_id == NULL ||
280 schema->classes_by_governsID_oid == NULL ||
281 schema->classes_by_cn == NULL) {
285 for (i=0, cur=schema->classes; cur; i++, cur=cur->next) {
286 schema->classes_by_lDAPDisplayName[i] = cur;
287 schema->classes_by_governsID_id[i] = cur;
288 schema->classes_by_governsID_oid[i] = cur;
289 schema->classes_by_cn[i] = cur;
292 /* sort the arrays */
293 TYPESAFE_QSORT(schema->classes_by_lDAPDisplayName, schema->num_classes, dsdb_compare_class_by_lDAPDisplayName);
294 TYPESAFE_QSORT(schema->classes_by_governsID_id, schema->num_classes, dsdb_compare_class_by_governsID_id);
295 TYPESAFE_QSORT(schema->classes_by_governsID_oid, schema->num_classes, dsdb_compare_class_by_governsID_oid);
296 TYPESAFE_QSORT(schema->classes_by_cn, schema->num_classes, dsdb_compare_class_by_cn);
298 /* now build the attribute accessor arrays */
300 /* count the attributes
301 * and attributes with msDS-IntId set */
303 for (i=0, a=schema->attributes; a; i++, a=a->next) {
304 if (a->msDS_IntId != 0) {
308 schema->num_attributes = i;
309 schema->num_int_id_attr = num_int_id;
311 /* setup attributes_by_* */
312 schema->attributes_by_lDAPDisplayName = talloc_array(schema, struct dsdb_attribute *, i);
313 schema->attributes_by_attributeID_id = talloc_array(schema, struct dsdb_attribute *, i);
314 schema->attributes_by_msDS_IntId = talloc_array(schema,
315 struct dsdb_attribute *, num_int_id);
316 schema->attributes_by_attributeID_oid = talloc_array(schema, struct dsdb_attribute *, i);
317 schema->attributes_by_linkID = talloc_array(schema, struct dsdb_attribute *, i);
318 if (schema->attributes_by_lDAPDisplayName == NULL ||
319 schema->attributes_by_attributeID_id == NULL ||
320 schema->attributes_by_msDS_IntId == NULL ||
321 schema->attributes_by_attributeID_oid == NULL ||
322 schema->attributes_by_linkID == NULL) {
327 for (i=0, a=schema->attributes; a; i++, a=a->next) {
328 schema->attributes_by_lDAPDisplayName[i] = a;
329 schema->attributes_by_attributeID_id[i] = a;
330 schema->attributes_by_attributeID_oid[i] = a;
331 schema->attributes_by_linkID[i] = a;
332 /* append attr-by-msDS-IntId values */
333 if (a->msDS_IntId != 0) {
334 schema->attributes_by_msDS_IntId[num_int_id] = a;
338 SMB_ASSERT(num_int_id == schema->num_int_id_attr);
340 /* sort the arrays */
341 TYPESAFE_QSORT(schema->attributes_by_lDAPDisplayName, schema->num_attributes, dsdb_compare_attribute_by_lDAPDisplayName);
342 TYPESAFE_QSORT(schema->attributes_by_attributeID_id, schema->num_attributes, dsdb_compare_attribute_by_attributeID_id);
343 TYPESAFE_QSORT(schema->attributes_by_msDS_IntId, schema->num_int_id_attr, dsdb_compare_attribute_by_attributeID_id);
344 TYPESAFE_QSORT(schema->attributes_by_attributeID_oid, schema->num_attributes, dsdb_compare_attribute_by_attributeID_oid);
345 TYPESAFE_QSORT(schema->attributes_by_linkID, schema->num_attributes, dsdb_compare_attribute_by_linkID);
350 dsdb_sorted_accessors_free(schema);
354 int dsdb_setup_schema_inversion(struct ldb_context *ldb, struct dsdb_schema *schema)
356 /* Walk the list of schema classes */
358 /* For each subClassOf, add us to subclasses of the parent */
360 /* collect these subclasses into a recursive list of total subclasses, preserving order */
362 /* For each subclass under 'top', write the index from it's
363 * order as an integer in the dsdb_class (for sorting
364 * objectClass lists efficiently) */
366 /* Walk the list of schema classes */
368 /* Create a 'total possible superiors' on each class */
373 * Attach the schema to an opaque pointer on the ldb, so ldb modules
377 int dsdb_set_schema(struct ldb_context *ldb, struct dsdb_schema *schema)
379 struct dsdb_schema *old_schema;
382 ret = dsdb_setup_sorted_accessors(ldb, schema);
383 if (ret != LDB_SUCCESS) {
387 ret = schema_fill_constructed(schema);
388 if (ret != LDB_SUCCESS) {
392 old_schema = ldb_get_opaque(ldb, "dsdb_schema");
394 ret = ldb_set_opaque(ldb, "dsdb_schema", schema);
395 if (ret != LDB_SUCCESS) {
399 /* Remove the reference to the schema we just overwrote - if there was
400 * none, NULL is harmless here */
401 if (old_schema != schema) {
402 talloc_unlink(ldb, old_schema);
403 talloc_steal(ldb, schema);
406 ret = ldb_set_opaque(ldb, "dsdb_use_global_schema", NULL);
407 if (ret != LDB_SUCCESS) {
411 /* Set the new attributes based on the new schema */
412 ret = dsdb_schema_set_attributes(ldb, schema, true);
413 if (ret != LDB_SUCCESS) {
421 * Global variable to hold one copy of the schema, used to avoid memory bloat
423 static struct dsdb_schema *global_schema;
426 * Make this ldb use a specified schema, already fully calculated and belonging to another ldb
428 int dsdb_reference_schema(struct ldb_context *ldb, struct dsdb_schema *schema,
429 bool write_attributes)
432 struct dsdb_schema *old_schema;
433 old_schema = ldb_get_opaque(ldb, "dsdb_schema");
434 ret = ldb_set_opaque(ldb, "dsdb_schema", schema);
435 if (ret != LDB_SUCCESS) {
439 /* Remove the reference to the schema we just overwrote - if there was
440 * none, NULL is harmless here */
441 talloc_unlink(ldb, old_schema);
443 if (talloc_reference(ldb, schema) == NULL) {
447 ret = dsdb_schema_set_attributes(ldb, schema, write_attributes);
448 if (ret != LDB_SUCCESS) {
456 * Make this ldb use the 'global' schema, setup to avoid having multiple copies in this process
458 int dsdb_set_global_schema(struct ldb_context *ldb)
461 void *use_global_schema = (void *)1;
462 if (!global_schema) {
465 ret = ldb_set_opaque(ldb, "dsdb_use_global_schema", use_global_schema);
466 if (ret != LDB_SUCCESS) {
470 /* Set the new attributes based on the new schema */
471 ret = dsdb_schema_set_attributes(ldb, global_schema, false /* Don't write attributes, it's expensive */);
472 if (ret == LDB_SUCCESS) {
473 /* Keep a reference to this schema, just in case the original copy is replaced */
474 if (talloc_reference(ldb, global_schema) == NULL) {
483 * Find the schema object for this ldb
485 * If reference_ctx is not NULL, then talloc_reference onto that context
488 struct dsdb_schema *dsdb_get_schema(struct ldb_context *ldb, TALLOC_CTX *reference_ctx)
491 struct dsdb_schema *schema_out;
492 struct dsdb_schema *schema_in;
493 bool use_global_schema;
494 TALLOC_CTX *tmp_ctx = talloc_new(reference_ctx);
499 /* see if we have a cached copy */
500 use_global_schema = (ldb_get_opaque(ldb, "dsdb_use_global_schema") != NULL);
501 if (use_global_schema) {
502 schema_in = global_schema;
504 p = ldb_get_opaque(ldb, "dsdb_schema");
506 schema_in = talloc_get_type(p, struct dsdb_schema);
508 talloc_free(tmp_ctx);
513 if (schema_in->refresh_fn && !schema_in->refresh_in_progress) {
514 if (!talloc_reference(tmp_ctx, schema_in)) {
515 /* ensure that the schema_in->refresh_in_progress
516 * remains valid for the right amount of time */
517 talloc_free(tmp_ctx);
520 schema_in->refresh_in_progress = true;
521 /* This may change schema, if it needs to reload it from disk */
522 schema_out = schema_in->refresh_fn(schema_in->loaded_from_module,
525 schema_in->refresh_in_progress = false;
527 schema_out = schema_in;
530 /* This removes the extra reference above */
531 talloc_free(tmp_ctx);
532 if (!reference_ctx) {
535 return talloc_reference(reference_ctx, schema_out);
540 * Make the schema found on this ldb the 'global' schema
543 void dsdb_make_schema_global(struct ldb_context *ldb, struct dsdb_schema *schema)
550 talloc_unlink(talloc_autofree_context(), global_schema);
553 /* we want the schema to be around permanently */
554 talloc_reparent(ldb, talloc_autofree_context(), schema);
555 global_schema = schema;
557 /* This calls the talloc_reference() of the global schema back onto the ldb */
558 dsdb_set_global_schema(ldb);
561 /* When loading the schema from LDIF files, we don't get the extended DNs.
563 We need to set these up, so that from the moment we start the provision,
564 the defaultObjectCategory links are set up correctly.
566 int dsdb_schema_fill_extended_dn(struct ldb_context *ldb, struct dsdb_schema *schema)
568 struct dsdb_class *cur;
569 const struct dsdb_class *target_class;
570 for (cur = schema->classes; cur; cur = cur->next) {
571 const struct ldb_val *rdn;
574 struct ldb_dn *dn = ldb_dn_new(NULL, ldb, cur->defaultObjectCategory);
577 return LDB_ERR_INVALID_DN_SYNTAX;
579 rdn = ldb_dn_get_component_val(dn, 0);
582 return LDB_ERR_INVALID_DN_SYNTAX;
584 target_class = dsdb_class_by_cn_ldb_val(schema, rdn);
587 return LDB_ERR_CONSTRAINT_VIOLATION;
590 status = GUID_to_ndr_blob(&target_class->objectGUID, dn, &guid);
591 if (!NT_STATUS_IS_OK(status)) {
593 return ldb_operr(ldb);
595 ldb_dn_set_extended_component(dn, "GUID", &guid);
597 cur->defaultObjectCategory = ldb_dn_get_extended_linearized(cur, dn, 1);
604 * Add an element to the schema (attribute or class) from an LDB message
606 WERROR dsdb_schema_set_el_from_ldb_msg(struct ldb_context *ldb, struct dsdb_schema *schema,
607 struct ldb_message *msg)
609 if (samdb_find_attribute(ldb, msg,
610 "objectclass", "attributeSchema") != NULL) {
611 return dsdb_attribute_from_ldb(ldb, schema, msg);
612 } else if (samdb_find_attribute(ldb, msg,
613 "objectclass", "classSchema") != NULL) {
614 return dsdb_class_from_ldb(schema, msg);
617 /* Don't fail on things not classes or attributes */
622 * Rather than read a schema from the LDB itself, read it from an ldif
623 * file. This allows schema to be loaded and used while adding the
624 * schema itself to the directory.
627 WERROR dsdb_set_schema_from_ldif(struct ldb_context *ldb, const char *pf, const char *df)
629 struct ldb_ldif *ldif;
630 struct ldb_message *msg;
634 struct dsdb_schema *schema;
635 const struct ldb_val *prefix_val;
636 const struct ldb_val *info_val;
637 struct ldb_val info_val_default;
640 mem_ctx = talloc_new(ldb);
645 schema = dsdb_new_schema(mem_ctx);
647 schema->fsmo.we_are_master = true;
648 schema->fsmo.master_dn = ldb_dn_new_fmt(schema, ldb, "@PROVISION_SCHEMA_MASTER");
649 if (!schema->fsmo.master_dn) {
654 * load the prefixMap attribute from pf
656 ldif = ldb_ldif_read_string(ldb, &pf);
658 status = WERR_INVALID_PARAM;
661 talloc_steal(mem_ctx, ldif);
663 msg = ldb_msg_canonicalize(ldb, ldif->msg);
667 talloc_steal(mem_ctx, msg);
670 prefix_val = ldb_msg_find_ldb_val(msg, "prefixMap");
672 status = WERR_INVALID_PARAM;
676 info_val = ldb_msg_find_ldb_val(msg, "schemaInfo");
678 status = dsdb_schema_info_blob_new(mem_ctx, &info_val_default);
679 W_ERROR_NOT_OK_GOTO(status, failed);
680 info_val = &info_val_default;
683 status = dsdb_load_oid_mappings_ldb(schema, prefix_val, info_val);
684 if (!W_ERROR_IS_OK(status)) {
685 DEBUG(0,("ERROR: dsdb_load_oid_mappings_ldb() failed with %s\n", win_errstr(status)));
690 * load the attribute and class definitions out of df
692 while ((ldif = ldb_ldif_read_string(ldb, &df))) {
693 talloc_steal(mem_ctx, ldif);
695 msg = ldb_msg_canonicalize(ldb, ldif->msg);
700 status = dsdb_schema_set_el_from_ldb_msg(ldb, schema, msg);
702 if (!W_ERROR_IS_OK(status)) {
707 ret = dsdb_set_schema(ldb, schema);
708 if (ret != LDB_SUCCESS) {
709 status = WERR_FOOBAR;
713 ret = dsdb_schema_fill_extended_dn(ldb, schema);
714 if (ret != LDB_SUCCESS) {
715 status = WERR_FOOBAR;
725 talloc_free(mem_ctx);