4 Copyright (C) Andrew Tridgell 2005
6 ** NOTE! The following LGPL license applies to the ldb
7 ** library. This does NOT imply that all of Samba is released
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.
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.
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/>.
25 ldb proxy module. At startup this looks for a record like this:
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
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
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"
46 struct ldb_context *upstream;
54 struct ldb_module *module;
55 struct ldb_request *req;
63 load the @PROXYINFO record
65 static int load_proxy_info(struct ldb_module *module)
67 struct proxy_data *proxy = talloc_get_type(module->private_data, struct proxy_data);
69 struct ldb_result *res = NULL;
71 const char *olddn, *newdn, *url, *username, *password, *oldstr, *newstr;
72 struct cli_credentials *creds;
74 /* see if we have already loaded it */
75 if (proxy->upstream != NULL) {
79 dn = ldb_dn_new(proxy, module->ldb, "@PROXYINFO");
83 ret = ldb_search(module->ldb, proxy, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
85 if (ret != LDB_SUCCESS || res->count != 1) {
86 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "Can't find @PROXYINFO\n");
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);
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");
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);
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);
115 proxy->upstream = ldb_init(proxy, ldb_get_event_context(ldb));
116 if (proxy->upstream == NULL) {
117 ldb_oom(module->ldb);
121 proxy->oldstr = str_list_make(proxy, oldstr, ", ");
122 if (proxy->oldstr == NULL) {
123 ldb_oom(module->ldb);
127 proxy->newstr = str_list_make(proxy, newstr, ", ");
128 if (proxy->newstr == NULL) {
129 ldb_oom(module->ldb);
133 /* setup credentials for connection */
134 creds = cli_credentials_init(proxy->upstream);
136 ldb_oom(module->ldb);
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);
143 ldb_set_opaque(proxy->upstream, "credentials", creds);
145 ret = ldb_connect(proxy->upstream, url, 0, NULL);
147 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "proxy failed to connect to %s\n", url);
151 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "proxy connected to %s\n", url);
159 talloc_free(proxy->olddn);
160 talloc_free(proxy->newdn);
161 talloc_free(proxy->upstream);
162 proxy->upstream = NULL;
168 convert a binary blob
170 static void proxy_convert_blob(TALLOC_CTX *mem_ctx, struct ldb_val *v,
171 const char *oldstr, const char *newstr)
173 int len1, len2, len3;
174 uint8_t *olddata = v->data;
175 char *p = strcasestr((char *)v->data, oldstr);
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);
188 convert a returned value
190 static void proxy_convert_value(struct proxy_data *proxy, struct ldb_message *msg, struct ldb_val *v)
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]);
203 convert a returned value
205 static struct ldb_parse_tree *proxy_convert_tree(TALLOC_CTX *mem_ctx,
206 struct proxy_data *proxy,
207 struct ldb_parse_tree *tree)
210 char *expression = ldb_filter_from_tree(mem_ctx, tree);
212 for (i=0;proxy->newstr[i];i++) {
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);
227 convert a returned record
229 static void proxy_convert_record(struct ldb_context *ldb,
230 struct proxy_data *proxy,
231 struct ldb_message *msg)
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);
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]);
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]);
256 static int proxy_search_callback(struct ldb_request *req,
257 struct ldb_reply *ares)
259 struct proxy_data *proxy;
260 struct proxy_ctx *ac;
263 ac = talloc_get_type(req->context, struct proxy_ctx);
264 proxy = talloc_get_type(ac->module->private_data, struct proxy_data);
267 return ldb_module_done(ac->req, NULL, NULL,
268 LDB_ERR_OPERATIONS_ERROR);
270 if (ares->error != LDB_SUCCESS) {
271 return ldb_module_done(ac->req, ares->controls,
272 ares->response, ares->error);
275 /* Only entries are interesting, and we only want the olddn */
276 switch (ares->type) {
277 case LDB_REPLY_ENTRY:
282 proxy_convert_record(ac->module->ldb, proxy, ares->message);
283 ret = ldb_module_send_entry(ac->req, ares->message);
286 case LDB_REPLY_REFERRAL:
288 /* ignore remote referrals */
294 printf("# record %d\n", ac->count+1);
297 return ldb_module_done(ac->req, NULL, NULL, LDB_SUCCESS);
305 static int proxy_search_bytree(struct ldb_module *module, struct ldb_request *req)
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;
314 if (req->op.search.base == NULL ||
315 (req->op.search.base->comp_num == 1 &&
316 req->op.search.base->components[0].name[0] == '@')) {
320 if (load_proxy_info(module) != 0) {
321 return LDB_ERR_OPERATIONS_ERROR;
324 /* see if the dn is within olddn */
325 if (ldb_dn_compare_base(module->ldb, proxy->newdn, req->op.search.base) != 0) {
329 ac = talloc(req, struct proxy_ctx);
331 return LDB_ERR_OPERATIONS_ERROR;
340 newtree = proxy_convert_tree(ac, proxy, req->op.search.tree);
342 /* convert the basedn of this search */
343 base = ldb_dn_copy(ac, req->op.search.base);
347 ldb_dn_remove_base_components(base, ldb_dn_get_comp_num(proxy->newdn));
348 ldb_dn_add_base(base, proxy->olddn);
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]);
356 ret = ldb_build_search_req_ex(&newreq, module->ldb, ac,
357 base, req->op.search.scope,
358 newtree, req->op.search.attrs,
360 ac, proxy_search_callback,
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));
369 ret = ldb_wait(newreq->handle, LDB_WAIT_ALL);
370 if (ret != LDB_SUCCESS) {
371 ldb_set_errstring(module->ldb, ldb_errstring(proxy->upstream));
376 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "proxy failed for %s\n",
377 ldb_dn_get_linearized(req->op.search.base));
380 return ldb_next_request(module, req);
383 static int proxy_request(struct ldb_module *module, struct ldb_request *req)
385 switch (req->operation) {
388 return proxy_search_bytree(module, req);
391 return ldb_next_request(module, req);
396 _PUBLIC_ const struct ldb_module_ops ldb_proxy_module_ops = {
398 .request = proxy_request