diff options
Diffstat (limited to '')
-rw-r--r-- | source4/dsdb/schema/schema_prefixmap.c | 710 |
1 files changed, 710 insertions, 0 deletions
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; +} |