diff options
author | Jelmer Vernooij <jelmer@samba.org> | 2008-10-11 14:44:10 +0200 |
---|---|---|
committer | Jelmer Vernooij <jelmer@samba.org> | 2008-10-11 14:44:10 +0200 |
commit | 6a78e56277799672b7ac187c57e546836e136f79 (patch) | |
tree | 87f0336cb1908d01690b74c56a44f4713559b5bc /source4/dsdb/samdb/ldb_modules/local_password.c | |
parent | ddbddbd80c80b872cdd36a01f9a3a6bc2eca1b1f (diff) | |
parent | f0a27064869871806343648de3b5a0667118872f (diff) | |
download | samba-6a78e56277799672b7ac187c57e546836e136f79.tar.gz samba-6a78e56277799672b7ac187c57e546836e136f79.tar.bz2 samba-6a78e56277799672b7ac187c57e546836e136f79.zip |
Merge branch 'master' of ssh://git.samba.org/data/git/samba into arc4
Diffstat (limited to 'source4/dsdb/samdb/ldb_modules/local_password.c')
-rw-r--r-- | source4/dsdb/samdb/ldb_modules/local_password.c | 1124 |
1 files changed, 670 insertions, 454 deletions
diff --git a/source4/dsdb/samdb/ldb_modules/local_password.c b/source4/dsdb/samdb/ldb_modules/local_password.c index a411c01513..622e444166 100644 --- a/source4/dsdb/samdb/ldb_modules/local_password.c +++ b/source4/dsdb/samdb/ldb_modules/local_password.c @@ -1,7 +1,7 @@ /* ldb database module - Copyright (C) Simo Sorce 2004-2006 + Copyright (C) Simo Sorce 2004-2008 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006 Copyright (C) Andrew Tridgell 2004 @@ -44,7 +44,7 @@ This allows the password database to be syncronised in a multi-master fashion, seperate to the more difficult concerns of the main database. (With passwords, the last writer always wins) - + Each incoming add/modify is split into a remote, and a local request, done in that order. We maintain a list of attributes that are kept locally: @@ -62,73 +62,89 @@ static const char * const password_attrs[] = { /* And we merge them back into search requests when asked to do so */ -struct lpdb_context { +struct lpdb_reply { + struct lpdb_reply *next; + struct ldb_reply *remote; + struct ldb_dn *local_dn; +}; - enum lpdb_type {LPDB_ADD, LPDB_MOD, LPDB_SEARCH} type; - enum lpdb_step {LPDB_ADD_REMOTE, LPDB_MOD_REMOTE, LPDB_MOD_SEARCH_SELF, LPDB_LOCAL, LPDB_SEARCH_REMOTE} step; +struct lpdb_context { struct ldb_module *module; - struct ldb_request *orig_req; - struct ldb_request *remote_req; - struct ldb_request *search_req; - struct ldb_request *local_req; + struct ldb_request *req; struct ldb_message *local_message; + struct lpdb_reply *list; + struct lpdb_reply *current; + struct ldb_reply *remote_done; + struct ldb_reply *remote; + bool added_objectGUID; bool added_objectClass; - struct ldb_reply *search_res; -}; - -struct lpdb_local_search_context { - struct lpdb_context *ac; - struct ldb_reply *remote_res; - struct ldb_reply *local_res; }; -static struct ldb_handle *lpdb_init_handle(struct ldb_request *req, struct ldb_module *module, enum lpdb_type type) +static struct lpdb_context *lpdb_init_context(struct ldb_module *module, + struct ldb_request *req) { struct lpdb_context *ac; - struct ldb_handle *h; - h = talloc_zero(req, struct ldb_handle); - if (h == NULL) { + ac = talloc_zero(req, struct lpdb_context); + if (ac == NULL) { ldb_set_errstring(module->ldb, "Out of Memory"); return NULL; } - h->module = module; + ac->module = module; + ac->req = req; - ac = talloc_zero(h, struct lpdb_context); - if (ac == NULL) { - ldb_set_errstring(module->ldb, "Out of Memory"); - talloc_free(h); - return NULL; - } + return ac; +} - h->private_data = (void *)ac; +static int lpdb_local_callback(struct ldb_request *req, struct ldb_reply *ares) +{ + struct lpdb_context *ac; - h->state = LDB_ASYNC_INIT; - h->status = LDB_SUCCESS; + ac = talloc_get_type(req->context, struct lpdb_context); - ac->type = type; - ac->module = module; - ac->orig_req = req; + if (!ares) { + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + if (ares->error != LDB_SUCCESS) { + return ldb_module_done(ac->req, ares->controls, + ares->response, ares->error); + } + + if (ares->type != LDB_REPLY_DONE) { + ldb_set_errstring(ac->module->ldb, "Unexpected reply type"); + talloc_free(ares); + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } - return h; + talloc_free(ares); + return ldb_module_done(ac->req, + ac->remote_done->controls, + ac->remote_done->response, + ac->remote_done->error); } -/* Add a record, splitting password attributes from the user's main - * record */ +/***************************************************************************** + * ADD + ****************************************************************************/ + +static int lpdb_add_callback(struct ldb_request *req, + struct ldb_reply *ares); static int local_password_add(struct ldb_module *module, struct ldb_request *req) { - struct ldb_handle *h; - struct lpdb_context *ac; struct ldb_message *remote_message; - struct ldb_message *local_message; + struct ldb_request *remote_req; + struct lpdb_context *ac; struct GUID objectGUID; + int ret; int i; ldb_debug(module->ldb, LDB_DEBUG_TRACE, "local_password_add\n"); @@ -163,22 +179,12 @@ static int local_password_add(struct ldb_module *module, struct ldb_request *req } /* From here, we assume we have password attributes to split off */ - h = lpdb_init_handle(req, module, LPDB_ADD); - if (!h) { + ac = lpdb_init_context(module, req); + if (!ac) { return LDB_ERR_OPERATIONS_ERROR; } - ac = talloc_get_type(h->private_data, struct lpdb_context); - ac->orig_req = req; - - ac->remote_req = talloc(ac, struct ldb_request); - if (ac->remote_req == NULL) { - return LDB_ERR_OPERATIONS_ERROR; - } - - *(ac->remote_req) = *(ac->orig_req); - - remote_message = ldb_msg_copy_shallow(ac->remote_req, ac->orig_req->op.add.message); + remote_message = ldb_msg_copy_shallow(remote_req, req->op.add.message); if (remote_message == NULL) { return LDB_ERR_OPERATIONS_ERROR; } @@ -188,81 +194,113 @@ static int local_password_add(struct ldb_module *module, struct ldb_request *req ldb_msg_remove_attr(remote_message, password_attrs[i]); } - ac->remote_req->op.add.message = remote_message; - - ac->remote_req->context = NULL; - ac->remote_req->callback = NULL; - - ac->local_req = talloc(ac, struct ldb_request); - if (ac->local_req == NULL) { - return LDB_ERR_OPERATIONS_ERROR; - } + /* Find the objectGUID to use as the key */ + objectGUID = samdb_result_guid(ac->req->op.add.message, "objectGUID"); - *(ac->local_req) = *(ac->orig_req); - local_message = ldb_msg_copy_shallow(ac->local_req, ac->orig_req->op.add.message); - if (local_message == NULL) { + ac->local_message = ldb_msg_copy_shallow(ac, req->op.add.message); + if (ac->local_message == NULL) { return LDB_ERR_OPERATIONS_ERROR; } /* Remove anything seen in the remote message from the local * message (leaving only password attributes) */ - for (i=0;i<ac->remote_req->op.add.message->num_elements;i++) { - ldb_msg_remove_attr(local_message, ac->remote_req->op.add.message->elements[i].name); + for (i=0; i < remote_message->num_elements; i++) { + ldb_msg_remove_attr(ac->local_message, remote_message->elements[i].name); } /* We must have an objectGUID already, or we don't know where * to add the password. This may be changed to an 'add and * search', to allow the directory to create the objectGUID */ - if (ldb_msg_find_ldb_val(ac->orig_req->op.add.message, "objectGUID") == NULL) { - ldb_set_errstring(module->ldb, - "no objectGUID found in search: local_password module must be configured below objectGUID module!\n"); + if (ldb_msg_find_ldb_val(req->op.add.message, "objectGUID") == NULL) { + ldb_set_errstring(module->ldb, + "no objectGUID found in search: " + "local_password module must be " + "onfigured below objectGUID module!\n"); return LDB_ERR_CONSTRAINT_VIOLATION; } - /* Find the objectGUID to use as the key */ - objectGUID = samdb_result_guid(ac->orig_req->op.add.message, "objectGUID"); - - local_message->dn = ldb_dn_new(local_message, module->ldb, LOCAL_BASE); - ldb_dn_add_child_fmt(local_message->dn, PASSWORD_GUID_ATTR "=%s", GUID_string(local_message, &objectGUID)); - - ac->local_req->op.add.message = local_message; - - ac->local_req->context = NULL; - ac->local_req->callback = NULL; - - ac->step = LPDB_ADD_REMOTE; + ac->local_message->dn = ldb_dn_new(ac->local_message, + module->ldb, LOCAL_BASE); + if ((ac->local_message->dn == NULL) || + ( ! ldb_dn_add_child_fmt(ac->local_message->dn, + PASSWORD_GUID_ATTR "=%s", + GUID_string(ac->local_message, + &objectGUID)))) { + return LDB_ERR_OPERATIONS_ERROR; + } - /* Return our own handle do deal with this call */ - req->handle = h; + ret = ldb_build_add_req(&remote_req, module->ldb, ac, + remote_message, + req->controls, + ac, lpdb_add_callback, + req); + if (ret != LDB_SUCCESS) { + return ret; + } - return ldb_next_request(module, ac->remote_req); + return ldb_next_request(module, remote_req); } -/* After adding the remote entry, add the local one */ -static int local_password_add_local(struct ldb_handle *h) { - +/* Add a record, splitting password attributes from the user's main + * record */ +static int lpdb_add_callback(struct ldb_request *req, + struct ldb_reply *ares) +{ + struct ldb_request *local_req; struct lpdb_context *ac; - ac = talloc_get_type(h->private_data, struct lpdb_context); + int ret; + + ac = talloc_get_type(req->context, struct lpdb_context); - h->state = LDB_ASYNC_INIT; - h->status = LDB_SUCCESS; + if (!ares) { + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + if (ares->error != LDB_SUCCESS) { + return ldb_module_done(ac->req, ares->controls, + ares->response, ares->error); + } - ac->step = LPDB_LOCAL; + if (ares->type != LDB_REPLY_DONE) { + ldb_set_errstring(ac->module->ldb, "Unexpected reply type"); + talloc_free(ares); + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } - ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req); + ac->remote_done = talloc_steal(ac, ares); - /* perform the local add */ - return ldb_next_request(ac->module, ac->local_req); + ret = ldb_build_add_req(&local_req, ac->module->ldb, ac, + ac->local_message, + NULL, + ac, lpdb_local_callback, + ac->req); + if (ret != LDB_SUCCESS) { + return ldb_module_done(ac->req, NULL, NULL, ret); + } + + ret = ldb_next_request(ac->module, local_req); + if (ret != LDB_SUCCESS) { + return ldb_module_done(ac->req, NULL, NULL, ret); + } + return LDB_SUCCESS; } -static int local_password_mod_search_self(struct ldb_handle *h); +/***************************************************************************** + * MODIFY + ****************************************************************************/ + +static int lpdb_modify_callabck(struct ldb_request *req, + struct ldb_reply *ares); +static int lpdb_mod_search_callback(struct ldb_request *req, + struct ldb_reply *ares); static int local_password_modify(struct ldb_module *module, struct ldb_request *req) { - struct ldb_handle *h; struct lpdb_context *ac; struct ldb_message *remote_message; - struct ldb_message *local_message; + struct ldb_request *remote_req; + int ret; int i; ldb_debug(module->ldb, LDB_DEBUG_TRACE, "local_password_modify\n"); @@ -289,243 +327,565 @@ static int local_password_modify(struct ldb_module *module, struct ldb_request * } /* From here, we assume we have password attributes to split off */ - h = lpdb_init_handle(req, module, LPDB_MOD); - if (!h) { + ac = lpdb_init_context(module, req); + if (!ac) { return LDB_ERR_OPERATIONS_ERROR; } - ac = talloc_get_type(h->private_data, struct lpdb_context); - ac->orig_req = req; - - ac->remote_req = talloc(ac, struct ldb_request); - if (ac->remote_req == NULL) { - return LDB_ERR_OPERATIONS_ERROR; - } - - *(ac->remote_req) = *(ac->orig_req); - remote_message = ldb_msg_copy_shallow(ac->remote_req, ac->orig_req->op.mod.message); + remote_message = ldb_msg_copy_shallow(ac, ac->req->op.mod.message); if (remote_message == NULL) { return LDB_ERR_OPERATIONS_ERROR; } - + /* Remove any password attributes from the remote message */ for (i=0; i < ARRAY_SIZE(password_attrs); i++) { ldb_msg_remove_attr(remote_message, password_attrs[i]); } - ac->remote_req->op.mod.message = remote_message; - - ac->remote_req->context = NULL; - ac->remote_req->callback = NULL; - - ac->local_req = talloc(ac, struct ldb_request); - if (ac->local_req == NULL) { - return LDB_ERR_OPERATIONS_ERROR; - } - - *(ac->local_req) = *(ac->orig_req); - local_message = ldb_msg_copy_shallow(ac->local_req, ac->orig_req->op.mod.message); - if (local_message == NULL) { + ac->local_message = ldb_msg_copy_shallow(ac, ac->req->op.mod.message); + if (ac->local_message == NULL) { return LDB_ERR_OPERATIONS_ERROR; } /* Remove anything seen in the remote message from the local * message (leaving only password attributes) */ - for (i=0;i<ac->remote_req->op.mod.message->num_elements;i++) { - ldb_msg_remove_attr(local_message, ac->remote_req->op.mod.message->elements[i].name); + for (i=0; i < remote_message->num_elements;i++) { + ldb_msg_remove_attr(ac->local_message, remote_message->elements[i].name); } - ac->local_req->op.mod.message = local_message; - ac->local_message = local_message; + ret = ldb_build_mod_req(&remote_req, module->ldb, ac, + remote_message, + req->controls, + ac, lpdb_modify_callabck, + req); + if (ret != LDB_SUCCESS) { + return ret; + } - ac->local_req->context = NULL; - ac->local_req->callback = NULL; + return ldb_next_request(module, remote_req); +} - ac->step = LPDB_MOD_REMOTE; +/* On a modify, we don't have the objectGUID handy, so we need to + * search our DN for it */ +static int lpdb_modify_callabck(struct ldb_request *req, + struct ldb_reply *ares) +{ + static const char * const attrs[] = { "objectGUID", "objectClass", NULL }; + struct ldb_request *search_req; + struct lpdb_context *ac; + int ret; + + ac = talloc_get_type(req->context, struct lpdb_context); + + if (!ares) { + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + if (ares->error != LDB_SUCCESS) { + return ldb_module_done(ac->req, ares->controls, + ares->response, ares->error); + } + + if (ares->type != LDB_REPLY_DONE) { + ldb_set_errstring(ac->module->ldb, "Unexpected reply type"); + talloc_free(ares); + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + + ac->remote_done = talloc_steal(ac, ares); - /* Return our own handle do deal with this call */ - req->handle = h; + /* prepare the search operation */ + ret = ldb_build_search_req(&search_req, ac->module->ldb, ac, + ac->req->op.mod.message->dn, LDB_SCOPE_BASE, + "(objectclass=*)", attrs, + NULL, + ac, lpdb_mod_search_callback, + ac->req); + if (ret != LDB_SUCCESS) { + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } - return ldb_next_request(module, ac->remote_req); + ret = ldb_next_request(ac->module, search_req); + if (ret != LDB_SUCCESS) { + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + return LDB_SUCCESS; } -/* Called when we search for our oen entry. Stores the one entry we +/* Called when we search for our own entry. Stores the one entry we * expect (as it is a base search) on the context pointer */ -static int get_self_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) +static int lpdb_mod_search_callback(struct ldb_request *req, + struct ldb_reply *ares) { + struct ldb_request *local_req; struct lpdb_context *ac; + struct ldb_dn *local_dn; + struct GUID objectGUID; + int ret = LDB_SUCCESS; - ac = talloc_get_type(context, struct lpdb_context); + ac = talloc_get_type(req->context, struct lpdb_context); + + if (!ares) { + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + if (ares->error != LDB_SUCCESS) { + return ldb_module_done(ac->req, ares->controls, + ares->response, ares->error); + } - /* we are interested only in the single reply (base search) we receive here */ - if (ares->type == LDB_REPLY_ENTRY) { - if (ac->search_res != NULL) { - ldb_set_errstring(ldb, "Too many results"); + switch (ares->type) { + case LDB_REPLY_ENTRY: + if (ac->remote != NULL) { + ldb_set_errstring(ac->module->ldb, "Too many results"); talloc_free(ares); - return LDB_ERR_OPERATIONS_ERROR; + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); } - ac->search_res = talloc_steal(ac, ares); - } else { + ac->remote = talloc_steal(ac, ares); + break; + + case LDB_REPLY_REFERRAL: + + /* ignore */ + talloc_free(ares); + break; + + case LDB_REPLY_DONE: + /* After we find out the objectGUID for the entry, modify the local + * password database as required */ + talloc_free(ares); + + /* if it is not an entry of type person this is an error */ + /* TODO: remove this when sambaPassword will be in schema */ + if (ac->remote == NULL) { + ldb_asprintf_errstring(ac->module->ldb, + "entry just modified (%s) not found!", + ldb_dn_get_linearized(req->op.search.base)); + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + if (!ldb_msg_check_string_attribute(ac->remote->message, + "objectClass", "person")) { + /* Not relevent to us */ + return ldb_module_done(ac->req, + ac->remote_done->controls, + ac->remote_done->response, + ac->remote_done->error); + } + + if (ldb_msg_find_ldb_val(ac->remote->message, + "objectGUID") == NULL) { + ldb_set_errstring(ac->module->ldb, + "no objectGUID found in search: " + "local_password module must be " + "configured below objectGUID " + "module!\n"); + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OBJECT_CLASS_VIOLATION); + } + + objectGUID = samdb_result_guid(ac->remote->message, + "objectGUID"); + + local_dn = ldb_dn_new(ac, ac->module->ldb, LOCAL_BASE); + if ((local_dn == NULL) || + ( ! ldb_dn_add_child_fmt(local_dn, + PASSWORD_GUID_ATTR "=%s", + GUID_string(ac, &objectGUID)))) { + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + ac->local_message->dn = local_dn; + + ret = ldb_build_mod_req(&local_req, ac->module->ldb, ac, + ac->local_message, + NULL, + ac, lpdb_local_callback, + ac->req); + if (ret != LDB_SUCCESS) { + return ldb_module_done(ac->req, NULL, NULL, ret); + } + + /* perform the local update */ + ret = ldb_next_request(ac->module, local_req); + if (ret != LDB_SUCCESS) { + return ldb_module_done(ac->req, NULL, NULL, ret); + } } return LDB_SUCCESS; } -/* On a modify, we don't have the objectGUID handy, so we need to - * search our DN for it */ -static int local_password_mod_search_self(struct ldb_handle *h) { +/***************************************************************************** + * DELETE + ****************************************************************************/ +static int lpdb_delete_callabck(struct ldb_request *req, + struct ldb_reply *ares); +static int lpdb_del_search_callback(struct ldb_request *req, + struct ldb_reply *ares); + +static int local_password_delete(struct ldb_module *module, + struct ldb_request *req) +{ + struct ldb_request *remote_req; struct lpdb_context *ac; - static const char * const attrs[] = { "objectGUID", "objectClass", NULL }; + int ret; - ac = talloc_get_type(h->private_data, struct lpdb_context); + ldb_debug(module->ldb, LDB_DEBUG_TRACE, "local_password_delete\n"); - /* prepare the search operation */ - ac->search_req = talloc_zero(ac, struct ldb_request); - if (ac->search_req == NULL) { - ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n"); - return LDB_ERR_OPERATIONS_ERROR; + /* do not manipulate our control entries */ + if (ldb_dn_is_special(req->op.mod.message->dn)) { + return ldb_next_request(module, req); + } + + /* If the caller is manipulating the local passwords directly, + * let them pass */ + if (ldb_dn_compare_base(ldb_dn_new(req, module->ldb, LOCAL_BASE), + req->op.del.dn) == 0) { + return ldb_next_request(module, req); } - ac->search_req->operation = LDB_SEARCH; - ac->search_req->op.search.base = ac->orig_req->op.mod.message->dn; - ac->search_req->op.search.scope = LDB_SCOPE_BASE; - ac->search_req->op.search.tree = ldb_parse_tree(ac->orig_req, NULL); - if (ac->search_req->op.search.tree == NULL) { - ldb_set_errstring(ac->module->ldb, "Invalid search filter"); + /* From here, we assume we have password attributes to split off */ + ac = lpdb_init_context(module, req); + if (!ac) { return LDB_ERR_OPERATIONS_ERROR; } - ac->search_req->op.search.attrs = attrs; - ac->search_req->controls = NULL; - ac->search_req->context = ac; - ac->search_req->callback = get_self_callback; - ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->search_req); - ac->step = LPDB_MOD_SEARCH_SELF; + ret = ldb_build_del_req(&remote_req, module->ldb, ac, + req->op.del.dn, + req->controls, + ac, lpdb_delete_callabck, + req); + if (ret != LDB_SUCCESS) { + return ret; + } - return ldb_next_request(ac->module, ac->search_req); + return ldb_next_request(module, remote_req); } -/* After we find out the objectGUID for the entry, modify the local - * password database as required */ -static int local_password_mod_local(struct ldb_handle *h) { +/* On a modify, we don't have the objectGUID handy, so we need to + * search our DN for it */ +static int lpdb_delete_callabck(struct ldb_request *req, + struct ldb_reply *ares) +{ + static const char * const attrs[] = { "objectGUID", "objectClass", NULL }; + struct ldb_request *search_req; + struct lpdb_context *ac; + int ret; + ac = talloc_get_type(req->context, struct lpdb_context); + + if (!ares) { + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + if (ares->error != LDB_SUCCESS) { + return ldb_module_done(ac->req, ares->controls, + ares->response, ares->error); + } + + if (ares->type != LDB_REPLY_DONE) { + ldb_set_errstring(ac->module->ldb, "Unexpected reply type"); + talloc_free(ares); + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + + ac->remote_done = talloc_steal(ac, ares); + + /* prepare the search operation */ + ret = ldb_build_search_req(&search_req, ac->module->ldb, ac, + ac->req->op.del.dn, LDB_SCOPE_BASE, + "(objectclass=*)", attrs, + NULL, + ac, lpdb_del_search_callback, + ac->req); + if (ret != LDB_SUCCESS) { + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + + ret = ldb_next_request(ac->module, search_req); + if (ret != LDB_SUCCESS) { + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + return LDB_SUCCESS; +} + +/* Called when we search for our own entry. Stores the one entry we + * expect (as it is a base search) on the context pointer */ +static int lpdb_del_search_callback(struct ldb_request *req, + struct ldb_reply *ares) +{ + struct ldb_request *local_req; struct lpdb_context *ac; + struct ldb_dn *local_dn; struct GUID objectGUID; - ac = talloc_get_type(h->private_data, struct lpdb_context); - - /* if it is not an entry of type person this is an error */ - /* TODO: remove this when these things are checked in the schema */ - if (!ac->search_res) { - ldb_asprintf_errstring(ac->module->ldb, - "entry just modified (%s) not found!", - ldb_dn_get_linearized(ac->remote_req->op.mod.message->dn)); - return LDB_ERR_OPERATIONS_ERROR; - } - if (!ldb_msg_check_string_attribute(ac->search_res->message, "objectClass", "person")) { - /* Not relevent to us */ - return LDB_SUCCESS; + int ret = LDB_SUCCESS; + + ac = talloc_get_type(req->context, struct lpdb_context); + + if (!ares) { + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); } - - if (ldb_msg_find_ldb_val(ac->search_res->message, "objectGUID") == NULL) { - ldb_set_errstring(ac->module->ldb, - "no objectGUID found in search: local_password module must be configured below objectGUID module!\n"); - return LDB_ERR_OBJECT_CLASS_VIOLATION; + if (ares->error != LDB_SUCCESS) { + return ldb_module_done(ac->req, ares->controls, + ares->response, ares->error); } - - objectGUID = samdb_result_guid(ac->search_res->message, "objectGUID"); - ac->local_message->dn = ldb_dn_new(ac, ac->module->ldb, LOCAL_BASE); - ldb_dn_add_child_fmt(ac->local_message->dn, PASSWORD_GUID_ATTR "=%s", GUID_string(ac, &objectGUID)); + switch (ares->type) { + case LDB_REPLY_ENTRY: + if (ac->remote != NULL) { + ldb_set_errstring(ac->module->ldb, "Too many results"); + talloc_free(ares); + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + + ac->remote = talloc_steal(ac, ares); + break; + + case LDB_REPLY_REFERRAL: - h->state = LDB_ASYNC_INIT; - h->status = LDB_SUCCESS; + /* ignore */ + talloc_free(ares); + break; - ac->step = LPDB_LOCAL; + case LDB_REPLY_DONE: + /* After we find out the objectGUID for the entry, modify the local + * password database as required */ - ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req); + talloc_free(ares); + + /* if it is not an entry of type person this is NOT an error */ + /* TODO: remove this when sambaPassword will be in schema */ + if (ac->remote == NULL) { + return ldb_module_done(ac->req, + ac->remote_done->controls, + ac->remote_done->response, + ac->remote_done->error); + } + if (!ldb_msg_check_string_attribute(ac->remote->message, + "objectClass", "person")) { + /* Not relevent to us */ + return ldb_module_done(ac->req, + ac->remote_done->controls, + ac->remote_done->response, + ac->remote_done->error); + } + + if (ldb_msg_find_ldb_val(ac->remote->message, + "objectGUID") == NULL) { + ldb_set_errstring(ac->module->ldb, + "no objectGUID found in search: " + "local_password module must be " + "configured below objectGUID " + "module!\n"); + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OBJECT_CLASS_VIOLATION); + } + + objectGUID = samdb_result_guid(ac->remote->message, + "objectGUID"); + + local_dn = ldb_dn_new(ac, ac->module->ldb, LOCAL_BASE); + if ((local_dn == NULL) || + ( ! ldb_dn_add_child_fmt(local_dn, + PASSWORD_GUID_ATTR "=%s", + GUID_string(ac, &objectGUID)))) { + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + + ret = ldb_build_del_req(&local_req, ac->module->ldb, ac, + local_dn, + NULL, + ac, lpdb_local_callback, + ac->req); + if (ret != LDB_SUCCESS) { + return ldb_module_done(ac->req, NULL, NULL, ret); + } - /* perform the local update */ - return ldb_next_request(ac->module, ac->local_req); + /* perform the local update */ + ret = ldb_next_request(ac->module, local_req); + if (ret != LDB_SUCCESS) { + return ldb_module_done(ac->req, NULL, NULL, ret); + } + } + + return LDB_SUCCESS; } -static int lpdb_local_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) +/***************************************************************************** + * SEARCH + ****************************************************************************/ + +static int lpdb_local_search_callback(struct ldb_request *req, + struct ldb_reply *ares); + +static int lpdb_local_search(struct lpdb_context *ac) +{ + struct ldb_request *local_req; + int ret; + + ret = ldb_build_search_req(&local_req, ac->module->ldb, ac, + ac->current->local_dn, + LDB_SCOPE_BASE, + "(objectclass=*)", + ac->req->op.search.attrs, + NULL, + ac, lpdb_local_search_callback, + ac->req); + if (ret != LDB_SUCCESS) { + return LDB_ERR_OPERATIONS_ERROR; + } + + return ldb_next_request(ac->module, local_req); +} + +static int lpdb_local_search_callback(struct ldb_request *req, + struct ldb_reply *ares) { - struct lpdb_local_search_context *local_context; + struct lpdb_context *ac; + struct ldb_reply *merge; + struct lpdb_reply *lr; + int ret; + int i; + + ac = talloc_get_type(req->context, struct lpdb_context); + + if (!ares) { + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + if (ares->error != LDB_SUCCESS) { + return ldb_module_done(ac->req, ares->controls, + ares->response, ares->error); + } - local_context = talloc_get_type(context, struct lpdb_local_search_context); + lr = ac->current; - /* we are interested only in the single reply (base search) we receive here */ + /* we are interested only in a single reply (base search) */ switch (ares->type) { case LDB_REPLY_ENTRY: - { - int i; - if (local_context->local_res != NULL) { - ldb_set_errstring(ldb, "Too many results to base search for password entry!"); + + if (lr->remote == NULL) { + ldb_set_errstring(ac->module->ldb, + "Too many results for password entry search!"); talloc_free(ares); - return LDB_ERR_OPERATIONS_ERROR; + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); } - - local_context->local_res = ares; - /* Make sure never to return the internal key attribute to the caller */ + merge = lr->remote; + lr->remote = NULL; + + /* steal the local results on the remote results to be + * returned all together */ + talloc_steal(merge, ares->message->elements); + + /* Make sure never to return the internal key attribute */ ldb_msg_remove_attr(ares->message, PASSWORD_GUID_ATTR); - talloc_steal(local_context->remote_res->message->elements, ares->message->elements); for (i=0; i < ares->message->num_elements; i++) { struct ldb_message_element *el; - el = ldb_msg_find_element(local_context->remote_res->message, + el = ldb_msg_find_element(merge->message, ares->message->elements[i].name); if (!el) { - if (ldb_msg_add_empty(local_context->remote_res->message, - ares->message->elements[i].name, 0, &el) != LDB_SUCCESS) { + ret = ldb_msg_add_empty(merge->message, + ares->message->elements[i].name, + 0, &el); + if (ret != LDB_SUCCESS) { talloc_free(ares); - return LDB_ERR_OPERATIONS_ERROR; + return ldb_module_done(ac->req, + NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); } *el = ares->message->elements[i]; } } - return local_context->ac->orig_req->callback(ldb, - local_context->ac->orig_req->context, - local_context->remote_res); - } - case LDB_REPLY_DONE: - { - /* Fire off the callback if there was no local entry, so we get the rest returned */ - if (local_context->local_res == NULL) { - return local_context->ac->orig_req->callback(ldb, - local_context->ac->orig_req->context, - local_context->remote_res); - } - return LDB_SUCCESS; + + /* free the rest */ + talloc_free(ares); + + return ldb_module_send_entry(ac->req, merge->message); + + case LDB_REPLY_REFERRAL: + /* ignore */ + talloc_free(ares); break; - } - default: - { + + case LDB_REPLY_DONE: + talloc_free(ares); - ldb_set_errstring(ldb, "Unexpected result type in base search for password entry!"); - return LDB_ERR_OPERATIONS_ERROR; - } + + /* if this entry was not returned yet, return it now */ + if (lr->remote) { + ret = ldb_module_send_entry(ac->req, ac->remote->message); + if (ret != LDB_SUCCESS) { + return ldb_module_done(ac->req, + NULL, NULL, ret); + } + lr->remote = NULL; + } + + if (lr->next->remote->type == LDB_REPLY_DONE) { + /* this was the last one */ + return ldb_module_done(ac->req, + lr->next->remote->controls, + lr->next->remote->response, + lr->next->remote->error); + } else { + /* next one */ + ac->current = lr->next; + talloc_free(lr); + + ret = lpdb_local_search(ac); + if (ret != LDB_SUCCESS) { + return ldb_module_done(ac->req, + NULL, NULL, ret); + } + } } + + return LDB_SUCCESS; } /* For each entry returned in a remote search, do a local base search, * based on the objectGUID we asked for as an additional attribute */ -static int lpdb_remote_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) +static int lpdb_remote_search_callback(struct ldb_request *req, + struct ldb_reply *ares) { struct lpdb_context *ac; + struct ldb_dn *local_dn; + struct GUID objectGUID; + struct lpdb_reply *lr; + int ret; - ac = talloc_get_type(context, struct lpdb_context); + ac = talloc_get_type(req->context, struct lpdb_context); - if (ares->type == LDB_REPLY_ENTRY) { - struct ldb_request *req; - struct lpdb_local_search_context *local_context; - struct GUID objectGUID; + if (!ares) { + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + if (ares->error != LDB_SUCCESS) { + return ldb_module_done(ac->req, ares->controls, + ares->response, ares->error); + } + switch (ares->type) { + case LDB_REPLY_ENTRY: /* No point searching further if it's not a 'person' entry */ if (!ldb_msg_check_string_attribute(ares->message, "objectClass", "person")) { @@ -538,13 +898,14 @@ static int lpdb_remote_search_callback(struct ldb_context *ldb, void *context, s ldb_msg_remove_attr(ares->message, "objectClass"); } - return ac->orig_req->callback(ldb, ac->orig_req->context, ares); + return ldb_module_send_entry(ac->req, ares->message); } if (ldb_msg_find_ldb_val(ares->message, "objectGUID") == NULL) { ldb_set_errstring(ac->module->ldb, "no objectGUID found in search: local_password module must be configured below objectGUID module!\n"); - return LDB_ERR_OPERATIONS_ERROR; + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); } objectGUID = samdb_result_guid(ares->message, "objectGUID"); @@ -557,44 +918,63 @@ static int lpdb_remote_search_callback(struct ldb_context *ldb, void *context, s ldb_msg_remove_attr(ares->message, "objectClass"); } - req = talloc_zero(ac, struct ldb_request); - if (!req) { - return LDB_ERR_OPERATIONS_ERROR; + local_dn = ldb_dn_new(ac, ac->module->ldb, LOCAL_BASE); + if ((local_dn == NULL) || + (! ldb_dn_add_child_fmt(local_dn, + PASSWORD_GUID_ATTR "=%s", + GUID_string(ac, &objectGUID)))) { + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + + lr = talloc_zero(ac, struct lpdb_reply); + if (lr == NULL) { + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); } + lr->local_dn = talloc_steal(lr, local_dn); + lr->remote = talloc_steal(lr, ares); - local_context = talloc(ac, struct lpdb_local_search_context); - if (!local_context) { - return LDB_ERR_OPERATIONS_ERROR; + if (ac->list) { + ac->current->next = lr; + } else { + ac->list = lr; } - local_context->ac = ac; - local_context->remote_res = ares; - local_context->local_res = NULL; + ac->current= lr; + + break; - req->op.search.base = ldb_dn_new(ac, ac->module->ldb, LOCAL_BASE); - if ( ! ldb_dn_add_child_fmt(req->op.search.base, PASSWORD_GUID_ATTR "=%s", GUID_string(ac, &objectGUID))) { - return LDB_ERR_OPERATIONS_ERROR; + case LDB_REPLY_REFERRAL: + + return ldb_module_send_referral(ac->req, ares->referral); + + case LDB_REPLY_DONE: + + if (ac->list == NULL) { + /* found nothing */ + return ldb_module_done(ac->req, ares->controls, + ares->response, ares->error); } - req->operation = LDB_SEARCH; - req->op.search.scope = LDB_SCOPE_BASE; - req->op.search.tree = ldb_parse_tree(req, NULL); - if (req->op.search.tree == NULL) { - ldb_set_errstring(ac->module->ldb, "Out of Memory"); - return LDB_ERR_OPERATIONS_ERROR; + + lr = talloc_zero(ac, struct lpdb_reply); + if (lr == NULL) { + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); } - req->op.search.attrs = ac->orig_req->op.search.attrs; - req->controls = NULL; - req->context = ac; - req->callback = get_self_callback; + lr->remote = talloc_steal(lr, ares); - ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, req); - - req->context = local_context; - req->callback = lpdb_local_search_callback; + ac->current->next = lr; - return ldb_next_request(ac->module, req); - } else { - return ac->orig_req->callback(ldb, ac->orig_req->context, ares); + /* rewind current and start local searches */ + ac->current= ac->list; + + ret = lpdb_local_search(ac); + if (ret != LDB_SUCCESS) { + return ldb_module_done(ac->req, NULL, NULL, ret); + } } + + return LDB_SUCCESS; } /* Search for passwords and other attributes. The passwords are @@ -603,7 +983,7 @@ static int lpdb_remote_search_callback(struct ldb_context *ldb, void *context, s static int local_password_search(struct ldb_module *module, struct ldb_request *req) { - struct ldb_handle *h; + struct ldb_request *remote_req; struct lpdb_context *ac; int i; int ret; @@ -615,6 +995,8 @@ static int local_password_search(struct ldb_module *module, struct ldb_request * return ldb_next_request(module, req); } + search_attrs = NULL; + /* If the caller is searching for the local passwords directly, let them pass */ if (ldb_dn_compare_base(ldb_dn_new(req, module->ldb, LOCAL_BASE), req->op.search.base) == 0) { @@ -634,32 +1016,15 @@ static int local_password_search(struct ldb_module *module, struct ldb_request * } } - h = lpdb_init_handle(req, module, LPDB_SEARCH); - if (!h) { - return LDB_ERR_OPERATIONS_ERROR; - } - - ac = talloc_get_type(h->private_data, struct lpdb_context); - - ac->orig_req = req; - - ac->remote_req = talloc(ac, struct ldb_request); - if (ac->remote_req == NULL) { + ac = lpdb_init_context(module, req); + if (!ac) { return LDB_ERR_OPERATIONS_ERROR; } /* Remote search is for all attributes: if the remote LDAP server has these attributes, then it overrides the local database */ - *(ac->remote_req) = *(ac->orig_req); - - /* Return our own handle do deal with this call */ - ac->remote_req->handle = h; - - ac->remote_req->context = ac; - ac->remote_req->callback = lpdb_remote_search_callback; - if (req->op.search.attrs && !ldb_attr_in_list(req->op.search.attrs, "*")) { if (!ldb_attr_in_list(req->op.search.attrs, "objectGUID")) { - search_attrs = ldb_attr_list_copy_add(req, req->op.search.attrs, "objectGUID"); + search_attrs = ldb_attr_list_copy_add(ac, req->op.search.attrs, "objectGUID"); ac->added_objectGUID = true; if (!search_attrs) { return LDB_ERR_OPERATIONS_ERROR; @@ -668,7 +1033,7 @@ static int local_password_search(struct ldb_module *module, struct ldb_request * search_attrs = req->op.search.attrs; } if (!ldb_attr_in_list(search_attrs, "objectClass")) { - search_attrs = ldb_attr_list_copy_add(req, search_attrs, "objectClass"); + search_attrs = ldb_attr_list_copy_add(ac, search_attrs, "objectClass"); ac->added_objectClass = true; if (!search_attrs) { return LDB_ERR_OPERATIONS_ERROR; @@ -678,175 +1043,26 @@ static int local_password_search(struct ldb_module *module, struct ldb_request * search_attrs = req->op.search.attrs; } - ac->remote_req->op.search.attrs = search_attrs; - - ldb_set_timeout_from_prev_req(module->ldb, ac->orig_req, ac->remote_req); - - h->state = LDB_ASYNC_INIT; - h->status = LDB_SUCCESS; - - ac->step = LPDB_SEARCH_REMOTE; - - /* perform the search */ - ret = ldb_next_request(module, ac->remote_req); - - if (ret == LDB_SUCCESS) { - req->handle = ac->remote_req->handle; - } - - return ret; -} - -static int lpdb_wait(struct ldb_handle *handle) { - struct lpdb_context *ac; - int ret; - - if (!handle || !handle->private_data) { + ret = ldb_build_search_req_ex(&remote_req, module->ldb, ac, + req->op.search.base, + req->op.search.scope, + req->op.search.tree, + search_attrs, + req->controls, + ac, lpdb_remote_search_callback, + req); + if (ret != LDB_SUCCESS) { return LDB_ERR_OPERATIONS_ERROR; } - if (handle->state == LDB_ASYNC_DONE) { - return handle->status; - } - - handle->state = LDB_ASYNC_PENDING; - handle->status = LDB_SUCCESS; - - ac = talloc_get_type(handle->private_data, struct lpdb_context); - - switch (ac->step) { - case LPDB_ADD_REMOTE: - ret = ldb_wait(ac->remote_req->handle, LDB_WAIT_NONE); - - if (ret != LDB_SUCCESS) { - handle->status = ret; - goto done; - } - if (ac->remote_req->handle->status != LDB_SUCCESS) { - handle->status = ac->remote_req->handle->status; - goto done; - } - - if (ac->remote_req->handle->state != LDB_ASYNC_DONE) { - return LDB_SUCCESS; - } - - /* original request done, go on */ - return local_password_add_local(handle); - - case LPDB_MOD_REMOTE: - ret = ldb_wait(ac->remote_req->handle, LDB_WAIT_NONE); - - if (ret != LDB_SUCCESS) { - handle->status = ret; - goto done; - } - if (ac->remote_req->handle->status != LDB_SUCCESS) { - handle->status = ac->remote_req->handle->status; - goto done; - } - - if (ac->remote_req->handle->state != LDB_ASYNC_DONE) { - return LDB_SUCCESS; - } - - /* original request done, go on */ - return local_password_mod_search_self(handle); - - case LPDB_MOD_SEARCH_SELF: - ret = ldb_wait(ac->search_req->handle, LDB_WAIT_NONE); - - if (ret != LDB_SUCCESS) { - handle->status = ret; - goto done; - } - if (ac->search_req->handle->status != LDB_SUCCESS) { - handle->status = ac->search_req->handle->status; - goto done; - } - - if (ac->search_req->handle->state != LDB_ASYNC_DONE) { - return LDB_SUCCESS; - } - - /* original request done, go on */ - return local_password_mod_local(handle); - - case LPDB_LOCAL: - ret = ldb_wait(ac->local_req->handle, LDB_WAIT_NONE); - - if (ret != LDB_SUCCESS) { - handle->status = ret; - goto done; - } - if (ac->local_req->handle->status != LDB_SUCCESS) { - handle->status = ac->local_req->handle->status; - goto done; - } - - if (ac->local_req->handle->state != LDB_ASYNC_DONE) { - return LDB_SUCCESS; - } - - break; - - case LPDB_SEARCH_REMOTE: - ret = ldb_wait(ac->remote_req->handle, LDB_WAIT_NONE); - - if (ret != LDB_SUCCESS) { - handle->status = ret; - goto done; - } - if (ac->remote_req->handle->status != LDB_SUCCESS) { - handle->status = ac->remote_req->handle->status; - goto done; - } - - if (ac->remote_req->handle->state != LDB_ASYNC_DONE) { - return LDB_SUCCESS; - } - - break; - - default: - ret = LDB_ERR_OPERATIONS_ERROR; - goto done; - } - - ret = LDB_SUCCESS; - -done: - handle->state = LDB_ASYNC_DONE; - return ret; -} - -static int lpdb_wait_all(struct ldb_handle *handle) { - - int ret; - - while (handle->state != LDB_ASYNC_DONE) { - ret = lpdb_wait(handle); - if (ret != LDB_SUCCESS) { - return ret; - } - } - - return handle->status; -} - -static int local_password_wait(struct ldb_handle *handle, enum ldb_wait_type type) -{ - if (type == LDB_WAIT_ALL) { - return lpdb_wait_all(handle); - } else { - return lpdb_wait(handle); - } + /* perform the search */ + return ldb_next_request(module, remote_req); } _PUBLIC_ const struct ldb_module_ops ldb_local_password_module_ops = { .name = "local_password", .add = local_password_add, .modify = local_password_modify, - .search = local_password_search, - .wait = local_password_wait + .del = local_password_delete, + .search = local_password_search }; |