s4/dsdb: schemaInfo revision may be 0
[Samba.git] / source4 / dsdb / schema / schema_set.c
blobfe6ef42e8c39a471c2422effbc57e59b02415ad5
1 /*
2 Unix SMB/CIFS mplementation.
3 DSDB schema header
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/>.
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"
33 override the name to attribute handler function
35 const struct ldb_schema_attribute *dsdb_attribute_handler_override(struct ldb_context *ldb,
36 void *private_data,
37 const char *name)
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;
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;
55 TALLOC_CTX *mem_ctx;
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) {
63 return ret;
66 mem_ctx = talloc_new(ldb);
67 if (!mem_ctx) {
68 return LDB_ERR_OPERATIONS_ERROR;
71 msg = ldb_msg_new(mem_ctx);
72 if (!msg) {
73 ldb_oom(ldb);
74 goto op_error;
76 msg_idx = ldb_msg_new(mem_ctx);
77 if (!msg_idx) {
78 ldb_oom(ldb);
79 goto op_error;
81 msg->dn = ldb_dn_new(msg, ldb, "@ATTRIBUTES");
82 if (!msg->dn) {
83 ldb_oom(ldb);
84 goto op_error;
86 msg_idx->dn = ldb_dn_new(msg, ldb, "@INDEXLIST");
87 if (!msg_idx->dn) {
88 ldb_oom(ldb);
89 goto op_error;
92 ret = ldb_msg_add_string(msg_idx, "@IDXONE", "1");
93 if (ret != LDB_SUCCESS) {
94 goto op_error;
97 for (attr = schema->attributes; attr; attr = attr->next) {
98 const char *syntax = attr->syntax->ldb_syntax;
100 if (!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) {
111 break;
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;
122 if (ret != LDB_SUCCESS) {
123 talloc_free(mem_ctx);
124 return ret;
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");
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 */
147 ret = LDB_SUCCESS;
149 if (ret != LDB_SUCCESS) {
150 talloc_free(mem_ctx);
151 return ret;
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);
162 } else {
163 ret = LDB_SUCCESS;
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 */
174 ret = LDB_SUCCESS;
176 talloc_free(mem_ctx);
177 return ret;
179 op_error:
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;
232 unsigned int i;
234 talloc_free(schema->classes_by_lDAPDisplayName);
235 talloc_free(schema->classes_by_governsID_id);
236 talloc_free(schema->classes_by_governsID_oid);
237 talloc_free(schema->classes_by_cn);
239 /* count the classes */
240 for (i=0, cur=schema->classes; cur; i++, cur=cur->next) /* noop */ ;
241 schema->num_classes = i;
243 /* setup classes_by_* */
244 schema->classes_by_lDAPDisplayName = talloc_array(schema, struct dsdb_class *, i);
245 schema->classes_by_governsID_id = talloc_array(schema, struct dsdb_class *, i);
246 schema->classes_by_governsID_oid = talloc_array(schema, struct dsdb_class *, i);
247 schema->classes_by_cn = talloc_array(schema, struct dsdb_class *, i);
248 if (schema->classes_by_lDAPDisplayName == NULL ||
249 schema->classes_by_governsID_id == NULL ||
250 schema->classes_by_governsID_oid == NULL ||
251 schema->classes_by_cn == NULL) {
252 goto failed;
255 for (i=0, cur=schema->classes; cur; i++, cur=cur->next) {
256 schema->classes_by_lDAPDisplayName[i] = cur;
257 schema->classes_by_governsID_id[i] = cur;
258 schema->classes_by_governsID_oid[i] = cur;
259 schema->classes_by_cn[i] = cur;
262 /* sort the arrays */
263 TYPESAFE_QSORT(schema->classes_by_lDAPDisplayName, schema->num_classes, dsdb_compare_class_by_lDAPDisplayName);
264 TYPESAFE_QSORT(schema->classes_by_governsID_id, schema->num_classes, dsdb_compare_class_by_governsID_id);
265 TYPESAFE_QSORT(schema->classes_by_governsID_oid, schema->num_classes, dsdb_compare_class_by_governsID_oid);
266 TYPESAFE_QSORT(schema->classes_by_cn, schema->num_classes, dsdb_compare_class_by_cn);
268 /* now build the attribute accessor arrays */
269 talloc_free(schema->attributes_by_lDAPDisplayName);
270 talloc_free(schema->attributes_by_attributeID_id);
271 talloc_free(schema->attributes_by_attributeID_oid);
272 talloc_free(schema->attributes_by_linkID);
274 /* count the attributes */
275 for (i=0, a=schema->attributes; a; i++, a=a->next) /* noop */ ;
276 schema->num_attributes = i;
278 /* setup attributes_by_* */
279 schema->attributes_by_lDAPDisplayName = talloc_array(schema, struct dsdb_attribute *, i);
280 schema->attributes_by_attributeID_id = talloc_array(schema, struct dsdb_attribute *, i);
281 schema->attributes_by_attributeID_oid = talloc_array(schema, struct dsdb_attribute *, i);
282 schema->attributes_by_linkID = talloc_array(schema, struct dsdb_attribute *, i);
283 if (schema->attributes_by_lDAPDisplayName == NULL ||
284 schema->attributes_by_attributeID_id == NULL ||
285 schema->attributes_by_attributeID_oid == NULL ||
286 schema->attributes_by_linkID == NULL) {
287 goto failed;
290 for (i=0, a=schema->attributes; a; i++, a=a->next) {
291 schema->attributes_by_lDAPDisplayName[i] = a;
292 schema->attributes_by_attributeID_id[i] = a;
293 schema->attributes_by_attributeID_oid[i] = a;
294 schema->attributes_by_linkID[i] = a;
297 /* sort the arrays */
298 TYPESAFE_QSORT(schema->attributes_by_lDAPDisplayName, schema->num_attributes, dsdb_compare_attribute_by_lDAPDisplayName);
299 TYPESAFE_QSORT(schema->attributes_by_attributeID_id, schema->num_attributes, dsdb_compare_attribute_by_attributeID_id);
300 TYPESAFE_QSORT(schema->attributes_by_attributeID_oid, schema->num_attributes, dsdb_compare_attribute_by_attributeID_oid);
301 TYPESAFE_QSORT(schema->attributes_by_linkID, schema->num_attributes, dsdb_compare_attribute_by_linkID);
303 return LDB_SUCCESS;
305 failed:
306 schema->classes_by_lDAPDisplayName = NULL;
307 schema->classes_by_governsID_id = NULL;
308 schema->classes_by_governsID_oid = NULL;
309 schema->classes_by_cn = NULL;
310 schema->attributes_by_lDAPDisplayName = NULL;
311 schema->attributes_by_attributeID_id = NULL;
312 schema->attributes_by_attributeID_oid = NULL;
313 schema->attributes_by_linkID = NULL;
314 ldb_oom(ldb);
315 return LDB_ERR_OPERATIONS_ERROR;
318 int dsdb_setup_schema_inversion(struct ldb_context *ldb, struct dsdb_schema *schema)
320 /* Walk the list of schema classes */
322 /* For each subClassOf, add us to subclasses of the parent */
324 /* collect these subclasses into a recursive list of total subclasses, preserving order */
326 /* For each subclass under 'top', write the index from it's
327 * order as an integer in the dsdb_class (for sorting
328 * objectClass lists efficiently) */
330 /* Walk the list of scheam classes */
332 /* Create a 'total possible superiors' on each class */
333 return LDB_SUCCESS;
337 * Attach the schema to an opaque pointer on the ldb, so ldb modules
338 * can find it
341 int dsdb_set_schema(struct ldb_context *ldb, struct dsdb_schema *schema)
343 int ret;
345 ret = dsdb_setup_sorted_accessors(ldb, schema);
346 if (ret != LDB_SUCCESS) {
347 return ret;
350 ret = schema_fill_constructed(schema);
351 if (ret != LDB_SUCCESS) {
352 return ret;
355 ret = ldb_set_opaque(ldb, "dsdb_schema", schema);
356 if (ret != LDB_SUCCESS) {
357 return ret;
360 ret = ldb_set_opaque(ldb, "dsdb_use_global_schema", NULL);
361 if (ret != LDB_SUCCESS) {
362 return ret;
365 /* Set the new attributes based on the new schema */
366 ret = dsdb_schema_set_attributes(ldb, schema, true);
367 if (ret != LDB_SUCCESS) {
368 return ret;
371 talloc_steal(ldb, schema);
373 return LDB_SUCCESS;
377 * Global variable to hold one copy of the schema, used to avoid memory bloat
379 static struct dsdb_schema *global_schema;
382 * Make this ldb use a specified schema, already fully calculated and belonging to another ldb
384 int dsdb_reference_schema(struct ldb_context *ldb, struct dsdb_schema *schema,
385 bool write_attributes)
387 int ret;
388 ret = ldb_set_opaque(ldb, "dsdb_schema", schema);
389 if (ret != LDB_SUCCESS) {
390 return ret;
393 return LDB_SUCCESS;
397 * Make this ldb use the 'global' schema, setup to avoid having multiple copies in this process
399 int dsdb_set_global_schema(struct ldb_context *ldb)
401 int ret;
402 void *use_global_schema = (void *)1;
403 if (!global_schema) {
404 return LDB_SUCCESS;
407 ret = ldb_set_opaque(ldb, "dsdb_use_global_schema", use_global_schema);
408 if (ret != LDB_SUCCESS) {
409 return ret;
412 /* Set the new attributes based on the new schema */
413 ret = dsdb_schema_set_attributes(ldb, global_schema, false /* Don't write attributes, it's expensive */);
414 if (ret == LDB_SUCCESS) {
415 /* Keep a reference to this schema, just incase the original copy is replaced */
416 if (talloc_reference(ldb, global_schema) == NULL) {
417 return LDB_ERR_OPERATIONS_ERROR;
421 return ret;
425 * Find the schema object for this ldb
427 * If reference_ctx is not NULL, then talloc_reference onto that context
430 struct dsdb_schema *dsdb_get_schema(struct ldb_context *ldb, TALLOC_CTX *reference_ctx)
432 const void *p;
433 struct dsdb_schema *schema_out;
434 struct dsdb_schema *schema_in;
435 bool use_global_schema;
437 /* see if we have a cached copy */
438 use_global_schema = (ldb_get_opaque(ldb, "dsdb_use_global_schema") != NULL);
439 if (use_global_schema) {
440 schema_in = global_schema;
441 } else {
442 p = ldb_get_opaque(ldb, "dsdb_schema");
444 schema_in = talloc_get_type(p, struct dsdb_schema);
445 if (!schema_in) {
446 return NULL;
450 if (schema_in->refresh_fn && !schema_in->refresh_in_progress) {
451 schema_in->refresh_in_progress = true;
452 /* This may change schema, if it needs to reload it from disk */
453 schema_out = schema_in->refresh_fn(schema_in->loaded_from_module,
454 schema_in,
455 use_global_schema);
456 schema_in->refresh_in_progress = false;
457 if (schema_out != schema_in) {
458 talloc_unlink(schema_in, ldb);
460 } else {
461 schema_out = schema_in;
464 if (!reference_ctx) {
465 return schema_out;
466 } else {
467 return talloc_reference(reference_ctx, schema_out);
472 * Make the schema found on this ldb the 'global' schema
475 void dsdb_make_schema_global(struct ldb_context *ldb, struct dsdb_schema *schema)
477 if (!schema) {
478 return;
481 if (global_schema) {
482 talloc_unlink(talloc_autofree_context(), global_schema);
485 /* Wipe any reference to the exact schema - we will set 'use the global schema' below */
486 ldb_set_opaque(ldb, "dsdb_schema", NULL);
488 /* we want the schema to be around permanently */
489 talloc_reparent(ldb, talloc_autofree_context(), schema);
490 global_schema = schema;
492 /* This calls the talloc_reference() of the global schema back onto the ldb */
493 dsdb_set_global_schema(ldb);
496 /* When loading the schema from LDIF files, we don't get the extended DNs.
498 We need to set these up, so that from the moment we start the provision, the defaultObjectCategory links are set up correctly.
500 int dsdb_schema_fill_extended_dn(struct ldb_context *ldb, struct dsdb_schema *schema)
502 struct dsdb_class *cur;
503 const struct dsdb_class *target_class;
504 for (cur = schema->classes; cur; cur = cur->next) {
505 const struct ldb_val *rdn;
506 struct ldb_val guid;
507 NTSTATUS status;
508 struct ldb_dn *dn = ldb_dn_new(NULL, ldb, cur->defaultObjectCategory);
510 if (!dn) {
511 return LDB_ERR_INVALID_DN_SYNTAX;
513 rdn = ldb_dn_get_component_val(dn, 0);
514 if (!rdn) {
515 talloc_free(dn);
516 return LDB_ERR_INVALID_DN_SYNTAX;
518 target_class = dsdb_class_by_cn_ldb_val(schema, rdn);
519 if (!target_class) {
520 talloc_free(dn);
521 return LDB_ERR_CONSTRAINT_VIOLATION;
524 status = GUID_to_ndr_blob(&target_class->objectGUID, dn, &guid);
525 if (!NT_STATUS_IS_OK(status)) {
526 talloc_free(dn);
527 return LDB_ERR_OPERATIONS_ERROR;
529 ldb_dn_set_extended_component(dn, "GUID", &guid);
531 cur->defaultObjectCategory = ldb_dn_get_extended_linearized(cur, dn, 1);
532 talloc_free(dn);
534 return LDB_SUCCESS;
537 /**
538 * Add an element to the schema (attribute or class) from an LDB message
540 WERROR dsdb_schema_set_el_from_ldb_msg(struct ldb_context *ldb, struct dsdb_schema *schema,
541 struct ldb_message *msg)
543 static struct ldb_parse_tree *attr_tree, *class_tree;
544 if (!attr_tree) {
545 attr_tree = ldb_parse_tree(talloc_autofree_context(), "(objectClass=attributeSchema)");
546 if (!attr_tree) {
547 return WERR_NOMEM;
551 if (!class_tree) {
552 class_tree = ldb_parse_tree(talloc_autofree_context(), "(objectClass=classSchema)");
553 if (!class_tree) {
554 return WERR_NOMEM;
558 if (ldb_match_msg(ldb, msg, attr_tree, NULL, LDB_SCOPE_BASE)) {
559 return dsdb_attribute_from_ldb(ldb, schema, msg);
560 } else if (ldb_match_msg(ldb, msg, class_tree, NULL, LDB_SCOPE_BASE)) {
561 return dsdb_class_from_ldb(schema, msg);
564 /* Don't fail on things not classes or attributes */
565 return WERR_OK;
569 * Rather than read a schema from the LDB itself, read it from an ldif
570 * file. This allows schema to be loaded and used while adding the
571 * schema itself to the directory.
574 WERROR dsdb_set_schema_from_ldif(struct ldb_context *ldb, const char *pf, const char *df)
576 struct ldb_ldif *ldif;
577 struct ldb_message *msg;
578 TALLOC_CTX *mem_ctx;
579 WERROR status;
580 int ret;
581 struct dsdb_schema *schema;
582 const struct ldb_val *prefix_val;
583 const struct ldb_val *info_val;
584 struct ldb_val info_val_default;
587 mem_ctx = talloc_new(ldb);
588 if (!mem_ctx) {
589 goto nomem;
592 schema = dsdb_new_schema(mem_ctx, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")));
594 schema->fsmo.we_are_master = true;
595 schema->fsmo.master_dn = ldb_dn_new_fmt(schema, ldb, "@PROVISION_SCHEMA_MASTER");
596 if (!schema->fsmo.master_dn) {
597 goto nomem;
601 * load the prefixMap attribute from pf
603 ldif = ldb_ldif_read_string(ldb, &pf);
604 if (!ldif) {
605 status = WERR_INVALID_PARAM;
606 goto failed;
608 talloc_steal(mem_ctx, ldif);
610 msg = ldb_msg_canonicalize(ldb, ldif->msg);
611 if (!msg) {
612 goto nomem;
614 talloc_steal(mem_ctx, msg);
615 talloc_free(ldif);
617 prefix_val = ldb_msg_find_ldb_val(msg, "prefixMap");
618 if (!prefix_val) {
619 status = WERR_INVALID_PARAM;
620 goto failed;
623 info_val = ldb_msg_find_ldb_val(msg, "schemaInfo");
624 if (!info_val) {
625 status = dsdb_schema_info_blob_new(mem_ctx, &info_val_default);
626 W_ERROR_NOT_OK_GOTO(status, failed);
627 info_val = &info_val_default;
630 status = dsdb_load_oid_mappings_ldb(schema, prefix_val, info_val);
631 if (!W_ERROR_IS_OK(status)) {
632 DEBUG(0,("ERROR: dsdb_load_oid_mappings_ldb() failed with %s\n", win_errstr(status)));
633 goto failed;
637 * load the attribute and class definitions outof df
639 while ((ldif = ldb_ldif_read_string(ldb, &df))) {
640 talloc_steal(mem_ctx, ldif);
642 msg = ldb_msg_canonicalize(ldb, ldif->msg);
643 if (!msg) {
644 goto nomem;
647 status = dsdb_schema_set_el_from_ldb_msg(ldb, schema, msg);
648 talloc_free(ldif);
649 if (!W_ERROR_IS_OK(status)) {
650 goto failed;
654 ret = dsdb_set_schema(ldb, schema);
655 if (ret != LDB_SUCCESS) {
656 status = WERR_FOOBAR;
657 goto failed;
660 ret = dsdb_schema_fill_extended_dn(ldb, schema);
661 if (ret != LDB_SUCCESS) {
662 status = WERR_FOOBAR;
663 goto failed;
666 goto done;
668 nomem:
669 status = WERR_NOMEM;
670 failed:
671 done:
672 talloc_free(mem_ctx);
673 return status;