summaryrefslogtreecommitdiffstats
path: root/source4/dsdb/schema
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
commit4f5791ebd03eaec1c7da0865a383175b05102712 (patch)
tree8ce7b00f7a76baa386372422adebbe64510812d4 /source4/dsdb/schema
parentInitial commit. (diff)
downloadsamba-4f5791ebd03eaec1c7da0865a383175b05102712.tar.xz
samba-4f5791ebd03eaec1c7da0865a383175b05102712.zip
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source4/dsdb/schema')
-rw-r--r--source4/dsdb/schema/dsdb_dn.c102
-rw-r--r--source4/dsdb/schema/prefixmap.h54
-rw-r--r--source4/dsdb/schema/schema.h349
-rw-r--r--source4/dsdb/schema/schema_convert_to_ol.c381
-rw-r--r--source4/dsdb/schema/schema_description.c397
-rw-r--r--source4/dsdb/schema/schema_filtered.c101
-rw-r--r--source4/dsdb/schema/schema_inferiors.c347
-rw-r--r--source4/dsdb/schema/schema_info_attr.c235
-rw-r--r--source4/dsdb/schema/schema_init.c1038
-rw-r--r--source4/dsdb/schema/schema_prefixmap.c710
-rw-r--r--source4/dsdb/schema/schema_query.c610
-rw-r--r--source4/dsdb/schema/schema_set.c1140
-rw-r--r--source4/dsdb/schema/schema_syntax.c2811
-rw-r--r--source4/dsdb/schema/tests/schema_syntax.c271
14 files changed, 8546 insertions, 0 deletions
diff --git a/source4/dsdb/schema/dsdb_dn.c b/source4/dsdb/schema/dsdb_dn.c
new file mode 100644
index 0000000..06565a9
--- /dev/null
+++ b/source4/dsdb/schema/dsdb_dn.c
@@ -0,0 +1,102 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+
+ Copyright (C) Andrew Tridgell 2009
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009
+
+ 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
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "dsdb/samdb/samdb.h"
+#include <ldb_module.h>
+#include "librpc/ndr/libndr.h"
+#include "libcli/security/dom_sid.h"
+
+/*
+ convert a dsdb_dn to a linked attribute data blob
+*/
+WERROR dsdb_dn_la_to_blob(struct ldb_context *sam_ctx,
+ const struct dsdb_attribute *schema_attrib,
+ const struct dsdb_schema *schema,
+ TALLOC_CTX *mem_ctx,
+ struct dsdb_dn *dsdb_dn, DATA_BLOB **blob)
+{
+ struct ldb_val v;
+ WERROR werr;
+ struct ldb_message_element val_el;
+ struct drsuapi_DsReplicaAttribute drs;
+ struct dsdb_syntax_ctx syntax_ctx;
+
+ /* use default syntax conversion context */
+ dsdb_syntax_ctx_init(&syntax_ctx, sam_ctx, schema);
+
+ /* we need a message_element with just one value in it */
+ v = data_blob_string_const(dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1));
+
+ val_el.name = schema_attrib->lDAPDisplayName;
+ val_el.values = &v;
+ val_el.num_values = 1;
+
+ werr = schema_attrib->syntax->ldb_to_drsuapi(&syntax_ctx, schema_attrib, &val_el, mem_ctx, &drs);
+ W_ERROR_NOT_OK_RETURN(werr);
+
+ if (drs.value_ctr.num_values != 1) {
+ DEBUG(1,(__location__ ": Failed to build DRS blob for linked attribute %s\n",
+ schema_attrib->lDAPDisplayName));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ *blob = drs.value_ctr.values[0].blob;
+ return WERR_OK;
+}
+
+/*
+ convert a data blob to a dsdb_dn
+ */
+WERROR dsdb_dn_la_from_blob(struct ldb_context *sam_ctx,
+ const struct dsdb_attribute *schema_attrib,
+ const struct dsdb_schema *schema,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ struct dsdb_dn **dsdb_dn)
+{
+ WERROR werr;
+ struct ldb_message_element new_el;
+ struct drsuapi_DsReplicaAttribute drs;
+ struct drsuapi_DsAttributeValue val;
+ struct dsdb_syntax_ctx syntax_ctx;
+
+ /* use default syntax conversion context */
+ dsdb_syntax_ctx_init(&syntax_ctx, sam_ctx, schema);
+
+ drs.value_ctr.num_values = 1;
+ drs.value_ctr.values = &val;
+ val.blob = blob;
+
+ werr = schema_attrib->syntax->drsuapi_to_ldb(&syntax_ctx, schema_attrib, &drs, mem_ctx, &new_el);
+ W_ERROR_NOT_OK_RETURN(werr);
+
+ if (new_el.num_values != 1) {
+ return WERR_INTERNAL_ERROR;
+ }
+
+ *dsdb_dn = dsdb_dn_parse(mem_ctx, sam_ctx, &new_el.values[0], schema_attrib->syntax->ldap_oid);
+ if (!*dsdb_dn) {
+ return WERR_INTERNAL_ERROR;
+ }
+
+ return WERR_OK;
+}
diff --git a/source4/dsdb/schema/prefixmap.h b/source4/dsdb/schema/prefixmap.h
new file mode 100644
index 0000000..339a221
--- /dev/null
+++ b/source4/dsdb/schema/prefixmap.h
@@ -0,0 +1,54 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ DRS::prefixMap data structures
+
+ Copyright (C) Kamen Mazdrashki <kamen.mazdrashki@postpath.com> 2009
+
+ 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
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _DSDB_PREFIXMAP_H
+#define _DSDB_PREFIXMAP_H
+
+/**
+ * ATTRTYP ranges
+ * Ref: MS-ADTS, 3.1.1.2.6 ATTRTYP
+ */
+enum dsdb_attid_type {
+ DSDB_ATTID_TYPE_PFM = 1, /* attid in [0x00000000..0x7FFFFFFF] */
+ DSDB_ATTID_TYPE_INTID = 2, /* attid in [0x80000000..0xBFFFFFFF] */
+ DSDB_ATTID_TYPE_RESERVED = 3, /* attid in [0xC0000000..0xFFFEFFFF] */
+ DSDB_ATTID_TYPE_INTERNAL = 4, /* attid in [0xFFFF0000..0xFFFFFFFF] */
+};
+
+/**
+ * oid-prefix in prefixmap
+ */
+struct dsdb_schema_prefixmap_oid {
+ uint32_t id;
+ DATA_BLOB bin_oid; /* partial binary-oid prefix */
+};
+
+/**
+ * DSDB prefixMap internal presentation
+ */
+struct dsdb_schema_prefixmap {
+ uint32_t length;
+ struct dsdb_schema_prefixmap_oid *prefixes;
+};
+
+
+
+#endif /* _DSDB_PREFIXMAP_H */
diff --git a/source4/dsdb/schema/schema.h b/source4/dsdb/schema/schema.h
new file mode 100644
index 0000000..8e33583
--- /dev/null
+++ b/source4/dsdb/schema/schema.h
@@ -0,0 +1,349 @@
+/*
+ Unix SMB/CIFS Implementation.
+ DSDB schema header
+
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 2006
+
+ 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
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef _DSDB_SCHEMA_H
+#define _DSDB_SCHEMA_H
+
+#include "prefixmap.h"
+
+enum dsdb_dn_format {
+ DSDB_NORMAL_DN,
+ DSDB_BINARY_DN,
+ DSDB_STRING_DN,
+ DSDB_INVALID_DN
+};
+
+
+struct dsdb_attribute;
+struct dsdb_class;
+struct dsdb_schema;
+struct dsdb_dn;
+
+struct dsdb_syntax_ctx {
+ struct ldb_context *ldb;
+ const struct dsdb_schema *schema;
+
+ /* set when converting objects under Schema NC */
+ bool is_schema_nc;
+
+ /* remote prefixMap to be used for drsuapi_to_ldb conversions */
+ const struct dsdb_schema_prefixmap *pfm_remote;
+};
+
+
+struct dsdb_syntax {
+ const char *name;
+ const char *ldap_oid;
+ uint32_t oMSyntax;
+ struct ldb_val oMObjectClass;
+ const char *attributeSyntax_oid;
+ const char *equality;
+ const char *substring;
+ const char *comment;
+ const char *ldb_syntax;
+
+ WERROR (*drsuapi_to_ldb)(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out);
+ WERROR (*ldb_to_drsuapi)(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out);
+ WERROR (*validate_ldb)(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in);
+ bool auto_normalise;
+ bool userParameters; /* Indicates the syntax userParameters should be forced to */
+};
+
+struct dsdb_attribute {
+ struct dsdb_attribute *prev, *next;
+
+ const char *cn;
+ const char *lDAPDisplayName;
+ const char *attributeID_oid;
+ uint32_t attributeID_id;
+ struct GUID schemaIDGUID;
+ uint32_t mAPIID;
+ uint32_t msDS_IntId;
+
+ struct GUID attributeSecurityGUID;
+ struct GUID objectGUID;
+
+ uint32_t searchFlags;
+ uint32_t systemFlags;
+ bool isMemberOfPartialAttributeSet;
+ uint32_t linkID;
+
+ const char *attributeSyntax_oid;
+ uint32_t attributeSyntax_id;
+ uint32_t oMSyntax;
+ struct ldb_val oMObjectClass;
+
+ bool isSingleValued;
+ uint32_t *rangeLower;
+ uint32_t *rangeUpper;
+ bool extendedCharsAllowed;
+
+ uint32_t schemaFlagsEx;
+ struct ldb_val msDs_Schema_Extensions;
+
+ bool showInAdvancedViewOnly;
+ const char *adminDisplayName;
+ const char *adminDescription;
+ const char *classDisplayName;
+ bool isEphemeral;
+ bool isDefunct;
+ bool systemOnly;
+
+ bool one_way_link;
+ enum dsdb_dn_format dn_format;
+
+ /* internal stuff */
+ const struct dsdb_syntax *syntax;
+ const struct ldb_schema_attribute *ldb_schema_attribute;
+};
+
+struct dsdb_class {
+ struct dsdb_class *prev, *next;
+
+ const char *cn;
+ const char *lDAPDisplayName;
+ const char *governsID_oid;
+ uint32_t governsID_id;
+ struct GUID schemaIDGUID;
+ struct GUID objectGUID;
+
+ uint32_t objectClassCategory;
+ const char *rDNAttID;
+ const char *defaultObjectCategory;
+
+ const char *subClassOf;
+
+ const char **systemAuxiliaryClass;
+ const char **systemPossSuperiors;
+ const char **systemMustContain;
+ const char **systemMayContain;
+
+ const char **auxiliaryClass;
+ const char **possSuperiors;
+ const char **mustContain;
+ const char **mayContain;
+ const char **possibleInferiors;
+ const char **systemPossibleInferiors;
+
+ const char *defaultSecurityDescriptor;
+
+ uint32_t schemaFlagsEx;
+ uint32_t systemFlags;
+ struct ldb_val msDs_Schema_Extensions;
+
+ bool showInAdvancedViewOnly;
+ const char *adminDisplayName;
+ const char *adminDescription;
+ const char *classDisplayName;
+ bool defaultHidingValue;
+ bool isDefunct;
+ bool systemOnly;
+
+ uint32_t subClassOf_id;
+ uint32_t *systemAuxiliaryClass_ids;
+ uint32_t *auxiliaryClass_ids;
+ uint32_t *systemMayContain_ids;
+ uint32_t *systemMustContain_ids;
+ uint32_t *possSuperiors_ids;
+ uint32_t *mustContain_ids;
+ uint32_t *mayContain_ids;
+ uint32_t *systemPossSuperiors_ids;
+
+ /* An ordered index showing how this subClass fits into the
+ * subClass tree. that is, an objectclass that is not
+ * subClassOf anything is 0 (just in case), and top is 1, and
+ * subClasses of top are 2, subclasses of those classes are
+ * 3 */
+ uint32_t subClass_order;
+
+ struct {
+ const char **supclasses;
+ const char **subclasses;
+ const char **subclasses_direct;
+ const char **posssuperiors;
+ } tmp;
+};
+
+enum schema_set_enum {
+ SCHEMA_MEMORY_ONLY = 0,
+ SCHEMA_WRITE = 1,
+ SCHEMA_COMPARE = 2,
+};
+
+/**
+ * data stored in schemaInfo attribute
+ */
+struct dsdb_schema_info {
+ uint32_t revision;
+ struct GUID invocation_id;
+};
+
+
+struct dsdb_schema {
+ struct dsdb_schema_prefixmap *prefixmap;
+
+ /*
+ * the last element of the prefix mapping table isn't a oid,
+ * it starts with 0xFF and has 21 bytes and is maybe a schema
+ * version number
+ *
+ * this is the content of the schemaInfo attribute of the
+ * Schema-Partition head object.
+ */
+ struct dsdb_schema_info *schema_info;
+
+ struct dsdb_attribute *attributes;
+ struct dsdb_class *classes;
+
+ struct dsdb_attribute **attributes_to_remove;
+ uint32_t attributes_to_remove_size;
+ struct dsdb_class **classes_to_remove;
+ uint32_t classes_to_remove_size;
+
+ /* lists of classes sorted by various attributes, for faster
+ access */
+ uint32_t num_classes;
+ struct dsdb_class **classes_by_lDAPDisplayName;
+ struct dsdb_class **classes_by_governsID_id;
+ struct dsdb_class **classes_by_governsID_oid;
+ struct dsdb_class **classes_by_cn;
+
+ /* lists of attributes sorted by various fields */
+ uint32_t num_attributes;
+ struct dsdb_attribute **attributes_by_lDAPDisplayName;
+ struct dsdb_attribute **attributes_by_attributeID_id;
+ struct dsdb_attribute **attributes_by_attributeID_oid;
+ struct dsdb_attribute **attributes_by_linkID;
+ uint32_t num_int_id_attr;
+ struct dsdb_attribute **attributes_by_msDS_IntId;
+
+ struct {
+ bool we_are_master;
+ bool update_allowed;
+ struct ldb_dn *master_dn;
+ } fsmo;
+
+ /* Was this schema loaded from ldb (if so, then we will reload it when we detect a change in ldb) */
+ bool refresh_in_progress;
+ time_t ts_last_change;
+ /* This 'opaque' is stored in the metadata and is used to check if the currently
+ * loaded schema needs a reload because another process has signaled that it has been
+ * requested to reload the schema (either due through DRS or via the schemaUpdateNow).
+ */
+ uint64_t metadata_usn;
+
+ /* Should the syntax handlers in this case handle all incoming OIDs automatically, assigning them as an OID if no text name is known? */
+ bool relax_OID_conversions;
+
+ /*
+ * we're currently trying to construct a working_schema
+ * in order to replicate the schema partition.
+ *
+ * We use this in order to avoid temporary failure DEBUG messages
+ */
+ bool resolving_in_progress;
+};
+
+#define DSDB_SCHEMA_COMMON_ATTRS \
+ "objectClass", \
+ "cn", \
+ "lDAPDisplayName", \
+ "schemaIDGUID", \
+ "objectGUID", \
+ "systemFlags", \
+ "schemaFlagsEx", \
+ "msDs-Schema-Extensions", \
+ "showInAdvancedViewOnly", \
+ "adminDisplayName", \
+ "adminDescription", \
+ "isDefunct", \
+ "systemOnly"
+
+#define DSDB_SCHEMA_ATTR_ATTRS \
+ "attributeID", \
+ "msDS-IntId", \
+ "mAPIID", \
+ "attributeSecurityGUID", \
+ "searchFlags", \
+ "isMemberOfPartialAttributeSet", \
+ "linkID", \
+ "attributeSyntax", \
+ "oMSyntax", \
+ "oMObjectClass", \
+ "isSingleValued", \
+ "rangeLower", \
+ "rangeUpper", \
+ "extendedCharsAllowed", \
+ "classDisplayName", \
+ "isEphemeral"
+
+#define DSDB_SCHEMA_CLASS_ATTRS \
+ "governsID", \
+ "objectClassCategory", \
+ "rDNAttID", \
+ "defaultObjectCategory", \
+ "subClassOf", \
+ "systemAuxiliaryClass", \
+ "auxiliaryClass", \
+ "systemMustContain", \
+ "systemMayContain", \
+ "mustContain", \
+ "mayContain", \
+ "systemPossSuperiors", \
+ "possSuperiors", \
+ "defaultSecurityDescriptor", \
+ "classDisplayName", \
+ "defaultHidingValue"
+
+enum dsdb_attr_list_query {
+ DSDB_SCHEMA_ALL_MAY,
+ DSDB_SCHEMA_ALL_MUST,
+ DSDB_SCHEMA_SYS_MAY,
+ DSDB_SCHEMA_SYS_MUST,
+ DSDB_SCHEMA_MAY,
+ DSDB_SCHEMA_MUST,
+ DSDB_SCHEMA_ALL
+};
+
+enum dsdb_schema_convert_target {
+ TARGET_OPENLDAP,
+ TARGET_FEDORA_DS,
+ TARGET_AD_SCHEMA_SUBENTRY
+};
+
+struct ldb_module;
+
+typedef struct dsdb_schema *(*dsdb_schema_refresh_fn)(struct ldb_module *module,
+ struct tevent_context *ev,
+ struct dsdb_schema *schema, bool is_global_schema);
+#include "dsdb/schema/proto.h"
+
+#endif /* _DSDB_SCHEMA_H */
diff --git a/source4/dsdb/schema/schema_convert_to_ol.c b/source4/dsdb/schema/schema_convert_to_ol.c
new file mode 100644
index 0000000..ebb886d
--- /dev/null
+++ b/source4/dsdb/schema/schema_convert_to_ol.c
@@ -0,0 +1,381 @@
+/*
+ schema conversion routines
+
+ Copyright (C) Andrew Bartlett 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
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "ldb.h"
+#include "dsdb/samdb/samdb.h"
+#include "system/locale.h"
+
+#undef strcasecmp
+
+#define SEPERATOR "\n "
+
+struct attr_map {
+ char *old_attr;
+ char *new_attr;
+};
+
+struct oid_map {
+ char *old_oid;
+ char *new_oid;
+};
+
+static char *print_schema_recursive(char *append_to_string, struct dsdb_schema *schema, const char *print_class,
+ enum dsdb_schema_convert_target target,
+ const char **attrs_skip, const struct attr_map *attr_map, const struct oid_map *oid_map)
+{
+ char *out = append_to_string;
+ const struct dsdb_class *objectclass;
+ objectclass = dsdb_class_by_lDAPDisplayName(schema, print_class);
+ if (!objectclass) {
+ DEBUG(0, ("Cannot find class %s in schema\n", print_class));
+ return NULL;
+ }
+
+ do {
+ TALLOC_CTX *mem_ctx = talloc_new(append_to_string);
+ const char *name = objectclass->lDAPDisplayName;
+ const char *oid = objectclass->governsID_oid;
+ const char *subClassOf = objectclass->subClassOf;
+ int objectClassCategory = objectclass->objectClassCategory;
+ const char **must;
+ const char **may;
+ char *schema_entry = NULL;
+ struct ldb_val objectclass_name_as_ldb_val = data_blob_string_const(objectclass->lDAPDisplayName);
+ struct ldb_message_element objectclass_name_as_el = {
+ .name = "objectClass",
+ .num_values = 1,
+ .values = &objectclass_name_as_ldb_val
+ };
+ unsigned int j;
+ unsigned int attr_idx;
+
+ if (!mem_ctx) {
+ DEBUG(0, ("Failed to create new talloc context\n"));
+ return NULL;
+ }
+
+ /* We have been asked to skip some attributes/objectClasses */
+ if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
+ continue;
+ }
+
+ /* We might have been asked to remap this oid, due to a conflict */
+ for (j=0; oid_map && oid_map[j].old_oid; j++) {
+ if (strcasecmp(oid, oid_map[j].old_oid) == 0) {
+ oid = oid_map[j].new_oid;
+ break;
+ }
+ }
+
+ /* We might have been asked to remap this name, due to a conflict */
+ for (j=0; name && attr_map && attr_map[j].old_attr; j++) {
+ if (strcasecmp(name, attr_map[j].old_attr) == 0) {
+ name = attr_map[j].new_attr;
+ break;
+ }
+ }
+
+ /* We might have been asked to remap this subClassOf, due to a conflict */
+ for (j=0; subClassOf && attr_map && attr_map[j].old_attr; j++) {
+ if (strcasecmp(subClassOf, attr_map[j].old_attr) == 0) {
+ subClassOf = attr_map[j].new_attr;
+ break;
+ }
+ }
+
+ may = dsdb_full_attribute_list(mem_ctx, schema, &objectclass_name_as_el, DSDB_SCHEMA_ALL_MAY);
+
+ for (j=0; may && may[j]; j++) {
+ /* We might have been asked to remap this name, due to a conflict */
+ for (attr_idx=0; attr_map && attr_map[attr_idx].old_attr; attr_idx++) {
+ if (strcasecmp(may[j], attr_map[attr_idx].old_attr) == 0) {
+ may[j] = attr_map[attr_idx].new_attr;
+ break;
+ }
+ }
+ }
+
+ must = dsdb_full_attribute_list(mem_ctx, schema, &objectclass_name_as_el, DSDB_SCHEMA_ALL_MUST);
+
+ for (j=0; must && must[j]; j++) {
+ /* We might have been asked to remap this name, due to a conflict */
+ for (attr_idx=0; attr_map && attr_map[attr_idx].old_attr; attr_idx++) {
+ if (strcasecmp(must[j], attr_map[attr_idx].old_attr) == 0) {
+ must[j] = attr_map[attr_idx].new_attr;
+ break;
+ }
+ }
+ }
+
+ schema_entry = schema_class_description(mem_ctx, target,
+ SEPERATOR,
+ oid,
+ name,
+ NULL,
+ subClassOf,
+ objectClassCategory,
+ must,
+ may,
+ NULL);
+ if (schema_entry == NULL) {
+ talloc_free(mem_ctx);
+ DEBUG(0, ("failed to generate schema description for %s\n", name));
+ return NULL;
+ }
+
+ switch (target) {
+ case TARGET_OPENLDAP:
+ out = talloc_asprintf_append(out, "objectclass %s\n\n", schema_entry);
+ break;
+ case TARGET_FEDORA_DS:
+ out = talloc_asprintf_append(out, "objectClasses: %s\n", schema_entry);
+ break;
+ default:
+ talloc_free(mem_ctx);
+ DEBUG(0,(__location__ " Wrong type of target %u!", (unsigned)target));
+ return NULL;
+ }
+ talloc_free(mem_ctx);
+ } while (0);
+
+
+ for (objectclass=schema->classes; objectclass; objectclass = objectclass->next) {
+ if (ldb_attr_cmp(objectclass->subClassOf, print_class) == 0
+ && ldb_attr_cmp(objectclass->lDAPDisplayName, print_class) != 0) {
+ out = print_schema_recursive(out, schema, objectclass->lDAPDisplayName,
+ target, attrs_skip, attr_map, oid_map);
+ }
+ }
+ return out;
+}
+
+/* Routine to linearise our internal schema into the format that
+ OpenLDAP and Fedora DS use for their backend.
+
+ The 'mappings' are of a format like:
+
+#Standard OpenLDAP attributes
+labeledURI
+#The memberOf plugin provides this attribute
+memberOf
+#These conflict with OpenLDAP builtins
+attributeTypes:samba4AttributeTypes
+2.5.21.5:1.3.6.1.4.1.7165.4.255.7
+
+*/
+
+
+char *dsdb_convert_schema_to_openldap(struct ldb_context *ldb, char *target_str, const char *mappings)
+{
+ /* Read list of attributes to skip, OIDs to map */
+ TALLOC_CTX *mem_ctx = talloc_new(ldb);
+ char *line;
+ char *out;
+ const char **attrs_skip = NULL;
+ unsigned int num_skip = 0;
+ struct oid_map *oid_map = NULL;
+ unsigned int num_oid_maps = 0;
+ struct attr_map *attr_map = NULL;
+ unsigned int num_attr_maps = 0;
+ struct dsdb_attribute *attribute;
+ struct dsdb_schema *schema;
+ enum dsdb_schema_convert_target target;
+
+ char *next_line = talloc_strdup(mem_ctx, mappings);
+
+ if (!target_str || strcasecmp(target_str, "openldap") == 0) {
+ target = TARGET_OPENLDAP;
+ } else if (strcasecmp(target_str, "fedora-ds") == 0) {
+ target = TARGET_FEDORA_DS;
+ } else {
+ talloc_free(mem_ctx);
+ DEBUG(0, ("Invalid target type for schema conversion %s\n", target_str));
+ return NULL;
+ }
+
+ /* The mappings are line-separated, and specify details such as OIDs to skip etc */
+ while (1) {
+ line = next_line;
+ next_line = strchr(line, '\n');
+ if (!next_line) {
+ break;
+ }
+ next_line[0] = '\0';
+ next_line++;
+
+ /* Blank Line */
+ if (line[0] == '\0') {
+ continue;
+ }
+ /* Comment */
+ if (line[0] == '#') {
+ continue;
+ }
+
+ if (isdigit(line[0])) {
+ char *p = strchr(line, ':');
+ if (!p) {
+ DEBUG(0, ("schema mapping file line has OID but no OID to map to: %s\n", line));
+ return NULL;
+ }
+ p[0] = '\0';
+ p++;
+ oid_map = talloc_realloc(mem_ctx, oid_map, struct oid_map, num_oid_maps + 2);
+ trim_string(line, " ", " ");
+ oid_map[num_oid_maps].old_oid = talloc_strdup(oid_map, line);
+ trim_string(p, " ", " ");
+ oid_map[num_oid_maps].new_oid = p;
+ num_oid_maps++;
+ oid_map[num_oid_maps].old_oid = NULL;
+ } else {
+ char *p = strchr(line, ':');
+ if (p) {
+ /* remap attribute/objectClass */
+ p[0] = '\0';
+ p++;
+ attr_map = talloc_realloc(mem_ctx, attr_map, struct attr_map, num_attr_maps + 2);
+ trim_string(line, " ", " ");
+ attr_map[num_attr_maps].old_attr = talloc_strdup(attr_map, line);
+ trim_string(p, " ", " ");
+ attr_map[num_attr_maps].new_attr = p;
+ num_attr_maps++;
+ attr_map[num_attr_maps].old_attr = NULL;
+ } else {
+ /* skip attribute/objectClass */
+ attrs_skip = talloc_realloc(mem_ctx, attrs_skip, const char *, num_skip + 2);
+ trim_string(line, " ", " ");
+ attrs_skip[num_skip] = talloc_strdup(attrs_skip, line);
+ num_skip++;
+ attrs_skip[num_skip] = NULL;
+ }
+ }
+ }
+
+ schema = dsdb_get_schema(ldb, mem_ctx);
+ if (!schema) {
+ talloc_free(mem_ctx);
+ DEBUG(0, ("No schema on ldb to convert!\n"));
+ return NULL;
+ }
+
+ switch (target) {
+ case TARGET_OPENLDAP:
+ out = talloc_strdup(mem_ctx, "");
+ break;
+ case TARGET_FEDORA_DS:
+ out = talloc_strdup(mem_ctx, "dn: cn=schema\n");
+ break;
+ default:
+ talloc_free(mem_ctx);
+ DEBUG(0,(__location__ " Wrong type of target %u!", (unsigned)target));
+ return NULL;
+ }
+
+ for (attribute=schema->attributes; attribute; attribute = attribute->next) {
+ const char *name = attribute->lDAPDisplayName;
+ const char *oid = attribute->attributeID_oid;
+ const char *syntax = attribute->attributeSyntax_oid;
+ const char *equality = NULL, *substring = NULL;
+ bool single_value = attribute->isSingleValued;
+
+ char *schema_entry = NULL;
+ unsigned int j;
+
+ /* We have been asked to skip some attributes/objectClasses */
+ if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
+ continue;
+ }
+
+ /* We might have been asked to remap this oid, due to a conflict */
+ for (j=0; oid && oid_map && oid_map[j].old_oid; j++) {
+ if (strcasecmp(oid, oid_map[j].old_oid) == 0) {
+ oid = oid_map[j].new_oid;
+ break;
+ }
+ }
+
+ if (attribute->syntax) {
+ /* We might have been asked to remap this oid,
+ * due to a conflict, or lack of
+ * implementation */
+ syntax = attribute->syntax->ldap_oid;
+ /* We might have been asked to remap this oid, due to a conflict */
+ for (j=0; syntax && oid_map && oid_map[j].old_oid; j++) {
+ if (strcasecmp(syntax, oid_map[j].old_oid) == 0) {
+ syntax = oid_map[j].new_oid;
+ break;
+ }
+ }
+
+ equality = attribute->syntax->equality;
+ substring = attribute->syntax->substring;
+ }
+
+ /* We might have been asked to remap this name, due to a conflict */
+ for (j=0; name && attr_map && attr_map[j].old_attr; j++) {
+ if (strcasecmp(name, attr_map[j].old_attr) == 0) {
+ name = attr_map[j].new_attr;
+ break;
+ }
+ }
+
+ schema_entry = schema_attribute_description(mem_ctx,
+ target,
+ SEPERATOR,
+ oid,
+ name,
+ equality,
+ substring,
+ syntax,
+ single_value,
+ false,
+ NULL, NULL,
+ NULL, NULL,
+ false, false);
+
+ if (schema_entry == NULL) {
+ talloc_free(mem_ctx);
+ DEBUG(0, ("failed to generate attribute description for %s\n", name));
+ return NULL;
+ }
+
+ switch (target) {
+ case TARGET_OPENLDAP:
+ out = talloc_asprintf_append(out, "attributetype %s\n\n", schema_entry);
+ break;
+ case TARGET_FEDORA_DS:
+ out = talloc_asprintf_append(out, "attributeTypes: %s\n", schema_entry);
+ break;
+ default:
+ talloc_free(mem_ctx);
+ DEBUG(0,(__location__ " Wrong type of target %u!", (unsigned)target));
+ return NULL;
+ }
+ }
+
+ out = print_schema_recursive(out, schema, "top", target, attrs_skip, attr_map, oid_map);
+
+ talloc_steal(ldb, out);
+ talloc_free(mem_ctx);
+
+ return out;
+}
+
diff --git a/source4/dsdb/schema/schema_description.c b/source4/dsdb/schema/schema_description.c
new file mode 100644
index 0000000..5fc7015
--- /dev/null
+++ b/source4/dsdb/schema/schema_description.c
@@ -0,0 +1,397 @@
+/*
+ Unix SMB/CIFS Implementation.
+ Print schema info into string format
+
+ Copyright (C) Andrew Bartlett 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
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+#include "includes.h"
+#include "dsdb/samdb/samdb.h"
+#include "librpc/ndr/libndr.h"
+
+#undef strcasecmp
+
+char *schema_attribute_description(TALLOC_CTX *mem_ctx,
+ enum dsdb_schema_convert_target target,
+ const char *separator,
+ const char *oid,
+ const char *name,
+ const char *equality,
+ const char *substring,
+ const char *syntax,
+ bool single_value, bool operational,
+ uint32_t *range_lower,
+ uint32_t *range_upper,
+ const char *property_guid,
+ const char *property_set_guid,
+ bool indexed, bool system_only)
+{
+ char *schema_entry = talloc_asprintf(mem_ctx,
+ "(%s%s%s", separator, oid, separator);
+
+ talloc_asprintf_addbuf(
+ &schema_entry, "NAME '%s'%s", name, separator);
+
+ if (equality) {
+ talloc_asprintf_addbuf(
+ &schema_entry, "EQUALITY %s%s", equality, separator);
+ }
+ if (substring) {
+ talloc_asprintf_addbuf(
+ &schema_entry, "SUBSTR %s%s", substring, separator);
+ }
+
+ if (syntax) {
+ talloc_asprintf_addbuf(
+ &schema_entry, "SYNTAX %s%s", syntax, separator);
+ }
+
+ if (single_value) {
+ talloc_asprintf_addbuf(
+ &schema_entry, "SINGLE-VALUE%s", separator);
+ }
+
+ if (operational) {
+ talloc_asprintf_addbuf(
+ &schema_entry, "NO-USER-MODIFICATION%s", separator);
+ }
+
+ if (range_lower) {
+ talloc_asprintf_addbuf(
+ &schema_entry,
+ "RANGE-LOWER '%u'%s",
+ *range_lower,
+ separator);
+ }
+
+ if (range_upper) {
+ talloc_asprintf_addbuf(
+ &schema_entry,
+ "RANGE-UPPER '%u'%s",
+ *range_upper,
+ separator);
+ }
+
+ if (property_guid) {
+ talloc_asprintf_addbuf(
+ &schema_entry,
+ "PROPERTY-GUID '%s'%s",
+ property_guid,
+ separator);
+ }
+
+ if (property_set_guid) {
+ talloc_asprintf_addbuf(
+ &schema_entry,
+ "PROPERTY-SET-GUID '%s'%s",
+ property_set_guid,
+ separator);
+ }
+
+ if (indexed) {
+ talloc_asprintf_addbuf(
+ &schema_entry, "INDEXED%s", separator);
+ }
+
+ if (system_only) {
+ talloc_asprintf_addbuf(
+ &schema_entry, "SYSTEM-ONLY%s", separator);
+ }
+
+ talloc_asprintf_addbuf(&schema_entry, ")");
+
+ return schema_entry;
+}
+
+char *schema_attribute_to_description(TALLOC_CTX *mem_ctx, const struct dsdb_attribute *attribute)
+{
+ char *schema_description;
+ const char *syntax = attribute->syntax->ldap_oid;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return NULL;
+ }
+
+ schema_description
+ = schema_attribute_description(mem_ctx,
+ TARGET_AD_SCHEMA_SUBENTRY,
+ " ",
+ attribute->attributeID_oid,
+ attribute->lDAPDisplayName,
+ NULL, NULL, talloc_asprintf(tmp_ctx, "'%s'", syntax),
+ attribute->isSingleValued,
+ attribute->systemOnly,/* TODO: is this correct? */
+ NULL, NULL, NULL, NULL,
+ false, false);
+ talloc_free(tmp_ctx);
+ return schema_description;
+}
+
+char *schema_attribute_to_extendedInfo(TALLOC_CTX *mem_ctx, const struct dsdb_attribute *attribute)
+{
+ char *schema_description;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return NULL;
+ }
+
+ schema_description
+ = schema_attribute_description(mem_ctx,
+ TARGET_AD_SCHEMA_SUBENTRY,
+ " ",
+ attribute->attributeID_oid,
+ attribute->lDAPDisplayName,
+ NULL, NULL, NULL,
+ false, false,
+ attribute->rangeLower,
+ attribute->rangeUpper,
+ GUID_hexstring(tmp_ctx, &attribute->schemaIDGUID),
+ GUID_hexstring(tmp_ctx, &attribute->attributeSecurityGUID),
+ /*
+ * We actually ignore the indexed
+ * flag for confidential
+ * attributes, but we'll include
+ * it for the purposes of
+ * description.
+ */
+ (attribute->searchFlags & SEARCH_FLAG_ATTINDEX),
+ attribute->systemOnly);
+ talloc_free(tmp_ctx);
+ return schema_description;
+}
+
+#define APPEND_ATTRS(attributes) \
+ do { \
+ unsigned int k; \
+ for (k=0; attributes && attributes[k]; k++) { \
+ const char *attr_name = attributes[k]; \
+ \
+ talloc_asprintf_addbuf(&schema_entry, \
+ "%s ", \
+ attr_name); \
+ if (attributes[k+1]) { \
+ if (target == TARGET_OPENLDAP && ((k+1)%5 == 0)) { \
+ talloc_asprintf_addbuf(&schema_entry, \
+ "$%s ", separator); \
+ } else { \
+ talloc_asprintf_addbuf(&schema_entry, \
+ "$ "); \
+ } \
+ } \
+ } \
+ } while (0)
+
+
+/* Print a schema class or dITContentRule as a string.
+ *
+ * To print a scheam class, specify objectClassCategory but not auxillary_classes
+ * To print a dITContentRule, specify auxillary_classes but set objectClassCategory == -1
+ *
+ */
+
+char *schema_class_description(TALLOC_CTX *mem_ctx,
+ enum dsdb_schema_convert_target target,
+ const char *separator,
+ const char *oid,
+ const char *name,
+ const char **auxillary_classes,
+ const char *subClassOf,
+ int objectClassCategory,
+ const char **must,
+ const char **may,
+ const char *schemaHexGUID)
+{
+ char *schema_entry = talloc_asprintf(mem_ctx,
+ "(%s%s%s", separator, oid, separator);
+
+ talloc_asprintf_addbuf(&schema_entry, "NAME '%s'%s", name, separator);
+
+ if (auxillary_classes) {
+ talloc_asprintf_addbuf(&schema_entry, "AUX ( ");
+
+ APPEND_ATTRS(auxillary_classes);
+
+ talloc_asprintf_addbuf(&schema_entry, ")%s", separator);
+ }
+
+ if (subClassOf && strcasecmp(subClassOf, name) != 0) {
+ talloc_asprintf_addbuf(
+ &schema_entry, "SUP %s%s", subClassOf, separator);
+ }
+
+ switch (objectClassCategory) {
+ case -1:
+ break;
+ /* Dummy case for when used for printing ditContentRules */
+ case 0:
+ /*
+ * NOTE: this is an type 88 class
+ * e.g. 2.5.6.6 NAME 'person'
+ * but w2k3 gives STRUCTURAL here!
+ */
+ talloc_asprintf_addbuf(
+ &schema_entry, "STRUCTURAL%s", separator);
+ break;
+ case 1:
+ talloc_asprintf_addbuf(
+ &schema_entry, "STRUCTURAL%s", separator);
+ break;
+ case 2:
+ talloc_asprintf_addbuf(
+ &schema_entry, "ABSTRACT%s", separator);
+ break;
+ case 3:
+ talloc_asprintf_addbuf(
+ &schema_entry, "AUXILIARY%s", separator);
+ break;
+ }
+
+ if (must) {
+ talloc_asprintf_addbuf(
+ &schema_entry,
+ "MUST (%s",
+ target == TARGET_AD_SCHEMA_SUBENTRY ? "" : " ");
+
+ APPEND_ATTRS(must);
+
+ talloc_asprintf_addbuf(
+ &schema_entry, ")%s", separator);
+ }
+
+ if (may) {
+ talloc_asprintf_addbuf(
+ &schema_entry,
+ "MAY (%s",
+ target == TARGET_AD_SCHEMA_SUBENTRY ? "" : " ");
+
+ APPEND_ATTRS(may);
+
+ talloc_asprintf_addbuf(
+ &schema_entry, ")%s", separator);
+ }
+
+ if (schemaHexGUID) {
+ talloc_asprintf_addbuf(
+ &schema_entry,
+ "CLASS-GUID '%s'%s",
+ schemaHexGUID,
+ separator);
+ }
+
+ talloc_asprintf_addbuf(&schema_entry, ")");
+
+ return schema_entry;
+}
+
+char *schema_class_to_description(TALLOC_CTX *mem_ctx, const struct dsdb_class *sclass)
+{
+ char *schema_description;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return NULL;
+ }
+
+ schema_description
+ = schema_class_description(mem_ctx,
+ TARGET_AD_SCHEMA_SUBENTRY,
+ " ",
+ sclass->governsID_oid,
+ sclass->lDAPDisplayName,
+ NULL,
+ sclass->subClassOf,
+ sclass->objectClassCategory,
+ dsdb_attribute_list(tmp_ctx,
+ sclass, DSDB_SCHEMA_ALL_MUST),
+ dsdb_attribute_list(tmp_ctx,
+ sclass, DSDB_SCHEMA_ALL_MAY),
+ NULL);
+ talloc_free(tmp_ctx);
+ return schema_description;
+}
+
+char *schema_class_to_dITContentRule(TALLOC_CTX *mem_ctx, const struct dsdb_class *sclass,
+ const struct dsdb_schema *schema)
+{
+ unsigned int i;
+ char *schema_description;
+ const char **aux_class_list = NULL;
+ const char **attrs;
+ const char **must_attr_list = NULL;
+ const char **may_attr_list = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ const struct dsdb_class *aux_class;
+ if (!tmp_ctx) {
+ return NULL;
+ }
+
+ aux_class_list = merge_attr_list(tmp_ctx, aux_class_list, sclass->systemAuxiliaryClass);
+ aux_class_list = merge_attr_list(tmp_ctx, aux_class_list, sclass->auxiliaryClass);
+
+ for (i=0; aux_class_list && aux_class_list[i]; i++) {
+ aux_class = dsdb_class_by_lDAPDisplayName(schema, aux_class_list[i]);
+
+ attrs = dsdb_attribute_list(mem_ctx, aux_class, DSDB_SCHEMA_ALL_MUST);
+ must_attr_list = merge_attr_list(mem_ctx, must_attr_list, attrs);
+
+ attrs = dsdb_attribute_list(mem_ctx, aux_class, DSDB_SCHEMA_ALL_MAY);
+ may_attr_list = merge_attr_list(mem_ctx, may_attr_list, attrs);
+ }
+
+ schema_description
+ = schema_class_description(mem_ctx,
+ TARGET_AD_SCHEMA_SUBENTRY,
+ " ",
+ sclass->governsID_oid,
+ sclass->lDAPDisplayName,
+ (const char **)aux_class_list,
+ NULL, /* Must not specify a
+ * SUP (subclass) in
+ * ditContentRules
+ * per MS-ADTS
+ * 3.1.1.3.1.1.1 */
+ -1, must_attr_list, may_attr_list,
+ NULL);
+ talloc_free(tmp_ctx);
+ return schema_description;
+}
+
+char *schema_class_to_extendedInfo(TALLOC_CTX *mem_ctx, const struct dsdb_class *sclass)
+{
+ char *schema_description = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return NULL;
+ }
+
+ schema_description
+ = schema_class_description(mem_ctx,
+ TARGET_AD_SCHEMA_SUBENTRY,
+ " ",
+ sclass->governsID_oid,
+ sclass->lDAPDisplayName,
+ NULL,
+ NULL, /* Must not specify a
+ * SUP (subclass) in
+ * ditContentRules
+ * per MS-ADTS
+ * 3.1.1.3.1.1.1 */
+ -1, NULL, NULL,
+ GUID_hexstring(tmp_ctx, &sclass->schemaIDGUID));
+ talloc_free(tmp_ctx);
+ return schema_description;
+}
+
+
diff --git a/source4/dsdb/schema/schema_filtered.c b/source4/dsdb/schema/schema_filtered.c
new file mode 100644
index 0000000..d341040
--- /dev/null
+++ b/source4/dsdb/schema/schema_filtered.c
@@ -0,0 +1,101 @@
+/*
+ Unix SMB/CIFS Implementation.
+ API for determining af an attribute belongs to the filtered set.
+
+ Copyright (C) Nadezhda Ivanova <nivanova@samba.org> 2010
+
+ 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
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+#include "includes.h"
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/common/util.h"
+#include <ldb_errors.h>
+#include "../lib/util/dlinklist.h"
+#include "param/param.h"
+
+static const char * const never_in_filtered_attrs[] = {
+ "accountExpires",
+ "codePage",
+ "creationTime",
+ "dNSHostName",
+ "displayName",
+ "domainReplica",
+ "fSMORoleOwner",
+ "flatName",
+ "isCriticalSystemObject",
+ "lockOutObservationWindow",
+ "lockoutDuration",
+ "lockoutTime",
+ "logonHours",
+ "maxPwdAge",
+ "minPwdAge",
+ "minPwdLength",
+ "msDS-AdditionalDnsHostName",
+ "msDS-AdditionalSamAccountName",
+ "msDS-AllowedToDelegateTo",
+ "msDS-AuthenticatedAtDC",
+ "msDS-ExecuteScriptPassword",
+ "msDS-KrbTgtLink",
+ "msDS-SPNSuffixes",
+ "msDS-SupportedEncryptionTypes",
+ "msDS-TrustForestTrustInfo",
+ "nETBIOSName",
+ "nTMixedDomain",
+ "notFiltlockoutThreshold",
+ "operatingSystem",
+ "operatingSystemServicePack",
+ "operatingSystemVersion",
+ "pwdHistoryLength",
+ "pwdLastSet",
+ "pwdProperties",
+ "rid",
+ "sIDHistory",
+ "securityIdentifier",
+ "servicePrincipalName",
+ "trustAttributes",
+ "trustDirection",
+ "trustParent",
+ "trustPartner",
+ "trustPosixOffset",
+ "trustType",
+ DSDB_SECRET_ATTRIBUTES
+};
+
+/* returns true if the attribute can be in a filtered replica */
+
+bool dsdb_attribute_is_attr_in_filtered_replica(struct dsdb_attribute *attribute)
+{
+ int i, size = sizeof(never_in_filtered_attrs)/sizeof(char *);
+ if (attribute->systemOnly ||
+ attribute->schemaFlagsEx & SCHEMA_FLAG_ATTR_IS_CRITICAL) {
+ return false;
+ }
+ if (attribute->systemFlags & (DS_FLAG_ATTR_NOT_REPLICATED |
+ DS_FLAG_ATTR_REQ_PARTIAL_SET_MEMBER |
+ DS_FLAG_ATTR_IS_CONSTRUCTED)) {
+ return false;
+ }
+
+ for (i=0; i < size; i++) {
+ if (strcmp(attribute->lDAPDisplayName, never_in_filtered_attrs[i]) == 0) {
+ return false;
+ }
+ }
+
+ if (attribute->searchFlags & SEARCH_FLAG_RODC_ATTRIBUTE) {
+ return false;
+ }
+ return true;
+}
diff --git a/source4/dsdb/schema/schema_inferiors.c b/source4/dsdb/schema/schema_inferiors.c
new file mode 100644
index 0000000..56f5733
--- /dev/null
+++ b/source4/dsdb/schema/schema_inferiors.c
@@ -0,0 +1,347 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ implement possibleInferiors calculation
+
+ Copyright (C) Andrew Tridgell 2009
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009
+
+ 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
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+/*
+ This module is a C implementation of the logic in the
+ dsdb/samdb/ldb_modules/tests/possibleInferiors.py code
+
+ To understand the C code, please see the python code first
+ */
+
+#include "includes.h"
+#include "dsdb/samdb/samdb.h"
+
+
+/*
+ create the SUPCLASSES() list
+ */
+static const char **schema_supclasses(const struct dsdb_schema *schema,
+ struct dsdb_class *schema_class)
+{
+ const char **list;
+
+ if (schema_class->tmp.supclasses) {
+ return schema_class->tmp.supclasses;
+ }
+
+ list = const_str_list(str_list_make_empty(schema_class));
+ if (list == NULL) {
+ DEBUG(0,(__location__ " out of memory\n"));
+ return NULL;
+ }
+
+ /* Cope with 'top SUP top', i.e. top is subClassOf top */
+ if (schema_class->subClassOf &&
+ strcmp(schema_class->lDAPDisplayName, schema_class->subClassOf) == 0) {
+ schema_class->tmp.supclasses = list;
+ return list;
+ }
+
+ if (schema_class->subClassOf) {
+ const struct dsdb_class *schema_class2 = dsdb_class_by_lDAPDisplayName(schema, schema_class->subClassOf);
+ const char **list2;
+ list = str_list_add_const(list, schema_class->subClassOf);
+
+ list2 = schema_supclasses(schema, discard_const_p(struct dsdb_class, schema_class2));
+ list = str_list_append_const(list, list2);
+ }
+
+ schema_class->tmp.supclasses = str_list_unique(list);
+
+ return schema_class->tmp.supclasses;
+}
+
+/*
+ this one is used internally
+ matches SUBCLASSES() python function
+ */
+static const char **schema_subclasses(const struct dsdb_schema *schema,
+ TALLOC_CTX *mem_ctx,
+ const char **oclist)
+{
+ const char **list = const_str_list(str_list_make_empty(mem_ctx));
+ unsigned int i;
+
+ for (i=0; oclist && oclist[i]; i++) {
+ const struct dsdb_class *schema_class = dsdb_class_by_lDAPDisplayName(schema, oclist[i]);
+ if (!schema_class) {
+ DEBUG(0, ("ERROR: Unable to locate subClass: '%s'\n", oclist[i]));
+ continue;
+ }
+ list = str_list_append_const(list, schema_class->tmp.subclasses);
+ }
+ return list;
+}
+
+
+/*
+ equivalent of the POSSSUPERIORS() python function
+ */
+static const char **schema_posssuperiors(const struct dsdb_schema *schema,
+ struct dsdb_class *schema_class)
+{
+ if (schema_class->tmp.posssuperiors == NULL) {
+ const char **list2 = const_str_list(str_list_make_empty(schema_class));
+ const char **list3;
+ unsigned int i;
+
+ list2 = str_list_append_const(list2, schema_class->systemPossSuperiors);
+ list2 = str_list_append_const(list2, schema_class->possSuperiors);
+ list3 = schema_supclasses(schema, schema_class);
+ for (i=0; list3 && list3[i]; i++) {
+ const struct dsdb_class *class2 = dsdb_class_by_lDAPDisplayName(schema, list3[i]);
+ if (!class2) {
+ DEBUG(0, ("ERROR: Unable to locate supClass: '%s'\n", list3[i]));
+ continue;
+ }
+ list2 = str_list_append_const(list2, schema_posssuperiors(schema,
+ discard_const_p(struct dsdb_class, class2)));
+ }
+ list2 = str_list_append_const(list2, schema_subclasses(schema, list2, list2));
+
+ schema_class->tmp.posssuperiors = str_list_unique(list2);
+ }
+
+ return schema_class->tmp.posssuperiors;
+}
+
+static const char **schema_subclasses_recurse(const struct dsdb_schema *schema,
+ struct dsdb_class *schema_class)
+{
+ const char **list = str_list_copy_const(schema_class, schema_class->tmp.subclasses_direct);
+ unsigned int i;
+ for (i=0;list && list[i]; i++) {
+ const struct dsdb_class *schema_class2 = dsdb_class_by_lDAPDisplayName(schema, list[i]);
+ if (schema_class != schema_class2) {
+ list = str_list_append_const(list, schema_subclasses_recurse(schema,
+ discard_const_p(struct dsdb_class, schema_class2)));
+ }
+ }
+ return list;
+}
+
+/* Walk down the subClass tree, setting a higher index as we go down
+ * each level. top is 1, subclasses of top are 2, etc */
+static void schema_subclasses_order_recurse(const struct dsdb_schema *schema,
+ struct dsdb_class *schema_class,
+ const int order)
+{
+ const char **list = schema_class->tmp.subclasses_direct;
+ unsigned int i;
+ schema_class->subClass_order = order;
+ for (i=0;list && list[i]; i++) {
+ const struct dsdb_class *schema_class2 = dsdb_class_by_lDAPDisplayName(schema, list[i]);
+ schema_subclasses_order_recurse(schema, discard_const_p(struct dsdb_class, schema_class2), order+1);
+ }
+ return;
+}
+
+static int schema_create_subclasses(const struct dsdb_schema *schema)
+{
+ struct dsdb_class *schema_class, *top;
+
+ for (schema_class=schema->classes; schema_class; schema_class=schema_class->next) {
+ struct dsdb_class *schema_class2 = discard_const_p(struct dsdb_class,
+ dsdb_class_by_lDAPDisplayName(schema, schema_class->subClassOf));
+ if (schema_class2 == NULL) {
+ DEBUG(0,("ERROR: no subClassOf '%s' for '%s'\n",
+ schema_class->subClassOf,
+ schema_class->lDAPDisplayName));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ if (schema_class2 && schema_class != schema_class2) {
+ if (schema_class2->tmp.subclasses_direct == NULL) {
+ schema_class2->tmp.subclasses_direct = const_str_list(str_list_make_empty(schema_class2));
+ if (!schema_class2->tmp.subclasses_direct) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+ schema_class2->tmp.subclasses_direct = str_list_add_const(schema_class2->tmp.subclasses_direct,
+ schema_class->lDAPDisplayName);
+ }
+ }
+
+ for (schema_class=schema->classes; schema_class; schema_class=schema_class->next) {
+ schema_class->tmp.subclasses = str_list_unique(schema_subclasses_recurse(schema, schema_class));
+
+ /* Initialize the subClass order, to ensure we can't have uninitialized sort on the subClass hierarchy */
+ schema_class->subClass_order = 0;
+ }
+
+ top = discard_const_p(struct dsdb_class, dsdb_class_by_lDAPDisplayName(schema, "top"));
+ if (!top) {
+ DEBUG(0,("ERROR: no 'top' class in loaded schema\n"));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ schema_subclasses_order_recurse(schema, top, 1);
+ return LDB_SUCCESS;
+}
+
+static void schema_fill_possible_inferiors(const struct dsdb_schema *schema,
+ struct dsdb_class *schema_class)
+{
+ struct dsdb_class *c2;
+ const char **poss_inf = NULL;
+ const char **sys_poss_inf = NULL;
+
+ for (c2 = schema->classes; c2; c2 = c2->next) {
+ const char **superiors = schema_posssuperiors(schema, c2);
+ if (c2->objectClassCategory != 2 &&
+ c2->objectClassCategory != 3 &&
+ str_list_check(superiors, schema_class->lDAPDisplayName))
+ {
+ if (c2->systemOnly == false) {
+ if (poss_inf == NULL) {
+ poss_inf = const_str_list(str_list_make_empty(schema_class));
+ }
+ poss_inf = str_list_add_const(poss_inf,
+ c2->lDAPDisplayName);
+ }
+ if (sys_poss_inf == NULL) {
+ sys_poss_inf = const_str_list(str_list_make_empty(schema_class));
+ }
+ sys_poss_inf = str_list_add_const(sys_poss_inf,
+ c2->lDAPDisplayName);
+ }
+ }
+ schema_class->systemPossibleInferiors = str_list_unique(sys_poss_inf);
+ schema_class->possibleInferiors = str_list_unique(poss_inf);
+}
+
+/*
+ fill in a string class name from a governs_ID
+ */
+static void schema_fill_from_class_one(const struct dsdb_schema *schema,
+ const struct dsdb_class *c,
+ const char **s,
+ const uint32_t id)
+{
+ if (*s == NULL && id != 0) {
+ const struct dsdb_class *c2 =
+ dsdb_class_by_governsID_id(schema, id);
+ if (c2) {
+ *s = c2->lDAPDisplayName;
+ }
+ }
+}
+
+/*
+ fill in a list of string class names from a governs_ID list
+ */
+static void schema_fill_from_class_list(const struct dsdb_schema *schema,
+ const struct dsdb_class *c,
+ const char ***s,
+ const uint32_t *ids)
+{
+ if (*s == NULL && ids != NULL) {
+ unsigned int i;
+ for (i=0;ids[i];i++) ;
+ *s = talloc_array(c, const char *, i+1);
+ for (i=0;ids[i];i++) {
+ const struct dsdb_class *c2 =
+ dsdb_class_by_governsID_id(schema, ids[i]);
+ if (c2) {
+ (*s)[i] = c2->lDAPDisplayName;
+ } else {
+ (*s)[i] = NULL;
+ }
+ }
+ (*s)[i] = NULL;
+ }
+}
+
+/*
+ fill in a list of string attribute names from a attributeID list
+ */
+static void schema_fill_from_attribute_list(const struct dsdb_schema *schema,
+ const struct dsdb_class *c,
+ const char ***s,
+ const uint32_t *ids)
+{
+ if (*s == NULL && ids != NULL) {
+ unsigned int i;
+ for (i=0;ids[i];i++) ;
+ *s = talloc_array(c, const char *, i+1);
+ for (i=0;ids[i];i++) {
+ const struct dsdb_attribute *a =
+ dsdb_attribute_by_attributeID_id(schema, ids[i]);
+ if (a) {
+ (*s)[i] = a->lDAPDisplayName;
+ } else {
+ (*s)[i] = NULL;
+ }
+ }
+ (*s)[i] = NULL;
+ }
+}
+
+/*
+ if the schema came from DRS then some attributes will be setup as IDs
+ */
+static void schema_fill_from_ids(const struct dsdb_schema *schema)
+{
+ struct dsdb_class *c;
+ for (c=schema->classes; c; c=c->next) {
+ schema_fill_from_class_one(schema, c, &c->subClassOf, c->subClassOf_id);
+ schema_fill_from_attribute_list(schema, c, &c->systemMayContain, c->systemMayContain_ids);
+ schema_fill_from_attribute_list(schema, c, &c->systemMustContain, c->systemMustContain_ids);
+ schema_fill_from_attribute_list(schema, c, &c->mustContain, c->mustContain_ids);
+ schema_fill_from_attribute_list(schema, c, &c->mayContain, c->mayContain_ids);
+ schema_fill_from_class_list(schema, c, &c->possSuperiors, c->possSuperiors_ids);
+ schema_fill_from_class_list(schema, c, &c->systemPossSuperiors, c->systemPossSuperiors_ids);
+ schema_fill_from_class_list(schema, c, &c->systemAuxiliaryClass, c->systemAuxiliaryClass_ids);
+ schema_fill_from_class_list(schema, c, &c->auxiliaryClass, c->auxiliaryClass_ids);
+ }
+}
+
+int schema_fill_constructed(const struct dsdb_schema *schema)
+{
+ int ret;
+ struct dsdb_class *schema_class;
+
+ /* make sure we start with a clean cache */
+ for (schema_class=schema->classes; schema_class; schema_class=schema_class->next) {
+ ZERO_STRUCT(schema_class->tmp);
+ }
+
+ schema_fill_from_ids(schema);
+
+ ret = schema_create_subclasses(schema);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ for (schema_class=schema->classes; schema_class; schema_class=schema_class->next) {
+ schema_fill_possible_inferiors(schema, schema_class);
+ }
+
+ /* free up our internal cache elements */
+ for (schema_class=schema->classes; schema_class; schema_class=schema_class->next) {
+ TALLOC_FREE(schema_class->tmp.supclasses);
+ TALLOC_FREE(schema_class->tmp.subclasses_direct);
+ TALLOC_FREE(schema_class->tmp.subclasses);
+ TALLOC_FREE(schema_class->tmp.posssuperiors);
+ }
+
+ return LDB_SUCCESS;
+}
diff --git a/source4/dsdb/schema/schema_info_attr.c b/source4/dsdb/schema/schema_info_attr.c
new file mode 100644
index 0000000..447bc9f
--- /dev/null
+++ b/source4/dsdb/schema/schema_info_attr.c
@@ -0,0 +1,235 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SCHEMA::schemaInfo implementation
+
+ Copyright (C) Kamen Mazdrashki <kamenim@samba.org> 2010
+
+ 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
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "dsdb/common/util.h"
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/samdb/ldb_modules/util.h"
+#include <ldb_module.h>
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "param/param.h"
+
+
+/**
+ * Creates and initializes new dsdb_schema_info value.
+ * Initial schemaInfo values is with:
+ * revision = 0
+ * invocationId = GUID_ZERO
+ */
+WERROR dsdb_schema_info_new(TALLOC_CTX *mem_ctx, struct dsdb_schema_info **_schema_info)
+{
+ struct dsdb_schema_info *schema_info;
+
+ schema_info = talloc_zero(mem_ctx, struct dsdb_schema_info);
+ W_ERROR_HAVE_NO_MEMORY(schema_info);
+
+ *_schema_info = schema_info;
+
+ return WERR_OK;
+}
+
+/**
+ * Creates and initializes new dsdb_schema_info blob value.
+ * Initial schemaInfo values is with:
+ * revision = 0
+ * invocationId = GUID_ZERO
+ */
+WERROR dsdb_schema_info_blob_new(TALLOC_CTX *mem_ctx, DATA_BLOB *_schema_info_blob)
+{
+ DATA_BLOB blob;
+
+ blob = data_blob_talloc_zero(mem_ctx, 21);
+ W_ERROR_HAVE_NO_MEMORY(blob.data);
+
+ /* Set the schemaInfo marker to 0xFF */
+ blob.data[0] = 0xFF;
+
+ *_schema_info_blob = blob;
+
+ return WERR_OK;
+}
+
+
+/**
+ * Verify the 'blob' is a valid schemaInfo blob
+ */
+bool dsdb_schema_info_blob_is_valid(const DATA_BLOB *blob)
+{
+ if (!blob || !blob->data) {
+ return false;
+ }
+
+ /* schemaInfo blob must be 21 bytes long */
+ if (blob->length != 21) {
+ return false;
+ }
+
+ /* schemaInfo blob should start with 0xFF */
+ if (blob->data[0] != 0xFF) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Parse schemaInfo structure from a data_blob
+ * (DATA_BLOB or ldb_val).
+ * Suitable for parsing blobs that comes from
+ * DRS interface of from LDB database
+ */
+WERROR dsdb_schema_info_from_blob(const DATA_BLOB *blob,
+ TALLOC_CTX *mem_ctx, struct dsdb_schema_info **_schema_info)
+{
+ TALLOC_CTX *temp_ctx;
+ enum ndr_err_code ndr_err;
+ struct dsdb_schema_info *schema_info;
+ struct schemaInfoBlob schema_info_blob;
+
+ /* verify schemaInfo blob is valid */
+ if (!dsdb_schema_info_blob_is_valid(blob)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ temp_ctx = talloc_new(mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(temp_ctx);
+
+ ndr_err = ndr_pull_struct_blob_all(blob, temp_ctx,
+ &schema_info_blob,
+ (ndr_pull_flags_fn_t)ndr_pull_schemaInfoBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
+ talloc_free(temp_ctx);
+ return ntstatus_to_werror(nt_status);
+ }
+
+ schema_info = talloc(mem_ctx, struct dsdb_schema_info);
+ if (!schema_info) {
+ talloc_free(temp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* note that we accept revision numbers of zero now - w2k8r2
+ sends a revision of zero on initial vampire */
+ schema_info->revision = schema_info_blob.revision;
+ schema_info->invocation_id = schema_info_blob.invocation_id;
+ *_schema_info = schema_info;
+
+ talloc_free(temp_ctx);
+ return WERR_OK;
+}
+
+/**
+ * Creates a blob from schemaInfo structure
+ * Suitable for packing schemaInfo into a blob
+ * which is to be used in DRS interface of LDB database
+ */
+WERROR dsdb_blob_from_schema_info(const struct dsdb_schema_info *schema_info,
+ TALLOC_CTX *mem_ctx, DATA_BLOB *blob)
+{
+ enum ndr_err_code ndr_err;
+ struct schemaInfoBlob schema_info_blob;
+
+ schema_info_blob.marker = 0xFF;
+ schema_info_blob.revision = schema_info->revision;
+ schema_info_blob.invocation_id = schema_info->invocation_id;
+
+ ndr_err = ndr_push_struct_blob(blob, mem_ctx,
+ &schema_info_blob,
+ (ndr_push_flags_fn_t)ndr_push_schemaInfoBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
+ return ntstatus_to_werror(nt_status);
+ }
+
+ return WERR_OK;
+}
+
+/**
+ * Compares schemaInfo signatures in dsdb_schema and prefixMap.
+ * NOTE: At present function compares schemaInfo values
+ * as string without taking into account schemVersion field
+ *
+ * @return WERR_OK if schemaInfos are equal
+ * WERR_DS_DRA_SCHEMA_MISMATCH if schemaInfos are different
+ */
+WERROR dsdb_schema_info_cmp(const struct dsdb_schema *schema,
+ const struct drsuapi_DsReplicaOIDMapping_Ctr *ctr)
+{
+ TALLOC_CTX *frame = NULL;
+ DATA_BLOB blob = data_blob_null;
+ struct dsdb_schema_info *schema_info = NULL;
+ const struct drsuapi_DsReplicaOIDMapping *mapping = NULL;
+ WERROR werr;
+
+ /* we should have at least schemaInfo element */
+ if (ctr->num_mappings < 1) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ /* verify schemaInfo element is valid */
+ mapping = &ctr->mappings[ctr->num_mappings - 1];
+ if (mapping->id_prefix != 0) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ blob = data_blob_const(mapping->oid.binary_oid, mapping->oid.length);
+ if (!dsdb_schema_info_blob_is_valid(&blob)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ frame = talloc_stackframe();
+ werr = dsdb_schema_info_from_blob(&blob, frame, &schema_info);
+ if (!W_ERROR_IS_OK(werr)) {
+ TALLOC_FREE(frame);
+ return werr;
+ }
+
+ /*
+ * shouldn't really be possible is dsdb_schema_info_from_blob
+ * succeeded, this check is just to satisfy static checker
+ */
+ if (schema_info == NULL) {
+ TALLOC_FREE(frame);
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (schema->schema_info->revision > schema_info->revision) {
+ /*
+ * It's ok if our schema is newer than the remote one
+ */
+ werr = WERR_OK;
+ } else if (schema->schema_info->revision < schema_info->revision) {
+ werr = WERR_DS_DRA_SCHEMA_MISMATCH;
+ } else if (!GUID_equal(&schema->schema_info->invocation_id,
+ &schema_info->invocation_id))
+ {
+ werr = WERR_DS_DRA_SCHEMA_CONFLICT;
+ } else {
+ werr = WERR_OK;
+ }
+
+ TALLOC_FREE(frame);
+ return werr;
+}
+
+
diff --git a/source4/dsdb/schema/schema_init.c b/source4/dsdb/schema/schema_init.c
new file mode 100644
index 0000000..c8197b8
--- /dev/null
+++ b/source4/dsdb/schema/schema_init.c
@@ -0,0 +1,1038 @@
+/*
+ Unix SMB/CIFS Implementation.
+ DSDB schema header
+
+ 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
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/common/util.h"
+#include <ldb_errors.h>
+#include "../lib/util/dlinklist.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "param/param.h"
+#include <ldb_module.h>
+#include "../lib/util/asn1.h"
+
+#undef strcasecmp
+
+struct dsdb_schema *dsdb_new_schema(TALLOC_CTX *mem_ctx)
+{
+ struct dsdb_schema *schema = talloc_zero(mem_ctx, struct dsdb_schema);
+ if (!schema) {
+ return NULL;
+ }
+
+ return schema;
+}
+
+struct dsdb_schema *dsdb_schema_copy_shallow(TALLOC_CTX *mem_ctx,
+ struct ldb_context *ldb,
+ const struct dsdb_schema *schema)
+{
+ int ret;
+ struct dsdb_class *cls;
+ struct dsdb_attribute *attr;
+ struct dsdb_schema *schema_copy;
+
+ schema_copy = dsdb_new_schema(mem_ctx);
+ if (!schema_copy) {
+ return NULL;
+ }
+
+ /* copy prexiMap & schemaInfo */
+ schema_copy->prefixmap = dsdb_schema_pfm_copy_shallow(schema_copy,
+ schema->prefixmap);
+ if (!schema_copy->prefixmap) {
+ goto failed;
+ }
+
+ schema_copy->schema_info = talloc(schema_copy, struct dsdb_schema_info);
+ if (!schema_copy->schema_info) {
+ goto failed;
+ }
+ *schema_copy->schema_info = *schema->schema_info;
+
+ /* copy classes and attributes*/
+ for (cls = schema->classes; cls; cls = cls->next) {
+ struct dsdb_class *class_copy = talloc_memdup(schema_copy,
+ cls, sizeof(*cls));
+ if (!class_copy) {
+ goto failed;
+ }
+ DLIST_ADD(schema_copy->classes, class_copy);
+ }
+ schema_copy->num_classes = schema->num_classes;
+
+ for (attr = schema->attributes; attr; attr = attr->next) {
+ struct dsdb_attribute *a_copy = talloc_memdup(schema_copy,
+ attr, sizeof(*attr));
+ if (!a_copy) {
+ goto failed;
+ }
+ DLIST_ADD(schema_copy->attributes, a_copy);
+ }
+ schema_copy->num_attributes = schema->num_attributes;
+
+ /* rebuild indexes */
+ ret = dsdb_setup_sorted_accessors(ldb, schema_copy);
+ if (ret != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ /* leave reload_seq_number = 0 so it will be refresh ASAP */
+
+ return schema_copy;
+
+failed:
+ talloc_free(schema_copy);
+ return NULL;
+}
+
+
+WERROR dsdb_load_prefixmap_from_drsuapi(struct dsdb_schema *schema,
+ const struct drsuapi_DsReplicaOIDMapping_Ctr *ctr)
+{
+ WERROR werr;
+ struct dsdb_schema_info *schema_info = NULL;
+ struct dsdb_schema_prefixmap *pfm = NULL;
+
+ werr = dsdb_schema_pfm_from_drsuapi_pfm(ctr, true, schema, &pfm, &schema_info);
+ W_ERROR_NOT_OK_RETURN(werr);
+
+ /* set loaded prefixMap */
+ talloc_free(schema->prefixmap);
+ schema->prefixmap = pfm;
+
+ talloc_free(schema->schema_info);
+ schema->schema_info = schema_info;
+
+ return WERR_OK;
+}
+
+static WERROR _dsdb_prefixmap_from_ldb_val(const struct ldb_val *pfm_ldb_val,
+ TALLOC_CTX *mem_ctx,
+ struct dsdb_schema_prefixmap **_pfm)
+{
+ WERROR werr;
+ enum ndr_err_code ndr_err;
+ struct prefixMapBlob pfm_blob;
+
+ TALLOC_CTX *temp_ctx = talloc_new(mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(temp_ctx);
+
+ ndr_err = ndr_pull_struct_blob(pfm_ldb_val, temp_ctx,
+ &pfm_blob,
+ (ndr_pull_flags_fn_t)ndr_pull_prefixMapBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(0,("_dsdb_prefixmap_from_ldb_val: Failed to parse prefixmap of length %u: %s\n",
+ (unsigned int)pfm_ldb_val->length, ndr_map_error2string(ndr_err)));
+ talloc_free(temp_ctx);
+ return ntstatus_to_werror(nt_status);
+ }
+
+ if (pfm_blob.version != PREFIX_MAP_VERSION_DSDB) {
+ DEBUG(0,("_dsdb_prefixmap_from_ldb_val: pfm_blob->version %u incorrect\n", (unsigned int)pfm_blob.version));
+ talloc_free(temp_ctx);
+ return WERR_VERSION_PARSE_ERROR;
+ }
+
+ /* call the drsuapi version */
+ werr = dsdb_schema_pfm_from_drsuapi_pfm(&pfm_blob.ctr.dsdb, false, mem_ctx, _pfm, NULL);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0, (__location__ " dsdb_schema_pfm_from_drsuapi_pfm failed: %s\n", win_errstr(werr)));
+ talloc_free(temp_ctx);
+ return werr;
+ }
+
+ talloc_free(temp_ctx);
+
+ return werr;
+}
+
+WERROR dsdb_load_oid_mappings_ldb(struct dsdb_schema *schema,
+ const struct ldb_val *prefixMap,
+ const struct ldb_val *schemaInfo)
+{
+ WERROR werr;
+ struct dsdb_schema_info *schema_info = NULL;
+ struct dsdb_schema_prefixmap *pfm = NULL;
+ TALLOC_CTX *mem_ctx;
+
+ /* verify schemaInfo blob is valid one */
+ if (!dsdb_schema_info_blob_is_valid(schemaInfo)) {
+ DEBUG(0,(__location__": dsdb_schema_info_blob_is_valid() failed.\n"));
+ return WERR_INVALID_PARAMETER;
+ }
+
+ mem_ctx = talloc_new(schema);
+ W_ERROR_HAVE_NO_MEMORY(mem_ctx);
+
+ /* fetch prefixMap */
+ werr = _dsdb_prefixmap_from_ldb_val(prefixMap,
+ mem_ctx, &pfm);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0, (__location__ " _dsdb_prefixmap_from_ldb_val failed: %s\n", win_errstr(werr)));
+ talloc_free(mem_ctx);
+ return werr;
+ }
+
+ /* decode schema_info */
+ werr = dsdb_schema_info_from_blob(schemaInfo, mem_ctx, &schema_info);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0, (__location__ " dsdb_schema_info_from_blob failed: %s\n", win_errstr(werr)));
+ talloc_free(mem_ctx);
+ return werr;
+ }
+
+ /* store prefixMap and schema_info into cached Schema */
+ talloc_free(schema->prefixmap);
+ schema->prefixmap = talloc_steal(schema, pfm);
+
+ talloc_free(schema->schema_info);
+ schema->schema_info = talloc_steal(schema, schema_info);
+
+ /* clean up locally allocated mem */
+ talloc_free(mem_ctx);
+
+ return WERR_OK;
+}
+
+WERROR dsdb_get_oid_mappings_drsuapi(const struct dsdb_schema *schema,
+ bool include_schema_info,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaOIDMapping_Ctr **_ctr)
+{
+ return dsdb_drsuapi_pfm_from_schema_pfm(schema->prefixmap,
+ include_schema_info ? schema->schema_info : NULL,
+ mem_ctx, _ctr);
+}
+
+WERROR dsdb_get_drsuapi_prefixmap_as_blob(const struct drsuapi_DsReplicaOIDMapping_Ctr *ctr,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_val *prefixMap)
+{
+ struct prefixMapBlob pfm;
+ enum ndr_err_code ndr_err;
+ pfm.version = PREFIX_MAP_VERSION_DSDB;
+ pfm.reserved = 0;
+ pfm.ctr.dsdb = *ctr;
+
+ ndr_err = ndr_push_struct_blob(prefixMap, mem_ctx, &pfm,
+ (ndr_push_flags_fn_t)ndr_push_prefixMapBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
+ return ntstatus_to_werror(nt_status);
+ }
+ return WERR_OK;
+}
+
+WERROR dsdb_get_oid_mappings_ldb(const struct dsdb_schema *schema,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_val *prefixMap,
+ struct ldb_val *schemaInfo)
+{
+ WERROR status;
+ struct drsuapi_DsReplicaOIDMapping_Ctr *ctr;
+
+ status = dsdb_get_oid_mappings_drsuapi(schema, false, mem_ctx, &ctr);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ status = dsdb_get_drsuapi_prefixmap_as_blob(ctr, mem_ctx, prefixMap);
+ talloc_free(ctr);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ status = dsdb_blob_from_schema_info(schema->schema_info, mem_ctx, schemaInfo);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ return WERR_OK;
+}
+
+
+/*
+ * 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)
+{
+ WERROR status;
+ uint32_t attid;
+ TALLOC_CTX *mem_ctx;
+ struct dsdb_schema_prefixmap *pfm;
+ struct dsdb_schema_prefixmap *orig_pfm = NULL;
+
+ mem_ctx = talloc_new(ldb);
+ W_ERROR_HAVE_NO_MEMORY(mem_ctx);
+
+ /* Read prefixes from disk*/
+ status = dsdb_read_prefixes_from_ldb(ldb, mem_ctx, &pfm);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("dsdb_create_prefix_mapping: dsdb_read_prefixes_from_ldb: %s\n",
+ win_errstr(status)));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ /* Check if there is a prefix for the oid in the prefixes array*/
+ status = dsdb_schema_pfm_find_oid(pfm, full_oid, NULL);
+ if (W_ERROR_IS_OK(status)) {
+ /* prefix found*/
+ talloc_free(mem_ctx);
+ return status;
+ } else if (!W_ERROR_EQUAL(status, WERR_NOT_FOUND)) {
+ /* error */
+ DEBUG(0,("dsdb_create_prefix_mapping: dsdb_find_prefix_for_oid: %s\n",
+ win_errstr(status)));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ /* Create the new mapping for the prefix of full_oid */
+ status = dsdb_schema_pfm_make_attid(pfm, full_oid, &attid);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("dsdb_create_prefix_mapping: dsdb_schema_pfm_make_attid: %s\n",
+ win_errstr(status)));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ /*
+ * We temporary replcate schema->prefixmap.
+ */
+ orig_pfm = schema->prefixmap;
+ schema->prefixmap = pfm;
+
+ /* Update prefixMap in ldb*/
+ status = dsdb_write_prefixes_from_schema_to_ldb(mem_ctx, ldb, schema);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("dsdb_create_prefix_mapping: dsdb_write_prefixes_to_ldb: %s\n",
+ win_errstr(status)));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ DEBUG(2,(__location__ " Added prefixMap %s - now have %u prefixes\n",
+ full_oid, schema->prefixmap->length));
+
+ /*
+ * We restore the original prefix map.
+ *
+ * The next schema reload should get an updated prefix map!
+ */
+ schema->prefixmap = orig_pfm;
+
+ talloc_free(mem_ctx);
+ return status;
+}
+
+
+WERROR dsdb_write_prefixes_from_schema_to_ldb(TALLOC_CTX *mem_ctx, struct ldb_context *ldb,
+ const struct dsdb_schema *schema)
+{
+ WERROR status;
+ int ldb_ret;
+ struct ldb_message *msg;
+ struct ldb_dn *schema_dn;
+ struct prefixMapBlob pfm_blob;
+ struct ldb_val ndr_blob;
+ enum ndr_err_code ndr_err;
+ TALLOC_CTX *temp_ctx;
+ struct drsuapi_DsReplicaOIDMapping_Ctr *ctr;
+
+ schema_dn = ldb_get_schema_basedn(ldb);
+ if (!schema_dn) {
+ DEBUG(0,("dsdb_write_prefixes_from_schema_to_ldb: no schema dn present\n"));
+ return WERR_FOOBAR;
+ }
+
+ temp_ctx = talloc_new(mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(temp_ctx);
+
+ /* convert schema_prefixMap to prefixMap blob */
+ status = dsdb_get_oid_mappings_drsuapi(schema, false, temp_ctx, &ctr);
+ if (!W_ERROR_IS_OK(status)) {
+ talloc_free(temp_ctx);
+ return status;
+ }
+
+ pfm_blob.version = PREFIX_MAP_VERSION_DSDB;
+ pfm_blob.ctr.dsdb = *ctr;
+
+ ndr_err = ndr_push_struct_blob(&ndr_blob, temp_ctx,
+ &pfm_blob,
+ (ndr_push_flags_fn_t)ndr_push_prefixMapBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(temp_ctx);
+ return WERR_FOOBAR;
+ }
+
+ /* write serialized prefixMap into LDB */
+ msg = ldb_msg_new(temp_ctx);
+ if (!msg) {
+ talloc_free(temp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ msg->dn = schema_dn;
+ ldb_ret = ldb_msg_add_value(msg, "prefixMap", &ndr_blob, NULL);
+ if (ldb_ret != 0) {
+ talloc_free(temp_ctx);
+ DEBUG(0,("dsdb_write_prefixes_from_schema_to_ldb: ldb_msg_add_value failed\n"));
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ldb_ret = dsdb_replace(ldb, msg, DSDB_FLAG_AS_SYSTEM);
+
+ talloc_free(temp_ctx);
+
+ if (ldb_ret != 0) {
+ DEBUG(0,("dsdb_write_prefixes_from_schema_to_ldb: dsdb_replace failed\n"));
+ return WERR_FOOBAR;
+ }
+
+ return WERR_OK;
+}
+
+WERROR dsdb_read_prefixes_from_ldb(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, struct dsdb_schema_prefixmap **_pfm)
+{
+ WERROR werr;
+ int ldb_ret;
+ const struct ldb_val *prefix_val;
+ struct ldb_dn *schema_dn;
+ struct ldb_result *schema_res = NULL;
+ static const char *schema_attrs[] = {
+ "prefixMap",
+ NULL
+ };
+
+ schema_dn = ldb_get_schema_basedn(ldb);
+ if (!schema_dn) {
+ DEBUG(0,("dsdb_read_prefixes_from_ldb: no schema dn present\n"));
+ return WERR_FOOBAR;
+ }
+
+ ldb_ret = ldb_search(ldb, mem_ctx, &schema_res, schema_dn, LDB_SCOPE_BASE, schema_attrs, NULL);
+ if (ldb_ret == LDB_ERR_NO_SUCH_OBJECT) {
+ DEBUG(0,("dsdb_read_prefixes_from_ldb: no prefix map present\n"));
+ talloc_free(schema_res);
+ return WERR_FOOBAR;
+ } else if (ldb_ret != LDB_SUCCESS) {
+ DEBUG(0,("dsdb_read_prefixes_from_ldb: failed to search the schema head\n"));
+ talloc_free(schema_res);
+ return WERR_FOOBAR;
+ }
+
+ prefix_val = ldb_msg_find_ldb_val(schema_res->msgs[0], "prefixMap");
+ if (!prefix_val) {
+ DEBUG(0,("dsdb_read_prefixes_from_ldb: no prefixMap attribute found\n"));
+ talloc_free(schema_res);
+ return WERR_FOOBAR;
+ }
+
+ werr = _dsdb_prefixmap_from_ldb_val(prefix_val,
+ mem_ctx,
+ _pfm);
+ talloc_free(schema_res);
+ W_ERROR_NOT_OK_RETURN(werr);
+
+ return WERR_OK;
+}
+
+/*
+ this will be replaced with something that looks at the right part of
+ the schema once we know where unique indexing information is hidden
+ */
+static bool dsdb_schema_unique_attribute(const char *attr)
+{
+ const char *attrs[] = { "objectGUID", NULL };
+ unsigned int i;
+ for (i=0;attrs[i];i++) {
+ if (ldb_attr_cmp(attr, attrs[i]) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/*
+ setup the ldb_schema_attribute field for a dsdb_attribute
+ */
+static int dsdb_schema_setup_ldb_schema_attribute(struct ldb_context *ldb,
+ struct dsdb_attribute *attr)
+{
+ const char *syntax = attr->syntax->ldb_syntax;
+ const struct ldb_schema_syntax *s;
+ struct ldb_schema_attribute *a;
+
+ if (!syntax) {
+ syntax = attr->syntax->ldap_oid;
+ }
+
+ s = ldb_samba_syntax_by_lDAPDisplayName(ldb, attr->lDAPDisplayName);
+ if (s == NULL) {
+ s = ldb_samba_syntax_by_name(ldb, syntax);
+ }
+ if (s == NULL) {
+ s = ldb_standard_syntax_by_name(ldb, syntax);
+ }
+
+ if (s == NULL) {
+ return ldb_operr(ldb);
+ }
+
+ attr->ldb_schema_attribute = a = talloc(attr, struct ldb_schema_attribute);
+ if (attr->ldb_schema_attribute == NULL) {
+ return ldb_oom(ldb);
+ }
+
+ a->name = attr->lDAPDisplayName;
+ a->flags = 0;
+ a->syntax = s;
+
+ if (dsdb_schema_unique_attribute(a->name)) {
+ a->flags |= LDB_ATTR_FLAG_UNIQUE_INDEX;
+ }
+ if (attr->isSingleValued) {
+ a->flags |= LDB_ATTR_FLAG_SINGLE_VALUE;
+ }
+
+ /*
+ * Is the attribute indexed? By treating confidential attributes as
+ * unindexed, we force searches to go through the unindexed search path,
+ * avoiding observable timing differences.
+ */
+ if (attr->searchFlags & SEARCH_FLAG_ATTINDEX &&
+ !(attr->searchFlags & SEARCH_FLAG_CONFIDENTIAL))
+ {
+ a->flags |= LDB_ATTR_FLAG_INDEXED;
+ }
+
+
+ return LDB_SUCCESS;
+}
+
+
+#define GET_STRING_LDB(msg, attr, mem_ctx, p, elem, strict) do { \
+ const struct ldb_val *get_string_val = ldb_msg_find_ldb_val(msg, attr); \
+ if (get_string_val == NULL) { \
+ if (strict) { \
+ d_printf("%s: %s == NULL in %s\n", __location__, attr, ldb_dn_get_linearized(msg->dn)); \
+ return WERR_INVALID_PARAMETER; \
+ } else { \
+ (p)->elem = NULL; \
+ } \
+ } else { \
+ (p)->elem = talloc_strndup(mem_ctx, \
+ (const char *)get_string_val->data, \
+ get_string_val->length); \
+ if (!(p)->elem) { \
+ d_printf("%s: talloc_strndup failed for %s\n", __location__, attr); \
+ return WERR_NOT_ENOUGH_MEMORY; \
+ } \
+ } \
+} while (0)
+
+#define GET_STRING_LIST_LDB(msg, attr, mem_ctx, p, elem) do { \
+ int get_string_list_counter; \
+ struct ldb_message_element *get_string_list_el = ldb_msg_find_element(msg, attr); \
+ /* We may get empty attributes over the replication channel */ \
+ if (get_string_list_el == NULL || get_string_list_el->num_values == 0) { \
+ (p)->elem = NULL; \
+ break; \
+ } \
+ (p)->elem = talloc_array(mem_ctx, const char *, get_string_list_el->num_values + 1); \
+ for (get_string_list_counter=0; \
+ get_string_list_counter < get_string_list_el->num_values; \
+ get_string_list_counter++) { \
+ (p)->elem[get_string_list_counter] = talloc_strndup((p)->elem, \
+ (const char *)get_string_list_el->values[get_string_list_counter].data, \
+ get_string_list_el->values[get_string_list_counter].length); \
+ if (!(p)->elem[get_string_list_counter]) { \
+ d_printf("%s: talloc_strndup failed for %s\n", __location__, attr); \
+ return WERR_NOT_ENOUGH_MEMORY; \
+ } \
+ (p)->elem[get_string_list_counter+1] = NULL; \
+ } \
+ talloc_steal(mem_ctx, (p)->elem); \
+} while (0)
+
+#define GET_BOOL_LDB(msg, attr, p, elem, strict) do { \
+ const char *str; \
+ str = ldb_msg_find_attr_as_string(msg, attr, NULL);\
+ if (str == NULL) { \
+ if (strict) { \
+ d_printf("%s: %s == NULL\n", __location__, attr); \
+ return WERR_INVALID_PARAMETER; \
+ } else { \
+ (p)->elem = false; \
+ } \
+ } else if (strcasecmp("TRUE", str) == 0) { \
+ (p)->elem = true; \
+ } else if (strcasecmp("FALSE", str) == 0) { \
+ (p)->elem = false; \
+ } else { \
+ d_printf("%s: %s == %s\n", __location__, attr, str); \
+ return WERR_INVALID_PARAMETER; \
+ } \
+} while (0)
+
+#define GET_UINT32_LDB(msg, attr, p, elem) do { \
+ (p)->elem = ldb_msg_find_attr_as_uint(msg, attr, 0);\
+} while (0)
+
+#define GET_UINT32_PTR_LDB(msg, attr, mem_ctx, p, elem) do { \
+ uint64_t _v = ldb_msg_find_attr_as_uint64(msg, attr, UINT64_MAX);\
+ if (_v == UINT64_MAX) { \
+ (p)->elem = NULL; \
+ } else if (_v > UINT32_MAX) { \
+ d_printf("%s: %s == 0x%llX\n", __location__, \
+ attr, (unsigned long long)_v); \
+ return WERR_INVALID_PARAMETER; \
+ } else { \
+ (p)->elem = talloc(mem_ctx, uint32_t); \
+ if (!(p)->elem) { \
+ d_printf("%s: talloc failed for %s\n", __location__, attr); \
+ return WERR_NOT_ENOUGH_MEMORY; \
+ } \
+ *(p)->elem = (uint32_t)_v; \
+ } \
+} while (0)
+
+#define GET_GUID_LDB(msg, attr, p, elem) do { \
+ (p)->elem = samdb_result_guid(msg, attr);\
+} while (0)
+
+#define GET_BLOB_LDB(msg, attr, mem_ctx, p, elem) do { \
+ const struct ldb_val *_val;\
+ _val = ldb_msg_find_ldb_val(msg, attr);\
+ if (_val) {\
+ (p)->elem = *_val;\
+ talloc_steal(mem_ctx, (p)->elem.data);\
+ } else {\
+ ZERO_STRUCT((p)->elem);\
+ }\
+} while (0)
+
+/** Create an dsdb_attribute out of ldb message, attr must be already talloced
+ *
+ * If supplied the attribute will be checked against the prefixmap to
+ * ensure it can be mapped. However we can't have this attribute
+ * const as dsdb_schema_pfm_attid_from_oid calls
+ * dsdb_schema_pfm_make_attid_impl() which may modify prefixmap in
+ * other situations.
+ */
+
+WERROR dsdb_attribute_from_ldb(struct dsdb_schema_prefixmap *prefixmap,
+ struct ldb_message *msg,
+ struct dsdb_attribute *attr)
+{
+ WERROR status;
+ if (attr == NULL) {
+ DEBUG(0, ("%s: attr is null, it's expected not to be so\n", __location__));
+ return WERR_INVALID_PARAMETER;
+ }
+
+ GET_STRING_LDB(msg, "cn", attr, attr, cn, false);
+
+ /*
+ * This allows for the fact that the CN attribute is not
+ * replicated over DRS, it is only replicated under the alias
+ * 'name'.
+ */
+ if (attr->cn == NULL) {
+ GET_STRING_LDB(msg, "name", attr, attr, cn, true);
+ }
+
+ GET_STRING_LDB(msg, "lDAPDisplayName", attr, attr, lDAPDisplayName, true);
+ GET_STRING_LDB(msg, "attributeID", attr, attr, attributeID_oid, true);
+ if (!prefixmap || prefixmap->length == 0) {
+ /* set an invalid value */
+ attr->attributeID_id = DRSUAPI_ATTID_INVALID;
+ } else {
+ status = dsdb_schema_pfm_attid_from_oid(prefixmap,
+ attr->attributeID_oid,
+ &attr->attributeID_id);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("%s: '%s': unable to map attributeID %s: %s\n",
+ __location__, attr->lDAPDisplayName, attr->attributeID_oid,
+ win_errstr(status)));
+ return status;
+ }
+ }
+ /* fetch msDS-IntId to be used in resolving ATTRTYP values */
+ GET_UINT32_LDB(msg, "msDS-IntId", attr, msDS_IntId);
+
+ GET_GUID_LDB(msg, "schemaIDGUID", attr, schemaIDGUID);
+ GET_UINT32_LDB(msg, "mAPIID", attr, mAPIID);
+
+ GET_GUID_LDB(msg, "attributeSecurityGUID", attr, attributeSecurityGUID);
+
+ GET_GUID_LDB(msg, "objectGUID", attr, objectGUID);
+
+ GET_UINT32_LDB(msg, "searchFlags", attr, searchFlags);
+ GET_UINT32_LDB(msg, "systemFlags", attr, systemFlags);
+ GET_BOOL_LDB(msg, "isMemberOfPartialAttributeSet", attr, isMemberOfPartialAttributeSet, false);
+ GET_UINT32_LDB(msg, "linkID", attr, linkID);
+
+ GET_STRING_LDB(msg, "attributeSyntax", attr, attr, attributeSyntax_oid, true);
+ if (!prefixmap || prefixmap->length == 0) {
+ /* set an invalid value */
+ attr->attributeSyntax_id = DRSUAPI_ATTID_INVALID;
+ } else {
+ status = dsdb_schema_pfm_attid_from_oid(prefixmap,
+ attr->attributeSyntax_oid,
+ &attr->attributeSyntax_id);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("%s: '%s': unable to map attributeSyntax_ %s: %s\n",
+ __location__, attr->lDAPDisplayName, attr->attributeSyntax_oid,
+ win_errstr(status)));
+ return status;
+ }
+ }
+ GET_UINT32_LDB(msg, "oMSyntax", attr, oMSyntax);
+ GET_BLOB_LDB(msg, "oMObjectClass", attr, attr, oMObjectClass);
+
+ GET_BOOL_LDB(msg, "isSingleValued", attr, isSingleValued, true);
+ GET_UINT32_PTR_LDB(msg, "rangeLower", attr, attr, rangeLower);
+ GET_UINT32_PTR_LDB(msg, "rangeUpper", attr, attr, rangeUpper);
+ GET_BOOL_LDB(msg, "extendedCharsAllowed", attr, extendedCharsAllowed, false);
+
+ GET_UINT32_LDB(msg, "schemaFlagsEx", attr, schemaFlagsEx);
+ GET_BLOB_LDB(msg, "msDs-Schema-Extensions", attr, attr, msDs_Schema_Extensions);
+
+ GET_BOOL_LDB(msg, "showInAdvancedViewOnly", attr, showInAdvancedViewOnly, false);
+ GET_STRING_LDB(msg, "adminDisplayName", attr, attr, adminDisplayName, false);
+ GET_STRING_LDB(msg, "adminDescription", attr, attr, adminDescription, false);
+ GET_STRING_LDB(msg, "classDisplayName", attr, attr, classDisplayName, false);
+ GET_BOOL_LDB(msg, "isEphemeral", attr, isEphemeral, false);
+ GET_BOOL_LDB(msg, "isDefunct", attr, isDefunct, false);
+ GET_BOOL_LDB(msg, "systemOnly", attr, systemOnly, false);
+
+ return WERR_OK;
+}
+
+WERROR dsdb_set_attribute_from_ldb_dups(struct ldb_context *ldb,
+ struct dsdb_schema *schema,
+ struct ldb_message *msg,
+ bool checkdups)
+{
+ WERROR status;
+ struct dsdb_attribute *attr = talloc_zero(schema, struct dsdb_attribute);
+ if (!attr) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ status = dsdb_attribute_from_ldb(schema->prefixmap, msg, attr);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ attr->syntax = dsdb_syntax_for_attribute(attr);
+ if (!attr->syntax) {
+ DEBUG(0,(__location__ ": Unknown schema syntax for %s\n",
+ attr->lDAPDisplayName));
+ return WERR_DS_ATT_SCHEMA_REQ_SYNTAX;
+ }
+
+ if (dsdb_schema_setup_ldb_schema_attribute(ldb, attr) != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Unknown schema syntax for %s - ldb_syntax: %s, ldap_oid: %s\n",
+ attr->lDAPDisplayName,
+ attr->syntax->ldb_syntax,
+ attr->syntax->ldap_oid));
+ return WERR_DS_ATT_SCHEMA_REQ_SYNTAX;
+ }
+
+ if (checkdups) {
+ const struct dsdb_attribute *a2;
+ struct dsdb_attribute **a;
+ uint32_t i;
+
+ a2 = dsdb_attribute_by_attributeID_id(schema,
+ attr->attributeID_id);
+ if (a2 == NULL) {
+ goto done;
+ }
+
+ i = schema->attributes_to_remove_size;
+ a = talloc_realloc(schema, schema->attributes_to_remove,
+ struct dsdb_attribute *, i + 1);
+ if (a == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ /* Mark the old attribute as to be removed */
+ a[i] = discard_const_p(struct dsdb_attribute, a2);
+ schema->attributes_to_remove = a;
+ schema->attributes_to_remove_size++;
+ }
+
+done:
+ DLIST_ADD(schema->attributes, attr);
+ return WERR_OK;
+}
+
+WERROR dsdb_set_attribute_from_ldb(struct ldb_context *ldb,
+ struct dsdb_schema *schema,
+ struct ldb_message *msg)
+{
+ return dsdb_set_attribute_from_ldb_dups(ldb, schema, msg, false);
+}
+
+WERROR dsdb_set_class_from_ldb_dups(struct dsdb_schema *schema,
+ struct ldb_message *msg,
+ bool checkdups)
+{
+ WERROR status;
+ struct dsdb_class *obj = talloc_zero(schema, struct dsdb_class);
+ if (!obj) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ GET_STRING_LDB(msg, "cn", obj, obj, cn, false);
+
+ /*
+ * This allows for the fact that the CN attribute is not
+ * replicated over DRS, it is only replicated under the alias
+ * 'name'.
+ */
+ if (obj->cn == NULL) {
+ GET_STRING_LDB(msg, "name", obj, obj, cn, true);
+ }
+
+ GET_STRING_LDB(msg, "lDAPDisplayName", obj, obj, lDAPDisplayName, true);
+ GET_STRING_LDB(msg, "governsID", obj, obj, governsID_oid, true);
+ if (!schema->prefixmap || schema->prefixmap->length == 0) {
+ /* set an invalid value */
+ obj->governsID_id = DRSUAPI_ATTID_INVALID;
+ } else {
+ status = dsdb_schema_pfm_attid_from_oid(schema->prefixmap,
+ obj->governsID_oid,
+ &obj->governsID_id);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("%s: '%s': unable to map governsID %s: %s\n",
+ __location__, obj->lDAPDisplayName, obj->governsID_oid,
+ win_errstr(status)));
+ return status;
+ }
+ }
+ GET_GUID_LDB(msg, "schemaIDGUID", obj, schemaIDGUID);
+ GET_GUID_LDB(msg, "objectGUID", obj, objectGUID);
+
+ GET_UINT32_LDB(msg, "objectClassCategory", obj, objectClassCategory);
+ GET_STRING_LDB(msg, "rDNAttID", obj, obj, rDNAttID, false);
+ GET_STRING_LDB(msg, "defaultObjectCategory", obj, obj, defaultObjectCategory, true);
+
+ GET_STRING_LDB(msg, "subClassOf", obj, obj, subClassOf, true);
+
+ GET_STRING_LIST_LDB(msg, "systemAuxiliaryClass", obj, obj, systemAuxiliaryClass);
+ GET_STRING_LIST_LDB(msg, "auxiliaryClass", obj, obj, auxiliaryClass);
+
+ GET_STRING_LIST_LDB(msg, "systemMustContain", obj, obj, systemMustContain);
+ GET_STRING_LIST_LDB(msg, "systemMayContain", obj, obj, systemMayContain);
+ GET_STRING_LIST_LDB(msg, "mustContain", obj, obj, mustContain);
+ GET_STRING_LIST_LDB(msg, "mayContain", obj, obj, mayContain);
+
+ GET_STRING_LIST_LDB(msg, "systemPossSuperiors", obj, obj, systemPossSuperiors);
+ GET_STRING_LIST_LDB(msg, "possSuperiors", obj, obj, possSuperiors);
+
+ GET_STRING_LDB(msg, "defaultSecurityDescriptor", obj, obj, defaultSecurityDescriptor, false);
+
+ GET_UINT32_LDB(msg, "schemaFlagsEx", obj, schemaFlagsEx);
+ GET_UINT32_LDB(msg, "systemFlags", obj, systemFlags);
+ GET_BLOB_LDB(msg, "msDs-Schema-Extensions", obj, obj, msDs_Schema_Extensions);
+
+ GET_BOOL_LDB(msg, "showInAdvancedViewOnly", obj, showInAdvancedViewOnly, false);
+ GET_STRING_LDB(msg, "adminDisplayName", obj, obj, adminDisplayName, false);
+ GET_STRING_LDB(msg, "adminDescription", obj, obj, adminDescription, false);
+ GET_STRING_LDB(msg, "classDisplayName", obj, obj, classDisplayName, false);
+ GET_BOOL_LDB(msg, "defaultHidingValue", obj, defaultHidingValue, false);
+ GET_BOOL_LDB(msg, "isDefunct", obj, isDefunct, false);
+ GET_BOOL_LDB(msg, "systemOnly", obj, systemOnly, false);
+
+ if (checkdups) {
+ const struct dsdb_class *c2;
+ struct dsdb_class **c;
+ uint32_t i;
+
+ c2 = dsdb_class_by_governsID_id(schema, obj->governsID_id);
+ if (c2 == NULL) {
+ goto done;
+ }
+
+ i = schema->classes_to_remove_size;
+ c = talloc_realloc(schema, schema->classes_to_remove,
+ struct dsdb_class *, i + 1);
+ if (c == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ /* Mark the old class to be removed */
+ c[i] = discard_const_p(struct dsdb_class, c2);
+ schema->classes_to_remove = c;
+ schema->classes_to_remove_size++;
+ }
+
+done:
+ DLIST_ADD(schema->classes, obj);
+ return WERR_OK;
+}
+
+WERROR dsdb_set_class_from_ldb(struct dsdb_schema *schema,
+ struct ldb_message *msg)
+{
+ return dsdb_set_class_from_ldb_dups(schema, msg, false);
+}
+
+#define dsdb_oom(error_string, mem_ctx) *error_string = talloc_asprintf(mem_ctx, "dsdb out of memory at %s:%d\n", __FILE__, __LINE__)
+
+/*
+ Fill a DSDB schema from the ldb results provided. This is called
+ directly when a schema must be created with a pre-initialised prefixMap
+*/
+
+int dsdb_load_ldb_results_into_schema(TALLOC_CTX *mem_ctx, struct ldb_context *ldb,
+ struct dsdb_schema *schema,
+ struct ldb_result *attrs_class_res,
+ char **error_string)
+{
+ unsigned int i;
+ WERROR status;
+
+ schema->ts_last_change = 0;
+ for (i=0; i < attrs_class_res->count; i++) {
+ const char *prefixMap = NULL;
+ /*
+ * attrs_class_res also includes the schema object;
+ * we only want to process classes & attributes
+ */
+ prefixMap = ldb_msg_find_attr_as_string(
+ attrs_class_res->msgs[i],
+ "prefixMap", NULL);
+ if (prefixMap != NULL) {
+ continue;
+ }
+
+ status = dsdb_schema_set_el_from_ldb_msg(ldb, schema,
+ attrs_class_res->msgs[i]);
+ if (!W_ERROR_IS_OK(status)) {
+ *error_string = talloc_asprintf(mem_ctx,
+ "dsdb_load_ldb_results_into_schema: failed to load attribute or class definition: %s:%s",
+ ldb_dn_get_linearized(attrs_class_res->msgs[i]->dn),
+ win_errstr(status));
+ DEBUG(0,(__location__ ": %s\n", *error_string));
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+/*
+ Create a DSDB schema from the ldb results provided. This is called
+ directly when the schema is provisioned from an on-disk LDIF file, or
+ from dsdb_schema_from_schema_dn in schema_fsmo
+*/
+
+int dsdb_schema_from_ldb_results(TALLOC_CTX *mem_ctx, struct ldb_context *ldb,
+ struct ldb_message *schema_msg,
+ struct ldb_result *attrs_class_res,
+ struct dsdb_schema **schema_out,
+ char **error_string)
+{
+ WERROR status;
+ const struct ldb_val *prefix_val;
+ const struct ldb_val *info_val;
+ struct ldb_val info_val_default;
+ struct dsdb_schema *schema;
+ void *lp_opaque = ldb_get_opaque(ldb, "loadparm");
+ int ret;
+
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ dsdb_oom(error_string, mem_ctx);
+ return ldb_operr(ldb);
+ }
+
+ schema = dsdb_new_schema(tmp_ctx);
+ if (!schema) {
+ dsdb_oom(error_string, mem_ctx);
+ talloc_free(tmp_ctx);
+ return ldb_operr(ldb);
+ }
+
+ if (lp_opaque) {
+ struct loadparm_context *lp_ctx = talloc_get_type_abort(lp_opaque, struct loadparm_context);
+ schema->fsmo.update_allowed = lpcfg_parm_bool(lp_ctx, NULL,
+ "dsdb", "schema update allowed",
+ false);
+ }
+
+ prefix_val = ldb_msg_find_ldb_val(schema_msg, "prefixMap");
+ if (!prefix_val) {
+ *error_string = talloc_asprintf(mem_ctx,
+ "schema_fsmo_init: no prefixMap attribute found");
+ DEBUG(0,(__location__ ": %s\n", *error_string));
+ talloc_free(tmp_ctx);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+ info_val = ldb_msg_find_ldb_val(schema_msg, "schemaInfo");
+ if (!info_val) {
+ status = dsdb_schema_info_blob_new(mem_ctx, &info_val_default);
+ if (!W_ERROR_IS_OK(status)) {
+ *error_string = talloc_asprintf(mem_ctx,
+ "schema_fsmo_init: dsdb_schema_info_blob_new() failed - %s",
+ win_errstr(status));
+ DEBUG(0,(__location__ ": %s\n", *error_string));
+ talloc_free(tmp_ctx);
+ return ldb_operr(ldb);
+ }
+ 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));
+ DEBUG(0,(__location__ ": %s\n", *error_string));
+ talloc_free(tmp_ctx);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ ret = dsdb_load_ldb_results_into_schema(mem_ctx, ldb, schema, attrs_class_res, error_string);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ schema->fsmo.master_dn = ldb_msg_find_attr_as_dn(ldb, schema, schema_msg, "fSMORoleOwner");
+ if (ldb_dn_compare(samdb_ntds_settings_dn(ldb, tmp_ctx), 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] updates allowed[%s]\n",
+ (schema->fsmo.we_are_master?"yes":"no"),
+ (schema->fsmo.update_allowed?"yes":"no")));
+
+ *schema_out = talloc_steal(mem_ctx, schema);
+ talloc_free(tmp_ctx);
+ return LDB_SUCCESS;
+}
diff --git a/source4/dsdb/schema/schema_prefixmap.c b/source4/dsdb/schema/schema_prefixmap.c
new file mode 100644
index 0000000..3a6a130
--- /dev/null
+++ b/source4/dsdb/schema/schema_prefixmap.c
@@ -0,0 +1,710 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ DRS::prefixMap implementation
+
+ Copyright (C) Kamen Mazdrashki <kamen.mazdrashki@postpath.com> 2009
+
+ 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
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "dsdb/samdb/samdb.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "../lib/util/asn1.h"
+#include "lib/util/smb_strtox.h"
+
+
+/**
+ * Determine range type for supplied ATTID
+ */
+enum dsdb_attid_type dsdb_pfm_get_attid_type(uint32_t attid)
+{
+ if (attid <= 0x7FFFFFFF) {
+ return DSDB_ATTID_TYPE_PFM;
+ }
+ else if (attid <= 0xBFFFFFFF) {
+ return DSDB_ATTID_TYPE_INTID;
+ }
+ else if (attid <= 0xFFFEFFFF) {
+ return DSDB_ATTID_TYPE_RESERVED;
+ }
+ else {
+ return DSDB_ATTID_TYPE_INTERNAL;
+ }
+}
+
+/**
+ * Allocates schema_prefixMap object in supplied memory context
+ */
+static struct dsdb_schema_prefixmap *_dsdb_schema_prefixmap_talloc(TALLOC_CTX *mem_ctx,
+ uint32_t length)
+{
+ struct dsdb_schema_prefixmap *pfm;
+
+ pfm = talloc_zero(mem_ctx, struct dsdb_schema_prefixmap);
+ if (!pfm) {
+ return NULL;
+ }
+
+ pfm->length = length;
+ pfm->prefixes = talloc_zero_array(pfm, struct dsdb_schema_prefixmap_oid,
+ pfm->length);
+ if (!pfm->prefixes) {
+ talloc_free(pfm);
+ return NULL;
+ }
+
+ return pfm;
+}
+
+/**
+ * Initial prefixMap creation according to:
+ * [MS-DRSR] section 5.12.2
+ */
+WERROR dsdb_schema_pfm_new(TALLOC_CTX *mem_ctx, struct dsdb_schema_prefixmap **_pfm)
+{
+ uint32_t i;
+ struct dsdb_schema_prefixmap *pfm;
+ const struct {
+ uint32_t id;
+ const char *oid_prefix;
+ } pfm_init_data[] = {
+ {.id=0x00000000, .oid_prefix="2.5.4"},
+ {.id=0x00000001, .oid_prefix="2.5.6"},
+ {.id=0x00000002, .oid_prefix="1.2.840.113556.1.2"},
+ {.id=0x00000003, .oid_prefix="1.2.840.113556.1.3"},
+ {.id=0x00000004, .oid_prefix="2.16.840.1.101.2.2.1"},
+ {.id=0x00000005, .oid_prefix="2.16.840.1.101.2.2.3"},
+ {.id=0x00000006, .oid_prefix="2.16.840.1.101.2.1.5"},
+ {.id=0x00000007, .oid_prefix="2.16.840.1.101.2.1.4"},
+ {.id=0x00000008, .oid_prefix="2.5.5"},
+ {.id=0x00000009, .oid_prefix="1.2.840.113556.1.4"},
+ {.id=0x0000000A, .oid_prefix="1.2.840.113556.1.5"},
+ {.id=0x00000013, .oid_prefix="0.9.2342.19200300.100"},
+ {.id=0x00000014, .oid_prefix="2.16.840.1.113730.3"},
+ {.id=0x00000015, .oid_prefix="0.9.2342.19200300.100.1"},
+ {.id=0x00000016, .oid_prefix="2.16.840.1.113730.3.1"},
+ {.id=0x00000017, .oid_prefix="1.2.840.113556.1.5.7000"},
+ {.id=0x00000018, .oid_prefix="2.5.21"},
+ {.id=0x00000019, .oid_prefix="2.5.18"},
+ {.id=0x0000001A, .oid_prefix="2.5.20"},
+ };
+
+ /* allocate mem for prefix map */
+ pfm = _dsdb_schema_prefixmap_talloc(mem_ctx, ARRAY_SIZE(pfm_init_data));
+ W_ERROR_HAVE_NO_MEMORY(pfm);
+
+ /* build prefixes */
+ for (i = 0; i < pfm->length; i++) {
+ if (!ber_write_partial_OID_String(pfm, &pfm->prefixes[i].bin_oid, pfm_init_data[i].oid_prefix)) {
+ talloc_free(pfm);
+ return WERR_INTERNAL_ERROR;
+ }
+ pfm->prefixes[i].id = pfm_init_data[i].id;
+ }
+
+ *_pfm = pfm;
+
+ return WERR_OK;
+}
+
+
+struct dsdb_schema_prefixmap *dsdb_schema_pfm_copy_shallow(TALLOC_CTX *mem_ctx,
+ const struct dsdb_schema_prefixmap *pfm)
+{
+ uint32_t i;
+ struct dsdb_schema_prefixmap *pfm_copy;
+
+ pfm_copy = _dsdb_schema_prefixmap_talloc(mem_ctx, pfm->length);
+ if (!pfm_copy) {
+ return NULL;
+ }
+ for (i = 0; i < pfm_copy->length; i++) {
+ pfm_copy->prefixes[i] = pfm->prefixes[i];
+ }
+
+ return pfm_copy;
+}
+
+/**
+ * Adds oid to prefix map.
+ * On success returns ID for newly added index
+ * or ID of existing entry that matches oid
+ * Reference: [MS-DRSR] section 5.12.2
+ *
+ * \param pfm prefixMap
+ * \param bin_oid OID prefix to be added to prefixMap
+ * \param pfm_id Location where to store prefixMap entry ID
+ */
+WERROR dsdb_schema_pfm_add_entry(struct dsdb_schema_prefixmap *pfm,
+ DATA_BLOB bin_oid,
+ const uint32_t *remote_id,
+ uint32_t *_idx)
+{
+ uint32_t i;
+ struct dsdb_schema_prefixmap_oid * pfm_entry;
+ struct dsdb_schema_prefixmap_oid * prefixes_new;
+
+ /* dup memory for bin-oid prefix to be added */
+ bin_oid = data_blob_dup_talloc(pfm, bin_oid);
+ W_ERROR_HAVE_NO_MEMORY(bin_oid.data);
+
+ /* make room for new entry */
+ prefixes_new = talloc_realloc(pfm, pfm->prefixes, struct dsdb_schema_prefixmap_oid, pfm->length + 1);
+ if (!prefixes_new) {
+ talloc_free(bin_oid.data);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ pfm->prefixes = prefixes_new;
+
+ /* make new unique ID in prefixMap */
+ pfm_entry = &pfm->prefixes[pfm->length];
+ pfm_entry->id = 0;
+ for (i = 0; i < pfm->length; i++) {
+ if (pfm_entry->id < pfm->prefixes[i].id) {
+ pfm_entry->id = pfm->prefixes[i].id;
+ }
+
+ if (remote_id == NULL) {
+ continue;
+ }
+
+ if (pfm->prefixes[i].id == *remote_id) {
+ /*
+ * We can't use the remote id.
+ * it's already in use.
+ */
+ remote_id = NULL;
+ }
+ }
+
+ /* add new bin-oid prefix */
+ if (remote_id != NULL) {
+ pfm_entry->id = *remote_id;
+ } else {
+ pfm_entry->id++;
+ }
+ pfm_entry->bin_oid = bin_oid;
+
+ if (_idx != NULL) {
+ *_idx = pfm->length;
+ }
+ pfm->length++;
+
+ return WERR_OK;
+}
+
+
+/**
+ * Make partial binary OID for supplied OID.
+ * Reference: [MS-DRSR] section 5.12.2
+ */
+static WERROR _dsdb_pfm_make_binary_oid(const char *full_oid, TALLOC_CTX *mem_ctx,
+ DATA_BLOB *_bin_oid, uint32_t *_last_subid)
+{
+ uint32_t last_subid;
+ const char *oid_subid;
+ int error = 0;
+
+ /* make last sub-identifier value */
+ oid_subid = strrchr(full_oid, '.');
+ if (!oid_subid) {
+ return WERR_INVALID_PARAMETER;
+ }
+ oid_subid++;
+ last_subid = smb_strtoul(oid_subid, NULL, 10, &error, SMB_STR_STANDARD);
+ if (error != 0) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ /* encode oid in BER format */
+ if (!ber_write_OID_String(mem_ctx, _bin_oid, full_oid)) {
+ DEBUG(0,("ber_write_OID_String() failed for %s\n", full_oid));
+ return WERR_INTERNAL_ERROR;
+ }
+
+ /* get the prefix of the OID */
+ if (last_subid < 128) {
+ _bin_oid->length -= 1;
+ } else {
+ _bin_oid->length -= 2;
+ }
+
+ /* return last_value if requested */
+ if (_last_subid) {
+ *_last_subid = last_subid;
+ }
+
+ return WERR_OK;
+}
+
+/**
+ * Lookup partial-binary-oid in prefixMap
+ */
+WERROR dsdb_schema_pfm_find_binary_oid(const struct dsdb_schema_prefixmap *pfm,
+ DATA_BLOB bin_oid,
+ uint32_t *_idx)
+{
+ uint32_t i;
+
+ for (i = 0; i < pfm->length; i++) {
+ if (pfm->prefixes[i].bin_oid.length != bin_oid.length) {
+ continue;
+ }
+
+ if (memcmp(pfm->prefixes[i].bin_oid.data, bin_oid.data, bin_oid.length) == 0) {
+ if (_idx) {
+ *_idx = i;
+ }
+ return WERR_OK;
+ }
+ }
+
+ return WERR_NOT_FOUND;
+}
+
+/**
+ * Lookup full-oid in prefixMap
+ * Note: this may be slow.
+ */
+WERROR dsdb_schema_pfm_find_oid(const struct dsdb_schema_prefixmap *pfm,
+ const char *full_oid,
+ uint32_t *_idx)
+{
+ WERROR werr;
+ DATA_BLOB bin_oid;
+
+ ZERO_STRUCT(bin_oid);
+
+ /* make partial-binary-oid to look for */
+ werr = _dsdb_pfm_make_binary_oid(full_oid, NULL, &bin_oid, NULL);
+ W_ERROR_NOT_OK_RETURN(werr);
+
+ /* lookup the partial-oid */
+ werr = dsdb_schema_pfm_find_binary_oid(pfm, bin_oid, _idx);
+
+ data_blob_free(&bin_oid);
+
+ return werr;
+}
+
+/**
+ * Make ATTID for given OID
+ * If OID is not in prefixMap, new prefix
+ * may be added depending on 'can_change_pfm' flag
+ * Reference: [MS-DRSR] section 5.12.2
+ */
+static WERROR dsdb_schema_pfm_make_attid_impl(struct dsdb_schema_prefixmap *pfm,
+ const char *oid,
+ bool can_change_pfm,
+ uint32_t *attid)
+{
+ WERROR werr;
+ uint32_t idx;
+ uint32_t lo_word, hi_word;
+ uint32_t last_subid;
+ DATA_BLOB bin_oid;
+
+ if (!pfm) {
+ return WERR_INVALID_PARAMETER;
+ }
+ if (!oid) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ werr = _dsdb_pfm_make_binary_oid(oid, pfm, &bin_oid, &last_subid);
+ W_ERROR_NOT_OK_RETURN(werr);
+
+ /* search the prefix in the prefix table, if none found, add
+ * one entry for new prefix.
+ */
+ werr = dsdb_schema_pfm_find_binary_oid(pfm, bin_oid, &idx);
+ if (W_ERROR_IS_OK(werr)) {
+ /* free memory allocated for bin_oid */
+ data_blob_free(&bin_oid);
+ } else {
+ /* return error in read-only mode */
+ if (!can_change_pfm) {
+ DEBUG(0, ("Unable to convert %s to an attid, and can_change_pfm=false!\n", oid));
+ return werr;
+ }
+
+ /* entry does not exists, add it */
+ werr = dsdb_schema_pfm_add_entry(pfm, bin_oid, NULL, &idx);
+ W_ERROR_NOT_OK_RETURN(werr);
+ }
+
+ /* compose the attid */
+ lo_word = last_subid % 16384; /* actually get lower 14 bits: lo_word & 0x3FFF */
+ if (last_subid >= 16384) {
+ /* mark it so that it is known to not be the whole lastValue
+ * This will raise 16-th bit*/
+ lo_word += 32768;
+ }
+ hi_word = pfm->prefixes[idx].id;
+
+ /* make ATTID:
+ * HIWORD is prefixMap id
+ * LOWORD is truncated binary-oid */
+ *attid = (hi_word * 65536) + lo_word;
+
+ return WERR_OK;
+}
+
+/**
+ * Make ATTID for given OID
+ * Reference: [MS-DRSR] section 5.12.2
+ *
+ * Note: This function may change prefixMap if prefix
+ * for supplied 'oid' doesn't exists yet.
+ * It is recommended to be used mostly when caller
+ * want to add new prefixes.
+ * Otherwise dsdb_schema_pfm_attid_from_oid() should be used.
+ */
+WERROR dsdb_schema_pfm_make_attid(struct dsdb_schema_prefixmap *pfm,
+ const char *oid,
+ uint32_t *attid)
+{
+ return dsdb_schema_pfm_make_attid_impl(pfm, oid, true, attid);
+}
+
+/**
+ * Make ATTID for given OID
+ * Reference: [MS-DRSR] section 5.12.2
+ */
+WERROR dsdb_schema_pfm_attid_from_oid(struct dsdb_schema_prefixmap *pfm,
+ const char *oid,
+ uint32_t *attid)
+{
+ return dsdb_schema_pfm_make_attid_impl(pfm, oid, false, attid);
+}
+
+/**
+ * Make OID for given ATTID.
+ * Reference: [MS-DRSR] section 5.12.2
+ */
+WERROR dsdb_schema_pfm_oid_from_attid(const struct dsdb_schema_prefixmap *pfm,
+ uint32_t attid,
+ TALLOC_CTX *mem_ctx, const char **_oid)
+{
+ uint32_t i;
+ uint32_t hi_word, lo_word;
+ DATA_BLOB bin_oid = {NULL, 0};
+ char *oid;
+ struct dsdb_schema_prefixmap_oid *pfm_entry;
+ WERROR werr = WERR_OK;
+
+ /* sanity check for attid requested */
+ if (dsdb_pfm_get_attid_type(attid) != DSDB_ATTID_TYPE_PFM) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ /* crack attid value */
+ hi_word = attid >> 16;
+ lo_word = attid & 0xFFFF;
+
+ /* locate corRespoNding prefixMap entry */
+ pfm_entry = NULL;
+ for (i = 0; i < pfm->length; i++) {
+ if (hi_word == pfm->prefixes[i].id) {
+ pfm_entry = &pfm->prefixes[i];
+ break;
+ }
+ }
+
+ if (!pfm_entry) {
+ DEBUG(1,("Failed to find prefixMap entry for ATTID = 0x%08X (%d)\n",
+ attid, attid));
+ return WERR_DS_NO_ATTRIBUTE_OR_VALUE;
+ }
+
+ /* copy oid prefix making enough room */
+ bin_oid.length = pfm_entry->bin_oid.length + 2;
+ bin_oid.data = talloc_array(mem_ctx, uint8_t, bin_oid.length);
+ W_ERROR_HAVE_NO_MEMORY(bin_oid.data);
+ memcpy(bin_oid.data, pfm_entry->bin_oid.data, pfm_entry->bin_oid.length);
+
+ if (lo_word < 128) {
+ bin_oid.length = bin_oid.length - 1;
+ bin_oid.data[bin_oid.length-1] = lo_word;
+ }
+ else {
+ if (lo_word >= 32768) {
+ lo_word -= 32768;
+ }
+ bin_oid.data[bin_oid.length-2] = (0x80 | ((lo_word>>7) & 0x7f));
+ bin_oid.data[bin_oid.length-1] = lo_word & 0x7f;
+ }
+
+ if (!ber_read_OID_String(mem_ctx, bin_oid, &oid)) {
+ DEBUG(0,("ber_read_OID_String() failed for %s\n",
+ hex_encode_talloc(bin_oid.data, bin_oid.data, bin_oid.length)));
+ werr = WERR_INTERNAL_ERROR;
+ }
+
+ /* free locally allocated memory */
+ talloc_free(bin_oid.data);
+
+ *_oid = oid;
+
+ return werr;
+}
+
+
+/**
+ * Verifies drsuapi mappings.
+ */
+static WERROR _dsdb_drsuapi_pfm_verify(const struct drsuapi_DsReplicaOIDMapping_Ctr *ctr,
+ bool have_schema_info)
+{
+ uint32_t i;
+ uint32_t num_mappings;
+ struct drsuapi_DsReplicaOIDMapping *mapping;
+
+ /* check input params */
+ if (!ctr) {
+ return WERR_INVALID_PARAMETER;
+ }
+ if (!ctr->mappings) {
+ return WERR_INVALID_PARAMETER;
+ }
+ num_mappings = ctr->num_mappings;
+
+ if (have_schema_info) {
+ DATA_BLOB blob;
+
+ if (ctr->num_mappings < 2) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ /* check last entry for being special */
+ mapping = &ctr->mappings[ctr->num_mappings - 1];
+ if (mapping->id_prefix != 0) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ /* verify schemaInfo blob is valid one */
+ blob = data_blob_const(mapping->oid.binary_oid, mapping->oid.length);
+ if (!dsdb_schema_info_blob_is_valid(&blob)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ /* get number of read mappings in the map */
+ num_mappings--;
+ }
+
+ /* now, verify rest of entries for being at least not null */
+ for (i = 0; i < num_mappings; i++) {
+ mapping = &ctr->mappings[i];
+ if (!mapping->oid.length) {
+ return WERR_INVALID_PARAMETER;
+ }
+ if (!mapping->oid.binary_oid) {
+ return WERR_INVALID_PARAMETER;
+ }
+ /* check it is not the special entry */
+ if (*mapping->oid.binary_oid == 0xFF) {
+ return WERR_INVALID_PARAMETER;
+ }
+ }
+
+ return WERR_OK;
+}
+
+/**
+ * Convert drsuapi_ prefix map to prefixMap internal presentation.
+ *
+ * \param ctr Pointer to drsuapi_DsReplicaOIDMapping_Ctr which represents drsuapi_ prefixMap
+ * \param have_schema_info if drsuapi_prefixMap have schem_info in it or not
+ * \param mem_ctx TALLOC_CTX to make allocations in
+ * \param _pfm Out pointer to hold newly created prefixMap
+ * \param _schema_info Out param to store schema_info to. If NULL, schema_info is not decoded
+ */
+WERROR dsdb_schema_pfm_from_drsuapi_pfm(const struct drsuapi_DsReplicaOIDMapping_Ctr *ctr,
+ bool have_schema_info,
+ TALLOC_CTX *mem_ctx,
+ struct dsdb_schema_prefixmap **_pfm,
+ struct dsdb_schema_info **_schema_info)
+{
+ WERROR werr;
+ uint32_t i;
+ DATA_BLOB blob;
+ uint32_t num_mappings;
+ struct dsdb_schema_prefixmap *pfm;
+
+ if (!_pfm) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * error out if schema_info is requested
+ * but it is not in the drsuapi_prefixMap
+ */
+ if (_schema_info && !have_schema_info) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ /* verify drsuapi_pefixMap */
+ werr =_dsdb_drsuapi_pfm_verify(ctr, have_schema_info);
+ W_ERROR_NOT_OK_RETURN(werr);
+
+ /* allocate mem for prefix map */
+ num_mappings = ctr->num_mappings;
+ if (have_schema_info) {
+ num_mappings--;
+ }
+ pfm = _dsdb_schema_prefixmap_talloc(mem_ctx, num_mappings);
+ W_ERROR_HAVE_NO_MEMORY(pfm);
+
+ /* copy entries from drsuapi_prefixMap */
+ for (i = 0; i < pfm->length; i++) {
+ blob = data_blob_talloc(pfm,
+ ctr->mappings[i].oid.binary_oid,
+ ctr->mappings[i].oid.length);
+ if (!blob.data) {
+ talloc_free(pfm);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ pfm->prefixes[i].id = ctr->mappings[i].id_prefix;
+ pfm->prefixes[i].bin_oid = blob;
+ }
+
+ /* fetch schema_info if requested */
+ if (_schema_info) {
+ /* by this time, i should have this value,
+ * but set it here for clarity */
+ i = ctr->num_mappings - 1;
+
+ blob = data_blob_const(ctr->mappings[i].oid.binary_oid,
+ ctr->mappings[i].oid.length);
+ werr = dsdb_schema_info_from_blob(&blob, mem_ctx, _schema_info);
+ if (!W_ERROR_IS_OK(werr)) {
+ talloc_free(pfm);
+ return werr;
+ }
+ }
+
+ /* schema_prefixMap created successfully */
+ *_pfm = pfm;
+
+ return WERR_OK;
+}
+
+/**
+ * Convert drsuapi_ prefix map to prefixMap internal presentation.
+ *
+ * \param pfm Schema prefixMap to be converted
+ * \param schema_info schema_info string - if NULL, we don't need it
+ * \param mem_ctx TALLOC_CTX to make allocations in
+ * \param _ctr Out pointer to drsuapi_DsReplicaOIDMapping_Ctr prefix map structure
+ */
+WERROR dsdb_drsuapi_pfm_from_schema_pfm(const struct dsdb_schema_prefixmap *pfm,
+ const struct dsdb_schema_info *schema_info,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaOIDMapping_Ctr **_ctr)
+{
+ uint32_t i;
+ DATA_BLOB blob;
+ struct drsuapi_DsReplicaOIDMapping_Ctr *ctr;
+
+ if (!_ctr) {
+ return WERR_INVALID_PARAMETER;
+ }
+ if (!pfm) {
+ return WERR_INVALID_PARAMETER;
+ }
+ if (pfm->length == 0) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ /* allocate memory for the structure */
+ ctr = talloc_zero(mem_ctx, struct drsuapi_DsReplicaOIDMapping_Ctr);
+ W_ERROR_HAVE_NO_MEMORY(ctr);
+
+ ctr->num_mappings = (schema_info ? pfm->length + 1 : pfm->length);
+ ctr->mappings = talloc_array(ctr, struct drsuapi_DsReplicaOIDMapping, ctr->num_mappings);
+ if (!ctr->mappings) {
+ talloc_free(ctr);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* copy entries from schema_prefixMap */
+ for (i = 0; i < pfm->length; i++) {
+ blob = data_blob_dup_talloc(ctr, pfm->prefixes[i].bin_oid);
+ if (!blob.data) {
+ talloc_free(ctr);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ ctr->mappings[i].id_prefix = pfm->prefixes[i].id;
+ ctr->mappings[i].oid.length = blob.length;
+ ctr->mappings[i].oid.binary_oid = blob.data;
+ }
+
+ /* make schema_info entry if needed */
+ if (schema_info) {
+ WERROR werr;
+
+ /* by this time, i should have this value,
+ * but set it here for clarity */
+ i = ctr->num_mappings - 1;
+
+ werr = dsdb_blob_from_schema_info(schema_info, ctr, &blob);
+ if (!W_ERROR_IS_OK(werr)) {
+ talloc_free(ctr);
+ return werr;
+ }
+
+ ctr->mappings[i].id_prefix = 0;
+ ctr->mappings[i].oid.length = blob.length;
+ ctr->mappings[i].oid.binary_oid = blob.data;
+ }
+
+ /* drsuapi_prefixMap constructed successfully */
+ *_ctr = ctr;
+
+ return WERR_OK;
+}
+
+/**
+ * Verifies schema prefixMap and drsuapi prefixMap are same.
+ * Note that we just need to verify pfm contains prefixes
+ * from ctr, not that those prefixes has same id_prefix.
+ */
+WERROR dsdb_schema_pfm_contains_drsuapi_pfm(const struct dsdb_schema_prefixmap *pfm,
+ const struct drsuapi_DsReplicaOIDMapping_Ctr *ctr)
+{
+ WERROR werr;
+ uint32_t i;
+ uint32_t idx;
+ DATA_BLOB bin_oid;
+
+ /* verify drsuapi_pefixMap */
+ werr = _dsdb_drsuapi_pfm_verify(ctr, true);
+ W_ERROR_NOT_OK_RETURN(werr);
+
+ /* check pfm contains every entry from ctr, except the last one */
+ for (i = 0; i < ctr->num_mappings - 1; i++) {
+ bin_oid.length = ctr->mappings[i].oid.length;
+ bin_oid.data = ctr->mappings[i].oid.binary_oid;
+
+ werr = dsdb_schema_pfm_find_binary_oid(pfm, bin_oid, &idx);
+ if (!W_ERROR_IS_OK(werr)) {
+ return WERR_DS_DRA_SCHEMA_MISMATCH;
+ }
+ }
+
+ return WERR_OK;
+}
diff --git a/source4/dsdb/schema/schema_query.c b/source4/dsdb/schema/schema_query.c
new file mode 100644
index 0000000..fc34764
--- /dev/null
+++ b/source4/dsdb/schema/schema_query.c
@@ -0,0 +1,610 @@
+/*
+ Unix SMB/CIFS Implementation.
+ DSDB schema header
+
+ 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
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "dsdb/samdb/samdb.h"
+#include <ldb_module.h>
+#include "lib/util/binsearch.h"
+#include "lib/util/tsort.h"
+#include "util/dlinklist.h"
+
+#undef strcasecmp
+#undef strncasecmp
+
+static const 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);
+
+static int uint32_cmp(uint32_t c1, uint32_t c2)
+{
+ if (c1 == c2) return 0;
+ return c1 > c2 ? 1 : -1;
+}
+
+static int strcasecmp_with_ldb_val(const struct ldb_val *target, const char *str)
+{
+ int ret = strncasecmp((const char *)target->data, str, target->length);
+ if (ret == 0) {
+ size_t len = strlen(str);
+ if (target->length > len) {
+ if (target->data[len] == 0) {
+ return 0;
+ }
+ return 1;
+ }
+ return (target->length - len);
+ }
+ return ret;
+}
+
+const struct dsdb_attribute *dsdb_attribute_by_attributeID_id(const struct dsdb_schema *schema,
+ uint32_t id)
+{
+ struct dsdb_attribute *c;
+
+ /*
+ * 0xFFFFFFFF is used as value when no mapping table is available,
+ * so don't try to match with it
+ */
+ if (id == 0xFFFFFFFF) return NULL;
+
+ /* check for msDS-IntId type attribute */
+ if (dsdb_pfm_get_attid_type(id) == DSDB_ATTID_TYPE_INTID) {
+ BINARY_ARRAY_SEARCH_P(schema->attributes_by_msDS_IntId,
+ schema->num_int_id_attr, msDS_IntId, id, uint32_cmp, c);
+ return c;
+ }
+
+ BINARY_ARRAY_SEARCH_P(schema->attributes_by_attributeID_id,
+ schema->num_attributes, attributeID_id, id, uint32_cmp, c);
+ return c;
+}
+
+const struct dsdb_attribute *dsdb_attribute_by_attributeID_oid(const struct dsdb_schema *schema,
+ const char *oid)
+{
+ struct dsdb_attribute *c;
+
+ if (!oid) return NULL;
+
+ BINARY_ARRAY_SEARCH_P(schema->attributes_by_attributeID_oid,
+ schema->num_attributes, attributeID_oid, oid, strcasecmp, c);
+ return c;
+}
+
+const struct dsdb_attribute *dsdb_attribute_by_lDAPDisplayName(const struct dsdb_schema *schema,
+ const char *name)
+{
+ struct dsdb_attribute *c;
+
+ if (!name) return NULL;
+
+ BINARY_ARRAY_SEARCH_P(schema->attributes_by_lDAPDisplayName,
+ schema->num_attributes, lDAPDisplayName, name, strcasecmp, c);
+ return c;
+}
+
+const struct dsdb_attribute *dsdb_attribute_by_lDAPDisplayName_ldb_val(const struct dsdb_schema *schema,
+ const struct ldb_val *name)
+{
+ struct dsdb_attribute *a;
+
+ if (!name) return NULL;
+
+ BINARY_ARRAY_SEARCH_P(schema->attributes_by_lDAPDisplayName,
+ schema->num_attributes, lDAPDisplayName, name, strcasecmp_with_ldb_val, a);
+ return a;
+}
+
+const struct dsdb_attribute *dsdb_attribute_by_linkID(const struct dsdb_schema *schema,
+ int linkID)
+{
+ struct dsdb_attribute *c;
+
+ BINARY_ARRAY_SEARCH_P(schema->attributes_by_linkID,
+ schema->num_attributes, linkID, linkID, uint32_cmp, c);
+ return c;
+}
+
+const struct dsdb_class *dsdb_class_by_governsID_id(const struct dsdb_schema *schema,
+ uint32_t id)
+{
+ struct dsdb_class *c;
+
+ /*
+ * 0xFFFFFFFF is used as value when no mapping table is available,
+ * so don't try to match with it
+ */
+ if (id == 0xFFFFFFFF) return NULL;
+
+ BINARY_ARRAY_SEARCH_P(schema->classes_by_governsID_id,
+ schema->num_classes, governsID_id, id, uint32_cmp, c);
+ return c;
+}
+
+const struct dsdb_class *dsdb_class_by_governsID_oid(const struct dsdb_schema *schema,
+ const char *oid)
+{
+ struct dsdb_class *c;
+ if (!oid) return NULL;
+ BINARY_ARRAY_SEARCH_P(schema->classes_by_governsID_oid,
+ schema->num_classes, governsID_oid, oid, strcasecmp, c);
+ return c;
+}
+
+const struct dsdb_class *dsdb_class_by_lDAPDisplayName(const struct dsdb_schema *schema,
+ const char *name)
+{
+ struct dsdb_class *c;
+ if (!name) return NULL;
+ BINARY_ARRAY_SEARCH_P(schema->classes_by_lDAPDisplayName,
+ schema->num_classes, lDAPDisplayName, name, strcasecmp, c);
+ return c;
+}
+
+const struct dsdb_class *dsdb_class_by_lDAPDisplayName_ldb_val(const struct dsdb_schema *schema,
+ const struct ldb_val *name)
+{
+ struct dsdb_class *c;
+ if (!name) return NULL;
+ BINARY_ARRAY_SEARCH_P(schema->classes_by_lDAPDisplayName,
+ schema->num_classes, lDAPDisplayName, name, strcasecmp_with_ldb_val, c);
+ return c;
+}
+
+const struct dsdb_class *dsdb_class_by_cn_ldb_val(const struct dsdb_schema *schema,
+ const struct ldb_val *cn)
+{
+ struct dsdb_class *c;
+ if (!cn) return NULL;
+ BINARY_ARRAY_SEARCH_P(schema->classes_by_cn,
+ schema->num_classes, cn, cn, strcasecmp_with_ldb_val, c);
+ return c;
+}
+
+const char *dsdb_lDAPDisplayName_by_id(const struct dsdb_schema *schema,
+ uint32_t id)
+{
+ const struct dsdb_attribute *a;
+ const struct dsdb_class *c;
+
+ a = dsdb_attribute_by_attributeID_id(schema, id);
+ if (a) {
+ return a->lDAPDisplayName;
+ }
+
+ c = dsdb_class_by_governsID_id(schema, id);
+ if (c) {
+ return c->lDAPDisplayName;
+ }
+
+ 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;
+ struct dsdb_attribute *cur;
+ unsigned int i = 0;
+ for (cur = schema->attributes; cur; cur = cur->next) {
+ if (cur->linkID == 0) continue;
+
+ attr_list = talloc_realloc(mem_ctx, attr_list, const char *, i+2);
+ if (!attr_list) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ attr_list[i] = cur->lDAPDisplayName;
+ i++;
+ }
+ if (attr_list != NULL && attr_list[i] != NULL) {
+ attr_list[i] = NULL;
+ }
+ *attr_list_ret = attr_list;
+ return WERR_OK;
+}
+
+const char **merge_attr_list(TALLOC_CTX *mem_ctx,
+ const char **attrs, const char * const*new_attrs)
+{
+ const char **ret_attrs;
+ unsigned int i;
+ size_t new_len, new_attr_len, orig_len = str_list_length(attrs);
+ if (new_attrs == NULL || new_attrs[0] == NULL) {
+ return attrs;
+ }
+ new_attr_len = str_list_length(new_attrs);
+
+ ret_attrs = talloc_realloc(mem_ctx,
+ attrs, const char *, orig_len + new_attr_len + 1);
+ if (ret_attrs) {
+ for (i = 0; i < new_attr_len; i++) {
+ ret_attrs[orig_len + i] = new_attrs[i];
+ }
+ new_len = orig_len + new_attr_len;
+
+ ret_attrs[new_len] = NULL;
+ }
+
+ return ret_attrs;
+}
+
+/*
+ Return a merged list of the attributes of exactly one class (not
+ considering subclasses, auxillary classes etc)
+*/
+
+const char **dsdb_attribute_list(TALLOC_CTX *mem_ctx, const struct dsdb_class *sclass, enum dsdb_attr_list_query query)
+{
+ const char **attr_list = NULL;
+ switch (query) {
+ case DSDB_SCHEMA_ALL_MAY:
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mayContain);
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMayContain);
+ break;
+
+ case DSDB_SCHEMA_ALL_MUST:
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mustContain);
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMustContain);
+ break;
+
+ case DSDB_SCHEMA_SYS_MAY:
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMayContain);
+ break;
+
+ case DSDB_SCHEMA_SYS_MUST:
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMustContain);
+ break;
+
+ case DSDB_SCHEMA_MAY:
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mayContain);
+ break;
+
+ case DSDB_SCHEMA_MUST:
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mustContain);
+ break;
+
+ case DSDB_SCHEMA_ALL:
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mayContain);
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMayContain);
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mustContain);
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMustContain);
+ break;
+ }
+ return attr_list;
+}
+
+static const char **attribute_list_from_class(TALLOC_CTX *mem_ctx,
+ const struct dsdb_schema *schema,
+ const struct dsdb_class *sclass,
+ enum dsdb_attr_list_query query)
+{
+ const char **this_class_list;
+ const char **system_recursive_list;
+ const char **recursive_list;
+ const char **attr_list;
+
+ this_class_list = dsdb_attribute_list(mem_ctx, sclass, query);
+
+ recursive_list = dsdb_full_attribute_list_internal(mem_ctx, schema,
+ sclass->systemAuxiliaryClass,
+ query);
+
+ system_recursive_list = dsdb_full_attribute_list_internal(mem_ctx, schema,
+ sclass->auxiliaryClass,
+ query);
+
+ attr_list = this_class_list;
+ attr_list = merge_attr_list(mem_ctx, attr_list, recursive_list);
+ attr_list = merge_attr_list(mem_ctx, attr_list, system_recursive_list);
+ return attr_list;
+}
+
+/* Return a full attribute list for a given class list
+
+ Via attribute_list_from_class() this calls itself when recursing on auxiliary classes
+ */
+static const 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)
+{
+ unsigned int i;
+ const char **attr_list = NULL;
+
+ for (i=0; class_list && class_list[i]; i++) {
+ const char **sclass_list
+ = attribute_list_from_class(mem_ctx, schema,
+ dsdb_class_by_lDAPDisplayName(schema, class_list[i]),
+ query);
+
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass_list);
+ }
+ return attr_list;
+}
+
+/* Return a full attribute list for a given class list (as a ldb_message_element)
+
+ Using the ldb_message_element ensures we do length-limited
+ comparisons, rather than casting the possibly-unterminated string
+
+ Via attribute_list_from_class() this calls
+ dsdb_full_attribute_list_internal() when recursing on auxiliary classes
+ */
+static const char **dsdb_full_attribute_list_internal_el(TALLOC_CTX *mem_ctx,
+ const struct dsdb_schema *schema,
+ const struct ldb_message_element *el,
+ enum dsdb_attr_list_query query)
+{
+ unsigned int i;
+ const char **attr_list = NULL;
+
+ for (i=0; i < el->num_values; i++) {
+ const char **sclass_list
+ = attribute_list_from_class(mem_ctx, schema,
+ dsdb_class_by_lDAPDisplayName_ldb_val(schema, &el->values[i]),
+ query);
+
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass_list);
+ }
+ return attr_list;
+}
+
+static int qsort_string(const char **s1, const char **s2)
+{
+ return strcasecmp(*s1, *s2);
+}
+
+/* Helper function to remove duplicates from the attribute list to be returned */
+static const char **dedup_attr_list(const char **attr_list)
+{
+ size_t new_len = str_list_length(attr_list);
+ /* Remove duplicates */
+ if (new_len > 1) {
+ size_t i;
+ TYPESAFE_QSORT(attr_list, new_len, qsort_string);
+
+ for (i=1; i < new_len; i++) {
+ const char **val1 = &attr_list[i-1];
+ const char **val2 = &attr_list[i];
+ if (ldb_attr_cmp(*val1, *val2) == 0) {
+ memmove(val1, val2, (new_len - i) * sizeof( *attr_list));
+ attr_list[new_len-1] = NULL;
+ new_len--;
+ i--;
+ }
+ }
+ }
+ return attr_list;
+}
+
+/* Return a full attribute list for a given class list (as a ldb_message_element)
+
+ Using the ldb_message_element ensures we do length-limited
+ comparisons, rather than casting the possibly-unterminated string
+
+ The result contains only unique values
+ */
+const char **dsdb_full_attribute_list(TALLOC_CTX *mem_ctx,
+ const struct dsdb_schema *schema,
+ const struct ldb_message_element *class_list,
+ enum dsdb_attr_list_query query)
+{
+ const char **attr_list = dsdb_full_attribute_list_internal_el(mem_ctx, schema, class_list, query);
+ return dedup_attr_list(attr_list);
+}
+
+/* Return the schemaIDGUID of a class */
+
+const struct GUID *class_schemaid_guid_by_lDAPDisplayName(const struct dsdb_schema *schema,
+ const char *name)
+{
+ const struct dsdb_class *object_class = dsdb_class_by_lDAPDisplayName(schema, name);
+ if (!object_class)
+ return NULL;
+
+ return &object_class->schemaIDGUID;
+}
+
+const struct GUID *attribute_schemaid_guid_by_lDAPDisplayName(const struct dsdb_schema *schema,
+ const char *name)
+{
+ const struct dsdb_attribute *attr = dsdb_attribute_by_lDAPDisplayName(schema, name);
+ if (!attr)
+ return NULL;
+
+ return &attr->schemaIDGUID;
+}
+
+/*
+ * Sort a "objectClass" attribute (LDB message element "objectclass_element")
+ * into correct order and validate that all object classes specified actually
+ * exist in the schema.
+ * The output is written in an existing LDB message element
+ * "out_objectclass_element" where the values will be allocated on "mem_ctx".
+ */
+int dsdb_sort_objectClass_attr(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct ldb_message_element *objectclass_element,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out_objectclass_element)
+{
+ unsigned int i, lowest;
+ struct class_list {
+ struct class_list *prev, *next;
+ const struct dsdb_class *objectclass;
+ } *unsorted = NULL, *sorted = NULL, *current = NULL,
+ *poss_parent = NULL, *new_parent = NULL,
+ *current_lowest = NULL, *current_lowest_struct = NULL;
+ struct ldb_message_element *el;
+ TALLOC_CTX *tmp_mem_ctx;
+
+ tmp_mem_ctx = talloc_new(mem_ctx);
+ if (tmp_mem_ctx == NULL) {
+ return ldb_oom(ldb);
+ }
+
+ /*
+ * DESIGN:
+ *
+ * We work on 4 different 'bins' (implemented here as linked lists):
+ *
+ * * sorted: the eventual list, in the order we wish to push
+ * into the database. This is the only ordered list.
+ *
+ * * parent_class: The current parent class 'bin' we are
+ * trying to find subclasses for
+ *
+ * * subclass: The subclasses we have found so far
+ *
+ * * unsorted: The remaining objectClasses
+ *
+ * The process is a matter of filtering objectClasses up from
+ * unsorted into sorted. Order is irrelevent in the later 3 'bins'.
+ *
+ * We start with 'top' (found and promoted to parent_class
+ * initially). Then we find (in unsorted) all the direct
+ * subclasses of 'top'. parent_classes is concatenated onto
+ * the end of 'sorted', and subclass becomes the list in
+ * parent_class.
+ *
+ * We then repeat, until we find no more subclasses. Any left
+ * over classes are added to the end.
+ *
+ */
+
+ /*
+ * Firstly, dump all the "objectClass" values into the unsorted bin,
+ * except for 'top', which is special
+ */
+ for (i=0; i < objectclass_element->num_values; i++) {
+ current = talloc(tmp_mem_ctx, struct class_list);
+ if (!current) {
+ talloc_free(tmp_mem_ctx);
+ return ldb_oom(ldb);
+ }
+ current->objectclass = dsdb_class_by_lDAPDisplayName_ldb_val(schema, &objectclass_element->values[i]);
+ if (!current->objectclass) {
+ ldb_asprintf_errstring(ldb, "objectclass %.*s is not a valid objectClass in schema",
+ (int)objectclass_element->values[i].length, (const char *)objectclass_element->values[i].data);
+ /* This looks weird, but windows apparently returns this for invalid objectClass values */
+ talloc_free(tmp_mem_ctx);
+ return LDB_ERR_NO_SUCH_ATTRIBUTE;
+ } else if (current->objectclass->isDefunct) {
+ ldb_asprintf_errstring(ldb, "objectclass %.*s marked as isDefunct objectClass in schema - not valid for new objects",
+ (int)objectclass_element->values[i].length, (const char *)objectclass_element->values[i].data);
+ /* This looks weird, but windows apparently returns this for invalid objectClass values */
+ talloc_free(tmp_mem_ctx);
+ return LDB_ERR_NO_SUCH_ATTRIBUTE;
+ }
+
+ /* Don't add top to list, we will do that later */
+ if (ldb_attr_cmp("top", current->objectclass->lDAPDisplayName) != 0) {
+ DLIST_ADD_END(unsorted, current);
+ }
+ }
+
+
+ /* Add top here, to prevent duplicates */
+ current = talloc(tmp_mem_ctx, struct class_list);
+ current->objectclass = dsdb_class_by_lDAPDisplayName(schema, "top");
+ DLIST_ADD_END(sorted, current);
+
+ /* For each object: find parent chain */
+ for (current = unsorted; current != NULL; current = current->next) {
+ for (poss_parent = unsorted; poss_parent; poss_parent = poss_parent->next) {
+ if (ldb_attr_cmp(poss_parent->objectclass->lDAPDisplayName, current->objectclass->subClassOf) == 0) {
+ break;
+ }
+ }
+ /* If we didn't get to the end of the list, we need to add this parent */
+ if (poss_parent || (ldb_attr_cmp("top", current->objectclass->subClassOf) == 0)) {
+ continue;
+ }
+
+ new_parent = talloc(tmp_mem_ctx, struct class_list);
+ new_parent->objectclass = dsdb_class_by_lDAPDisplayName(schema, current->objectclass->subClassOf);
+ DLIST_ADD_END(unsorted, new_parent);
+ }
+
+ /* For each object: order by hierarchy */
+ while (unsorted != NULL) {
+ lowest = UINT_MAX;
+ current_lowest = current_lowest_struct = NULL;
+ for (current = unsorted; current != NULL; current = current->next) {
+ if (current->objectclass->subClass_order <= lowest) {
+ /*
+ * According to MS-ADTS 3.1.1.1.4 structural
+ * and 88 object classes are always listed after
+ * the other class types in a subclass hierarchy
+ */
+ if (current->objectclass->objectClassCategory > 1) {
+ current_lowest = current;
+ } else {
+ current_lowest_struct = current;
+ }
+ lowest = current->objectclass->subClass_order;
+ }
+ }
+ if (current_lowest == NULL) {
+ current_lowest = current_lowest_struct;
+ }
+
+ if (current_lowest != NULL) {
+ DLIST_REMOVE(unsorted,current_lowest);
+ DLIST_ADD_END(sorted,current_lowest);
+ }
+ }
+
+ /* Now rebuild the sorted "objectClass" message element */
+ el = out_objectclass_element;
+
+ el->flags = objectclass_element->flags;
+ el->name = talloc_strdup(mem_ctx, objectclass_element->name);
+ if (el->name == NULL) {
+ talloc_free(tmp_mem_ctx);
+ return ldb_oom(ldb);
+ }
+ el->num_values = 0;
+ el->values = NULL;
+ for (current = sorted; current != NULL; current = current->next) {
+ el->values = talloc_realloc(mem_ctx, el->values,
+ struct ldb_val, el->num_values + 1);
+ if (el->values == NULL) {
+ talloc_free(tmp_mem_ctx);
+ return ldb_oom(ldb);
+ }
+ el->values[el->num_values] = data_blob_string_const(current->objectclass->lDAPDisplayName);
+
+ ++(el->num_values);
+ }
+
+ talloc_free(tmp_mem_ctx);
+ return LDB_SUCCESS;
+}
diff --git a/source4/dsdb/schema/schema_set.c b/source4/dsdb/schema/schema_set.c
new file mode 100644
index 0000000..03cf240
--- /dev/null
+++ b/source4/dsdb/schema/schema_set.c
@@ -0,0 +1,1140 @@
+/*
+ Unix SMB/CIFS implementation.
+ DSDB schema header
+
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 2006-2007
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006-2008
+ Copyright (C) Matthieu Patou <mat@matws.net> 2011
+
+ 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
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "lib/util/dlinklist.h"
+#include "dsdb/samdb/samdb.h"
+#include <ldb_module.h>
+#include "param/param.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "lib/util/tsort.h"
+
+#undef strcasecmp
+
+/* change this when we change something in our schema code that
+ * requires a re-index of the database
+ */
+#define SAMDB_INDEXING_VERSION "3"
+
+/*
+ override the name to attribute handler function
+ */
+const struct ldb_schema_attribute *dsdb_attribute_handler_override(struct ldb_context *ldb,
+ void *private_data,
+ const char *name)
+{
+ struct dsdb_schema *schema = talloc_get_type_abort(private_data, struct dsdb_schema);
+ const struct dsdb_attribute *a = dsdb_attribute_by_lDAPDisplayName(schema, name);
+ if (a == NULL) {
+ /* this will fall back to ldb internal handling */
+ return NULL;
+ }
+ return a->ldb_schema_attribute;
+}
+
+/*
+ * Set the attribute handlers onto the LDB, and potentially write the
+ * @INDEXLIST, @IDXONE and @ATTRIBUTES records. The @ATTRIBUTES records
+ * are required so we can operate on a schema-less database (say the
+ * backend during emergency fixes) and during the schema load.
+ */
+int dsdb_schema_set_indices_and_attributes(struct ldb_context *ldb,
+ struct dsdb_schema *schema,
+ enum schema_set_enum mode)
+{
+ int ret = LDB_SUCCESS;
+ struct ldb_result *res;
+ struct ldb_result *res_idx;
+ struct dsdb_attribute *attr;
+ struct ldb_message *mod_msg;
+ TALLOC_CTX *mem_ctx;
+ struct ldb_message *msg;
+ struct ldb_message *msg_idx;
+
+ struct loadparm_context *lp_ctx =
+ talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
+ struct loadparm_context);
+ bool guid_indexing = true;
+ bool declare_ordered_integer_in_attributes = true;
+ uint32_t pack_format_override;
+ if (lp_ctx != NULL) {
+ /*
+ * GUID indexing is wanted by Samba by default. This allows
+ * an override in a specific case for downgrades.
+ */
+ guid_indexing = lpcfg_parm_bool(lp_ctx,
+ NULL,
+ "dsdb",
+ "guid index",
+ true);
+ /*
+ * If the pack format has been overridden to a previous
+ * version, then act like ORDERED_INTEGER doesn't exist,
+ * because it's a new type and we don't want to deal with
+ * possible issues with databases containing version 1 pack
+ * format and ordered types.
+ *
+ * This approach means that the @ATTRIBUTES will be
+ * incorrect for integers. Many other @ATTRIBUTES
+ * values are gross simplifications, but the presence
+ * of the ORDERED_INTEGER keyword prevents the old
+ * Samba from starting and then forcing a reindex.
+ *
+ * It is too difficult to override the actual index
+ * formatter, but this doesn't matter in practice.
+ */
+ pack_format_override =
+ (intptr_t)ldb_get_opaque(ldb, "pack_format_override");
+ if (pack_format_override == LDB_PACKING_FORMAT ||
+ pack_format_override == LDB_PACKING_FORMAT_NODN) {
+ declare_ordered_integer_in_attributes = false;
+ }
+ }
+ /* setup our own attribute name to schema handler */
+ ldb_schema_attribute_set_override_handler(ldb, dsdb_attribute_handler_override, schema);
+ ldb_schema_set_override_indexlist(ldb, true);
+ if (guid_indexing) {
+ ldb_schema_set_override_GUID_index(ldb, "objectGUID", "GUID");
+ }
+
+ if (mode == SCHEMA_MEMORY_ONLY) {
+ return ret;
+ }
+
+ mem_ctx = talloc_new(ldb);
+ if (!mem_ctx) {
+ return ldb_oom(ldb);
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (!msg) {
+ ldb_oom(ldb);
+ goto op_error;
+ }
+ msg_idx = ldb_msg_new(mem_ctx);
+ if (!msg_idx) {
+ ldb_oom(ldb);
+ goto op_error;
+ }
+ msg->dn = ldb_dn_new(msg, ldb, "@ATTRIBUTES");
+ if (!msg->dn) {
+ ldb_oom(ldb);
+ goto op_error;
+ }
+ msg_idx->dn = ldb_dn_new(msg_idx, ldb, "@INDEXLIST");
+ if (!msg_idx->dn) {
+ ldb_oom(ldb);
+ goto op_error;
+ }
+
+ ret = ldb_msg_add_string(msg_idx, "@IDXONE", "1");
+ if (ret != LDB_SUCCESS) {
+ goto op_error;
+ }
+
+ if (guid_indexing) {
+ ret = ldb_msg_add_string(msg_idx, "@IDXGUID", "objectGUID");
+ if (ret != LDB_SUCCESS) {
+ goto op_error;
+ }
+
+ ret = ldb_msg_add_string(msg_idx, "@IDX_DN_GUID", "GUID");
+ if (ret != LDB_SUCCESS) {
+ goto op_error;
+ }
+ }
+
+ ret = ldb_msg_add_string(msg_idx, "@SAMDB_INDEXING_VERSION", SAMDB_INDEXING_VERSION);
+ if (ret != LDB_SUCCESS) {
+ goto op_error;
+ }
+
+ ret = ldb_msg_add_string(msg_idx, SAMBA_FEATURES_SUPPORTED_FLAG, "1");
+ if (ret != LDB_SUCCESS) {
+ goto op_error;
+ }
+
+ for (attr = schema->attributes; attr; attr = attr->next) {
+ const char *syntax = attr->syntax->ldb_syntax;
+
+ if (!syntax) {
+ syntax = attr->syntax->ldap_oid;
+ }
+
+ /*
+ * Write out a rough approximation of the schema
+ * as an @ATTRIBUTES value, for bootstrapping.
+ * Only write ORDERED_INTEGER if we're using GUID indexes,
+ */
+ if (strcmp(syntax, LDB_SYNTAX_INTEGER) == 0) {
+ ret = ldb_msg_add_string(msg, attr->lDAPDisplayName, "INTEGER");
+ } else if (strcmp(syntax, LDB_SYNTAX_ORDERED_INTEGER) == 0) {
+ if (declare_ordered_integer_in_attributes &&
+ guid_indexing) {
+ /*
+ * The normal production case
+ */
+ ret = ldb_msg_add_string(msg,
+ attr->lDAPDisplayName,
+ "ORDERED_INTEGER");
+ } else {
+ /*
+ * For this mode, we are going back to
+ * before GUID indexing so we write it out
+ * as INTEGER
+ *
+ * Down in LDB, the special handler
+ * (index_format_fn) that made
+ * ORDERED_INTEGER and INTEGER
+ * different has been disabled.
+ */
+ ret = ldb_msg_add_string(msg,
+ attr->lDAPDisplayName,
+ "INTEGER");
+ }
+ } else if (strcmp(syntax, LDB_SYNTAX_DIRECTORY_STRING) == 0) {
+ ret = ldb_msg_add_string(msg, attr->lDAPDisplayName,
+ "CASE_INSENSITIVE");
+ }
+ if (ret != LDB_SUCCESS) {
+ break;
+ }
+
+ /*
+ * Is the attribute indexed? By treating confidential attributes
+ * as unindexed, we force searches to go through the unindexed
+ * search path, avoiding observable timing differences.
+ */
+ if (attr->searchFlags & SEARCH_FLAG_ATTINDEX &&
+ !(attr->searchFlags & SEARCH_FLAG_CONFIDENTIAL))
+ {
+ /*
+ * When preparing to downgrade Samba, we need to write
+ * out an LDB without the new key word ORDERED_INTEGER.
+ */
+ if (strcmp(syntax, LDB_SYNTAX_ORDERED_INTEGER) == 0
+ && !declare_ordered_integer_in_attributes) {
+ /*
+ * Ugly, but do nothing, the best
+ * thing is to omit the reference
+ * entirely, the next transaction will
+ * spot this and rewrite everything.
+ *
+ * This way nothing will look at the
+ * index for this attribute until
+ * Samba starts and this is all
+ * rewritten.
+ */
+ } else {
+ ret = ldb_msg_add_string(msg_idx, "@IDXATTR", attr->lDAPDisplayName);
+ if (ret != LDB_SUCCESS) {
+ break;
+ }
+ }
+ }
+ }
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ /*
+ * Try to avoid churning the attributes too much,
+ * we only want to do this if they have changed
+ */
+ ret = ldb_search(ldb, mem_ctx, &res, msg->dn, LDB_SCOPE_BASE, NULL,
+ NULL);
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ if (mode == SCHEMA_COMPARE) {
+ /* We are probably not in a transaction */
+ goto wrong_mode;
+ }
+ ret = ldb_add(ldb, msg);
+ } else if (ret != LDB_SUCCESS) {
+ } else if (res->count != 1) {
+ if (mode == SCHEMA_COMPARE) {
+ /* We are probably not in a transaction */
+ goto wrong_mode;
+ }
+ ret = ldb_add(ldb, msg);
+ } else {
+ /* Annoyingly added to our search results */
+ ldb_msg_remove_attr(res->msgs[0], "distinguishedName");
+
+ ret = ldb_msg_difference(ldb, mem_ctx,
+ res->msgs[0], msg, &mod_msg);
+ if (ret != LDB_SUCCESS) {
+ goto op_error;
+ }
+ if (mod_msg->num_elements > 0) {
+ /*
+ * Do the replace with the difference, as we
+ * are under the read lock and we wish to do a
+ * delete of any removed/renamed attributes
+ */
+ if (mode == SCHEMA_COMPARE) {
+ /* We are probably not in a transaction */
+ goto wrong_mode;
+ }
+ ret = dsdb_modify(ldb, mod_msg, 0);
+ }
+ talloc_free(mod_msg);
+ }
+
+ if (ret == LDB_ERR_OPERATIONS_ERROR || ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS || ret == LDB_ERR_INVALID_DN_SYNTAX) {
+ /* We might be on a read-only DB or LDAP */
+ ret = LDB_SUCCESS;
+ }
+ if (ret != LDB_SUCCESS) {
+ DBG_ERR("Failed to set schema into @ATTRIBUTES: %s\n",
+ ldb_errstring(ldb));
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ /* Now write out the indexes, as found in the schema (if they have changed) */
+
+ ret = ldb_search(ldb, mem_ctx, &res_idx, msg_idx->dn, LDB_SCOPE_BASE,
+ NULL, NULL);
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ if (mode == SCHEMA_COMPARE) {
+ /* We are probably not in a transaction */
+ goto wrong_mode;
+ }
+ ret = ldb_add(ldb, msg_idx);
+ } else if (ret != LDB_SUCCESS) {
+ } else if (res_idx->count != 1) {
+ if (mode == SCHEMA_COMPARE) {
+ /* We are probably not in a transaction */
+ goto wrong_mode;
+ }
+ ret = ldb_add(ldb, msg_idx);
+ } else {
+ /* Annoyingly added to our search results */
+ ldb_msg_remove_attr(res_idx->msgs[0], "distinguishedName");
+
+ ret = ldb_msg_difference(ldb, mem_ctx,
+ res_idx->msgs[0], msg_idx, &mod_msg);
+ if (ret != LDB_SUCCESS) {
+ goto op_error;
+ }
+
+ /*
+ * We don't want to re-index just because we didn't
+ * see this flag
+ *
+ * DO NOT backport this logic earlier than 4.7, it
+ * isn't needed and would be dangerous before 4.6,
+ * where we add logic to samba_dsdb to manage
+ * @SAMBA_FEATURES_SUPPORTED and need to know if the
+ * DB has been re-opened by an earlier version.
+ *
+ */
+
+ if (mod_msg->num_elements == 1
+ && ldb_attr_cmp(mod_msg->elements[0].name,
+ SAMBA_FEATURES_SUPPORTED_FLAG) == 0) {
+ /*
+ * Ignore only adding
+ * @SAMBA_FEATURES_SUPPORTED
+ */
+ } else if (mod_msg->num_elements > 0) {
+
+ /*
+ * Do the replace with the difference, as we
+ * are under the read lock and we wish to do a
+ * delete of any removed/renamed attributes
+ */
+ if (mode == SCHEMA_COMPARE) {
+ /* We are probably not in a transaction */
+ goto wrong_mode;
+ }
+ ret = dsdb_modify(ldb, mod_msg, 0);
+ }
+ talloc_free(mod_msg);
+ }
+ if (ret == LDB_ERR_OPERATIONS_ERROR || ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS || ret == LDB_ERR_INVALID_DN_SYNTAX) {
+ /* We might be on a read-only DB */
+ ret = LDB_SUCCESS;
+ }
+
+ if (ret != LDB_SUCCESS) {
+ DBG_ERR("Failed to set schema into @INDEXLIST: %s\n",
+ ldb_errstring(ldb));
+ }
+
+ talloc_free(mem_ctx);
+ return ret;
+
+op_error:
+ talloc_free(mem_ctx);
+ return ldb_operr(ldb);
+
+wrong_mode:
+ talloc_free(mem_ctx);
+ return LDB_ERR_BUSY;
+}
+
+
+/*
+ create extra attribute shortcuts
+ */
+static void dsdb_setup_attribute_shortcuts(struct ldb_context *ldb, struct dsdb_schema *schema)
+{
+ struct dsdb_attribute *attribute;
+
+ /* setup fast access to one_way_link and DN format */
+ for (attribute=schema->attributes; attribute; attribute=attribute->next) {
+ attribute->dn_format = dsdb_dn_oid_to_format(attribute->syntax->ldap_oid);
+
+ if (attribute->dn_format == DSDB_INVALID_DN) {
+ attribute->one_way_link = false;
+ continue;
+ }
+
+ /* these are not considered to be one way links for
+ the purpose of DN link fixups */
+ if (ldb_attr_cmp("distinguishedName", attribute->lDAPDisplayName) == 0 ||
+ ldb_attr_cmp("objectCategory", attribute->lDAPDisplayName) == 0) {
+ attribute->one_way_link = false;
+ continue;
+ }
+
+ if (attribute->linkID == 0) {
+ attribute->one_way_link = true;
+ continue;
+ }
+ /* handle attributes with a linkID but no backlink */
+ if ((attribute->linkID & 1) == 0 &&
+ dsdb_attribute_by_linkID(schema, attribute->linkID + 1) == NULL) {
+ attribute->one_way_link = true;
+ continue;
+ }
+ attribute->one_way_link = false;
+ }
+}
+
+static int uint32_cmp(uint32_t c1, uint32_t c2)
+{
+ if (c1 == c2) return 0;
+ return c1 > c2 ? 1 : -1;
+}
+
+static int dsdb_compare_class_by_lDAPDisplayName(struct dsdb_class **c1, struct dsdb_class **c2)
+{
+ return strcasecmp((*c1)->lDAPDisplayName, (*c2)->lDAPDisplayName);
+}
+static int dsdb_compare_class_by_governsID_id(struct dsdb_class **c1, struct dsdb_class **c2)
+{
+ return uint32_cmp((*c1)->governsID_id, (*c2)->governsID_id);
+}
+static int dsdb_compare_class_by_governsID_oid(struct dsdb_class **c1, struct dsdb_class **c2)
+{
+ return strcasecmp((*c1)->governsID_oid, (*c2)->governsID_oid);
+}
+static int dsdb_compare_class_by_cn(struct dsdb_class **c1, struct dsdb_class **c2)
+{
+ return strcasecmp((*c1)->cn, (*c2)->cn);
+}
+
+static int dsdb_compare_attribute_by_lDAPDisplayName(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
+{
+ return strcasecmp((*a1)->lDAPDisplayName, (*a2)->lDAPDisplayName);
+}
+static int dsdb_compare_attribute_by_attributeID_id(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
+{
+ return uint32_cmp((*a1)->attributeID_id, (*a2)->attributeID_id);
+}
+static int dsdb_compare_attribute_by_msDS_IntId(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
+{
+ return uint32_cmp((*a1)->msDS_IntId, (*a2)->msDS_IntId);
+}
+static int dsdb_compare_attribute_by_attributeID_oid(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
+{
+ return strcasecmp((*a1)->attributeID_oid, (*a2)->attributeID_oid);
+}
+static int dsdb_compare_attribute_by_linkID(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
+{
+ return uint32_cmp((*a1)->linkID, (*a2)->linkID);
+}
+
+/**
+ * Clean up Classes and Attributes accessor arrays
+ */
+static void dsdb_sorted_accessors_free(struct dsdb_schema *schema)
+{
+ /* free classes accessors */
+ TALLOC_FREE(schema->classes_by_lDAPDisplayName);
+ TALLOC_FREE(schema->classes_by_governsID_id);
+ TALLOC_FREE(schema->classes_by_governsID_oid);
+ TALLOC_FREE(schema->classes_by_cn);
+ /* free attribute accessors */
+ TALLOC_FREE(schema->attributes_by_lDAPDisplayName);
+ TALLOC_FREE(schema->attributes_by_attributeID_id);
+ TALLOC_FREE(schema->attributes_by_msDS_IntId);
+ TALLOC_FREE(schema->attributes_by_attributeID_oid);
+ TALLOC_FREE(schema->attributes_by_linkID);
+}
+
+/*
+ create the sorted accessor arrays for the schema
+ */
+int dsdb_setup_sorted_accessors(struct ldb_context *ldb,
+ struct dsdb_schema *schema)
+{
+ struct dsdb_class *cur;
+ struct dsdb_attribute *a;
+ unsigned int i;
+ unsigned int num_int_id;
+ int ret;
+
+ for (i=0; i < schema->classes_to_remove_size; i++) {
+ DLIST_REMOVE(schema->classes, schema->classes_to_remove[i]);
+ TALLOC_FREE(schema->classes_to_remove[i]);
+ }
+ for (i=0; i < schema->attributes_to_remove_size; i++) {
+ DLIST_REMOVE(schema->attributes, schema->attributes_to_remove[i]);
+ TALLOC_FREE(schema->attributes_to_remove[i]);
+ }
+
+ TALLOC_FREE(schema->classes_to_remove);
+ schema->classes_to_remove_size = 0;
+ TALLOC_FREE(schema->attributes_to_remove);
+ schema->attributes_to_remove_size = 0;
+
+ /* free all caches */
+ dsdb_sorted_accessors_free(schema);
+
+ /* count the classes */
+ for (i=0, cur=schema->classes; cur; i++, cur=cur->next) /* noop */ ;
+ schema->num_classes = i;
+
+ /* setup classes_by_* */
+ schema->classes_by_lDAPDisplayName = talloc_array(schema, struct dsdb_class *, i);
+ schema->classes_by_governsID_id = talloc_array(schema, struct dsdb_class *, i);
+ schema->classes_by_governsID_oid = talloc_array(schema, struct dsdb_class *, i);
+ schema->classes_by_cn = talloc_array(schema, struct dsdb_class *, i);
+ if (schema->classes_by_lDAPDisplayName == NULL ||
+ schema->classes_by_governsID_id == NULL ||
+ schema->classes_by_governsID_oid == NULL ||
+ schema->classes_by_cn == NULL) {
+ goto failed;
+ }
+
+ for (i=0, cur=schema->classes; cur; i++, cur=cur->next) {
+ schema->classes_by_lDAPDisplayName[i] = cur;
+ schema->classes_by_governsID_id[i] = cur;
+ schema->classes_by_governsID_oid[i] = cur;
+ schema->classes_by_cn[i] = cur;
+ }
+
+ /* sort the arrays */
+ TYPESAFE_QSORT(schema->classes_by_lDAPDisplayName, schema->num_classes, dsdb_compare_class_by_lDAPDisplayName);
+ TYPESAFE_QSORT(schema->classes_by_governsID_id, schema->num_classes, dsdb_compare_class_by_governsID_id);
+ TYPESAFE_QSORT(schema->classes_by_governsID_oid, schema->num_classes, dsdb_compare_class_by_governsID_oid);
+ TYPESAFE_QSORT(schema->classes_by_cn, schema->num_classes, dsdb_compare_class_by_cn);
+
+ /* now build the attribute accessor arrays */
+
+ /* count the attributes
+ * and attributes with msDS-IntId set */
+ num_int_id = 0;
+ for (i=0, a=schema->attributes; a; i++, a=a->next) {
+ if (a->msDS_IntId != 0) {
+ num_int_id++;
+ }
+ }
+ schema->num_attributes = i;
+ schema->num_int_id_attr = num_int_id;
+
+ /* setup attributes_by_* */
+ schema->attributes_by_lDAPDisplayName = talloc_array(schema, struct dsdb_attribute *, i);
+ schema->attributes_by_attributeID_id = talloc_array(schema, struct dsdb_attribute *, i);
+ schema->attributes_by_msDS_IntId = talloc_array(schema,
+ struct dsdb_attribute *, num_int_id);
+ schema->attributes_by_attributeID_oid = talloc_array(schema, struct dsdb_attribute *, i);
+ schema->attributes_by_linkID = talloc_array(schema, struct dsdb_attribute *, i);
+ if (schema->attributes_by_lDAPDisplayName == NULL ||
+ schema->attributes_by_attributeID_id == NULL ||
+ schema->attributes_by_msDS_IntId == NULL ||
+ schema->attributes_by_attributeID_oid == NULL ||
+ schema->attributes_by_linkID == NULL) {
+ goto failed;
+ }
+
+ num_int_id = 0;
+ for (i=0, a=schema->attributes; a; i++, a=a->next) {
+ schema->attributes_by_lDAPDisplayName[i] = a;
+ schema->attributes_by_attributeID_id[i] = a;
+ schema->attributes_by_attributeID_oid[i] = a;
+ schema->attributes_by_linkID[i] = a;
+ /* append attr-by-msDS-IntId values */
+ if (a->msDS_IntId != 0) {
+ schema->attributes_by_msDS_IntId[num_int_id] = a;
+ num_int_id++;
+ }
+ }
+ SMB_ASSERT(num_int_id == schema->num_int_id_attr);
+
+ /* sort the arrays */
+ TYPESAFE_QSORT(schema->attributes_by_lDAPDisplayName, schema->num_attributes, dsdb_compare_attribute_by_lDAPDisplayName);
+ TYPESAFE_QSORT(schema->attributes_by_attributeID_id, schema->num_attributes, dsdb_compare_attribute_by_attributeID_id);
+ TYPESAFE_QSORT(schema->attributes_by_msDS_IntId, schema->num_int_id_attr, dsdb_compare_attribute_by_msDS_IntId);
+ TYPESAFE_QSORT(schema->attributes_by_attributeID_oid, schema->num_attributes, dsdb_compare_attribute_by_attributeID_oid);
+ TYPESAFE_QSORT(schema->attributes_by_linkID, schema->num_attributes, dsdb_compare_attribute_by_linkID);
+
+ dsdb_setup_attribute_shortcuts(ldb, schema);
+
+ ret = schema_fill_constructed(schema);
+ if (ret != LDB_SUCCESS) {
+ dsdb_sorted_accessors_free(schema);
+ return ret;
+ }
+
+ return LDB_SUCCESS;
+
+failed:
+ dsdb_sorted_accessors_free(schema);
+ return ldb_oom(ldb);
+}
+
+/**
+ * Attach the schema to an opaque pointer on the ldb,
+ * so ldb modules can find it
+ */
+int dsdb_set_schema_refresh_function(struct ldb_context *ldb,
+ dsdb_schema_refresh_fn refresh_fn,
+ struct ldb_module *module)
+{
+ int ret = ldb_set_opaque(ldb, "dsdb_schema_refresh_fn", refresh_fn);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_set_opaque(ldb, "dsdb_schema_refresh_fn_private_data", module);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ return LDB_SUCCESS;
+}
+
+/**
+ * Attach the schema to an opaque pointer on the ldb,
+ * so ldb modules can find it
+ */
+int dsdb_set_schema(struct ldb_context *ldb,
+ struct dsdb_schema *schema,
+ enum schema_set_enum write_indices_and_attributes)
+{
+ struct dsdb_schema *old_schema;
+ int ret;
+
+ ret = dsdb_setup_sorted_accessors(ldb, schema);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ old_schema = ldb_get_opaque(ldb, "dsdb_schema");
+
+ ret = ldb_set_opaque(ldb, "dsdb_use_global_schema", NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_set_opaque(ldb, "dsdb_schema", schema);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ talloc_steal(ldb, schema);
+
+ /* Set the new attributes based on the new schema */
+ ret = dsdb_schema_set_indices_and_attributes(ldb, schema,
+ write_indices_and_attributes);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /*
+ * Remove the reference to the schema we just overwrote - if there was
+ * none, NULL is harmless here.
+ */
+ if (old_schema != schema) {
+ talloc_unlink(ldb, old_schema);
+ }
+
+ return ret;
+}
+
+/**
+ * Global variable to hold one copy of the schema, used to avoid memory bloat
+ */
+static struct dsdb_schema *global_schema;
+
+/**
+ * Make this ldb use a specified schema, already fully calculated and belonging to another ldb
+ *
+ * The write_indices_and_attributes controls writing of the @ records
+ * because we cannot write to a database that does not yet exist on
+ * disk.
+ */
+int dsdb_reference_schema(struct ldb_context *ldb, struct dsdb_schema *schema,
+ enum schema_set_enum write_indices_and_attributes)
+{
+ int ret;
+ void *ptr;
+ void *schema_parent = NULL;
+ bool is_already_parent;
+ struct dsdb_schema *old_schema;
+ old_schema = ldb_get_opaque(ldb, "dsdb_schema");
+ ret = ldb_set_opaque(ldb, "dsdb_schema", schema);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* Remove the reference to the schema we just overwrote - if there was
+ * none, NULL is harmless here */
+ talloc_unlink(ldb, old_schema);
+
+ /* Reference schema on ldb if it wasn't done already */
+ schema_parent = talloc_parent(schema);
+ is_already_parent = (schema_parent == ldb);
+ if (!is_already_parent) {
+ ptr = talloc_reference(ldb, schema);
+ if (ptr == NULL) {
+ return ldb_oom(ldb);
+ }
+ }
+
+ /* Make this ldb use local schema preferably */
+ ret = ldb_set_opaque(ldb, "dsdb_use_global_schema", NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_set_opaque(ldb, "dsdb_refresh_fn", NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_set_opaque(ldb, "dsdb_refresh_fn_private_data", NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = dsdb_schema_set_indices_and_attributes(ldb, schema, write_indices_and_attributes);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return LDB_SUCCESS;
+}
+
+/**
+ * Make this ldb use the 'global' schema, setup to avoid having multiple copies in this process
+ */
+int dsdb_set_global_schema(struct ldb_context *ldb)
+{
+ int ret;
+ void *use_global_schema = (void *)1;
+ void *ptr;
+ struct dsdb_schema *old_schema = ldb_get_opaque(ldb, "dsdb_schema");
+
+ ret = ldb_set_opaque(ldb, "dsdb_use_global_schema", use_global_schema);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ if (global_schema == NULL) {
+ return LDB_SUCCESS;
+ }
+
+ /* Remove any pointer to a previous schema */
+ ret = ldb_set_opaque(ldb, "dsdb_schema", NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* Remove the reference to the schema we just overwrote - if there was
+ * none, NULL is harmless here */
+ talloc_unlink(ldb, old_schema);
+
+ /* Set the new attributes based on the new schema */
+ /* Don't write indices and attributes, it's expensive */
+ ret = dsdb_schema_set_indices_and_attributes(ldb, global_schema, SCHEMA_MEMORY_ONLY);
+ if (ret == LDB_SUCCESS) {
+ void *schema_parent = talloc_parent(global_schema);
+ bool is_already_parent =
+ (schema_parent == ldb);
+ if (!is_already_parent) {
+ ptr = talloc_reference(ldb, global_schema);
+ if (ptr == NULL) {
+ return ldb_oom(ldb);
+ }
+ ret = ldb_set_opaque(ldb, "dsdb_schema", global_schema);
+ }
+ }
+
+ return ret;
+}
+
+bool dsdb_uses_global_schema(struct ldb_context *ldb)
+{
+ return (ldb_get_opaque(ldb, "dsdb_use_global_schema") != NULL);
+}
+
+/**
+ * Find the schema object for this ldb
+ *
+ * If reference_ctx is not NULL, then talloc_reference onto that context
+ */
+
+struct dsdb_schema *dsdb_get_schema(struct ldb_context *ldb, TALLOC_CTX *reference_ctx)
+{
+ const void *p;
+ struct dsdb_schema *schema_out = NULL;
+ struct dsdb_schema *schema_in = NULL;
+ dsdb_schema_refresh_fn refresh_fn;
+ struct ldb_module *loaded_from_module;
+ bool use_global_schema;
+ TALLOC_CTX *tmp_ctx = talloc_new(reference_ctx);
+ if (tmp_ctx == NULL) {
+ return NULL;
+ }
+
+ /* see if we have a cached copy */
+ use_global_schema = dsdb_uses_global_schema(ldb);
+ if (use_global_schema) {
+ schema_in = global_schema;
+ } else {
+ p = ldb_get_opaque(ldb, "dsdb_schema");
+ if (p != NULL) {
+ schema_in = talloc_get_type_abort(p, struct dsdb_schema);
+ }
+ }
+
+ refresh_fn = ldb_get_opaque(ldb, "dsdb_schema_refresh_fn");
+ if (refresh_fn) {
+ loaded_from_module = ldb_get_opaque(ldb, "dsdb_schema_refresh_fn_private_data");
+
+ SMB_ASSERT(loaded_from_module && (ldb_module_get_ctx(loaded_from_module) == ldb));
+ }
+
+ if (refresh_fn) {
+ /* We need to guard against recursive calls here */
+ if (ldb_set_opaque(ldb, "dsdb_schema_refresh_fn", NULL) != LDB_SUCCESS) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "dsdb_get_schema: clearing dsdb_schema_refresh_fn failed");
+ } else {
+ schema_out = refresh_fn(loaded_from_module,
+ ldb_get_event_context(ldb),
+ schema_in,
+ use_global_schema);
+ }
+ if (ldb_set_opaque(ldb, "dsdb_schema_refresh_fn", refresh_fn) != LDB_SUCCESS) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "dsdb_get_schema: re-setting dsdb_schema_refresh_fn failed");
+ }
+ if (!schema_out) {
+ schema_out = schema_in;
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "dsdb_get_schema: refresh_fn() failed");
+ }
+ } else {
+ schema_out = schema_in;
+ }
+
+ /* This removes the extra reference above */
+ talloc_free(tmp_ctx);
+
+ /*
+ * If ref ctx exists and doesn't already reference schema, then add
+ * a reference. Otherwise, just return schema.
+ *
+ * We must use talloc_parent(), which is not quite free (there
+ * is no direct parent pointer in talloc, only one on the
+ * first child within a linked list), but is much cheaper than
+ * talloc_is_parent() which walks the whole tree up to the top
+ * looking for a potential grand-grand(etc)-parent.
+ */
+ if (reference_ctx == NULL) {
+ return schema_out;
+ } else {
+ void *schema_parent = talloc_parent(schema_out);
+ bool is_already_parent =
+ schema_parent == reference_ctx;
+ if (is_already_parent) {
+ return schema_out;
+ } else {
+ return talloc_reference(reference_ctx,
+ schema_out);
+ }
+ }
+}
+
+/**
+ * Make the schema found on this ldb the 'global' schema
+ */
+
+void dsdb_make_schema_global(struct ldb_context *ldb, struct dsdb_schema *schema)
+{
+ if (!schema) {
+ return;
+ }
+
+ if (global_schema) {
+ talloc_unlink(NULL, global_schema);
+ }
+
+ /* we want the schema to be around permanently */
+ talloc_reparent(ldb, NULL, schema);
+ global_schema = schema;
+
+ /* This calls the talloc_reference() of the global schema back onto the ldb */
+ dsdb_set_global_schema(ldb);
+}
+
+/**
+ * When loading the schema from LDIF files, we don't get the extended DNs.
+ *
+ * We need to set these up, so that from the moment we start the provision,
+ * the defaultObjectCategory links are set up correctly.
+ */
+int dsdb_schema_fill_extended_dn(struct ldb_context *ldb, struct dsdb_schema *schema)
+{
+ struct dsdb_class *cur;
+ const struct dsdb_class *target_class;
+ for (cur = schema->classes; cur; cur = cur->next) {
+ const struct ldb_val *rdn;
+ struct ldb_val guid;
+ NTSTATUS status;
+ int ret;
+ struct ldb_dn *dn = ldb_dn_new(NULL, ldb, cur->defaultObjectCategory);
+
+ if (!dn) {
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+ rdn = ldb_dn_get_component_val(dn, 0);
+ if (!rdn) {
+ talloc_free(dn);
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+ target_class = dsdb_class_by_cn_ldb_val(schema, rdn);
+ if (!target_class) {
+ talloc_free(dn);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ status = GUID_to_ndr_blob(&target_class->objectGUID, dn, &guid);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(dn);
+ return ldb_operr(ldb);
+ }
+ ret = ldb_dn_set_extended_component(dn, "GUID", &guid);
+ if (ret != LDB_SUCCESS) {
+ ret = ldb_error(ldb, ret, "Could not set GUID");
+ talloc_free(dn);
+ return ret;
+ }
+
+ cur->defaultObjectCategory = ldb_dn_get_extended_linearized(cur, dn, 1);
+ talloc_free(dn);
+ }
+ return LDB_SUCCESS;
+}
+
+/**
+ * @brief Add a new element to the schema and checks if it's a duplicate
+ *
+ * This function will add a new element to the schema and checks for existing
+ * duplicates.
+ *
+ * @param[in] ldb A pointer to an LDB context
+ *
+ * @param[in] schema A pointer to the dsdb_schema where the element
+ * will be added.
+ *
+ * @param[in] msg The ldb_message object representing the element
+ * to add.
+ *
+ * @param[in] checkdups A boolean to indicate if checks for duplicates
+ * should be done.
+ *
+ * @return A WERROR code
+ */
+WERROR dsdb_schema_set_el_from_ldb_msg_dups(struct ldb_context *ldb, struct dsdb_schema *schema,
+ struct ldb_message *msg, bool checkdups)
+{
+ const char* tstring;
+ time_t ts;
+ tstring = ldb_msg_find_attr_as_string(msg, "whenChanged", NULL);
+ /* keep a trace of the ts of the most recently changed object */
+ if (tstring) {
+ ts = ldb_string_to_time(tstring);
+ if (ts > schema->ts_last_change) {
+ schema->ts_last_change = ts;
+ }
+ }
+ if (samdb_find_attribute(ldb, msg,
+ "objectclass", "attributeSchema") != NULL) {
+
+ return dsdb_set_attribute_from_ldb_dups(ldb, schema, msg, checkdups);
+ } else if (samdb_find_attribute(ldb, msg,
+ "objectclass", "classSchema") != NULL) {
+ return dsdb_set_class_from_ldb_dups(schema, msg, checkdups);
+ }
+ /* Don't fail on things not classes or attributes */
+ return WERR_OK;
+}
+
+WERROR dsdb_schema_set_el_from_ldb_msg(struct ldb_context *ldb,
+ struct dsdb_schema *schema,
+ struct ldb_message *msg)
+{
+ return dsdb_schema_set_el_from_ldb_msg_dups(ldb, schema, msg, false);
+}
+
+/**
+ * Rather than read a schema from the LDB itself, read it from an ldif
+ * file. This allows schema to be loaded and used while adding the
+ * schema itself to the directory.
+ *
+ * Should be called with a transaction (or failing that, have no concurrent
+ * access while called).
+ */
+
+WERROR dsdb_set_schema_from_ldif(struct ldb_context *ldb,
+ const char *pf, const char *df,
+ const char *dn)
+{
+ struct ldb_ldif *ldif;
+ struct ldb_message *msg;
+ TALLOC_CTX *mem_ctx;
+ WERROR status;
+ int ret;
+ struct dsdb_schema *schema;
+ const struct ldb_val *prefix_val;
+ const struct ldb_val *info_val;
+ struct ldb_val info_val_default;
+
+
+ mem_ctx = talloc_new(ldb);
+ if (!mem_ctx) {
+ goto nomem;
+ }
+
+ schema = dsdb_new_schema(mem_ctx);
+ if (!schema) {
+ goto nomem;
+ }
+ schema->fsmo.we_are_master = true;
+ schema->fsmo.update_allowed = true;
+ schema->fsmo.master_dn = ldb_dn_new(schema, ldb, "@PROVISION_SCHEMA_MASTER");
+ if (!schema->fsmo.master_dn) {
+ goto nomem;
+ }
+
+ /*
+ * load the prefixMap attribute from pf
+ */
+ ldif = ldb_ldif_read_string(ldb, &pf);
+ if (!ldif) {
+ status = WERR_INVALID_PARAMETER;
+ goto failed;
+ }
+ talloc_steal(mem_ctx, ldif);
+
+ ret = ldb_msg_normalize(ldb, mem_ctx, ldif->msg, &msg);
+ if (ret != LDB_SUCCESS) {
+ goto nomem;
+ }
+ talloc_free(ldif);
+
+ prefix_val = ldb_msg_find_ldb_val(msg, "prefixMap");
+ if (!prefix_val) {
+ status = WERR_INVALID_PARAMETER;
+ goto failed;
+ }
+
+ info_val = ldb_msg_find_ldb_val(msg, "schemaInfo");
+ if (!info_val) {
+ status = dsdb_schema_info_blob_new(mem_ctx, &info_val_default);
+ W_ERROR_NOT_OK_GOTO(status, failed);
+ info_val = &info_val_default;
+ }
+
+ status = dsdb_load_oid_mappings_ldb(schema, prefix_val, info_val);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("ERROR: dsdb_load_oid_mappings_ldb() failed with %s\n", win_errstr(status)));
+ goto failed;
+ }
+
+ schema->ts_last_change = 0;
+ /* load the attribute and class definitions out of df */
+ while ((ldif = ldb_ldif_read_string(ldb, &df))) {
+ talloc_steal(mem_ctx, ldif);
+
+ ret = ldb_msg_normalize(ldb, ldif, ldif->msg, &msg);
+ if (ret != LDB_SUCCESS) {
+ goto nomem;
+ }
+
+ status = dsdb_schema_set_el_from_ldb_msg(ldb, schema, msg);
+ talloc_free(ldif);
+ if (!W_ERROR_IS_OK(status)) {
+ goto failed;
+ }
+ }
+
+ /*
+ * TODO We may need a transaction here, otherwise this causes races.
+ *
+ * To do so may require an ldb_in_transaction function. In the
+ * meantime, assume that this is always called with a transaction or in
+ * isolation.
+ */
+ ret = dsdb_set_schema(ldb, schema, SCHEMA_WRITE);
+ if (ret != LDB_SUCCESS) {
+ status = WERR_FOOBAR;
+ DEBUG(0,("ERROR: dsdb_set_schema() failed with %s / %s\n",
+ ldb_strerror(ret), ldb_errstring(ldb)));
+ goto failed;
+ }
+
+ ret = dsdb_schema_fill_extended_dn(ldb, schema);
+ if (ret != LDB_SUCCESS) {
+ status = WERR_FOOBAR;
+ goto failed;
+ }
+
+ goto done;
+
+nomem:
+ status = WERR_NOT_ENOUGH_MEMORY;
+failed:
+done:
+ talloc_free(mem_ctx);
+ return status;
+}
diff --git a/source4/dsdb/schema/schema_syntax.c b/source4/dsdb/schema/schema_syntax.c
new file mode 100644
index 0000000..b3df10a
--- /dev/null
+++ b/source4/dsdb/schema/schema_syntax.c
@@ -0,0 +1,2811 @@
+/*
+ Unix SMB/CIFS Implementation.
+ DSDB schema syntaxes
+
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 2006
+ Copyright (C) Simo Sorce 2005
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+#include "includes.h"
+#include "dsdb/samdb/samdb.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include <ldb.h>
+#include <ldb_errors.h>
+#include "system/time.h"
+#include "../lib/util/charset/charset.h"
+#include "librpc/ndr/libndr.h"
+#include "../lib/util/asn1.h"
+
+#undef strcasecmp
+
+/**
+ * Initialize dsdb_syntax_ctx with default values
+ * for common cases.
+ */
+void dsdb_syntax_ctx_init(struct dsdb_syntax_ctx *ctx,
+ struct ldb_context *ldb,
+ const struct dsdb_schema *schema)
+{
+ ctx->ldb = ldb;
+ ctx->schema = schema;
+
+ /*
+ * 'true' will keep current behavior,
+ * i.e. attributeID_id will be returned by default
+ */
+ ctx->is_schema_nc = true;
+
+ ctx->pfm_remote = NULL;
+}
+
+
+/**
+ * Returns ATTID for DRS attribute.
+ *
+ * ATTID depends on whether we are replicating
+ * Schema NC or msDs-IntId is set for schemaAttribute
+ * for the attribute.
+ */
+uint32_t dsdb_attribute_get_attid(const struct dsdb_attribute *attr,
+ bool for_schema_nc)
+{
+ if (!for_schema_nc && attr->msDS_IntId) {
+ return attr->msDS_IntId;
+ }
+
+ return attr->attributeID_id;
+}
+
+/**
+ * Map an ATTID from remote DC to a local ATTID
+ * using remote prefixMap
+ */
+static bool dsdb_syntax_attid_from_remote_attid(const struct dsdb_syntax_ctx *ctx,
+ TALLOC_CTX *mem_ctx,
+ uint32_t id_remote,
+ uint32_t *id_local)
+{
+ WERROR werr;
+ const char *oid;
+
+ /*
+ * map remote ATTID to local directly in case
+ * of no remote prefixMap (during provision for instance)
+ */
+ if (!ctx->pfm_remote) {
+ *id_local = id_remote;
+ return true;
+ }
+
+ werr = dsdb_schema_pfm_oid_from_attid(ctx->pfm_remote, id_remote, mem_ctx, &oid);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0,("ATTID->OID failed (%s) for: 0x%08X\n", win_errstr(werr), id_remote));
+ return false;
+ }
+
+ werr = dsdb_schema_pfm_attid_from_oid(ctx->schema->prefixmap, oid, id_local);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0,("OID->ATTID failed (%s) for: %s\n", win_errstr(werr), oid));
+ return false;
+ }
+
+ return true;
+}
+
+static WERROR dsdb_syntax_FOOBAR_drsuapi_to_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ unsigned int i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ char *str;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ str = talloc_asprintf(out->values, "%s: not implemented",
+ attr->syntax->name);
+ W_ERROR_HAVE_NO_MEMORY(str);
+
+ out->values[i] = data_blob_string_const(str);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_FOOBAR_ldb_to_drsuapi(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ return WERR_FOOBAR;
+}
+
+static WERROR dsdb_syntax_FOOBAR_validate_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in)
+{
+ return WERR_FOOBAR;
+}
+
+static WERROR dsdb_syntax_BOOL_drsuapi_to_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ unsigned int i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ uint32_t v;
+ char *str;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length != 4) {
+ return WERR_FOOBAR;
+ }
+
+ v = IVAL(in->value_ctr.values[i].blob->data, 0);
+
+ if (v != 0) {
+ str = talloc_strdup(out->values, "TRUE");
+ W_ERROR_HAVE_NO_MEMORY(str);
+ } else {
+ str = talloc_strdup(out->values, "FALSE");
+ W_ERROR_HAVE_NO_MEMORY(str);
+ }
+
+ out->values[i] = data_blob_string_const(str);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_BOOL_ldb_to_drsuapi(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ unsigned int i;
+ DATA_BLOB *blobs;
+
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ out->attid = dsdb_attribute_get_attid(attr,
+ ctx->is_schema_nc);
+ out->value_ctr.num_values = in->num_values;
+ out->value_ctr.values = talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ out->value_ctr.values[i].blob = &blobs[i];
+
+ blobs[i] = data_blob_talloc(blobs, NULL, 4);
+ W_ERROR_HAVE_NO_MEMORY(blobs[i].data);
+
+ if (in->values[i].length >= 4 &&
+ strncmp("TRUE", (const char *)in->values[i].data, in->values[i].length) == 0) {
+ SIVAL(blobs[i].data, 0, 0x00000001);
+ } else if (in->values[i].length >= 5 &&
+ strncmp("FALSE", (const char *)in->values[i].data, in->values[i].length) == 0) {
+ SIVAL(blobs[i].data, 0, 0x00000000);
+ } else {
+ return WERR_FOOBAR;
+ }
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_BOOL_validate_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in)
+{
+ unsigned int i;
+
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ for (i=0; i < in->num_values; i++) {
+ if (in->values[i].length == 0) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ if (in->values[i].length >= 4 &&
+ strncmp("TRUE",
+ (const char *)in->values[i].data,
+ in->values[i].length) == 0) {
+ continue;
+ }
+ if (in->values[i].length >= 5 &&
+ strncmp("FALSE",
+ (const char *)in->values[i].data,
+ in->values[i].length) == 0) {
+ continue;
+ }
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_INT32_drsuapi_to_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ unsigned int i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ int32_t v;
+ char *str;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length != 4) {
+ return WERR_FOOBAR;
+ }
+
+ v = IVALS(in->value_ctr.values[i].blob->data, 0);
+
+ str = talloc_asprintf(out->values, "%d", v);
+ W_ERROR_HAVE_NO_MEMORY(str);
+
+ out->values[i] = data_blob_string_const(str);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_INT32_ldb_to_drsuapi(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ unsigned int i;
+ DATA_BLOB *blobs;
+
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ out->attid = dsdb_attribute_get_attid(attr,
+ ctx->is_schema_nc);
+ out->value_ctr.num_values = in->num_values;
+ out->value_ctr.values = talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ int32_t v;
+
+ out->value_ctr.values[i].blob = &blobs[i];
+
+ blobs[i] = data_blob_talloc(blobs, NULL, 4);
+ W_ERROR_HAVE_NO_MEMORY(blobs[i].data);
+
+ /* We've to use "strtoll" here to have the intended overflows.
+ * Otherwise we may get "LONG_MAX" and the conversion is wrong. */
+ v = (int32_t) strtoll((char *)in->values[i].data, NULL, 0);
+
+ SIVALS(blobs[i].data, 0, v);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_INT32_validate_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in)
+{
+ unsigned int i;
+
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ for (i=0; i < in->num_values; i++) {
+ long v;
+ char buf[sizeof("-2147483648")];
+ char *end = NULL;
+
+ ZERO_STRUCT(buf);
+ if (in->values[i].length >= sizeof(buf)) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ memcpy(buf, in->values[i].data, in->values[i].length);
+ errno = 0;
+ v = strtol(buf, &end, 10);
+ if (errno != 0) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ if (end && end[0] != '\0') {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ if (attr->rangeLower) {
+ if ((int32_t)v < (int32_t)*attr->rangeLower) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ }
+
+ if (attr->rangeUpper) {
+ if ((int32_t)v > (int32_t)*attr->rangeUpper) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ }
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_INT64_drsuapi_to_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ unsigned int i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ int64_t v;
+ char *str;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length != 8) {
+ return WERR_FOOBAR;
+ }
+
+ v = BVALS(in->value_ctr.values[i].blob->data, 0);
+
+ str = talloc_asprintf(out->values, "%lld", (long long int)v);
+ W_ERROR_HAVE_NO_MEMORY(str);
+
+ out->values[i] = data_blob_string_const(str);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_INT64_ldb_to_drsuapi(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ unsigned int i;
+ DATA_BLOB *blobs;
+
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ out->attid = dsdb_attribute_get_attid(attr,
+ ctx->is_schema_nc);
+ out->value_ctr.num_values = in->num_values;
+ out->value_ctr.values = talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ int64_t v;
+
+ out->value_ctr.values[i].blob = &blobs[i];
+
+ blobs[i] = data_blob_talloc(blobs, NULL, 8);
+ W_ERROR_HAVE_NO_MEMORY(blobs[i].data);
+
+ v = strtoll((const char *)in->values[i].data, NULL, 10);
+
+ SBVALS(blobs[i].data, 0, v);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_INT64_validate_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in)
+{
+ unsigned int i;
+
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ for (i=0; i < in->num_values; i++) {
+ long long v;
+ char buf[sizeof("-9223372036854775808")];
+ char *end = NULL;
+
+ ZERO_STRUCT(buf);
+ if (in->values[i].length >= sizeof(buf)) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ memcpy(buf, in->values[i].data, in->values[i].length);
+
+ errno = 0;
+ v = strtoll(buf, &end, 10);
+ if (errno != 0) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ if (end && end[0] != '\0') {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ if (attr->rangeLower) {
+ if ((int64_t)v < (int64_t)*attr->rangeLower) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ }
+
+ if (attr->rangeUpper) {
+ if ((int64_t)v > (int64_t)*attr->rangeUpper) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ }
+ }
+
+ return WERR_OK;
+}
+static WERROR dsdb_syntax_NTTIME_UTC_drsuapi_to_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ unsigned int i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ NTTIME v;
+ time_t t;
+ char *str;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length != 8) {
+ return WERR_FOOBAR;
+ }
+
+ v = BVAL(in->value_ctr.values[i].blob->data, 0);
+ if (v == 0) {
+ /* special case for 1601 zero timestamp */
+ out->values[i] = data_blob_string_const("16010101000000.0Z");
+ continue;
+ }
+ v *= 10000000;
+ t = nt_time_to_unix(v);
+
+ /*
+ * NOTE: On a w2k3 server you can set a GeneralizedTime string
+ * via LDAP, but you get back an UTCTime string,
+ * but via DRSUAPI you get back the NTTIME_1sec value
+ * that represents the GeneralizedTime value!
+ *
+ * So if we store the UTCTime string in our ldb
+ * we'll loose information!
+ */
+ str = ldb_timestring_utc(out->values, t);
+ W_ERROR_HAVE_NO_MEMORY(str);
+ out->values[i] = data_blob_string_const(str);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_NTTIME_UTC_ldb_to_drsuapi(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ unsigned int i;
+ DATA_BLOB *blobs;
+
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ out->attid = dsdb_attribute_get_attid(attr,
+ ctx->is_schema_nc);
+ out->value_ctr.num_values = in->num_values;
+ out->value_ctr.values = talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ NTTIME v;
+ time_t t;
+
+ out->value_ctr.values[i].blob = &blobs[i];
+
+ blobs[i] = data_blob_talloc(blobs, NULL, 8);
+ W_ERROR_HAVE_NO_MEMORY(blobs[i].data);
+
+ if (ldb_val_string_cmp(&in->values[i], "16010101000000.0Z") == 0) {
+ SBVALS(blobs[i].data, 0, 0);
+ continue;
+ }
+
+ t = ldb_string_utc_to_time((const char *)in->values[i].data);
+ unix_to_nt_time(&v, t);
+ v /= 10000000;
+
+ SBVAL(blobs[i].data, 0, v);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_NTTIME_UTC_validate_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in)
+{
+ unsigned int i;
+
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ for (i=0; i < in->num_values; i++) {
+ time_t t;
+ char buf[sizeof("090826075717Z")];
+
+ ZERO_STRUCT(buf);
+ if (in->values[i].length >= sizeof(buf)) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ memcpy(buf, in->values[i].data, in->values[i].length);
+
+ t = ldb_string_utc_to_time(buf);
+ if (t == 0) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ if (attr->rangeLower) {
+ if ((int32_t)t < (int32_t)*attr->rangeLower) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ }
+
+ if (attr->rangeUpper) {
+ if ((int32_t)t > (int32_t)*attr->rangeUpper) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ }
+
+ /*
+ * TODO: verify the comment in the
+ * dsdb_syntax_NTTIME_UTC_drsuapi_to_ldb() function!
+ */
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_NTTIME_drsuapi_to_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ unsigned int i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ NTTIME v;
+ time_t t;
+ char *str;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length != 8) {
+ return WERR_FOOBAR;
+ }
+
+ v = BVAL(in->value_ctr.values[i].blob->data, 0);
+ if (v == 0) {
+ /* special case for 1601 zero timestamp */
+ out->values[i] = data_blob_string_const("16010101000000.0Z");
+ continue;
+ }
+ v *= 10000000;
+ t = nt_time_to_unix(v);
+
+ str = ldb_timestring(out->values, t);
+ W_ERROR_HAVE_NO_MEMORY(str);
+
+ out->values[i] = data_blob_string_const(str);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_NTTIME_ldb_to_drsuapi(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ unsigned int i;
+ DATA_BLOB *blobs;
+
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ out->attid = dsdb_attribute_get_attid(attr,
+ ctx->is_schema_nc);
+ out->value_ctr.num_values = in->num_values;
+ out->value_ctr.values = talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ NTTIME v;
+ time_t t;
+ int ret;
+
+ out->value_ctr.values[i].blob = &blobs[i];
+
+ blobs[i] = data_blob_talloc(blobs, NULL, 8);
+ W_ERROR_HAVE_NO_MEMORY(blobs[i].data);
+
+ if (ldb_val_string_cmp(&in->values[i], "16010101000000.0Z") == 0) {
+ SBVALS(blobs[i].data, 0, 0);
+ continue;
+ }
+
+ ret = ldb_val_to_time(&in->values[i], &t);
+ if (ret != LDB_SUCCESS) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ unix_to_nt_time(&v, t);
+ v /= 10000000;
+
+ SBVAL(blobs[i].data, 0, v);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_NTTIME_validate_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in)
+{
+ unsigned int i;
+
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ for (i=0; i < in->num_values; i++) {
+ time_t t;
+ int ret;
+
+ ret = ldb_val_to_time(&in->values[i], &t);
+ if (ret != LDB_SUCCESS) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ if (attr->rangeLower) {
+ if ((int32_t)t < (int32_t)*attr->rangeLower) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ }
+
+ if (attr->rangeUpper) {
+ if ((int32_t)t > (int32_t)*attr->rangeUpper) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ }
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_DATA_BLOB_drsuapi_to_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ unsigned int i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length == 0) {
+ return WERR_FOOBAR;
+ }
+
+ out->values[i] = data_blob_dup_talloc(out->values,
+ *in->value_ctr.values[i].blob);
+ W_ERROR_HAVE_NO_MEMORY(out->values[i].data);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_DATA_BLOB_ldb_to_drsuapi(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ unsigned int i;
+ DATA_BLOB *blobs;
+
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ out->attid = dsdb_attribute_get_attid(attr,
+ ctx->is_schema_nc);
+ out->value_ctr.num_values = in->num_values;
+ out->value_ctr.values = talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ out->value_ctr.values[i].blob = &blobs[i];
+
+ blobs[i] = data_blob_dup_talloc(blobs, in->values[i]);
+ W_ERROR_HAVE_NO_MEMORY(blobs[i].data);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_DATA_BLOB_validate_one_val(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_val *val)
+{
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ if (attr->rangeLower) {
+ if ((uint32_t)val->length < (uint32_t)*attr->rangeLower) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ }
+
+ if (attr->rangeUpper) {
+ if ((uint32_t)val->length > (uint32_t)*attr->rangeUpper) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_DATA_BLOB_validate_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in)
+{
+ unsigned int i;
+ WERROR status;
+
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ for (i=0; i < in->num_values; i++) {
+ if (in->values[i].length == 0) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ status = dsdb_syntax_DATA_BLOB_validate_one_val(ctx,
+ attr,
+ &in->values[i]);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ return WERR_OK;
+}
+
+static WERROR _dsdb_syntax_auto_OID_drsuapi_to_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ unsigned int i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ uint32_t v;
+ const struct dsdb_class *c;
+ const struct dsdb_attribute *a;
+ const char *str = NULL;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length != 4) {
+ return WERR_FOOBAR;
+ }
+
+ v = IVAL(in->value_ctr.values[i].blob->data, 0);
+
+ if ((c = dsdb_class_by_governsID_id(ctx->schema, v))) {
+ str = talloc_strdup(out->values, c->lDAPDisplayName);
+ } else if ((a = dsdb_attribute_by_attributeID_id(ctx->schema, v))) {
+ str = talloc_strdup(out->values, a->lDAPDisplayName);
+ } else {
+ WERROR werr;
+ SMB_ASSERT(ctx->pfm_remote);
+ werr = dsdb_schema_pfm_oid_from_attid(ctx->pfm_remote, v,
+ out->values, &str);
+ W_ERROR_NOT_OK_RETURN(werr);
+ }
+ W_ERROR_HAVE_NO_MEMORY(str);
+
+ /* the values need to be reversed */
+ out->values[out->num_values - (i + 1)] = data_blob_string_const(str);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR _dsdb_syntax_OID_obj_drsuapi_to_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ unsigned int i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ uint32_t v, vo;
+ const struct dsdb_class *c;
+ const char *str;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length != 4) {
+ return WERR_FOOBAR;
+ }
+
+ v = IVAL(in->value_ctr.values[i].blob->data, 0);
+ vo = v;
+
+ /* convert remote ATTID to local ATTID */
+ if (!dsdb_syntax_attid_from_remote_attid(ctx, mem_ctx, v, &v)) {
+ DEBUG(1,(__location__ ": Failed to map remote ATTID to local ATTID!\n"));
+ return WERR_FOOBAR;
+ }
+
+ c = dsdb_class_by_governsID_id(ctx->schema, v);
+ if (!c) {
+ int dbg_level = ctx->schema->resolving_in_progress ? 10 : 0;
+ DEBUG(dbg_level,(__location__ ": %s unknown local governsID 0x%08X remote 0x%08X%s\n",
+ attr->lDAPDisplayName, v, vo,
+ ctx->schema->resolving_in_progress ? "resolving in progress" : ""));
+ return WERR_DS_OBJ_CLASS_NOT_DEFINED;
+ }
+
+ str = talloc_strdup(out->values, c->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(str);
+
+ /* the values need to be reversed */
+ out->values[out->num_values - (i + 1)] = data_blob_string_const(str);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR _dsdb_syntax_OID_attr_drsuapi_to_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ unsigned int i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ uint32_t v, vo;
+ const struct dsdb_attribute *a;
+ const char *str;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ DEBUG(0, ("Attribute has no value\n"));
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length != 4) {
+ DEBUG(0, ("Attribute has a value with 0 length\n"));
+ return WERR_FOOBAR;
+ }
+
+ v = IVAL(in->value_ctr.values[i].blob->data, 0);
+ vo = v;
+
+ /* convert remote ATTID to local ATTID */
+ if (!dsdb_syntax_attid_from_remote_attid(ctx, mem_ctx, v, &v)) {
+ DEBUG(1,(__location__ ": Failed to map remote ATTID to local ATTID!\n"));
+ return WERR_FOOBAR;
+ }
+
+ a = dsdb_attribute_by_attributeID_id(ctx->schema, v);
+ if (!a) {
+ int dbg_level = ctx->schema->resolving_in_progress ? 10 : 0;
+ DEBUG(dbg_level,(__location__ ": %s unknown local attributeID_id 0x%08X remote 0x%08X%s\n",
+ attr->lDAPDisplayName, v, vo,
+ ctx->schema->resolving_in_progress ? "resolving in progress" : ""));
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ str = talloc_strdup(out->values, a->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(str);
+
+ /* the values need to be reversed */
+ out->values[out->num_values - (i + 1)] = data_blob_string_const(str);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR _dsdb_syntax_OID_oid_drsuapi_to_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ unsigned int i;
+ const struct dsdb_schema_prefixmap *prefixmap;
+
+ if (ctx->pfm_remote != NULL) {
+ prefixmap = ctx->pfm_remote;
+ } else {
+ prefixmap = ctx->schema->prefixmap;
+ }
+ SMB_ASSERT(prefixmap);
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ uint32_t attid;
+ WERROR status;
+ const char *oid;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length != 4) {
+ return WERR_FOOBAR;
+ }
+
+ attid = IVAL(in->value_ctr.values[i].blob->data, 0);
+
+ status = dsdb_schema_pfm_oid_from_attid(prefixmap, attid,
+ out->values, &oid);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,(__location__ ": Error: Unknown ATTID 0x%08X\n",
+ attid));
+ return status;
+ }
+
+ out->values[i] = data_blob_string_const(oid);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR _dsdb_syntax_auto_OID_ldb_to_drsuapi(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ unsigned int i;
+ DATA_BLOB *blobs;
+
+ out->attid= dsdb_attribute_get_attid(attr,
+ ctx->is_schema_nc);
+ out->value_ctr.num_values= in->num_values;
+ out->value_ctr.values= talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ const struct dsdb_class *obj_class;
+ const struct dsdb_attribute *obj_attr;
+ struct ldb_val *v;
+
+ out->value_ctr.values[i].blob= &blobs[i];
+
+ blobs[i] = data_blob_talloc(blobs, NULL, 4);
+ W_ERROR_HAVE_NO_MEMORY(blobs[i].data);
+
+ /* in DRS windows puts the classes in the opposite
+ order to the order used in ldap */
+ v = &in->values[(in->num_values-1)-i];
+
+ if ((obj_class = dsdb_class_by_lDAPDisplayName_ldb_val(ctx->schema, v))) {
+ SIVAL(blobs[i].data, 0, obj_class->governsID_id);
+ } else if ((obj_attr = dsdb_attribute_by_lDAPDisplayName_ldb_val(ctx->schema, v))) {
+ SIVAL(blobs[i].data, 0, obj_attr->attributeID_id);
+ } else {
+ uint32_t attid;
+ WERROR werr;
+ werr = dsdb_schema_pfm_attid_from_oid(ctx->schema->prefixmap,
+ (const char *)v->data,
+ &attid);
+ W_ERROR_NOT_OK_RETURN(werr);
+ SIVAL(blobs[i].data, 0, attid);
+ }
+
+ }
+
+
+ return WERR_OK;
+}
+
+static WERROR _dsdb_syntax_OID_obj_ldb_to_drsuapi(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ unsigned int i;
+ DATA_BLOB *blobs;
+
+ out->attid= dsdb_attribute_get_attid(attr,
+ ctx->is_schema_nc);
+ out->value_ctr.num_values= in->num_values;
+ out->value_ctr.values= talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ const struct dsdb_class *obj_class;
+
+ out->value_ctr.values[i].blob= &blobs[i];
+
+ blobs[i] = data_blob_talloc(blobs, NULL, 4);
+ W_ERROR_HAVE_NO_MEMORY(blobs[i].data);
+
+ /* in DRS windows puts the classes in the opposite
+ order to the order used in ldap */
+ obj_class = dsdb_class_by_lDAPDisplayName(ctx->schema,
+ (const char *)in->values[(in->num_values-1)-i].data);
+ if (!obj_class) {
+ return WERR_FOOBAR;
+ }
+ SIVAL(blobs[i].data, 0, obj_class->governsID_id);
+ }
+
+
+ return WERR_OK;
+}
+
+static WERROR _dsdb_syntax_OID_attr_ldb_to_drsuapi(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ unsigned int i;
+ DATA_BLOB *blobs;
+
+ out->attid= dsdb_attribute_get_attid(attr,
+ ctx->is_schema_nc);
+ out->value_ctr.num_values= in->num_values;
+ out->value_ctr.values= talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ const struct dsdb_attribute *obj_attr;
+
+ out->value_ctr.values[i].blob= &blobs[i];
+
+ blobs[i] = data_blob_talloc(blobs, NULL, 4);
+ W_ERROR_HAVE_NO_MEMORY(blobs[i].data);
+
+ obj_attr = dsdb_attribute_by_lDAPDisplayName(ctx->schema, (const char *)in->values[i].data);
+ if (!obj_attr) {
+ DEBUG(0, ("Unable to find attribute %s in the schema\n", (const char *)in->values[i].data));
+ return WERR_FOOBAR;
+ }
+ SIVAL(blobs[i].data, 0, obj_attr->attributeID_id);
+ }
+
+
+ return WERR_OK;
+}
+
+static WERROR _dsdb_syntax_OID_oid_ldb_to_drsuapi(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ unsigned int i;
+ DATA_BLOB *blobs;
+
+ out->attid= dsdb_attribute_get_attid(attr,
+ ctx->is_schema_nc);
+ out->value_ctr.num_values= in->num_values;
+ out->value_ctr.values= talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ uint32_t attid;
+ WERROR status;
+
+ out->value_ctr.values[i].blob= &blobs[i];
+
+ blobs[i] = data_blob_talloc(blobs, NULL, 4);
+ W_ERROR_HAVE_NO_MEMORY(blobs[i].data);
+
+ status = dsdb_schema_pfm_attid_from_oid(ctx->schema->prefixmap,
+ (const char *)in->values[i].data,
+ &attid);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ SIVAL(blobs[i].data, 0, attid);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_OID_drsuapi_to_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ WERROR werr;
+
+ switch (attr->attributeID_id) {
+ case DRSUAPI_ATTID_objectClass:
+ case DRSUAPI_ATTID_subClassOf:
+ case DRSUAPI_ATTID_auxiliaryClass:
+ case DRSUAPI_ATTID_systemAuxiliaryClass:
+ case DRSUAPI_ATTID_systemPossSuperiors:
+ case DRSUAPI_ATTID_possSuperiors:
+ werr = _dsdb_syntax_OID_obj_drsuapi_to_ldb(ctx, attr, in, mem_ctx, out);
+ break;
+ case DRSUAPI_ATTID_systemMustContain:
+ case DRSUAPI_ATTID_systemMayContain:
+ case DRSUAPI_ATTID_mustContain:
+ case DRSUAPI_ATTID_rDNAttId:
+ case DRSUAPI_ATTID_transportAddressAttribute:
+ case DRSUAPI_ATTID_mayContain:
+ werr = _dsdb_syntax_OID_attr_drsuapi_to_ldb(ctx, attr, in, mem_ctx, out);
+ break;
+ case DRSUAPI_ATTID_governsID:
+ case DRSUAPI_ATTID_attributeID:
+ case DRSUAPI_ATTID_attributeSyntax:
+ werr = _dsdb_syntax_OID_oid_drsuapi_to_ldb(ctx, attr, in, mem_ctx, out);
+ break;
+ default:
+ DEBUG(0,(__location__ ": Unknown handling for attributeID_id for %s\n",
+ attr->lDAPDisplayName));
+ return _dsdb_syntax_auto_OID_drsuapi_to_ldb(ctx, attr, in, mem_ctx, out);
+ }
+
+ /* When we are doing the vampire of a schema, we don't want
+ * the inability to reference an OID to get in the way.
+ * Otherwise, we won't get the new schema with which to
+ * understand this */
+ if (!W_ERROR_IS_OK(werr) && ctx->schema->relax_OID_conversions) {
+ return _dsdb_syntax_OID_oid_drsuapi_to_ldb(ctx, attr, in, mem_ctx, out);
+ }
+ return werr;
+}
+
+static WERROR dsdb_syntax_OID_ldb_to_drsuapi(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ switch (attr->attributeID_id) {
+ case DRSUAPI_ATTID_objectClass:
+ case DRSUAPI_ATTID_subClassOf:
+ case DRSUAPI_ATTID_auxiliaryClass:
+ case DRSUAPI_ATTID_systemAuxiliaryClass:
+ case DRSUAPI_ATTID_systemPossSuperiors:
+ case DRSUAPI_ATTID_possSuperiors:
+ return _dsdb_syntax_OID_obj_ldb_to_drsuapi(ctx, attr, in, mem_ctx, out);
+ case DRSUAPI_ATTID_systemMustContain:
+ case DRSUAPI_ATTID_systemMayContain:
+ case DRSUAPI_ATTID_mustContain:
+ case DRSUAPI_ATTID_rDNAttId:
+ case DRSUAPI_ATTID_transportAddressAttribute:
+ case DRSUAPI_ATTID_mayContain:
+ return _dsdb_syntax_OID_attr_ldb_to_drsuapi(ctx, attr, in, mem_ctx, out);
+ case DRSUAPI_ATTID_governsID:
+ case DRSUAPI_ATTID_attributeID:
+ case DRSUAPI_ATTID_attributeSyntax:
+ return _dsdb_syntax_OID_oid_ldb_to_drsuapi(ctx, attr, in, mem_ctx, out);
+ }
+
+ DEBUG(0,(__location__ ": Unknown handling for attributeID_id for %s\n",
+ attr->lDAPDisplayName));
+
+ return _dsdb_syntax_auto_OID_ldb_to_drsuapi(ctx, attr, in, mem_ctx, out);
+}
+
+static WERROR _dsdb_syntax_OID_validate_numericoid(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in)
+{
+ unsigned int i;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(ctx->ldb);
+ W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
+
+ for (i=0; i < in->num_values; i++) {
+ DATA_BLOB blob;
+ char *oid_out;
+ const char *oid = (const char*)in->values[i].data;
+
+ if (in->values[i].length == 0) {
+ talloc_free(tmp_ctx);
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ if (!ber_write_OID_String(tmp_ctx, &blob, oid)) {
+ DEBUG(0,("ber_write_OID_String() failed for %s\n", oid));
+ talloc_free(tmp_ctx);
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (!ber_read_OID_String(tmp_ctx, blob, &oid_out)) {
+ DEBUG(0,("ber_read_OID_String() failed for %s\n",
+ hex_encode_talloc(tmp_ctx, blob.data, blob.length)));
+ talloc_free(tmp_ctx);
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (strcmp(oid, oid_out) != 0) {
+ talloc_free(tmp_ctx);
+ return WERR_INVALID_PARAMETER;
+ }
+ }
+
+ talloc_free(tmp_ctx);
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_OID_validate_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in)
+{
+ WERROR status;
+ struct drsuapi_DsReplicaAttribute drs_tmp;
+ struct ldb_message_element ldb_tmp;
+ TALLOC_CTX *tmp_ctx;
+
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ switch (attr->attributeID_id) {
+ case DRSUAPI_ATTID_governsID:
+ case DRSUAPI_ATTID_attributeID:
+ case DRSUAPI_ATTID_attributeSyntax:
+ return _dsdb_syntax_OID_validate_numericoid(ctx, attr, in);
+ }
+
+ /*
+ * TODO: optimize and verify this code
+ */
+
+ tmp_ctx = talloc_new(ctx->ldb);
+ W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
+
+ status = dsdb_syntax_OID_ldb_to_drsuapi(ctx,
+ attr,
+ in,
+ tmp_ctx,
+ &drs_tmp);
+ if (!W_ERROR_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ status = dsdb_syntax_OID_drsuapi_to_ldb(ctx,
+ attr,
+ &drs_tmp,
+ tmp_ctx,
+ &ldb_tmp);
+ if (!W_ERROR_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ talloc_free(tmp_ctx);
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_UNICODE_drsuapi_to_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ unsigned int i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ size_t converted_size = 0;
+ char *str;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length == 0) {
+ return WERR_FOOBAR;
+ }
+
+ if (!convert_string_talloc(out->values,
+ CH_UTF16, CH_UNIX,
+ in->value_ctr.values[i].blob->data,
+ in->value_ctr.values[i].blob->length,
+ (void **)&str, &converted_size)) {
+ return WERR_FOOBAR;
+ }
+
+ out->values[i] = data_blob_const(str, converted_size);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_UNICODE_ldb_to_drsuapi(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ unsigned int i;
+ DATA_BLOB *blobs;
+
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ out->attid = dsdb_attribute_get_attid(attr,
+ ctx->is_schema_nc);
+ out->value_ctr.num_values = in->num_values;
+ out->value_ctr.values = talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ out->value_ctr.values[i].blob = &blobs[i];
+
+ if (!convert_string_talloc(blobs,
+ CH_UNIX, CH_UTF16,
+ in->values[i].data, in->values[i].length,
+ (void **)&blobs[i].data, &blobs[i].length)) {
+ return WERR_FOOBAR;
+ }
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_UNICODE_validate_one_val(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_val *val)
+{
+ void *dst = NULL;
+ size_t size;
+ bool ok;
+
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ ok = convert_string_talloc(ctx->ldb,
+ CH_UNIX, CH_UTF16,
+ val->data,
+ val->length,
+ (void **)&dst,
+ &size);
+ TALLOC_FREE(dst);
+ if (!ok) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ if (attr->rangeLower) {
+ if ((size/2) < *attr->rangeLower) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ }
+
+ if (attr->rangeUpper) {
+ if ((size/2) > *attr->rangeUpper) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_UNICODE_validate_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in)
+{
+ WERROR status;
+ unsigned int i;
+
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ for (i=0; i < in->num_values; i++) {
+ if (in->values[i].length == 0) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ status = dsdb_syntax_UNICODE_validate_one_val(ctx,
+ attr,
+ &in->values[i]);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_one_DN_drsuapi_to_ldb(TALLOC_CTX *mem_ctx, struct ldb_context *ldb,
+ const struct dsdb_syntax *syntax,
+ const DATA_BLOB *in, DATA_BLOB *out)
+{
+ struct drsuapi_DsReplicaObjectIdentifier3 id3;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB guid_blob;
+ struct ldb_dn *dn;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ int ret;
+ NTSTATUS status;
+
+ if (!tmp_ctx) {
+ W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
+ }
+
+ if (in == NULL) {
+ talloc_free(tmp_ctx);
+ return WERR_FOOBAR;
+ }
+
+ if (in->length == 0) {
+ talloc_free(tmp_ctx);
+ return WERR_FOOBAR;
+ }
+
+
+ /* windows sometimes sends an extra two pad bytes here */
+ ndr_err = ndr_pull_struct_blob(in,
+ tmp_ctx, &id3,
+ (ndr_pull_flags_fn_t)ndr_pull_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ talloc_free(tmp_ctx);
+ return ntstatus_to_werror(status);
+ }
+
+ dn = ldb_dn_new(tmp_ctx, ldb, id3.dn);
+ if (!dn) {
+ talloc_free(tmp_ctx);
+ /* If this fails, it must be out of memory, as it does not do much parsing */
+ W_ERROR_HAVE_NO_MEMORY(dn);
+ }
+
+ if (!GUID_all_zero(&id3.guid)) {
+ status = GUID_to_ndr_blob(&id3.guid, tmp_ctx, &guid_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return ntstatus_to_werror(status);
+ }
+
+ ret = ldb_dn_set_extended_component(dn, "GUID", &guid_blob);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return WERR_FOOBAR;
+ }
+ talloc_free(guid_blob.data);
+ }
+
+ if (id3.__ndr_size_sid) {
+ DATA_BLOB sid_blob;
+ ndr_err = ndr_push_struct_blob(&sid_blob, tmp_ctx, &id3.sid,
+ (ndr_push_flags_fn_t)ndr_push_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ talloc_free(tmp_ctx);
+ return ntstatus_to_werror(status);
+ }
+
+ ret = ldb_dn_set_extended_component(dn, "SID", &sid_blob);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return WERR_FOOBAR;
+ }
+ }
+
+ *out = data_blob_string_const(ldb_dn_get_extended_linearized(mem_ctx, dn, 1));
+ talloc_free(tmp_ctx);
+ W_ERROR_HAVE_NO_MEMORY(out->data);
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_DN_drsuapi_to_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ unsigned int i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ WERROR status = dsdb_syntax_one_DN_drsuapi_to_ldb(out->values, ctx->ldb, attr->syntax,
+ in->value_ctr.values[i].blob,
+ &out->values[i]);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_DN_ldb_to_drsuapi(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ unsigned int i;
+ DATA_BLOB *blobs;
+
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ out->attid = dsdb_attribute_get_attid(attr,
+ ctx->is_schema_nc);
+ out->value_ctr.num_values = in->num_values;
+ out->value_ctr.values = talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ struct drsuapi_DsReplicaObjectIdentifier3 id3;
+ enum ndr_err_code ndr_err;
+ struct ldb_dn *dn;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ NTSTATUS status;
+
+ W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
+
+ out->value_ctr.values[i].blob = &blobs[i];
+
+ dn = ldb_dn_from_ldb_val(tmp_ctx, ctx->ldb, &in->values[i]);
+
+ W_ERROR_HAVE_NO_MEMORY(dn);
+
+ ZERO_STRUCT(id3);
+
+ status = dsdb_get_extended_dn_guid(dn, &id3.guid, "GUID");
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ talloc_free(tmp_ctx);
+ return ntstatus_to_werror(status);
+ }
+
+ status = dsdb_get_extended_dn_sid(dn, &id3.sid, "SID");
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ talloc_free(tmp_ctx);
+ return ntstatus_to_werror(status);
+ }
+
+ id3.dn = ldb_dn_get_linearized(dn);
+
+ ndr_err = ndr_push_struct_blob(&blobs[i], blobs, &id3, (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ talloc_free(tmp_ctx);
+ return ntstatus_to_werror(status);
+ }
+ talloc_free(tmp_ctx);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_DN_validate_one_val(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_val *val,
+ TALLOC_CTX *mem_ctx,
+ struct dsdb_dn **_dsdb_dn)
+{
+ static const char * const extended_list[] = { "GUID", "SID", NULL };
+ enum ndr_err_code ndr_err;
+ struct GUID guid;
+ struct dom_sid sid;
+ const DATA_BLOB *sid_blob;
+ struct dsdb_dn *dsdb_dn;
+ struct ldb_dn *dn;
+ char *dn_str;
+ struct ldb_dn *dn2;
+ char *dn2_str;
+ int num_components;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ NTSTATUS status;
+
+ W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
+
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ dsdb_dn = dsdb_dn_parse(tmp_ctx, ctx->ldb, val,
+ attr->syntax->ldap_oid);
+ if (!dsdb_dn) {
+ talloc_free(tmp_ctx);
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ dn = dsdb_dn->dn;
+
+ dn2 = ldb_dn_copy(tmp_ctx, dn);
+ if (dn == NULL) {
+ talloc_free(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ num_components = ldb_dn_get_comp_num(dn);
+
+ status = dsdb_get_extended_dn_guid(dn, &guid, "GUID");
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ num_components++;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ sid_blob = ldb_dn_get_extended_component(dn, "SID");
+ if (sid_blob) {
+ num_components++;
+ ndr_err = ndr_pull_struct_blob_all(sid_blob,
+ tmp_ctx,
+ &sid,
+ (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(tmp_ctx);
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ }
+
+ /* Do not allow links to the RootDSE */
+ if (num_components == 0) {
+ talloc_free(tmp_ctx);
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ /*
+ * We need to check that only "GUID" and "SID" are
+ * specified as extended components, we do that
+ * by comparing the dn's after removing all components
+ * from one dn and only the allowed subset from the other
+ * one.
+ */
+ ldb_dn_extended_filter(dn, extended_list);
+
+ dn_str = ldb_dn_get_extended_linearized(tmp_ctx, dn, 0);
+ if (dn_str == NULL) {
+ talloc_free(tmp_ctx);
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ dn2_str = ldb_dn_get_extended_linearized(tmp_ctx, dn2, 0);
+ if (dn2_str == NULL) {
+ talloc_free(tmp_ctx);
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ if (strcmp(dn_str, dn2_str) != 0) {
+ talloc_free(tmp_ctx);
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ *_dsdb_dn = talloc_move(mem_ctx, &dsdb_dn);
+ talloc_free(tmp_ctx);
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_DN_validate_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in)
+{
+ unsigned int i;
+
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ for (i=0; i < in->num_values; i++) {
+ WERROR status;
+ struct dsdb_dn *dsdb_dn;
+ TALLOC_CTX *tmp_ctx = talloc_new(ctx->ldb);
+ W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
+
+ status = dsdb_syntax_DN_validate_one_val(ctx,
+ attr,
+ &in->values[i],
+ tmp_ctx, &dsdb_dn);
+ if (!W_ERROR_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ if (dsdb_dn->dn_format != DSDB_NORMAL_DN) {
+ talloc_free(tmp_ctx);
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ talloc_free(tmp_ctx);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_DN_BINARY_drsuapi_to_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ unsigned int i;
+ int ret;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ struct drsuapi_DsReplicaObjectIdentifier3Binary id3;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB guid_blob;
+ struct ldb_dn *dn;
+ struct dsdb_dn *dsdb_dn;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
+ }
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ talloc_free(tmp_ctx);
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length == 0) {
+ talloc_free(tmp_ctx);
+ return WERR_FOOBAR;
+ }
+
+
+ /* windows sometimes sends an extra two pad bytes here */
+ ndr_err = ndr_pull_struct_blob(in->value_ctr.values[i].blob,
+ tmp_ctx, &id3,
+ (ndr_pull_flags_fn_t)ndr_pull_drsuapi_DsReplicaObjectIdentifier3Binary);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ talloc_free(tmp_ctx);
+ return ntstatus_to_werror(status);
+ }
+
+ dn = ldb_dn_new(tmp_ctx, ctx->ldb, id3.dn);
+ if (!dn) {
+ talloc_free(tmp_ctx);
+ /* If this fails, it must be out of memory, as it does not do much parsing */
+ W_ERROR_HAVE_NO_MEMORY(dn);
+ }
+
+ if (!GUID_all_zero(&id3.guid)) {
+ status = GUID_to_ndr_blob(&id3.guid, tmp_ctx, &guid_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return ntstatus_to_werror(status);
+ }
+
+ ret = ldb_dn_set_extended_component(dn, "GUID", &guid_blob);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return WERR_FOOBAR;
+ }
+ talloc_free(guid_blob.data);
+ }
+
+ if (id3.__ndr_size_sid) {
+ DATA_BLOB sid_blob;
+ ndr_err = ndr_push_struct_blob(&sid_blob, tmp_ctx, &id3.sid,
+ (ndr_push_flags_fn_t)ndr_push_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ talloc_free(tmp_ctx);
+ return ntstatus_to_werror(status);
+ }
+
+ ret = ldb_dn_set_extended_component(dn, "SID", &sid_blob);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return WERR_FOOBAR;
+ }
+ }
+
+ /* set binary stuff */
+ dsdb_dn = dsdb_dn_construct(tmp_ctx, dn, id3.binary, attr->syntax->ldap_oid);
+ if (!dsdb_dn) {
+ if (errno == EINVAL) {
+ /*
+ * This might be Object(OR-Name)
+ * failing because of a non empty
+ * binary part.
+ */
+ talloc_free(tmp_ctx);
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ talloc_free(tmp_ctx);
+ W_ERROR_HAVE_NO_MEMORY(dsdb_dn);
+ }
+ out->values[i] = data_blob_string_const(dsdb_dn_get_extended_linearized(out->values, dsdb_dn, 1));
+ talloc_free(tmp_ctx);
+ W_ERROR_HAVE_NO_MEMORY(out->values[i].data);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_DN_BINARY_ldb_to_drsuapi(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ unsigned int i;
+ DATA_BLOB *blobs;
+
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ out->attid = dsdb_attribute_get_attid(attr,
+ ctx->is_schema_nc);
+ out->value_ctr.num_values = in->num_values;
+ out->value_ctr.values = talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ struct drsuapi_DsReplicaObjectIdentifier3Binary id3;
+ enum ndr_err_code ndr_err;
+ const DATA_BLOB *sid_blob;
+ struct dsdb_dn *dsdb_dn;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ NTSTATUS status;
+
+ W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
+
+ out->value_ctr.values[i].blob = &blobs[i];
+
+ dsdb_dn = dsdb_dn_parse(tmp_ctx, ctx->ldb, &in->values[i], attr->syntax->ldap_oid);
+
+ if (!dsdb_dn) {
+ talloc_free(tmp_ctx);
+ return ntstatus_to_werror(NT_STATUS_INVALID_PARAMETER);
+ }
+
+ ZERO_STRUCT(id3);
+
+ status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &id3.guid, "GUID");
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ talloc_free(tmp_ctx);
+ return ntstatus_to_werror(status);
+ }
+
+ sid_blob = ldb_dn_get_extended_component(dsdb_dn->dn, "SID");
+ if (sid_blob) {
+
+ ndr_err = ndr_pull_struct_blob_all(sid_blob,
+ tmp_ctx, &id3.sid,
+ (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ talloc_free(tmp_ctx);
+ return ntstatus_to_werror(status);
+ }
+ }
+
+ id3.dn = ldb_dn_get_linearized(dsdb_dn->dn);
+
+ /* get binary stuff */
+ id3.binary = dsdb_dn->extra_part;
+
+ ndr_err = ndr_push_struct_blob(&blobs[i], blobs, &id3, (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3Binary);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ talloc_free(tmp_ctx);
+ return ntstatus_to_werror(status);
+ }
+ talloc_free(tmp_ctx);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_DN_BINARY_validate_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in)
+{
+ unsigned int i;
+
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ for (i=0; i < in->num_values; i++) {
+ WERROR status;
+ struct dsdb_dn *dsdb_dn;
+ TALLOC_CTX *tmp_ctx = talloc_new(ctx->ldb);
+ W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
+
+ status = dsdb_syntax_DN_validate_one_val(ctx,
+ attr,
+ &in->values[i],
+ tmp_ctx, &dsdb_dn);
+ if (!W_ERROR_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ if (dsdb_dn->dn_format != DSDB_BINARY_DN) {
+ talloc_free(tmp_ctx);
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ status = dsdb_syntax_DATA_BLOB_validate_one_val(ctx,
+ attr,
+ &dsdb_dn->extra_part);
+ if (!W_ERROR_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ talloc_free(tmp_ctx);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_DN_STRING_drsuapi_to_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ return dsdb_syntax_DN_BINARY_drsuapi_to_ldb(ctx,
+ attr,
+ in,
+ mem_ctx,
+ out);
+}
+
+static WERROR dsdb_syntax_DN_STRING_ldb_to_drsuapi(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ return dsdb_syntax_DN_BINARY_ldb_to_drsuapi(ctx,
+ attr,
+ in,
+ mem_ctx,
+ out);
+}
+
+static WERROR dsdb_syntax_DN_STRING_validate_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in)
+{
+ unsigned int i;
+
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ for (i=0; i < in->num_values; i++) {
+ WERROR status;
+ struct dsdb_dn *dsdb_dn;
+ TALLOC_CTX *tmp_ctx = talloc_new(ctx->ldb);
+ W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
+
+ status = dsdb_syntax_DN_validate_one_val(ctx,
+ attr,
+ &in->values[i],
+ tmp_ctx, &dsdb_dn);
+ if (!W_ERROR_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ if (dsdb_dn->dn_format != DSDB_STRING_DN) {
+ talloc_free(tmp_ctx);
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ status = dsdb_syntax_UNICODE_validate_one_val(ctx,
+ attr,
+ &dsdb_dn->extra_part);
+ if (!W_ERROR_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ talloc_free(tmp_ctx);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_PRESENTATION_ADDRESS_drsuapi_to_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ unsigned int i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ size_t len;
+ size_t converted_size = 0;
+ char *str;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length < 4) {
+ return WERR_FOOBAR;
+ }
+
+ len = IVAL(in->value_ctr.values[i].blob->data, 0);
+
+ if (len != in->value_ctr.values[i].blob->length) {
+ return WERR_FOOBAR;
+ }
+
+ if (!convert_string_talloc(out->values, CH_UTF16, CH_UNIX,
+ in->value_ctr.values[i].blob->data+4,
+ in->value_ctr.values[i].blob->length-4,
+ (void **)&str, &converted_size)) {
+ return WERR_FOOBAR;
+ }
+
+ out->values[i] = data_blob_string_const(str);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_PRESENTATION_ADDRESS_ldb_to_drsuapi(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ unsigned int i;
+ DATA_BLOB *blobs;
+
+ if (attr->attributeID_id == DRSUAPI_ATTID_INVALID) {
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ out->attid = dsdb_attribute_get_attid(attr,
+ ctx->is_schema_nc);
+ out->value_ctr.num_values = in->num_values;
+ out->value_ctr.values = talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ uint8_t *data;
+ size_t ret;
+
+ out->value_ctr.values[i].blob = &blobs[i];
+
+ if (!convert_string_talloc(blobs, CH_UNIX, CH_UTF16,
+ in->values[i].data,
+ in->values[i].length,
+ (void **)&data, &ret)) {
+ return WERR_FOOBAR;
+ }
+
+ blobs[i] = data_blob_talloc(blobs, NULL, 4 + ret);
+ W_ERROR_HAVE_NO_MEMORY(blobs[i].data);
+
+ SIVAL(blobs[i].data, 0, 4 + ret);
+
+ if (ret > 0) {
+ memcpy(blobs[i].data + 4, data, ret);
+ talloc_free(data);
+ }
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_PRESENTATION_ADDRESS_validate_ldb(const struct dsdb_syntax_ctx *ctx,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in)
+{
+ return dsdb_syntax_UNICODE_validate_ldb(ctx,
+ attr,
+ in);
+}
+
+#define OMOBJECTCLASS(val) { .length = sizeof(val) - 1, .data = discard_const_p(uint8_t, val) }
+
+static const struct dsdb_syntax dsdb_syntaxes[] = {
+ {
+ .name = "Boolean",
+ .ldap_oid = LDB_SYNTAX_BOOLEAN,
+ .oMSyntax = 1,
+ .attributeSyntax_oid = "2.5.5.8",
+ .drsuapi_to_ldb = dsdb_syntax_BOOL_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_BOOL_ldb_to_drsuapi,
+ .validate_ldb = dsdb_syntax_BOOL_validate_ldb,
+ .equality = "booleanMatch",
+ .comment = "Boolean",
+ .auto_normalise = true
+ },{
+ .name = "Integer",
+ .ldap_oid = LDB_SYNTAX_INTEGER,
+ .oMSyntax = 2,
+ .attributeSyntax_oid = "2.5.5.9",
+ .drsuapi_to_ldb = dsdb_syntax_INT32_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_INT32_ldb_to_drsuapi,
+ .validate_ldb = dsdb_syntax_INT32_validate_ldb,
+ .equality = "integerMatch",
+ .comment = "Integer",
+ .ldb_syntax = LDB_SYNTAX_SAMBA_INT32,
+ .auto_normalise = true
+ },{
+ .name = "String(Octet)",
+ .ldap_oid = LDB_SYNTAX_OCTET_STRING,
+ .oMSyntax = 4,
+ .attributeSyntax_oid = "2.5.5.10",
+ .drsuapi_to_ldb = dsdb_syntax_DATA_BLOB_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DATA_BLOB_ldb_to_drsuapi,
+ .validate_ldb = dsdb_syntax_DATA_BLOB_validate_ldb,
+ .equality = "octetStringMatch",
+ .comment = "Octet String",
+ .userParameters = true,
+ .ldb_syntax = LDB_SYNTAX_SAMBA_OCTET_STRING
+ },{
+ .name = "String(Sid)",
+ .ldap_oid = LDB_SYNTAX_OCTET_STRING,
+ .oMSyntax = 4,
+ .attributeSyntax_oid = "2.5.5.17",
+ .drsuapi_to_ldb = dsdb_syntax_DATA_BLOB_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DATA_BLOB_ldb_to_drsuapi,
+ .validate_ldb = dsdb_syntax_DATA_BLOB_validate_ldb,
+ .equality = "octetStringMatch",
+ .comment = "Octet String - Security Identifier (SID)",
+ .ldb_syntax = LDB_SYNTAX_SAMBA_SID
+ },{
+ .name = "String(Object-Identifier)",
+ .ldap_oid = "1.3.6.1.4.1.1466.115.121.1.38",
+ .oMSyntax = 6,
+ .attributeSyntax_oid = "2.5.5.2",
+ .drsuapi_to_ldb = dsdb_syntax_OID_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_OID_ldb_to_drsuapi,
+ .validate_ldb = dsdb_syntax_OID_validate_ldb,
+ .equality = "caseIgnoreMatch", /* Would use "objectIdentifierMatch" but most are ldap attribute/class names */
+ .comment = "OID String",
+ .ldb_syntax = LDB_SYNTAX_DIRECTORY_STRING
+ },{
+ .name = "Enumeration",
+ .ldap_oid = LDB_SYNTAX_INTEGER,
+ .oMSyntax = 10,
+ .attributeSyntax_oid = "2.5.5.9",
+ .drsuapi_to_ldb = dsdb_syntax_INT32_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_INT32_ldb_to_drsuapi,
+ .validate_ldb = dsdb_syntax_INT32_validate_ldb,
+ .ldb_syntax = LDB_SYNTAX_SAMBA_INT32,
+ .auto_normalise = true
+ },{
+ /* not used in w2k3 forest */
+ .name = "String(Numeric)",
+ .ldap_oid = "1.3.6.1.4.1.1466.115.121.1.36",
+ .oMSyntax = 18,
+ .attributeSyntax_oid = "2.5.5.6",
+ .drsuapi_to_ldb = dsdb_syntax_DATA_BLOB_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DATA_BLOB_ldb_to_drsuapi,
+ .validate_ldb = dsdb_syntax_DATA_BLOB_validate_ldb,
+ .equality = "numericStringMatch",
+ .substring = "numericStringSubstringsMatch",
+ .comment = "Numeric String",
+ .ldb_syntax = LDB_SYNTAX_DIRECTORY_STRING,
+ },{
+ .name = "String(Printable)",
+ .ldap_oid = "1.3.6.1.4.1.1466.115.121.1.44",
+ .oMSyntax = 19,
+ .attributeSyntax_oid = "2.5.5.5",
+ .drsuapi_to_ldb = dsdb_syntax_DATA_BLOB_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DATA_BLOB_ldb_to_drsuapi,
+ .validate_ldb = dsdb_syntax_DATA_BLOB_validate_ldb,
+ .ldb_syntax = LDB_SYNTAX_SAMBA_OCTET_STRING,
+ },{
+ .name = "String(Teletex)",
+ .ldap_oid = "1.2.840.113556.1.4.905",
+ .oMSyntax = 20,
+ .attributeSyntax_oid = "2.5.5.4",
+ .drsuapi_to_ldb = dsdb_syntax_DATA_BLOB_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DATA_BLOB_ldb_to_drsuapi,
+ .validate_ldb = dsdb_syntax_DATA_BLOB_validate_ldb,
+ .equality = "caseIgnoreMatch",
+ .substring = "caseIgnoreSubstringsMatch",
+ .comment = "Case Insensitive String",
+ .ldb_syntax = LDB_SYNTAX_DIRECTORY_STRING,
+ },{
+ .name = "String(IA5)",
+ .ldap_oid = "1.3.6.1.4.1.1466.115.121.1.26",
+ .oMSyntax = 22,
+ .attributeSyntax_oid = "2.5.5.5",
+ .drsuapi_to_ldb = dsdb_syntax_DATA_BLOB_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DATA_BLOB_ldb_to_drsuapi,
+ .validate_ldb = dsdb_syntax_DATA_BLOB_validate_ldb,
+ .equality = "caseExactIA5Match",
+ .comment = "Printable String",
+ .ldb_syntax = LDB_SYNTAX_SAMBA_OCTET_STRING,
+ },{
+ .name = "String(UTC-Time)",
+ .ldap_oid = "1.3.6.1.4.1.1466.115.121.1.53",
+ .oMSyntax = 23,
+ .attributeSyntax_oid = "2.5.5.11",
+ .drsuapi_to_ldb = dsdb_syntax_NTTIME_UTC_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_NTTIME_UTC_ldb_to_drsuapi,
+ .validate_ldb = dsdb_syntax_NTTIME_UTC_validate_ldb,
+ .equality = "generalizedTimeMatch",
+ .comment = "UTC Time",
+ .auto_normalise = true
+ },{
+ .name = "String(Generalized-Time)",
+ .ldap_oid = "1.3.6.1.4.1.1466.115.121.1.24",
+ .oMSyntax = 24,
+ .attributeSyntax_oid = "2.5.5.11",
+ .drsuapi_to_ldb = dsdb_syntax_NTTIME_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_NTTIME_ldb_to_drsuapi,
+ .validate_ldb = dsdb_syntax_NTTIME_validate_ldb,
+ .equality = "generalizedTimeMatch",
+ .comment = "Generalized Time",
+ .auto_normalise = true
+ },{
+ /* not used in w2k3 schema */
+ .name = "String(Case Sensitive)",
+ .ldap_oid = "1.2.840.113556.1.4.1362",
+ .oMSyntax = 27,
+ .attributeSyntax_oid = "2.5.5.3",
+ .drsuapi_to_ldb = dsdb_syntax_DATA_BLOB_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DATA_BLOB_ldb_to_drsuapi,
+ .validate_ldb = dsdb_syntax_DATA_BLOB_validate_ldb,
+ .equality = "caseExactMatch",
+ .substring = "caseExactSubstringsMatch",
+ /* TODO (kim): according to LDAP rfc we should be using same comparison
+ * as Directory String (LDB_SYNTAX_DIRECTORY_STRING), but case sensitive.
+ * But according to ms docs binary compare should do the job:
+ * http://msdn.microsoft.com/en-us/library/cc223200(v=PROT.10).aspx */
+ .ldb_syntax = LDB_SYNTAX_SAMBA_OCTET_STRING,
+ },{
+ .name = "String(Unicode)",
+ .ldap_oid = LDB_SYNTAX_DIRECTORY_STRING,
+ .oMSyntax = 64,
+ .attributeSyntax_oid = "2.5.5.12",
+ .drsuapi_to_ldb = dsdb_syntax_UNICODE_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_UNICODE_ldb_to_drsuapi,
+ .validate_ldb = dsdb_syntax_UNICODE_validate_ldb,
+ .equality = "caseIgnoreMatch",
+ .substring = "caseIgnoreSubstringsMatch",
+ .comment = "Directory String",
+ },{
+ .name = "Interval/LargeInteger",
+ .ldap_oid = "1.2.840.113556.1.4.906",
+ .oMSyntax = 65,
+ .attributeSyntax_oid = "2.5.5.16",
+ .drsuapi_to_ldb = dsdb_syntax_INT64_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_INT64_ldb_to_drsuapi,
+ .validate_ldb = dsdb_syntax_INT64_validate_ldb,
+ .equality = "integerMatch",
+ .comment = "Large Integer",
+ .ldb_syntax = LDB_SYNTAX_ORDERED_INTEGER,
+ .auto_normalise = true
+ },{
+ .name = "String(NT-Sec-Desc)",
+ .ldap_oid = LDB_SYNTAX_SAMBA_SECURITY_DESCRIPTOR,
+ .oMSyntax = 66,
+ .attributeSyntax_oid = "2.5.5.15",
+ .drsuapi_to_ldb = dsdb_syntax_DATA_BLOB_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DATA_BLOB_ldb_to_drsuapi,
+ .validate_ldb = dsdb_syntax_DATA_BLOB_validate_ldb,
+ },{
+ .name = "Object(DS-DN)",
+ .ldap_oid = LDB_SYNTAX_DN,
+ .oMSyntax = 127,
+ .oMObjectClass = OMOBJECTCLASS("\x2b\x0c\x02\x87\x73\x1c\x00\x85\x4a"),
+ .attributeSyntax_oid = "2.5.5.1",
+ .drsuapi_to_ldb = dsdb_syntax_DN_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DN_ldb_to_drsuapi,
+ .validate_ldb = dsdb_syntax_DN_validate_ldb,
+ .equality = "distinguishedNameMatch",
+ .comment = "Object(DS-DN) == a DN",
+ },{
+ .name = "Object(DN-Binary)",
+ .ldap_oid = DSDB_SYNTAX_BINARY_DN,
+ .oMSyntax = 127,
+ .oMObjectClass = OMOBJECTCLASS("\x2a\x86\x48\x86\xf7\x14\x01\x01\x01\x0b"),
+ .attributeSyntax_oid = "2.5.5.7",
+ .drsuapi_to_ldb = dsdb_syntax_DN_BINARY_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DN_BINARY_ldb_to_drsuapi,
+ .validate_ldb = dsdb_syntax_DN_BINARY_validate_ldb,
+ .equality = "octetStringMatch",
+ .comment = "OctetString: Binary+DN",
+ },{
+ /* not used in w2k3 schema, but used in Exchange schema*/
+ .name = "Object(OR-Name)",
+ .ldap_oid = DSDB_SYNTAX_OR_NAME,
+ .oMSyntax = 127,
+ .oMObjectClass = OMOBJECTCLASS("\x56\x06\x01\x02\x05\x0b\x1D"),
+ .attributeSyntax_oid = "2.5.5.7",
+ .drsuapi_to_ldb = dsdb_syntax_DN_BINARY_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DN_BINARY_ldb_to_drsuapi,
+ .validate_ldb = dsdb_syntax_DN_validate_ldb,
+ .equality = "distinguishedNameMatch",
+ .ldb_syntax = LDB_SYNTAX_DN,
+ },{
+ /*
+ * TODO: verify if DATA_BLOB is correct here...!
+ *
+ * repsFrom and repsTo are the only attributes using
+ * this attribute syntax, but they're not replicated...
+ */
+ .name = "Object(Replica-Link)",
+ .ldap_oid = "1.3.6.1.4.1.1466.115.121.1.40",
+ .oMSyntax = 127,
+ .oMObjectClass = OMOBJECTCLASS("\x2a\x86\x48\x86\xf7\x14\x01\x01\x01\x06"),
+ .attributeSyntax_oid = "2.5.5.10",
+ .drsuapi_to_ldb = dsdb_syntax_DATA_BLOB_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DATA_BLOB_ldb_to_drsuapi,
+ .validate_ldb = dsdb_syntax_DATA_BLOB_validate_ldb,
+ },{
+ .name = "Object(Presentation-Address)",
+ .ldap_oid = "1.3.6.1.4.1.1466.115.121.1.43",
+ .oMSyntax = 127,
+ .oMObjectClass = OMOBJECTCLASS("\x2b\x0c\x02\x87\x73\x1c\x00\x85\x5c"),
+ .attributeSyntax_oid = "2.5.5.13",
+ .drsuapi_to_ldb = dsdb_syntax_PRESENTATION_ADDRESS_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_PRESENTATION_ADDRESS_ldb_to_drsuapi,
+ .validate_ldb = dsdb_syntax_PRESENTATION_ADDRESS_validate_ldb,
+ .comment = "Presentation Address",
+ .ldb_syntax = LDB_SYNTAX_DIRECTORY_STRING,
+ },{
+ /* not used in w2k3 schema */
+ .name = "Object(Access-Point)",
+ .ldap_oid = DSDB_SYNTAX_ACCESS_POINT,
+ .oMSyntax = 127,
+ .oMObjectClass = OMOBJECTCLASS("\x2b\x0c\x02\x87\x73\x1c\x00\x85\x3e"),
+ .attributeSyntax_oid = "2.5.5.14",
+ .drsuapi_to_ldb = dsdb_syntax_FOOBAR_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_FOOBAR_ldb_to_drsuapi,
+ .validate_ldb = dsdb_syntax_FOOBAR_validate_ldb,
+ .ldb_syntax = LDB_SYNTAX_DIRECTORY_STRING,
+ },{
+ /* not used in w2k3 schema */
+ .name = "Object(DN-String)",
+ .ldap_oid = DSDB_SYNTAX_STRING_DN,
+ .oMSyntax = 127,
+ .oMObjectClass = OMOBJECTCLASS("\x2a\x86\x48\x86\xf7\x14\x01\x01\x01\x0c"),
+ .attributeSyntax_oid = "2.5.5.14",
+ .drsuapi_to_ldb = dsdb_syntax_DN_STRING_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DN_STRING_ldb_to_drsuapi,
+ .validate_ldb = dsdb_syntax_DN_STRING_validate_ldb,
+ .equality = "octetStringMatch",
+ .comment = "OctetString: String+DN",
+ }
+};
+
+const struct dsdb_syntax *find_syntax_map_by_ad_oid(const char *ad_oid)
+{
+ unsigned int i;
+ for (i=0; i < ARRAY_SIZE(dsdb_syntaxes); i++) {
+ if (strcasecmp(ad_oid, dsdb_syntaxes[i].attributeSyntax_oid) == 0) {
+ return &dsdb_syntaxes[i];
+ }
+ }
+ return NULL;
+}
+
+const struct dsdb_syntax *find_syntax_map_by_ad_syntax(int oMSyntax)
+{
+ unsigned int i;
+ for (i=0; i < ARRAY_SIZE(dsdb_syntaxes); i++) {
+ if (oMSyntax == dsdb_syntaxes[i].oMSyntax) {
+ return &dsdb_syntaxes[i];
+ }
+ }
+ return NULL;
+}
+
+const struct dsdb_syntax *find_syntax_map_by_standard_oid(const char *standard_oid)
+{
+ unsigned int i;
+ for (i=0; i < ARRAY_SIZE(dsdb_syntaxes); i++) {
+ if (strcasecmp(standard_oid, dsdb_syntaxes[i].ldap_oid) == 0) {
+ return &dsdb_syntaxes[i];
+ }
+ }
+ return NULL;
+}
+
+const struct dsdb_syntax *dsdb_syntax_for_attribute(const struct dsdb_attribute *attr)
+{
+ unsigned int i;
+
+ for (i=0; i < ARRAY_SIZE(dsdb_syntaxes); i++) {
+ /*
+ * We must pretend that userParameters was declared
+ * binary string, so we can store the 'UTF16' (not
+ * really string) structure as given over SAMR to samba
+ */
+ if (dsdb_syntaxes[i].userParameters &&
+ (strcasecmp(attr->lDAPDisplayName, "userParameters") == 0))
+ {
+ return &dsdb_syntaxes[i];
+ }
+ if (attr->oMSyntax != dsdb_syntaxes[i].oMSyntax) continue;
+
+ if (attr->oMObjectClass.length != dsdb_syntaxes[i].oMObjectClass.length) continue;
+
+ if (attr->oMObjectClass.length) {
+ int ret;
+ ret = memcmp(attr->oMObjectClass.data,
+ dsdb_syntaxes[i].oMObjectClass.data,
+ attr->oMObjectClass.length);
+ if (ret != 0) continue;
+ }
+
+ if (strcmp(attr->attributeSyntax_oid, dsdb_syntaxes[i].attributeSyntax_oid) != 0) continue;
+
+ return &dsdb_syntaxes[i];
+ }
+
+ return NULL;
+}
+
+WERROR dsdb_attribute_drsuapi_remote_to_local(const struct dsdb_syntax_ctx *ctx,
+ enum drsuapi_DsAttributeId remote_attid_as_enum,
+ enum drsuapi_DsAttributeId *local_attid_as_enum,
+ const struct dsdb_attribute **_sa)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ const struct dsdb_attribute *sa;
+ uint32_t attid_local;
+ bool ok;
+
+ if (ctx->pfm_remote == NULL) {
+ smb_panic(__location__);
+ }
+
+ switch (dsdb_pfm_get_attid_type(remote_attid_as_enum)) {
+ case DSDB_ATTID_TYPE_PFM:
+ /* map remote ATTID to local ATTID */
+ ok = dsdb_syntax_attid_from_remote_attid(ctx, frame,
+ remote_attid_as_enum,
+ &attid_local);
+ if (!ok) {
+ DEBUG(0,(__location__ ": Can't find local ATTID for 0x%08X\n",
+ remote_attid_as_enum));
+ TALLOC_FREE(frame);
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+ break;
+ case DSDB_ATTID_TYPE_INTID:
+ /* use IntId value directly */
+ attid_local = remote_attid_as_enum;
+ break;
+ default:
+ /* we should never get here */
+ DEBUG(0,(__location__ ": Invalid ATTID type passed for conversion - 0x%08X\n",
+ remote_attid_as_enum));
+ TALLOC_FREE(frame);
+ return WERR_INVALID_PARAMETER;
+ }
+
+ sa = dsdb_attribute_by_attributeID_id(ctx->schema, attid_local);
+ if (!sa) {
+ int dbg_level = ctx->schema->resolving_in_progress ? 10 : 0;
+ DEBUG(dbg_level,(__location__ ": Unknown local attributeID_id 0x%08X remote 0x%08X%s\n",
+ attid_local, remote_attid_as_enum,
+ ctx->schema->resolving_in_progress ? "resolving in progress" : ""));
+ TALLOC_FREE(frame);
+ return WERR_DS_ATT_NOT_DEF_IN_SCHEMA;
+ }
+
+ /*
+ * We return the same class of attid as we were given. That
+ * is, we trust the remote server not to use an
+ * msDS-IntId value in the schema partition
+ */
+ if (local_attid_as_enum != NULL) {
+ *local_attid_as_enum = (enum drsuapi_DsAttributeId)attid_local;
+ }
+
+ if (_sa != NULL) {
+ *_sa = sa;
+ }
+
+ TALLOC_FREE(frame);
+ return WERR_OK;
+}
+
+WERROR dsdb_attribute_drsuapi_to_ldb(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_schema_prefixmap *pfm_remote,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out,
+ enum drsuapi_DsAttributeId *local_attid_as_enum)
+{
+ struct dsdb_syntax_ctx syntax_ctx;
+ const struct dsdb_attribute *sa = NULL;
+ WERROR werr;
+
+ /* use default syntax conversion context */
+ dsdb_syntax_ctx_init(&syntax_ctx, ldb, schema);
+ syntax_ctx.pfm_remote = pfm_remote;
+
+ werr = dsdb_attribute_drsuapi_remote_to_local(&syntax_ctx,
+ in->attid,
+ local_attid_as_enum,
+ &sa);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ return sa->syntax->drsuapi_to_ldb(&syntax_ctx, sa, in, mem_ctx, out);
+}
diff --git a/source4/dsdb/schema/tests/schema_syntax.c b/source4/dsdb/schema/tests/schema_syntax.c
new file mode 100644
index 0000000..7eba102
--- /dev/null
+++ b/source4/dsdb/schema/tests/schema_syntax.c
@@ -0,0 +1,271 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Test DSDB syntax functions
+
+ Copyright (C) Andrew Bartlet <abartlet@samba.org> 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
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include <ldb.h>
+#include <ldb_errors.h>
+#include "lib/ldb-samba/ldif_handlers.h"
+#include "ldb_wrap.h"
+#include "dsdb/samdb/samdb.h"
+#include "param/param.h"
+#include "torture/smbtorture.h"
+#include "torture/local/proto.h"
+#include "param/provision.h"
+
+
+struct torture_dsdb_syntax {
+ struct ldb_context *ldb;
+ struct dsdb_schema *schema;
+};
+
+DATA_BLOB hexstr_to_data_blob(TALLOC_CTX *mem_ctx, const char *string)
+{
+ DATA_BLOB binary = data_blob_talloc(mem_ctx, NULL, strlen(string)/2);
+ binary.length = strhex_to_str((char *)binary.data, binary.length, string, strlen(string));
+ return binary;
+}
+
+static bool torture_syntax_add_OR_Name(struct torture_context *tctx,
+ struct ldb_context *ldb,
+ struct dsdb_schema *schema)
+{
+ WERROR werr;
+ int ldb_res;
+ struct ldb_ldif *ldif;
+ const char *ldif_str = "dn: CN=ms-Exch-Auth-Orig,CN=Schema,CN=Configuration,DC=kma-exch,DC=devel\n"
+ "changetype: add\n"
+ "cn: ms-Exch-Auth-Orig\n"
+ "attributeID: 1.2.840.113556.1.2.129\n"
+ "attributeSyntax: 2.5.5.7\n"
+ "isSingleValued: FALSE\n"
+ "linkID: 110\n"
+ "showInAdvancedViewOnly: TRUE\n"
+ "adminDisplayName: ms-Exch-Auth-Orig\n"
+ "oMObjectClass:: VgYBAgULHQ==\n"
+ "adminDescription: ms-Exch-Auth-Orig\n"
+ "oMSyntax: 127\n"
+ "searchFlags: 16\n"
+ "lDAPDisplayName: authOrig\n"
+ "name: ms-Exch-Auth-Orig\n"
+ "objectGUID:: 7tqEWktjAUqsZXqsFPQpRg==\n"
+ "schemaIDGUID:: l3PfqOrF0RG7ywCAx2ZwwA==\n"
+ "attributeSecurityGUID:: VAGN5Pi80RGHAgDAT7lgUA==\n"
+ "isMemberOfPartialAttributeSet: TRUE\n";
+
+ ldif = ldb_ldif_read_string(ldb, &ldif_str);
+ torture_assert(tctx, ldif, "Failed to parse LDIF for authOrig");
+
+ werr = dsdb_set_attribute_from_ldb(ldb, schema, ldif->msg);
+ ldb_ldif_read_free(ldb, ldif);
+ torture_assert_werr_ok(tctx, werr, "dsdb_set_attribute_from_ldb() failed!");
+
+ ldb_res = dsdb_set_schema(ldb, schema, SCHEMA_WRITE);
+ torture_assert_int_equal(tctx, ldb_res, LDB_SUCCESS, "dsdb_set_schema() failed");
+
+ return true;
+};
+
+static bool torture_test_syntax(struct torture_context *torture,
+ struct torture_dsdb_syntax *priv,
+ const char *oid,
+ const char *attr_string,
+ const char *ldb_str,
+ const char *drs_str)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(torture);
+ DATA_BLOB drs_binary = hexstr_to_data_blob(tmp_ctx, drs_str);
+ DATA_BLOB ldb_blob = data_blob_string_const(ldb_str);
+ struct drsuapi_DsReplicaAttribute drs, drs2;
+ struct drsuapi_DsAttributeValue val;
+ const struct dsdb_syntax *syntax;
+ const struct dsdb_attribute *attr;
+ struct ldb_message_element el;
+ struct ldb_context *ldb = priv->ldb;
+ struct dsdb_schema *schema = priv->schema;
+ struct dsdb_syntax_ctx syntax_ctx;
+
+ /* use default syntax conversion context */
+ dsdb_syntax_ctx_init(&syntax_ctx, ldb, schema);
+
+ drs.value_ctr.num_values = 1;
+ drs.value_ctr.values = &val;
+ val.blob = &drs_binary;
+
+ torture_assert(torture, syntax = find_syntax_map_by_standard_oid(oid), "Failed to find syntax handler");
+ torture_assert(torture, attr = dsdb_attribute_by_lDAPDisplayName(schema, attr_string), "Failed to find attribute handler");
+ torture_assert_str_equal(torture, attr->syntax->name, syntax->name, "Syntax from schema not as expected");
+
+
+ torture_assert_werr_ok(torture, syntax->drsuapi_to_ldb(&syntax_ctx, attr, &drs, tmp_ctx, &el), "Failed to convert from DRS to ldb format");
+
+ torture_assert_data_blob_equal(torture, el.values[0], ldb_blob, "Incorrect conversion from DRS to ldb format");
+
+ torture_assert_werr_ok(torture, syntax->validate_ldb(&syntax_ctx, attr, &el), "Failed to validate ldb format");
+
+ torture_assert_werr_ok(torture, syntax->ldb_to_drsuapi(&syntax_ctx, attr, &el, tmp_ctx, &drs2), "Failed to convert from ldb to DRS format");
+
+ torture_assert(torture, drs2.value_ctr.values[0].blob, "No blob returned from conversion");
+
+ torture_assert_data_blob_equal(torture, *drs2.value_ctr.values[0].blob, drs_binary, "Incorrect conversion from ldb to DRS format");
+ return true;
+}
+
+static bool torture_dsdb_drs_DN_BINARY(struct torture_context *torture, struct torture_dsdb_syntax *priv)
+{
+ bool ret;
+ const char *ldb_str = "B:32:A9D1CA15768811D1ADED00C04FD8D5CD:<GUID=a8378c29-6319-45b3-b216-6a3108452d6c>;CN=Users,DC=ad,DC=ruth,DC=abartlet,DC=net";
+ const char *drs_str = "8C00000000000000298C37A81963B345B2166A3108452D6C000000000000000000000000000000000000000000000000000000002900000043004E003D00550073006500720073002C00440043003D00610064002C00440043003D0072007500740068002C00440043003D00610062006100720074006C00650074002C00440043003D006E0065007400000014000000A9D1CA15768811D1ADED00C04FD8D5CD";
+ const char *ldb_str2 = "B:8:00000002:<GUID=2b475208-3180-4ad4-b6bb-a26cfb44ac50>;<SID=S-1-5-21-3686369990-3108025515-1819299124>;DC=ad,DC=ruth,DC=abartlet,DC=net";
+ const char *drs_str2 = "7A000000180000000852472B8031D44AB6BBA26CFB44AC50010400000000000515000000C68AB9DBABB440B9344D706C0000000020000000440043003D00610064002C00440043003D0072007500740068002C00440043003D00610062006100720074006C00650074002C00440043003D006E0065007400000000000800000000000002";
+ ret = torture_test_syntax(torture, priv, DSDB_SYNTAX_BINARY_DN, "wellKnownObjects", ldb_str, drs_str);
+ if (!ret) return false;
+ return torture_test_syntax(torture, priv, DSDB_SYNTAX_BINARY_DN, "msDS-HasInstantiatedNCs", ldb_str2, drs_str2);
+}
+
+static bool torture_dsdb_drs_DN(struct torture_context *torture, struct torture_dsdb_syntax *priv)
+{
+ const char *ldb_str = "<GUID=fbee08fd-6f75-4bd4-af3f-e4f063a6379e>;OU=Domain Controllers,DC=ad,DC=naomi,DC=abartlet,DC=net";
+ const char *drs_str = "A800000000000000FD08EEFB756FD44BAF3FE4F063A6379E00000000000000000000000000000000000000000000000000000000370000004F0055003D0044006F006D00610069006E00200043006F006E00740072006F006C006C006500720073002C00440043003D00610064002C00440043003D006E0061006F006D0069002C00440043003D00610062006100720074006C00650074002C00440043003D006E00650074000000";
+ if (!torture_test_syntax(torture, priv, LDB_SYNTAX_DN, "lastKnownParent", ldb_str, drs_str)) {
+ return false;
+ }
+
+ /* extended_dn with GUID and SID in it */
+ ldb_str = "<GUID=23cc7d16-3da0-4f3a-9921-0ad60a99230f>;<SID=S-1-5-21-3427639452-1671929926-2759570404-500>;CN=Administrator,CN=Users,DC=kma-exch,DC=devel";
+ drs_str = "960000001C000000167DCC23A03D3A4F99210AD60A99230F0105000000000005150000009CA04DCC46A0A763E4B37BA4F40100002E00000043004E003D00410064006D0069006E006900730074007200610074006F0072002C0043004E003D00550073006500720073002C00440043003D006B006D0061002D0065007800630068002C00440043003D0064006500760065006C000000";
+ return torture_test_syntax(torture, priv, LDB_SYNTAX_DN, "lastKnownParent", ldb_str, drs_str);
+}
+
+static bool torture_dsdb_drs_OR_Name(struct torture_context *torture, struct torture_dsdb_syntax *priv)
+{
+ const char *ldb_str = "<GUID=23cc7d16-3da0-4f3a-9921-0ad60a99230f>;<SID=S-1-5-21-3427639452-1671929926-2759570404-500>;CN=Administrator,CN=Users,DC=kma-exch,DC=devel";
+ const char *drs_str = "960000001C000000167DCC23A03D3A4F99210AD60A99230F0105000000000005150000009CA04DCC46A0A763E4B37BA4F40100002E00000043004E003D00410064006D0069006E006900730074007200610074006F0072002C0043004E003D00550073006500720073002C00440043003D006B006D0061002D0065007800630068002C00440043003D0064006500760065006C000000000004000000";
+ return torture_test_syntax(torture, priv, DSDB_SYNTAX_OR_NAME, "authOrig", ldb_str, drs_str);
+}
+
+static bool torture_dsdb_drs_INT32(struct torture_context *torture, struct torture_dsdb_syntax *priv)
+{
+ const char *ldb_str = "532480";
+ const char *drs_str = "00200800";
+ return torture_test_syntax(torture, priv, LDB_SYNTAX_INTEGER, "userAccountControl", ldb_str, drs_str);
+}
+
+static bool torture_dsdb_drs_INT64(struct torture_context *torture, struct torture_dsdb_syntax *priv)
+{
+ const char *ldb_str = "129022979538281250";
+ const char *drs_str = "22E33D5FB761CA01";
+ return torture_test_syntax(torture, priv, "1.2.840.113556.1.4.906", "pwdLastSet", ldb_str, drs_str);
+}
+
+static bool torture_dsdb_drs_NTTIME(struct torture_context *torture, struct torture_dsdb_syntax *priv)
+{
+ const char *ldb_str = "20091109003446.0Z";
+ const char *drs_str = "A6F4070103000000";
+ return torture_test_syntax(torture, priv, "1.3.6.1.4.1.1466.115.121.1.24", "whenCreated", ldb_str, drs_str);
+}
+
+static bool torture_dsdb_drs_BOOL(struct torture_context *torture, struct torture_dsdb_syntax *priv)
+{
+ const char *ldb_str = "TRUE";
+ const char *drs_str = "01000000";
+ return torture_test_syntax(torture, priv, LDB_SYNTAX_BOOLEAN, "isDeleted", ldb_str, drs_str);
+}
+
+static bool torture_dsdb_drs_UNICODE(struct torture_context *torture, struct torture_dsdb_syntax *priv)
+{
+ const char *ldb_str = "primaryTelexNumber,Numéro de télex";
+ const char *drs_str = "7000720069006D00610072007900540065006C00650078004E0075006D006200650072002C004E0075006D00E90072006F0020006400650020007400E9006C0065007800";
+ return torture_test_syntax(torture, priv, LDB_SYNTAX_DIRECTORY_STRING, "attributeDisplayNames", ldb_str, drs_str);
+}
+
+/*
+ * DSDB-SYNTAX fixture setup/teardown handlers implementation
+ */
+static bool torture_dsdb_syntax_tcase_setup(struct torture_context *tctx, void **data)
+{
+ struct torture_dsdb_syntax *priv;
+
+ priv = talloc_zero(tctx, struct torture_dsdb_syntax);
+ torture_assert(tctx, priv, "No memory");
+
+ priv->ldb = provision_get_schema(priv, tctx->lp_ctx, NULL, NULL);
+ torture_assert(tctx, priv->ldb, "Failed to load schema from disk");
+
+ priv->schema = dsdb_get_schema(priv->ldb, NULL);
+ torture_assert(tctx, priv->schema, "Failed to fetch schema");
+
+ /* add 'authOrig' attribute with OR-Name syntax to schema */
+ if (!torture_syntax_add_OR_Name(tctx, priv->ldb, priv->schema)) {
+ return false;
+ }
+
+ *data = priv;
+ return true;
+}
+
+static bool torture_dsdb_syntax_tcase_teardown(struct torture_context *tctx, void *data)
+{
+ struct torture_dsdb_syntax *priv;
+
+ priv = talloc_get_type_abort(data, struct torture_dsdb_syntax);
+ talloc_unlink(priv, priv->ldb);
+ talloc_free(priv);
+
+ return true;
+}
+
+/**
+ * DSDB-SYNTAX test suite creation
+ */
+struct torture_suite *torture_dsdb_syntax(TALLOC_CTX *mem_ctx)
+{
+ typedef bool (*pfn_run)(struct torture_context *, void *);
+
+ struct torture_tcase *tc;
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "dsdb.syntax");
+
+ if (suite == NULL) {
+ return NULL;
+ }
+
+ tc = torture_suite_add_tcase(suite, "tc");
+ if (!tc) {
+ return NULL;
+ }
+
+ torture_tcase_set_fixture(tc,
+ torture_dsdb_syntax_tcase_setup,
+ torture_dsdb_syntax_tcase_teardown);
+
+ torture_tcase_add_simple_test(tc, "DN-BINARY", (pfn_run)torture_dsdb_drs_DN_BINARY);
+ torture_tcase_add_simple_test(tc, "DN", (pfn_run)torture_dsdb_drs_DN);
+ torture_tcase_add_simple_test(tc, "OR-Name", (pfn_run)torture_dsdb_drs_OR_Name);
+ torture_tcase_add_simple_test(tc, "INT32", (pfn_run)torture_dsdb_drs_INT32);
+ torture_tcase_add_simple_test(tc, "INT64", (pfn_run)torture_dsdb_drs_INT64);
+ torture_tcase_add_simple_test(tc, "NTTIME", (pfn_run)torture_dsdb_drs_NTTIME);
+ torture_tcase_add_simple_test(tc, "BOOL", (pfn_run)torture_dsdb_drs_BOOL);
+ torture_tcase_add_simple_test(tc, "UNICODE", (pfn_run)torture_dsdb_drs_UNICODE);
+
+ suite->description = talloc_strdup(suite, "DSDB syntax tests");
+
+ return suite;
+}