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