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