1 /*
2    ldb database library
3 
4    Copyright (C) Simo Sorce  2005
5 
6      ** NOTE! The following LGPL license applies to the ldb
7      ** library. This does NOT imply that all of Samba is released
8      ** under the LGPL
9 
10    This library is free software; you can redistribute it and/or
11    modify it under the terms of the GNU Lesser General Public
12    License as published by the Free Software Foundation; either
13    version 2 of the License, or (at your option) any later version.
14 
15    This library is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    Lesser General Public License for more details.
19 
20    You should have received a copy of the GNU Lesser General Public
21    License along with this library; if not, write to the Free Software
22    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23 */
24 
25 /*
26  *  Name: ldb
27  *
28  *  Component: ldb extended dn control module
29  *
30  *  Description: this module builds a special dn
31  *
32  *  Author: Simo Sorce
33  */
34 
35 #include "includes.h"
36 #include "ldb/include/ldb.h"
37 #include "ldb/include/ldb_errors.h"
38 #include "ldb/include/ldb_private.h"
39 #include "librpc/gen_ndr/ndr_misc.h"
40 #include "dsdb/samdb/samdb.h"
41 #include "libcli/security/security.h"
42 
43 #include <time.h>
44 
is_attr_in_list(const char * const * attrs,const char * attr)45 static BOOL is_attr_in_list(const char * const * attrs, const char *attr)
46 {
47 	int i;
48 
49 	for (i = 0; attrs[i]; i++) {
50 		if (strcasecmp(attrs[i], attr) == 0)
51 			return True;
52 	}
53 
54 	return False;
55 }
56 
copy_attrs(void * mem_ctx,const char * const * attrs)57 static char **copy_attrs(void *mem_ctx, const char * const * attrs)
58 {
59 	char **new;
60 	int i, num;
61 
62 	for (num = 0; attrs[num]; num++);
63 
64 	new = talloc_array(mem_ctx, char *, num + 1);
65 	if (!new) return NULL;
66 
67 	for(i = 0; i < num; i++) {
68 		new[i] = talloc_strdup(new, attrs[i]);
69 		if (!new[i]) {
70 			talloc_free(new);
71 			return NULL;
72 		}
73 	}
74 	new[i] = NULL;
75 
76 	return new;
77 }
78 
add_attrs(void * mem_ctx,char *** attrs,const char * attr)79 static BOOL add_attrs(void *mem_ctx, char ***attrs, const char *attr)
80 {
81 	char **new;
82 	int num;
83 
84 	for (num = 0; (*attrs)[num]; num++);
85 
86 	new = talloc_realloc(mem_ctx, *attrs, char *, num + 2);
87 	if (!new) return False;
88 
89 	*attrs = new;
90 
91 	new[num] = talloc_strdup(new, attr);
92 	if (!new[num]) return False;
93 
94 	new[num + 1] = NULL;
95 
96 	return True;
97 }
98 
inject_extended_dn(struct ldb_message * msg,struct ldb_context * ldb,int type,BOOL remove_guid,BOOL remove_sid)99 static BOOL inject_extended_dn(struct ldb_message *msg,
100 				struct ldb_context *ldb,
101 				int type,
102 				BOOL remove_guid,
103 				BOOL remove_sid)
104 {
105 	const struct ldb_val *val;
106 	struct GUID guid;
107 	struct dom_sid *sid;
108 	char *object_guid;
109 	char *object_sid;
110 	char *new_dn;
111 
112 	/* retrieve object_guid */
113 	guid = samdb_result_guid(msg, "objectGUID");
114 	object_guid = GUID_string(msg, &guid);
115 	if (!object_guid)
116 		return False;
117 
118 	if (remove_guid)
119 		ldb_msg_remove_attr(msg, "objectGUID");
120 
121 	/* retrieve object_sid */
122 	object_sid = NULL;
123 	sid = samdb_result_dom_sid(msg, msg, "objectSID");
124 	if (sid) {
125 		object_sid = dom_sid_string(msg, sid);
126 		if (!object_sid)
127 			return False;
128 
129 		if (remove_sid)
130 			ldb_msg_remove_attr(msg, "objectSID");
131 	}
132 
133 	/* TODO: handle type */
134 	switch (type) {
135 		case 0:
136 		case 1:
137 			if (object_sid) {
138 				new_dn = talloc_asprintf(msg, "<GUID=%s>;<SID=%s>;%s",
139 							 object_guid, object_sid,
140 							 ldb_dn_get_linearized(msg->dn));
141 			} else {
142 				new_dn = talloc_asprintf(msg, "<GUID=%s>;%s",
143 							 object_guid,
144 							 ldb_dn_get_linearized(msg->dn));
145 			}
146 			break;
147 		default:
148 			return False;
149 	}
150 
151 	if (!new_dn)
152 		return False;
153 
154 	msg->dn = ldb_dn_new(msg, ldb, new_dn);
155 	if (! ldb_dn_validate(msg->dn))
156 		return False;
157 
158 	val = ldb_msg_find_ldb_val(msg, "distinguishedName");
159 	if (val) {
160 		ldb_msg_remove_attr(msg, "distinguishedName");
161 		if (ldb_msg_add_steal_string(msg, "distinguishedName", new_dn))
162 			return False;
163 	}
164 
165 	return True;
166 }
167 
168 /* search */
169 struct extended_context {
170 
171 	struct ldb_module *module;
172 	void *up_context;
173 	int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
174 
175 	const char * const *attrs;
176 	BOOL remove_guid;
177 	BOOL remove_sid;
178 	int extended_type;
179 };
180 
extended_callback(struct ldb_context * ldb,void * context,struct ldb_reply * ares)181 static int extended_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
182 {
183 	struct extended_context *ac;
184 
185 	if (!context || !ares) {
186 		ldb_set_errstring(ldb, "NULL Context or Result in callback");
187 		goto error;
188 	}
189 
190 	ac = talloc_get_type(context, struct extended_context);
191 
192 	if (ares->type == LDB_REPLY_ENTRY) {
193 		/* for each record returned post-process to add any derived
194 		   attributes that have been asked for */
195 		if (!inject_extended_dn(ares->message, ldb, ac->extended_type, ac->remove_guid, ac->remove_sid)) {
196 			goto error;
197 		}
198 	}
199 
200 	return ac->up_callback(ldb, ac->up_context, ares);
201 
202 error:
203 	talloc_free(ares);
204 	return LDB_ERR_OPERATIONS_ERROR;
205 }
206 
extended_search(struct ldb_module * module,struct ldb_request * req)207 static int extended_search(struct ldb_module *module, struct ldb_request *req)
208 {
209 	struct ldb_control *control;
210 	struct ldb_extended_dn_control *extended_ctrl;
211 	struct ldb_control **saved_controls;
212 	struct extended_context *ac;
213 	struct ldb_request *down_req;
214 	char **new_attrs;
215 	int ret;
216 
217 	/* check if there's an extended dn control */
218 	control = get_control_from_list(req->controls, LDB_CONTROL_EXTENDED_DN_OID);
219 	if (control == NULL) {
220 		/* not found go on */
221 		return ldb_next_request(module, req);
222 	}
223 
224 	extended_ctrl = talloc_get_type(control->data, struct ldb_extended_dn_control);
225 	if (!extended_ctrl) {
226 		return LDB_ERR_PROTOCOL_ERROR;
227 	}
228 
229 	ac = talloc(req, struct extended_context);
230 	if (ac == NULL) {
231 		return LDB_ERR_OPERATIONS_ERROR;
232 	}
233 
234 	ac->module = module;
235 	ac->up_context = req->context;
236 	ac->up_callback = req->callback;
237 	ac->attrs = req->op.search.attrs;
238 	ac->remove_guid = False;
239 	ac->remove_sid = False;
240 	ac->extended_type = extended_ctrl->type;
241 
242 	down_req = talloc_zero(req, struct ldb_request);
243 	if (down_req == NULL) {
244 		return LDB_ERR_OPERATIONS_ERROR;
245 	}
246 
247 	down_req->operation = req->operation;
248 	down_req->op.search.base = req->op.search.base;
249 	down_req->op.search.scope = req->op.search.scope;
250 	down_req->op.search.tree = req->op.search.tree;
251 
252 	/* check if attrs only is specified, in that case check wether we need to modify them */
253 	if (req->op.search.attrs) {
254 		if (! is_attr_in_list(req->op.search.attrs, "objectGUID")) {
255 			ac->remove_guid = True;
256 		}
257 		if (! is_attr_in_list(req->op.search.attrs, "objectSID")) {
258 			ac->remove_sid = True;
259 		}
260 		if (ac->remove_guid || ac->remove_sid) {
261 			new_attrs = copy_attrs(down_req, req->op.search.attrs);
262 			if (new_attrs == NULL)
263 				return LDB_ERR_OPERATIONS_ERROR;
264 
265 			if (ac->remove_guid) {
266 				if (!add_attrs(down_req, &new_attrs, "objectGUID"))
267 					return LDB_ERR_OPERATIONS_ERROR;
268 			}
269 			if (ac->remove_sid) {
270 				if (!add_attrs(down_req, &new_attrs, "objectSID"))
271 					return LDB_ERR_OPERATIONS_ERROR;
272 			}
273 
274 			down_req->op.search.attrs = (const char * const *)new_attrs;
275 		}
276 	}
277 
278 	down_req->controls = req->controls;
279 
280 	/* save it locally and remove it from the list */
281 	/* we do not need to replace them later as we
282 	 * are keeping the original req intact */
283 	if (!save_controls(control, down_req, &saved_controls)) {
284 		return LDB_ERR_OPERATIONS_ERROR;
285 	}
286 
287 	down_req->context = ac;
288 	down_req->callback = extended_callback;
289 	ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
290 
291 	/* perform the search */
292 	ret = ldb_next_request(module, down_req);
293 
294 	/* do not free down_req as the call results may be linked to it,
295 	 * it will be freed when the upper level request get freed */
296 	if (ret == LDB_SUCCESS) {
297 		req->handle = down_req->handle;
298 	}
299 
300 	return ret;
301 }
302 
extended_init(struct ldb_module * module)303 static int extended_init(struct ldb_module *module)
304 {
305 	struct ldb_request *req;
306 	int ret;
307 
308 	req = talloc(module, struct ldb_request);
309 	if (req == NULL) {
310 		return LDB_ERR_OPERATIONS_ERROR;
311 	}
312 
313 	req->operation = LDB_REQ_REGISTER_CONTROL;
314 	req->op.reg_control.oid = LDB_CONTROL_EXTENDED_DN_OID;
315 	req->controls = NULL;
316 
317 	ret = ldb_request(module->ldb, req);
318 	if (ret != LDB_SUCCESS) {
319 		ldb_debug(module->ldb, LDB_DEBUG_ERROR, "extended_dn: Unable to register control with rootdse!\n");
320 		talloc_free(req);
321 		return LDB_ERR_OPERATIONS_ERROR;
322 	}
323 
324 	talloc_free(req);
325 	return ldb_next_init(module);
326 }
327 
328 static const struct ldb_module_ops extended_dn_ops = {
329 	.name		   = "extended_dn",
330 	.search            = extended_search,
331 	.init_context	   = extended_init
332 };
333 
ldb_extended_dn_init(void)334 int ldb_extended_dn_init(void)
335 {
336 	return ldb_register_module(&extended_dn_ops);
337 }
338