diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
commit | 4f5791ebd03eaec1c7da0865a383175b05102712 (patch) | |
tree | 8ce7b00f7a76baa386372422adebbe64510812d4 /source4/dsdb/schema | |
parent | Initial commit. (diff) | |
download | samba-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 '')
-rw-r--r-- | source4/dsdb/schema/dsdb_dn.c | 102 | ||||
-rw-r--r-- | source4/dsdb/schema/prefixmap.h | 54 | ||||
-rw-r--r-- | source4/dsdb/schema/schema.h | 349 | ||||
-rw-r--r-- | source4/dsdb/schema/schema_convert_to_ol.c | 381 | ||||
-rw-r--r-- | source4/dsdb/schema/schema_description.c | 397 | ||||
-rw-r--r-- | source4/dsdb/schema/schema_filtered.c | 101 | ||||
-rw-r--r-- | source4/dsdb/schema/schema_inferiors.c | 347 | ||||
-rw-r--r-- | source4/dsdb/schema/schema_info_attr.c | 235 | ||||
-rw-r--r-- | source4/dsdb/schema/schema_init.c | 1038 | ||||
-rw-r--r-- | source4/dsdb/schema/schema_prefixmap.c | 710 | ||||
-rw-r--r-- | source4/dsdb/schema/schema_query.c | 610 | ||||
-rw-r--r-- | source4/dsdb/schema/schema_set.c | 1140 | ||||
-rw-r--r-- | source4/dsdb/schema/schema_syntax.c | 2811 | ||||
-rw-r--r-- | source4/dsdb/schema/tests/schema_syntax.c | 271 |
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; +} |