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/>.
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
32 * This means that sorted links will be in the same order as a new DC would
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
));
40 struct ldb_val v2
= data_blob_const(v2_data
, sizeof(v2_data
));
43 ndr_push_struct_into_fixed_blob(&v1
, guid1
,
44 (ndr_push_flags_fn_t
)ndr_push_GUID
);
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
,
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
,
70 if (ret
!= LDB_SUCCESS
) {
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
));
84 return data_blob_cmp(&ctx
->extra_part
,
85 &p
->dsdb_dn
->extra_part
);
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
)
98 struct dsdb_dn
*dsdb_dn
= dsdb_dn_parse_trusted(mem_ctx
, ldb
, pdn
->v
,
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
;
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 */
118 *pdn
= talloc_zero_array(mem_ctx
, struct parsed_dn
,
121 return LDB_ERR_OPERATIONS_ERROR
;
124 for (i
= 0; i
< el
->num_values
; i
++) {
125 (*pdn
)[i
].v
= &el
->values
[i
];
132 int parsed_dn_find(struct ldb_context
*ldb
, struct parsed_dn
*pdn
,
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
)
144 struct compare_ctx ctx
;
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.
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 "
171 return LDB_ERR_OPERATIONS_ERROR
;
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
++) {
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
);
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
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
)));
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
;
222 BINARY_ARRAY_SEARCH_GTE(pdn
, count
, &ctx
, la_guid_compare_with_trusted_dn
,