Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : Helpers to search for links in the DB
5 :
6 : Copyright (C) Catalyst.Net Ltd 2017
7 :
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.
12 :
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.
17 :
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/>.
20 : */
21 :
22 : #include "includes.h"
23 : #include "dsdb/samdb/samdb.h"
24 : #include "lib/util/binsearch.h"
25 : #include "librpc/gen_ndr/ndr_misc.h"
26 :
27 : /*
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().
31 : *
32 : * This means that sorted links will be in the same order as a new DC would
33 : * see them.
34 : */
35 8547891 : int ndr_guid_compare(const struct GUID *guid1, const struct GUID *guid2)
36 : {
37 8547891 : uint8_t v1_data[16] = { 0 };
38 8547891 : struct ldb_val v1 = data_blob_const(v1_data, sizeof(v1_data));
39 3429 : uint8_t v2_data[16];
40 8547891 : struct ldb_val v2 = data_blob_const(v2_data, sizeof(v2_data));
41 :
42 : /* This can't fail */
43 8547891 : ndr_push_struct_into_fixed_blob(&v1, guid1,
44 : (ndr_push_flags_fn_t)ndr_push_GUID);
45 : /* This can't fail */
46 8547891 : ndr_push_struct_into_fixed_blob(&v2, guid2,
47 : (ndr_push_flags_fn_t)ndr_push_GUID);
48 8547891 : return data_blob_cmp(&v1, &v2);
49 : }
50 :
51 :
52 491852 : static int la_guid_compare_with_trusted_dn(struct compare_ctx *ctx,
53 : struct parsed_dn *p)
54 : {
55 491852 : int cmp = 0;
56 : /*
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.
60 : *
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.
63 : *
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.
66 : */
67 491852 : if (p->dsdb_dn == NULL) {
68 349483 : int ret = really_parse_trusted_dn(ctx->mem_ctx, ctx->ldb, p,
69 : ctx->ldap_oid);
70 349483 : if (ret != LDB_SUCCESS) {
71 0 : ctx->err = ret;
72 0 : return 0;
73 : }
74 : }
75 491852 : cmp = ndr_guid_compare(ctx->guid, &p->guid);
76 491852 : if (cmp == 0 && ctx->compare_extra_part) {
77 65616 : if (ctx->partial_extra_part_length != 0) {
78 : /* Allow a prefix match on the blob. */
79 1284 : return memcmp(ctx->extra_part.data,
80 1284 : p->dsdb_dn->extra_part.data,
81 1284 : MIN(ctx->partial_extra_part_length,
82 : p->dsdb_dn->extra_part.length));
83 : } else {
84 64332 : return data_blob_cmp(&ctx->extra_part,
85 64332 : &p->dsdb_dn->extra_part);
86 : }
87 : }
88 :
89 425962 : return cmp;
90 : }
91 :
92 : /* When a parsed_dn comes from the database, sometimes it is not really parsed. */
93 :
94 2603485 : int really_parse_trusted_dn(TALLOC_CTX *mem_ctx, struct ldb_context *ldb,
95 : struct parsed_dn *pdn, const char *ldap_oid)
96 : {
97 387 : NTSTATUS status;
98 2603485 : struct dsdb_dn *dsdb_dn = dsdb_dn_parse_trusted(mem_ctx, ldb, pdn->v,
99 : ldap_oid);
100 2603485 : if (dsdb_dn == NULL) {
101 0 : return LDB_ERR_INVALID_DN_SYNTAX;
102 : }
103 :
104 2603485 : status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &pdn->guid, "GUID");
105 2603485 : if (!NT_STATUS_IS_OK(status)) {
106 0 : return LDB_ERR_OPERATIONS_ERROR;
107 : }
108 2603485 : pdn->dsdb_dn = dsdb_dn;
109 2603485 : return LDB_SUCCESS;
110 : }
111 :
112 :
113 117530 : int get_parsed_dns_trusted(TALLOC_CTX *mem_ctx, struct ldb_message_element *el,
114 : struct parsed_dn **pdn)
115 : {
116 : /* Here we get a list of 'struct parsed_dns' without the parsing */
117 204 : unsigned int i;
118 117530 : *pdn = talloc_zero_array(mem_ctx, struct parsed_dn,
119 : el->num_values);
120 117530 : if (!*pdn) {
121 0 : return LDB_ERR_OPERATIONS_ERROR;
122 : }
123 :
124 9652677 : for (i = 0; i < el->num_values; i++) {
125 9535147 : (*pdn)[i].v = &el->values[i];
126 : }
127 :
128 117326 : return LDB_SUCCESS;
129 : }
130 :
131 :
132 106343 : 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)
142 : {
143 259 : unsigned int i;
144 259 : struct compare_ctx ctx;
145 106343 : if (pdn == NULL) {
146 3373 : *exact = NULL;
147 3373 : *next = NULL;
148 3373 : return LDB_SUCCESS;
149 : }
150 :
151 102970 : if (unlikely(GUID_all_zero(guid))) {
152 : /*
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.
161 : *
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.
164 : */
165 0 : struct parsed_dn *p;
166 0 : if (target_dn == NULL) {
167 : /* We don't know the target DN, so we can't search for DN */
168 0 : 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 0 : return LDB_ERR_OPERATIONS_ERROR;
172 : }
173 0 : *exact = NULL;
174 0 : *next = NULL;
175 :
176 0 : 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)));
179 :
180 0 : for (i = 0; i < count; i++) {
181 0 : int cmp;
182 0 : p = &pdn[i];
183 0 : if (p->dsdb_dn == NULL) {
184 0 : int ret = really_parse_trusted_dn(pdn, ldb, p, ldap_oid);
185 0 : if (ret != LDB_SUCCESS) {
186 0 : return LDB_ERR_OPERATIONS_ERROR;
187 : }
188 : }
189 :
190 0 : cmp = ldb_dn_compare(p->dsdb_dn->dn, target_dn);
191 0 : if (cmp == 0) {
192 0 : *exact = p;
193 0 : return LDB_SUCCESS;
194 : }
195 : }
196 : /*
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.
201 : *
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.
205 : */
206 0 : 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 0 : *next = pdn;
210 0 : return LDB_SUCCESS;
211 : }
212 :
213 102970 : ctx.guid = guid;
214 102970 : ctx.ldb = ldb;
215 102970 : ctx.mem_ctx = pdn;
216 102970 : ctx.ldap_oid = ldap_oid;
217 102970 : ctx.extra_part = extra_part;
218 102970 : ctx.partial_extra_part_length = partial_extra_part_length;
219 102970 : ctx.compare_extra_part = compare_extra_part;
220 102970 : ctx.err = 0;
221 :
222 594822 : BINARY_ARRAY_SEARCH_GTE(pdn, count, &ctx, la_guid_compare_with_trusted_dn,
223 : *exact, *next);
224 :
225 102970 : if (ctx.err != 0) {
226 0 : return ctx.err;
227 : }
228 102758 : return LDB_SUCCESS;
229 : }
|