1 /*
2    ldb database library
3 
4    Copyright (C) Simo Sorce  2005
5    Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
6    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009
7    Copyright (C) Matthias Dieter Wallnöfer 2010
8 
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22 
23 /*
24  *  Name: ldb
25  *
26  *  Component: ldb deleted objects control module
27  *
28  *  Description: this module hides deleted and recylced objects, and returns
29  *  them if the right control is there
30  *
31  *  Author: Stefan Metzmacher
32  */
33 
34 #include "includes.h"
35 #include <ldb_module.h>
36 #include "dsdb/samdb/samdb.h"
37 #include "dsdb/samdb/ldb_modules/util.h"
38 
39 struct show_deleted_state {
40 	bool need_refresh;
41 	bool recycle_bin_enabled;
42 };
43 
show_deleted_search(struct ldb_module * module,struct ldb_request * req)44 static int show_deleted_search(struct ldb_module *module, struct ldb_request *req)
45 {
46 	struct ldb_context *ldb;
47 	struct ldb_control *show_del, *show_rec;
48 	struct ldb_request *down_req;
49 	struct ldb_parse_tree *new_tree = req->op.search.tree;
50 	struct show_deleted_state *state;
51 	int ret;
52 	const char *exclude_filter = NULL;
53 
54 	/* do not manipulate our control entries */
55 	if (ldb_dn_is_special(req->op.search.base)) {
56 		return ldb_next_request(module, req);
57 	}
58 
59 	ldb = ldb_module_get_ctx(module);
60 
61 	/* This is the logic from MS-ADTS 3.1.1.3.4.1.14 that
62 	   determines if objects are visible
63 
64 	   Extended control name                     Deleted-objects      Tombstones        Recycled-objects
65 	   LDAP_SERVER_SHOW_DELETED_OID              Visible              Visible           Not Visible
66 	   LDAP_SERVER_SHOW_RECYCLED_OID             Visible              Visible           Visible
67 
68 	   Note that if the recycle bin is disabled, then the
69 	   isRecycled attribute is ignored, and objects are either
70 	   "normal" or "tombstone".
71 
72 	   When the recycle bin is enabled, then objects are in one of
73 	   3 states, "normal", "deleted" or "recycled"
74 	*/
75 
76 	/* check if there's a show deleted control */
77 	show_del = ldb_request_get_control(req, LDB_CONTROL_SHOW_DELETED_OID);
78 	/* check if there's a show recycled control */
79 	show_rec = ldb_request_get_control(req, LDB_CONTROL_SHOW_RECYCLED_OID);
80 
81 	/*
82 	 * When recycle bin is not enabled, then all we look
83 	 * at is the isDeleted attribute. We hide objects with this
84 	 * attribute set to TRUE when the client has not specified either
85 	 * SHOW_DELETED or SHOW_RECYCLED
86 	 */
87 	if (show_rec == NULL && show_del == NULL) {
88 		/* We don't want deleted or recycled objects,
89 		 * which we get by filtering on isDeleted */
90 		exclude_filter = "isDeleted";
91 	} else {
92 		state = talloc_get_type(ldb_module_get_private(module), struct show_deleted_state);
93 
94 		/* Note that state may be NULL during initialisation */
95 		if (state != NULL && state->need_refresh) {
96 			/* Do not move this assignment, it can cause recursion loops! */
97 			state->need_refresh = false;
98 			ret = dsdb_recyclebin_enabled(module, &state->recycle_bin_enabled);
99 			if (ret != LDB_SUCCESS) {
100 				state->recycle_bin_enabled = false;
101 				/*
102 				 * We can fail to find the feature object
103 				 * during provision. Ignore any such error and
104 				 * assume the recycle bin cannot be enabled at
105 				 * this point in time.
106 				 */
107 				if (ret != LDB_ERR_NO_SUCH_OBJECT) {
108 					state->need_refresh = true;
109 					return LDB_ERR_UNWILLING_TO_PERFORM;
110 				}
111 			}
112 		}
113 
114 		if (state != NULL && state->recycle_bin_enabled) {
115 			/*
116 			 * The recycle bin is enabled, so we want deleted not
117 			 * recycled.
118 			 */
119 			if (show_rec == NULL) {
120 				exclude_filter = "isRecycled";
121 			}
122 		}
123 	}
124 
125 	if (exclude_filter != NULL) {
126 		new_tree = talloc(req, struct ldb_parse_tree);
127 		if (!new_tree) {
128 			return ldb_oom(ldb);
129 		}
130 		new_tree->operation = LDB_OP_AND;
131 		new_tree->u.list.num_elements = 2;
132 		new_tree->u.list.elements = talloc_array(new_tree, struct ldb_parse_tree *, 2);
133 		if (!new_tree->u.list.elements) {
134 			return ldb_oom(ldb);
135 		}
136 
137 		new_tree->u.list.elements[0] = talloc(new_tree->u.list.elements, struct ldb_parse_tree);
138 		new_tree->u.list.elements[0]->operation = LDB_OP_NOT;
139 		new_tree->u.list.elements[0]->u.isnot.child =
140 			talloc(new_tree->u.list.elements, struct ldb_parse_tree);
141 		if (!new_tree->u.list.elements[0]->u.isnot.child) {
142 			return ldb_oom(ldb);
143 		}
144 		new_tree->u.list.elements[0]->u.isnot.child->operation = LDB_OP_EQUALITY;
145 		new_tree->u.list.elements[0]->u.isnot.child->u.equality.attr = exclude_filter;
146 		new_tree->u.list.elements[0]->u.isnot.child->u.equality.value = data_blob_string_const("TRUE");
147 		new_tree->u.list.elements[1] = req->op.search.tree;
148 	}
149 
150 	ret = ldb_build_search_req_ex(&down_req, ldb, req,
151 				      req->op.search.base,
152 				      req->op.search.scope,
153 				      new_tree,
154 				      req->op.search.attrs,
155 				      req->controls,
156 				      req, dsdb_next_callback,
157 				      req);
158 	LDB_REQ_SET_LOCATION(down_req);
159 	if (ret != LDB_SUCCESS) {
160 		return ret;
161 	}
162 
163 	/* mark the controls as done */
164 	if (show_del != NULL) {
165 		show_del->critical = 0;
166 	}
167 	if (show_rec != NULL) {
168 		show_rec->critical = 0;
169 	}
170 
171 	/* perform the search */
172 	return ldb_next_request(module, down_req);
173 }
174 
show_deleted_init(struct ldb_module * module)175 static int show_deleted_init(struct ldb_module *module)
176 {
177 	struct ldb_context *ldb;
178 	int ret;
179 	struct show_deleted_state *state;
180 
181 	state = talloc_zero(module, struct show_deleted_state);
182 	if (state == NULL) {
183 		return ldb_module_oom(module);
184 	}
185 	state->need_refresh = true;
186 
187 	ldb = ldb_module_get_ctx(module);
188 
189 	ret = ldb_mod_register_control(module, LDB_CONTROL_SHOW_DELETED_OID);
190 	if (ret != LDB_SUCCESS) {
191 		ldb_debug(ldb, LDB_DEBUG_ERROR,
192 			"show_deleted: Unable to register control with rootdse!\n");
193 		return ldb_operr(ldb);
194 	}
195 
196 	ret = ldb_mod_register_control(module, LDB_CONTROL_SHOW_RECYCLED_OID);
197 	if (ret != LDB_SUCCESS) {
198 		ldb_debug(ldb, LDB_DEBUG_ERROR,
199 			"show_deleted: Unable to register control with rootdse!\n");
200 		return ldb_operr(ldb);
201 	}
202 
203 	ret = ldb_next_init(module);
204 
205 	ldb_module_set_private(module, state);
206 
207 	return ret;
208 }
209 
210 static const struct ldb_module_ops ldb_show_deleted_module_ops = {
211 	.name		   = "show_deleted",
212 	.search            = show_deleted_search,
213 	.init_context	   = show_deleted_init
214 };
215 
ldb_show_deleted_module_init(const char * version)216 int ldb_show_deleted_module_init(const char *version)
217 {
218 	LDB_MODULE_CHECK_VERSION(version);
219 	return ldb_register_module(&ldb_show_deleted_module_ops);
220 }
221