s4-dsdb Remove unused schema function
[amitay/samba.git] / source4 / dsdb / schema / schema_set.c
1 /*
2    Unix SMB/CIFS implementation.
3    DSDB schema header
4
5    Copyright (C) Stefan Metzmacher <metze@samba.org> 2006-2007
6    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006-2008
7    Copyright (C) Matthieu Patou <mat@matws.net> 2011
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
22 */
23
24 #include "includes.h"
25 #include "lib/util/dlinklist.h"
26 #include "dsdb/samdb/samdb.h"
27 #include <ldb_module.h>
28 #include "param/param.h"
29 #include "librpc/ndr/libndr.h"
30 #include "librpc/gen_ndr/ndr_misc.h"
31 #include "lib/util/tsort.h"
32
33 /* change this when we change something in our schema code that
34  * requires a re-index of the database
35  */
36 #define SAMDB_INDEXING_VERSION "2"
37
38 /*
39   override the name to attribute handler function
40  */
41 const struct ldb_schema_attribute *dsdb_attribute_handler_override(struct ldb_context *ldb,
42                                                                    void *private_data,
43                                                                    const char *name)
44 {
45         struct dsdb_schema *schema = talloc_get_type_abort(private_data, struct dsdb_schema);
46         const struct dsdb_attribute *a = dsdb_attribute_by_lDAPDisplayName(schema, name);
47         if (a == NULL) {
48                 /* this will fall back to ldb internal handling */
49                 return NULL;
50         }
51         return a->ldb_schema_attribute;
52 }
53
54 static int dsdb_schema_set_attributes(struct ldb_context *ldb, struct dsdb_schema *schema, bool write_attributes)
55 {
56         int ret = LDB_SUCCESS;
57         struct ldb_result *res;
58         struct ldb_result *res_idx;
59         struct dsdb_attribute *attr;
60         struct ldb_message *mod_msg;
61         TALLOC_CTX *mem_ctx;
62         struct ldb_message *msg;
63         struct ldb_message *msg_idx;
64
65         /* setup our own attribute name to schema handler */
66         ldb_schema_attribute_set_override_handler(ldb, dsdb_attribute_handler_override, schema);
67
68         if (!write_attributes) {
69                 return ret;
70         }
71
72         mem_ctx = talloc_new(ldb);
73         if (!mem_ctx) {
74                 return ldb_oom(ldb);
75         }
76
77         msg = ldb_msg_new(mem_ctx);
78         if (!msg) {
79                 ldb_oom(ldb);
80                 goto op_error;
81         }
82         msg_idx = ldb_msg_new(mem_ctx);
83         if (!msg_idx) {
84                 ldb_oom(ldb);
85                 goto op_error;
86         }
87         msg->dn = ldb_dn_new(msg, ldb, "@ATTRIBUTES");
88         if (!msg->dn) {
89                 ldb_oom(ldb);
90                 goto op_error;
91         }
92         msg_idx->dn = ldb_dn_new(msg_idx, ldb, "@INDEXLIST");
93         if (!msg_idx->dn) {
94                 ldb_oom(ldb);
95                 goto op_error;
96         }
97
98         ret = ldb_msg_add_string(msg_idx, "@IDXONE", "1");
99         if (ret != LDB_SUCCESS) {
100                 goto op_error;
101         }
102
103
104         ret = ldb_msg_add_string(msg_idx, "@IDXVERSION", SAMDB_INDEXING_VERSION);
105         if (ret != LDB_SUCCESS) {
106                 goto op_error;
107         }
108
109         for (attr = schema->attributes; attr; attr = attr->next) {
110                 const char *syntax = attr->syntax->ldb_syntax;
111
112                 if (!syntax) {
113                         syntax = attr->syntax->ldap_oid;
114                 }
115
116                 /*
117                  * Write out a rough approximation of the schema
118                  * as an @ATTRIBUTES value, for bootstrapping
119                  */
120                 if (strcmp(syntax, LDB_SYNTAX_INTEGER) == 0) {
121                         ret = ldb_msg_add_string(msg, attr->lDAPDisplayName, "INTEGER");
122                 } else if (strcmp(syntax, LDB_SYNTAX_DIRECTORY_STRING) == 0) {
123                         ret = ldb_msg_add_string(msg, attr->lDAPDisplayName, "CASE_INSENSITIVE");
124                 }
125                 if (ret != LDB_SUCCESS) {
126                         break;
127                 }
128
129                 if (attr->searchFlags & SEARCH_FLAG_ATTINDEX) {
130                         ret = ldb_msg_add_string(msg_idx, "@IDXATTR", attr->lDAPDisplayName);
131                         if (ret != LDB_SUCCESS) {
132                                 break;
133                         }
134                 }
135         }
136
137         if (ret != LDB_SUCCESS) {
138                 talloc_free(mem_ctx);
139                 return ret;
140         }
141
142         /*
143          * Try to avoid churning the attributes too much,
144          * we only want to do this if they have changed
145          */
146         ret = ldb_search(ldb, mem_ctx, &res, msg->dn, LDB_SCOPE_BASE, NULL,
147                          NULL);
148         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
149                 ret = ldb_add(ldb, msg);
150         } else if (ret != LDB_SUCCESS) {
151         } else if (res->count != 1) {
152                 ret = ldb_add(ldb, msg);
153         } else {
154                 ret = LDB_SUCCESS;
155                 /* Annoyingly added to our search results */
156                 ldb_msg_remove_attr(res->msgs[0], "distinguishedName");
157
158                 ret = ldb_msg_difference(ldb, mem_ctx,
159                                          res->msgs[0], msg, &mod_msg);
160                 if (ret != LDB_SUCCESS) {
161                         goto op_error;
162                 }
163                 if (mod_msg->num_elements > 0) {
164                         ret = dsdb_replace(ldb, mod_msg, 0);
165                 }
166                 talloc_free(mod_msg);
167         }
168
169         if (ret == LDB_ERR_OPERATIONS_ERROR || ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS || ret == LDB_ERR_INVALID_DN_SYNTAX) {
170                 /* We might be on a read-only DB or LDAP */
171                 ret = LDB_SUCCESS;
172         }
173         if (ret != LDB_SUCCESS) {
174                 talloc_free(mem_ctx);
175                 return ret;
176         }
177
178         /* Now write out the indexes, as found in the schema (if they have changed) */
179
180         ret = ldb_search(ldb, mem_ctx, &res_idx, msg_idx->dn, LDB_SCOPE_BASE,
181                          NULL, NULL);
182         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
183                 ret = ldb_add(ldb, msg_idx);
184         } else if (ret != LDB_SUCCESS) {
185         } else if (res_idx->count != 1) {
186                 ret = ldb_add(ldb, msg_idx);
187         } else {
188                 ret = LDB_SUCCESS;
189                 /* Annoyingly added to our search results */
190                 ldb_msg_remove_attr(res_idx->msgs[0], "distinguishedName");
191
192                 ret = ldb_msg_difference(ldb, mem_ctx,
193                                          res_idx->msgs[0], msg_idx, &mod_msg);
194                 if (ret != LDB_SUCCESS) {
195                         goto op_error;
196                 }
197                 if (mod_msg->num_elements > 0) {
198                         ret = dsdb_replace(ldb, mod_msg, 0);
199                 }
200                 talloc_free(mod_msg);
201         }
202         if (ret == LDB_ERR_OPERATIONS_ERROR || ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS || ret == LDB_ERR_INVALID_DN_SYNTAX) {
203                 /* We might be on a read-only DB */
204                 ret = LDB_SUCCESS;
205         }
206         talloc_free(mem_ctx);
207         return ret;
208
209 op_error:
210         talloc_free(mem_ctx);
211         return ldb_operr(ldb);
212 }
213
214
215 /*
216   create extra attribute shortcuts
217  */
218 static void dsdb_setup_attribute_shortcuts(struct ldb_context *ldb, struct dsdb_schema *schema)
219 {
220         struct dsdb_attribute *attribute;
221
222         /* setup fast access to one_way_link and DN format */
223         for (attribute=schema->attributes; attribute; attribute=attribute->next) {
224                 attribute->dn_format = dsdb_dn_oid_to_format(attribute->syntax->ldap_oid);
225
226                 if (attribute->dn_format == DSDB_INVALID_DN) {
227                         attribute->one_way_link = false;
228                         continue;
229                 }
230
231                 /* these are not considered to be one way links for
232                    the purpose of DN link fixups */
233                 if (ldb_attr_cmp("distinguishedName", attribute->lDAPDisplayName) == 0 ||
234                     ldb_attr_cmp("objectCategory", attribute->lDAPDisplayName) == 0) {
235                         attribute->one_way_link = false;
236                         continue;
237                 }
238
239                 if (attribute->linkID == 0) {
240                         attribute->one_way_link = true;
241                         continue;
242                 }
243                 /* handle attributes with a linkID but no backlink */
244                 if (dsdb_attribute_by_linkID(schema, attribute->linkID) == NULL) {
245                         attribute->one_way_link = true;
246                         continue;
247                 }
248                 attribute->one_way_link = false;
249         }
250 }
251
252 static int uint32_cmp(uint32_t c1, uint32_t c2)
253 {
254         if (c1 == c2) return 0;
255         return c1 > c2 ? 1 : -1;
256 }
257
258 static int dsdb_compare_class_by_lDAPDisplayName(struct dsdb_class **c1, struct dsdb_class **c2)
259 {
260         return strcasecmp((*c1)->lDAPDisplayName, (*c2)->lDAPDisplayName);
261 }
262 static int dsdb_compare_class_by_governsID_id(struct dsdb_class **c1, struct dsdb_class **c2)
263 {
264         return uint32_cmp((*c1)->governsID_id, (*c2)->governsID_id);
265 }
266 static int dsdb_compare_class_by_governsID_oid(struct dsdb_class **c1, struct dsdb_class **c2)
267 {
268         return strcasecmp((*c1)->governsID_oid, (*c2)->governsID_oid);
269 }
270 static int dsdb_compare_class_by_cn(struct dsdb_class **c1, struct dsdb_class **c2)
271 {
272         return strcasecmp((*c1)->cn, (*c2)->cn);
273 }
274
275 static int dsdb_compare_attribute_by_lDAPDisplayName(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
276 {
277         return strcasecmp((*a1)->lDAPDisplayName, (*a2)->lDAPDisplayName);
278 }
279 static int dsdb_compare_attribute_by_attributeID_id(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
280 {
281         return uint32_cmp((*a1)->attributeID_id, (*a2)->attributeID_id);
282 }
283 static int dsdb_compare_attribute_by_msDS_IntId(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
284 {
285         return uint32_cmp((*a1)->msDS_IntId, (*a2)->msDS_IntId);
286 }
287 static int dsdb_compare_attribute_by_attributeID_oid(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
288 {
289         return strcasecmp((*a1)->attributeID_oid, (*a2)->attributeID_oid);
290 }
291 static int dsdb_compare_attribute_by_linkID(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
292 {
293         return uint32_cmp((*a1)->linkID, (*a2)->linkID);
294 }
295
296 /**
297  * Clean up Classes and Attributes accessor arrays
298  */
299 static void dsdb_sorted_accessors_free(struct dsdb_schema *schema)
300 {
301         /* free classes accessors */
302         TALLOC_FREE(schema->classes_by_lDAPDisplayName);
303         TALLOC_FREE(schema->classes_by_governsID_id);
304         TALLOC_FREE(schema->classes_by_governsID_oid);
305         TALLOC_FREE(schema->classes_by_cn);
306         /* free attribute accessors */
307         TALLOC_FREE(schema->attributes_by_lDAPDisplayName);
308         TALLOC_FREE(schema->attributes_by_attributeID_id);
309         TALLOC_FREE(schema->attributes_by_msDS_IntId);
310         TALLOC_FREE(schema->attributes_by_attributeID_oid);
311         TALLOC_FREE(schema->attributes_by_linkID);
312 }
313
314 /*
315   create the sorted accessor arrays for the schema
316  */
317 int dsdb_setup_sorted_accessors(struct ldb_context *ldb,
318                                 struct dsdb_schema *schema)
319 {
320         struct dsdb_class *cur;
321         struct dsdb_attribute *a;
322         unsigned int i;
323         unsigned int num_int_id;
324
325         /* free all caches */
326         dsdb_sorted_accessors_free(schema);
327
328         /* count the classes */
329         for (i=0, cur=schema->classes; cur; i++, cur=cur->next) /* noop */ ;
330         schema->num_classes = i;
331
332         /* setup classes_by_* */
333         schema->classes_by_lDAPDisplayName = talloc_array(schema, struct dsdb_class *, i);
334         schema->classes_by_governsID_id    = talloc_array(schema, struct dsdb_class *, i);
335         schema->classes_by_governsID_oid   = talloc_array(schema, struct dsdb_class *, i);
336         schema->classes_by_cn              = talloc_array(schema, struct dsdb_class *, i);
337         if (schema->classes_by_lDAPDisplayName == NULL ||
338             schema->classes_by_governsID_id == NULL ||
339             schema->classes_by_governsID_oid == NULL ||
340             schema->classes_by_cn == NULL) {
341                 goto failed;
342         }
343
344         for (i=0, cur=schema->classes; cur; i++, cur=cur->next) {
345                 schema->classes_by_lDAPDisplayName[i] = cur;
346                 schema->classes_by_governsID_id[i]    = cur;
347                 schema->classes_by_governsID_oid[i]   = cur;
348                 schema->classes_by_cn[i]              = cur;
349         }
350
351         /* sort the arrays */
352         TYPESAFE_QSORT(schema->classes_by_lDAPDisplayName, schema->num_classes, dsdb_compare_class_by_lDAPDisplayName);
353         TYPESAFE_QSORT(schema->classes_by_governsID_id, schema->num_classes, dsdb_compare_class_by_governsID_id);
354         TYPESAFE_QSORT(schema->classes_by_governsID_oid, schema->num_classes, dsdb_compare_class_by_governsID_oid);
355         TYPESAFE_QSORT(schema->classes_by_cn, schema->num_classes, dsdb_compare_class_by_cn);
356
357         /* now build the attribute accessor arrays */
358
359         /* count the attributes
360          * and attributes with msDS-IntId set */
361         num_int_id = 0;
362         for (i=0, a=schema->attributes; a; i++, a=a->next) {
363                 if (a->msDS_IntId != 0) {
364                         num_int_id++;
365                 }
366         }
367         schema->num_attributes = i;
368         schema->num_int_id_attr = num_int_id;
369
370         /* setup attributes_by_* */
371         schema->attributes_by_lDAPDisplayName = talloc_array(schema, struct dsdb_attribute *, i);
372         schema->attributes_by_attributeID_id    = talloc_array(schema, struct dsdb_attribute *, i);
373         schema->attributes_by_msDS_IntId        = talloc_array(schema,
374                                                                struct dsdb_attribute *, num_int_id);
375         schema->attributes_by_attributeID_oid   = talloc_array(schema, struct dsdb_attribute *, i);
376         schema->attributes_by_linkID              = talloc_array(schema, struct dsdb_attribute *, i);
377         if (schema->attributes_by_lDAPDisplayName == NULL ||
378             schema->attributes_by_attributeID_id == NULL ||
379             schema->attributes_by_msDS_IntId == NULL ||
380             schema->attributes_by_attributeID_oid == NULL ||
381             schema->attributes_by_linkID == NULL) {
382                 goto failed;
383         }
384
385         num_int_id = 0;
386         for (i=0, a=schema->attributes; a; i++, a=a->next) {
387                 schema->attributes_by_lDAPDisplayName[i] = a;
388                 schema->attributes_by_attributeID_id[i]    = a;
389                 schema->attributes_by_attributeID_oid[i]   = a;
390                 schema->attributes_by_linkID[i]          = a;
391                 /* append attr-by-msDS-IntId values */
392                 if (a->msDS_IntId != 0) {
393                         schema->attributes_by_msDS_IntId[num_int_id] = a;
394                         num_int_id++;
395                 }
396         }
397         SMB_ASSERT(num_int_id == schema->num_int_id_attr);
398
399         /* sort the arrays */
400         TYPESAFE_QSORT(schema->attributes_by_lDAPDisplayName, schema->num_attributes, dsdb_compare_attribute_by_lDAPDisplayName);
401         TYPESAFE_QSORT(schema->attributes_by_attributeID_id, schema->num_attributes, dsdb_compare_attribute_by_attributeID_id);
402         TYPESAFE_QSORT(schema->attributes_by_msDS_IntId, schema->num_int_id_attr, dsdb_compare_attribute_by_msDS_IntId);
403         TYPESAFE_QSORT(schema->attributes_by_attributeID_oid, schema->num_attributes, dsdb_compare_attribute_by_attributeID_oid);
404         TYPESAFE_QSORT(schema->attributes_by_linkID, schema->num_attributes, dsdb_compare_attribute_by_linkID);
405
406         dsdb_setup_attribute_shortcuts(ldb, schema);
407
408         return LDB_SUCCESS;
409
410 failed:
411         dsdb_sorted_accessors_free(schema);
412         return ldb_oom(ldb);
413 }
414
415 /**
416  * Attach the schema to an opaque pointer on the ldb,
417  * so ldb modules can find it
418  */
419 int dsdb_set_schema(struct ldb_context *ldb, struct dsdb_schema *schema)
420 {
421         struct dsdb_schema *old_schema;
422         int ret;
423
424         ret = dsdb_setup_sorted_accessors(ldb, schema);
425         if (ret != LDB_SUCCESS) {
426                 return ret;
427         }
428
429         ret = schema_fill_constructed(schema);
430         if (ret != LDB_SUCCESS) {
431                 return ret;
432         }
433
434         old_schema = ldb_get_opaque(ldb, "dsdb_schema");
435
436         ret = ldb_set_opaque(ldb, "dsdb_schema", schema);
437         if (ret != LDB_SUCCESS) {
438                 return ret;
439         }
440
441         /* Remove the reference to the schema we just overwrote - if there was
442          * none, NULL is harmless here */
443         if (old_schema != schema) {
444                 talloc_unlink(ldb, old_schema);
445                 talloc_steal(ldb, schema);
446         }
447
448         ret = ldb_set_opaque(ldb, "dsdb_use_global_schema", NULL);
449         if (ret != LDB_SUCCESS) {
450                 return ret;
451         }
452
453         /* Set the new attributes based on the new schema */
454         ret = dsdb_schema_set_attributes(ldb, schema, true);
455         if (ret != LDB_SUCCESS) {
456                 return ret;
457         }
458
459         return LDB_SUCCESS;
460 }
461
462 /**
463  * Global variable to hold one copy of the schema, used to avoid memory bloat
464  */
465 static struct dsdb_schema *global_schema;
466
467 /**
468  * Make this ldb use a specified schema, already fully calculated and belonging to another ldb
469  */
470 int dsdb_reference_schema(struct ldb_context *ldb, struct dsdb_schema *schema,
471                           bool write_attributes)
472 {
473         int ret;
474         struct dsdb_schema *old_schema;
475         old_schema = ldb_get_opaque(ldb, "dsdb_schema");
476         ret = ldb_set_opaque(ldb, "dsdb_schema", schema);
477         if (ret != LDB_SUCCESS) {
478                 return ret;
479         }
480
481         /* Remove the reference to the schema we just overwrote - if there was
482          * none, NULL is harmless here */
483         talloc_unlink(ldb, old_schema);
484
485         if (talloc_reference(ldb, schema) == NULL) {
486                 return ldb_oom(ldb);
487         }
488
489         /* Make this ldb use local schema preferably */
490         ret = ldb_set_opaque(ldb, "dsdb_use_global_schema", NULL);
491         if (ret != LDB_SUCCESS) {
492                 return ret;
493         }
494
495         ret = dsdb_schema_set_attributes(ldb, schema, write_attributes);
496         if (ret != LDB_SUCCESS) {
497                 return ret;
498         }
499
500         return LDB_SUCCESS;
501 }
502
503 /**
504  * Make this ldb use the 'global' schema, setup to avoid having multiple copies in this process
505  */
506 int dsdb_set_global_schema(struct ldb_context *ldb)
507 {
508         int ret;
509         void *use_global_schema = (void *)1;
510         if (!global_schema) {
511                 return LDB_SUCCESS;
512         }
513         ret = ldb_set_opaque(ldb, "dsdb_use_global_schema", use_global_schema);
514         if (ret != LDB_SUCCESS) {
515                 return ret;
516         }
517
518         /* Set the new attributes based on the new schema */
519         ret = dsdb_schema_set_attributes(ldb, global_schema, false /* Don't write attributes, it's expensive */);
520         if (ret == LDB_SUCCESS) {
521                 /* Keep a reference to this schema, just in case the original copy is replaced */
522                 if (talloc_reference(ldb, global_schema) == NULL) {
523                         return ldb_oom(ldb);
524                 }
525         }
526
527         return ret;
528 }
529
530 bool dsdb_uses_global_schema(struct ldb_context *ldb)
531 {
532         return (ldb_get_opaque(ldb, "dsdb_use_global_schema") != NULL);
533 }
534
535 /**
536  * Find the schema object for this ldb
537  *
538  * If reference_ctx is not NULL, then talloc_reference onto that context
539  */
540
541 struct dsdb_schema *dsdb_get_schema(struct ldb_context *ldb, TALLOC_CTX *reference_ctx)
542 {
543         const void *p;
544         struct dsdb_schema *schema_out;
545         struct dsdb_schema *schema_in;
546         bool use_global_schema;
547         TALLOC_CTX *tmp_ctx = talloc_new(reference_ctx);
548         if (!tmp_ctx) {
549                 return NULL;
550         }
551
552         /* see if we have a cached copy */
553         use_global_schema = dsdb_uses_global_schema(ldb);
554         if (use_global_schema) {
555                 schema_in = global_schema;
556         } else {
557                 p = ldb_get_opaque(ldb, "dsdb_schema");
558
559                 schema_in = talloc_get_type(p, struct dsdb_schema);
560                 if (!schema_in) {
561                         talloc_free(tmp_ctx);
562                         return NULL;
563                 }
564         }
565
566         if (schema_in->refresh_fn && !schema_in->refresh_in_progress) {
567                 if (!talloc_reference(tmp_ctx, schema_in)) {
568                         /*
569                          * ensure that the schema_in->refresh_in_progress
570                          * remains valid for the right amount of time
571                          */
572                         talloc_free(tmp_ctx);
573                         return NULL;
574                 }
575                 schema_in->refresh_in_progress = true;
576                 /* This may change schema, if it needs to reload it from disk */
577                 schema_out = schema_in->refresh_fn(schema_in->loaded_from_module,
578                                                    schema_in,
579                                                    use_global_schema);
580                 schema_in->refresh_in_progress = false;
581         } else {
582                 schema_out = schema_in;
583         }
584
585         /* This removes the extra reference above */
586         talloc_free(tmp_ctx);
587         if (!reference_ctx) {
588                 return schema_out;
589         } else {
590                 return talloc_reference(reference_ctx, schema_out);
591         }
592 }
593
594 /**
595  * Make the schema found on this ldb the 'global' schema
596  */
597
598 void dsdb_make_schema_global(struct ldb_context *ldb, struct dsdb_schema *schema)
599 {
600         if (!schema) {
601                 return;
602         }
603
604         if (global_schema) {
605                 talloc_unlink(talloc_autofree_context(), global_schema);
606         }
607
608         /* we want the schema to be around permanently */
609         talloc_reparent(ldb, talloc_autofree_context(), schema);
610         global_schema = schema;
611
612         /* This calls the talloc_reference() of the global schema back onto the ldb */
613         dsdb_set_global_schema(ldb);
614 }
615
616 /**
617  * When loading the schema from LDIF files, we don't get the extended DNs.
618  *
619  * We need to set these up, so that from the moment we start the provision,
620  * the defaultObjectCategory links are set up correctly.
621  */
622 int dsdb_schema_fill_extended_dn(struct ldb_context *ldb, struct dsdb_schema *schema)
623 {
624         struct dsdb_class *cur;
625         const struct dsdb_class *target_class;
626         for (cur = schema->classes; cur; cur = cur->next) {
627                 const struct ldb_val *rdn;
628                 struct ldb_val guid;
629                 NTSTATUS status;
630                 struct ldb_dn *dn = ldb_dn_new(NULL, ldb, cur->defaultObjectCategory);
631
632                 if (!dn) {
633                         return LDB_ERR_INVALID_DN_SYNTAX;
634                 }
635                 rdn = ldb_dn_get_component_val(dn, 0);
636                 if (!rdn) {
637                         talloc_free(dn);
638                         return LDB_ERR_INVALID_DN_SYNTAX;
639                 }
640                 target_class = dsdb_class_by_cn_ldb_val(schema, rdn);
641                 if (!target_class) {
642                         talloc_free(dn);
643                         return LDB_ERR_CONSTRAINT_VIOLATION;
644                 }
645
646                 status = GUID_to_ndr_blob(&target_class->objectGUID, dn, &guid);
647                 if (!NT_STATUS_IS_OK(status)) {
648                         talloc_free(dn);
649                         return ldb_operr(ldb);
650                 }
651                 ldb_dn_set_extended_component(dn, "GUID", &guid);
652
653                 cur->defaultObjectCategory = ldb_dn_get_extended_linearized(cur, dn, 1);
654                 talloc_free(dn);
655         }
656         return LDB_SUCCESS;
657 }
658
659 /**
660  * Add an element to the schema (attribute or class) from an LDB message
661  */
662 WERROR dsdb_schema_set_el_from_ldb_msg(struct ldb_context *ldb, struct dsdb_schema *schema,
663                                        struct ldb_message *msg)
664 {
665         if (samdb_find_attribute(ldb, msg,
666                                  "objectclass", "attributeSchema") != NULL) {
667                 return dsdb_attribute_from_ldb(ldb, schema, msg);
668         } else if (samdb_find_attribute(ldb, msg,
669                                  "objectclass", "classSchema") != NULL) {
670                 return dsdb_class_from_ldb(schema, msg);
671         }
672
673         /* Don't fail on things not classes or attributes */
674         return WERR_OK;
675 }
676
677 /**
678  * Rather than read a schema from the LDB itself, read it from an ldif
679  * file.  This allows schema to be loaded and used while adding the
680  * schema itself to the directory.
681  */
682
683 WERROR dsdb_set_schema_from_ldif(struct ldb_context *ldb, const char *pf, const char *df)
684 {
685         struct ldb_ldif *ldif;
686         struct ldb_message *msg;
687         TALLOC_CTX *mem_ctx;
688         WERROR status;
689         int ret;
690         struct dsdb_schema *schema;
691         const struct ldb_val *prefix_val;
692         const struct ldb_val *info_val;
693         struct ldb_val info_val_default;
694
695
696         mem_ctx = talloc_new(ldb);
697         if (!mem_ctx) {
698                 goto nomem;
699         }
700
701         schema = dsdb_new_schema(mem_ctx);
702
703         schema->fsmo.we_are_master = true;
704         schema->fsmo.master_dn = ldb_dn_new_fmt(schema, ldb, "@PROVISION_SCHEMA_MASTER");
705         if (!schema->fsmo.master_dn) {
706                 goto nomem;
707         }
708
709         /*
710          * load the prefixMap attribute from pf
711          */
712         ldif = ldb_ldif_read_string(ldb, &pf);
713         if (!ldif) {
714                 status = WERR_INVALID_PARAM;
715                 goto failed;
716         }
717         talloc_steal(mem_ctx, ldif);
718
719         ret = ldb_msg_normalize(ldb, mem_ctx, ldif->msg, &msg);
720         if (ret != LDB_SUCCESS) {
721                 goto nomem;
722         }
723         talloc_free(ldif);
724
725         prefix_val = ldb_msg_find_ldb_val(msg, "prefixMap");
726         if (!prefix_val) {
727                 status = WERR_INVALID_PARAM;
728                 goto failed;
729         }
730
731         info_val = ldb_msg_find_ldb_val(msg, "schemaInfo");
732         if (!info_val) {
733                 status = dsdb_schema_info_blob_new(mem_ctx, &info_val_default);
734                 W_ERROR_NOT_OK_GOTO(status, failed);
735                 info_val = &info_val_default;
736         }
737
738         status = dsdb_load_oid_mappings_ldb(schema, prefix_val, info_val);
739         if (!W_ERROR_IS_OK(status)) {
740                 DEBUG(0,("ERROR: dsdb_load_oid_mappings_ldb() failed with %s\n", win_errstr(status)));
741                 goto failed;
742         }
743
744         /* load the attribute and class definitions out of df */
745         while ((ldif = ldb_ldif_read_string(ldb, &df))) {
746                 talloc_steal(mem_ctx, ldif);
747
748                 ret = ldb_msg_normalize(ldb, ldif, ldif->msg, &msg);
749                 if (ret != LDB_SUCCESS) {
750                         goto nomem;
751                 }
752
753                 status = dsdb_schema_set_el_from_ldb_msg(ldb, schema, msg);
754                 talloc_free(ldif);
755                 if (!W_ERROR_IS_OK(status)) {
756                         goto failed;
757                 }
758         }
759
760         ret = dsdb_set_schema(ldb, schema);
761         if (ret != LDB_SUCCESS) {
762                 status = WERR_FOOBAR;
763                 goto failed;
764         }
765
766         ret = dsdb_schema_fill_extended_dn(ldb, schema);
767         if (ret != LDB_SUCCESS) {
768                 status = WERR_FOOBAR;
769                 goto failed;
770         }
771
772         goto done;
773
774 nomem:
775         status = WERR_NOMEM;
776 failed:
777 done:
778         talloc_free(mem_ctx);
779         return status;
780 }