s4-dsdb: use ldb_operr() in the dsdb code
[samba.git] / source4 / dsdb / samdb / ldb_modules / proxy.c
1 /* 
2    samdb proxy module
3
4    Copyright (C) Andrew Tridgell 2005
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   ldb proxy module. At startup this looks for a record like this:
22
23    dn=@PROXYINFO
24    url=destination url
25    olddn = basedn to proxy in upstream server
26    newdn = basedn in local server
27    username = username to connect to upstream
28    password = password for upstream
29
30    NOTE: this module is a complete hack at this stage. I am committing it just
31    so others can know how I am investigating mmc support
32    
33  */
34
35 #include "includes.h"
36 #include "ldb_module.h"
37 #include "auth/credentials/credentials.h"
38
39 struct proxy_data {
40         struct ldb_context *upstream;
41         struct ldb_dn *olddn;
42         struct ldb_dn *newdn;
43         const char **oldstr;
44         const char **newstr;
45 };
46
47 struct proxy_ctx {
48         struct ldb_module *module;
49         struct ldb_request *req;
50
51 #ifdef DEBUG_PROXY
52         int count;
53 #endif
54 };
55
56 /*
57   load the @PROXYINFO record
58 */
59 static int load_proxy_info(struct ldb_module *module)
60 {
61         struct ldb_context *ldb = ldb_module_get_ctx(module);
62         struct proxy_data *proxy = talloc_get_type(ldb_module_get_private(module), struct proxy_data);
63         struct ldb_dn *dn;
64         struct ldb_result *res = NULL;
65         int ret;
66         const char *olddn, *newdn, *url, *username, *password, *oldstr, *newstr;
67         struct cli_credentials *creds;
68
69         /* see if we have already loaded it */
70         if (proxy->upstream != NULL) {
71                 return LDB_SUCCESS;
72         }
73
74         dn = ldb_dn_new(proxy, ldb, "@PROXYINFO");
75         if (dn == NULL) {
76                 goto failed;
77         }
78         ret = ldb_search(ldb, proxy, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
79         talloc_free(dn);
80         if (ret != LDB_SUCCESS || res->count != 1) {
81                 ldb_debug(ldb, LDB_DEBUG_FATAL, "Can't find @PROXYINFO\n");
82                 goto failed;
83         }
84
85         url      = ldb_msg_find_attr_as_string(res->msgs[0], "url", NULL);
86         olddn    = ldb_msg_find_attr_as_string(res->msgs[0], "olddn", NULL);
87         newdn    = ldb_msg_find_attr_as_string(res->msgs[0], "newdn", NULL);
88         username = ldb_msg_find_attr_as_string(res->msgs[0], "username", NULL);
89         password = ldb_msg_find_attr_as_string(res->msgs[0], "password", NULL);
90         oldstr   = ldb_msg_find_attr_as_string(res->msgs[0], "oldstr", NULL);
91         newstr   = ldb_msg_find_attr_as_string(res->msgs[0], "newstr", NULL);
92
93         if (url == NULL || olddn == NULL || newdn == NULL || username == NULL || password == NULL) {
94                 ldb_debug(ldb, LDB_DEBUG_FATAL, "Need url, olddn, newdn, oldstr, newstr, username and password in @PROXYINFO\n");
95                 goto failed;
96         }
97
98         proxy->olddn = ldb_dn_new(proxy, ldb, olddn);
99         if (proxy->olddn == NULL) {
100                 ldb_debug(ldb, LDB_DEBUG_FATAL, "Failed to explode olddn '%s'\n", olddn);
101                 goto failed;
102         }
103         
104         proxy->newdn = ldb_dn_new(proxy, ldb, newdn);
105         if (proxy->newdn == NULL) {
106                 ldb_debug(ldb, LDB_DEBUG_FATAL, "Failed to explode newdn '%s'\n", newdn);
107                 goto failed;
108         }
109
110         proxy->upstream = ldb_init(proxy, ldb_get_event_context(ldb));
111         if (proxy->upstream == NULL) {
112                 ldb_oom(ldb);
113                 goto failed;
114         }
115
116         proxy->oldstr = str_list_make(proxy, oldstr, ", ");
117         if (proxy->oldstr == NULL) {
118                 ldb_oom(ldb);
119                 goto failed;
120         }
121
122         proxy->newstr = str_list_make(proxy, newstr, ", ");
123         if (proxy->newstr == NULL) {
124                 ldb_oom(ldb);
125                 goto failed;
126         }
127
128         /* setup credentials for connection */
129         creds = cli_credentials_init(proxy->upstream);
130         if (creds == NULL) {
131                 ldb_oom(ldb);
132                 goto failed;
133         }
134         cli_credentials_guess(creds, ldb_get_opaque(ldb, "loadparm"));
135         cli_credentials_set_username(creds, username, CRED_SPECIFIED);
136         cli_credentials_set_password(creds, password, CRED_SPECIFIED);
137
138         ldb_set_opaque(proxy->upstream, "credentials", creds);
139
140         ret = ldb_connect(proxy->upstream, url, 0, NULL);
141         if (ret != 0) {
142                 ldb_debug(ldb, LDB_DEBUG_FATAL, "proxy failed to connect to %s\n", url);
143                 goto failed;
144         }
145
146         ldb_debug(ldb, LDB_DEBUG_TRACE, "proxy connected to %s\n", url);
147
148         talloc_free(res);
149
150         return LDB_SUCCESS;
151
152 failed:
153         talloc_free(res);
154         talloc_free(proxy->olddn);
155         talloc_free(proxy->newdn);
156         talloc_free(proxy->upstream);
157         proxy->upstream = NULL;
158         return ldb_operr(ldb);
159 }
160
161
162 /*
163   convert a binary blob
164 */
165 static void proxy_convert_blob(TALLOC_CTX *mem_ctx, struct ldb_val *v,
166                                const char *oldstr, const char *newstr)
167 {
168         size_t len1, len2, len3;
169         uint8_t *olddata = v->data;
170         char *p = strcasestr((char *)v->data, oldstr);
171
172         len1 = (p - (char *)v->data);
173         len2 = strlen(newstr);
174         len3 = v->length - (p+strlen(oldstr) - (char *)v->data);
175         v->length = len1+len2+len3;
176         v->data = talloc_size(mem_ctx, v->length);
177         memcpy(v->data, olddata, len1);
178         memcpy(v->data+len1, newstr, len2);
179         memcpy(v->data+len1+len2, olddata + len1 + strlen(oldstr), len3);
180 }
181
182 /*
183   convert a returned value
184 */
185 static void proxy_convert_value(struct proxy_data *proxy, struct ldb_message *msg, struct ldb_val *v)
186 {
187         size_t i;
188
189         for (i=0;proxy->oldstr[i];i++) {
190                 char *p = strcasestr((char *)v->data, proxy->oldstr[i]);
191                 if (p == NULL) continue;
192                 proxy_convert_blob(msg, v, proxy->oldstr[i], proxy->newstr[i]);
193         }
194 }
195
196
197 /*
198   convert a returned value
199 */
200 static struct ldb_parse_tree *proxy_convert_tree(TALLOC_CTX *mem_ctx,
201                                                  struct proxy_data *proxy,
202                                                  struct ldb_parse_tree *tree)
203 {
204         size_t i;
205         char *expression = ldb_filter_from_tree(mem_ctx, tree);
206
207         for (i=0;proxy->newstr[i];i++) {
208                 struct ldb_val v;
209                 char *p = strcasestr(expression, proxy->newstr[i]);
210                 if (p == NULL) continue;
211                 v.data = (uint8_t *)expression;
212                 v.length = strlen(expression)+1;
213                 proxy_convert_blob(mem_ctx, &v, proxy->newstr[i], proxy->oldstr[i]);
214                 return ldb_parse_tree(mem_ctx, (const char *)v.data);
215         }
216         return tree;
217 }
218
219
220
221 /*
222   convert a returned record
223 */
224 static void proxy_convert_record(struct ldb_context *ldb,
225                                  struct proxy_data *proxy,
226                                  struct ldb_message *msg)
227 {
228         unsigned int attr, v;
229
230         /* fix the message DN */
231         if (ldb_dn_compare_base(proxy->olddn, msg->dn) == 0) {
232                 ldb_dn_remove_base_components(msg->dn, ldb_dn_get_comp_num(proxy->olddn));
233                 ldb_dn_add_base(msg->dn, proxy->newdn);
234         }
235
236         /* fix any attributes */
237         for (attr=0;attr<msg->num_elements;attr++) {
238                 for (v=0;v<msg->elements[attr].num_values;v++) {
239                         proxy_convert_value(proxy, msg, &msg->elements[attr].values[v]);
240                 }
241         }
242
243         /* fix any DN components */
244         for (attr=0;attr<msg->num_elements;attr++) {
245                 for (v=0;v<msg->elements[attr].num_values;v++) {
246                         proxy_convert_value(proxy, msg, &msg->elements[attr].values[v]);
247                 }
248         }
249 }
250
251 static int proxy_search_callback(struct ldb_request *req,
252                                   struct ldb_reply *ares)
253 {
254         struct ldb_context *ldb;
255         struct proxy_data *proxy;
256         struct proxy_ctx *ac;
257         int ret;
258
259         ac = talloc_get_type(req->context, struct proxy_ctx);
260         ldb = ldb_module_get_ctx(ac->module);
261         proxy = talloc_get_type(ldb_module_get_private(module), struct proxy_data);
262
263         if (!ares) {
264                 return ldb_module_done(ac->req, NULL, NULL,
265                                         LDB_ERR_OPERATIONS_ERROR);
266         }
267         if (ares->error != LDB_SUCCESS) {
268                 return ldb_module_done(ac->req, ares->controls,
269                                         ares->response, ares->error);
270         }
271
272         /* Only entries are interesting, and we only want the olddn */
273         switch (ares->type) {
274         case LDB_REPLY_ENTRY:
275
276 #ifdef DEBUG_PROXY
277                 ac->count++;
278 #endif
279                 proxy_convert_record(ldb, proxy, ares->message);
280                 ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
281                 break;
282
283         case LDB_REPLY_REFERRAL:
284
285                 /* ignore remote referrals */
286                 break;
287
288         case LDB_REPLY_DONE:
289
290 #ifdef DEBUG_PROXY
291                 printf("# record %d\n", ac->count+1);
292 #endif
293
294                 return ldb_module_done(ac->req, NULL, NULL, LDB_SUCCESS);
295         }
296
297         talloc_free(ares);
298         return ret;
299 }
300
301 /* search */
302 static int proxy_search_bytree(struct ldb_module *module, struct ldb_request *req)
303 {
304         struct ldb_context *ldb;
305         struct proxy_ctx *ac;
306         struct ldb_parse_tree *newtree;
307         struct proxy_data *proxy = talloc_get_type(ldb_module_get_private(module), struct proxy_data);
308         struct ldb_request *newreq;
309         struct ldb_dn *base;
310         unsigned int i;
311         int ret;
312
313         ldb = ldb_module_get_ctx(module);
314
315         if (req->op.search.base == NULL ||
316                 (req->op.search.base->comp_num == 1 &&
317                         req->op.search.base->components[0].name[0] == '@')) {
318                 goto passthru;
319         }
320
321         if (load_proxy_info(module) != LDB_SUCCESS) {
322                 return ldb_operr(ldb);
323         }
324
325         /* see if the dn is within olddn */
326         if (ldb_dn_compare_base(proxy->newdn, req->op.search.base) != 0) {
327                 goto passthru;
328         }
329
330         ac = talloc(req, struct proxy_ctx);
331         if (ac == NULL) {
332                 return ldb_oom(ldb);
333         }
334
335         ac->module = module;
336         ac->req = req;
337 #ifdef DEBUG_PROXY
338         ac->count = 0;
339 #endif
340
341         newtree = proxy_convert_tree(ac, proxy, req->op.search.tree);
342
343         /* convert the basedn of this search */
344         base = ldb_dn_copy(ac, req->op.search.base);
345         if (base == NULL) {
346                 goto failed;
347         }
348         ldb_dn_remove_base_components(base, ldb_dn_get_comp_num(proxy->newdn));
349         ldb_dn_add_base(base, proxy->olddn);
350
351         ldb_debug(ldb, LDB_DEBUG_FATAL, "proxying: '%s' with dn '%s' \n", 
352                   ldb_filter_from_tree(ac, newreq->op.search.tree), ldb_dn_get_linearized(newreq->op.search.base));
353         for (i = 0; req->op.search.attrs && req->op.search.attrs[i]; i++) {
354                 ldb_debug(ldb, LDB_DEBUG_FATAL, "attr: '%s'\n", req->op.search.attrs[i]);
355         }
356
357         ret = ldb_build_search_req_ex(&newreq, ldb, ac,
358                                       base, req->op.search.scope,
359                                       newtree, req->op.search.attrs,
360                                       req->controls,
361                                       ac, proxy_search_callback,
362                                       req);
363
364         /* FIXME: warning, need a real event system hooked up for this to work properly,
365          *        for now this makes the module *not* ASYNC */
366         ret = ldb_request(proxy->upstream, newreq);
367         if (ret != LDB_SUCCESS) {
368                 ldb_set_errstring(ldb, ldb_errstring(proxy->upstream));
369         }
370         ret = ldb_wait(newreq->handle, LDB_WAIT_ALL);
371         if (ret != LDB_SUCCESS) {
372                 ldb_set_errstring(ldb, ldb_errstring(proxy->upstream));
373         }
374         return ret;
375
376 failed:
377         ldb_debug(ldb, LDB_DEBUG_TRACE, "proxy failed for %s\n", 
378                   ldb_dn_get_linearized(req->op.search.base));
379
380 passthru:
381         return ldb_next_request(module, req); 
382 }
383
384 static int proxy_request(struct ldb_module *module, struct ldb_request *req)
385 {
386         switch (req->operation) {
387
388         case LDB_REQ_SEARCH:
389                 return proxy_search_bytree(module, req);
390
391         default:
392                 return ldb_next_request(module, req);
393
394         }
395 }
396
397 _PUBLIC_ const struct ldb_module_ops ldb_proxy_module_ops = {
398         .name           = "proxy",
399         .request        = proxy_request
400 };