dsdb: Move parsed_dn_find into a common location
authorGarming Sam <garming@catalyst.net.nz>
Sun, 12 Mar 2017 23:14:23 +0000 (12:14 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Mon, 13 Mar 2017 04:10:12 +0000 (05:10 +0100)
Signed-off-by: Garming Sam <garming@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
source4/dsdb/common/util_links.c [new file with mode: 0644]
source4/dsdb/common/util_links.h [new file with mode: 0644]
source4/dsdb/samdb/ldb_modules/repl_meta_data.c
source4/dsdb/samdb/samdb.h
source4/dsdb/wscript_build

diff --git a/source4/dsdb/common/util_links.c b/source4/dsdb/common/util_links.c
new file mode 100644 (file)
index 0000000..8192da4
--- /dev/null
@@ -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 <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];
+       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 (file)
index 0000000..c9f6fa5
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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__ */
index 9597fc53fefe64d4e47cc9ea5b4d8eccd61a0d89..68277f32d28acc7b19dcbff4cc295983293b084f 100644 (file)
@@ -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
index a85d05c93ae2af79b3664584e64a8b873cf5cb28..1e427d0e78a9a94202927604ab8eda8be0221e9b 100644 (file)
@@ -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"
 
index 97e4207ef4b8fe776cb202caa34b7c2fcbcebcac..c7a4c161af11ad050cdb1d9ee1b816f1e02ba486 100644 (file)
@@ -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'