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, "dn=%s", ldb_dn_get_linearized(msg->dn));
129 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
130 ret = ldb_add(ldb, msg);
131 } else if (ret != LDB_SUCCESS) {
132 } else if (res->count != 1) {
133 ret = ldb_add(ldb, msg);
136 /* Annoyingly added to our search results */
137 ldb_msg_remove_attr(res->msgs[0], "distinguishedName");
139 mod_msg = ldb_msg_diff(ldb, res->msgs[0], msg);
140 if (mod_msg->num_elements > 0) {
141 ret = dsdb_replace(ldb, mod_msg, 0);
143 talloc_free(mod_msg);
146 if (ret == LDB_ERR_OPERATIONS_ERROR || ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS || ret == LDB_ERR_INVALID_DN_SYNTAX) {
147 /* We might be on a read-only DB or LDAP */
150 if (ret != LDB_SUCCESS) {
151 talloc_free(mem_ctx);
155 /* Now write out the indexs, as found in the schema (if they have changed) */
157 ret = ldb_search(ldb, mem_ctx, &res_idx, msg_idx->dn, LDB_SCOPE_BASE, NULL, "dn=%s", ldb_dn_get_linearized(msg_idx->dn));
158 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
159 ret = ldb_add(ldb, msg_idx);
160 } else if (ret != LDB_SUCCESS) {
161 } else if (res_idx->count != 1) {
162 ret = ldb_add(ldb, msg_idx);
165 /* Annoyingly added to our search results */
166 ldb_msg_remove_attr(res_idx->msgs[0], "distinguishedName");
168 mod_msg = ldb_msg_diff(ldb, res_idx->msgs[0], msg_idx);
169 if (mod_msg->num_elements > 0) {
170 ret = dsdb_replace(ldb, mod_msg, 0);
172 talloc_free(mod_msg);
174 if (ret == LDB_ERR_OPERATIONS_ERROR || ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS || ret == LDB_ERR_INVALID_DN_SYNTAX) {
175 /* We might be on a read-only DB */
178 talloc_free(mem_ctx);
182 talloc_free(mem_ctx);
183 return LDB_ERR_OPERATIONS_ERROR;
186 static int uint32_cmp(uint32_t c1, uint32_t c2)
188 if (c1 == c2) return 0;
189 return c1 > c2 ? 1 : -1;
192 static int dsdb_compare_class_by_lDAPDisplayName(struct dsdb_class **c1, struct dsdb_class **c2)
194 return strcasecmp((*c1)->lDAPDisplayName, (*c2)->lDAPDisplayName);
196 static int dsdb_compare_class_by_governsID_id(struct dsdb_class **c1, struct dsdb_class **c2)
198 return uint32_cmp((*c1)->governsID_id, (*c2)->governsID_id);
200 static int dsdb_compare_class_by_governsID_oid(struct dsdb_class **c1, struct dsdb_class **c2)
202 return strcasecmp((*c1)->governsID_oid, (*c2)->governsID_oid);
204 static int dsdb_compare_class_by_cn(struct dsdb_class **c1, struct dsdb_class **c2)
206 return strcasecmp((*c1)->cn, (*c2)->cn);
209 static int dsdb_compare_attribute_by_lDAPDisplayName(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
211 return strcasecmp((*a1)->lDAPDisplayName, (*a2)->lDAPDisplayName);
213 static int dsdb_compare_attribute_by_attributeID_id(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
215 return uint32_cmp((*a1)->attributeID_id, (*a2)->attributeID_id);
217 static int dsdb_compare_attribute_by_attributeID_oid(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
219 return strcasecmp((*a1)->attributeID_oid, (*a2)->attributeID_oid);
221 static int dsdb_compare_attribute_by_linkID(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
223 return uint32_cmp((*a1)->linkID, (*a2)->linkID);
227 * Clean up Classes and Attributes accessor arrays
229 static void dsdb_sorted_accessors_free(struct dsdb_schema *schema)
231 /* free classes accessors */
232 TALLOC_FREE(schema->classes_by_lDAPDisplayName);
233 TALLOC_FREE(schema->classes_by_governsID_id);
234 TALLOC_FREE(schema->classes_by_governsID_oid);
235 TALLOC_FREE(schema->classes_by_cn);
236 /* free attribute accessors */
237 TALLOC_FREE(schema->attributes_by_lDAPDisplayName);
238 TALLOC_FREE(schema->attributes_by_attributeID_id);
239 TALLOC_FREE(schema->attributes_by_msDS_IntId);
240 TALLOC_FREE(schema->attributes_by_attributeID_oid);
241 TALLOC_FREE(schema->attributes_by_linkID);
245 create the sorted accessor arrays for the schema
247 static int dsdb_setup_sorted_accessors(struct ldb_context *ldb,
248 struct dsdb_schema *schema)
250 struct dsdb_class *cur;
251 struct dsdb_attribute *a;
253 unsigned int num_int_id;
255 /* free all caches */
256 dsdb_sorted_accessors_free(schema);
258 /* count the classes */
259 for (i=0, cur=schema->classes; cur; i++, cur=cur->next) /* noop */ ;
260 schema->num_classes = i;
262 /* setup classes_by_* */
263 schema->classes_by_lDAPDisplayName = talloc_array(schema, struct dsdb_class *, i);
264 schema->classes_by_governsID_id = talloc_array(schema, struct dsdb_class *, i);
265 schema->classes_by_governsID_oid = talloc_array(schema, struct dsdb_class *, i);
266 schema->classes_by_cn = talloc_array(schema, struct dsdb_class *, i);
267 if (schema->classes_by_lDAPDisplayName == NULL ||
268 schema->classes_by_governsID_id == NULL ||
269 schema->classes_by_governsID_oid == NULL ||
270 schema->classes_by_cn == NULL) {
274 for (i=0, cur=schema->classes; cur; i++, cur=cur->next) {
275 schema->classes_by_lDAPDisplayName[i] = cur;
276 schema->classes_by_governsID_id[i] = cur;
277 schema->classes_by_governsID_oid[i] = cur;
278 schema->classes_by_cn[i] = cur;
281 /* sort the arrays */
282 TYPESAFE_QSORT(schema->classes_by_lDAPDisplayName, schema->num_classes, dsdb_compare_class_by_lDAPDisplayName);
283 TYPESAFE_QSORT(schema->classes_by_governsID_id, schema->num_classes, dsdb_compare_class_by_governsID_id);
284 TYPESAFE_QSORT(schema->classes_by_governsID_oid, schema->num_classes, dsdb_compare_class_by_governsID_oid);
285 TYPESAFE_QSORT(schema->classes_by_cn, schema->num_classes, dsdb_compare_class_by_cn);
287 /* now build the attribute accessor arrays */
289 /* count the attributes
290 * and attributes with msDS-IntId set */
292 for (i=0, a=schema->attributes; a; i++, a=a->next) {
293 if (a->msDS_IntId != 0) {
297 schema->num_attributes = i;
298 schema->num_int_id_attr = num_int_id;
300 /* setup attributes_by_* */
301 schema->attributes_by_lDAPDisplayName = talloc_array(schema, struct dsdb_attribute *, i);
302 schema->attributes_by_attributeID_id = talloc_array(schema, struct dsdb_attribute *, i);
303 schema->attributes_by_msDS_IntId = talloc_array(schema,
304 struct dsdb_attribute *, num_int_id);
305 schema->attributes_by_attributeID_oid = talloc_array(schema, struct dsdb_attribute *, i);
306 schema->attributes_by_linkID = talloc_array(schema, struct dsdb_attribute *, i);
307 if (schema->attributes_by_lDAPDisplayName == NULL ||
308 schema->attributes_by_attributeID_id == NULL ||
309 schema->attributes_by_msDS_IntId == NULL ||
310 schema->attributes_by_attributeID_oid == NULL ||
311 schema->attributes_by_linkID == NULL) {
316 for (i=0, a=schema->attributes; a; i++, a=a->next) {
317 schema->attributes_by_lDAPDisplayName[i] = a;
318 schema->attributes_by_attributeID_id[i] = a;
319 schema->attributes_by_attributeID_oid[i] = a;
320 schema->attributes_by_linkID[i] = a;
321 /* append attr-by-msDS-IntId values */
322 if (a->msDS_IntId != 0) {
323 schema->attributes_by_msDS_IntId[num_int_id] = a;
327 SMB_ASSERT(num_int_id == schema->num_int_id_attr);
329 /* sort the arrays */
330 TYPESAFE_QSORT(schema->attributes_by_lDAPDisplayName, schema->num_attributes, dsdb_compare_attribute_by_lDAPDisplayName);
331 TYPESAFE_QSORT(schema->attributes_by_attributeID_id, schema->num_attributes, dsdb_compare_attribute_by_attributeID_id);
332 TYPESAFE_QSORT(schema->attributes_by_msDS_IntId, schema->num_int_id_attr, dsdb_compare_attribute_by_attributeID_id);
333 TYPESAFE_QSORT(schema->attributes_by_attributeID_oid, schema->num_attributes, dsdb_compare_attribute_by_attributeID_oid);
334 TYPESAFE_QSORT(schema->attributes_by_linkID, schema->num_attributes, dsdb_compare_attribute_by_linkID);
339 dsdb_sorted_accessors_free(schema);
341 return LDB_ERR_OPERATIONS_ERROR;
344 int dsdb_setup_schema_inversion(struct ldb_context *ldb, struct dsdb_schema *schema)
346 /* Walk the list of schema classes */
348 /* For each subClassOf, add us to subclasses of the parent */
350 /* collect these subclasses into a recursive list of total subclasses, preserving order */
352 /* For each subclass under 'top', write the index from it's
353 * order as an integer in the dsdb_class (for sorting
354 * objectClass lists efficiently) */
356 /* Walk the list of scheam classes */
358 /* Create a 'total possible superiors' on each class */
363 * Attach the schema to an opaque pointer on the ldb, so ldb modules
367 int dsdb_set_schema(struct ldb_context *ldb, struct dsdb_schema *schema)
369 struct dsdb_schema *old_schema;
372 ret = dsdb_setup_sorted_accessors(ldb, schema);
373 if (ret != LDB_SUCCESS) {
377 ret = schema_fill_constructed(schema);
378 if (ret != LDB_SUCCESS) {
382 old_schema = ldb_get_opaque(ldb, "dsdb_schema");
384 ret = ldb_set_opaque(ldb, "dsdb_schema", schema);
385 if (ret != LDB_SUCCESS) {
389 /* Remove the reference to the schema we just overwrote - if there was
390 * none, NULL is harmless here */
391 if (old_schema != schema) {
392 talloc_unlink(ldb, old_schema);
393 talloc_steal(ldb, schema);
396 ret = ldb_set_opaque(ldb, "dsdb_use_global_schema", NULL);
397 if (ret != LDB_SUCCESS) {
401 /* Set the new attributes based on the new schema */
402 ret = dsdb_schema_set_attributes(ldb, schema, true);
403 if (ret != LDB_SUCCESS) {
411 * Global variable to hold one copy of the schema, used to avoid memory bloat
413 static struct dsdb_schema *global_schema;
416 * Make this ldb use a specified schema, already fully calculated and belonging to another ldb
418 int dsdb_reference_schema(struct ldb_context *ldb, struct dsdb_schema *schema,
419 bool write_attributes)
422 struct dsdb_schema *old_schema;
423 old_schema = ldb_get_opaque(ldb, "dsdb_schema");
424 ret = ldb_set_opaque(ldb, "dsdb_schema", schema);
425 if (ret != LDB_SUCCESS) {
429 /* Remove the refernece to the schema we just overwrote - if there was none, NULL is harmless here */
430 talloc_unlink(ldb, old_schema);
432 if (talloc_reference(ldb, schema) == NULL) {
433 return LDB_ERR_OPERATIONS_ERROR;
436 ret = dsdb_schema_set_attributes(ldb, schema, write_attributes);
437 if (ret != LDB_SUCCESS) {
445 * Make this ldb use the 'global' schema, setup to avoid having multiple copies in this process
447 int dsdb_set_global_schema(struct ldb_context *ldb)
450 void *use_global_schema = (void *)1;
451 if (!global_schema) {
454 ret = ldb_set_opaque(ldb, "dsdb_use_global_schema", use_global_schema);
455 if (ret != LDB_SUCCESS) {
459 /* Set the new attributes based on the new schema */
460 ret = dsdb_schema_set_attributes(ldb, global_schema, false /* Don't write attributes, it's expensive */);
461 if (ret == LDB_SUCCESS) {
462 /* Keep a reference to this schema, just incase the original copy is replaced */
463 if (talloc_reference(ldb, global_schema) == NULL) {
464 return LDB_ERR_OPERATIONS_ERROR;
472 * Find the schema object for this ldb
474 * If reference_ctx is not NULL, then talloc_reference onto that context
477 struct dsdb_schema *dsdb_get_schema(struct ldb_context *ldb, TALLOC_CTX *reference_ctx)
480 struct dsdb_schema *schema_out;
481 struct dsdb_schema *schema_in;
482 bool use_global_schema;
483 TALLOC_CTX *tmp_ctx = talloc_new(reference_ctx);
488 /* see if we have a cached copy */
489 use_global_schema = (ldb_get_opaque(ldb, "dsdb_use_global_schema") != NULL);
490 if (use_global_schema) {
491 schema_in = global_schema;
493 p = ldb_get_opaque(ldb, "dsdb_schema");
495 schema_in = talloc_get_type(p, struct dsdb_schema);
497 talloc_free(tmp_ctx);
502 if (schema_in->refresh_fn && !schema_in->refresh_in_progress) {
503 if (!talloc_reference(tmp_ctx, schema_in)) {
504 /* ensure that the schema_in->refresh_in_progress remains valid for the right amount of time */
505 talloc_free(tmp_ctx);
508 schema_in->refresh_in_progress = true;
509 /* This may change schema, if it needs to reload it from disk */
510 schema_out = schema_in->refresh_fn(schema_in->loaded_from_module,
513 schema_in->refresh_in_progress = false;
515 schema_out = schema_in;
518 /* This removes the extra reference above */
519 talloc_free(tmp_ctx);
520 if (!reference_ctx) {
523 return talloc_reference(reference_ctx, schema_out);
528 * Make the schema found on this ldb the 'global' schema
531 void dsdb_make_schema_global(struct ldb_context *ldb, struct dsdb_schema *schema)
538 talloc_unlink(talloc_autofree_context(), global_schema);
541 /* we want the schema to be around permanently */
542 talloc_reparent(ldb, talloc_autofree_context(), schema);
543 global_schema = schema;
545 /* This calls the talloc_reference() of the global schema back onto the ldb */
546 dsdb_set_global_schema(ldb);
549 /* When loading the schema from LDIF files, we don't get the extended DNs.
551 We need to set these up, so that from the moment we start the provision, the defaultObjectCategory links are set up correctly.
553 int dsdb_schema_fill_extended_dn(struct ldb_context *ldb, struct dsdb_schema *schema)
555 struct dsdb_class *cur;
556 const struct dsdb_class *target_class;
557 for (cur = schema->classes; cur; cur = cur->next) {
558 const struct ldb_val *rdn;
561 struct ldb_dn *dn = ldb_dn_new(NULL, ldb, cur->defaultObjectCategory);
564 return LDB_ERR_INVALID_DN_SYNTAX;
566 rdn = ldb_dn_get_component_val(dn, 0);
569 return LDB_ERR_INVALID_DN_SYNTAX;
571 target_class = dsdb_class_by_cn_ldb_val(schema, rdn);
574 return LDB_ERR_CONSTRAINT_VIOLATION;
577 status = GUID_to_ndr_blob(&target_class->objectGUID, dn, &guid);
578 if (!NT_STATUS_IS_OK(status)) {
580 return LDB_ERR_OPERATIONS_ERROR;
582 ldb_dn_set_extended_component(dn, "GUID", &guid);
584 cur->defaultObjectCategory = ldb_dn_get_extended_linearized(cur, dn, 1);
591 * Add an element to the schema (attribute or class) from an LDB message
593 WERROR dsdb_schema_set_el_from_ldb_msg(struct ldb_context *ldb, struct dsdb_schema *schema,
594 struct ldb_message *msg)
596 if (samdb_find_attribute(ldb, msg,
597 "objectclass", "attributeSchema") != NULL) {
598 return dsdb_attribute_from_ldb(ldb, schema, msg);
599 } else if (samdb_find_attribute(ldb, msg,
600 "objectclass", "classSchema") != NULL) {
601 return dsdb_class_from_ldb(schema, msg);
604 /* Don't fail on things not classes or attributes */
609 * Rather than read a schema from the LDB itself, read it from an ldif
610 * file. This allows schema to be loaded and used while adding the
611 * schema itself to the directory.
614 WERROR dsdb_set_schema_from_ldif(struct ldb_context *ldb, const char *pf, const char *df)
616 struct ldb_ldif *ldif;
617 struct ldb_message *msg;
621 struct dsdb_schema *schema;
622 const struct ldb_val *prefix_val;
623 const struct ldb_val *info_val;
624 struct ldb_val info_val_default;
627 mem_ctx = talloc_new(ldb);
632 schema = dsdb_new_schema(mem_ctx);
634 schema->fsmo.we_are_master = true;
635 schema->fsmo.master_dn = ldb_dn_new_fmt(schema, ldb, "@PROVISION_SCHEMA_MASTER");
636 if (!schema->fsmo.master_dn) {
641 * load the prefixMap attribute from pf
643 ldif = ldb_ldif_read_string(ldb, &pf);
645 status = WERR_INVALID_PARAM;
648 talloc_steal(mem_ctx, ldif);
650 msg = ldb_msg_canonicalize(ldb, ldif->msg);
654 talloc_steal(mem_ctx, msg);
657 prefix_val = ldb_msg_find_ldb_val(msg, "prefixMap");
659 status = WERR_INVALID_PARAM;
663 info_val = ldb_msg_find_ldb_val(msg, "schemaInfo");
665 status = dsdb_schema_info_blob_new(mem_ctx, &info_val_default);
666 W_ERROR_NOT_OK_GOTO(status, failed);
667 info_val = &info_val_default;
670 status = dsdb_load_oid_mappings_ldb(schema, prefix_val, info_val);
671 if (!W_ERROR_IS_OK(status)) {
672 DEBUG(0,("ERROR: dsdb_load_oid_mappings_ldb() failed with %s\n", win_errstr(status)));
677 * load the attribute and class definitions outof df
679 while ((ldif = ldb_ldif_read_string(ldb, &df))) {
680 talloc_steal(mem_ctx, ldif);
682 msg = ldb_msg_canonicalize(ldb, ldif->msg);
687 status = dsdb_schema_set_el_from_ldb_msg(ldb, schema, msg);
689 if (!W_ERROR_IS_OK(status)) {
694 ret = dsdb_set_schema(ldb, schema);
695 if (ret != LDB_SUCCESS) {
696 status = WERR_FOOBAR;
700 ret = dsdb_schema_fill_extended_dn(ldb, schema);
701 if (ret != LDB_SUCCESS) {
702 status = WERR_FOOBAR;
712 talloc_free(mem_ctx);