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, 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);
145 if (ret == LDB_ERR_OPERATIONS_ERROR || ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS || ret == LDB_ERR_INVALID_DN_SYNTAX) {
146 /* We might be on a read-only DB or LDAP */
149 if (ret != LDB_SUCCESS) {
150 talloc_free(mem_ctx);
154 /* Now write out the indexs, as found in the schema (if they have changed) */
156 ret = ldb_search(ldb, mem_ctx, &res_idx, msg_idx->dn, LDB_SCOPE_BASE, NULL, "dn=%s", ldb_dn_get_linearized(msg_idx->dn));
157 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
158 ret = ldb_add(ldb, msg_idx);
159 } else if (ret != LDB_SUCCESS) {
160 } else if (res_idx->count != 1) {
161 ret = ldb_add(ldb, msg_idx);
164 /* Annoyingly added to our search results */
165 ldb_msg_remove_attr(res_idx->msgs[0], "distinguishedName");
167 mod_msg = ldb_msg_diff(ldb, res_idx->msgs[0], msg_idx);
168 if (mod_msg->num_elements > 0) {
169 ret = dsdb_replace(ldb, mod_msg, 0);
172 if (ret == LDB_ERR_OPERATIONS_ERROR || ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS || ret == LDB_ERR_INVALID_DN_SYNTAX) {
173 /* We might be on a read-only DB */
176 talloc_free(mem_ctx);
180 talloc_free(mem_ctx);
181 return LDB_ERR_OPERATIONS_ERROR;
184 static int uint32_cmp(uint32_t c1, uint32_t c2)
186 if (c1 == c2) return 0;
187 return c1 > c2 ? 1 : -1;
190 static int dsdb_compare_class_by_lDAPDisplayName(struct dsdb_class **c1, struct dsdb_class **c2)
192 return strcasecmp((*c1)->lDAPDisplayName, (*c2)->lDAPDisplayName);
194 static int dsdb_compare_class_by_governsID_id(struct dsdb_class **c1, struct dsdb_class **c2)
196 return uint32_cmp((*c1)->governsID_id, (*c2)->governsID_id);
198 static int dsdb_compare_class_by_governsID_oid(struct dsdb_class **c1, struct dsdb_class **c2)
200 return strcasecmp((*c1)->governsID_oid, (*c2)->governsID_oid);
202 static int dsdb_compare_class_by_cn(struct dsdb_class **c1, struct dsdb_class **c2)
204 return strcasecmp((*c1)->cn, (*c2)->cn);
207 static int dsdb_compare_attribute_by_lDAPDisplayName(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
209 return strcasecmp((*a1)->lDAPDisplayName, (*a2)->lDAPDisplayName);
211 static int dsdb_compare_attribute_by_attributeID_id(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
213 return uint32_cmp((*a1)->attributeID_id, (*a2)->attributeID_id);
215 static int dsdb_compare_attribute_by_attributeID_oid(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
217 return strcasecmp((*a1)->attributeID_oid, (*a2)->attributeID_oid);
219 static int dsdb_compare_attribute_by_linkID(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
221 return uint32_cmp((*a1)->linkID, (*a2)->linkID);
225 create the sorted accessor arrays for the schema
227 static int dsdb_setup_sorted_accessors(struct ldb_context *ldb,
228 struct dsdb_schema *schema)
230 struct dsdb_class *cur;
231 struct dsdb_attribute *a;
233 unsigned int num_int_id;
235 talloc_free(schema->classes_by_lDAPDisplayName);
236 talloc_free(schema->classes_by_governsID_id);
237 talloc_free(schema->classes_by_governsID_oid);
238 talloc_free(schema->classes_by_cn);
240 /* count the classes */
241 for (i=0, cur=schema->classes; cur; i++, cur=cur->next) /* noop */ ;
242 schema->num_classes = i;
244 /* setup classes_by_* */
245 schema->classes_by_lDAPDisplayName = talloc_array(schema, struct dsdb_class *, i);
246 schema->classes_by_governsID_id = talloc_array(schema, struct dsdb_class *, i);
247 schema->classes_by_governsID_oid = talloc_array(schema, struct dsdb_class *, i);
248 schema->classes_by_cn = talloc_array(schema, struct dsdb_class *, i);
249 if (schema->classes_by_lDAPDisplayName == NULL ||
250 schema->classes_by_governsID_id == NULL ||
251 schema->classes_by_governsID_oid == NULL ||
252 schema->classes_by_cn == NULL) {
256 for (i=0, cur=schema->classes; cur; i++, cur=cur->next) {
257 schema->classes_by_lDAPDisplayName[i] = cur;
258 schema->classes_by_governsID_id[i] = cur;
259 schema->classes_by_governsID_oid[i] = cur;
260 schema->classes_by_cn[i] = cur;
263 /* sort the arrays */
264 TYPESAFE_QSORT(schema->classes_by_lDAPDisplayName, schema->num_classes, dsdb_compare_class_by_lDAPDisplayName);
265 TYPESAFE_QSORT(schema->classes_by_governsID_id, schema->num_classes, dsdb_compare_class_by_governsID_id);
266 TYPESAFE_QSORT(schema->classes_by_governsID_oid, schema->num_classes, dsdb_compare_class_by_governsID_oid);
267 TYPESAFE_QSORT(schema->classes_by_cn, schema->num_classes, dsdb_compare_class_by_cn);
269 /* now build the attribute accessor arrays */
270 talloc_free(schema->attributes_by_lDAPDisplayName);
271 talloc_free(schema->attributes_by_attributeID_id);
272 talloc_free(schema->attributes_by_msDS_IntId);
273 talloc_free(schema->attributes_by_attributeID_oid);
274 talloc_free(schema->attributes_by_linkID);
276 /* count the attributes
277 * and attributes with msDS-IntId set */
279 for (i=0, a=schema->attributes; a; i++, a=a->next) {
280 if (a->msDS_IntId != 0) {
284 schema->num_attributes = i;
285 schema->num_int_id_attr = num_int_id;
287 /* setup attributes_by_* */
288 schema->attributes_by_lDAPDisplayName = talloc_array(schema, struct dsdb_attribute *, i);
289 schema->attributes_by_attributeID_id = talloc_array(schema, struct dsdb_attribute *, i);
290 schema->attributes_by_msDS_IntId = talloc_array(schema,
291 struct dsdb_attribute *, num_int_id);
292 schema->attributes_by_attributeID_oid = talloc_array(schema, struct dsdb_attribute *, i);
293 schema->attributes_by_linkID = talloc_array(schema, struct dsdb_attribute *, i);
294 if (schema->attributes_by_lDAPDisplayName == NULL ||
295 schema->attributes_by_attributeID_id == NULL ||
296 schema->attributes_by_msDS_IntId == NULL ||
297 schema->attributes_by_attributeID_oid == NULL ||
298 schema->attributes_by_linkID == NULL) {
303 for (i=0, a=schema->attributes; a; i++, a=a->next) {
304 schema->attributes_by_lDAPDisplayName[i] = a;
305 schema->attributes_by_attributeID_id[i] = a;
306 schema->attributes_by_attributeID_oid[i] = a;
307 schema->attributes_by_linkID[i] = a;
308 /* append attr-by-msDS-IntId values */
309 if (a->msDS_IntId != 0) {
310 schema->attributes_by_msDS_IntId[num_int_id] = a;
314 SMB_ASSERT(num_int_id == schema->num_int_id_attr);
316 /* sort the arrays */
317 TYPESAFE_QSORT(schema->attributes_by_lDAPDisplayName, schema->num_attributes, dsdb_compare_attribute_by_lDAPDisplayName);
318 TYPESAFE_QSORT(schema->attributes_by_attributeID_id, schema->num_attributes, dsdb_compare_attribute_by_attributeID_id);
319 TYPESAFE_QSORT(schema->attributes_by_msDS_IntId, schema->num_int_id_attr, dsdb_compare_attribute_by_attributeID_id);
320 TYPESAFE_QSORT(schema->attributes_by_attributeID_oid, schema->num_attributes, dsdb_compare_attribute_by_attributeID_oid);
321 TYPESAFE_QSORT(schema->attributes_by_linkID, schema->num_attributes, dsdb_compare_attribute_by_linkID);
326 schema->classes_by_lDAPDisplayName = NULL;
327 schema->classes_by_governsID_id = NULL;
328 schema->classes_by_governsID_oid = NULL;
329 schema->classes_by_cn = NULL;
330 schema->attributes_by_lDAPDisplayName = NULL;
331 schema->attributes_by_attributeID_id = NULL;
332 schema->attributes_by_msDS_IntId = NULL;
333 schema->attributes_by_attributeID_oid = NULL;
334 schema->attributes_by_linkID = NULL;
336 return LDB_ERR_OPERATIONS_ERROR;
339 int dsdb_setup_schema_inversion(struct ldb_context *ldb, struct dsdb_schema *schema)
341 /* Walk the list of schema classes */
343 /* For each subClassOf, add us to subclasses of the parent */
345 /* collect these subclasses into a recursive list of total subclasses, preserving order */
347 /* For each subclass under 'top', write the index from it's
348 * order as an integer in the dsdb_class (for sorting
349 * objectClass lists efficiently) */
351 /* Walk the list of scheam classes */
353 /* Create a 'total possible superiors' on each class */
358 * Attach the schema to an opaque pointer on the ldb, so ldb modules
362 int dsdb_set_schema(struct ldb_context *ldb, struct dsdb_schema *schema)
366 ret = dsdb_setup_sorted_accessors(ldb, schema);
367 if (ret != LDB_SUCCESS) {
371 ret = schema_fill_constructed(schema);
372 if (ret != LDB_SUCCESS) {
376 ret = ldb_set_opaque(ldb, "dsdb_schema", schema);
377 if (ret != LDB_SUCCESS) {
381 ret = ldb_set_opaque(ldb, "dsdb_use_global_schema", NULL);
382 if (ret != LDB_SUCCESS) {
386 /* Set the new attributes based on the new schema */
387 ret = dsdb_schema_set_attributes(ldb, schema, true);
388 if (ret != LDB_SUCCESS) {
392 talloc_steal(ldb, schema);
398 * Global variable to hold one copy of the schema, used to avoid memory bloat
400 static struct dsdb_schema *global_schema;
403 * Make this ldb use a specified schema, already fully calculated and belonging to another ldb
405 int dsdb_reference_schema(struct ldb_context *ldb, struct dsdb_schema *schema,
406 bool write_attributes)
409 ret = ldb_set_opaque(ldb, "dsdb_schema", schema);
410 if (ret != LDB_SUCCESS) {
414 if (talloc_reference(ldb, schema) == NULL) {
415 return LDB_ERR_OPERATIONS_ERROR;
418 ret = dsdb_schema_set_attributes(ldb, schema, write_attributes);
419 if (ret != LDB_SUCCESS) {
427 * Make this ldb use the 'global' schema, setup to avoid having multiple copies in this process
429 int dsdb_set_global_schema(struct ldb_context *ldb)
432 void *use_global_schema = (void *)1;
433 if (!global_schema) {
437 ret = ldb_set_opaque(ldb, "dsdb_use_global_schema", use_global_schema);
438 if (ret != LDB_SUCCESS) {
442 /* Set the new attributes based on the new schema */
443 ret = dsdb_schema_set_attributes(ldb, global_schema, false /* Don't write attributes, it's expensive */);
444 if (ret == LDB_SUCCESS) {
445 /* Keep a reference to this schema, just incase the original copy is replaced */
446 if (talloc_reference(ldb, global_schema) == NULL) {
447 return LDB_ERR_OPERATIONS_ERROR;
455 * Find the schema object for this ldb
457 * If reference_ctx is not NULL, then talloc_reference onto that context
460 struct dsdb_schema *dsdb_get_schema(struct ldb_context *ldb, TALLOC_CTX *reference_ctx)
463 struct dsdb_schema *schema_out;
464 struct dsdb_schema *schema_in;
465 bool use_global_schema;
467 /* see if we have a cached copy */
468 use_global_schema = (ldb_get_opaque(ldb, "dsdb_use_global_schema") != NULL);
469 if (use_global_schema) {
470 schema_in = global_schema;
472 p = ldb_get_opaque(ldb, "dsdb_schema");
474 schema_in = talloc_get_type(p, struct dsdb_schema);
480 if (schema_in->refresh_fn && !schema_in->refresh_in_progress) {
481 schema_in->refresh_in_progress = true;
482 /* This may change schema, if it needs to reload it from disk */
483 schema_out = schema_in->refresh_fn(schema_in->loaded_from_module,
486 schema_in->refresh_in_progress = false;
487 if (schema_out != schema_in) {
488 talloc_unlink(schema_in, ldb);
491 schema_out = schema_in;
494 if (!reference_ctx) {
497 return talloc_reference(reference_ctx, schema_out);
502 * Make the schema found on this ldb the 'global' schema
505 void dsdb_make_schema_global(struct ldb_context *ldb, struct dsdb_schema *schema)
512 talloc_unlink(talloc_autofree_context(), global_schema);
515 /* Wipe any reference to the exact schema - we will set 'use the global schema' below */
516 ldb_set_opaque(ldb, "dsdb_schema", NULL);
518 /* we want the schema to be around permanently */
519 talloc_reparent(ldb, talloc_autofree_context(), schema);
520 global_schema = schema;
522 /* This calls the talloc_reference() of the global schema back onto the ldb */
523 dsdb_set_global_schema(ldb);
526 /* When loading the schema from LDIF files, we don't get the extended DNs.
528 We need to set these up, so that from the moment we start the provision, the defaultObjectCategory links are set up correctly.
530 int dsdb_schema_fill_extended_dn(struct ldb_context *ldb, struct dsdb_schema *schema)
532 struct dsdb_class *cur;
533 const struct dsdb_class *target_class;
534 for (cur = schema->classes; cur; cur = cur->next) {
535 const struct ldb_val *rdn;
538 struct ldb_dn *dn = ldb_dn_new(NULL, ldb, cur->defaultObjectCategory);
541 return LDB_ERR_INVALID_DN_SYNTAX;
543 rdn = ldb_dn_get_component_val(dn, 0);
546 return LDB_ERR_INVALID_DN_SYNTAX;
548 target_class = dsdb_class_by_cn_ldb_val(schema, rdn);
551 return LDB_ERR_CONSTRAINT_VIOLATION;
554 status = GUID_to_ndr_blob(&target_class->objectGUID, dn, &guid);
555 if (!NT_STATUS_IS_OK(status)) {
557 return LDB_ERR_OPERATIONS_ERROR;
559 ldb_dn_set_extended_component(dn, "GUID", &guid);
561 cur->defaultObjectCategory = ldb_dn_get_extended_linearized(cur, dn, 1);
568 * Add an element to the schema (attribute or class) from an LDB message
570 WERROR dsdb_schema_set_el_from_ldb_msg(struct ldb_context *ldb, struct dsdb_schema *schema,
571 struct ldb_message *msg)
573 if (samdb_find_attribute(ldb, msg,
574 "objectclass", "attributeSchema") != NULL) {
575 return dsdb_attribute_from_ldb(ldb, schema, msg);
576 } else if (samdb_find_attribute(ldb, msg,
577 "objectclass", "classSchema") != NULL) {
578 return dsdb_class_from_ldb(schema, msg);
581 /* Don't fail on things not classes or attributes */
586 * Rather than read a schema from the LDB itself, read it from an ldif
587 * file. This allows schema to be loaded and used while adding the
588 * schema itself to the directory.
591 WERROR dsdb_set_schema_from_ldif(struct ldb_context *ldb, const char *pf, const char *df)
593 struct ldb_ldif *ldif;
594 struct ldb_message *msg;
598 struct dsdb_schema *schema;
599 const struct ldb_val *prefix_val;
600 const struct ldb_val *info_val;
601 struct ldb_val info_val_default;
604 mem_ctx = talloc_new(ldb);
609 schema = dsdb_new_schema(mem_ctx);
611 schema->fsmo.we_are_master = true;
612 schema->fsmo.master_dn = ldb_dn_new_fmt(schema, ldb, "@PROVISION_SCHEMA_MASTER");
613 if (!schema->fsmo.master_dn) {
618 * load the prefixMap attribute from pf
620 ldif = ldb_ldif_read_string(ldb, &pf);
622 status = WERR_INVALID_PARAM;
625 talloc_steal(mem_ctx, ldif);
627 msg = ldb_msg_canonicalize(ldb, ldif->msg);
631 talloc_steal(mem_ctx, msg);
634 prefix_val = ldb_msg_find_ldb_val(msg, "prefixMap");
636 status = WERR_INVALID_PARAM;
640 info_val = ldb_msg_find_ldb_val(msg, "schemaInfo");
642 status = dsdb_schema_info_blob_new(mem_ctx, &info_val_default);
643 W_ERROR_NOT_OK_GOTO(status, failed);
644 info_val = &info_val_default;
647 status = dsdb_load_oid_mappings_ldb(schema, prefix_val, info_val);
648 if (!W_ERROR_IS_OK(status)) {
649 DEBUG(0,("ERROR: dsdb_load_oid_mappings_ldb() failed with %s\n", win_errstr(status)));
654 * load the attribute and class definitions outof df
656 while ((ldif = ldb_ldif_read_string(ldb, &df))) {
657 talloc_steal(mem_ctx, ldif);
659 msg = ldb_msg_canonicalize(ldb, ldif->msg);
664 status = dsdb_schema_set_el_from_ldb_msg(ldb, schema, msg);
666 if (!W_ERROR_IS_OK(status)) {
671 ret = dsdb_set_schema(ldb, schema);
672 if (ret != LDB_SUCCESS) {
673 status = WERR_FOOBAR;
677 ret = dsdb_schema_fill_extended_dn(ldb, schema);
678 if (ret != LDB_SUCCESS) {
679 status = WERR_FOOBAR;
689 talloc_free(mem_ctx);