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/common/util_links.c | |
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 'source4/dsdb/common/util_links.c')
-rw-r--r-- | source4/dsdb/common/util_links.c | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/source4/dsdb/common/util_links.c b/source4/dsdb/common/util_links.c new file mode 100644 index 0000000..08fc2d6 --- /dev/null +++ b/source4/dsdb/common/util_links.c @@ -0,0 +1,229 @@ +/* + Unix SMB/CIFS implementation. + + Helpers to search for links in the DB + + Copyright (C) Catalyst.Net Ltd 2017 + + 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 "lib/util/binsearch.h" +#include "librpc/gen_ndr/ndr_misc.h" + +/* + * We choose, as the sort order, the same order as is used in DRS replication, + * which is the memcmp() order of the NDR GUID, not that obtained from + * GUID_compare(). + * + * This means that sorted links will be in the same order as a new DC would + * see them. + */ +int ndr_guid_compare(const struct GUID *guid1, const struct GUID *guid2) +{ + uint8_t v1_data[16] = { 0 }; + struct ldb_val v1 = data_blob_const(v1_data, sizeof(v1_data)); + uint8_t v2_data[16]; + struct ldb_val v2 = data_blob_const(v2_data, sizeof(v2_data)); + + /* This can't fail */ + ndr_push_struct_into_fixed_blob(&v1, guid1, + (ndr_push_flags_fn_t)ndr_push_GUID); + /* This can't fail */ + ndr_push_struct_into_fixed_blob(&v2, guid2, + (ndr_push_flags_fn_t)ndr_push_GUID); + return data_blob_cmp(&v1, &v2); +} + + +static int la_guid_compare_with_trusted_dn(struct compare_ctx *ctx, + struct parsed_dn *p) +{ + int cmp = 0; + /* + * This works like a standard compare function in its return values, + * but has an extra trick to deal with errors: zero is returned and + * ctx->err is set to the ldb error code. + * + * That is, if (as is expected in most cases) you get a non-zero + * result, you don't need to check for errors. + * + * We assume the second argument refers to a DN is from the database + * and has a GUID -- but this GUID might not have been parsed out yet. + */ + if (p->dsdb_dn == NULL) { + int ret = really_parse_trusted_dn(ctx->mem_ctx, ctx->ldb, p, + ctx->ldap_oid); + if (ret != LDB_SUCCESS) { + ctx->err = ret; + return 0; + } + } + cmp = ndr_guid_compare(ctx->guid, &p->guid); + if (cmp == 0 && ctx->compare_extra_part) { + if (ctx->partial_extra_part_length != 0) { + /* Allow a prefix match on the blob. */ + return memcmp(ctx->extra_part.data, + p->dsdb_dn->extra_part.data, + MIN(ctx->partial_extra_part_length, + p->dsdb_dn->extra_part.length)); + } else { + return data_blob_cmp(&ctx->extra_part, + &p->dsdb_dn->extra_part); + } + } + + return cmp; +} + +/* When a parsed_dn comes from the database, sometimes it is not really parsed. */ + +int really_parse_trusted_dn(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, + struct parsed_dn *pdn, const char *ldap_oid) +{ + NTSTATUS status; + struct dsdb_dn *dsdb_dn = dsdb_dn_parse_trusted(mem_ctx, ldb, pdn->v, + ldap_oid); + if (dsdb_dn == NULL) { + return LDB_ERR_INVALID_DN_SYNTAX; + } + + status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &pdn->guid, "GUID"); + if (!NT_STATUS_IS_OK(status)) { + return LDB_ERR_OPERATIONS_ERROR; + } + pdn->dsdb_dn = dsdb_dn; + return LDB_SUCCESS; +} + + +int get_parsed_dns_trusted(TALLOC_CTX *mem_ctx, struct ldb_message_element *el, + struct parsed_dn **pdn) +{ + /* Here we get a list of 'struct parsed_dns' without the parsing */ + unsigned int i; + *pdn = talloc_zero_array(mem_ctx, struct parsed_dn, + el->num_values); + if (!*pdn) { + return LDB_ERR_OPERATIONS_ERROR; + } + + for (i = 0; i < el->num_values; i++) { + (*pdn)[i].v = &el->values[i]; + } + + return LDB_SUCCESS; +} + + +int parsed_dn_find(struct ldb_context *ldb, struct parsed_dn *pdn, + unsigned int count, + const struct GUID *guid, + struct ldb_dn *target_dn, + DATA_BLOB extra_part, + size_t partial_extra_part_length, + struct parsed_dn **exact, + struct parsed_dn **next, + const char *ldap_oid, + bool compare_extra_part) +{ + unsigned int i; + struct compare_ctx ctx; + if (pdn == NULL) { + *exact = NULL; + *next = NULL; + return LDB_SUCCESS; + } + + if (unlikely(GUID_all_zero(guid))) { + /* + * When updating a link using DRS, we sometimes get a NULL + * GUID when a forward link has been deleted and its GUID has + * for some reason been forgotten. The best we can do is try + * and match by DN via a linear search. Note that this + * probably only happens in the ADD case, in which we only + * allow modification of link if it is already deleted, so + * this seems very close to an elaborate NO-OP, but we are not + * quite prepared to declare it so. + * + * If the DN is not in our list, we have to add it to the + * beginning of the list, where it would naturally sort. + */ + struct parsed_dn *p; + if (target_dn == NULL) { + /* We don't know the target DN, so we can't search for DN */ + DEBUG(1, ("parsed_dn_find has a NULL GUID for a linked " + "attribute but we don't have a DN to compare " + "it with\n")); + return LDB_ERR_OPERATIONS_ERROR; + } + *exact = NULL; + *next = NULL; + + DEBUG(3, ("parsed_dn_find has a NULL GUID for a link to DN " + "%s; searching through links for it", + ldb_dn_get_linearized(target_dn))); + + for (i = 0; i < count; i++) { + int cmp; + p = &pdn[i]; + if (p->dsdb_dn == NULL) { + int ret = really_parse_trusted_dn(pdn, ldb, p, ldap_oid); + if (ret != LDB_SUCCESS) { + return LDB_ERR_OPERATIONS_ERROR; + } + } + + cmp = ldb_dn_compare(p->dsdb_dn->dn, target_dn); + if (cmp == 0) { + *exact = p; + return LDB_SUCCESS; + } + } + /* + * Here we have a null guid which doesn't match any existing + * link. This is a bit unexpected because null guids occur + * when a forward link has been deleted and we are replicating + * that deletion. + * + * The best thing to do is weep into the logs and add the + * offending link to the beginning of the list which is + * at least the correct sort position. + */ + DEBUG(1, ("parsed_dn_find has been given a NULL GUID for a " + "link to unknown DN %s\n", + ldb_dn_get_linearized(target_dn))); + *next = pdn; + return LDB_SUCCESS; + } + + ctx.guid = guid; + ctx.ldb = ldb; + ctx.mem_ctx = pdn; + ctx.ldap_oid = ldap_oid; + ctx.extra_part = extra_part; + ctx.partial_extra_part_length = partial_extra_part_length; + ctx.compare_extra_part = compare_extra_part; + ctx.err = 0; + + BINARY_ARRAY_SEARCH_GTE(pdn, count, &ctx, la_guid_compare_with_trusted_dn, + *exact, *next); + + if (ctx.err != 0) { + return ctx.err; + } + return LDB_SUCCESS; +} |