s4-dsdb: Explicitly mark some internal ldb requests as trusted
[nivanova/samba.git] / source4 / dsdb / samdb / ldb_modules / ranged_results.c
1 /* 
2    ldb database library
3
4    Copyright (C) Andrew Bartlett 2007
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /*
21  *  Name: ldb
22  *
23  *  Component: ldb ranged results module
24  *
25  *  Description: munge AD-style 'ranged results' requests into
26  *  requests for all values in an attribute, then return the range to
27  *  the client.
28  *
29  *  Author: Andrew Bartlett
30  */
31
32 #include "includes.h"
33 #include "ldb_module.h"
34
35 struct rr_context {
36         struct ldb_module *module;
37         struct ldb_request *req;
38 };
39
40 static struct rr_context *rr_init_context(struct ldb_module *module,
41                                           struct ldb_request *req)
42 {
43         struct rr_context *ac;
44
45         ac = talloc_zero(req, struct rr_context);
46         if (ac == NULL) {
47                 ldb_set_errstring(ldb_module_get_ctx(module), "Out of Memory");
48                 return NULL;
49         }
50
51         ac->module = module;
52         ac->req = req;
53
54         return ac;
55 }
56
57 static int rr_search_callback(struct ldb_request *req, struct ldb_reply *ares)
58 {
59         struct ldb_context *ldb;
60         struct rr_context *ac;
61         unsigned int i, j;
62         TALLOC_CTX *temp_ctx;
63
64         ac = talloc_get_type(req->context, struct rr_context);
65         ldb = ldb_module_get_ctx(ac->module);
66
67         if (!ares) {
68                 return ldb_module_done(ac->req, NULL, NULL,
69                                         LDB_ERR_OPERATIONS_ERROR);
70         }
71         if (ares->error != LDB_SUCCESS) {
72                 return ldb_module_done(ac->req, ares->controls,
73                                         ares->response, ares->error);
74         }
75
76         if (ares->type == LDB_REPLY_REFERRAL) {
77                 return ldb_module_send_referral(ac->req, ares->referral);
78         }
79
80         if (ares->type == LDB_REPLY_DONE) {
81                 return ldb_module_done(ac->req, ares->controls,
82                                         ares->response, ares->error);
83         }
84
85         /* LDB_REPLY_ENTRY */
86
87         temp_ctx = talloc_new(ac->req);
88         if (!temp_ctx) {
89                 ldb_module_oom(ac->module);
90                 return ldb_module_done(ac->req, NULL, NULL,
91                                        LDB_ERR_OPERATIONS_ERROR);
92         }
93
94         /* Find those that are range requests from the attribute list */
95         for (i = 0; ac->req->op.search.attrs[i]; i++) {
96                 char *p, *new_attr;
97                 const char *end_str;
98                 unsigned int start, end, orig_num_values;
99                 struct ldb_message_element *el;
100                 struct ldb_val *orig_values;
101
102                 p = strchr(ac->req->op.search.attrs[i], ';');
103                 if (!p) {
104                         continue;
105                 }
106                 if (strncasecmp(p, ";range=", strlen(";range=")) != 0) {
107                         continue;
108                 }
109                 if (sscanf(p, ";range=%u-%u", &start, &end) != 2) {
110                         if (sscanf(p, ";range=%u-*", &start) == 1) {
111                                 end = (unsigned int)-1;
112                         } else {
113                                 continue;
114                         }
115                 }
116                 new_attr = talloc_strndup(temp_ctx,
117                                           ac->req->op.search.attrs[i],
118                                           (size_t)(p - ac->req->op.search.attrs[i]));
119
120                 if (!new_attr) {
121                         ldb_oom(ldb);
122                         return ldb_module_done(ac->req, NULL, NULL,
123                                                 LDB_ERR_OPERATIONS_ERROR);
124                 }
125                 el = ldb_msg_find_element(ares->message, new_attr);
126                 talloc_free(new_attr);
127                 if (!el) {
128                         continue;
129                 }
130                 if (end >= (el->num_values - 1)) {
131                         /* Need to leave the requested attribute in
132                          * there (so add an empty one to match) */
133                         end_str = "*";
134                         end = el->num_values - 1;
135                 } else {
136                         end_str = talloc_asprintf(temp_ctx, "%u", end);
137                         if (!end_str) {
138                                 ldb_oom(ldb);
139                                 return ldb_module_done(ac->req, NULL, NULL,
140                                                         LDB_ERR_OPERATIONS_ERROR);
141                         }
142                 }
143                 /* If start is greater then where we are find the end to be */
144                 if (start > end) {
145                         el->num_values = 0;
146                         el->values = NULL;
147                 } else {
148                         orig_values = el->values;
149                         orig_num_values = el->num_values;
150                         
151                         if ((start + end < start) || (start + end < end)) {
152                                 ldb_asprintf_errstring(ldb,
153                                         "range request error: start or end would overflow!");
154                                 return ldb_module_done(ac->req, NULL, NULL,
155                                                         LDB_ERR_UNWILLING_TO_PERFORM);
156                         }
157                         
158                         el->num_values = 0;
159                         
160                         el->values = talloc_array(ares->message->elements,
161                                                   struct ldb_val,
162                                                   (end - start) + 1);
163                         if (!el->values) {
164                                 ldb_oom(ldb);
165                                 return ldb_module_done(ac->req, NULL, NULL,
166                                                         LDB_ERR_OPERATIONS_ERROR);
167                         }
168                         for (j=start; j <= end; j++) {
169                                 el->values[el->num_values] = orig_values[j];
170                                 el->num_values++;
171                         }
172                 }
173                 el->name = talloc_asprintf(ares->message->elements,
174                                            "%s;range=%u-%s", el->name, start,
175                                            end_str);
176                 if (!el->name) {
177                         ldb_oom(ldb);
178                         return ldb_module_done(ac->req, NULL, NULL,
179                                                 LDB_ERR_OPERATIONS_ERROR);
180                 }
181         }
182
183         talloc_free(temp_ctx);
184
185         return ldb_module_send_entry(ac->req, ares->message, ares->controls);
186 }
187
188 /* search */
189 static int rr_search(struct ldb_module *module, struct ldb_request *req)
190 {
191         struct ldb_context *ldb;
192         unsigned int i;
193         unsigned int start, end;
194         const char **new_attrs = NULL;
195         bool found_rr = false;
196         struct ldb_request *down_req;
197         struct rr_context *ac;
198         int ret;
199
200         ldb = ldb_module_get_ctx(module);
201
202         /* Strip the range request from the attribute */
203         for (i = 0; req->op.search.attrs && req->op.search.attrs[i]; i++) {
204                 char *p;
205                 new_attrs = talloc_realloc(req, new_attrs, const char *, i+2);
206                 new_attrs[i] = req->op.search.attrs[i];
207                 new_attrs[i+1] = NULL;
208                 p = strchr(new_attrs[i], ';');
209                 if (!p) {
210                         continue;
211                 }
212                 if (strncasecmp(p, ";range=", strlen(";range=")) != 0) {
213                         continue;
214                 }
215                 end = (unsigned int)-1;
216                 if (sscanf(p, ";range=%u-*", &start) != 1) {
217                         if (sscanf(p, ";range=%u-%u", &start, &end) != 2) {
218                                 ldb_asprintf_errstring(ldb,
219                                         "range request error: "
220                                         "range request malformed");
221                                 return LDB_ERR_UNWILLING_TO_PERFORM;
222                         }
223                 }
224                 if (start > end) {
225                         ldb_asprintf_errstring(ldb, "range request error: start must not be greater than end");
226                         return LDB_ERR_UNWILLING_TO_PERFORM;
227                 }
228
229                 found_rr = true;
230                 new_attrs[i] = talloc_strndup(new_attrs, new_attrs[i],
231                                               (size_t)(p - new_attrs[i]));
232
233                 if (!new_attrs[i]) {
234                         return ldb_oom(ldb);
235                 }
236         }
237
238         if (found_rr) {
239                 ac = rr_init_context(module, req);
240                 if (!ac) {
241                         return ldb_operr(ldb);
242                 }
243
244                 ret = ldb_build_search_req_ex(&down_req, ldb, ac,
245                                               req->op.search.base,
246                                               req->op.search.scope,
247                                               req->op.search.tree,
248                                               new_attrs,
249                                               req->controls,
250                                               ac, rr_search_callback,
251                                               req);
252                 ldb_req_mark_trusted(down_req);
253                 LDB_REQ_SET_LOCATION(down_req);
254                 if (ret != LDB_SUCCESS) {
255                         return ret;
256                 }
257                 return ldb_next_request(module, down_req);
258         }
259
260         /* No change, just run the original request as if we were never here */
261         talloc_free(new_attrs);
262         return ldb_next_request(module, req);
263 }
264
265 static const struct ldb_module_ops ldb_ranged_results_module_ops = {
266         .name              = "ranged_results",
267         .search            = rr_search,
268 };
269
270 int ldb_ranged_results_module_init(const char *version)
271 {
272         LDB_MODULE_CHECK_VERSION(version);
273         return ldb_register_module(&ldb_ranged_results_module_ops);
274 }