util:datablob: data_blob_pad checks its alignment assumption
[samba4-gss.git] / source4 / dsdb / common / util_links.c
blobd41d1f2a88489f4f0011b93db97a9f9a91d30a47
1 /*
2 Unix SMB/CIFS implementation.
4 Helpers to search for links in the DB
6 Copyright (C) Catalyst.Net Ltd 2017
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include "dsdb/samdb/samdb.h"
24 #include "lib/util/binsearch.h"
25 #include "librpc/gen_ndr/ndr_misc.h"
28 * We choose, as the sort order, the same order as is used in DRS replication,
29 * which is the memcmp() order of the NDR GUID, not that obtained from
30 * GUID_compare().
32 * This means that sorted links will be in the same order as a new DC would
33 * see them.
35 int ndr_guid_compare(const struct GUID *guid1, const struct GUID *guid2)
37 uint8_t v1_data[16] = { 0 };
38 struct ldb_val v1 = data_blob_const(v1_data, sizeof(v1_data));
39 uint8_t v2_data[16];
40 struct ldb_val v2 = data_blob_const(v2_data, sizeof(v2_data));
42 /* This can't fail */
43 ndr_push_struct_into_fixed_blob(&v1, guid1,
44 (ndr_push_flags_fn_t)ndr_push_GUID);
45 /* This can't fail */
46 ndr_push_struct_into_fixed_blob(&v2, guid2,
47 (ndr_push_flags_fn_t)ndr_push_GUID);
48 return data_blob_cmp(&v1, &v2);
52 static int la_guid_compare_with_trusted_dn(struct compare_ctx *ctx,
53 struct parsed_dn *p)
55 int cmp = 0;
57 * This works like a standard compare function in its return values,
58 * but has an extra trick to deal with errors: zero is returned and
59 * ctx->err is set to the ldb error code.
61 * That is, if (as is expected in most cases) you get a non-zero
62 * result, you don't need to check for errors.
64 * We assume the second argument refers to a DN is from the database
65 * and has a GUID -- but this GUID might not have been parsed out yet.
67 if (p->dsdb_dn == NULL) {
68 int ret = really_parse_trusted_dn(ctx->mem_ctx, ctx->ldb, p,
69 ctx->ldap_oid);
70 if (ret != LDB_SUCCESS) {
71 ctx->err = ret;
72 return 0;
75 cmp = ndr_guid_compare(ctx->guid, &p->guid);
76 if (cmp == 0 && ctx->compare_extra_part) {
77 if (ctx->partial_extra_part_length != 0) {
78 /* Allow a prefix match on the blob. */
79 return memcmp(ctx->extra_part.data,
80 p->dsdb_dn->extra_part.data,
81 MIN(ctx->partial_extra_part_length,
82 p->dsdb_dn->extra_part.length));
83 } else {
84 return data_blob_cmp(&ctx->extra_part,
85 &p->dsdb_dn->extra_part);
89 return cmp;
92 /* When a parsed_dn comes from the database, sometimes it is not really parsed. */
94 int really_parse_trusted_dn(TALLOC_CTX *mem_ctx, struct ldb_context *ldb,
95 struct parsed_dn *pdn, const char *ldap_oid)
97 NTSTATUS status;
98 struct dsdb_dn *dsdb_dn = dsdb_dn_parse_trusted(mem_ctx, ldb, pdn->v,
99 ldap_oid);
100 if (dsdb_dn == NULL) {
101 return LDB_ERR_INVALID_DN_SYNTAX;
104 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &pdn->guid, "GUID");
105 if (!NT_STATUS_IS_OK(status)) {
106 return LDB_ERR_OPERATIONS_ERROR;
108 pdn->dsdb_dn = dsdb_dn;
109 return LDB_SUCCESS;
113 int get_parsed_dns_trusted(TALLOC_CTX *mem_ctx, struct ldb_message_element *el,
114 struct parsed_dn **pdn)
116 /* Here we get a list of 'struct parsed_dns' without the parsing */
117 unsigned int i;
118 *pdn = talloc_zero_array(mem_ctx, struct parsed_dn,
119 el->num_values);
120 if (!*pdn) {
121 return LDB_ERR_OPERATIONS_ERROR;
124 for (i = 0; i < el->num_values; i++) {
125 (*pdn)[i].v = &el->values[i];
128 return LDB_SUCCESS;
132 int parsed_dn_find(struct ldb_context *ldb, struct parsed_dn *pdn,
133 unsigned int count,
134 const struct GUID *guid,
135 struct ldb_dn *target_dn,
136 DATA_BLOB extra_part,
137 size_t partial_extra_part_length,
138 struct parsed_dn **exact,
139 struct parsed_dn **next,
140 const char *ldap_oid,
141 bool compare_extra_part)
143 unsigned int i;
144 struct compare_ctx ctx;
145 if (pdn == NULL) {
146 *exact = NULL;
147 *next = NULL;
148 return LDB_SUCCESS;
151 if (unlikely(GUID_all_zero(guid))) {
153 * When updating a link using DRS, we sometimes get a NULL
154 * GUID when a forward link has been deleted and its GUID has
155 * for some reason been forgotten. The best we can do is try
156 * and match by DN via a linear search. Note that this
157 * probably only happens in the ADD case, in which we only
158 * allow modification of link if it is already deleted, so
159 * this seems very close to an elaborate NO-OP, but we are not
160 * quite prepared to declare it so.
162 * If the DN is not in our list, we have to add it to the
163 * beginning of the list, where it would naturally sort.
165 struct parsed_dn *p;
166 if (target_dn == NULL) {
167 /* We don't know the target DN, so we can't search for DN */
168 DEBUG(1, ("parsed_dn_find has a NULL GUID for a linked "
169 "attribute but we don't have a DN to compare "
170 "it with\n"));
171 return LDB_ERR_OPERATIONS_ERROR;
173 *exact = NULL;
174 *next = NULL;
176 DEBUG(3, ("parsed_dn_find has a NULL GUID for a link to DN "
177 "%s; searching through links for it\n",
178 ldb_dn_get_linearized(target_dn)));
180 for (i = 0; i < count; i++) {
181 int cmp;
182 p = &pdn[i];
183 if (p->dsdb_dn == NULL) {
184 int ret = really_parse_trusted_dn(pdn, ldb, p, ldap_oid);
185 if (ret != LDB_SUCCESS) {
186 return LDB_ERR_OPERATIONS_ERROR;
190 cmp = ldb_dn_compare(p->dsdb_dn->dn, target_dn);
191 if (cmp == 0) {
192 *exact = p;
193 return LDB_SUCCESS;
197 * Here we have a null guid which doesn't match any existing
198 * link. This is a bit unexpected because null guids occur
199 * when a forward link has been deleted and we are replicating
200 * that deletion.
202 * The best thing to do is weep into the logs and add the
203 * offending link to the beginning of the list which is
204 * at least the correct sort position.
206 DEBUG(1, ("parsed_dn_find has been given a NULL GUID for a "
207 "link to unknown DN %s\n",
208 ldb_dn_get_linearized(target_dn)));
209 *next = pdn;
210 return LDB_SUCCESS;
213 ctx.guid = guid;
214 ctx.ldb = ldb;
215 ctx.mem_ctx = pdn;
216 ctx.ldap_oid = ldap_oid;
217 ctx.extra_part = extra_part;
218 ctx.partial_extra_part_length = partial_extra_part_length;
219 ctx.compare_extra_part = compare_extra_part;
220 ctx.err = 0;
222 BINARY_ARRAY_SEARCH_GTE(pdn, count, &ctx, la_guid_compare_with_trusted_dn,
223 *exact, *next);
225 if (ctx.err != 0) {
226 return ctx.err;
228 return LDB_SUCCESS;