s4:dsdb Ensure we free old schema copies
[metze/samba/wip.git] / source4 / dsdb / schema / schema_set.c
1 /* 
2    Unix SMB/CIFS mplementation.
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
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.
12    
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.
17    
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/>.
20    
21 */
22
23 #include "includes.h"
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"
31
32 /*
33   override the name to attribute handler function
34  */
35 const struct ldb_schema_attribute *dsdb_attribute_handler_override(struct ldb_context *ldb, 
36                                                                    void *private_data,
37                                                                    const char *name)
38 {
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);
41         if (a == NULL) {
42                 /* this will fall back to ldb internal handling */
43                 return NULL;
44         }
45         return a->ldb_schema_attribute;
46 }
47
48 static int dsdb_schema_set_attributes(struct ldb_context *ldb, struct dsdb_schema *schema, bool write_attributes)
49 {
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;
55         TALLOC_CTX *mem_ctx;
56         struct ldb_message *msg;
57         struct ldb_message *msg_idx;
58
59         /* setup our own attribute name to schema handler */
60         ldb_schema_attribute_set_override_handler(ldb, dsdb_attribute_handler_override, schema);
61
62         if (!write_attributes) {
63                 return ret;
64         }
65
66         mem_ctx = talloc_new(ldb);
67         if (!mem_ctx) {
68                 return LDB_ERR_OPERATIONS_ERROR;
69         }
70
71         msg = ldb_msg_new(mem_ctx);
72         if (!msg) {
73                 ldb_oom(ldb);
74                 goto op_error;
75         }
76         msg_idx = ldb_msg_new(mem_ctx);
77         if (!msg_idx) {
78                 ldb_oom(ldb);
79                 goto op_error;
80         }
81         msg->dn = ldb_dn_new(msg, ldb, "@ATTRIBUTES");
82         if (!msg->dn) {
83                 ldb_oom(ldb);
84                 goto op_error;
85         }
86         msg_idx->dn = ldb_dn_new(msg_idx, ldb, "@INDEXLIST");
87         if (!msg_idx->dn) {
88                 ldb_oom(ldb);
89                 goto op_error;
90         }
91
92         ret = ldb_msg_add_string(msg_idx, "@IDXONE", "1");
93         if (ret != LDB_SUCCESS) {
94                 goto op_error;
95         }
96
97         for (attr = schema->attributes; attr; attr = attr->next) {
98                 const char *syntax = attr->syntax->ldb_syntax;
99                 
100                 if (!syntax) {
101                         syntax = attr->syntax->ldap_oid;
102                 }
103
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");
109                 } 
110                 if (ret != LDB_SUCCESS) {
111                         break;
112                 }
113
114                 if (attr->searchFlags & SEARCH_FLAG_ATTINDEX) {
115                         ret = ldb_msg_add_string(msg_idx, "@IDXATTR", attr->lDAPDisplayName);
116                         if (ret != LDB_SUCCESS) {
117                                 break;
118                         }
119                 }
120         }
121
122         if (ret != LDB_SUCCESS) {
123                 talloc_free(mem_ctx);
124                 return ret;
125         }
126
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);
134         } else {
135                 ret = LDB_SUCCESS;
136                 /* Annoyingly added to our search results */
137                 ldb_msg_remove_attr(res->msgs[0], "distinguishedName");
138                 
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);
142                 }
143                 talloc_free(mod_msg);
144         }
145
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 */
148                 ret = LDB_SUCCESS;
149         }
150         if (ret != LDB_SUCCESS) {
151                 talloc_free(mem_ctx);
152                 return ret;
153         }
154
155         /* Now write out the indexs, as found in the schema (if they have changed) */
156
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);
163         } else {
164                 ret = LDB_SUCCESS;
165                 /* Annoyingly added to our search results */
166                 ldb_msg_remove_attr(res_idx->msgs[0], "distinguishedName");
167
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);
171                 }
172                 talloc_free(mod_msg);
173         }
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 */
176                 ret = LDB_SUCCESS;
177         }
178         talloc_free(mem_ctx);
179         return ret;
180
181 op_error:
182         talloc_free(mem_ctx);
183         return LDB_ERR_OPERATIONS_ERROR;
184 }
185
186 static int uint32_cmp(uint32_t c1, uint32_t c2)
187 {
188         if (c1 == c2) return 0;
189         return c1 > c2 ? 1 : -1;
190 }
191
192 static int dsdb_compare_class_by_lDAPDisplayName(struct dsdb_class **c1, struct dsdb_class **c2)
193 {
194         return strcasecmp((*c1)->lDAPDisplayName, (*c2)->lDAPDisplayName);
195 }
196 static int dsdb_compare_class_by_governsID_id(struct dsdb_class **c1, struct dsdb_class **c2)
197 {
198         return uint32_cmp((*c1)->governsID_id, (*c2)->governsID_id);
199 }
200 static int dsdb_compare_class_by_governsID_oid(struct dsdb_class **c1, struct dsdb_class **c2)
201 {
202         return strcasecmp((*c1)->governsID_oid, (*c2)->governsID_oid);
203 }
204 static int dsdb_compare_class_by_cn(struct dsdb_class **c1, struct dsdb_class **c2)
205 {
206         return strcasecmp((*c1)->cn, (*c2)->cn);
207 }
208
209 static int dsdb_compare_attribute_by_lDAPDisplayName(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
210 {
211         return strcasecmp((*a1)->lDAPDisplayName, (*a2)->lDAPDisplayName);
212 }
213 static int dsdb_compare_attribute_by_attributeID_id(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
214 {
215         return uint32_cmp((*a1)->attributeID_id, (*a2)->attributeID_id);
216 }
217 static int dsdb_compare_attribute_by_attributeID_oid(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
218 {
219         return strcasecmp((*a1)->attributeID_oid, (*a2)->attributeID_oid);
220 }
221 static int dsdb_compare_attribute_by_linkID(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
222 {
223         return uint32_cmp((*a1)->linkID, (*a2)->linkID);
224 }
225
226 /**
227  * Clean up Classes and Attributes accessor arrays
228  */
229 static void dsdb_sorted_accessors_free(struct dsdb_schema *schema)
230 {
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);
242 }
243
244 /*
245   create the sorted accessor arrays for the schema
246  */
247 static int dsdb_setup_sorted_accessors(struct ldb_context *ldb,
248                                        struct dsdb_schema *schema)
249 {
250         struct dsdb_class *cur;
251         struct dsdb_attribute *a;
252         unsigned int i;
253         unsigned int num_int_id;
254
255         /* free all caches */
256         dsdb_sorted_accessors_free(schema);
257
258         /* count the classes */
259         for (i=0, cur=schema->classes; cur; i++, cur=cur->next) /* noop */ ;
260         schema->num_classes = i;
261
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) {
271                 goto failed;
272         }
273
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;
279         }
280
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);
286
287         /* now build the attribute accessor arrays */
288
289         /* count the attributes
290          * and attributes with msDS-IntId set */
291         num_int_id = 0;
292         for (i=0, a=schema->attributes; a; i++, a=a->next) {
293                 if (a->msDS_IntId != 0) {
294                         num_int_id++;
295                 }
296         }
297         schema->num_attributes = i;
298         schema->num_int_id_attr = num_int_id;
299
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) {
312                 goto failed;
313         }
314
315         num_int_id = 0;
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;
324                         num_int_id++;
325                 }
326         }
327         SMB_ASSERT(num_int_id == schema->num_int_id_attr);
328
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);
335
336         return LDB_SUCCESS;
337
338 failed:
339         dsdb_sorted_accessors_free(schema);
340         ldb_oom(ldb);
341         return LDB_ERR_OPERATIONS_ERROR;
342 }
343
344 int dsdb_setup_schema_inversion(struct ldb_context *ldb, struct dsdb_schema *schema)
345 {
346         /* Walk the list of schema classes */
347
348         /*  For each subClassOf, add us to subclasses of the parent */
349
350         /* collect these subclasses into a recursive list of total subclasses, preserving order */
351
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) */
355
356         /* Walk the list of scheam classes */
357         
358         /*  Create a 'total possible superiors' on each class */
359         return LDB_SUCCESS;
360 }
361
362 /**
363  * Attach the schema to an opaque pointer on the ldb, so ldb modules
364  * can find it 
365  */
366
367 int dsdb_set_schema(struct ldb_context *ldb, struct dsdb_schema *schema)
368 {
369         struct dsdb_schema *old_schema;
370         int ret;
371
372         ret = dsdb_setup_sorted_accessors(ldb, schema);
373         if (ret != LDB_SUCCESS) {
374                 return ret;
375         }
376
377         ret = schema_fill_constructed(schema);
378         if (ret != LDB_SUCCESS) {
379                 return ret;
380         }
381
382         old_schema = ldb_get_opaque(ldb, "dsdb_schema");
383
384         ret = ldb_set_opaque(ldb, "dsdb_schema", schema);
385         if (ret != LDB_SUCCESS) {
386                 return ret;
387         }
388         /* Remove the refernece to the schema we just overwrote - if there was none, NULL is harmless here */
389         if (old_schema != schema) {
390                 talloc_unlink(ldb, old_schema);
391                 talloc_steal(ldb, schema);
392         }
393
394         ret = ldb_set_opaque(ldb, "dsdb_use_global_schema", NULL);
395         if (ret != LDB_SUCCESS) {
396                 return ret;
397         }
398
399         /* Set the new attributes based on the new schema */
400         ret = dsdb_schema_set_attributes(ldb, schema, true);
401         if (ret != LDB_SUCCESS) {
402                 return ret;
403         }
404
405         return LDB_SUCCESS;
406 }
407
408 /**
409  * Global variable to hold one copy of the schema, used to avoid memory bloat
410  */
411 static struct dsdb_schema *global_schema;
412
413 /**
414  * Make this ldb use a specified schema, already fully calculated and belonging to another ldb
415  */
416 int dsdb_reference_schema(struct ldb_context *ldb, struct dsdb_schema *schema,
417                           bool write_attributes)
418 {
419         int ret;
420         struct dsdb_schema *old_schema;
421         old_schema = ldb_get_opaque(ldb, "dsdb_schema");
422         ret = ldb_set_opaque(ldb, "dsdb_schema", schema);
423         if (ret != LDB_SUCCESS) {
424                 return ret;
425         }
426
427         /* Remove the refernece to the schema we just overwrote - if there was none, NULL is harmless here */
428         talloc_unlink(ldb, old_schema);
429
430         if (talloc_reference(ldb, schema) == NULL) {
431                 return LDB_ERR_OPERATIONS_ERROR;
432         }
433
434         ret = dsdb_schema_set_attributes(ldb, schema, write_attributes);
435         if (ret != LDB_SUCCESS) {
436                 return ret;
437         }
438
439         return LDB_SUCCESS;
440 }
441
442 /**
443  * Make this ldb use the 'global' schema, setup to avoid having multiple copies in this process
444  */
445 int dsdb_set_global_schema(struct ldb_context *ldb)
446 {
447         int ret;
448         void *use_global_schema = (void *)1;
449         if (!global_schema) {
450                 return LDB_SUCCESS;
451         }
452         ret = ldb_set_opaque(ldb, "dsdb_use_global_schema", use_global_schema);
453         if (ret != LDB_SUCCESS) {
454                 return ret;
455         }
456
457         /* Set the new attributes based on the new schema */
458         ret = dsdb_schema_set_attributes(ldb, global_schema, false /* Don't write attributes, it's expensive */);
459         if (ret == LDB_SUCCESS) {
460                 /* Keep a reference to this schema, just incase the original copy is replaced */
461                 if (talloc_reference(ldb, global_schema) == NULL) {
462                         return LDB_ERR_OPERATIONS_ERROR;
463                 }
464         }
465
466         return ret;
467 }
468
469 /**
470  * Find the schema object for this ldb
471  *
472  * If reference_ctx is not NULL, then talloc_reference onto that context
473  */
474
475 struct dsdb_schema *dsdb_get_schema(struct ldb_context *ldb, TALLOC_CTX *reference_ctx)
476 {
477         const void *p;
478         struct dsdb_schema *schema_out;
479         struct dsdb_schema *schema_in;
480         bool use_global_schema;
481         TALLOC_CTX *tmp_ctx = talloc_new(reference_ctx);
482         if (!tmp_ctx) {
483                 return NULL;
484         }
485
486         /* see if we have a cached copy */
487         use_global_schema = (ldb_get_opaque(ldb, "dsdb_use_global_schema") != NULL);
488         if (use_global_schema) {
489                 schema_in = global_schema;
490         } else {
491                 p = ldb_get_opaque(ldb, "dsdb_schema");
492
493                 schema_in = talloc_get_type(p, struct dsdb_schema);
494                 if (!schema_in) {
495                         talloc_free(tmp_ctx);
496                         return NULL;
497                 }
498         }
499
500         if (schema_in->refresh_fn && !schema_in->refresh_in_progress) {
501                 if (!talloc_reference(tmp_ctx, schema_in)) {
502                         /* ensure that the schema_in->refresh_in_progress remains valid for the right amount of time */
503                         talloc_free(tmp_ctx);
504                         return NULL;
505                 }
506                 schema_in->refresh_in_progress = true;
507                 /* This may change schema, if it needs to reload it from disk */
508                 schema_out = schema_in->refresh_fn(schema_in->loaded_from_module,
509                                                    schema_in,
510                                                    use_global_schema);
511                 schema_in->refresh_in_progress = false;
512         } else {
513                 schema_out = schema_in;
514         }
515
516         /* This removes the extra reference above */
517         talloc_free(tmp_ctx);
518         if (!reference_ctx) {
519                 return schema_out;
520         } else {
521                 return talloc_reference(reference_ctx, schema_out);
522         }
523 }
524
525 /**
526  * Make the schema found on this ldb the 'global' schema
527  */
528
529 void dsdb_make_schema_global(struct ldb_context *ldb, struct dsdb_schema *schema)
530 {
531         if (!schema) {
532                 return;
533         }
534
535         if (global_schema) {
536                 talloc_unlink(talloc_autofree_context(), global_schema);
537         }
538
539         /* Wipe any reference to the exact schema - we will set 'use the global schema' below */
540         ldb_set_opaque(ldb, "dsdb_schema", NULL);
541
542         /* we want the schema to be around permanently */
543         talloc_reparent(ldb, talloc_autofree_context(), schema);
544         global_schema = schema;
545
546         /* This calls the talloc_reference() of the global schema back onto the ldb */
547         dsdb_set_global_schema(ldb);
548 }
549
550 /* When loading the schema from LDIF files, we don't get the extended DNs. 
551    
552    We need to set these up, so that from the moment we start the provision, the defaultObjectCategory links are set up correctly. 
553  */
554 int dsdb_schema_fill_extended_dn(struct ldb_context *ldb, struct dsdb_schema *schema)
555 {
556         struct dsdb_class *cur;
557         const struct dsdb_class *target_class;
558         for (cur = schema->classes; cur; cur = cur->next) {
559                 const struct ldb_val *rdn;
560                 struct ldb_val guid;
561                 NTSTATUS status;
562                 struct ldb_dn *dn = ldb_dn_new(NULL, ldb, cur->defaultObjectCategory);
563
564                 if (!dn) {
565                         return LDB_ERR_INVALID_DN_SYNTAX;
566                 }
567                 rdn = ldb_dn_get_component_val(dn, 0);
568                 if (!rdn) {
569                         talloc_free(dn);
570                         return LDB_ERR_INVALID_DN_SYNTAX;
571                 }
572                 target_class = dsdb_class_by_cn_ldb_val(schema, rdn);
573                 if (!target_class) {
574                         talloc_free(dn);
575                         return LDB_ERR_CONSTRAINT_VIOLATION;
576                 }
577                 
578                 status = GUID_to_ndr_blob(&target_class->objectGUID, dn, &guid);
579                 if (!NT_STATUS_IS_OK(status)) {
580                         talloc_free(dn);
581                         return LDB_ERR_OPERATIONS_ERROR;
582                 }
583                 ldb_dn_set_extended_component(dn, "GUID", &guid);
584
585                 cur->defaultObjectCategory = ldb_dn_get_extended_linearized(cur, dn, 1);
586                 talloc_free(dn);
587         }
588         return LDB_SUCCESS;
589 }
590
591 /** 
592  * Add an element to the schema (attribute or class) from an LDB message
593  */
594 WERROR dsdb_schema_set_el_from_ldb_msg(struct ldb_context *ldb, struct dsdb_schema *schema, 
595                                        struct ldb_message *msg) 
596 {
597         if (samdb_find_attribute(ldb, msg,
598                                  "objectclass", "attributeSchema") != NULL) {
599                 return dsdb_attribute_from_ldb(ldb, schema, msg);
600         } else if (samdb_find_attribute(ldb, msg,
601                                  "objectclass", "classSchema") != NULL) {
602                 return dsdb_class_from_ldb(schema, msg);
603         }
604
605         /* Don't fail on things not classes or attributes */
606         return WERR_OK;
607 }
608
609 /**
610  * Rather than read a schema from the LDB itself, read it from an ldif
611  * file.  This allows schema to be loaded and used while adding the
612  * schema itself to the directory.
613  */
614
615 WERROR dsdb_set_schema_from_ldif(struct ldb_context *ldb, const char *pf, const char *df)
616 {
617         struct ldb_ldif *ldif;
618         struct ldb_message *msg;
619         TALLOC_CTX *mem_ctx;
620         WERROR status;
621         int ret;
622         struct dsdb_schema *schema;
623         const struct ldb_val *prefix_val;
624         const struct ldb_val *info_val;
625         struct ldb_val info_val_default;
626
627
628         mem_ctx = talloc_new(ldb);
629         if (!mem_ctx) {
630                 goto nomem;
631         }
632
633         schema = dsdb_new_schema(mem_ctx);
634
635         schema->fsmo.we_are_master = true;
636         schema->fsmo.master_dn = ldb_dn_new_fmt(schema, ldb, "@PROVISION_SCHEMA_MASTER");
637         if (!schema->fsmo.master_dn) {
638                 goto nomem;
639         }
640
641         /*
642          * load the prefixMap attribute from pf
643          */
644         ldif = ldb_ldif_read_string(ldb, &pf);
645         if (!ldif) {
646                 status = WERR_INVALID_PARAM;
647                 goto failed;
648         }
649         talloc_steal(mem_ctx, ldif);
650
651         msg = ldb_msg_canonicalize(ldb, ldif->msg);
652         if (!msg) {
653                 goto nomem;
654         }
655         talloc_steal(mem_ctx, msg);
656         talloc_free(ldif);
657
658         prefix_val = ldb_msg_find_ldb_val(msg, "prefixMap");
659         if (!prefix_val) {
660                 status = WERR_INVALID_PARAM;
661                 goto failed;
662         }
663
664         info_val = ldb_msg_find_ldb_val(msg, "schemaInfo");
665         if (!info_val) {
666                 status = dsdb_schema_info_blob_new(mem_ctx, &info_val_default);
667                 W_ERROR_NOT_OK_GOTO(status, failed);
668                 info_val = &info_val_default;
669         }
670
671         status = dsdb_load_oid_mappings_ldb(schema, prefix_val, info_val);
672         if (!W_ERROR_IS_OK(status)) {
673                 DEBUG(0,("ERROR: dsdb_load_oid_mappings_ldb() failed with %s\n", win_errstr(status)));
674                 goto failed;
675         }
676
677         /*
678          * load the attribute and class definitions outof df
679          */
680         while ((ldif = ldb_ldif_read_string(ldb, &df))) {
681                 talloc_steal(mem_ctx, ldif);
682
683                 msg = ldb_msg_canonicalize(ldb, ldif->msg);
684                 if (!msg) {
685                         goto nomem;
686                 }
687
688                 status = dsdb_schema_set_el_from_ldb_msg(ldb, schema, msg);
689                 talloc_free(ldif);
690                 if (!W_ERROR_IS_OK(status)) {
691                         goto failed;
692                 }
693         }
694
695         ret = dsdb_set_schema(ldb, schema);
696         if (ret != LDB_SUCCESS) {
697                 status = WERR_FOOBAR;
698                 goto failed;
699         }
700
701         ret = dsdb_schema_fill_extended_dn(ldb, schema);
702         if (ret != LDB_SUCCESS) {
703                 status = WERR_FOOBAR;
704                 goto failed;
705         }
706
707         goto done;
708
709 nomem:
710         status = WERR_NOMEM;
711 failed:
712 done:
713         talloc_free(mem_ctx);
714         return status;
715 }