• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src/router/samba-3.5.8/source4/dsdb/samdb/ldb_modules/
1/*
2   ldb database library
3
4   Copyright (C) Simo Sorce 2005-2008
5   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007-2008
6
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.
11
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.
16
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/>.
19*/
20
21/*
22 *  Name: ldb
23 *
24 *  Component: ldb extended dn control module
25 *
26 *  Description: this module builds a special dn for returned search
27 *  results, and fixes some other aspects of the result (returned case issues)
28 *  values.
29 *
30 *  Authors: Simo Sorce
31 *           Andrew Bartlett
32 */
33
34#include "includes.h"
35#include "ldb/include/ldb.h"
36#include "ldb/include/ldb_errors.h"
37#include "ldb/include/ldb_module.h"
38#include "librpc/gen_ndr/ndr_misc.h"
39#include "librpc/ndr/libndr.h"
40#include "dsdb/samdb/samdb.h"
41
42struct extended_dn_out_private {
43	bool dereference;
44	bool normalise;
45	struct dsdb_openldap_dereference_control *dereference_control;
46};
47
48static bool is_attr_in_list(const char * const * attrs, const char *attr)
49{
50	int i;
51
52	for (i = 0; attrs[i]; i++) {
53		if (ldb_attr_cmp(attrs[i], attr) == 0)
54			return true;
55	}
56
57	return false;
58}
59
60static char **copy_attrs(void *mem_ctx, const char * const * attrs)
61{
62	char **nattrs;
63	int i, num;
64
65	for (num = 0; attrs[num]; num++);
66
67	nattrs = talloc_array(mem_ctx, char *, num + 1);
68	if (!nattrs) return NULL;
69
70	for(i = 0; i < num; i++) {
71		nattrs[i] = talloc_strdup(nattrs, attrs[i]);
72		if (!nattrs[i]) {
73			talloc_free(nattrs);
74			return NULL;
75		}
76	}
77	nattrs[i] = NULL;
78
79	return nattrs;
80}
81
82static bool add_attrs(void *mem_ctx, char ***attrs, const char *attr)
83{
84	char **nattrs;
85	int num;
86
87	for (num = 0; (*attrs)[num]; num++);
88
89	nattrs = talloc_realloc(mem_ctx, *attrs, char *, num + 2);
90	if (!nattrs) return false;
91
92	*attrs = nattrs;
93
94	nattrs[num] = talloc_strdup(nattrs, attr);
95	if (!nattrs[num]) return false;
96
97	nattrs[num + 1] = NULL;
98
99	return true;
100}
101
102/* Fix the DN so that the relative attribute names are in upper case so that the DN:
103   cn=Adminstrator,cn=users,dc=samba,dc=example,dc=com becomes
104   CN=Adminstrator,CN=users,DC=samba,DC=example,DC=com
105*/
106
107
108static int fix_dn(struct ldb_dn *dn)
109{
110	int i, ret;
111	char *upper_rdn_attr;
112
113	for (i=0; i < ldb_dn_get_comp_num(dn); i++) {
114		/* We need the attribute name in upper case */
115		upper_rdn_attr = strupper_talloc(dn,
116						 ldb_dn_get_component_name(dn, i));
117		if (!upper_rdn_attr) {
118			return LDB_ERR_OPERATIONS_ERROR;
119		}
120
121		/* And replace it with CN=foo (we need the attribute in upper case */
122		ret = ldb_dn_set_component(dn, i, upper_rdn_attr,
123					   *ldb_dn_get_component_val(dn, i));
124		talloc_free(upper_rdn_attr);
125		if (ret != LDB_SUCCESS) {
126			return ret;
127		}
128	}
129	return LDB_SUCCESS;
130}
131
132/* Inject the extended DN components, so the DN cn=Adminstrator,cn=users,dc=samba,dc=example,dc=com becomes
133   <GUID=541203ae-f7d6-47ef-8390-bfcf019f9583>;<SID=S-1-5-21-4177067393-1453636373-93818737-500>;cn=Adminstrator,cn=users,dc=samba,dc=example,dc=com */
134
135static int inject_extended_dn_out(struct ldb_reply *ares,
136				  struct ldb_context *ldb,
137				  int type,
138				  bool remove_guid,
139				  bool remove_sid)
140{
141	int ret;
142	const DATA_BLOB *guid_blob;
143	const DATA_BLOB *sid_blob;
144
145	guid_blob = ldb_msg_find_ldb_val(ares->message, "objectGUID");
146	sid_blob = ldb_msg_find_ldb_val(ares->message, "objectSID");
147
148	if (!guid_blob) {
149		ldb_set_errstring(ldb, "Did not find objectGUID to inject into extended DN");
150		return LDB_ERR_OPERATIONS_ERROR;
151	}
152
153	ret = ldb_dn_set_extended_component(ares->message->dn, "GUID", guid_blob);
154	if (ret != LDB_SUCCESS) {
155		return ret;
156	}
157	if (sid_blob) {
158		ret = ldb_dn_set_extended_component(ares->message->dn, "SID", sid_blob);
159		if (ret != LDB_SUCCESS) {
160			return ret;
161		}
162	}
163
164	if (remove_guid) {
165		ldb_msg_remove_attr(ares->message, "objectGUID");
166	}
167
168	if (sid_blob && remove_sid) {
169		ldb_msg_remove_attr(ares->message, "objectSID");
170	}
171
172	return LDB_SUCCESS;
173}
174
175static int handle_dereference(struct ldb_dn *dn,
176			      struct dsdb_openldap_dereference_result **dereference_attrs,
177			      const char *attr, const DATA_BLOB *val)
178{
179	const struct ldb_val *entryUUIDblob, *sid_blob;
180	struct ldb_message fake_msg; /* easier to use routines that expect an ldb_message */
181	int j;
182
183	fake_msg.num_elements = 0;
184
185	/* Look for this attribute in the returned control */
186	for (j = 0; dereference_attrs && dereference_attrs[j]; j++) {
187		struct ldb_val source_dn = data_blob_string_const(dereference_attrs[j]->dereferenced_dn);
188		if (ldb_attr_cmp(dereference_attrs[j]->source_attribute, attr) == 0
189		    && data_blob_cmp(&source_dn, val) == 0) {
190			fake_msg.num_elements = dereference_attrs[j]->num_attributes;
191			fake_msg.elements = dereference_attrs[j]->attributes;
192			break;
193		}
194	}
195	if (!fake_msg.num_elements) {
196		return LDB_SUCCESS;
197	}
198	/* Look for an OpenLDAP entryUUID */
199
200	entryUUIDblob = ldb_msg_find_ldb_val(&fake_msg, "entryUUID");
201	if (entryUUIDblob) {
202		NTSTATUS status;
203		enum ndr_err_code ndr_err;
204
205		struct ldb_val guid_blob;
206		struct GUID guid;
207
208		status = GUID_from_data_blob(entryUUIDblob, &guid);
209
210		if (!NT_STATUS_IS_OK(status)) {
211			return LDB_ERR_INVALID_DN_SYNTAX;
212		}
213		ndr_err = ndr_push_struct_blob(&guid_blob, NULL, NULL, &guid,
214					       (ndr_push_flags_fn_t)ndr_push_GUID);
215		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
216			return LDB_ERR_INVALID_DN_SYNTAX;
217		}
218
219		ldb_dn_set_extended_component(dn, "GUID", &guid_blob);
220	}
221
222	sid_blob = ldb_msg_find_ldb_val(&fake_msg, "objectSID");
223
224	/* Look for the objectSID */
225	if (sid_blob) {
226		ldb_dn_set_extended_component(dn, "SID", sid_blob);
227	}
228	return LDB_SUCCESS;
229}
230
231/* search */
232struct extended_search_context {
233	struct ldb_module *module;
234	const struct dsdb_schema *schema;
235	struct ldb_request *req;
236	bool inject;
237	bool remove_guid;
238	bool remove_sid;
239	int extended_type;
240};
241
242static int extended_callback(struct ldb_request *req, struct ldb_reply *ares)
243{
244	struct extended_search_context *ac;
245	struct ldb_control *control;
246	struct dsdb_openldap_dereference_result_control *dereference_control = NULL;
247	int ret, i, j;
248	struct ldb_message *msg = ares->message;
249	struct extended_dn_out_private *p;
250
251	ac = talloc_get_type(req->context, struct extended_search_context);
252	p = talloc_get_type(ldb_module_get_private(ac->module), struct extended_dn_out_private);
253
254	if (!ares) {
255		return ldb_module_done(ac->req, NULL, NULL,
256					LDB_ERR_OPERATIONS_ERROR);
257	}
258	if (ares->error != LDB_SUCCESS) {
259		return ldb_module_done(ac->req, ares->controls,
260					ares->response, ares->error);
261	}
262
263	switch (ares->type) {
264	case LDB_REPLY_REFERRAL:
265		return ldb_module_send_referral(ac->req, ares->referral);
266
267	case LDB_REPLY_DONE:
268		return ldb_module_done(ac->req, ares->controls,
269					ares->response, LDB_SUCCESS);
270	case LDB_REPLY_ENTRY:
271		break;
272	}
273
274	if (p && p->normalise) {
275		ret = fix_dn(ares->message->dn);
276		if (ret != LDB_SUCCESS) {
277			return ldb_module_done(ac->req, NULL, NULL, ret);
278		}
279	}
280
281	if (ac->inject) {
282		/* for each record returned post-process to add any derived
283		   attributes that have been asked for */
284		ret = inject_extended_dn_out(ares, ldb_module_get_ctx(ac->module),
285					     ac->extended_type, ac->remove_guid,
286					     ac->remove_sid);
287		if (ret != LDB_SUCCESS) {
288			return ldb_module_done(ac->req, NULL, NULL, ret);
289		}
290	}
291
292	if ((p && p->normalise) || ac->inject) {
293		const struct ldb_val *val = ldb_msg_find_ldb_val(ares->message, "distinguishedName");
294		if (val) {
295			ldb_msg_remove_attr(ares->message, "distinguishedName");
296			if (ac->inject) {
297				ret = ldb_msg_add_steal_string(ares->message, "distinguishedName",
298							       ldb_dn_get_extended_linearized(ares->message, ares->message->dn, ac->extended_type));
299			} else {
300				ret = ldb_msg_add_string(ares->message, "distinguishedName",
301							 ldb_dn_get_linearized(ares->message->dn));
302			}
303			if (ret != LDB_SUCCESS) {
304				ldb_oom(ldb_module_get_ctx(ac->module));
305				return LDB_ERR_OPERATIONS_ERROR;
306			}
307		}
308	}
309
310	if (p && p->dereference) {
311		control = ldb_reply_get_control(ares, DSDB_OPENLDAP_DEREFERENCE_CONTROL);
312
313		if (control && control->data) {
314			dereference_control = talloc_get_type(control->data, struct dsdb_openldap_dereference_result_control);
315		}
316	}
317
318	/* Walk the retruned elements (but only if we have a schema to interpret the list with) */
319	for (i = 0; ac->schema && i < msg->num_elements; i++) {
320		const struct dsdb_attribute *attribute;
321		attribute = dsdb_attribute_by_lDAPDisplayName(ac->schema, msg->elements[i].name);
322		if (!attribute) {
323			continue;
324		}
325
326		if (p->normalise) {
327			/* If we are also in 'normalise' mode, then
328			 * fix the attribute names to be in the
329			 * correct case */
330			msg->elements[i].name = talloc_strdup(msg->elements, attribute->lDAPDisplayName);
331			if (!msg->elements[i].name) {
332				ldb_oom(ldb_module_get_ctx(ac->module));
333				return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
334			}
335		}
336
337		/* distinguishedName has been dealt with above */
338		if (ldb_attr_cmp(msg->elements[i].name, "distinguishedName") == 0) {
339			continue;
340		}
341
342		/* Look to see if this attributeSyntax is a DN */
343		if (strcmp(attribute->attributeSyntax_oid, "2.5.5.1") != 0) {
344			continue;
345		}
346
347		for (j = 0; j < msg->elements[i].num_values; j++) {
348			const char *dn_str;
349			struct ldb_dn *dn = ldb_dn_from_ldb_val(ac, ldb_module_get_ctx(ac->module), &msg->elements[i].values[j]);
350			if (!dn || !ldb_dn_validate(dn)) {
351				return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_INVALID_DN_SYNTAX);
352			}
353
354			if (p->normalise) {
355				ret = fix_dn(dn);
356				if (ret != LDB_SUCCESS) {
357					return ldb_module_done(ac->req, NULL, NULL, ret);
358				}
359			}
360
361			/* If we are running in dereference mode (such
362			 * as against OpenLDAP) then the DN in the msg
363			 * above does not contain the extended values,
364			 * and we need to look in the dereference
365			 * result */
366
367			/* Look for this value in the attribute */
368
369			if (dereference_control) {
370				ret = handle_dereference(dn,
371							 dereference_control->attributes,
372							 msg->elements[i].name,
373							 &msg->elements[i].values[j]);
374				if (ret != LDB_SUCCESS) {
375					return ldb_module_done(ac->req, NULL, NULL, ret);
376				}
377			}
378
379			if (!ac->inject) {
380				dn_str = talloc_steal(msg->elements[i].values,
381						      ldb_dn_get_linearized(dn));
382			} else {
383				dn_str = talloc_steal(msg->elements[i].values,
384						      ldb_dn_get_extended_linearized(msg->elements[i].values,
385										     dn, ac->extended_type));
386			}
387			if (!dn_str) {
388				ldb_oom(ldb_module_get_ctx(ac->module));
389				return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
390			}
391			msg->elements[i].values[j] = data_blob_string_const(dn_str);
392			talloc_free(dn);
393		}
394	}
395	return ldb_module_send_entry(ac->req, msg, ares->controls);
396}
397
398
399static int extended_dn_out_search(struct ldb_module *module, struct ldb_request *req)
400{
401	struct ldb_control *control;
402	struct ldb_control *storage_format_control;
403	struct ldb_extended_dn_control *extended_ctrl = NULL;
404	struct ldb_control **saved_controls;
405	struct extended_search_context *ac;
406	struct ldb_request *down_req;
407	char **new_attrs;
408	const char * const *const_attrs;
409	int ret;
410
411	struct extended_dn_out_private *p = talloc_get_type(ldb_module_get_private(module), struct extended_dn_out_private);
412
413	/* check if there's an extended dn control */
414	control = ldb_request_get_control(req, LDB_CONTROL_EXTENDED_DN_OID);
415	if (control && control->data) {
416		extended_ctrl = talloc_get_type(control->data, struct ldb_extended_dn_control);
417		if (!extended_ctrl) {
418			return LDB_ERR_PROTOCOL_ERROR;
419		}
420	}
421
422	/* Look to see if, as we are in 'store DN+GUID+SID' mode, the
423	 * client is after the storage format (to fill in linked
424	 * attributes) */
425	storage_format_control = ldb_request_get_control(req, DSDB_CONTROL_DN_STORAGE_FORMAT_OID);
426	if (!control && storage_format_control && storage_format_control->data) {
427		extended_ctrl = talloc_get_type(storage_format_control->data, struct ldb_extended_dn_control);
428		if (!extended_ctrl) {
429			ldb_set_errstring(ldb_module_get_ctx(module), "extended_dn_out: extended_ctrl was of the wrong data type");
430			return LDB_ERR_PROTOCOL_ERROR;
431		}
432	}
433
434	ac = talloc_zero(req, struct extended_search_context);
435	if (ac == NULL) {
436		ldb_oom(ldb_module_get_ctx(module));
437		return LDB_ERR_OPERATIONS_ERROR;
438	}
439
440	ac->module = module;
441	ac->schema = dsdb_get_schema(ldb_module_get_ctx(module));
442	ac->req = req;
443	ac->inject = false;
444	ac->remove_guid = false;
445	ac->remove_sid = false;
446
447	const_attrs = req->op.search.attrs;
448
449	/* We only need to do special processing if we were asked for
450	 * the extended DN, or we are 'store DN+GUID+SID'
451	 * (!dereference) mode.  (This is the normal mode for LDB on
452	 * tdb). */
453	if (control || (storage_format_control && p && !p->dereference)) {
454		ac->inject = true;
455		if (extended_ctrl) {
456			ac->extended_type = extended_ctrl->type;
457		} else {
458			ac->extended_type = 0;
459		}
460
461		/* check if attrs only is specified, in that case check wether we need to modify them */
462		if (req->op.search.attrs && !is_attr_in_list(req->op.search.attrs, "*")) {
463			if (! is_attr_in_list(req->op.search.attrs, "objectGUID")) {
464				ac->remove_guid = true;
465			}
466			if (! is_attr_in_list(req->op.search.attrs, "objectSID")) {
467				ac->remove_sid = true;
468			}
469			if (ac->remove_guid || ac->remove_sid) {
470				new_attrs = copy_attrs(ac, req->op.search.attrs);
471				if (new_attrs == NULL) {
472					ldb_oom(ldb_module_get_ctx(module));
473					return LDB_ERR_OPERATIONS_ERROR;
474				}
475
476				if (ac->remove_guid) {
477					if (!add_attrs(ac, &new_attrs, "objectGUID"))
478						return LDB_ERR_OPERATIONS_ERROR;
479				}
480				if (ac->remove_sid) {
481					if (!add_attrs(ac, &new_attrs, "objectSID"))
482						return LDB_ERR_OPERATIONS_ERROR;
483				}
484				const_attrs = (const char * const *)new_attrs;
485			}
486		}
487	}
488
489	ret = ldb_build_search_req_ex(&down_req,
490				      ldb_module_get_ctx(module), ac,
491				      req->op.search.base,
492				      req->op.search.scope,
493				      req->op.search.tree,
494				      const_attrs,
495				      req->controls,
496				      ac, extended_callback,
497				      req);
498	if (ret != LDB_SUCCESS) {
499		return ret;
500	}
501
502	/* Remove extended DN and storage format controls */
503
504	if (control) {
505		/* save it locally and remove it from the list */
506		/* we do not need to replace them later as we
507		 * are keeping the original req intact */
508		if (!save_controls(control, down_req, &saved_controls)) {
509			return LDB_ERR_OPERATIONS_ERROR;
510		}
511	}
512
513	if (storage_format_control) {
514		/* save it locally and remove it from the list */
515		/* we do not need to replace them later as we
516		 * are keeping the original req intact */
517		if (!save_controls(storage_format_control, down_req, &saved_controls)) {
518			return LDB_ERR_OPERATIONS_ERROR;
519		}
520	}
521
522	/* Add in dereference control, if we were asked to, we are
523	 * using the 'dereference' mode (such as with an OpenLDAP
524	 * backend) and have the control prepared */
525	if (control && p && p->dereference && p->dereference_control) {
526		ret = ldb_request_add_control(down_req,
527					      DSDB_OPENLDAP_DEREFERENCE_CONTROL,
528					      false, p->dereference_control);
529		if (ret != LDB_SUCCESS) {
530			return ret;
531		}
532	}
533
534	/* perform the search */
535	return ldb_next_request(module, down_req);
536}
537
538static int extended_dn_out_ldb_init(struct ldb_module *module)
539{
540	int ret;
541
542	struct extended_dn_out_private *p = talloc(module, struct extended_dn_out_private);
543
544	ldb_module_set_private(module, p);
545
546	if (!p) {
547		ldb_oom(ldb_module_get_ctx(module));
548		return LDB_ERR_OPERATIONS_ERROR;
549	}
550
551	p->dereference = false;
552	p->normalise = false;
553
554	ret = ldb_mod_register_control(module, LDB_CONTROL_EXTENDED_DN_OID);
555	if (ret != LDB_SUCCESS) {
556		ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
557			"extended_dn_out: Unable to register control with rootdse!\n");
558		return LDB_ERR_OPERATIONS_ERROR;
559	}
560
561	return ldb_next_init(module);
562}
563
564static int extended_dn_out_dereference_init(struct ldb_module *module)
565{
566	int ret, i = 0;
567	struct extended_dn_out_private *p = talloc_zero(module, struct extended_dn_out_private);
568	struct dsdb_openldap_dereference_control *dereference_control;
569	struct dsdb_attribute *cur;
570
571	struct dsdb_schema *schema;
572
573	ldb_module_set_private(module, p);
574
575	if (!p) {
576		ldb_oom(ldb_module_get_ctx(module));
577		return LDB_ERR_OPERATIONS_ERROR;
578	}
579
580	p->dereference = true;
581
582	/* At the moment, servers that need dereference also need the
583	 * DN and attribute names to be normalised */
584	p->normalise = true;
585
586	ret = ldb_mod_register_control(module, LDB_CONTROL_EXTENDED_DN_OID);
587	if (ret != LDB_SUCCESS) {
588		ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
589			"extended_dn_out: Unable to register control with rootdse!\n");
590		return LDB_ERR_OPERATIONS_ERROR;
591	}
592
593	ret = ldb_next_init(module);
594
595	if (ret != LDB_SUCCESS) {
596		return ret;
597	}
598
599	schema = dsdb_get_schema(ldb_module_get_ctx(module));
600	if (!schema) {
601		/* No schema on this DB (yet) */
602		return LDB_SUCCESS;
603	}
604
605	p->dereference_control = dereference_control
606		= talloc_zero(p, struct dsdb_openldap_dereference_control);
607
608	if (!p->dereference_control) {
609		ldb_oom(ldb_module_get_ctx(module));
610		return LDB_ERR_OPERATIONS_ERROR;
611	}
612
613	for (cur = schema->attributes; cur; cur = cur->next) {
614		static const char *attrs[] = {
615			"entryUUID",
616			"objectSID",
617			NULL
618		};
619
620		if (strcmp(cur->syntax->attributeSyntax_oid, "2.5.5.1") != 0) {
621			continue;
622		}
623		dereference_control->dereference
624			= talloc_realloc(p, dereference_control->dereference,
625					 struct dsdb_openldap_dereference *, i + 2);
626		if (!dereference_control) {
627			ldb_oom(ldb_module_get_ctx(module));
628			return LDB_ERR_OPERATIONS_ERROR;
629		}
630		dereference_control->dereference[i] = talloc(dereference_control->dereference,
631					 struct dsdb_openldap_dereference);
632		if (!dereference_control->dereference[i]) {
633			ldb_oom(ldb_module_get_ctx(module));
634			return LDB_ERR_OPERATIONS_ERROR;
635		}
636		dereference_control->dereference[i]->source_attribute = cur->lDAPDisplayName;
637		dereference_control->dereference[i]->dereference_attribute = attrs;
638		i++;
639		dereference_control->dereference[i] = NULL;
640	}
641	return LDB_SUCCESS;
642}
643
644_PUBLIC_ const struct ldb_module_ops ldb_extended_dn_out_ldb_module_ops = {
645	.name		   = "extended_dn_out_ldb",
646	.search            = extended_dn_out_search,
647	.init_context	   = extended_dn_out_ldb_init,
648};
649
650
651_PUBLIC_ const struct ldb_module_ops ldb_extended_dn_out_dereference_module_ops = {
652	.name		   = "extended_dn_out_dereference",
653	.search            = extended_dn_out_search,
654	.init_context	   = extended_dn_out_dereference_init,
655};
656