4 Copyright (C) Simo Sorce 2005-2008
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007-2008
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 * Component: ldb extended dn control module
26 * Description: this module builds a special dn for returned search
27 * results nad creates the special DN in the backend store for new
30 * This also has the curious result that we convert <SID=S-1-2-345>
31 * in an attribute value into a normal DN for the rest of the stack
39 #include "ldb/include/ldb.h"
40 #include "ldb/include/ldb_errors.h"
41 #include "ldb/include/ldb_private.h"
42 #include "librpc/gen_ndr/ndr_misc.h"
43 #include "dsdb/samdb/samdb.h"
44 #include "libcli/security/security.h"
48 struct extended_dn_replace_list {
49 struct extended_dn_replace_list *next;
52 struct ldb_val *replace_dn;
53 struct extended_dn_context *ac;
54 struct ldb_request *search_req;
58 struct extended_dn_context {
59 const struct dsdb_schema *schema;
60 struct ldb_module *module;
61 struct ldb_request *req;
62 struct ldb_request *new_req;
64 struct extended_dn_replace_list *ops;
65 struct extended_dn_replace_list *cur;
69 static struct extended_dn_context *extended_dn_context_init(struct ldb_module *module,
70 struct ldb_request *req)
72 struct extended_dn_context *ac;
74 ac = talloc_zero(req, struct extended_dn_context);
80 ac->schema = dsdb_get_schema(module->ldb);
87 /* An extra layer of indirection because LDB does not allow the original request to be altered */
89 static int extended_final_callback(struct ldb_request *req, struct ldb_reply *ares)
91 int ret = LDB_ERR_OPERATIONS_ERROR;
92 struct extended_dn_context *ac;
93 ac = talloc_get_type(req->context, struct extended_dn_context);
95 if (ares->error != LDB_SUCCESS) {
96 ret = ldb_module_done(ac->req, ares->controls,
97 ares->response, ares->error);
100 case LDB_REPLY_ENTRY:
102 ret = ldb_module_send_entry(ac->req, ares->message);
104 case LDB_REPLY_REFERRAL:
106 ret = ldb_module_send_referral(ac->req, ares->referral);
110 ret = ldb_module_done(ac->req, ares->controls,
111 ares->response, ares->error);
118 static int extended_replace_dn(struct ldb_request *req, struct ldb_reply *ares)
120 struct extended_dn_replace_list *os = talloc_get_type(req->context,
121 struct extended_dn_replace_list);
124 return ldb_module_done(os->ac->req, NULL, NULL,
125 LDB_ERR_OPERATIONS_ERROR);
127 if (ares->error == LDB_ERR_NO_SUCH_OBJECT) {
128 /* Don't worry too much about dangling references */
130 ldb_reset_err_string(os->ac->module->ldb);
132 struct extended_dn_replace_list *next;
139 return ldb_next_request(os->ac->module, next->search_req);
141 /* Otherwise, we are done - let's run the
142 * request now we have swapped the DNs for the
144 return ldb_next_request(os->ac->module, os->ac->req);
147 if (ares->error != LDB_SUCCESS) {
148 return ldb_module_done(os->ac->req, ares->controls,
149 ares->response, ares->error);
152 /* Only entries are interesting, and we only want the olddn */
153 switch (ares->type) {
154 case LDB_REPLY_ENTRY:
156 /* This *must* be the right DN, as this is a base
157 * search. We can't check, as it could be an extended
158 * DN, so a module below will resolve it */
159 struct ldb_dn *dn = ares->message->dn;
161 *os->replace_dn = data_blob_string_const(
162 ldb_dn_extended_linearized(os->mem_ctx,
164 if (os->replace_dn->data == NULL) {
165 return ldb_module_done(os->ac->req, NULL, NULL,
166 LDB_ERR_OPERATIONS_ERROR);
170 case LDB_REPLY_REFERRAL:
178 /* Run the next search */
181 struct extended_dn_replace_list *next;
188 return ldb_next_request(os->ac->module, next->search_req);
190 /* Otherwise, we are done - let's run the
191 * request now we have swapped the DNs for the
193 return ldb_next_request(os->ac->module, os->ac->new_req);
201 /* We have a 'normal' DN in the inbound request. We need to find out
202 * what the GUID and SID are on the DN it points to, so we can
203 * construct an extended DN for storage.
205 * This creates a list of DNs to look up, and the plain DN to replace
208 static int extended_store_replace(struct extended_dn_context *ac,
209 TALLOC_CTX *callback_mem_ctx,
210 struct ldb_val *plain_dn)
213 struct extended_dn_replace_list *os;
214 static const char *attrs[] = {
220 os = talloc_zero(ac, struct extended_dn_replace_list);
222 return LDB_ERR_OPERATIONS_ERROR;
227 os->mem_ctx = callback_mem_ctx;
229 os->dn = ldb_dn_from_ldb_val(os, ac->module->ldb, plain_dn);
230 if (!os->dn || !ldb_dn_validate(os->dn)) {
232 ldb_asprintf_errstring(ac->module->ldb,
233 "could not parse %.*s as a DN", (int)plain_dn->length, plain_dn->data);
234 return LDB_ERR_INVALID_DN_SYNTAX;
237 os->replace_dn = plain_dn;
239 /* The search request here might happen to be for an
240 * 'extended' style DN, such as <GUID=abced...>. The next
241 * module in the stack will convert this into a normal DN for
243 ret = ldb_build_search_req(&os->search_req,
244 ac->module->ldb, os, os->dn, LDB_SCOPE_BASE, NULL,
245 attrs, NULL, os, extended_replace_dn,
248 if (ret != LDB_SUCCESS) {
253 ret = ldb_request_add_control(os->search_req,
254 DSDB_CONTROL_DN_STORAGE_FORMAT_OID,
256 if (ret != LDB_SUCCESS) {
273 static int extended_dn_add(struct ldb_module *module, struct ldb_request *req)
275 struct extended_dn_context *ac;
279 if (ldb_dn_is_special(req->op.add.message->dn)) {
280 /* do not manipulate our control entries */
281 return ldb_next_request(module, req);
284 ac = extended_dn_context_init(module, req);
286 return LDB_ERR_OPERATIONS_ERROR;
290 /* without schema, this doesn't make any sense */
292 return ldb_next_request(module, req);
295 for (i=0; i < req->op.add.message->num_elements; i++) {
296 const struct ldb_message_element *el = &req->op.add.message->elements[i];
297 const struct dsdb_attribute *schema_attr
298 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
303 /* We only setup an extended DN GUID on these particular DN objects */
304 if (strcmp(schema_attr->attributeSyntax_oid, "2.5.5.1") != 0) {
308 /* Before we setup a procedure to modify the incoming message, we must copy it */
310 struct ldb_message *msg = ldb_msg_copy(ac, req->op.add.message);
312 ldb_oom(module->ldb);
313 return LDB_ERR_OPERATIONS_ERROR;
316 ret = ldb_build_add_req(&ac->new_req, module->ldb, ac, msg, req->controls, ac, extended_final_callback, req);
317 if (ret != LDB_SUCCESS) {
321 /* Re-calculate el */
322 el = &ac->new_req->op.add.message->elements[i];
323 for (j = 0; j < el->num_values; j++) {
324 ret = extended_store_replace(ac, ac->new_req->op.add.message->elements, &el->values[j]);
325 if (ret != LDB_SUCCESS) {
331 /* if DNs were set continue */
332 if (ac->ops == NULL) {
334 return ldb_next_request(module, req);
337 /* start with the searches */
338 return ldb_next_request(module, ac->ops->search_req);
342 static int extended_dn_modify(struct ldb_module *module, struct ldb_request *req)
344 /* Look over list of modifications */
345 /* Find if any are for linked attributes */
346 /* Determine the effect of the modification */
347 /* Apply the modify to the linked entry */
350 struct extended_dn_context *ac;
353 if (ldb_dn_is_special(req->op.mod.message->dn)) {
354 /* do not manipulate our control entries */
355 return ldb_next_request(module, req);
358 ac = extended_dn_context_init(module, req);
360 return LDB_ERR_OPERATIONS_ERROR;
364 /* without schema, this doesn't make any sense */
365 return ldb_next_request(module, req);
368 for (i=0; i < req->op.mod.message->num_elements; i++) {
369 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
370 const struct dsdb_attribute *schema_attr
371 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
376 /* We only setup an extended DN GUID on these particular DN objects */
377 if (strcmp(schema_attr->attributeSyntax_oid, "2.5.5.1") != 0) {
381 /* Before we setup a procedure to modify the incoming message, we must copy it */
383 struct ldb_message *msg = ldb_msg_copy(ac, req->op.mod.message);
385 ldb_oom(module->ldb);
386 return LDB_ERR_OPERATIONS_ERROR;
389 ret = ldb_build_mod_req(&ac->new_req, module->ldb, ac, msg, req->controls, ac, extended_final_callback, req);
390 if (ret != LDB_SUCCESS) {
394 /* Re-calculate el */
395 el = &ac->new_req->op.mod.message->elements[i];
396 /* For each value being added, we need to setup the lookups to fill in the extended DN */
397 for (j = 0; j < el->num_values; j++) {
398 struct ldb_dn *dn = ldb_dn_from_ldb_val(ac, module->ldb, &el->values[j]);
399 if (!dn || !ldb_dn_validate(dn)) {
400 ldb_asprintf_errstring(module->ldb,
401 "could not parse attribute %s as a DN", el->name);
402 return LDB_ERR_INVALID_DN_SYNTAX;
404 if (((el->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_DELETE) && !ldb_dn_has_extended(dn)) {
405 /* NO need to figure this DN out, it's going to be deleted anyway */
408 ret = extended_store_replace(ac, req->op.mod.message->elements, &el->values[j]);
409 if (ret != LDB_SUCCESS) {
415 /* if DNs were set continue */
416 if (ac->ops == NULL) {
418 return ldb_next_request(module, req);
421 /* start with the searches */
422 return ldb_next_request(module, ac->ops->search_req);
425 _PUBLIC_ const struct ldb_module_ops ldb_extended_dn_store_module_ops = {
426 .name = "extended_dn_store",
427 .add = extended_dn_add,
428 .modify = extended_dn_modify,