From 1c48b5a62f73234ed26bb20f0ab345ab61cda0ab Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Thu, 18 Feb 2010 07:49:04 -0500 Subject: Rename server/ directory to src/ Also update BUILD.txt --- src/db/sysdb_ops.c | 5059 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 5059 insertions(+) create mode 100644 src/db/sysdb_ops.c (limited to 'src/db/sysdb_ops.c') diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c new file mode 100644 index 00000000..33cfd91f --- /dev/null +++ b/src/db/sysdb_ops.c @@ -0,0 +1,5059 @@ +/* + SSSD + + System Database + + Copyright (C) Simo Sorce 2008 + + 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 . +*/ + +#include "util/util.h" +#include "db/sysdb_private.h" +#include "util/sha512crypt.h" +#include + +static int add_string(struct ldb_message *msg, int flags, + const char *attr, const char *value) +{ + int ret; + + ret = ldb_msg_add_empty(msg, attr, flags, NULL); + if (ret == LDB_SUCCESS) { + ret = ldb_msg_add_string(msg, attr, value); + if (ret == LDB_SUCCESS) return EOK; + } + return ENOMEM; +} + +static int add_ulong(struct ldb_message *msg, int flags, + const char *attr, unsigned long value) +{ + int ret; + + ret = ldb_msg_add_empty(msg, attr, flags, NULL); + if (ret == LDB_SUCCESS) { + ret = ldb_msg_add_fmt(msg, attr, "%lu", value); + if (ret == LDB_SUCCESS) return EOK; + } + return ENOMEM; +} + +static uint32_t get_attr_as_uint32(struct ldb_message *msg, const char *attr) +{ + const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr); + long long int l; + + if (!v || !v->data) { + return 0; + } + + errno = 0; + l = strtoll((const char *)v->data, NULL, 0); + if (errno) { + return (uint32_t)-1; + } + + if (l < 0 || l > ((uint32_t)(-1))) { + return (uint32_t)-1; + } + + return l; +} + +#define ERROR_OUT(v, r, l) do { v = r; goto l; } while(0); + +/* =LDB-Request-(tevent_req-style)======================================== */ + +struct sldb_request_state { + struct tevent_context *ev; + struct ldb_context *ldbctx; + struct ldb_request *ldbreq; + struct ldb_reply *ldbreply; +}; + +static void sldb_request_wakeup(struct tevent_req *subreq); +static int sldb_request_callback(struct ldb_request *ldbreq, + struct ldb_reply *ldbreply); + +static struct tevent_req *sldb_request_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ldb_context *ldbctx, + struct ldb_request *ldbreq) +{ + struct tevent_req *req, *subreq; + struct sldb_request_state *state; + struct timeval tv = { 0, 0 }; + + req = tevent_req_create(mem_ctx, &state, struct sldb_request_state); + if (!req) return NULL; + + state->ev = ev; + state->ldbctx = ldbctx; + state->ldbreq = ldbreq; + state->ldbreply = NULL; + + subreq = tevent_wakeup_send(state, ev, tv); + if (!subreq) { + DEBUG(1, ("Failed to add critical timer to run next ldb operation!\n")); + talloc_zfree(req); + return NULL; + } + tevent_req_set_callback(subreq, sldb_request_wakeup, req); + + return req; +} + +static void sldb_request_wakeup(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sldb_request_state *state = tevent_req_data(req, + struct sldb_request_state); + int ret; + + if (!tevent_wakeup_recv(subreq)) return; + talloc_zfree(subreq); + + state->ldbreq->callback = sldb_request_callback; + state->ldbreq->context = req; + + ret = ldb_request(state->ldbctx, state->ldbreq); + if (ret != LDB_SUCCESS) { + int err = sysdb_error_to_errno(ret); + DEBUG(6, ("Error: %d (%s)\n", err, strerror(err))); + tevent_req_error(req, err); + } +} + +static int sldb_request_callback(struct ldb_request *ldbreq, + struct ldb_reply *ldbreply) +{ + struct tevent_req *req = talloc_get_type(ldbreq->context, + struct tevent_req); + struct sldb_request_state *state = tevent_req_data(req, + struct sldb_request_state); + int err; + + if (!ldbreply) { + DEBUG(6, ("Error: Missing ldbreply")); + ERROR_OUT(err, EIO, fail); + } + + state->ldbreply = talloc_steal(state, ldbreply); + + if (ldbreply->error != LDB_SUCCESS) { + DEBUG(6, ("LDB Error: %d (%s)\n", + ldbreply->error, ldb_errstring(state->ldbctx))); + ERROR_OUT(err, sysdb_error_to_errno(ldbreply->error), fail); + } + + if (ldbreply->type == LDB_REPLY_DONE) { + tevent_req_done(req); + return EOK; + } + + tevent_req_notify_callback(req); + return EOK; + +fail: + tevent_req_error(req, err); + return EOK; +} + +static int sldb_request_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct ldb_reply **ldbreply) +{ + struct sldb_request_state *state = tevent_req_data(req, + struct sldb_request_state); + enum tevent_req_state tstate; + uint64_t err = 0; + + if (state->ldbreply) { + *ldbreply = talloc_move(mem_ctx, &state->ldbreply); + } + + if (tevent_req_is_error(req, &tstate, &err)) { + switch (tstate) { + case TEVENT_REQ_USER_ERROR: + return err; + case TEVENT_REQ_IN_PROGRESS: + return EOK; + default: + return EIO; + } + } + + return EOK; +} + +/* =Standard-Sysdb-Operations-utility-functions=========================== */ + +struct sysdb_op_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + + bool ignore_not_found; + + struct ldb_reply *ldbreply; + size_t msgs_count; + struct ldb_message **msgs; +}; + +static void sysdb_op_default_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_op_state *state = tevent_req_data(req, + struct sysdb_op_state); + int ret; + + ret = sldb_request_recv(subreq, state, &state->ldbreply); + talloc_zfree(subreq); + if (ret) { + if (state->ignore_not_found && ret == ENOENT) { + goto done; + } + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + if (state->ldbreply->type != LDB_REPLY_DONE) { + DEBUG(6, ("Error: %d (%s)\n", EIO, strerror(EIO))); + tevent_req_error(req, EIO); + return; + } + +done: + tevent_req_done(req); +} + +static int sysdb_op_default_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + + +/* =Remove-Entry-From-Sysdb=============================================== */ + +struct tevent_req *sysdb_delete_entry_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct ldb_dn *dn, + bool ignore_not_found) +{ + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; + struct ldb_request *ldbreq; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->ignore_not_found = ignore_not_found; + state->ldbreply = NULL; + + ret = ldb_build_del_req(&ldbreq, handle->ctx->ldb, state, dn, + NULL, NULL, NULL, NULL); + + if (ret != LDB_SUCCESS) { + DEBUG(1, ("LDB Error: %s(%d)\nError Message: [%s]\n", + ldb_strerror(ret), ret, ldb_errstring(handle->ctx->ldb))); + ERROR_OUT(ret, sysdb_error_to_errno(ret), fail); + } + + subreq = sldb_request_send(state, ev, handle->ctx->ldb, ldbreq); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_op_default_done, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +int sysdb_delete_entry_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Remove-Subentries-From-Sysdb=============================================== */ + +struct sysdb_delete_recursive_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + + bool ignore_not_found; + + struct ldb_reply *ldbreply; + size_t msgs_count; + struct ldb_message **msgs; + size_t current_item; +}; + +static void sysdb_delete_search_done(struct tevent_req *subreq); +static void sysdb_delete_recursive_prepare_op(struct tevent_req *req); +static void sysdb_delete_recursive_op_done(struct tevent_req *req); + +struct tevent_req *sysdb_delete_recursive_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct ldb_dn *dn, + bool ignore_not_found) +{ + struct tevent_req *req, *subreq; + struct sysdb_delete_recursive_state *state; + int ret; + const char **no_attrs; + + req = tevent_req_create(mem_ctx, &state, + struct sysdb_delete_recursive_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->ignore_not_found = ignore_not_found; + state->ldbreply = NULL; + state->msgs_count = 0; + state->msgs = NULL; + state->current_item = 0; + + no_attrs = talloc_array(state, const char *, 1); + if (no_attrs == NULL) { + ERROR_OUT(ret, ENOMEM, fail); + } + no_attrs[0] = NULL; + + subreq = sysdb_search_entry_send(state, ev, handle, dn, LDB_SCOPE_SUBTREE, + "(distinguishedName=*)", no_attrs); + + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_delete_search_done, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_delete_search_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_delete_recursive_state *state = tevent_req_data(req, + struct sysdb_delete_recursive_state); + int ret; + + ret = sysdb_search_entry_recv(subreq, state, &state->msgs_count, + &state->msgs); + talloc_zfree(subreq); + if (ret) { + if (state->ignore_not_found && ret == ENOENT) { + tevent_req_done(req); + return; + } + DEBUG(6, ("Search error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + DEBUG(9, ("Found [%d] items to delete.\n", state->msgs_count)); + + qsort(state->msgs, state->msgs_count, sizeof(struct ldb_message *), + compare_ldb_dn_comp_num); + + state->current_item = 0; + sysdb_delete_recursive_prepare_op(req); +} + +static void sysdb_delete_recursive_prepare_op(struct tevent_req *req) +{ + struct sysdb_delete_recursive_state *state = tevent_req_data(req, + struct sysdb_delete_recursive_state); + struct tevent_req *subreq; + int ret; + struct ldb_request *ldbreq; + + if (state->current_item < state->msgs_count) { + DEBUG(9 ,("Trying to delete [%s].\n", + ldb_dn_canonical_string(state, + state->msgs[state->current_item]->dn))); + ret = ldb_build_del_req(&ldbreq, state->handle->ctx->ldb, state, + state->msgs[state->current_item]->dn, NULL, + NULL, NULL, NULL); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("LDB Error: %s(%d)\nError Message: [%s]\n", + ldb_strerror(ret), ret, + ldb_errstring(state->handle->ctx->ldb))); + ret = sysdb_error_to_errno(ret); + goto fail; + } + + subreq = sldb_request_send(state, state->ev, state->handle->ctx->ldb, + ldbreq); + if (!subreq) { + ret = ENOMEM; + goto fail; + } + + state->current_item++; + tevent_req_set_callback(subreq, sysdb_delete_recursive_op_done, req); + return; + } + + tevent_req_done(req); + return; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); +} + +static void sysdb_delete_recursive_op_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_op_default_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Delete error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + sysdb_delete_recursive_prepare_op(req); +} + +int sysdb_delete_recursive_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Search-Entry========================================================== */ + +static void sysdb_search_entry_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_search_entry_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct ldb_dn *base_dn, + int scope, + const char *filter, + const char **attrs) +{ + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; + struct ldb_request *ldbreq; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->ignore_not_found = false; + state->ldbreply = NULL; + state->msgs_count = 0; + state->msgs = NULL; + + ret = ldb_build_search_req(&ldbreq, handle->ctx->ldb, state, + base_dn, scope, filter, attrs, + NULL, NULL, NULL, NULL); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to build search request: %s(%d)[%s]\n", + ldb_strerror(ret), ret, ldb_errstring(handle->ctx->ldb))); + ERROR_OUT(ret, sysdb_error_to_errno(ret), fail); + } + + subreq = sldb_request_send(state, ev, handle->ctx->ldb, ldbreq); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_search_entry_done, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_search_entry_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_op_state *state = tevent_req_data(req, + struct sysdb_op_state); + struct ldb_reply *ldbreply; + struct ldb_message **dummy; + int ret; + + ret = sldb_request_recv(subreq, state, &ldbreply); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + switch (ldbreply->type) { + case LDB_REPLY_ENTRY: + dummy = talloc_realloc(state, state->msgs, + struct ldb_message *, + state->msgs_count + 2); + if (dummy == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + state->msgs = dummy; + + state->msgs[state->msgs_count + 1] = NULL; + + state->msgs[state->msgs_count] = talloc_steal(state->msgs, + ldbreply->message); + state->msgs_count++; + + talloc_zfree(ldbreply); + return; + + case LDB_REPLY_DONE: + talloc_zfree(subreq); + talloc_zfree(ldbreply); + if (state->msgs_count == 0) { + DEBUG(6, ("Error: Entry not Found!\n")); + tevent_req_error(req, ENOENT); + return; + } + return tevent_req_done(req); + + default: + /* unexpected stuff */ + talloc_zfree(ldbreply); + DEBUG(6, ("Error: Unknown error!\n")); + tevent_req_error(req, EIO); + return; + } +} + +int sysdb_search_entry_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *msgs_count, + struct ldb_message ***msgs) +{ + struct sysdb_op_state *state = tevent_req_data(req, + struct sysdb_op_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *msgs_count = state->msgs_count; + *msgs = talloc_move(mem_ctx, &state->msgs); + + return EOK; +} + + +/* =Search-User-by-[UID/NAME]============================================= */ + +struct sysdb_search_user_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + + struct ldb_dn *basedn; + const char **attrs; + const char *filter; + int scope; + + size_t msgs_count; + struct ldb_message **msgs; +}; + +static void sysdb_search_user_cont(struct tevent_req *subreq); +static void sysdb_search_user_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_search_user_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, + const char **attrs) +{ + struct tevent_req *req, *subreq; + struct sysdb_search_user_state *state; + static const char *def_attrs[] = { SYSDB_NAME, SYSDB_UIDNUM, NULL }; + int ret; + + if (!sysdb && !handle) return NULL; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_search_user_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->msgs_count = 0; + state->msgs = NULL; + + state->attrs = attrs ? attrs : def_attrs; + state->filter = NULL; + state->scope = LDB_SCOPE_BASE; + + if (!sysdb) sysdb = handle->ctx; + + state->basedn = sysdb_user_dn(sysdb, state, domain->name, name); + if (!state->basedn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + if (!handle) { + subreq = sysdb_operation_send(state, state->ev, sysdb); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_search_user_cont, req); + } + else { + subreq = sysdb_search_entry_send(state, state->ev, state->handle, + state->basedn, state->scope, + state->filter, state->attrs); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_search_user_done, req); + } + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +struct tevent_req *sysdb_search_user_by_uid_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + uid_t uid, + const char **attrs) +{ + struct tevent_req *req, *subreq; + struct sysdb_search_user_state *state; + static const char *def_attrs[] = { SYSDB_NAME, SYSDB_UIDNUM, NULL }; + int ret; + + if (!sysdb && !handle) return NULL; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_search_user_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->msgs_count = 0; + state->msgs = NULL; + state->attrs = attrs ? attrs : def_attrs; + + if (!sysdb) sysdb = handle->ctx; + + state->basedn = ldb_dn_new_fmt(state, sysdb->ldb, + SYSDB_TMPL_USER_BASE, domain->name); + if (!state->basedn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + state->filter = talloc_asprintf(state, SYSDB_PWUID_FILTER, + (unsigned long)uid); + if (!state->filter) { + ERROR_OUT(ret, ENOMEM, fail); + } + + state->scope = LDB_SCOPE_ONELEVEL; + + if (!handle) { + subreq = sysdb_operation_send(state, state->ev, sysdb); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_search_user_cont, req); + } + else { + subreq = sysdb_search_entry_send(state, state->ev, state->handle, + state->basedn, state->scope, + state->filter, state->attrs); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_search_user_done, req); + } + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_search_user_cont(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_search_user_state *state = tevent_req_data(req, + struct sysdb_search_user_state); + int ret; + + ret = sysdb_operation_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + subreq = sysdb_search_entry_send(state, state->ev, state->handle, + state->basedn, state->scope, + state->filter, state->attrs); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_search_user_done, req); +} + +static void sysdb_search_user_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_search_user_state *state = tevent_req_data(req, + struct sysdb_search_user_state); + int ret; + + ret = sysdb_search_entry_recv(subreq, state, &state->msgs_count, + &state->msgs); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_search_user_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct ldb_message **msg) +{ + struct sysdb_search_user_state *state = tevent_req_data(req, + struct sysdb_search_user_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + if (state->msgs_count > 1) { + DEBUG(1, ("More than one result found.\n")); + return EFAULT; + } + + *msg = talloc_move(mem_ctx, &state->msgs[0]); + + return EOK; +} + + +/* =Search-Group-by-[GID/NAME]============================================ */ + +struct sysdb_search_group_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + + struct ldb_dn *basedn; + const char **attrs; + const char *filter; + int scope; + + size_t msgs_count; + struct ldb_message **msgs; +}; + +static void sysdb_search_group_cont(struct tevent_req *subreq); +static void sysdb_search_group_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_search_group_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, + const char **attrs) +{ + struct tevent_req *req, *subreq; + struct sysdb_search_group_state *state; + static const char *def_attrs[] = { SYSDB_NAME, SYSDB_GIDNUM, NULL }; + int ret; + + if (!sysdb && !handle) return NULL; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_search_group_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->msgs_count = 0; + state->msgs = NULL; + + state->attrs = attrs ? attrs : def_attrs; + state->filter = NULL; + state->scope = LDB_SCOPE_BASE; + + if (!sysdb) sysdb = handle->ctx; + + state->basedn = sysdb_group_dn(sysdb, state, domain->name, name); + if (!state->basedn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + if (!handle) { + subreq = sysdb_operation_send(state, state->ev, sysdb); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_search_group_cont, req); + } + else { + subreq = sysdb_search_entry_send(state, state->ev, state->handle, + state->basedn, state->scope, + state->filter, state->attrs); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_search_group_done, req); + } + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +struct tevent_req *sysdb_search_group_by_gid_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + gid_t gid, + const char **attrs) +{ + struct tevent_req *req, *subreq; + struct sysdb_search_group_state *state; + static const char *def_attrs[] = { SYSDB_NAME, SYSDB_GIDNUM, NULL }; + int ret; + + if (!sysdb && !handle) return NULL; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_search_group_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->msgs_count = 0; + state->msgs = NULL; + state->attrs = attrs ? attrs : def_attrs; + + if (!sysdb) sysdb = handle->ctx; + + state->basedn = ldb_dn_new_fmt(state, sysdb->ldb, + SYSDB_TMPL_GROUP_BASE, domain->name); + if (!state->basedn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + state->filter = talloc_asprintf(state, SYSDB_GRGID_FILTER, + (unsigned long)gid); + if (!state->filter) { + ERROR_OUT(ret, ENOMEM, fail); + } + + state->scope = LDB_SCOPE_ONELEVEL; + + if (!handle) { + subreq = sysdb_operation_send(state, state->ev, sysdb); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_search_group_cont, req); + } + else { + subreq = sysdb_search_entry_send(state, state->ev, state->handle, + state->basedn, state->scope, + state->filter, state->attrs); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_search_group_done, req); + } + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_search_group_cont(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_search_group_state *state = tevent_req_data(req, + struct sysdb_search_group_state); + int ret; + + ret = sysdb_operation_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + subreq = sysdb_search_entry_send(state, state->ev, state->handle, + state->basedn, state->scope, + state->filter, state->attrs); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_search_group_done, req); +} + +static void sysdb_search_group_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_search_group_state *state = tevent_req_data(req, + struct sysdb_search_group_state); + int ret; + + ret = sysdb_search_entry_recv(subreq, state, &state->msgs_count, + &state->msgs); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_search_group_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct ldb_message **msg) +{ + struct sysdb_search_group_state *state = tevent_req_data(req, + struct sysdb_search_group_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + if (state->msgs_count > 1) { + DEBUG(1, ("More than one result found.\n")); + return EFAULT; + } + + *msg = talloc_move(mem_ctx, &state->msgs[0]); + + return EOK; +} + + +/* =Replace-Attributes-On-Entry=========================================== */ + +struct tevent_req *sysdb_set_entry_attr_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct ldb_dn *entry_dn, + struct sysdb_attrs *attrs, + int mod_op) +{ + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; + struct ldb_request *ldbreq; + struct ldb_message *msg; + int i, ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->ignore_not_found = false; + state->ldbreply = NULL; + + if (!entry_dn) { + ERROR_OUT(ret, EINVAL, fail); + } + + if (attrs->num == 0) { + ERROR_OUT(ret, EINVAL, fail); + } + + msg = ldb_msg_new(state); + if (!msg) { + ERROR_OUT(ret, ENOMEM, fail); + } + + msg->dn = entry_dn; + + msg->elements = talloc_array(msg, struct ldb_message_element, attrs->num); + if (!msg->elements) { + ERROR_OUT(ret, ENOMEM, fail); + } + + for (i = 0; i < attrs->num; i++) { + msg->elements[i] = attrs->a[i]; + msg->elements[i].flags = mod_op; + } + + msg->num_elements = attrs->num; + + ret = ldb_build_mod_req(&ldbreq, handle->ctx->ldb, state, msg, + NULL, NULL, NULL, NULL); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to build modify request: %s(%d)[%s]\n", + ldb_strerror(ret), ret, ldb_errstring(handle->ctx->ldb))); + ERROR_OUT(ret, sysdb_error_to_errno(ret), fail); + } + + subreq = sldb_request_send(state, ev, handle->ctx->ldb, ldbreq); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_op_default_done, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +int sysdb_set_entry_attr_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Replace-Attributes-On-User============================================ */ + +static void sysdb_set_user_attr_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_set_user_attr_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, + struct sysdb_attrs *attrs, + int mod_op) +{ + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; + struct ldb_dn *dn; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->ignore_not_found = false; + state->ldbreply = NULL; + + dn = sysdb_user_dn(handle->ctx, state, domain->name, name); + if (!dn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + subreq = sysdb_set_entry_attr_send(state, ev, handle, dn, attrs, mod_op); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_set_user_attr_done, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_set_user_attr_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_set_entry_attr_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_set_user_attr_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Replace-Attributes-On-Group=========================================== */ + +static void sysdb_set_group_attr_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_set_group_attr_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, + struct sysdb_attrs *attrs, + int mod_op) +{ + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; + struct ldb_dn *dn; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->ignore_not_found = false; + state->ldbreply = NULL; + + dn = sysdb_group_dn(handle->ctx, state, domain->name, name); + if (!dn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + subreq = sysdb_set_entry_attr_send(state, ev, handle, dn, attrs, mod_op); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_set_group_attr_done, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_set_group_attr_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_set_entry_attr_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_set_group_attr_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Get-New-ID============================================================ */ + +struct sysdb_get_new_id_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + struct sss_domain_info *domain; + + struct ldb_dn *base_dn; + struct ldb_message *base; + + struct ldb_message **v_msgs; + int v_count; + + uint32_t new_id; +}; + +static void sysdb_get_new_id_base(struct tevent_req *subreq); +static void sysdb_get_new_id_verify(struct tevent_req *subreq); +static void sysdb_get_new_id_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_get_new_id_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain) +{ + struct tevent_req *req, *subreq; + struct sysdb_get_new_id_state *state; + static const char *attrs[] = { SYSDB_NEXTID, NULL }; + struct ldb_request *ldbreq; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_get_new_id_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->domain = domain; + state->base = NULL; + state->v_msgs = NULL; + state->v_count = 0; + state->new_id = 0; + + state->base_dn = sysdb_domain_dn(handle->ctx, state, domain->name); + if (!state->base_dn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + ret = ldb_build_search_req(&ldbreq, handle->ctx->ldb, state, + state->base_dn, LDB_SCOPE_BASE, + SYSDB_NEXTID_FILTER, attrs, + NULL, NULL, NULL, NULL); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to build search request: %s(%d)[%s]\n", + ldb_strerror(ret), ret, ldb_errstring(handle->ctx->ldb))); + ERROR_OUT(ret, sysdb_error_to_errno(ret), fail); + } + + subreq = sldb_request_send(state, ev, handle->ctx->ldb, ldbreq); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_get_new_id_base, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_get_new_id_base(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_get_new_id_state *state = tevent_req_data(req, + struct sysdb_get_new_id_state); + static const char *attrs[] = { SYSDB_UIDNUM, SYSDB_GIDNUM, NULL }; + struct ldb_reply *ldbreply; + struct ldb_request *ldbreq; + char *filter; + int ret; + + ret = sldb_request_recv(subreq, state, &ldbreply); + if (ret) { + talloc_zfree(subreq); + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + switch (ldbreply->type) { + case LDB_REPLY_ENTRY: + if (state->base) { + DEBUG(1, ("More than one reply for a base search ?! " + "DB seems corrupted, aborting.")); + tevent_req_error(req, EFAULT); + return; + } + + state->base = talloc_move(state, &ldbreply->message); + if (!state->base) { + DEBUG(6, ("Error: Out of memory!\n")); + tevent_req_error(req, ENOMEM); + return; + } + + /* just return, wait for a LDB_REPLY_DONE entry */ + talloc_zfree(ldbreply); + return; + + case LDB_REPLY_DONE: + break; + + default: + /* unexpected stuff */ + DEBUG(6, ("Error: Unknown error\n")); + tevent_req_error(req, EIO); + talloc_zfree(ldbreply); + return; + } + + talloc_zfree(subreq); + + if (state->base) { + state->new_id = get_attr_as_uint32(state->base, SYSDB_NEXTID); + if (state->new_id == (uint32_t)(-1)) { + DEBUG(1, ("Invalid Next ID in domain %s\n", state->domain->name)); + tevent_req_error(req, ERANGE); + return; + } + + if (state->new_id < state->domain->id_min) { + state->new_id = state->domain->id_min; + } + + if ((state->domain->id_max != 0) && + (state->new_id > state->domain->id_max)) { + DEBUG(0, ("Failed to allocate new id, out of range (%u/%u)\n", + state->new_id, state->domain->id_max)); + tevent_req_error(req, ERANGE); + return; + } + + } else { + /* looks like the domain is not initialized yet, use min_id */ + state->new_id = state->domain->id_min; + } + + /* verify the id is actually really free. + * search all entries with id >= new_id and < max_id */ + if (state->domain->id_max) { + filter = talloc_asprintf(state, + "(|(&(%s>=%u)(%s<=%u))(&(%s>=%u)(%s<=%u)))", + SYSDB_UIDNUM, state->new_id, + SYSDB_UIDNUM, state->domain->id_max, + SYSDB_GIDNUM, state->new_id, + SYSDB_GIDNUM, state->domain->id_max); + } + else { + filter = talloc_asprintf(state, + "(|(%s>=%u)(%s>=%u))", + SYSDB_UIDNUM, state->new_id, + SYSDB_GIDNUM, state->new_id); + } + if (!filter) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + + ret = ldb_build_search_req(&ldbreq, state->handle->ctx->ldb, state, + state->base_dn, LDB_SCOPE_SUBTREE, + filter, attrs, + NULL, NULL, NULL, NULL); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to build search request: %s(%d)[%s]\n", + ldb_strerror(ret), ret, + ldb_errstring(state->handle->ctx->ldb))); + tevent_req_error(req, sysdb_error_to_errno(ret)); + return; + } + + subreq = sldb_request_send(state, state->ev, + state->handle->ctx->ldb, ldbreq); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_get_new_id_verify, req); + + return; +} + +static void sysdb_get_new_id_verify(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_get_new_id_state *state = tevent_req_data(req, + struct sysdb_get_new_id_state); + struct ldb_reply *ldbreply; + struct ldb_request *ldbreq; + struct ldb_message *msg; + int ret, i; + + ret = sldb_request_recv(subreq, state, &ldbreply); + if (ret) { + talloc_zfree(subreq); + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + switch (ldbreply->type) { + case LDB_REPLY_ENTRY: + state->v_msgs = talloc_realloc(state, state->v_msgs, + struct ldb_message *, + state->v_count + 2); + if (!state->v_msgs) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + + state->v_msgs[state->v_count] = talloc_move(state, &ldbreply->message); + if (!state->v_msgs[state->v_count]) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + state->v_count++; + + /* just return, wait for a LDB_REPLY_DONE entry */ + talloc_zfree(ldbreply); + return; + + case LDB_REPLY_DONE: + break; + + default: + /* unexpected stuff */ + DEBUG(6, ("Error: Unknown error\n")); + tevent_req_error(req, EIO); + talloc_zfree(ldbreply); + return; + } + + talloc_zfree(subreq); + + /* if anything was found, find the maximum and increment past it */ + if (state->v_count) { + uint32_t id; + + for (i = 0; i < state->v_count; i++) { + id = get_attr_as_uint32(state->v_msgs[i], SYSDB_UIDNUM); + if (id != (uint32_t)(-1)) { + if (id > state->new_id) state->new_id = id; + } + id = get_attr_as_uint32(state->v_msgs[i], SYSDB_GIDNUM); + if (id != (uint32_t)(-1)) { + if (id > state->new_id) state->new_id = id; + } + } + + state->new_id++; + + /* check again we are not falling out of range */ + if ((state->domain->id_max != 0) && + (state->new_id > state->domain->id_max)) { + DEBUG(0, ("Failed to allocate new id, out of range (%u/%u)\n", + state->new_id, state->domain->id_max)); + tevent_req_error(req, ERANGE); + return; + } + + talloc_zfree(state->v_msgs); + state->v_count = 0; + } + + /* finally store the new next id */ + msg = ldb_msg_new(state); + if (!msg) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + msg->dn = state->base_dn; + + ret = add_ulong(msg, LDB_FLAG_MOD_REPLACE, + SYSDB_NEXTID, state->new_id + 1); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + ret = ldb_build_mod_req(&ldbreq, state->handle->ctx->ldb, state, msg, + NULL, NULL, NULL, NULL); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to build modify request: %s(%d)[%s]\n", + ldb_strerror(ret), ret, + ldb_errstring(state->handle->ctx->ldb))); + tevent_req_error(req, ret); + return; + } + + subreq = sldb_request_send(state, state->ev, + state->handle->ctx->ldb, ldbreq); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_get_new_id_done, req); +} + +static void sysdb_get_new_id_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_get_new_id_state *state = tevent_req_data(req, + struct sysdb_get_new_id_state); + struct ldb_reply *ldbreply; + int ret; + + ret = sldb_request_recv(subreq, state, &ldbreply); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + if (ldbreply->type != LDB_REPLY_DONE) { + DEBUG(6, ("Error: %d (%s)\n", EIO, strerror(EIO))); + tevent_req_error(req, EIO); + return; + } + + tevent_req_done(req); +} + +int sysdb_get_new_id_recv(struct tevent_req *req, uint32_t *id) +{ + struct sysdb_get_new_id_state *state = tevent_req_data(req, + struct sysdb_get_new_id_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *id = state->new_id; + + return EOK; +} + + +/* =Add-Basic-User-NO-CHECKS============================================== */ + +struct tevent_req *sysdb_add_basic_user_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, + uid_t uid, gid_t gid, + const char *gecos, + const char *homedir, + const char *shell) +{ + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; + struct ldb_request *ldbreq; + struct ldb_message *msg; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->ignore_not_found = false; + state->ldbreply = NULL; + + msg = ldb_msg_new(state); + if (!msg) { + ERROR_OUT(ret, ENOMEM, fail); + } + + /* user dn */ + msg->dn = sysdb_user_dn(handle->ctx, msg, domain->name, name); + if (!msg->dn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + ret = add_string(msg, LDB_FLAG_MOD_ADD, "objectClass", SYSDB_USER_CLASS); + if (ret) goto fail; + + ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_NAME, name); + if (ret) goto fail; + + ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_UIDNUM, (unsigned long)uid); + if (ret) goto fail; + + ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_GIDNUM, (unsigned long)gid); + if (ret) goto fail; + + /* We set gecos to be the same as fullname on user creation, + * But we will not enforce coherency after that, it's up to + * admins to decide if they want to keep it in sync if they change + * one of the 2 */ + if (gecos && *gecos) { + ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_FULLNAME, gecos); + if (ret) goto fail; + ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_GECOS, gecos); + if (ret) goto fail; + } + + if (homedir && *homedir) { + ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_HOMEDIR, homedir); + if (ret) goto fail; + } + + if (shell && *shell) { + ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_SHELL, shell); + if (ret) goto fail; + } + + /* creation time */ + ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_CREATE_TIME, + (unsigned long)time(NULL)); + if (ret) goto fail; + + + ret = ldb_build_add_req(&ldbreq, handle->ctx->ldb, state, msg, + NULL, NULL, NULL, NULL); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to build modify request: %s(%d)[%s]\n", + ldb_strerror(ret), ret, ldb_errstring(handle->ctx->ldb))); + ERROR_OUT(ret, sysdb_error_to_errno(ret), fail); + } + + subreq = sldb_request_send(state, ev, handle->ctx->ldb, ldbreq); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_op_default_done, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +int sysdb_add_basic_user_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Add-User-Function===================================================== */ + +struct sysdb_add_user_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + struct sss_domain_info *domain; + + const char *name; + uid_t uid; + gid_t gid; + const char *gecos; + const char *homedir; + const char *shell; + struct sysdb_attrs *attrs; + + int cache_timeout; +}; + +static void sysdb_add_user_group_check(struct tevent_req *subreq); +static void sysdb_add_user_uid_check(struct tevent_req *subreq); +static void sysdb_add_user_basic_done(struct tevent_req *subreq); +static void sysdb_add_user_get_id_done(struct tevent_req *subreq); +static void sysdb_add_user_set_id_done(struct tevent_req *subreq); +static void sysdb_add_user_set_attrs(struct tevent_req *req); +static void sysdb_add_user_set_attrs_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_add_user_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, + uid_t uid, gid_t gid, + const char *gecos, + const char *homedir, + const char *shell, + struct sysdb_attrs *attrs, + int cache_timeout) +{ + struct tevent_req *req, *subreq; + struct sysdb_add_user_state *state; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_add_user_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->domain = domain; + state->name = name; + state->uid = uid; + state->gid = gid; + state->gecos = gecos; + state->homedir = homedir; + state->shell = shell; + state->attrs = attrs; + state->cache_timeout = cache_timeout; + + if (handle->ctx->mpg) { + if (gid != 0) { + DEBUG(0, ("Cannot add user with arbitrary GID in MPG domain!\n")); + ERROR_OUT(ret, EINVAL, fail); + } + state->gid = state->uid; + } + + if (domain->id_max != 0 && uid != 0 && + (uid < domain->id_min || uid > domain->id_max)) { + DEBUG(2, ("Supplied uid [%d] is not in the allowed range [%d-%d].\n", + uid, domain->id_min, domain->id_max)); + ERROR_OUT(ret, ERANGE, fail); + } + + if (domain->id_max != 0 && gid != 0 && + (gid < domain->id_min || gid > domain->id_max)) { + DEBUG(2, ("Supplied gid [%d] is not in the allowed range [%d-%d].\n", + gid, domain->id_min, domain->id_max)); + ERROR_OUT(ret, ERANGE, fail); + } + + if (handle->ctx->mpg) { + /* In MPG domains you can't have groups with the same name as users, + * search if a group with the same name exists. + * Don't worry about users, if we try to add a user with the same + * name the operation will fail */ + + subreq = sysdb_search_group_by_name_send(state, ev, NULL, handle, + domain, name, NULL); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_add_user_group_check, req); + return req; + } + + /* check no other user with the same uid exist */ + if (state->uid != 0) { + subreq = sysdb_search_user_by_uid_send(state, ev, NULL, handle, + domain, uid, NULL); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_add_user_uid_check, req); + return req; + } + + /* try to add the user */ + subreq = sysdb_add_basic_user_send(state, ev, handle, + domain, name, uid, gid, + gecos, homedir, shell); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_add_user_basic_done, req); + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_add_user_group_check(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_add_user_state *state = tevent_req_data(req, + struct sysdb_add_user_state); + struct ldb_message *msg; + int ret; + + /* We can succeed only if we get an ENOENT error, which means no groups + * with the same name exist. + * If any other error is returned fail as well. */ + ret = sysdb_search_group_recv(subreq, state, &msg); + talloc_zfree(subreq); + if (ret != ENOENT) { + if (ret == EOK) ret = EEXIST; + tevent_req_error(req, ret); + return; + } + + /* check no other user with the same uid exist */ + if (state->uid != 0) { + subreq = sysdb_search_user_by_uid_send(state, state->ev, + NULL, state->handle, + state->domain, state->uid, + NULL); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_user_uid_check, req); + return; + } + + /* try to add the user */ + subreq = sysdb_add_basic_user_send(state, state->ev, state->handle, + state->domain, state->name, + state->uid, state->gid, + state->gecos, + state->homedir, + state->shell); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_user_basic_done, req); +} + +static void sysdb_add_user_uid_check(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_add_user_state *state = tevent_req_data(req, + struct sysdb_add_user_state); + struct ldb_message *msg; + int ret; + + /* We can succeed only if we get an ENOENT error, which means no user + * with the same uid exist. + * If any other error is returned fail as well. */ + ret = sysdb_search_user_recv(subreq, state, &msg); + talloc_zfree(subreq); + if (ret != ENOENT) { + if (ret == EOK) ret = EEXIST; + tevent_req_error(req, ret); + return; + } + + /* try to add the user */ + subreq = sysdb_add_basic_user_send(state, state->ev, state->handle, + state->domain, state->name, + state->uid, state->gid, + state->gecos, + state->homedir, + state->shell); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_user_basic_done, req); +} + +static void sysdb_add_user_basic_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_add_user_state *state = tevent_req_data(req, + struct sysdb_add_user_state); + int ret; + + ret = sysdb_add_basic_user_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + if (state->uid == 0) { + subreq = sysdb_get_new_id_send(state, + state->ev, state->handle, + state->domain); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_user_get_id_done, req); + return; + } + + sysdb_add_user_set_attrs(req); +} + +static void sysdb_add_user_get_id_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_add_user_state *state = tevent_req_data(req, + struct sysdb_add_user_state); + struct sysdb_attrs *id_attrs; + uint32_t id; + int ret; + + ret = sysdb_get_new_id_recv(subreq, &id); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + if (state->uid == 0) { + id_attrs = sysdb_new_attrs(state); + if (!id_attrs) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + ret = sysdb_attrs_add_uint32(id_attrs, SYSDB_UIDNUM, id); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + if (state->handle->ctx->mpg) { + ret = sysdb_attrs_add_uint32(id_attrs, SYSDB_GIDNUM, id); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + } + + subreq = sysdb_set_user_attr_send(state, state->ev, state->handle, + state->domain, state->name, + id_attrs, SYSDB_MOD_REP); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_user_set_id_done, req); + return; + } + + sysdb_add_user_set_attrs(req); +} + +static void sysdb_add_user_set_id_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_add_user_state *state = tevent_req_data(req, + struct sysdb_add_user_state); + int ret; + + ret = sysdb_set_user_attr_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + if (state->attrs) { + } + + tevent_req_done(req); +} + +static void sysdb_add_user_set_attrs(struct tevent_req *req) +{ + struct sysdb_add_user_state *state = tevent_req_data(req, + struct sysdb_add_user_state); + struct tevent_req *subreq; + time_t now = time(NULL); + int ret; + + if (!state->attrs) { + state->attrs = sysdb_new_attrs(state); + if (!state->attrs) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + } + + ret = sysdb_attrs_add_time_t(state->attrs, SYSDB_LAST_UPDATE, now); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + ret = sysdb_attrs_add_time_t(state->attrs, SYSDB_CACHE_EXPIRE, + ((state->cache_timeout) ? + (now + state->cache_timeout) : 0)); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + subreq = sysdb_set_user_attr_send(state, state->ev, + state->handle, state->domain, + state->name, state->attrs, + SYSDB_MOD_REP); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_user_set_attrs_done, req); +} + +static void sysdb_add_user_set_attrs_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_set_user_attr_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_add_user_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Add-Basic-Group-NO-CHECKS============================================= */ + +struct tevent_req *sysdb_add_basic_group_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, gid_t gid) +{ + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; + struct ldb_request *ldbreq; + struct ldb_message *msg; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->ignore_not_found = false; + state->ldbreply = NULL; + + msg = ldb_msg_new(state); + if (!msg) { + ERROR_OUT(ret, ENOMEM, fail); + } + + /* user dn */ + msg->dn = sysdb_group_dn(handle->ctx, msg, domain->name, name); + if (!msg->dn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + ret = add_string(msg, LDB_FLAG_MOD_ADD, "objectClass", SYSDB_GROUP_CLASS); + if (ret) goto fail; + + ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_NAME, name); + if (ret) goto fail; + + ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_GIDNUM, (unsigned long)gid); + if (ret) goto fail; + + /* creation time */ + ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_CREATE_TIME, + (unsigned long)time(NULL)); + if (ret) goto fail; + + + ret = ldb_build_add_req(&ldbreq, handle->ctx->ldb, state, msg, + NULL, NULL, NULL, NULL); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to build modify request: %s(%d)[%s]\n", + ldb_strerror(ret), ret, ldb_errstring(handle->ctx->ldb))); + ERROR_OUT(ret, sysdb_error_to_errno(ret), fail); + } + + subreq = sldb_request_send(state, ev, handle->ctx->ldb, ldbreq); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_op_default_done, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +int sysdb_add_basic_group_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Add-Group-Function==================================================== */ + +struct sysdb_add_group_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + struct sss_domain_info *domain; + + const char *name; + gid_t gid; + struct sysdb_attrs *attrs; + + int cache_timeout; +}; + +static void sysdb_add_group_user_check(struct tevent_req *subreq); +static void sysdb_add_group_gid_check(struct tevent_req *subreq); +static void sysdb_add_group_basic_done(struct tevent_req *subreq); +static void sysdb_add_group_get_id_done(struct tevent_req *subreq); +static void sysdb_add_group_set_attrs(struct tevent_req *req); +static void sysdb_add_group_set_attrs_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_add_group_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, gid_t gid, + struct sysdb_attrs *attrs, + int cache_timeout) +{ + struct tevent_req *req, *subreq; + struct sysdb_add_group_state *state; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_add_group_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->domain = domain; + state->name = name; + state->gid = gid; + state->attrs = attrs; + state->cache_timeout = cache_timeout; + + if (domain->id_max != 0 && gid != 0 && + (gid < domain->id_min || gid > domain->id_max)) { + DEBUG(2, ("Supplied gid [%d] is not in the allowed range [%d-%d].\n", + gid, domain->id_min, domain->id_max)); + ERROR_OUT(ret, ERANGE, fail); + } + + if (handle->ctx->mpg) { + /* In MPG domains you can't have groups with the same name as users, + * search if a group with the same name exists. + * Don't worry about users, if we try to add a user with the same + * name the operation will fail */ + + subreq = sysdb_search_user_by_name_send(state, ev, NULL, handle, + domain, name, NULL); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_add_group_user_check, req); + return req; + } + + /* check no other groups with the same gid exist */ + if (state->gid != 0) { + subreq = sysdb_search_group_by_gid_send(state, ev, NULL, handle, + domain, gid, NULL); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_add_group_gid_check, req); + return req; + } + + /* try to add the group */ + subreq = sysdb_add_basic_group_send(state, ev, handle, + domain, name, gid); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_add_group_basic_done, req); + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_add_group_user_check(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_add_group_state *state = tevent_req_data(req, + struct sysdb_add_group_state); + struct ldb_message *msg; + int ret; + + /* We can succeed only if we get an ENOENT error, which means no users + * with the same name exist. + * If any other error is returned fail as well. */ + ret = sysdb_search_user_recv(subreq, state, &msg); + talloc_zfree(subreq); + if (ret != ENOENT) { + if (ret == EOK) ret = EEXIST; + tevent_req_error(req, ret); + return; + } + + /* check no other group with the same gid exist */ + if (state->gid != 0) { + subreq = sysdb_search_group_by_gid_send(state, state->ev, + NULL, state->handle, + state->domain, state->gid, + NULL); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_group_gid_check, req); + return; + } + + /* try to add the group */ + subreq = sysdb_add_basic_group_send(state, state->ev, + state->handle, state->domain, + state->name, state->gid); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_group_basic_done, req); +} + +static void sysdb_add_group_gid_check(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_add_group_state *state = tevent_req_data(req, + struct sysdb_add_group_state); + struct ldb_message *msg; + int ret; + + /* We can succeed only if we get an ENOENT error, which means no group + * with the same gid exist. + * If any other error is returned fail as well. */ + ret = sysdb_search_group_recv(subreq, state, &msg); + talloc_zfree(subreq); + if (ret != ENOENT) { + if (ret == EOK) ret = EEXIST; + tevent_req_error(req, ret); + return; + } + + /* try to add the group */ + subreq = sysdb_add_basic_group_send(state, state->ev, + state->handle, state->domain, + state->name, state->gid); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_group_basic_done, req); +} + +static void sysdb_add_group_basic_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_add_group_state *state = tevent_req_data(req, + struct sysdb_add_group_state); + int ret; + + ret = sysdb_add_basic_group_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + if (state->gid == 0) { + subreq = sysdb_get_new_id_send(state, + state->ev, state->handle, + state->domain); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_group_get_id_done, req); + return; + } + + sysdb_add_group_set_attrs(req); +} + +static void sysdb_add_group_get_id_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_add_group_state *state = tevent_req_data(req, + struct sysdb_add_group_state); + uint32_t id; + int ret; + + ret = sysdb_get_new_id_recv(subreq, &id); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + if (state->gid == 0) { + if (!state->attrs) { + state->attrs = sysdb_new_attrs(state); + if (!state->attrs) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + } + + ret = sysdb_attrs_add_uint32(state->attrs, SYSDB_GIDNUM, id); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + } + + sysdb_add_group_set_attrs(req); +} + +static void sysdb_add_group_set_attrs(struct tevent_req *req) +{ + struct sysdb_add_group_state *state = tevent_req_data(req, + struct sysdb_add_group_state); + struct tevent_req *subreq; + time_t now = time(NULL); + int ret; + + if (!state->attrs) { + state->attrs = sysdb_new_attrs(state); + if (!state->attrs) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + } + + ret = sysdb_attrs_add_time_t(state->attrs, SYSDB_LAST_UPDATE, now); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + ret = sysdb_attrs_add_time_t(state->attrs, SYSDB_CACHE_EXPIRE, + ((state->cache_timeout) ? + (now + state->cache_timeout) : 0)); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + subreq = sysdb_set_group_attr_send(state, state->ev, + state->handle, state->domain, + state->name, state->attrs, + SYSDB_MOD_REP); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_group_set_attrs_done, req); +} + +static void sysdb_add_group_set_attrs_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_set_group_attr_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_add_group_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Add-Or-Remove-Group-Memeber=========================================== */ + +/* mod_op must be either SYSDB_MOD_ADD or SYSDB_MOD_DEL */ +struct tevent_req *sysdb_mod_group_member_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct ldb_dn *member_dn, + struct ldb_dn *group_dn, + int mod_op) +{ + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; + struct ldb_request *ldbreq; + struct ldb_message *msg; + const char *dn; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->ignore_not_found = false; + state->ldbreply = NULL; + + msg = ldb_msg_new(state); + if (!msg) { + ERROR_OUT(ret, ENOMEM, fail); + } + + msg->dn = group_dn; + ret = ldb_msg_add_empty(msg, SYSDB_MEMBER, mod_op, NULL); + if (ret != LDB_SUCCESS) { + ERROR_OUT(ret, ENOMEM, fail); + } + + dn = ldb_dn_get_linearized(member_dn); + if (!dn) { + ERROR_OUT(ret, EINVAL, fail); + } + + ret = ldb_msg_add_fmt(msg, SYSDB_MEMBER, "%s", dn); + if (ret != LDB_SUCCESS) { + ERROR_OUT(ret, EINVAL, fail); + } + + ret = ldb_build_mod_req(&ldbreq, handle->ctx->ldb, state, msg, + NULL, NULL, NULL, NULL); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to build modify request: %s(%d)[%s]\n", + ldb_strerror(ret), ret, ldb_errstring(handle->ctx->ldb))); + ERROR_OUT(ret, sysdb_error_to_errno(ret), fail); + } + + subreq = sldb_request_send(state, ev, handle->ctx->ldb, ldbreq); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_op_default_done, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +int sysdb_mod_group_member_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Store-Users-(Native/Legacy)-(replaces-existing-data)================== */ + +/* if one of the basic attributes is empty ("") as opposed to NULL, + * this will just remove it */ + +struct sysdb_store_user_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + struct sss_domain_info *domain; + + const char *name; + uid_t uid; + gid_t gid; + const char *gecos; + const char *homedir; + const char *shell; + struct sysdb_attrs *attrs; + + uint64_t cache_timeout; +}; + +static void sysdb_store_user_check(struct tevent_req *subreq); +static void sysdb_store_user_add_done(struct tevent_req *subreq); +static void sysdb_store_user_attr_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_store_user_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, + const char *pwd, + uid_t uid, gid_t gid, + const char *gecos, + const char *homedir, + const char *shell, + struct sysdb_attrs *attrs, + uint64_t cache_timeout) +{ + struct tevent_req *req, *subreq; + struct sysdb_store_user_state *state; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_store_user_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->domain = domain; + state->name = name; + state->uid = uid; + state->gid = gid; + state->gecos = gecos; + state->homedir = homedir; + state->shell = shell; + state->attrs = attrs; + state->cache_timeout = cache_timeout; + + if (pwd && (domain->legacy_passwords || !*pwd)) { + ret = sysdb_attrs_add_string(state->attrs, SYSDB_PWD, pwd); + if (ret) goto fail; + } + + subreq = sysdb_search_user_by_name_send(state, ev, NULL, handle, + domain, name, NULL); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_store_user_check, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_store_user_check(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_store_user_state *state = tevent_req_data(req, + struct sysdb_store_user_state); + struct ldb_message *msg; + time_t now = time(NULL); + int ret; + + ret = sysdb_search_user_recv(subreq, state, &msg); + talloc_zfree(subreq); + if (ret && ret != ENOENT) { + tevent_req_error(req, ret); + return; + } + + if (ret == ENOENT) { + /* users doesn't exist, turn into adding a user */ + subreq = sysdb_add_user_send(state, state->ev, state->handle, + state->domain, state->name, + state->uid, state->gid, + state->gecos, state->homedir, + state->shell, state->attrs, + state->cache_timeout); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_store_user_add_done, req); + return; + } + + /* the user exists, let's just replace attributes when set */ + if (!state->attrs) { + state->attrs = sysdb_new_attrs(state); + if (!state->attrs) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + } + + if (state->uid) { + ret = sysdb_attrs_add_uint32(state->attrs, SYSDB_UIDNUM, state->uid); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + } + + if (state->gid) { + ret = sysdb_attrs_add_uint32(state->attrs, SYSDB_GIDNUM, state->gid); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + } + + if (state->uid && !state->gid && state->handle->ctx->mpg) { + ret = sysdb_attrs_add_uint32(state->attrs, SYSDB_GIDNUM, state->uid); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + } + + if (state->gecos) { + ret = sysdb_attrs_add_string(state->attrs, SYSDB_GECOS, state->gecos); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + } + + if (state->homedir) { + ret = sysdb_attrs_add_string(state->attrs, + SYSDB_HOMEDIR, state->homedir); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + } + + if (state->shell) { + ret = sysdb_attrs_add_string(state->attrs, SYSDB_SHELL, state->shell); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + } + + ret = sysdb_attrs_add_time_t(state->attrs, SYSDB_LAST_UPDATE, now); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + ret = sysdb_attrs_add_time_t(state->attrs, SYSDB_CACHE_EXPIRE, + ((state->cache_timeout) ? + (now + state->cache_timeout) : 0)); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + subreq = sysdb_set_user_attr_send(state, state->ev, + state->handle, state->domain, + state->name, state->attrs, + SYSDB_MOD_REP); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_store_user_attr_done, req); +} + +static void sysdb_store_user_add_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_add_user_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +static void sysdb_store_user_attr_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_set_user_attr_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_store_user_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + +/* =Store-Group-(Native/Legacy)-(replaces-existing-data)================== */ + +/* this function does not check that all user members are actually present */ + +struct sysdb_store_group_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + struct sss_domain_info *domain; + + const char *name; + gid_t gid; + + struct sysdb_attrs *attrs; + + uint64_t cache_timeout; +}; + +static void sysdb_store_group_check(struct tevent_req *subreq); +static void sysdb_store_group_add_done(struct tevent_req *subreq); +static void sysdb_store_group_attr_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_store_group_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, + gid_t gid, + struct sysdb_attrs *attrs, + uint64_t cache_timeout) +{ + struct tevent_req *req, *subreq; + struct sysdb_store_group_state *state; + static const char *src_attrs[] = { SYSDB_NAME, SYSDB_GIDNUM, + SYSDB_ORIG_MODSTAMP, NULL }; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_store_group_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->domain = domain; + state->name = name; + state->gid = gid; + state->attrs = attrs; + state->cache_timeout = cache_timeout; + + subreq = sysdb_search_group_by_name_send(state, ev, NULL, handle, + domain, name, src_attrs); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_store_group_check, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_store_group_check(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_store_group_state *state = tevent_req_data(req, + struct sysdb_store_group_state); + struct ldb_message *msg; + time_t now = time(NULL); + bool new_group = false; + int ret; + + ret = sysdb_search_group_recv(subreq, state, &msg); + talloc_zfree(subreq); + if (ret && ret != ENOENT) { + tevent_req_error(req, ret); + return; + } + if (ret == ENOENT) { + new_group = true; + } + + /* FIXME: use the remote modification timestamp to know if the + * group needs any update */ + + if (new_group) { + /* group doesn't exist, turn into adding a group */ + subreq = sysdb_add_group_send(state, state->ev, state->handle, + state->domain, state->name, + state->gid, state->attrs, + state->cache_timeout); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_store_group_add_done, req); + + return; + } + + /* the group exists, let's just replace attributes when set */ + + if (!state->attrs) { + state->attrs = sysdb_new_attrs(state); + if (!state->attrs) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + } + + if (state->gid) { + ret = sysdb_attrs_add_uint32(state->attrs, SYSDB_GIDNUM, state->gid); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + } + + ret = sysdb_attrs_add_time_t(state->attrs, SYSDB_LAST_UPDATE, now); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + ret = sysdb_attrs_add_time_t(state->attrs, SYSDB_CACHE_EXPIRE, + ((state->cache_timeout) ? + (now + state->cache_timeout) : 0)); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + subreq = sysdb_set_group_attr_send(state, state->ev, + state->handle, state->domain, + state->name, state->attrs, + SYSDB_MOD_REP); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_store_group_attr_done, req); +} + +static void sysdb_store_group_add_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_add_group_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +static void sysdb_store_group_attr_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_set_group_attr_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_store_group_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Add-User-to-Group(Native/Legacy)====================================== */ + +static void sysdb_add_group_member_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_add_group_member_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *group, + const char *user) +{ + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; + struct ldb_dn *group_dn, *user_dn; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->ignore_not_found = false; + state->ldbreply = NULL; + + group_dn = sysdb_group_dn(handle->ctx, state, domain->name, group); + if (!group_dn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + user_dn = sysdb_user_dn(handle->ctx, state, domain->name, user); + if (!user_dn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + subreq = sysdb_mod_group_member_send(state, ev, handle, + user_dn, group_dn, + SYSDB_MOD_ADD); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_add_group_member_done, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_add_group_member_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_mod_group_member_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_add_group_member_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Remove-member-from-Group(Native/Legacy)=============================== */ + +static void sysdb_remove_group_member_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_remove_group_member_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *group, + const char *user) +{ + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; + struct ldb_dn *group_dn, *user_dn; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->ignore_not_found = false; + state->ldbreply = NULL; + + group_dn = sysdb_group_dn(handle->ctx, state, domain->name, group); + if (!group_dn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + user_dn = sysdb_user_dn(handle->ctx, state, domain->name, user); + if (!user_dn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + subreq = sysdb_mod_group_member_send(state, ev, handle, + user_dn, group_dn, + SYSDB_MOD_DEL); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_remove_group_member_done, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_remove_group_member_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_mod_group_member_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_remove_group_member_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Password-Caching====================================================== */ + +struct sysdb_cache_pw_state { + struct tevent_context *ev; + struct sss_domain_info *domain; + + const char *username; + struct sysdb_attrs *attrs; + + struct sysdb_handle *handle; + bool commit; +}; + +static void sysdb_cache_password_trans(struct tevent_req *subreq); +static void sysdb_cache_password_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_cache_password_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *username, + const char *password) +{ + struct tevent_req *req, *subreq; + struct sysdb_cache_pw_state *state; + char *hash = NULL; + char *salt; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_cache_pw_state); + if (!req) return NULL; + + state->ev = ev; + state->domain = domain; + state->username = username; + + ret = s3crypt_gen_salt(state, &salt); + if (ret) { + DEBUG(4, ("Failed to generate random salt.\n")); + goto fail; + } + + ret = s3crypt_sha512(state, password, salt, &hash); + if (ret) { + DEBUG(4, ("Failed to create password hash.\n")); + goto fail; + } + + state->attrs = sysdb_new_attrs(state); + if (!state->attrs) { + ERROR_OUT(ret, ENOMEM, fail); + } + + ret = sysdb_attrs_add_string(state->attrs, SYSDB_CACHEDPWD, hash); + if (ret) goto fail; + + /* FIXME: should we use a different attribute for chache passwords ?? */ + ret = sysdb_attrs_add_long(state->attrs, "lastCachedPasswordChange", + (long)time(NULL)); + if (ret) goto fail; + + ret = sysdb_attrs_add_uint32(state->attrs, SYSDB_FAILED_LOGIN_ATTEMPTS, 0U); + if (ret) goto fail; + + state->handle = NULL; + + if (handle) { + state->handle = handle; + state->commit = false; + + subreq = sysdb_set_user_attr_send(state, state->ev, state->handle, + state->domain, state->username, + state->attrs, SYSDB_MOD_REP); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_cache_password_done, req); + } else { + state->commit = true; + + subreq = sysdb_transaction_send(state, state->ev, sysdb); + if (!subreq) { + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sysdb_cache_password_trans, req); + } + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_cache_password_trans(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_cache_pw_state *state = tevent_req_data(req, + struct sysdb_cache_pw_state); + int ret; + + ret = sysdb_transaction_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + subreq = sysdb_set_user_attr_send(state, state->ev, state->handle, + state->domain, state->username, + state->attrs, SYSDB_MOD_REP); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_cache_password_done, req); +} + +static void sysdb_cache_password_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_cache_pw_state *state = tevent_req_data(req, + struct sysdb_cache_pw_state); + int ret; + + ret = sysdb_set_user_attr_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + if (state->commit) { + subreq = sysdb_transaction_commit_send(state, state->ev, + state->handle); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_transaction_complete, req); + return; + } + + tevent_req_done(req); +} + +int sysdb_cache_password_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + +/* = sysdb_check_handle ================== */ +struct sysdb_check_handle_state { + struct tevent_context *ev; + struct sysdb_handle *handle; +}; + +static void sysdb_check_handle_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_check_handle_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle) +{ + struct tevent_req *req; + struct tevent_req *subreq; + struct sysdb_check_handle_state *state; + + if (sysdb == NULL && handle == NULL) { + DEBUG(1, ("Sysdb context not available.\n")); + return NULL; + } + + req = tevent_req_create(mem_ctx, &state, struct sysdb_check_handle_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ev = ev; + + if (handle != NULL) { + state->handle = talloc_memdup(state, handle, sizeof(struct sysdb_handle)); + if (state->handle == NULL) { + DEBUG(1, ("talloc_memdup failed.\n")); + tevent_req_error(req, ENOMEM); + } else { + tevent_req_done(req); + } + tevent_req_post(req, ev); + return req; + } + + state->handle = NULL; + + subreq = sysdb_operation_send(state, state->ev, sysdb); + if (!subreq) { + DEBUG(1, ("sysdb_operation_send failed.\n")); + tevent_req_error(req, ENOMEM); + tevent_req_post(req, ev); + return req; + } + tevent_req_set_callback(subreq, sysdb_check_handle_done, req); + + return req; +} + +static void sysdb_check_handle_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_check_handle_state *state = tevent_req_data(req, + struct sysdb_check_handle_state); + int ret; + + ret = sysdb_operation_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); + return; +} + +int sysdb_check_handle_recv(struct tevent_req *req, TALLOC_CTX *memctx, + struct sysdb_handle **handle) +{ + struct sysdb_check_handle_state *state = tevent_req_data(req, + struct sysdb_check_handle_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *handle = talloc_move(memctx, &state->handle); + + return EOK; + +} + +/* =Custom Search================== */ +struct sysdb_search_custom_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + + struct ldb_dn *basedn; + const char **attrs; + const char *filter; + int scope; + bool expect_not_more_than_one; + + size_t msgs_count; + struct ldb_message **msgs; +}; + +static void sysdb_search_custom_check_handle_done(struct tevent_req *subreq); +static void sysdb_search_custom_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_search_custom_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *filter, + const char *subtree_name, + const char **attrs) +{ + struct tevent_req *req, *subreq; + struct sysdb_search_custom_state *state; + int ret; + + if (sysdb == NULL && handle == NULL) return NULL; + + if (filter == NULL || subtree_name == NULL) return NULL; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_search_custom_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ev = ev; + state->handle = handle; + state->attrs = attrs; + state->filter = filter; + state->scope = LDB_SCOPE_SUBTREE; + state->expect_not_more_than_one = false; + state->msgs_count = 0; + state->msgs = NULL; + + if (sysdb == NULL) { + sysdb = handle->ctx; + } + state->basedn = sysdb_custom_subtree_dn(sysdb, state, domain->name, + subtree_name); + if (state->basedn == NULL) { + DEBUG(1, ("sysdb_custom_subtree_dn failed.\n")); + ret = ENOMEM; + goto fail; + } + if (!ldb_dn_validate(state->basedn)) { + DEBUG(1, ("Failed to create DN.\n")); + ret = EINVAL; + goto fail; + } + + subreq = sysdb_check_handle_send(state, state->ev, sysdb, state->handle); + if (!subreq) { + DEBUG(1, ("sysdb_check_handle_send failed.\n")); + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sysdb_search_custom_check_handle_done, req); + + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +struct tevent_req *sysdb_search_custom_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *object_name, + const char *subtree_name, + const char **attrs) +{ + struct tevent_req *req, *subreq; + struct sysdb_search_custom_state *state; + int ret; + + if (sysdb == NULL && handle == NULL) return NULL; + + if (object_name == NULL || subtree_name == NULL) return NULL; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_search_custom_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ev = ev; + state->handle = handle; + state->attrs = attrs; + state->filter = NULL; + state->scope = LDB_SCOPE_BASE; + state->expect_not_more_than_one = true; + state->msgs_count = 0; + state->msgs = NULL; + + if (sysdb == NULL) { + sysdb = handle->ctx; + } + state->basedn = sysdb_custom_dn(sysdb, state, domain->name, object_name, + subtree_name); + if (state->basedn == NULL) { + DEBUG(1, ("sysdb_custom_dn failed.\n")); + ret = ENOMEM; + goto fail; + } + if (!ldb_dn_validate(state->basedn)) { + DEBUG(1, ("Failed to create DN.\n")); + ret = EINVAL; + goto fail; + } + + subreq = sysdb_check_handle_send(state, state->ev, sysdb, state->handle); + if (!subreq) { + DEBUG(1, ("sysdb_check_handle_send failed.\n")); + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sysdb_search_custom_check_handle_done, req); + + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_search_custom_check_handle_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_search_custom_state *state = tevent_req_data(req, + struct sysdb_search_custom_state); + int ret; + + ret = sysdb_check_handle_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + subreq = sysdb_search_entry_send(state, state->ev, state->handle, + state->basedn, state->scope, + state->filter, state->attrs); + if (!subreq) { + DEBUG(1, ("sysdb_search_entry_send failed.\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_search_custom_done, req); + return; +} + +static void sysdb_search_custom_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_search_custom_state *state = tevent_req_data(req, + struct sysdb_search_custom_state); + int ret; + + ret = sysdb_search_entry_recv(subreq, state, &state->msgs_count, + &state->msgs); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + if (state->expect_not_more_than_one && state->msgs_count > 1) { + DEBUG(1, ("More than one result found.\n")); + tevent_req_error(req, EFAULT); + return; + } + + tevent_req_done(req); +} + +int sysdb_search_custom_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *msgs_count, + struct ldb_message ***msgs) +{ + struct sysdb_search_custom_state *state = tevent_req_data(req, + struct sysdb_search_custom_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *msgs_count = state->msgs_count; + *msgs = talloc_move(mem_ctx, &state->msgs); + + return EOK; +} + + +/* =Custom Store (replaces-existing-data)================== */ + +struct sysdb_store_custom_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + struct sss_domain_info *domain; + + const char *object_name; + const char *subtree_name; + struct ldb_dn *dn; + struct sysdb_attrs *attrs; + struct ldb_message *msg; +}; + +static void sysdb_store_custom_check_done(struct tevent_req *subreq); +static void sysdb_store_custom_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_store_custom_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *object_name, + const char *subtree_name, + struct sysdb_attrs *attrs) +{ + struct tevent_req *req, *subreq; + struct sysdb_store_custom_state *state; + int ret; + const char **search_attrs; + + if (object_name == NULL || subtree_name == NULL) return NULL; + + if (handle == NULL) { + DEBUG(1, ("Sysdb context not available.\n")); + return NULL; + } + + req = tevent_req_create(mem_ctx, &state, struct sysdb_store_custom_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ev = ev; + state->handle = handle; + state->domain = domain; + state->object_name = object_name; + state->subtree_name = subtree_name; + state->attrs = attrs; + state->msg = NULL; + state->dn = sysdb_custom_dn(handle->ctx, state, domain->name, object_name, + subtree_name); + if (state->dn == NULL) { + DEBUG(1, ("sysdb_custom_dn failed.\n")); + ret = ENOMEM; + goto fail; + } + + search_attrs = talloc_array(state, const char *, 2); + if (search_attrs == NULL) { + DEBUG(1, ("talloc_array failed.\n")); + ret = ENOMEM; + goto fail; + } + search_attrs[0] = "*"; + search_attrs[1] = NULL; + + subreq = sysdb_search_custom_by_name_send(state, state->ev, NULL, + state->handle, + state->domain, + state->object_name, + state->subtree_name, + search_attrs); + if (!subreq) { + DEBUG(1, ("sysdb_search_custom_by_name_send failed.\n")); + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sysdb_store_custom_check_done, req); + + return req; +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_store_custom_check_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_store_custom_state *state = tevent_req_data(req, + struct sysdb_store_custom_state); + int ret; + int i; + size_t resp_count = 0; + struct ldb_message **resp; + struct ldb_message *msg; + struct ldb_request *ldbreq; + struct ldb_message_element *el; + bool add_object = false; + + ret = sysdb_search_custom_recv(subreq, state, &resp_count, &resp); + talloc_zfree(subreq); + if (ret != EOK && ret != ENOENT) { + tevent_req_error(req, ret); + return; + } + + if (ret == ENOENT) { + add_object = true; + } + + msg = ldb_msg_new(state); + if (msg == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + + msg->dn = state->dn; + + msg->elements = talloc_array(msg, struct ldb_message_element, + state->attrs->num); + if (!msg->elements) { + tevent_req_error(req, ENOMEM); + return; + } + + for (i = 0; i < state->attrs->num; i++) { + msg->elements[i] = state->attrs->a[i]; + if (add_object) { + msg->elements[i].flags = LDB_FLAG_MOD_ADD; + } else { + el = ldb_msg_find_element(resp[0], state->attrs->a[i].name); + if (el == NULL) { + msg->elements[i].flags = LDB_FLAG_MOD_ADD; + } else { + msg->elements[i].flags = LDB_FLAG_MOD_REPLACE; + } + } + } + msg->num_elements = state->attrs->num; + + if (add_object) { + ret = ldb_build_add_req(&ldbreq, state->handle->ctx->ldb, state, msg, + NULL, NULL, NULL, NULL); + } else { + ret = ldb_build_mod_req(&ldbreq, state->handle->ctx->ldb, state, msg, + NULL, NULL, NULL, NULL); + } + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to build request: %s(%d)[%s]\n", + ldb_strerror(ret), ret, + ldb_errstring(state->handle->ctx->ldb))); + tevent_req_error(req, sysdb_error_to_errno(ret)); + return; + } + + subreq = sldb_request_send(state, state->ev, state->handle->ctx->ldb, + ldbreq); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_store_custom_done, req); + return; +} + +static void sysdb_store_custom_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_op_default_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); + return; +} + +int sysdb_store_custom_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +/* = Custom Delete======================================= */ + +struct sysdb_delete_custom_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + struct sss_domain_info *domain; + + const char *object_name; + const char *subtree_name; + struct ldb_dn *dn; +}; +static void sysdb_delete_custom_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_delete_custom_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *object_name, + const char *subtree_name) +{ + struct tevent_req *req, *subreq; + struct sysdb_delete_custom_state *state; + int ret; + + if (object_name == NULL || subtree_name == NULL) return NULL; + + if (handle == NULL) { + DEBUG(1, ("Sysdb context not available.\n")); + return NULL; + } + + req = tevent_req_create(mem_ctx, &state, struct sysdb_store_custom_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ev = ev; + state->handle = handle; + state->domain = domain; + state->object_name = object_name; + state->subtree_name = subtree_name; + state->dn = sysdb_custom_dn(handle->ctx, state, domain->name, object_name, + subtree_name); + if (state->dn == NULL) { + DEBUG(1, ("sysdb_custom_dn failed.\n")); + ret = ENOMEM; + goto fail; + } + + subreq = sysdb_delete_entry_send(state, state->ev, state->handle, + state->dn, true); + if (!subreq) { + DEBUG(1, ("sysdb_delete_entry_send failed.\n")); + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sysdb_delete_custom_done, req); + + return req; +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_delete_custom_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_delete_entry_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); + return; +} + +int sysdb_delete_custom_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +/* = ASQ search request ======================================== */ +struct sysdb_asq_search_state { + struct tevent_context *ev; + struct sysdb_ctx *sysdb; + struct sysdb_handle *handle; + struct sss_domain_info *domain; + struct ldb_dn *base_dn; + const char *asq_attribute; + const char **attrs; + const char *expression; + + int msgs_count; + struct ldb_message **msgs; +}; + +void sysdb_asq_search_check_handle_done(struct tevent_req *subreq); +static void sysdb_asq_search_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_asq_search_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + struct ldb_dn *base_dn, + const char *expression, + const char *asq_attribute, + const char **attrs) +{ + struct tevent_req *req; + struct tevent_req *subreq; + struct sysdb_asq_search_state *state; + int ret; + + if (sysdb == NULL && handle == NULL) { + DEBUG(1, ("Sysdb context not available.\n")); + return NULL; + } + + req = tevent_req_create(mem_ctx, &state, struct sysdb_asq_search_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ev = ev; + state->sysdb = (sysdb == NULL) ? handle->ctx : sysdb; + state->handle = handle; + state->domain = domain; + state->base_dn = base_dn; + state->expression = expression; + state->asq_attribute = asq_attribute; + state->attrs = attrs; + + state->msgs_count = 0; + state->msgs = NULL; + + subreq = sysdb_check_handle_send(state, state->ev, state->sysdb, + state->handle); + if (!subreq) { + DEBUG(1, ("sysdb_check_handle_send failed.\n")); + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sysdb_asq_search_check_handle_done, req); + + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +void sysdb_asq_search_check_handle_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_asq_search_state *state = tevent_req_data(req, + struct sysdb_asq_search_state); + struct ldb_request *ldb_req; + struct ldb_control **ctrl; + struct ldb_asq_control *asq_control; + int ret; + + ret = sysdb_check_handle_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + ctrl = talloc_array(state, struct ldb_control *, 2); + if (ctrl == NULL) { + ret = ENOMEM; + goto fail; + } + + ctrl[0] = talloc(ctrl, struct ldb_control); + if (ctrl[0] == NULL) { + ret = ENOMEM; + goto fail; + } + ctrl[1] = NULL; + + ctrl[0]->oid = LDB_CONTROL_ASQ_OID; + ctrl[0]->critical = 1; + + asq_control = talloc(ctrl[0], struct ldb_asq_control); + if (asq_control == NULL) { + ret = ENOMEM; + goto fail; + } + + asq_control->request = 1; + asq_control->source_attribute = talloc_strdup(asq_control, + state->asq_attribute); + if (asq_control->source_attribute == NULL) { + ret = ENOMEM; + goto fail; + } + asq_control->src_attr_len = strlen(asq_control->source_attribute); + ctrl[0]->data = asq_control; + + ret = ldb_build_search_req(&ldb_req, state->handle->ctx->ldb, state, + state->base_dn, LDB_SCOPE_BASE, + state->expression, state->attrs, ctrl, + NULL, NULL, NULL); + if (ret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(ret); + goto fail; + } + + subreq = sldb_request_send(state, state->ev, state->handle->ctx->ldb, + ldb_req); + if (!subreq) { + ret = ENOMEM; + goto fail; + } + + tevent_req_set_callback(subreq, sysdb_asq_search_done, req); + return; + +fail: + tevent_req_error(req, ret); + return; +} + +static void sysdb_asq_search_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_asq_search_state *state = tevent_req_data(req, + struct sysdb_asq_search_state); + struct ldb_reply *ldbreply; + int ret; + + ret = sldb_request_recv(subreq, state, &ldbreply); + /* DO NOT free the subreq here, the subrequest search is not + * finished until we get an ldbreply of type LDB_REPLY_DONE */ + if (ret != EOK) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + switch (ldbreply->type) { + case LDB_REPLY_ENTRY: + state->msgs = talloc_realloc(state, state->msgs, + struct ldb_message *, + state->msgs_count + 2); + if (state->msgs == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + + state->msgs[state->msgs_count + 1] = NULL; + + state->msgs[state->msgs_count] = talloc_steal(state->msgs, + ldbreply->message); + state->msgs_count++; + + talloc_zfree(ldbreply); + return; + + case LDB_REPLY_DONE: + /* now it is safe to free the subrequest, the search is complete */ + talloc_zfree(subreq); + break; + + default: + DEBUG(1, ("Unknown ldb reply type [%d].\n", ldbreply->type)); + tevent_req_error(req, EINVAL); + return; + } + + tevent_req_done(req); +} + +int sysdb_asq_search_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + size_t *msgs_count, struct ldb_message ***msgs) +{ + struct sysdb_asq_search_state *state = tevent_req_data(req, + struct sysdb_asq_search_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *msgs_count = state->msgs_count; + *msgs = talloc_move(mem_ctx, &state->msgs); + + return EOK; +} + +/* =Search-Users-with-Custom-Filter====================================== */ + +struct sysdb_search_users_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + struct sss_domain_info *domain; + const char *sub_filter; + const char **attrs; + + struct ldb_message **msgs; + size_t msgs_count; +}; + +void sysdb_search_users_check_handle(struct tevent_req *subreq); +static void sysdb_search_users_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_search_users_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *sub_filter, + const char **attrs) +{ + struct tevent_req *req, *subreq; + struct sysdb_search_users_state *state; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_search_users_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ev = ev; + state->handle = handle; + state->domain = domain; + state->sub_filter = sub_filter; + state->attrs = attrs; + + state->msgs_count = 0; + state->msgs = NULL; + + subreq = sysdb_check_handle_send(state, ev, sysdb, handle); + if (!subreq) { + DEBUG(1, ("sysdb_check_handle_send failed.\n")); + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sysdb_search_users_check_handle, req); + + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +void sysdb_search_users_check_handle(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_search_users_state *state = tevent_req_data(req, + struct sysdb_search_users_state); + struct ldb_dn *basedn; + char *filter; + int ret; + + ret = sysdb_check_handle_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + basedn = ldb_dn_new_fmt(state, state->handle->ctx->ldb, + SYSDB_TMPL_USER_BASE, state->domain->name); + if (!basedn) { + DEBUG(2, ("Failed to build base dn\n")); + tevent_req_error(req, ENOMEM); + return; + } + + filter = talloc_asprintf(state, "(&(%s)%s)", + SYSDB_UC, state->sub_filter); + if (!filter) { + DEBUG(2, ("Failed to build filter\n")); + tevent_req_error(req, ENOMEM); + return; + } + + DEBUG(6, ("Search users with filter: %s\n", filter)); + + subreq = sysdb_search_entry_send(state, state->ev, state->handle, + basedn, LDB_SCOPE_SUBTREE, + filter, state->attrs); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_search_users_done, req); +} + +static void sysdb_search_users_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_search_users_state *state = tevent_req_data(req, + struct sysdb_search_users_state); + int ret; + + ret = sysdb_search_entry_recv(subreq, state, + &state->msgs_count, &state->msgs); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_search_users_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + size_t *msgs_count, struct ldb_message ***msgs) +{ + struct sysdb_search_users_state *state = tevent_req_data(req, + struct sysdb_search_users_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *msgs_count = state->msgs_count; + *msgs = talloc_move(mem_ctx, &state->msgs); + + return EOK; +} + +/* =Delete-User-by-Name-OR-uid============================================ */ + +struct sysdb_delete_user_state { + struct tevent_context *ev; + struct sss_domain_info *domain; + + const char *name; + uid_t uid; + + struct sysdb_handle *handle; +}; + +void sysdb_delete_user_check_handle(struct tevent_req *subreq); +static void sysdb_delete_user_found(struct tevent_req *subreq); +static void sysdb_delete_user_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_delete_user_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, uid_t uid) +{ + struct tevent_req *req, *subreq; + struct sysdb_delete_user_state *state; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_delete_user_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->domain = domain; + state->name = name; + state->uid = uid; + + subreq = sysdb_check_handle_send(state, ev, sysdb, handle); + if (!subreq) { + DEBUG(1, ("sysdb_check_handle_send failed.\n")); + tevent_req_error(req, ENOMEM); + tevent_req_post(req, ev); + return req; + } + tevent_req_set_callback(subreq, sysdb_delete_user_check_handle, req); + + return req; +} + +void sysdb_delete_user_check_handle(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_delete_user_state *state = tevent_req_data(req, + struct sysdb_delete_user_state); + static const char *attrs[] = { SYSDB_NAME, SYSDB_UIDNUM, NULL }; + int ret; + + ret = sysdb_check_handle_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + if (state->name) { + subreq = sysdb_search_user_by_name_send(state, state->ev, NULL, + state->handle, state->domain, + state->name, attrs); + } else { + subreq = sysdb_search_user_by_uid_send(state, state->ev, NULL, + state->handle, state->domain, + state->uid, NULL); + } + + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_delete_user_found, req); +} + +static void sysdb_delete_user_found(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_delete_user_state *state = tevent_req_data(req, + struct sysdb_delete_user_state); + struct ldb_message *msg; + int ret; + + ret = sysdb_search_user_recv(subreq, state, &msg); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + if (state->name && state->uid) { + /* verify name/gid match */ + const char *name; + uint64_t uid; + + name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL); + uid = ldb_msg_find_attr_as_uint64(msg, SYSDB_UIDNUM, 0); + if (name == NULL || uid == 0) { + DEBUG(2, ("Attribute is missing but this should never happen!\n")); + tevent_req_error(req, EFAULT); + return; + } + if (strcmp(state->name, name) || state->uid != uid) { + /* this is not the entry we are looking for */ + tevent_req_error(req, EINVAL); + return; + } + } + + subreq = sysdb_delete_entry_send(state, state->ev, + state->handle, msg->dn, false); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_delete_user_done, req); +} + +static void sysdb_delete_user_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_delete_entry_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_delete_user_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Search-Groups-with-Custom-Filter===================================== */ + +struct sysdb_search_groups_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + struct sss_domain_info *domain; + const char *sub_filter; + const char **attrs; + + struct ldb_message **msgs; + size_t msgs_count; +}; + +void sysdb_search_groups_check_handle(struct tevent_req *subreq); +static void sysdb_search_groups_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_search_groups_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *sub_filter, + const char **attrs) +{ + struct tevent_req *req, *subreq; + struct sysdb_search_groups_state *state; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_search_groups_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ev = ev; + state->handle = handle; + state->domain = domain; + state->sub_filter = sub_filter; + state->attrs = attrs; + + state->msgs_count = 0; + state->msgs = NULL; + + subreq = sysdb_check_handle_send(state, ev, sysdb, handle); + if (!subreq) { + DEBUG(1, ("sysdb_check_handle_send failed.\n")); + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sysdb_search_groups_check_handle, req); + + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +void sysdb_search_groups_check_handle(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_search_groups_state *state = tevent_req_data(req, + struct sysdb_search_groups_state); + struct ldb_dn *basedn; + char *filter; + int ret; + + ret = sysdb_check_handle_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + basedn = ldb_dn_new_fmt(state, state->handle->ctx->ldb, + SYSDB_TMPL_GROUP_BASE, state->domain->name); + if (!basedn) { + DEBUG(2, ("Failed to build base dn\n")); + tevent_req_error(req, ENOMEM); + return; + } + + filter = talloc_asprintf(state, "(&(%s)%s)", + SYSDB_GC, state->sub_filter); + if (!filter) { + DEBUG(2, ("Failed to build filter\n")); + tevent_req_error(req, ENOMEM); + return; + } + + DEBUG(6, ("Search groups with filter: %s\n", filter)); + + subreq = sysdb_search_entry_send(state, state->ev, state->handle, + basedn, LDB_SCOPE_SUBTREE, + filter, state->attrs); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_search_groups_done, req); +} + +static void sysdb_search_groups_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_search_groups_state *state = tevent_req_data(req, + struct sysdb_search_groups_state); + int ret; + + ret = sysdb_search_entry_recv(subreq, state, + &state->msgs_count, &state->msgs); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_search_groups_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + size_t *msgs_count, struct ldb_message ***msgs) +{ + struct sysdb_search_groups_state *state = tevent_req_data(req, + struct sysdb_search_groups_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *msgs_count = state->msgs_count; + *msgs = talloc_move(mem_ctx, &state->msgs); + + return EOK; +} + +/* =Delete-Group-by-Name-OR-gid=========================================== */ + +struct sysdb_delete_group_state { + struct tevent_context *ev; + struct sss_domain_info *domain; + + const char *name; + gid_t gid; + + struct sysdb_handle *handle; +}; + +void sysdb_delete_group_check_handle(struct tevent_req *subreq); +static void sysdb_delete_group_found(struct tevent_req *subreq); +static void sysdb_delete_group_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_delete_group_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, gid_t gid) +{ + struct tevent_req *req, *subreq; + struct sysdb_delete_group_state *state; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_delete_group_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->domain = domain; + state->name = name; + state->gid = gid; + + subreq = sysdb_check_handle_send(state, ev, sysdb, handle); + if (!subreq) { + DEBUG(1, ("sysdb_check_handle_send failed.\n")); + tevent_req_error(req, ENOMEM); + tevent_req_post(req, ev); + return req; + } + tevent_req_set_callback(subreq, sysdb_delete_group_check_handle, req); + + return req; +} + +void sysdb_delete_group_check_handle(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_delete_group_state *state = tevent_req_data(req, + struct sysdb_delete_group_state); + static const char *attrs[] = { SYSDB_NAME, SYSDB_GIDNUM, NULL }; + int ret; + + ret = sysdb_check_handle_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + if (state->name) { + subreq = sysdb_search_group_by_name_send(state, state->ev, NULL, + state->handle, state->domain, + state->name, attrs); + } else { + subreq = sysdb_search_group_by_gid_send(state, state->ev, NULL, + state->handle, state->domain, + state->gid, NULL); + } + + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_delete_group_found, req); +} + +static void sysdb_delete_group_found(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_delete_group_state *state = tevent_req_data(req, + struct sysdb_delete_group_state); + struct ldb_message *msg; + int ret; + + ret = sysdb_search_group_recv(subreq, state, &msg); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + if (state->name && state->gid) { + /* verify name/gid match */ + const char *name; + uint64_t gid; + + name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL); + gid = ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0); + if (name == NULL || gid == 0) { + DEBUG(2, ("Attribute is missing but this should never happen!\n")); + tevent_req_error(req, EFAULT); + return; + } + if (strcmp(state->name, name) || state->gid != gid) { + /* this is not the entry we are looking for */ + tevent_req_error(req, EINVAL); + return; + } + } + + subreq = sysdb_delete_entry_send(state, state->ev, + state->handle, msg->dn, false); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_delete_group_done, req); +} + +static void sysdb_delete_group_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_delete_entry_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_delete_group_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + +/* ========= Authentication against cached password ============ */ + +struct sysdb_cache_auth_state { + struct tevent_context *ev; + const char *name; + const uint8_t *authtok; + size_t authtok_size; + struct sss_domain_info *domain; + struct sysdb_ctx *sysdb; + struct confdb_ctx *cdb; + struct sysdb_attrs *update_attrs; + bool authentication_successful; + struct sysdb_handle *handle; + time_t expire_date; + time_t delayed_until; +}; + +errno_t check_failed_login_attempts(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb, + struct ldb_message *ldb_msg, + uint32_t *failed_login_attempts, + time_t *delayed_until) +{ + int ret; + int allowed_failed_login_attempts; + int failed_login_delay; + time_t last_failed_login; + time_t end; + + *delayed_until = -1; + *failed_login_attempts = ldb_msg_find_attr_as_uint(ldb_msg, + SYSDB_FAILED_LOGIN_ATTEMPTS, 0); + last_failed_login = (time_t) ldb_msg_find_attr_as_int64(ldb_msg, + SYSDB_LAST_FAILED_LOGIN, 0); + ret = confdb_get_int(cdb, mem_ctx, CONFDB_PAM_CONF_ENTRY, + CONFDB_PAM_FAILED_LOGIN_ATTEMPTS, + CONFDB_DEFAULT_PAM_FAILED_LOGIN_ATTEMPTS, + &allowed_failed_login_attempts); + if (ret != EOK) { + DEBUG(1, ("Failed to read the number of allowed failed login " + "attempts.\n")); + return EIO; + } + ret = confdb_get_int(cdb, mem_ctx, CONFDB_PAM_CONF_ENTRY, + CONFDB_PAM_FAILED_LOGIN_DELAY, + CONFDB_DEFAULT_PAM_FAILED_LOGIN_DELAY, + &failed_login_delay); + if (ret != EOK) { + DEBUG(1, ("Failed to read the failed login delay.\n")); + return EIO; + } + DEBUG(9, ("Failed login attempts [%d], allowed failed login attempts [%d], " + "failed login delay [%d].\n", *failed_login_attempts, + allowed_failed_login_attempts, failed_login_delay)); + + if (allowed_failed_login_attempts) { + if (*failed_login_attempts >= allowed_failed_login_attempts) { + if (failed_login_delay) { + end = last_failed_login + (failed_login_delay * 60); + if (end < time(NULL)) { + DEBUG(7, ("failed_login_delay has passed, " + "resetting failed_login_attempts.\n")); + *failed_login_attempts = 0; + } else { + DEBUG(7, ("login delayed until %lld.\n", (long long) end)); + *delayed_until = end; + return EACCES; + } + } else { + DEBUG(4, ("Too many failed logins.\n")); + return EACCES; + } + } + } + + return EOK; +} + +static void sysdb_cache_auth_get_attrs_done(struct tevent_req *subreq); +static void sysdb_cache_auth_transaction_start_done(struct tevent_req *subreq); +static void sysdb_cache_auth_attr_update_done(struct tevent_req *subreq); +static void sysdb_cache_auth_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_cache_auth_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *name, + const uint8_t *authtok, + size_t authtok_size, + struct confdb_ctx *cdb) +{ + struct tevent_req *req; + struct tevent_req *subreq; + struct sysdb_cache_auth_state *state; + + if (name == NULL || *name == '\0') { + DEBUG(1, ("Missing user name.\n")); + return NULL; + } + + if (cdb == NULL) { + DEBUG(1, ("Missing config db context.\n")); + return NULL; + } + + if (sysdb == NULL) { + DEBUG(1, ("Missing sysdb db context.\n")); + return NULL; + } + + if (!domain->cache_credentials) { + DEBUG(3, ("Cached credentials not available.\n")); + return NULL; + } + + static const char *attrs[] = {SYSDB_NAME, + SYSDB_CACHEDPWD, + SYSDB_DISABLED, + SYSDB_LAST_LOGIN, + SYSDB_LAST_ONLINE_AUTH, + "lastCachedPasswordChange", + "accountExpires", + SYSDB_FAILED_LOGIN_ATTEMPTS, + SYSDB_LAST_FAILED_LOGIN, + NULL}; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_cache_auth_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ev = ev; + state->name = name; + state->authtok = authtok; + state->authtok_size = authtok_size; + state->domain = domain; + state->sysdb = sysdb; + state->cdb = cdb; + state->update_attrs = NULL; + state->authentication_successful = false; + state->handle = NULL; + state->expire_date = -1; + state->delayed_until = -1; + + subreq = sysdb_search_user_by_name_send(state, ev, sysdb, NULL, domain, + name, attrs); + if (subreq == NULL) { + DEBUG(1, ("sysdb_search_user_by_name_send failed.\n")); + talloc_zfree(req); + return NULL; + } + tevent_req_set_callback(subreq, sysdb_cache_auth_get_attrs_done, req); + + return req; +} + +static void sysdb_cache_auth_get_attrs_done(struct tevent_req *subreq) +{ + struct ldb_message *ldb_msg; + const char *userhash; + char *comphash; + char *password = NULL; + int i; + int ret; + uint64_t lastLogin = 0; + int cred_expiration; + uint32_t failed_login_attempts = 0; + + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + + struct sysdb_cache_auth_state *state = tevent_req_data(req, + struct sysdb_cache_auth_state); + + ret = sysdb_search_user_recv(subreq, state, &ldb_msg); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(1, ("sysdb_search_user_by_name_send failed [%d][%s].\n", + ret, strerror(ret))); + tevent_req_error(req, ENOENT); + return; + } + + /* Check offline_auth_cache_timeout */ + lastLogin = ldb_msg_find_attr_as_uint64(ldb_msg, + SYSDB_LAST_ONLINE_AUTH, + 0); + + ret = confdb_get_int(state->cdb, state, CONFDB_PAM_CONF_ENTRY, + CONFDB_PAM_CRED_TIMEOUT, 0, &cred_expiration); + if (ret != EOK) { + DEBUG(1, ("Failed to read expiration time of offline credentials.\n")); + ret = EACCES; + goto done; + } + DEBUG(9, ("Offline credentials expiration is [%d] days.\n", + cred_expiration)); + + if (cred_expiration) { + state->expire_date = lastLogin + (cred_expiration * 86400); + if (state->expire_date < time(NULL)) { + DEBUG(4, ("Cached user entry is too old.\n")); + state->expire_date = 0; + ret = EACCES; + goto done; + } + } else { + state->expire_date = 0; + } + + ret = check_failed_login_attempts(state, state->cdb, ldb_msg, + &failed_login_attempts, + &state->delayed_until); + if (ret != EOK) { + goto done; + } + + /* TODO: verify user account (disabled, expired ...) */ + + password = talloc_strndup(state, (const char *) state->authtok, + state->authtok_size); + if (password == NULL) { + DEBUG(1, ("talloc_strndup failed.\n")); + ret = ENOMEM; + goto done; + } + + userhash = ldb_msg_find_attr_as_string(ldb_msg, SYSDB_CACHEDPWD, NULL); + if (userhash == NULL || *userhash == '\0') { + DEBUG(4, ("Cached credentials not available.\n")); + ret = ENOENT; + goto done; + } + + ret = s3crypt_sha512(state, password, userhash, &comphash); + if (ret) { + DEBUG(4, ("Failed to create password hash.\n")); + ret = EFAULT; + goto done; + } + + state->update_attrs = sysdb_new_attrs(state); + if (state->update_attrs == NULL) { + DEBUG(1, ("sysdb_new_attrs failed.\n")); + goto done; + } + + if (strcmp(userhash, comphash) == 0) { + /* TODO: probable good point for audit logging */ + DEBUG(4, ("Hashes do match!\n")); + state->authentication_successful = true; + + ret = sysdb_attrs_add_time_t(state->update_attrs, SYSDB_LAST_LOGIN, + time(NULL)); + if (ret != EOK) { + DEBUG(3, ("sysdb_attrs_add_time_t failed, " + "but authentication is successful.\n")); + ret = EOK; + goto done; + } + + ret = sysdb_attrs_add_uint32(state->update_attrs, + SYSDB_FAILED_LOGIN_ATTEMPTS, 0U); + if (ret != EOK) { + DEBUG(3, ("sysdb_attrs_add_uint32 failed, " + "but authentication is successful.\n")); + ret = EOK; + goto done; + } + + + } else { + DEBUG(4, ("Authentication failed.\n")); + state->authentication_successful = false; + + ret = sysdb_attrs_add_time_t(state->update_attrs, + SYSDB_LAST_FAILED_LOGIN, + time(NULL)); + if (ret != EOK) { + DEBUG(3, ("sysdb_attrs_add_time_t failed\n.")); + ret = EINVAL; + goto done; + } + + ret = sysdb_attrs_add_uint32(state->update_attrs, + SYSDB_FAILED_LOGIN_ATTEMPTS, + ++failed_login_attempts); + if (ret != EOK) { + DEBUG(3, ("sysdb_attrs_add_uint32 failed.\n")); + ret = EINVAL; + goto done; + } + } + + subreq = sysdb_transaction_send(state, state->ev, state->sysdb); + if (subreq == NULL) { + DEBUG(1, ("sysdb_transaction_send failed.\n")); + goto done; + } + tevent_req_set_callback(subreq, sysdb_cache_auth_transaction_start_done, + req); + return; + +done: + if (password) for (i = 0; password[i]; i++) password[i] = 0; + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + return; +} + +static void sysdb_cache_auth_transaction_start_done(struct tevent_req *subreq) +{ + int ret; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + + struct sysdb_cache_auth_state *state = tevent_req_data(req, + struct sysdb_cache_auth_state); + + ret = sysdb_transaction_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(1, ("sysdb_transaction_send failed [%d][%s].\n", + ret, strerror(ret))); + goto done; + } + + + subreq = sysdb_set_user_attr_send(state, state->ev, state->handle, + state->domain, state->name, + state->update_attrs, + LDB_FLAG_MOD_REPLACE); + if (subreq == NULL) { + DEBUG(1, ("sysdb_set_user_attr_send failed.\n")); + goto done; + } + tevent_req_set_callback(subreq, sysdb_cache_auth_attr_update_done, + req); + return; + +done: + if (state->authentication_successful) { + tevent_req_done(req); + } else { + tevent_req_error(req, EINVAL); + } + return; +} + +static void sysdb_cache_auth_attr_update_done(struct tevent_req *subreq) +{ + int ret; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + + struct sysdb_cache_auth_state *state = tevent_req_data(req, + struct sysdb_cache_auth_state); + + ret = sysdb_set_user_attr_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(1, ("sysdb_set_user_attr request failed [%d][%s].\n", + ret, strerror(ret))); + goto done; + } + + subreq = sysdb_transaction_commit_send(state, state->ev, state->handle); + if (subreq == NULL) { + DEBUG(1, ("sysdb_transaction_commit_send failed.\n")); + goto done; + } + tevent_req_set_callback(subreq, sysdb_cache_auth_done, req); + return; + +done: + if (state->authentication_successful) { + tevent_req_done(req); + } else { + tevent_req_error(req, EINVAL); + } + return; +} + +static void sysdb_cache_auth_done(struct tevent_req *subreq) +{ + int ret; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + + struct sysdb_cache_auth_state *state = tevent_req_data(req, + struct sysdb_cache_auth_state); + + ret = sysdb_transaction_commit_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(1, ("sysdb_transaction_commit_send failed [%d][%s].\n", + ret, strerror(ret))); + } + + if (state->authentication_successful) { + tevent_req_done(req); + } else { + tevent_req_error(req, EINVAL); + } + return; +} + +int sysdb_cache_auth_recv(struct tevent_req *req, time_t *expire_date, + time_t *delayed_until) { + struct sysdb_cache_auth_state *state = tevent_req_data(req, + struct sysdb_cache_auth_state); + *expire_date = state->expire_date; + *delayed_until = state->delayed_until; + + TEVENT_REQ_RETURN_ON_ERROR(req); + + return (state->authentication_successful ? EOK : EINVAL); +} -- cgit