From: Garming Sam Date: Sun, 12 Mar 2017 23:14:23 +0000 (+1300) Subject: dsdb: Move parsed_dn_find into a common location X-Git-Tag: tdb-1.3.13~478 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=608307745ea1d9ec41fafef89cf1f3e7b3680576;p=thirdparty%2Fsamba.git dsdb: Move parsed_dn_find into a common location Signed-off-by: Garming Sam Reviewed-by: Andrew Bartlett --- diff --git a/source4/dsdb/common/util_links.c b/source4/dsdb/common/util_links.c new file mode 100644 index 00000000000..8192da43245 --- /dev/null +++ b/source4/dsdb/common/util_links.c @@ -0,0 +1,199 @@ +/* + 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 . +*/ + +#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]; + 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) { + 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 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, + 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.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; +} diff --git a/source4/dsdb/common/util_links.h b/source4/dsdb/common/util_links.h new file mode 100644 index 00000000000..c9f6fa5852b --- /dev/null +++ b/source4/dsdb/common/util_links.h @@ -0,0 +1,42 @@ +/* + 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 . +*/ + +#ifndef __DSDB_COMMON_UTIL_LINKS_H__ +#define __DSDB_COMMON_UTIL_LINKS_H__ + +struct compare_ctx { + const struct GUID *guid; + struct ldb_context *ldb; + TALLOC_CTX *mem_ctx; + const char *ldap_oid; + int err; + const struct GUID *invocation_id; + DATA_BLOB extra_part; + bool compare_extra_part; +}; + +struct parsed_dn { + struct dsdb_dn *dsdb_dn; + struct GUID guid; + struct ldb_val *v; +}; + +#endif /* __DSDB_COMMON_UTIL_LINKS_H__ */ diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c index 9597fc53fef..68277f32d28 100644 --- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c +++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c @@ -39,6 +39,7 @@ #include "ldb_module.h" #include "dsdb/samdb/samdb.h" #include "dsdb/common/proto.h" +#include "dsdb/common/util.h" #include "../libds/common/flags.h" #include "librpc/gen_ndr/ndr_misc.h" #include "librpc/gen_ndr/ndr_drsuapi.h" @@ -47,7 +48,6 @@ #include "libcli/security/security.h" #include "lib/util/dlinklist.h" #include "dsdb/samdb/ldb_modules/util.h" -#include "lib/util/binsearch.h" #include "lib/util/tsort.h" /* @@ -106,12 +106,6 @@ struct replmd_replicated_request { bool isDeleted; }; -struct parsed_dn { - struct dsdb_dn *dsdb_dn; - struct GUID guid; - struct ldb_val *v; -}; - static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar); static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete); static int replmd_check_upgrade_links(struct ldb_context *ldb, @@ -1894,61 +1888,6 @@ static int replmd_update_rpmd(struct ldb_module *module, return LDB_SUCCESS; } -struct compare_ctx { - struct GUID *guid; - struct ldb_context *ldb; - TALLOC_CTX *mem_ctx; - const char *ldap_oid; - int err; - const struct GUID *invocation_id; - DATA_BLOB extra_part; - bool compare_extra_part; -}; - -/* When a parsed_dn comes from the database, sometimes it is not really parsed. */ - -static 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; -} - -/* - * 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. - */ -static int ndr_guid_compare(struct GUID *guid1, struct GUID *guid2) -{ - uint8_t v1_data[16]; - 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 parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2) { int ret = ndr_guid_compare(&pdn1->guid, &pdn2->guid); @@ -1959,136 +1898,6 @@ static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2) return ret; } -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) { - return data_blob_cmp(&ctx->extra_part, &p->dsdb_dn->extra_part); - } - - return cmp; -} - - - -static int parsed_dn_find(struct ldb_context *ldb, struct parsed_dn *pdn, - unsigned int count, - struct GUID *guid, - struct ldb_dn *target_dn, - DATA_BLOB extra_part, - 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.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; -} - /* get a series of message element values as an array of DNs and GUIDs the result is sorted by GUID diff --git a/source4/dsdb/samdb/samdb.h b/source4/dsdb/samdb/samdb.h index a85d05c93ae..1e427d0e78a 100644 --- a/source4/dsdb/samdb/samdb.h +++ b/source4/dsdb/samdb/samdb.h @@ -40,6 +40,7 @@ struct dsdb_trust_routing_table; #include "dsdb/schema/schema.h" #include "dsdb/samdb/samdb_proto.h" #include "dsdb/common/dsdb_dn.h" +#include "dsdb/common/util_links.h" #include "dsdb/common/proto.h" #include "../libds/common/flags.h" diff --git a/source4/dsdb/wscript_build b/source4/dsdb/wscript_build index 97e4207ef4b..c7a4c161af1 100644 --- a/source4/dsdb/wscript_build +++ b/source4/dsdb/wscript_build @@ -13,7 +13,7 @@ bld.SAMBA_LIBRARY('samdb', ) bld.SAMBA_LIBRARY('samdb-common', - source='common/util.c common/util_trusts.c common/util_groups.c common/util_samr.c common/dsdb_dn.c common/dsdb_access.c', + source='common/util.c common/util_trusts.c common/util_groups.c common/util_samr.c common/dsdb_dn.c common/dsdb_access.c common/util_links.c', autoproto='common/proto.h', private_library=True, deps='ldb NDR_DRSBLOBS util_ldb LIBCLI_AUTH samba-hostconfig samba_socket cli-ldap-common flag_mapping UTIL_RUNCMD'