mirror of
https://github.com/samba-team/samba.git
synced 2025-10-08 07:33:19 +03:00
859 lines
24 KiB
C
859 lines
24 KiB
C
/*
|
|
ldb database module
|
|
|
|
Copyright (C) Simo Sorce 2004-2006
|
|
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006
|
|
Copyright (C) Andrew Tridgell 2004
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* Name: ldb
|
|
*
|
|
* Component: ldb local_password module
|
|
*
|
|
* Description: correctly update hash values based on changes to sambaPassword and friends
|
|
*
|
|
* Author: Andrew Bartlett
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include "libcli/ldap/ldap.h"
|
|
#include "ldb/include/ldb_errors.h"
|
|
#include "ldb/include/ldb_private.h"
|
|
#include "dsdb/samdb/samdb.h"
|
|
#include "librpc/ndr/libndr.h"
|
|
#include "dsdb/samdb/ldb_modules/password_modules.h"
|
|
|
|
#define PASSWORD_GUID_ATTR "masterGUID"
|
|
|
|
/* This module maintains a local password database, seperate from the main LDAP server.
|
|
|
|
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:
|
|
*/
|
|
|
|
static const char * const password_attrs[] = {
|
|
"supplementalCredentials",
|
|
"unicodePwd",
|
|
"dBCSPwd",
|
|
"lmPwdHistory",
|
|
"ntPwdHistory",
|
|
"msDS-KeyVersionNumber",
|
|
"pwdLastSet"
|
|
};
|
|
|
|
/* And we merge them back into search requests when asked to do so */
|
|
|
|
struct lpdb_context {
|
|
|
|
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 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_message *local_message;
|
|
|
|
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)
|
|
{
|
|
struct lpdb_context *ac;
|
|
struct ldb_handle *h;
|
|
|
|
h = talloc_zero(req, struct ldb_handle);
|
|
if (h == NULL) {
|
|
ldb_set_errstring(module->ldb, "Out of Memory");
|
|
return NULL;
|
|
}
|
|
|
|
h->module = module;
|
|
|
|
ac = talloc_zero(h, struct lpdb_context);
|
|
if (ac == NULL) {
|
|
ldb_set_errstring(module->ldb, "Out of Memory");
|
|
talloc_free(h);
|
|
return NULL;
|
|
}
|
|
|
|
h->private_data = (void *)ac;
|
|
|
|
h->state = LDB_ASYNC_INIT;
|
|
h->status = LDB_SUCCESS;
|
|
|
|
ac->type = type;
|
|
ac->module = module;
|
|
ac->orig_req = req;
|
|
|
|
return h;
|
|
}
|
|
|
|
/* Add a record, splitting password attributes from the user's main
|
|
* record */
|
|
|
|
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 GUID objectGUID;
|
|
int i;
|
|
|
|
ldb_debug(module->ldb, LDB_DEBUG_TRACE, "local_password_add\n");
|
|
|
|
if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
|
|
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.add.message->dn) == 0) {
|
|
return ldb_next_request(module, req);
|
|
}
|
|
|
|
for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
|
|
if (ldb_msg_find_element(req->op.add.message, password_attrs[i])) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* It didn't match any of our password attributes, go on */
|
|
if (i == ARRAY_SIZE(password_attrs)) {
|
|
return ldb_next_request(module, req);
|
|
}
|
|
|
|
/* TODO: remove this when sambaPassword will be in schema */
|
|
if (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "person")) {
|
|
ldb_asprintf_errstring(module->ldb,
|
|
"Cannot relocate a password on entry: %s, does not have objectClass 'person'",
|
|
ldb_dn_get_linearized(req->op.add.message->dn));
|
|
return LDB_ERR_OBJECT_CLASS_VIOLATION;
|
|
}
|
|
|
|
/* From here, we assume we have password attributes to split off */
|
|
h = lpdb_init_handle(req, module, LPDB_ADD);
|
|
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) {
|
|
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);
|
|
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.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;
|
|
}
|
|
|
|
*(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) {
|
|
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);
|
|
}
|
|
|
|
/* 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");
|
|
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;
|
|
|
|
/* Return our own handle do deal with this call */
|
|
req->handle = h;
|
|
|
|
return ldb_next_request(module, ac->remote_req);
|
|
}
|
|
|
|
/* After adding the remote entry, add the local one */
|
|
static int local_password_add_local(struct ldb_handle *h) {
|
|
|
|
struct lpdb_context *ac;
|
|
ac = talloc_get_type(h->private_data, struct lpdb_context);
|
|
|
|
h->state = LDB_ASYNC_INIT;
|
|
h->status = LDB_SUCCESS;
|
|
|
|
ac->step = LPDB_LOCAL;
|
|
|
|
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
|
|
|
|
/* perform the local add */
|
|
return ldb_next_request(ac->module, ac->local_req);
|
|
}
|
|
|
|
static int local_password_mod_search_self(struct ldb_handle *h);
|
|
|
|
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;
|
|
int i;
|
|
|
|
ldb_debug(module->ldb, LDB_DEBUG_TRACE, "local_password_modify\n");
|
|
|
|
if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
|
|
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.mod.message->dn) == 0) {
|
|
return ldb_next_request(module, req);
|
|
}
|
|
|
|
for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
|
|
if (ldb_msg_find_element(req->op.add.message, password_attrs[i])) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* It didn't match any of our password attributes, then we have nothing to do here */
|
|
if (i == ARRAY_SIZE(password_attrs)) {
|
|
return ldb_next_request(module, req);
|
|
}
|
|
|
|
/* From here, we assume we have password attributes to split off */
|
|
h = lpdb_init_handle(req, module, LPDB_MOD);
|
|
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) {
|
|
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);
|
|
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) {
|
|
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);
|
|
}
|
|
|
|
ac->local_req->op.mod.message = local_message;
|
|
ac->local_message = local_message;
|
|
|
|
ac->local_req->context = NULL;
|
|
ac->local_req->callback = NULL;
|
|
|
|
ac->step = LPDB_MOD_REMOTE;
|
|
|
|
/* Return our own handle do deal with this call */
|
|
req->handle = h;
|
|
|
|
return ldb_next_request(module, ac->remote_req);
|
|
}
|
|
|
|
/* Called when we search for our oen 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)
|
|
{
|
|
struct lpdb_context *ac;
|
|
|
|
ac = talloc_get_type(context, struct lpdb_context);
|
|
|
|
/* 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");
|
|
talloc_free(ares);
|
|
return LDB_ERR_OPERATIONS_ERROR;
|
|
}
|
|
|
|
ac->search_res = talloc_steal(ac, ares);
|
|
} else {
|
|
talloc_free(ares);
|
|
}
|
|
|
|
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) {
|
|
|
|
struct lpdb_context *ac;
|
|
static const char * const attrs[] = { "objectGUID", "objectClass", NULL };
|
|
|
|
ac = talloc_get_type(h->private_data, struct lpdb_context);
|
|
|
|
/* 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;
|
|
}
|
|
|
|
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");
|
|
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;
|
|
|
|
return ldb_next_request(ac->module, ac->search_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) {
|
|
|
|
struct lpdb_context *ac;
|
|
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 sambaPassword will be in 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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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));
|
|
|
|
h->state = LDB_ASYNC_INIT;
|
|
h->status = LDB_SUCCESS;
|
|
|
|
ac->step = LPDB_LOCAL;
|
|
|
|
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
|
|
|
|
/* perform the local update */
|
|
return ldb_next_request(ac->module, ac->local_req);
|
|
}
|
|
|
|
|
|
static int lpdb_local_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
|
|
{
|
|
struct lpdb_local_search_context *local_context;
|
|
|
|
local_context = talloc_get_type(context, struct lpdb_local_search_context);
|
|
|
|
/* we are interested only in the single reply (base search) we receive here */
|
|
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!");
|
|
talloc_free(ares);
|
|
return LDB_ERR_OPERATIONS_ERROR;
|
|
}
|
|
|
|
local_context->local_res = ares;
|
|
|
|
/* Make sure never to return the internal key attribute to the caller */
|
|
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,
|
|
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) {
|
|
talloc_free(ares);
|
|
return 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;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
talloc_free(ares);
|
|
ldb_set_errstring(ldb, "Unexpected result type in base search for password entry!");
|
|
return LDB_ERR_OPERATIONS_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
struct lpdb_context *ac;
|
|
|
|
ac = talloc_get_type(context, struct lpdb_context);
|
|
|
|
if (ares->type == LDB_REPLY_ENTRY) {
|
|
struct ldb_request *req;
|
|
struct lpdb_local_search_context *local_context;
|
|
struct GUID objectGUID;
|
|
|
|
/* No point searching further if it's not a 'person' entry */
|
|
if (!ldb_msg_check_string_attribute(ares->message, "objectClass", "person")) {
|
|
|
|
/* Make sure to remove anything we added */
|
|
if (ac->added_objectGUID) {
|
|
ldb_msg_remove_attr(ares->message, "objectGUID");
|
|
}
|
|
|
|
if (ac->added_objectClass) {
|
|
ldb_msg_remove_attr(ares->message, "objectClass");
|
|
}
|
|
|
|
return ac->orig_req->callback(ldb, ac->orig_req->context, ares);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
objectGUID = samdb_result_guid(ares->message, "objectGUID");
|
|
|
|
if (ac->added_objectGUID) {
|
|
ldb_msg_remove_attr(ares->message, "objectGUID");
|
|
}
|
|
|
|
if (ac->added_objectClass) {
|
|
ldb_msg_remove_attr(ares->message, "objectClass");
|
|
}
|
|
|
|
req = talloc_zero(ac, struct ldb_request);
|
|
if (!req) {
|
|
return LDB_ERR_OPERATIONS_ERROR;
|
|
}
|
|
|
|
local_context = talloc(ac, struct lpdb_local_search_context);
|
|
if (!local_context) {
|
|
return LDB_ERR_OPERATIONS_ERROR;
|
|
}
|
|
local_context->ac = ac;
|
|
local_context->remote_res = ares;
|
|
local_context->local_res = NULL;
|
|
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
req->op.search.attrs = ac->orig_req->op.search.attrs;
|
|
req->controls = NULL;
|
|
req->context = ac;
|
|
req->callback = get_self_callback;
|
|
|
|
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, req);
|
|
|
|
req->context = local_context;
|
|
req->callback = lpdb_local_search_callback;
|
|
|
|
return ldb_next_request(ac->module, req);
|
|
} else {
|
|
return ac->orig_req->callback(ldb, ac->orig_req->context, ares);
|
|
}
|
|
}
|
|
|
|
/* Search for passwords and other attributes. The passwords are
|
|
* local, but the other attributes are remote, and we need to glue the
|
|
* two search spaces back togeather */
|
|
|
|
static int local_password_search(struct ldb_module *module, struct ldb_request *req)
|
|
{
|
|
struct ldb_handle *h;
|
|
struct lpdb_context *ac;
|
|
int i;
|
|
int ret;
|
|
const char * const *search_attrs = NULL;
|
|
|
|
ldb_debug(module->ldb, LDB_DEBUG_TRACE, "local_password_search\n");
|
|
|
|
if (ldb_dn_is_special(req->op.search.base)) { /* do not manipulate our control entries */
|
|
return ldb_next_request(module, req);
|
|
}
|
|
|
|
/* 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) {
|
|
return ldb_next_request(module, req);
|
|
}
|
|
|
|
if (req->op.search.attrs && (!ldb_attr_in_list(req->op.search.attrs, "*"))) {
|
|
for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
|
|
if (ldb_attr_in_list(req->op.search.attrs, password_attrs[i])) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* It didn't match any of our password attributes, go on */
|
|
if (i == ARRAY_SIZE(password_attrs)) {
|
|
return ldb_next_request(module, req);
|
|
}
|
|
}
|
|
|
|
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) {
|
|
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");
|
|
ac->added_objectGUID = true;
|
|
if (!search_attrs) {
|
|
return LDB_ERR_OPERATIONS_ERROR;
|
|
}
|
|
} else {
|
|
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");
|
|
ac->added_objectClass = true;
|
|
if (!search_attrs) {
|
|
return LDB_ERR_OPERATIONS_ERROR;
|
|
}
|
|
}
|
|
} else {
|
|
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) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
static const struct ldb_module_ops local_password_ops = {
|
|
.name = "local_password",
|
|
.add = local_password_add,
|
|
.modify = local_password_modify,
|
|
.search = local_password_search,
|
|
.wait = local_password_wait
|
|
};
|
|
|
|
|
|
int local_password_module_init(void)
|
|
{
|
|
return ldb_register_module(&local_password_ops);
|
|
}
|