Use common code to fill in allowedAttributes in kludge_acl.
[kai/samba.git] / source4 / dsdb / schema / schema_init.c
index 6f8958dab8f29b2486c53d24758eb5237aa78d8c..71d952b944bbaa926fd4aaab9fccb3a89246cc8c 100644 (file)
@@ -2,8 +2,8 @@
    Unix SMB/CIFS mplementation.
    DSDB schema header
    
-   Copyright (C) Stefan Metzmacher <metze@samba.org> 2006
-   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
+   Copyright (C) Stefan Metzmacher <metze@samba.org> 2006-2007
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006-2008
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -334,6 +334,29 @@ WERROR dsdb_map_int2oid(const struct dsdb_schema *schema, uint32_t in, TALLOC_CT
        return WERR_DS_NO_MSDS_INTID;
 }
 
+/*
+ * this function is called from within a ldb transaction from the schema_fsmo module
+ */
+WERROR dsdb_create_prefix_mapping(struct ldb_context *ldb, struct dsdb_schema *schema, const char *full_oid)
+{
+       /*
+        * TODO:
+        *      - (maybe) read the old prefixMap attribute and parse it
+        *
+        *      - recheck the prefix doesn't exist (because the ldb
+        *        has maybe a more uptodate value than schem->prefixes
+        *
+        *      - calculate a new mapping for the oid prefix of full_oid
+        *      - store the new prefixMap attribute
+        *
+        *      - (maybe) update schema->prefixes
+        *      or
+        *      - better find a way to indicate a schema reload,
+        *        so that other processes also notice the schema change
+        */
+       return WERR_NOT_SUPPORTED;
+}
+
 #define GET_STRING_LDB(msg, attr, mem_ctx, p, elem, strict) do { \
        (p)->elem = samdb_result_string(msg, attr, NULL);\
        if (strict && (p)->elem == NULL) { \
@@ -512,9 +535,8 @@ WERROR dsdb_class_from_ldb(const struct dsdb_schema *schema,
  
        GET_STRING_LDB(msg, "subClassOf", mem_ctx, obj, subClassOf, true);
 
-       obj->systemAuxiliaryClass       = NULL;
-
-       obj->auxiliaryClass             = NULL;
+       GET_STRING_LIST_LDB(msg, "systemAuxiliaryClass", mem_ctx, obj, systemAuxiliaryClass, false);
+       GET_STRING_LIST_LDB(msg, "auxiliaryClass", mem_ctx, obj, auxiliaryClass, false);
 
        GET_STRING_LIST_LDB(msg, "systemMustContain", mem_ctx, obj, systemMustContain, false);
        GET_STRING_LIST_LDB(msg, "systemMayContain", mem_ctx, obj, systemMayContain, false);
@@ -541,6 +563,299 @@ WERROR dsdb_class_from_ldb(const struct dsdb_schema *schema,
        return WERR_OK;
 }
 
+#define dsdb_oom(error_string, mem_ctx) *error_string = talloc_asprintf(mem_ctx, "dsdb out of memory at %s:%d\n", __FILE__, __LINE__)
+
+int dsdb_schema_from_ldb_results(TALLOC_CTX *mem_ctx, struct ldb_context *ldb,
+                                struct smb_iconv_convenience *iconv_convenience, 
+                                struct ldb_result *schema_res,
+                                struct ldb_result *attrs_res, struct ldb_result *objectclass_res, 
+                                struct dsdb_schema **schema_out,
+                                char **error_string)
+{
+       WERROR status;
+       uint32_t i;
+       const struct ldb_val *prefix_val;
+       const struct ldb_val *info_val;
+       struct ldb_val info_val_default;
+       struct dsdb_schema *schema;
+
+       schema = dsdb_new_schema(mem_ctx, iconv_convenience);
+       if (!schema) {
+               dsdb_oom(error_string, mem_ctx);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       prefix_val = ldb_msg_find_ldb_val(schema_res->msgs[0], "prefixMap");
+       if (!prefix_val) {
+               *error_string = talloc_asprintf(mem_ctx, 
+                                               "schema_fsmo_init: no prefixMap attribute found");
+               talloc_free(mem_ctx);
+               return LDB_ERR_CONSTRAINT_VIOLATION;
+       }
+       info_val = ldb_msg_find_ldb_val(schema_res->msgs[0], "schemaInfo");
+       if (!info_val) {
+               info_val_default = strhex_to_data_blob("FF0000000000000000000000000000000000000000");
+               if (!info_val_default.data) {
+                       dsdb_oom(error_string, mem_ctx);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+               talloc_steal(mem_ctx, info_val_default.data);
+               info_val = &info_val_default;
+       }
+
+       status = dsdb_load_oid_mappings_ldb(schema, prefix_val, info_val);
+       if (!W_ERROR_IS_OK(status)) {
+               *error_string = talloc_asprintf(mem_ctx, 
+                             "schema_fsmo_init: failed to load oid mappings: %s",
+                             win_errstr(status));
+               talloc_free(mem_ctx);
+               return LDB_ERR_CONSTRAINT_VIOLATION;
+       }
+
+       for (i=0; i < attrs_res->count; i++) {
+               struct dsdb_attribute *sa;
+
+               sa = talloc_zero(schema, struct dsdb_attribute);
+               if (!sa) {
+                       dsdb_oom(error_string, mem_ctx);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+
+               status = dsdb_attribute_from_ldb(schema, attrs_res->msgs[i], sa, sa);
+               if (!W_ERROR_IS_OK(status)) {
+                       *error_string = talloc_asprintf(mem_ctx, 
+                                     "schema_fsmo_init: failed to load attribute definition: %s:%s",
+                                     ldb_dn_get_linearized(attrs_res->msgs[i]->dn),
+                                     win_errstr(status));
+                       talloc_free(mem_ctx);
+                       return LDB_ERR_CONSTRAINT_VIOLATION;
+               }
+
+               DLIST_ADD_END(schema->attributes, sa, struct dsdb_attribute *);
+       }
+
+       for (i=0; i < objectclass_res->count; i++) {
+               struct dsdb_class *sc;
+
+               sc = talloc_zero(schema, struct dsdb_class);
+               if (!sc) {
+                       dsdb_oom(error_string, mem_ctx);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+
+               status = dsdb_class_from_ldb(schema, objectclass_res->msgs[i], sc, sc);
+               if (!W_ERROR_IS_OK(status)) {
+                       *error_string = talloc_asprintf(mem_ctx, 
+                                     "schema_fsmo_init: failed to load class definition: %s:%s",
+                                     ldb_dn_get_linearized(objectclass_res->msgs[i]->dn),
+                                     win_errstr(status));
+                       talloc_free(mem_ctx);
+                       return LDB_ERR_CONSTRAINT_VIOLATION;
+               }
+
+               DLIST_ADD_END(schema->classes, sc, struct dsdb_class *);
+       }
+
+       schema->fsmo.master_dn = ldb_msg_find_attr_as_dn(ldb, schema, schema_res->msgs[0], "fSMORoleOwner");
+       if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), schema->fsmo.master_dn) == 0) {
+               schema->fsmo.we_are_master = true;
+       } else {
+               schema->fsmo.we_are_master = false;
+       }
+
+       DEBUG(5, ("schema_fsmo_init: we are master: %s\n",
+                 (schema->fsmo.we_are_master?"yes":"no")));
+
+       *schema_out = schema;
+       return LDB_SUCCESS;
+}
+
+/* This recursive load of the objectClasses presumes that they
+ * everything is in a strict subClassOf hirarchy.  
+ *
+ * We load this in order so we produce certain outputs (such as the
+ * exported schema for openldap, and sorted objectClass attribute) 'in
+ * order' */
+
+static int fetch_oc_recursive(struct ldb_context *ldb, struct ldb_dn *schemadn, 
+                             TALLOC_CTX *mem_ctx, 
+                             struct ldb_result *search_from,
+                             struct ldb_result *res_list)
+{
+       int i;
+       int ret = 0;
+       for (i=0; i < search_from->count; i++) {
+               struct ldb_result *res;
+               const char *name = ldb_msg_find_attr_as_string(search_from->msgs[i], 
+                                                              "lDAPDisplayname", NULL);
+
+               ret = ldb_search_exp_fmt(ldb, mem_ctx, &res,
+                                       schemadn, LDB_SCOPE_SUBTREE, NULL,
+                                       "(&(&(objectClass=classSchema)(subClassOf=%s))(!(lDAPDisplayName=%s)))",
+                                       name, name);
+               if (ret != LDB_SUCCESS) {
+                       printf("Search failed: %s\n", ldb_errstring(ldb));
+                       return ret;
+               }
+               
+               res_list->msgs = talloc_realloc(res_list, res_list->msgs, 
+                                               struct ldb_message *, res_list->count + 2);
+               if (!res_list->msgs) {
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+               res_list->msgs[res_list->count] = talloc_move(res_list, 
+                                                             &search_from->msgs[i]);
+               res_list->count++;
+               res_list->msgs[res_list->count] = NULL;
+
+               if (res->count > 0) {
+                       ret = fetch_oc_recursive(ldb, schemadn, mem_ctx, res, res_list); 
+               }
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+       }
+       return ret;
+}
+
+static int fetch_objectclass_schema(struct ldb_context *ldb, struct ldb_dn *schemadn, 
+                                   TALLOC_CTX *mem_ctx, 
+                                   struct ldb_result **objectclasses_res)
+{
+       TALLOC_CTX *local_ctx = talloc_new(mem_ctx);
+       struct ldb_result *top_res, *ret_res;
+       int ret;
+       if (!local_ctx) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       
+       /* Downlaod 'top' */
+       ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE, 
+                        "(&(objectClass=classSchema)(lDAPDisplayName=top))", 
+                        NULL, &top_res);
+       if (ret != LDB_SUCCESS) {
+               printf("Search failed: %s\n", ldb_errstring(ldb));
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       talloc_steal(local_ctx, top_res);
+
+       if (top_res->count != 1) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ret_res = talloc_zero(local_ctx, struct ldb_result);
+       if (!ret_res) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ret = fetch_oc_recursive(ldb, schemadn, local_ctx, top_res, ret_res); 
+
+       if (ret != LDB_SUCCESS) {
+               printf("Search failed: %s\n", ldb_errstring(ldb));
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       *objectclasses_res = talloc_move(mem_ctx, &ret_res);
+       return ret;
+}
+
+int dsdb_schema_from_schema_dn(TALLOC_CTX *mem_ctx, struct ldb_context *ldb,
+                              struct smb_iconv_convenience *iconv_convenience, 
+                              struct ldb_dn *schema_dn,
+                              struct dsdb_schema **schema,
+                              char **error_string_out) 
+{
+       TALLOC_CTX *tmp_ctx;
+       char *error_string;
+       int ret;
+
+       struct ldb_result *schema_res;
+       struct ldb_result *a_res;
+       struct ldb_result *c_res;
+       static const char *schema_attrs[] = {
+               "prefixMap",
+               "schemaInfo",
+               "fSMORoleOwner",
+               NULL
+       };
+
+       tmp_ctx = talloc_new(mem_ctx);
+       if (!tmp_ctx) {
+               dsdb_oom(error_string_out, mem_ctx);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       /*
+        * setup the prefix mappings and schema info
+        */
+       ret = ldb_search(ldb, schema_dn,
+                        LDB_SCOPE_BASE,
+                        NULL, schema_attrs,
+                        &schema_res);
+       if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+               talloc_free(tmp_ctx);
+               return ret;
+       } else if (ret != LDB_SUCCESS) {
+               *error_string_out = talloc_asprintf(mem_ctx, 
+                                      "dsdb_schema: failed to search the schema head: %s",
+                                      ldb_errstring(ldb));
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+       talloc_steal(tmp_ctx, schema_res);
+       if (schema_res->count != 1) {
+               *error_string_out = talloc_asprintf(mem_ctx, 
+                             "dsdb_schema: [%u] schema heads found on a base search",
+                             schema_res->count);
+               talloc_free(tmp_ctx);
+               return LDB_ERR_CONSTRAINT_VIOLATION;
+       }
+
+       /*
+        * load the attribute definitions
+        */
+       ret = ldb_search(ldb, schema_dn,
+                        LDB_SCOPE_ONELEVEL,
+                        "(objectClass=attributeSchema)", NULL,
+                        &a_res);
+       if (ret != LDB_SUCCESS) {
+               *error_string_out = talloc_asprintf(mem_ctx, 
+                                      "dsdb_schema: failed to search attributeSchema objects: %s",
+                                      ldb_errstring(ldb));
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+       talloc_steal(tmp_ctx, a_res);
+
+       /*
+        * load the objectClass definitions
+        */
+       ret = fetch_objectclass_schema(ldb, schema_dn, tmp_ctx, &c_res);
+       if (ret != LDB_SUCCESS) {
+               *error_string_out = talloc_asprintf(mem_ctx, 
+                                      "Failed to fetch objectClass schema elements: %s\n", ldb_errstring(ldb));
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       ret = dsdb_schema_from_ldb_results(tmp_ctx, ldb,
+                                          lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+                                          schema_res, a_res, c_res, schema, &error_string);
+       if (ret != LDB_SUCCESS) {
+               *error_string_out = talloc_asprintf(mem_ctx, 
+                                                   "dsdb_schema load failed: %s",
+                                                   error_string);
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+       talloc_steal(mem_ctx, *schema);
+       talloc_free(tmp_ctx);
+
+       return LDB_SUCCESS;
+}      
+
+
 static const struct {
        const char *name;
        const char *oid;
@@ -1037,6 +1352,13 @@ const char *dsdb_lDAPDisplayName_by_id(const struct dsdb_schema *schema,
        return NULL;
 }
 
+/** 
+    Return a list of linked attributes, in lDAPDisplayName format.
+
+    This may be used to determine if a modification would require
+    backlinks to be updated, for example
+*/
+
 WERROR dsdb_linked_attribute_lDAPDisplayName_list(const struct dsdb_schema *schema, TALLOC_CTX *mem_ctx, const char ***attr_list_ret)
 {
        const char **attr_list = NULL;
@@ -1057,6 +1379,123 @@ WERROR dsdb_linked_attribute_lDAPDisplayName_list(const struct dsdb_schema *sche
        return WERR_OK;
 }
 
+static char **merge_attr_list(TALLOC_CTX *mem_ctx, 
+                             char **attrs, const char **new_attrs) 
+{
+       char **ret_attrs;
+       int i;
+       size_t new_len, orig_len = str_list_length((const char **)attrs);
+       if (!new_attrs) {
+               return attrs;
+       }
+
+       ret_attrs = talloc_realloc(mem_ctx, 
+                                  attrs, char *, orig_len + str_list_length(new_attrs) + 1);
+       if (ret_attrs) {
+               for (i=0; i < str_list_length(new_attrs); i++) {
+                       ret_attrs[orig_len + i] = new_attrs[i];
+               }
+               new_len = orig_len + str_list_length(new_attrs);
+
+               ret_attrs[new_len] = NULL;
+
+       }
+
+       return ret_attrs;
+}
+
+char **dsdb_full_attribute_list_internal(TALLOC_CTX *mem_ctx, 
+                                        const struct dsdb_schema *schema, 
+                                        const char **class_list,
+                                        enum dsdb_attr_list_query query)
+{
+       int i;
+       const struct dsdb_class *class;
+       
+       char **attr_list = NULL;
+       char **recursive_list;
+
+       for (i=0; class_list && class_list[i]; i++) {
+               class = dsdb_class_by_lDAPDisplayName(schema, class_list[i]);
+               
+               switch (query) {
+               case DSDB_SCHEMA_ALL_MAY:
+                       attr_list = merge_attr_list(mem_ctx, attr_list, class->mayContain);
+                       attr_list = merge_attr_list(mem_ctx, attr_list, class->systemMayContain);
+                       break;
+
+               case DSDB_SCHEMA_ALL_MUST:
+                       attr_list = merge_attr_list(mem_ctx, attr_list, class->mustContain);
+                       attr_list = merge_attr_list(mem_ctx, attr_list, class->systemMustContain);
+                       break;
+
+               case DSDB_SCHEMA_SYS_MAY:
+                       attr_list = merge_attr_list(mem_ctx, attr_list, class->systemMayContain);
+                       break;
+
+               case DSDB_SCHEMA_SYS_MUST:
+                       attr_list = merge_attr_list(mem_ctx, attr_list, class->systemMustContain);
+                       break;
+
+               case DSDB_SCHEMA_MAY:
+                       attr_list = merge_attr_list(mem_ctx, attr_list, class->mayContain);
+                       break;
+
+               case DSDB_SCHEMA_MUST:
+                       attr_list = merge_attr_list(mem_ctx, attr_list, class->mustContain);
+                       break;
+
+               case DSDB_SCHEMA_ALL:
+                       attr_list = merge_attr_list(mem_ctx, attr_list, class->mayContain);
+                       attr_list = merge_attr_list(mem_ctx, attr_list, class->systemMayContain);
+                       attr_list = merge_attr_list(mem_ctx, attr_list, class->mustContain);
+                       attr_list = merge_attr_list(mem_ctx, attr_list, class->systemMustContain);
+                       break;
+               }
+
+               recursive_list = dsdb_full_attribute_list_internal(mem_ctx, schema, 
+                                                                  class->systemAuxiliaryClass, 
+                                                                  query);
+               
+               attr_list = merge_attr_list(mem_ctx, attr_list, (const char **)recursive_list);
+               
+               recursive_list = dsdb_full_attribute_list_internal(mem_ctx, schema, 
+                                                                  class->auxiliaryClass, 
+                                                                  query);
+
+               attr_list = merge_attr_list(mem_ctx, attr_list, (const char **)recursive_list);
+               
+       }
+       return attr_list;
+}
+
+char **dsdb_full_attribute_list(TALLOC_CTX *mem_ctx, 
+                               const struct dsdb_schema *schema, 
+                               const char **class_list,
+                               enum dsdb_attr_list_query query)
+{
+       char **attr_list = dsdb_full_attribute_list_internal(mem_ctx, schema, class_list, query);
+       size_t new_len = str_list_length((const char **)attr_list);
+
+       /* Remove duplicates */
+       if (new_len > 1) {
+               int i;
+               qsort(attr_list, new_len,
+                     sizeof(*attr_list),
+                     (comparison_fn_t)strcasecmp);
+               
+               for (i=1 ; i < new_len; i++) {
+                       char **val1 = &attr_list[i-1];
+                       char **val2 = &attr_list[i];
+                       if (ldb_attr_cmp(*val1, *val2) == 0) {
+                               memmove(val1, val2, (new_len - i) * sizeof( *attr_list)); 
+                               new_len--;
+                               i--;
+                       }
+               }
+       }
+       return attr_list;
+}
 /**
  * Attach the schema to an opaque pointer on the ldb, so ldb modules
  * can find it