move schema_fill_constructed() call to dsdb_setup_sorted_accessors()
[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         int ret;
325
326         /* free all caches */
327         dsdb_sorted_accessors_free(schema);
328
329         /* count the classes */
330         for (i=0, cur=schema->classes; cur; i++, cur=cur->next) /* noop */ ;
331         schema->num_classes = i;
332
333         /* setup classes_by_* */
334         schema->classes_by_lDAPDisplayName = talloc_array(schema, struct dsdb_class *, i);
335         schema->classes_by_governsID_id    = talloc_array(schema, struct dsdb_class *, i);
336         schema->classes_by_governsID_oid   = talloc_array(schema, struct dsdb_class *, i);
337         schema->classes_by_cn              = talloc_array(schema, struct dsdb_class *, i);
338         if (schema->classes_by_lDAPDisplayName == NULL ||
339             schema->classes_by_governsID_id == NULL ||
340             schema->classes_by_governsID_oid == NULL ||
341             schema->classes_by_cn == NULL) {
342                 goto failed;
343         }
344
345         for (i=0, cur=schema->classes; cur; i++, cur=cur->next) {
346                 schema->classes_by_lDAPDisplayName[i] = cur;
347                 schema->classes_by_governsID_id[i]    = cur;
348                 schema->classes_by_governsID_oid[i]   = cur;
349                 schema->classes_by_cn[i]              = cur;
350         }
351
352         /* sort the arrays */
353         TYPESAFE_QSORT(schema->classes_by_lDAPDisplayName, schema->num_classes, dsdb_compare_class_by_lDAPDisplayName);
354         TYPESAFE_QSORT(schema->classes_by_governsID_id, schema->num_classes, dsdb_compare_class_by_governsID_id);
355         TYPESAFE_QSORT(schema->classes_by_governsID_oid, schema->num_classes, dsdb_compare_class_by_governsID_oid);
356         TYPESAFE_QSORT(schema->classes_by_cn, schema->num_classes, dsdb_compare_class_by_cn);
357
358         /* now build the attribute accessor arrays */
359
360         /* count the attributes
361          * and attributes with msDS-IntId set */
362         num_int_id = 0;
363         for (i=0, a=schema->attributes; a; i++, a=a->next) {
364                 if (a->msDS_IntId != 0) {
365                         num_int_id++;
366                 }
367         }
368         schema->num_attributes = i;
369         schema->num_int_id_attr = num_int_id;
370
371         /* setup attributes_by_* */
372         schema->attributes_by_lDAPDisplayName = talloc_array(schema, struct dsdb_attribute *, i);
373         schema->attributes_by_attributeID_id    = talloc_array(schema, struct dsdb_attribute *, i);
374         schema->attributes_by_msDS_IntId        = talloc_array(schema,
375                                                                struct dsdb_attribute *, num_int_id);
376         schema->attributes_by_attributeID_oid   = talloc_array(schema, struct dsdb_attribute *, i);
377         schema->attributes_by_linkID              = talloc_array(schema, struct dsdb_attribute *, i);
378         if (schema->attributes_by_lDAPDisplayName == NULL ||
379             schema->attributes_by_attributeID_id == NULL ||
380             schema->attributes_by_msDS_IntId == NULL ||
381             schema->attributes_by_attributeID_oid == NULL ||
382             schema->attributes_by_linkID == NULL) {
383                 goto failed;
384         }
385
386         num_int_id = 0;
387         for (i=0, a=schema->attributes; a; i++, a=a->next) {
388                 schema->attributes_by_lDAPDisplayName[i] = a;
389                 schema->attributes_by_attributeID_id[i]    = a;
390                 schema->attributes_by_attributeID_oid[i]   = a;
391                 schema->attributes_by_linkID[i]          = a;
392                 /* append attr-by-msDS-IntId values */
393                 if (a->msDS_IntId != 0) {
394                         schema->attributes_by_msDS_IntId[num_int_id] = a;
395                         num_int_id++;
396                 }
397         }
398         SMB_ASSERT(num_int_id == schema->num_int_id_attr);
399
400         /* sort the arrays */
401         TYPESAFE_QSORT(schema->attributes_by_lDAPDisplayName, schema->num_attributes, dsdb_compare_attribute_by_lDAPDisplayName);
402         TYPESAFE_QSORT(schema->attributes_by_attributeID_id, schema->num_attributes, dsdb_compare_attribute_by_attributeID_id);
403         TYPESAFE_QSORT(schema->attributes_by_msDS_IntId, schema->num_int_id_attr, dsdb_compare_attribute_by_msDS_IntId);
404         TYPESAFE_QSORT(schema->attributes_by_attributeID_oid, schema->num_attributes, dsdb_compare_attribute_by_attributeID_oid);
405         TYPESAFE_QSORT(schema->attributes_by_linkID, schema->num_attributes, dsdb_compare_attribute_by_linkID);
406
407         dsdb_setup_attribute_shortcuts(ldb, schema);
408
409         ret = schema_fill_constructed(schema);
410         if (ret != LDB_SUCCESS) {
411                 dsdb_sorted_accessors_free(schema);
412                 return ret;
413         }
414
415         return LDB_SUCCESS;
416
417 failed:
418         dsdb_sorted_accessors_free(schema);
419         return ldb_oom(ldb);
420 }
421
422 /**
423  * Attach the schema to an opaque pointer on the ldb,
424  * so ldb modules can find it
425  */
426 int dsdb_set_schema(struct ldb_context *ldb, struct dsdb_schema *schema)
427 {
428         struct dsdb_schema *old_schema;
429         int ret;
430
431         ret = dsdb_setup_sorted_accessors(ldb, schema);
432         if (ret != LDB_SUCCESS) {
433                 return ret;
434         }
435
436         old_schema = ldb_get_opaque(ldb, "dsdb_schema");
437
438         ret = ldb_set_opaque(ldb, "dsdb_schema", schema);
439         if (ret != LDB_SUCCESS) {
440                 return ret;
441         }
442
443         /* Remove the reference to the schema we just overwrote - if there was
444          * none, NULL is harmless here */
445         if (old_schema != schema) {
446                 talloc_unlink(ldb, old_schema);
447                 talloc_steal(ldb, schema);
448         }
449
450         ret = ldb_set_opaque(ldb, "dsdb_use_global_schema", NULL);
451         if (ret != LDB_SUCCESS) {
452                 return ret;
453         }
454
455         /* Set the new attributes based on the new schema */
456         ret = dsdb_schema_set_attributes(ldb, schema, true);
457         if (ret != LDB_SUCCESS) {
458                 return ret;
459         }
460
461         return LDB_SUCCESS;
462 }
463
464 /**
465  * Global variable to hold one copy of the schema, used to avoid memory bloat
466  */
467 static struct dsdb_schema *global_schema;
468
469 /**
470  * Make this ldb use a specified schema, already fully calculated and belonging to another ldb
471  */
472 int dsdb_reference_schema(struct ldb_context *ldb, struct dsdb_schema *schema,
473                           bool write_attributes)
474 {
475         int ret;
476         struct dsdb_schema *old_schema;
477         old_schema = ldb_get_opaque(ldb, "dsdb_schema");
478         ret = ldb_set_opaque(ldb, "dsdb_schema", schema);
479         if (ret != LDB_SUCCESS) {
480                 return ret;
481         }
482
483         /* Remove the reference to the schema we just overwrote - if there was
484          * none, NULL is harmless here */
485         talloc_unlink(ldb, old_schema);
486
487         if (talloc_reference(ldb, schema) == NULL) {
488                 return ldb_oom(ldb);
489         }
490
491         /* Make this ldb use local schema preferably */
492         ret = ldb_set_opaque(ldb, "dsdb_use_global_schema", NULL);
493         if (ret != LDB_SUCCESS) {
494                 return ret;
495         }
496
497         ret = dsdb_schema_set_attributes(ldb, schema, write_attributes);
498         if (ret != LDB_SUCCESS) {
499                 return ret;
500         }
501
502         return LDB_SUCCESS;
503 }
504
505 /**
506  * Make this ldb use the 'global' schema, setup to avoid having multiple copies in this process
507  */
508 int dsdb_set_global_schema(struct ldb_context *ldb)
509 {
510         int ret;
511         void *use_global_schema = (void *)1;
512         if (!global_schema) {
513                 return LDB_SUCCESS;
514         }
515         ret = ldb_set_opaque(ldb, "dsdb_use_global_schema", use_global_schema);
516         if (ret != LDB_SUCCESS) {
517                 return ret;
518         }
519
520         /* Set the new attributes based on the new schema */
521         ret = dsdb_schema_set_attributes(ldb, global_schema, false /* Don't write attributes, it's expensive */);
522         if (ret == LDB_SUCCESS) {
523                 /* Keep a reference to this schema, just in case the original copy is replaced */
524                 if (talloc_reference(ldb, global_schema) == NULL) {
525                         return ldb_oom(ldb);
526                 }
527         }
528
529         return ret;
530 }
531
532 bool dsdb_uses_global_schema(struct ldb_context *ldb)
533 {
534         return (ldb_get_opaque(ldb, "dsdb_use_global_schema") != NULL);
535 }
536
537 /**
538  * Find the schema object for this ldb
539  *
540  * If reference_ctx is not NULL, then talloc_reference onto that context
541  */
542
543 struct dsdb_schema *dsdb_get_schema(struct ldb_context *ldb, TALLOC_CTX *reference_ctx)
544 {
545         const void *p;
546         struct dsdb_schema *schema_out;
547         struct dsdb_schema *schema_in;
548         bool use_global_schema;
549         TALLOC_CTX *tmp_ctx = talloc_new(reference_ctx);
550         if (!tmp_ctx) {
551                 return NULL;
552         }
553
554         /* see if we have a cached copy */
555         use_global_schema = dsdb_uses_global_schema(ldb);
556         if (use_global_schema) {
557                 schema_in = global_schema;
558         } else {
559                 p = ldb_get_opaque(ldb, "dsdb_schema");
560
561                 schema_in = talloc_get_type(p, struct dsdb_schema);
562                 if (!schema_in) {
563                         talloc_free(tmp_ctx);
564                         return NULL;
565                 }
566         }
567
568         if (schema_in->refresh_fn && !schema_in->refresh_in_progress) {
569                 if (!talloc_reference(tmp_ctx, schema_in)) {
570                         /*
571                          * ensure that the schema_in->refresh_in_progress
572                          * remains valid for the right amount of time
573                          */
574                         talloc_free(tmp_ctx);
575                         return NULL;
576                 }
577                 schema_in->refresh_in_progress = true;
578                 /* This may change schema, if it needs to reload it from disk */
579                 schema_out = schema_in->refresh_fn(schema_in->loaded_from_module,
580                                                    schema_in,
581                                                    use_global_schema);
582                 schema_in->refresh_in_progress = false;
583         } else {
584                 schema_out = schema_in;
585         }
586
587         /* This removes the extra reference above */
588         talloc_free(tmp_ctx);
589         if (!reference_ctx) {
590                 return schema_out;
591         } else {
592                 return talloc_reference(reference_ctx, schema_out);
593         }
594 }
595
596 /**
597  * Make the schema found on this ldb the 'global' schema
598  */
599
600 void dsdb_make_schema_global(struct ldb_context *ldb, struct dsdb_schema *schema)
601 {
602         if (!schema) {
603                 return;
604         }
605
606         if (global_schema) {
607                 talloc_unlink(talloc_autofree_context(), global_schema);
608         }
609
610         /* we want the schema to be around permanently */
611         talloc_reparent(ldb, talloc_autofree_context(), schema);
612         global_schema = schema;
613
614         /* This calls the talloc_reference() of the global schema back onto the ldb */
615         dsdb_set_global_schema(ldb);
616 }
617
618 /**
619  * When loading the schema from LDIF files, we don't get the extended DNs.
620  *
621  * We need to set these up, so that from the moment we start the provision,
622  * the defaultObjectCategory links are set up correctly.
623  */
624 int dsdb_schema_fill_extended_dn(struct ldb_context *ldb, struct dsdb_schema *schema)
625 {
626         struct dsdb_class *cur;
627         const struct dsdb_class *target_class;
628         for (cur = schema->classes; cur; cur = cur->next) {
629                 const struct ldb_val *rdn;
630                 struct ldb_val guid;
631                 NTSTATUS status;
632                 struct ldb_dn *dn = ldb_dn_new(NULL, ldb, cur->defaultObjectCategory);
633
634                 if (!dn) {
635                         return LDB_ERR_INVALID_DN_SYNTAX;
636                 }
637                 rdn = ldb_dn_get_component_val(dn, 0);
638                 if (!rdn) {
639                         talloc_free(dn);
640                         return LDB_ERR_INVALID_DN_SYNTAX;
641                 }
642                 target_class = dsdb_class_by_cn_ldb_val(schema, rdn);
643                 if (!target_class) {
644                         talloc_free(dn);
645                         return LDB_ERR_CONSTRAINT_VIOLATION;
646                 }
647
648                 status = GUID_to_ndr_blob(&target_class->objectGUID, dn, &guid);
649                 if (!NT_STATUS_IS_OK(status)) {
650                         talloc_free(dn);
651                         return ldb_operr(ldb);
652                 }
653                 ldb_dn_set_extended_component(dn, "GUID", &guid);
654
655                 cur->defaultObjectCategory = ldb_dn_get_extended_linearized(cur, dn, 1);
656                 talloc_free(dn);
657         }
658         return LDB_SUCCESS;
659 }
660
661 /**
662  * Add an element to the schema (attribute or class) from an LDB message
663  */
664 WERROR dsdb_schema_set_el_from_ldb_msg(struct ldb_context *ldb, struct dsdb_schema *schema,
665                                        struct ldb_message *msg)
666 {
667         if (samdb_find_attribute(ldb, msg,
668                                  "objectclass", "attributeSchema") != NULL) {
669                 return dsdb_attribute_from_ldb(ldb, schema, msg);
670         } else if (samdb_find_attribute(ldb, msg,
671                                  "objectclass", "classSchema") != NULL) {
672                 return dsdb_class_from_ldb(schema, msg);
673         }
674
675         /* Don't fail on things not classes or attributes */
676         return WERR_OK;
677 }
678
679 /**
680  * Rather than read a schema from the LDB itself, read it from an ldif
681  * file.  This allows schema to be loaded and used while adding the
682  * schema itself to the directory.
683  */
684
685 WERROR dsdb_set_schema_from_ldif(struct ldb_context *ldb, const char *pf, const char *df)
686 {
687         struct ldb_ldif *ldif;
688         struct ldb_message *msg;
689         TALLOC_CTX *mem_ctx;
690         WERROR status;
691         int ret;
692         struct dsdb_schema *schema;
693         const struct ldb_val *prefix_val;
694         const struct ldb_val *info_val;
695         struct ldb_val info_val_default;
696
697
698         mem_ctx = talloc_new(ldb);
699         if (!mem_ctx) {
700                 goto nomem;
701         }
702
703         schema = dsdb_new_schema(mem_ctx);
704
705         schema->fsmo.we_are_master = true;
706         schema->fsmo.master_dn = ldb_dn_new_fmt(schema, ldb, "@PROVISION_SCHEMA_MASTER");
707         if (!schema->fsmo.master_dn) {
708                 goto nomem;
709         }
710
711         /*
712          * load the prefixMap attribute from pf
713          */
714         ldif = ldb_ldif_read_string(ldb, &pf);
715         if (!ldif) {
716                 status = WERR_INVALID_PARAM;
717                 goto failed;
718         }
719         talloc_steal(mem_ctx, ldif);
720
721         ret = ldb_msg_normalize(ldb, mem_ctx, ldif->msg, &msg);
722         if (ret != LDB_SUCCESS) {
723                 goto nomem;
724         }
725         talloc_free(ldif);
726
727         prefix_val = ldb_msg_find_ldb_val(msg, "prefixMap");
728         if (!prefix_val) {
729                 status = WERR_INVALID_PARAM;
730                 goto failed;
731         }
732
733         info_val = ldb_msg_find_ldb_val(msg, "schemaInfo");
734         if (!info_val) {
735                 status = dsdb_schema_info_blob_new(mem_ctx, &info_val_default);
736                 W_ERROR_NOT_OK_GOTO(status, failed);
737                 info_val = &info_val_default;
738         }
739
740         status = dsdb_load_oid_mappings_ldb(schema, prefix_val, info_val);
741         if (!W_ERROR_IS_OK(status)) {
742                 DEBUG(0,("ERROR: dsdb_load_oid_mappings_ldb() failed with %s\n", win_errstr(status)));
743                 goto failed;
744         }
745
746         /* load the attribute and class definitions out of df */
747         while ((ldif = ldb_ldif_read_string(ldb, &df))) {
748                 talloc_steal(mem_ctx, ldif);
749
750                 ret = ldb_msg_normalize(ldb, ldif, ldif->msg, &msg);
751                 if (ret != LDB_SUCCESS) {
752                         goto nomem;
753                 }
754
755                 status = dsdb_schema_set_el_from_ldb_msg(ldb, schema, msg);
756                 talloc_free(ldif);
757                 if (!W_ERROR_IS_OK(status)) {
758                         goto failed;
759                 }
760         }
761
762         ret = dsdb_set_schema(ldb, schema);
763         if (ret != LDB_SUCCESS) {
764                 status = WERR_FOOBAR;
765                 goto failed;
766         }
767
768         ret = dsdb_schema_fill_extended_dn(ldb, schema);
769         if (ret != LDB_SUCCESS) {
770                 status = WERR_FOOBAR;
771                 goto failed;
772         }
773
774         goto done;
775
776 nomem:
777         status = WERR_NOMEM;
778 failed:
779 done:
780         talloc_free(mem_ctx);
781         return status;
782 }