From 94ec51d8b53f636d41a879ed1d0d39127168cb21 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Wed, 24 Jun 2009 16:40:56 -0400 Subject: Rework transaction code to use tevent_req This is part of a set of patches to rewrite sysdb to a hopefully better API, that will also let use use tevent_req async style calls to manipulate our cache. --- server/db/sysdb_ops.c | 3705 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 2419 insertions(+), 1286 deletions(-) (limited to 'server/db/sysdb_ops.c') diff --git a/server/db/sysdb_ops.c b/server/db/sysdb_ops.c index 9b3f28283..785ce15e0 100644 --- a/server/db/sysdb_ops.c +++ b/server/db/sysdb_ops.c @@ -24,25 +24,6 @@ #include "util/nss_sha512crypt.h" #include -struct sysdb_cb_ctx { - sysdb_callback_t fn; - void *pvt; - - bool ignore_not_found; -}; - -static int sysdb_ret_error(struct sysdb_cb_ctx *ctx, int ret, int lret) -{ - ctx->fn(ctx->pvt, ret, NULL); - return lret; -}; - -static int sysdb_ret_done(struct sysdb_cb_ctx *ctx) -{ - ctx->fn(ctx->pvt, EOK, NULL); - return LDB_SUCCESS; -}; - static int add_string(struct ldb_message *msg, int flags, const char *attr, const char *value) { @@ -51,8 +32,9 @@ static int add_string(struct ldb_message *msg, int flags, 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 ret; + return ENOMEM; } static int add_ulong(struct ldb_message *msg, int flags, @@ -63,8 +45,9 @@ static int add_ulong(struct ldb_message *msg, int flags, 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 ret; + return ENOMEM; } static uint32_t get_attr_as_uint32(struct ldb_message *msg, const char *attr) @@ -89,1798 +72,2948 @@ static uint32_t get_attr_as_uint32(struct ldb_message *msg, const char *attr) return l; } -static int sysdb_op_callback(struct ldb_request *req, struct ldb_reply *rep) +#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 sysdb_cb_ctx *cbctx; - int err; + struct tevent_req *req, *subreq; + struct sldb_request_state *state; + struct timeval tv = { 0, 0 }; - cbctx = talloc_get_type(req->context, struct sysdb_cb_ctx); + req = tevent_req_create(mem_ctx, &state, struct sldb_request_state); + if (!req) return NULL; - if (!rep) { - return sysdb_ret_error(cbctx, EIO, LDB_ERR_OPERATIONS_ERROR); - } - if (rep->error != LDB_SUCCESS) { - if (! (cbctx->ignore_not_found && - rep->error == LDB_ERR_NO_SUCH_OBJECT)) { - err = sysdb_error_to_errno(rep->error); - return sysdb_ret_error(cbctx, err, rep->error); - } - } + state->ev = ev; + state->ldbctx = ldbctx; + state->ldbreq = ldbreq; + state->ldbreply = NULL; - if (rep->type != LDB_REPLY_DONE) { - sysdb_ret_error(cbctx, EINVAL, LDB_ERR_OPERATIONS_ERROR); + 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 sysdb_ret_done(cbctx); + return req; } -int sysdb_add_group_member(struct sysdb_handle *handle, - struct ldb_dn *member_dn, - struct ldb_dn *group_dn, - sysdb_callback_t fn, void *pvt) +static void sldb_request_wakeup(struct tevent_req *subreq) { - struct sysdb_ctx *ctx; - struct sysdb_cb_ctx *cbctx; - struct ldb_request *req; - struct ldb_message *msg; - const char *dn; + 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 (!sysdb_handle_check_running(handle)) { - DEBUG(2, ("Invalid request! Not running at this time.\n")); - return EINVAL; - } - - ctx = sysdb_handle_get_ctx(handle); + if (!tevent_wakeup_recv(subreq)) return; + talloc_zfree(subreq); - cbctx = talloc_zero(handle, struct sysdb_cb_ctx); - if (!cbctx) return ENOMEM; + state->ldbreq->callback = sldb_request_callback; + state->ldbreq->context = req; - cbctx->fn = fn; - cbctx->pvt = pvt; + ret = ldb_request(state->ldbctx, state->ldbreq); + if (ret != LDB_SUCCESS) { + tevent_req_error(req, sysdb_error_to_errno(ret)); + } +} - /* Add the member_dn as a member of the group */ - msg = ldb_msg_new(cbctx); - if(msg == NULL) return ENOMEM; +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; - msg->dn = group_dn; - ret = ldb_msg_add_empty(msg, SYSDB_MEMBER, - LDB_FLAG_MOD_ADD, NULL); - if (ret != LDB_SUCCESS) return ENOMEM; + if (!ldbreply) { + ERROR_OUT(err, EIO, fail); + } - dn = ldb_dn_get_linearized(member_dn); - if (!dn) return EINVAL; + state->ldbreply = talloc_steal(state, ldbreply); - ret = ldb_msg_add_fmt(msg, SYSDB_MEMBER, "%s", dn); - if (ret != LDB_SUCCESS) return EINVAL; + if (ldbreply->error != LDB_SUCCESS) { + ERROR_OUT(err, sysdb_error_to_errno(ldbreply->error), fail); + } - ret = ldb_build_mod_req(&req, ctx->ldb, cbctx, msg, - NULL, cbctx, sysdb_op_callback, NULL); - if (ret != LDB_SUCCESS) { - DEBUG(1, ("Failed to build modify request: %s(%d)[%s]\n", - ldb_strerror(ret), ret, ldb_errstring(ctx->ldb))); - return sysdb_error_to_errno(ret); + if (ldbreply->type == LDB_REPLY_DONE) { + tevent_req_done(req); + return EOK; } - ret = ldb_request(ctx->ldb, req); - if (ret != LDB_SUCCESS) return sysdb_error_to_errno(ret); + tevent_req_notify_callback(req); + return EOK; +fail: + tevent_req_error(req, err); return EOK; } -int sysdb_remove_group_member(struct sysdb_handle *handle, - struct ldb_dn *member_dn, - struct ldb_dn *group_dn, - sysdb_callback_t fn, void *pvt) +static int sldb_request_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct ldb_reply **ldbreply) { - struct sysdb_ctx *ctx; - struct sysdb_cb_ctx *cbctx; - struct ldb_request *req; - struct ldb_message *msg; - const char *dn; - int ret; + struct sldb_request_state *state = tevent_req_data(req, + struct sldb_request_state); + enum tevent_req_state tstate; + uint64_t err = 0; - if (!sysdb_handle_check_running(handle)) { - DEBUG(2, ("Invalid request! Not running at this time.\n")); - return EINVAL; + if (state->ldbreply) { + *ldbreply = talloc_move(mem_ctx, &state->ldbreply); } - ctx = sysdb_handle_get_ctx(handle); + if (tevent_req_is_error(req, &tstate, &err)) { + if (err != 0) return err; + if (tstate == TEVENT_REQ_IN_PROGRESS) return EOK; + return EIO; + } - cbctx = talloc_zero(handle, struct sysdb_cb_ctx); - if (!cbctx) return ENOMEM; + return EOK; +} - cbctx->fn = fn; - cbctx->pvt = pvt; +/* =Standard-Sysdb-Operations-utility-functions=========================== */ - /* Add the member_dn as a member of the group */ - msg = ldb_msg_new(cbctx); - if(msg == NULL) return ENOMEM; +struct sysdb_op_state { + struct tevent_context *ev; + struct sysdb_handle *handle; - msg->dn = group_dn; - ret = ldb_msg_add_empty(msg, SYSDB_MEMBER, - LDB_FLAG_MOD_DELETE, NULL); - if (ret != LDB_SUCCESS) return ENOMEM; + bool ignore_not_found; - dn = ldb_dn_get_linearized(member_dn); - if (!dn) return EINVAL; + struct ldb_reply *ldbreply; +}; - ret = ldb_msg_add_fmt(msg, SYSDB_MEMBER, "%s", dn); - if (ret != LDB_SUCCESS) return EINVAL; +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 = ldb_build_mod_req(&req, ctx->ldb, cbctx, msg, - NULL, cbctx, sysdb_op_callback, NULL); - if (ret != LDB_SUCCESS) { - DEBUG(1, ("Failed to build modify request: %s(%d)[%s]\n", - ldb_strerror(ret), ret, ldb_errstring(ctx->ldb))); - return sysdb_error_to_errno(ret); + ret = sldb_request_recv(subreq, state, &state->ldbreply); + talloc_zfree(subreq); + if (ret) { + if (state->ignore_not_found && ret == ENOENT) { + goto done; + } + tevent_req_error(req, ret); + return; } - ret = ldb_request(ctx->ldb, req); - if (ret != LDB_SUCCESS) return sysdb_error_to_errno(ret); + if (state->ldbreply->type != LDB_REPLY_DONE) { + tevent_req_error(req, EIO); + return; + } - return EOK; +done: + tevent_req_done(req); } -int sysdb_delete_entry(struct sysdb_handle *handle, - struct ldb_dn *dn, - sysdb_callback_t fn, void *pvt) +static int sysdb_op_default_recv(struct tevent_req *req) { - struct sysdb_ctx *ctx; - struct sysdb_cb_ctx *cbctx; - struct ldb_request *req; - int ret; + enum tevent_req_state tstate; + uint64_t err; - if (!sysdb_handle_check_running(handle)) { - DEBUG(2, ("Invalid request! Not running at this time.\n")); - return EINVAL; + if (tevent_req_is_error(req, &tstate, &err)) { + return err; } - ctx = sysdb_handle_get_ctx(handle); + 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) +{ + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; + struct ldb_request *ldbreq; + int ret; - cbctx = talloc_zero(handle, struct sysdb_cb_ctx); - if (!cbctx) return ENOMEM; + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; - cbctx->fn = fn; - cbctx->pvt = pvt; - cbctx->ignore_not_found = true; + state->ev = ev; + state->handle = handle; + state->ignore_not_found = true; + state->ldbreply = NULL; - ret = ldb_build_del_req(&req, ctx->ldb, cbctx, dn, NULL, - cbctx, sysdb_op_callback, 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(ctx->ldb))); - return sysdb_error_to_errno(ret); + ldb_strerror(ret), ret, ldb_errstring(handle->ctx->ldb))); + ERROR_OUT(ret, sysdb_error_to_errno(ret), fail); } - ret = ldb_request(ctx->ldb, req); - if (ret != LDB_SUCCESS) return sysdb_error_to_errno(ret); + 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 EOK; + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; } -struct delete_ctx { - struct sysdb_handle *handle; - struct sysdb_cb_ctx *cbctx; +int sysdb_delete_entry_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} - struct ldb_result *res; -}; -static int delete_callback(struct ldb_request *req, struct ldb_reply *rep) +/* =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, + const char *filter, + const char **attrs) { - struct delete_ctx *del_ctx; - struct sysdb_cb_ctx *cbctx; - struct sysdb_ctx *ctx; - struct ldb_request *delreq; - struct ldb_result *res; - struct ldb_dn *dn; - int ret, err; + 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; - del_ctx = talloc_get_type(req->context, struct delete_ctx); - ctx = sysdb_handle_get_ctx(del_ctx->handle); - cbctx = del_ctx->cbctx; - res = del_ctx->res; + state->ev = ev; + state->handle = handle; + state->ignore_not_found = false; + state->ldbreply = NULL; + + ret = ldb_build_search_req(&ldbreq, handle->ctx->ldb, 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(handle->ctx->ldb))); + ERROR_OUT(ret, sysdb_error_to_errno(ret), fail); + } - if (!rep) { - return sysdb_ret_error(cbctx, EIO, LDB_ERR_OPERATIONS_ERROR); + subreq = sldb_request_send(state, ev, handle->ctx->ldb, ldbreq); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); } - if (rep->error != LDB_SUCCESS) { - err = sysdb_error_to_errno(rep->error); - return sysdb_ret_error(cbctx, err, rep->error); + tevent_req_set_callback(subreq, sysdb_search_entry_done, req); + + return req; + +fail: + 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; + int ret; + + ret = sldb_request_recv(subreq, state, &ldbreply); + if (ret) { + tevent_req_error(req, ret); + return; } - switch (rep->type) { + switch (ldbreply->type) { case LDB_REPLY_ENTRY: - if (res->msgs != NULL) { + if (state->ldbreply) { DEBUG(1, ("More than one reply for a base search ?! " "DB seems corrupted, aborting.")); - return sysdb_ret_error(cbctx, EFAULT, LDB_ERR_OPERATIONS_ERROR); - } - res->msgs = talloc_realloc(res, res->msgs, struct ldb_message *, 2); - if (!res->msgs) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); + tevent_req_error(req, EFAULT); + return; } - res->msgs[0] = talloc_steal(res->msgs, rep->message); - res->msgs[1] = NULL; - res->count = 1; + /* save the entry so that it can be retrieved by the caller */ + state->ldbreply = ldbreply; - break; + /* just return, wait for a LDB_REPLY_DONE entry */ + return; case LDB_REPLY_DONE: - - if (res->count == 0) { - DEBUG(7, ("Base search returned no results\n")); - return sysdb_ret_done(cbctx); + if (!state->ldbreply) { + talloc_zfree(ldbreply); + tevent_req_error(req, ENOENT); + return; } + talloc_zfree(ldbreply); + return tevent_req_done(req); - dn = ldb_dn_copy(del_ctx, res->msgs[0]->dn); - if (!dn) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); - } - - talloc_free(res); - del_ctx->res = res = NULL; + default: + /* unexpected stuff */ + talloc_zfree(ldbreply); + tevent_req_error(req, EIO); + return; + } +} - ret = ldb_build_del_req(&delreq, ctx->ldb, cbctx, dn, NULL, - cbctx, sysdb_op_callback, NULL); - if (ret == LDB_SUCCESS) { - ret = ldb_request(ctx->ldb, delreq); - } - if (ret != LDB_SUCCESS) { - err = sysdb_error_to_errno(ret); - return sysdb_ret_error(cbctx, err, ret); - } - break; +int sysdb_search_entry_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct ldb_message **msg) +{ + struct sysdb_op_state *state = tevent_req_data(req, + struct sysdb_op_state); + enum tevent_req_state tstate; + uint64_t err; - default: - return sysdb_ret_error(cbctx, EINVAL, LDB_ERR_OPERATIONS_ERROR); + if (tevent_req_is_error(req, &tstate, &err)) { + return err; } - talloc_free(rep); - return LDB_SUCCESS; + *msg = talloc_move(mem_ctx, &state->ldbreply->message); + + return EOK; } -int sysdb_delete_user_by_uid(struct sysdb_handle *handle, - struct sss_domain_info *domain, - uid_t uid, - sysdb_callback_t fn, void *pvt) + +/* =Search-User-by-[UID/NAME]============================================= */ + +struct sysdb_search_user_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + + struct ldb_message *msg; +}; + +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_handle *handle, + struct sss_domain_info *domain, + const char *name) { + struct tevent_req *req, *subreq; + struct sysdb_search_user_state *state; static const char *attrs[] = { SYSDB_NAME, SYSDB_UIDNUM, NULL }; - struct delete_ctx *del_ctx; - struct sysdb_ctx *ctx; struct ldb_dn *base_dn; - struct ldb_request *req; char *filter; int ret; - if (!sysdb_handle_check_running(handle)) { - DEBUG(2, ("Invalid request! Not running at this time.\n")); - return EINVAL; - } - - ctx = sysdb_handle_get_ctx(handle); - - del_ctx = talloc_zero(handle, struct delete_ctx); - if (!del_ctx) return ENOMEM; + req = tevent_req_create(mem_ctx, &state, struct sysdb_search_user_state); + if (!req) return NULL; - del_ctx->cbctx = talloc_zero(del_ctx, struct sysdb_cb_ctx); - if (!del_ctx->cbctx) return ENOMEM; + state->ev = ev; + state->handle = handle; + state->msg = NULL; - del_ctx->handle = handle; - del_ctx->cbctx->fn = fn; - del_ctx->cbctx->pvt = pvt; - del_ctx->cbctx->ignore_not_found = true; - - del_ctx->res = talloc_zero(del_ctx, struct ldb_result); - if (!del_ctx->res) return ENOMEM; - - base_dn = ldb_dn_new_fmt(del_ctx, ctx->ldb, + base_dn = ldb_dn_new_fmt(state, handle->ctx->ldb, SYSDB_TMPL_USER_BASE, domain->name); - if (!base_dn) return ENOMEM; + if (!base_dn) + ERROR_OUT(ret, ENOMEM, fail); - filter = talloc_asprintf(del_ctx, SYSDB_PWUID_FILTER, (unsigned long)uid); - if (!filter) return ENOMEM; + filter = talloc_asprintf(state, SYSDB_PWNAM_FILTER, name); + if (!filter) + ERROR_OUT(ret, ENOMEM, fail); - ret = ldb_build_search_req(&req, ctx->ldb, del_ctx, - base_dn, LDB_SCOPE_ONELEVEL, - filter, attrs, NULL, - del_ctx, delete_callback, NULL); - if (ret != LDB_SUCCESS) { - DEBUG(1, ("Failed to build search request: %s(%d)[%s]\n", - ldb_strerror(ret), ret, ldb_errstring(ctx->ldb))); - return sysdb_error_to_errno(ret); + subreq = sysdb_search_entry_send(state, ev, handle, + base_dn, filter, attrs); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); } + tevent_req_set_callback(subreq, sysdb_search_user_done, req); - ret = ldb_request(ctx->ldb, req); - if (ret != LDB_SUCCESS) return sysdb_error_to_errno(ret); + return req; - return EOK; +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; } -int sysdb_delete_group_by_gid(struct sysdb_handle *handle, - struct sss_domain_info *domain, - gid_t gid, - sysdb_callback_t fn, void *pvt) +struct tevent_req *sysdb_search_user_by_uid_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + uid_t uid) { - static const char *attrs[] = { SYSDB_NAME, SYSDB_GIDNUM, NULL }; - struct delete_ctx *del_ctx; - struct sysdb_ctx *ctx; + struct tevent_req *req, *subreq; + struct sysdb_search_user_state *state; + static const char *attrs[] = { SYSDB_NAME, SYSDB_UIDNUM, NULL }; struct ldb_dn *base_dn; - struct ldb_request *req; char *filter; int ret; - if (!sysdb_handle_check_running(handle)) { - DEBUG(2, ("Invalid request! Not running at this time.\n")); - return EINVAL; - } + req = tevent_req_create(mem_ctx, &state, struct sysdb_search_user_state); + if (!req) return NULL; - ctx = sysdb_handle_get_ctx(handle); + state->ev = ev; + state->handle = handle; + state->msg = NULL; - del_ctx = talloc_zero(handle, struct delete_ctx); - if (!del_ctx) return ENOMEM; + base_dn = ldb_dn_new_fmt(state, handle->ctx->ldb, + SYSDB_TMPL_USER_BASE, domain->name); + if (!base_dn) + ERROR_OUT(ret, ENOMEM, fail); - del_ctx->cbctx = talloc_zero(del_ctx, struct sysdb_cb_ctx); - if (!del_ctx->cbctx) return ENOMEM; + filter = talloc_asprintf(state, SYSDB_PWUID_FILTER, (unsigned long)uid); + if (!filter) + ERROR_OUT(ret, ENOMEM, fail); - del_ctx->handle = handle; - del_ctx->cbctx->fn = fn; - del_ctx->cbctx->pvt = pvt; - del_ctx->cbctx->ignore_not_found = true; + subreq = sysdb_search_entry_send(state, ev, handle, + base_dn, filter, attrs); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_search_user_done, req); - del_ctx->res = talloc_zero(del_ctx, struct ldb_result); - if (!del_ctx->res) return ENOMEM; + return req; - base_dn = ldb_dn_new_fmt(del_ctx, ctx->ldb, - SYSDB_TMPL_GROUP_BASE, domain->name); - if (!base_dn) return ENOMEM; +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} - filter = talloc_asprintf(del_ctx, SYSDB_GRGID_FILTER, (unsigned long)gid); - if (!filter) return ENOMEM; +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 = ldb_build_search_req(&req, ctx->ldb, del_ctx, - base_dn, LDB_SCOPE_ONELEVEL, - filter, attrs, NULL, - del_ctx, delete_callback, NULL); - if (ret != LDB_SUCCESS) { - DEBUG(1, ("Failed to build search request: %s(%d)[%s]\n", - ldb_strerror(ret), ret, ldb_errstring(ctx->ldb))); - return sysdb_error_to_errno(ret); + ret = sysdb_search_entry_recv(subreq, state, &state->msg); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; } - ret = ldb_request(ctx->ldb, req); - if (ret != LDB_SUCCESS) return sysdb_error_to_errno(ret); - - return EOK; + tevent_req_done(req); } -int sysdb_set_user_attr(struct sysdb_handle *handle, - struct sss_domain_info *domain, - const char *name, - struct sysdb_attrs *attrs, - sysdb_callback_t fn, void *pvt) +int sysdb_search_user_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct ldb_message **msg) { - struct sysdb_ctx *ctx; - struct sysdb_cb_ctx *cbctx; - struct ldb_message *msg; - struct ldb_request *req; - int i, ret; + struct sysdb_search_user_state *state = tevent_req_data(req, + struct sysdb_search_user_state); + enum tevent_req_state tstate; + uint64_t err; - if (!sysdb_handle_check_running(handle)) { - DEBUG(2, ("Invalid request! Not running at this time.\n")); - return EINVAL; + if (tevent_req_is_error(req, &tstate, &err)) { + return err; } - if (attrs->num == 0) return EINVAL; + *msg = talloc_move(mem_ctx, &state->msg); + + return EOK; +} - ctx = sysdb_handle_get_ctx(handle); - cbctx = talloc_zero(handle, struct sysdb_cb_ctx); - if (!cbctx) return ENOMEM; +/* =Delete-User-by-UID==================================================== */ - cbctx->fn = fn; - cbctx->pvt = pvt; +static void sysdb_delete_user_by_uid_found(struct tevent_req *subreq); +static void sysdb_delete_user_by_uid_done(struct tevent_req *subreq); - msg = ldb_msg_new(cbctx); - if (!msg) return ENOMEM; +struct tevent_req *sysdb_delete_user_by_uid_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + uid_t uid) +{ + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; - msg->dn = sysdb_user_dn(ctx, msg, domain->name, name); - if (!msg->dn) return ENOMEM; + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; - msg->elements = talloc_array(msg, struct ldb_message_element, attrs->num); - if (!msg->elements) return ENOMEM; + state->ev = ev; + state->handle = handle; + state->ignore_not_found = true; + state->ldbreply = NULL; - for (i = 0; i < attrs->num; i++) { - msg->elements[i] = attrs->a[i]; - msg->elements[i].flags = LDB_FLAG_MOD_REPLACE; + subreq = sysdb_search_user_by_uid_send(state, ev, handle, domain, uid); + if (!subreq) { + talloc_zfree(req); + return NULL; } + tevent_req_set_callback(subreq, sysdb_delete_user_by_uid_found, req); - msg->num_elements = attrs->num; + return req; +} - ret = ldb_build_mod_req(&req, ctx->ldb, cbctx, msg, NULL, - cbctx, sysdb_op_callback, NULL); - if (ret == LDB_SUCCESS) { - ret = ldb_request(ctx->ldb, req); +static void sysdb_delete_user_by_uid_found(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_message *msg; + int ret; + + ret = sysdb_search_user_recv(subreq, state, &msg); + talloc_zfree(subreq); + if (ret) { + if (state->ignore_not_found && ret == ENOENT) { + return tevent_req_done(req); + } + tevent_req_error(req, ret); + return; } - if (ret != LDB_SUCCESS) { - return sysdb_error_to_errno(ret); + + subreq = sysdb_delete_entry_send(state, state->ev, state->handle, msg->dn); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; } + tevent_req_set_callback(subreq, sysdb_delete_user_by_uid_done, req); +} - return EOK; +static void sysdb_delete_user_by_uid_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); } -struct next_id { - uint32_t id; -}; +int sysdb_delete_user_by_uid_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} -struct next_id_ctx { - struct sysdb_handle *handle; - struct sss_domain_info *domain; - struct sysdb_cb_ctx *cbctx; - struct ldb_dn *base_dn; - struct ldb_result *res; - uint32_t tmp_id; +/* =Search-Group-by-[GID/NAME]============================================ */ - enum next_step { NEXTID_SEARCH=0, NEXTID_VERIFY, NEXTID_STORE } step; +struct sysdb_search_group_state { + struct tevent_context *ev; + struct sysdb_handle *handle; - struct next_id *result; + struct ldb_message *msg; }; -static int nextid_callback(struct ldb_request *req, struct ldb_reply *rep); +static void sysdb_search_group_done(struct tevent_req *subreq); -static int sysdb_get_next_available_id(struct sysdb_handle *handle, - struct sss_domain_info *domain, - struct next_id *result, - sysdb_callback_t fn, void *pvt) +struct tevent_req *sysdb_search_group_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name) { - static const char *attrs[] = { SYSDB_NEXTID, NULL }; - struct sysdb_ctx *ctx; - struct next_id_ctx *idctx; - struct ldb_request *req; + struct tevent_req *req, *subreq; + struct sysdb_search_group_state *state; + static const char *attrs[] = { SYSDB_NAME, SYSDB_GIDNUM, NULL }; + struct ldb_dn *base_dn; + char *filter; int ret; - if (!sysdb_handle_check_running(handle)) { - DEBUG(2, ("Invalid request! Not running at this time.\n")); - return EINVAL; - } - - ctx = sysdb_handle_get_ctx(handle); - - idctx = talloc_zero(handle, struct next_id_ctx); - if (!idctx) return ENOMEM; + req = tevent_req_create(mem_ctx, &state, struct sysdb_search_group_state); + if (!req) return NULL; - idctx->handle = handle; - idctx->domain = domain; - idctx->result = result; + state->ev = ev; + state->handle = handle; + state->msg = NULL; - idctx->cbctx = talloc_zero(handle, struct sysdb_cb_ctx); - if (!idctx->cbctx) return ENOMEM; - - idctx->cbctx->fn = fn; - idctx->cbctx->pvt = pvt; - - idctx->base_dn = sysdb_domain_dn(ctx, idctx, domain->name); - if (!idctx->base_dn) return ENOMEM; + base_dn = ldb_dn_new_fmt(state, handle->ctx->ldb, + SYSDB_TMPL_GROUP_BASE, domain->name); + if (!base_dn) + ERROR_OUT(ret, ENOMEM, fail); - idctx->res = talloc_zero(idctx, struct ldb_result); - if (!idctx->res) return ENOMEM; + filter = talloc_asprintf(state, SYSDB_GRNAM_FILTER, name); + if (!filter) + ERROR_OUT(ret, ENOMEM, fail); - ret = ldb_build_search_req(&req, ctx->ldb, idctx, - idctx->base_dn, LDB_SCOPE_BASE, - SYSDB_NEXTID_FILTER, attrs, NULL, - idctx, nextid_callback, NULL); - if (ret != LDB_SUCCESS) { - DEBUG(1, ("Failed to build search request: %s(%d)[%s]\n", - ldb_strerror(ret), ret, ldb_errstring(ctx->ldb))); - return sysdb_error_to_errno(ret); + subreq = sysdb_search_entry_send(state, ev, handle, + base_dn, filter, attrs); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); } + tevent_req_set_callback(subreq, sysdb_search_group_done, req); - ret = ldb_request(ctx->ldb, req); - if (ret != LDB_SUCCESS) return sysdb_error_to_errno(ret); + return req; - return EOK; +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; } -static int nextid_callback(struct ldb_request *req, struct ldb_reply *rep) +struct tevent_req *sysdb_search_group_by_gid_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + gid_t gid) { - static const char *attrs[] = { SYSDB_UIDNUM, SYSDB_GIDNUM, NULL }; - struct next_id_ctx *idctx; - struct sysdb_cb_ctx *cbctx; - struct sysdb_ctx *ctx; - struct ldb_request *nreq; - struct ldb_message *msg; - struct ldb_result *res; + struct tevent_req *req, *subreq; + struct sysdb_search_group_state *state; + static const char *attrs[] = { SYSDB_NAME, SYSDB_GIDNUM, NULL }; + struct ldb_dn *base_dn; char *filter; - int ret, err; + int ret; - idctx = talloc_get_type(req->context, struct next_id_ctx); - ctx = sysdb_handle_get_ctx(idctx->handle); - cbctx = idctx->cbctx; - res = idctx->res; + req = tevent_req_create(mem_ctx, &state, struct sysdb_search_group_state); + if (!req) return NULL; - if (!rep) { - return sysdb_ret_error(cbctx, EIO, LDB_ERR_OPERATIONS_ERROR); - } - if (rep->error != LDB_SUCCESS) { - err = sysdb_error_to_errno(rep->error); - return sysdb_ret_error(cbctx, err, rep->error); - } + state->ev = ev; + state->handle = handle; + state->msg = NULL; - switch (rep->type) { - case LDB_REPLY_ENTRY: + base_dn = ldb_dn_new_fmt(state, handle->ctx->ldb, + SYSDB_TMPL_GROUP_BASE, domain->name); + if (!base_dn) + ERROR_OUT(ret, ENOMEM, fail); - if (idctx->step == NEXTID_VERIFY) { - res->count++; - break; - } + filter = talloc_asprintf(state, SYSDB_GRGID_FILTER, (unsigned long)gid); + if (!filter) + ERROR_OUT(ret, ENOMEM, fail); - /* NEXTID_SEARCH */ - if (res->msgs != NULL) { - DEBUG(1, ("More than one reply for a base search ?! " - "DB seems corrupted, aborting.")); - return sysdb_ret_error(cbctx, EFAULT, LDB_ERR_OPERATIONS_ERROR); - } + subreq = sysdb_search_entry_send(state, ev, handle, + base_dn, filter, attrs); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_search_group_done, req); - res->msgs = talloc_realloc(res, res->msgs, struct ldb_message *, 2); - if (!res->msgs) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); - } + return req; - res->msgs[0] = talloc_steal(res->msgs, rep->message); - res->msgs[1] = NULL; - res->count = 1; +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} - break; +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; - case LDB_REPLY_DONE: + ret = sysdb_search_entry_recv(subreq, state, &state->msg); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } - switch (idctx->step) { - case NEXTID_SEARCH: - if (res->count != 0) { - idctx->tmp_id = get_attr_as_uint32(res->msgs[0], SYSDB_NEXTID); - if (idctx->tmp_id == (uint32_t)(-1)) { - DEBUG(1, ("Invalid Next ID in domain %s\n", - idctx->domain->name)); - return sysdb_ret_error(cbctx, ERANGE, LDB_ERR_OPERATIONS_ERROR); - } - } else { - DEBUG(4, ("Base search returned no results, adding min id!\n")); - } + tevent_req_done(req); +} - if (idctx->tmp_id < idctx->domain->id_min) { - DEBUG(2, ("Initializing domain next id to id min %u\n", - idctx->domain->id_min)); - idctx->tmp_id = idctx->domain->id_min; - } - if ((idctx->domain->id_max != 0) && - (idctx->tmp_id > idctx->domain->id_max)) { - DEBUG(0, ("Failed to allocate new id, out of range (%u/%u)\n", - idctx->tmp_id, idctx->domain->id_max)); - return sysdb_ret_error(cbctx, ERANGE, LDB_ERR_OPERATIONS_ERROR); - } +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); + enum tevent_req_state tstate; + uint64_t err; - talloc_free(res->msgs); - res->msgs = NULL; - res->count = 0; + if (tevent_req_is_error(req, &tstate, &err)) { + return err; + } - idctx->step = NEXTID_VERIFY; - break; + *msg = talloc_move(mem_ctx, &state->msg); - case NEXTID_VERIFY: - if (res->count) { - /* actually something's using the id, try next */ - idctx->tmp_id++; - } else { - /* ok store new next_id */ - idctx->result->id = idctx->tmp_id; - idctx->tmp_id++; - idctx->step = NEXTID_STORE; - } - break; + return EOK; +} - default: - DEBUG(1, ("Invalid step, aborting.\n")); - return sysdb_ret_error(cbctx, EFAULT, LDB_ERR_OPERATIONS_ERROR); - } - switch (idctx->step) { - case NEXTID_VERIFY: - filter = talloc_asprintf(idctx, "(|(%s=%u)(%s=%u))", - SYSDB_UIDNUM, idctx->tmp_id, - SYSDB_GIDNUM, idctx->tmp_id); - if (!filter) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); - } - ret = ldb_build_search_req(&nreq, ctx->ldb, idctx, - idctx->base_dn, LDB_SCOPE_SUBTREE, - filter, attrs, NULL, - idctx, nextid_callback, NULL); - break; - - case NEXTID_STORE: - msg = ldb_msg_new(idctx); - if (!msg) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); - } +/* =Delete-Group-by-GID=================================================== */ - msg->dn = idctx->base_dn; +static void sysdb_delete_group_by_gid_found(struct tevent_req *subreq); +static void sysdb_delete_group_by_gid_done(struct tevent_req *subreq); - ret = add_ulong(msg, LDB_FLAG_MOD_REPLACE, - SYSDB_NEXTID, idctx->tmp_id); - if (ret != LDB_SUCCESS) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); - } +struct tevent_req *sysdb_delete_group_by_gid_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + gid_t gid) +{ + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; - ret = ldb_build_mod_req(&nreq, ctx->ldb, idctx, msg, NULL, - cbctx, sysdb_op_callback, NULL); - break; + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; - default: - DEBUG(1, ("Invalid step, aborting.\n")); - return sysdb_ret_error(cbctx, EFAULT, LDB_ERR_OPERATIONS_ERROR); - } + state->ev = ev; + state->handle = handle; + state->ignore_not_found = true; + state->ldbreply = NULL; - if (ret != LDB_SUCCESS) { - DEBUG(1, ("Failed to build search request: %s(%d)[%s]\n", - ldb_strerror(ret), ret, ldb_errstring(ctx->ldb))); - err = sysdb_error_to_errno(ret); - return sysdb_ret_error(cbctx, err, ret); - } + subreq = sysdb_search_group_by_gid_send(state, ev, handle, domain, gid); + if (!subreq) { + talloc_zfree(req); + return NULL; + } + tevent_req_set_callback(subreq, sysdb_delete_group_by_gid_found, req); - ret = ldb_request(ctx->ldb, nreq); - if (ret != LDB_SUCCESS) { - err = sysdb_error_to_errno(ret); - return sysdb_ret_error(cbctx, err, ret); - } + return req; +} - break; +static void sysdb_delete_group_by_gid_found(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_message *msg; + int ret; - default: - return sysdb_ret_error(cbctx, EINVAL, LDB_ERR_OPERATIONS_ERROR); + ret = sysdb_search_group_recv(subreq, state, &msg); + talloc_zfree(subreq); + if (ret) { + if (state->ignore_not_found && ret == ENOENT) { + return tevent_req_done(req); + } + tevent_req_error(req, ret); + return; } - talloc_free(rep); - return LDB_SUCCESS; + subreq = sysdb_delete_entry_send(state, state->ev, state->handle, msg->dn); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_delete_group_by_gid_done, req); } -static int check_name_callback(struct ldb_request *req, struct ldb_reply *rep); - -int sysdb_check_name_unique(struct sysdb_handle *handle, - struct sss_domain_info *domain, - TALLOC_CTX *mem_ctx, const char *name, - sysdb_callback_t fn, void *pvt) +static void sysdb_delete_group_by_gid_done(struct tevent_req *subreq) { - static const char *attrs[] = { SYSDB_NAME, NULL }; - struct sysdb_cb_ctx *cbctx; - struct sysdb_ctx *ctx; - struct ldb_dn *base_dn; - struct ldb_request *req; - char *filter; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); int ret; - if (!sysdb_handle_check_running(handle)) { - DEBUG(2, ("Invalid request! Not running at this time.\n")); - return EINVAL; + ret = sysdb_delete_entry_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; } - ctx = sysdb_handle_get_ctx(handle); - - cbctx = talloc_zero(mem_ctx, struct sysdb_cb_ctx); - if (!cbctx) return ENOMEM; + tevent_req_done(req); +} - cbctx->fn = fn; - cbctx->pvt = pvt; +int sysdb_delete_group_by_gid_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} - base_dn = sysdb_domain_dn(ctx, cbctx, domain->name); - if (!base_dn) return ENOMEM; - filter = talloc_asprintf(cbctx, SYSDB_CHECK_FILTER, name); - if (!filter) return ENOMEM; +/* =Replace-Attributes-On-Entry=========================================== */ - ret = ldb_build_search_req(&req, ctx->ldb, mem_ctx, - base_dn, LDB_SCOPE_SUBTREE, - filter, attrs, NULL, - cbctx, check_name_callback, NULL); +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 search request: %s(%d)[%s]\n", - ldb_strerror(ret), ret, ldb_errstring(ctx->ldb))); - return sysdb_error_to_errno(ret); + 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); } - ret = ldb_request(ctx->ldb, req); - if (ret != LDB_SUCCESS) return sysdb_error_to_errno(ret); + 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 EOK; + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; } -static int check_name_callback(struct ldb_request *req, struct ldb_reply *rep) +int sysdb_set_entry_attr_recv(struct tevent_req *req) { - struct sysdb_cb_ctx *cbctx; - int err; + 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: + 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) { + 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: + 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) { + 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); + } - cbctx = talloc_get_type(req->context, struct sysdb_cb_ctx); + 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); + } - if (!rep) { - return sysdb_ret_error(cbctx, EIO, LDB_ERR_OPERATIONS_ERROR); + subreq = sldb_request_send(state, ev, handle->ctx->ldb, ldbreq); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); } - if (rep->error != LDB_SUCCESS) { - err = sysdb_error_to_errno(rep->error); - return sysdb_ret_error(cbctx, err, rep->error); + tevent_req_set_callback(subreq, sysdb_get_new_id_base, req); + + return req; + +fail: + 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); + tevent_req_error(req, ret); + return; } - switch (rep->type) { + 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; + } - /* one found, that means name is not available */ - /* return EEXIST */ - return sysdb_ret_error(cbctx, EEXIST, LDB_ERR_ENTRY_ALREADY_EXISTS); + state->base = talloc_move(state, &ldbreply->message); + if (!state->base) { + 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 */ + 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 */ + 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); + if (!filter) { + tevent_req_error(req, ENOMEM); + return; + } - return sysdb_ret_done(cbctx); + 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) { + 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); + 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) { + tevent_req_error(req, ENOMEM); + return; + } + + state->v_msgs[state->v_count] = talloc_move(state, &ldbreply->message); + if (!state->v_msgs[state->v_count]) { + 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: - return sysdb_ret_error(cbctx, EINVAL, LDB_ERR_OPERATIONS_ERROR); + /* unexpected stuff */ + 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) { + 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) { + 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) { + 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) { + tevent_req_error(req, ret); + return; + } + + if (ldbreply->type != LDB_REPLY_DONE) { + 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); + enum tevent_req_state tstate; + uint64_t err; + + if (tevent_req_is_error(req, &tstate, &err)) { + return err; + } + + *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; } - return LDB_SUCCESS; + 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: + 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); +} -struct user_add_ctx { + +/* =Add-User-Function===================================================== */ + +struct sysdb_add_user_state { + struct tevent_context *ev; struct sysdb_handle *handle; - struct sysdb_cb_ctx *cbctx; struct sss_domain_info *domain; const char *name; uid_t uid; gid_t gid; - const char *fullname; + const char *gecos; const char *homedir; const char *shell; - - struct next_id id; + struct sysdb_attrs *attrs; }; -static void user_check_callback(void *pvt, int error, struct ldb_result *res); -static int user_add_id(struct user_add_ctx *user_ctx); -static void user_add_id_callback(void *pvt, int error, struct ldb_result *res); -static int user_add_call(struct user_add_ctx *user_ctx); +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_done(struct tevent_req *subreq); -int sysdb_add_user(struct sysdb_handle *handle, - struct sss_domain_info *domain, - const char *name, - uid_t uid, gid_t gid, const char *fullname, - const char *homedir, const char *shell, - sysdb_callback_t fn, void *pvt) +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) { - struct user_add_ctx *user_ctx; + struct tevent_req *req, *subreq; + struct sysdb_add_user_state *state; + int ret; - if (!sysdb_handle_check_running(handle)) { - DEBUG(2, ("Invalid request! Not running at this time.\n")); - return EINVAL; + 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; + + if (domain->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)); - return EINVAL; + ERROR_OUT(ret, EINVAL, 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)); - return EINVAL; + ERROR_OUT(ret, EINVAL, fail); } + if (domain->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, handle, + domain, name); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_add_user_group_check, req); + return req; + } - user_ctx = talloc(handle, struct user_add_ctx); - if (!user_ctx) return ENOMEM; + /* check no other user with the same uid exist */ + if (state->uid != 0) { + subreq = sysdb_search_user_by_uid_send(state, ev, handle, domain, uid); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_add_user_uid_check, req); + return req; + } - user_ctx->cbctx = talloc_zero(user_ctx, struct sysdb_cb_ctx); - if (!user_ctx->cbctx) return ENOMEM; + /* 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; - user_ctx->handle = handle; - user_ctx->domain = domain; - user_ctx->cbctx->fn = fn; - user_ctx->cbctx->pvt = pvt; - user_ctx->name = name; - user_ctx->uid = uid; - user_ctx->gid = gid; - user_ctx->fullname = fullname; - user_ctx->homedir = homedir; - user_ctx->shell = shell; +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} - if (domain->mpg) { - /* if the domain is mpg we need to check we do not have there are no - * name conflicts */ +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; - return sysdb_check_name_unique(handle, domain, user_ctx, name, - user_check_callback, user_ctx); + /* 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, state->handle, + state->domain, state->uid); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_user_uid_check, req); + return; } - return user_add_id(user_ctx); + /* 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) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_user_basic_done, req); } -static void user_check_callback(void *pvt, int error, struct ldb_result *res) +static void sysdb_add_user_uid_check(struct tevent_req *subreq) { - struct user_add_ctx *user_ctx; + 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; - user_ctx = talloc_get_type(pvt, struct user_add_ctx); - if (error != EOK) { - sysdb_ret_error(user_ctx->cbctx, error, LDB_ERR_OPERATIONS_ERROR); + /* 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; } - ret = user_add_id(user_ctx); - if (ret != EOK) { - sysdb_ret_error(user_ctx->cbctx, ret, LDB_ERR_OPERATIONS_ERROR); + /* 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) { + tevent_req_error(req, ENOMEM); + return; } + tevent_req_set_callback(subreq, sysdb_add_user_basic_done, req); } -static int user_add_id(struct user_add_ctx *user_ctx) +static void sysdb_add_user_basic_done(struct tevent_req *subreq) { - if (user_ctx->uid != 0 && user_ctx->gid == 0) { - if(user_ctx->domain->mpg) { - user_ctx->gid = user_ctx->uid; + 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) { + 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) { + tevent_req_error(req, ENOMEM); + return; } + tevent_req_set_callback(subreq, sysdb_add_user_get_id_done, req); + return; } - if (user_ctx->uid == 0 || user_ctx->gid == 0) { - /* Must generate uid/gid pair */ - return sysdb_get_next_available_id(user_ctx->handle, - user_ctx->domain, - &(user_ctx->id), - user_add_id_callback, user_ctx); + if (state->attrs) { + subreq = sysdb_set_user_attr_send(state, state->ev, state->handle, + state->domain, state->name, + state->attrs, SYSDB_MOD_ADD); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_user_set_attrs_done, req); + return; } - return user_add_call(user_ctx); + tevent_req_done(req); } -static void user_add_id_callback(void *pvt, int error, struct ldb_result *res) +static void sysdb_add_user_get_id_done(struct tevent_req *subreq) { - struct user_add_ctx *user_ctx; + 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; - user_ctx = talloc_get_type(pvt, struct user_add_ctx); - if (error != EOK) { - sysdb_ret_error(user_ctx->cbctx, error, LDB_ERR_OPERATIONS_ERROR); + ret = sysdb_get_new_id_recv(subreq, &id); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); return; } - /* ok id has been allocated, fill in uid and gid fields if not - * already set */ - if (user_ctx->uid == 0) { - user_ctx->uid = user_ctx->id.id; + if (state->uid == 0) { + id_attrs = sysdb_new_attrs(state); + if (!id_attrs) { + tevent_req_error(req, ENOMEM); + return; + } + ret = sysdb_attrs_add_uint32(id_attrs, SYSDB_UIDNUM, id); + if (ret) { + tevent_req_error(req, ret); + return; + } + if (state->domain->mpg) { + ret = sysdb_attrs_add_uint32(id_attrs, SYSDB_GIDNUM, id); + if (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) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_user_set_id_done, req); + return; + } + + if (state->attrs) { + subreq = sysdb_set_user_attr_send(state, state->ev, + state->handle, state->domain, + state->name, state->attrs, + SYSDB_MOD_REP); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_user_set_attrs_done, req); + return; } - if (user_ctx->gid == 0) { - user_ctx->gid = user_ctx->id.id; + + tevent_req_done(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; } - ret = user_add_call(user_ctx); - if (ret != EOK) { - sysdb_ret_error(user_ctx->cbctx, ret, LDB_ERR_OPERATIONS_ERROR); + if (state->attrs) { + subreq = sysdb_set_user_attr_send(state, state->ev, + state->handle, state->domain, + state->name, state->attrs, + SYSDB_MOD_REP); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_user_set_attrs_done, req); + return; } + + tevent_req_done(req); } -static int user_add_call(struct user_add_ctx *user_ctx) +static void sysdb_add_user_set_attrs_done(struct tevent_req *subreq) { - struct sysdb_ctx *ctx; - struct ldb_message *msg; - struct ldb_request *req; - int flags = LDB_FLAG_MOD_ADD; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); int ret; - ctx = sysdb_handle_get_ctx(user_ctx->handle); + ret = sysdb_set_user_attr_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} - msg = ldb_msg_new(user_ctx); - if (!msg) return ENOMEM; +int sysdb_add_user_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} - msg->dn = sysdb_user_dn(ctx, msg, user_ctx->domain->name, user_ctx->name); - if (!msg->dn) return ENOMEM; - ret = add_string(msg, flags, "objectClass", SYSDB_USER_CLASS); - if (ret != LDB_SUCCESS) return ENOMEM; +/* =Add-Basic-Group-NO-CHECKS============================================= */ - ret = add_string(msg, flags, SYSDB_NAME, user_ctx->name); - if (ret != LDB_SUCCESS) return ENOMEM; +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; - if (user_ctx->uid) { - ret = add_ulong(msg, flags, SYSDB_UIDNUM, - (unsigned long)(user_ctx->uid)); - if (ret != LDB_SUCCESS) return ENOMEM; - } else { - DEBUG(0, ("Cached users can't have UID == 0\n")); - return EINVAL; - } + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; - if (user_ctx->gid) { - ret = add_ulong(msg, flags, SYSDB_GIDNUM, - (unsigned long)(user_ctx->gid)); - if (ret != LDB_SUCCESS) return ENOMEM; - } else { - DEBUG(0, ("Cached users can't have GID == 0\n")); - return EINVAL; - } + state->ev = ev; + state->handle = handle; + state->ignore_not_found = false; + state->ldbreply = NULL; - /* 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 (user_ctx->fullname && *user_ctx->fullname) { - ret = add_string(msg, flags, SYSDB_FULLNAME, user_ctx->fullname); - if (ret != LDB_SUCCESS) return ENOMEM; - ret = add_string(msg, flags, SYSDB_GECOS, user_ctx->fullname); - if (ret != LDB_SUCCESS) return ENOMEM; + msg = ldb_msg_new(state); + if (!msg) { + ERROR_OUT(ret, ENOMEM, fail); } - if (user_ctx->homedir && *user_ctx->homedir) { - ret = add_string(msg, flags, SYSDB_HOMEDIR, user_ctx->homedir); - if (ret != LDB_SUCCESS) return ENOMEM; + /* user dn */ + msg->dn = sysdb_group_dn(handle->ctx, msg, domain->name, name); + if (!msg->dn) { + ERROR_OUT(ret, ENOMEM, fail); } - if (user_ctx->shell && *user_ctx->shell) { - ret = add_string(msg, flags, SYSDB_SHELL, user_ctx->shell); - if (ret != LDB_SUCCESS) return ENOMEM; - } + 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, flags, SYSDB_CREATE_TIME, (unsigned long)time(NULL)); - if (ret != LDB_SUCCESS) return ENOMEM; + ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_CREATE_TIME, + (unsigned long)time(NULL)); + if (ret) goto fail; - ret = ldb_build_add_req(&req, ctx->ldb, user_ctx, msg, NULL, - user_ctx->cbctx, sysdb_op_callback, NULL); - if (ret == LDB_SUCCESS) { - ret = ldb_request(ctx->ldb, req); - } + + ret = ldb_build_add_req(&ldbreq, handle->ctx->ldb, state, msg, + NULL, NULL, NULL, NULL); if (ret != LDB_SUCCESS) { - return sysdb_error_to_errno(ret); + 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); } - return EOK; + 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: + 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); } -struct group_add_ctx { + +/* =Add-Group-Function==================================================== */ + +struct sysdb_add_group_state { + struct tevent_context *ev; struct sysdb_handle *handle; - struct sysdb_cb_ctx *cbctx; struct sss_domain_info *domain; const char *name; gid_t gid; - - struct next_id id; + struct sysdb_attrs *attrs; }; -static void group_check_callback(void *pvt, int error, struct ldb_result *res); -static int group_add_id(struct group_add_ctx *group_ctx); -static void group_add_id_callback(void *pvt, int error, struct ldb_result *res); -static int group_add_call(struct group_add_ctx *group_ctx); - -int sysdb_add_group(struct sysdb_handle *handle, - struct sss_domain_info *domain, - const char *name, gid_t gid, - sysdb_callback_t fn, void *pvt) +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_id_done(struct tevent_req *subreq); +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) { - struct group_add_ctx *group_ctx; + struct tevent_req *req, *subreq; + struct sysdb_add_group_state *state; + int ret; - if (!sysdb_handle_check_running(handle)) { - DEBUG(2, ("Invalid request! Not running at this time.\n")); - return EINVAL; - } + 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; 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)); - return EINVAL; + ERROR_OUT(ret, EINVAL, fail); } - group_ctx = talloc(handle, struct group_add_ctx); - if (!group_ctx) return ENOMEM; + if (domain->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, handle, + domain, name); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_add_group_user_check, req); + return req; + } - group_ctx->cbctx = talloc_zero(group_ctx, struct sysdb_cb_ctx); - if (!group_ctx->cbctx) return ENOMEM; + /* check no other groups with the same gid exist */ + if (state->gid != 0) { + subreq = sysdb_search_group_by_gid_send(state, ev, handle, + domain, gid); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_add_group_gid_check, req); + return req; + } - group_ctx->handle = handle; - group_ctx->domain = domain; - group_ctx->cbctx->fn = fn; - group_ctx->cbctx->pvt = pvt; - group_ctx->name = name; - group_ctx->gid = gid; + /* 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; - if (domain->mpg) { - /* if the domain is mpg we need to check we do not have there are no - * name conflicts */ +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} - return sysdb_check_name_unique(handle, domain, group_ctx, name, - group_check_callback, group_ctx); +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; } - return group_add_id(group_ctx); + /* check no other group with the same gid exist */ + if (state->gid != 0) { + subreq = sysdb_search_group_by_gid_send(state, + state->ev, state->handle, + state->domain, state->gid); + if (!subreq) { + 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) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_group_basic_done, req); } -static void group_check_callback(void *pvt, int error, struct ldb_result *res) +static void sysdb_add_group_gid_check(struct tevent_req *subreq) { - struct group_add_ctx *group_ctx; + 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; - group_ctx = talloc_get_type(pvt, struct group_add_ctx); - if (error != EOK) { - sysdb_ret_error(group_ctx->cbctx, error, LDB_ERR_OPERATIONS_ERROR); + /* 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; } - ret = group_add_id(group_ctx); - if (ret != EOK) { - sysdb_ret_error(group_ctx->cbctx, ret, LDB_ERR_OPERATIONS_ERROR); + /* try to add the group */ + subreq = sysdb_add_basic_group_send(state, state->ev, + state->handle, state->domain, + state->name, state->gid); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; } + tevent_req_set_callback(subreq, sysdb_add_group_basic_done, req); } -static int group_add_id(struct group_add_ctx *group_ctx) +static void sysdb_add_group_basic_done(struct tevent_req *subreq) { - if (group_ctx->gid == 0) { - /* Must generate uid/gid pair */ - return sysdb_get_next_available_id(group_ctx->handle, - group_ctx->domain, - &(group_ctx->id), - group_add_id_callback, group_ctx); + 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) { + 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) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_group_get_id_done, req); + return; } - return group_add_call(group_ctx); + if (state->attrs) { + subreq = sysdb_set_group_attr_send(state, state->ev, + state->handle, state->domain, + state->name, state->attrs, + SYSDB_MOD_ADD); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_group_set_attrs_done, req); + return; + } + + tevent_req_done(req); } -static void group_add_id_callback(void *pvt, int error, struct ldb_result *res) +static void sysdb_add_group_get_id_done(struct tevent_req *subreq) { - struct group_add_ctx *group_ctx; + 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 sysdb_attrs *id_attrs; + uint32_t id; int ret; - group_ctx = talloc_get_type(pvt, struct group_add_ctx); - if (error != EOK) { - sysdb_ret_error(group_ctx->cbctx, error, LDB_ERR_OPERATIONS_ERROR); + ret = sysdb_get_new_id_recv(subreq, &id); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + if (state->gid == 0) { + id_attrs = sysdb_new_attrs(state); + if (!id_attrs) { + tevent_req_error(req, ENOMEM); + return; + } + ret = sysdb_attrs_add_uint32(id_attrs, SYSDB_GIDNUM, id); + if (ret) { + tevent_req_error(req, ret); + return; + } + + subreq = sysdb_set_group_attr_send(state, state->ev, state->handle, + state->domain, state->name, + id_attrs, SYSDB_MOD_REP); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_group_set_id_done, req); + return; + } + + if (state->attrs) { + subreq = sysdb_set_group_attr_send(state, state->ev, + state->handle, state->domain, + state->name, state->attrs, + SYSDB_MOD_ADD); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_group_set_attrs_done, req); + return; + } + + tevent_req_done(req); +} + +static void sysdb_add_group_set_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); + int ret; + + ret = sysdb_set_group_attr_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + if (state->attrs) { + subreq = sysdb_set_group_attr_send(state, state->ev, + state->handle, state->domain, + state->name, state->attrs, + SYSDB_MOD_ADD); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_group_set_attrs_done, req); + return; + } + + tevent_req_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) { + tevent_req_error(req, ret); return; } - /* ok id has been allocated, fill in uid and gid fields */ - group_ctx->gid = group_ctx->id.id; - - ret = group_add_call(group_ctx); - if (ret != EOK) { - sysdb_ret_error(group_ctx->cbctx, ret, LDB_ERR_OPERATIONS_ERROR); - } + tevent_req_done(req); } -static int group_add_call(struct group_add_ctx *group_ctx) +int sysdb_add_group_recv(struct tevent_req *req) { - struct sysdb_ctx *ctx; - struct ldb_message *msg; - struct ldb_request *req; - int flags = LDB_FLAG_MOD_ADD; - int ret; + return sysdb_op_default_recv(req); +} - ctx = sysdb_handle_get_ctx(group_ctx->handle); - msg = ldb_msg_new(group_ctx); - if (!msg) return ENOMEM; +/* =Add-Or-Remove-Group-Memeber=========================================== */ - msg->dn = sysdb_group_dn(ctx, msg, group_ctx->domain->name, group_ctx->name); - if (!msg->dn) return ENOMEM; +/* 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; - ret = add_string(msg, flags, "objectClass", SYSDB_GROUP_CLASS); - if (ret != LDB_SUCCESS) return ENOMEM; + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; - ret = add_string(msg, flags, SYSDB_NAME, group_ctx->name); - if (ret != LDB_SUCCESS) return ENOMEM; + state->ev = ev; + state->handle = handle; + state->ignore_not_found = false; + state->ldbreply = NULL; - if (group_ctx->gid) { - ret = add_ulong(msg, flags, SYSDB_GIDNUM, - (unsigned long)(group_ctx->gid)); - if (ret != LDB_SUCCESS) return ENOMEM; - } else { - DEBUG(0, ("Cached groups can't have GID == 0\n")); - return EINVAL; + msg = ldb_msg_new(state); + if (!msg) { + ERROR_OUT(ret, ENOMEM, fail); } - /* creation time */ - ret = add_ulong(msg, flags, SYSDB_CREATE_TIME, (unsigned long)time(NULL)); - if (ret != LDB_SUCCESS) return ENOMEM; - - ret = ldb_build_add_req(&req, ctx->ldb, group_ctx, msg, NULL, - group_ctx->cbctx, sysdb_op_callback, NULL); - if (ret == LDB_SUCCESS) { - ret = ldb_request(ctx->ldb, req); - } + msg->dn = group_dn; + ret = ldb_msg_add_empty(msg, SYSDB_MEMBER, mod_op, NULL); if (ret != LDB_SUCCESS) { - return sysdb_error_to_errno(ret); + ERROR_OUT(ret, ENOMEM, fail); } - return EOK; -} - -/* This function is not safe, but is included for completeness - * It is much better to allow SSSD to internally manage the - * group GID values. sysdb_set_group_gid() will perform no - * validation that the new GID is unused. The only check it - * will perform is whether the requested GID is in the range - * of IDs allocated for the domain. - */ -int sysdb_set_group_gid(struct sysdb_handle *handle, - struct sss_domain_info *domain, - const char *name, gid_t gid, - sysdb_callback_t fn, void *pvt) -{ - struct group_add_ctx *group_ctx; - struct sysdb_ctx *sysdb; - struct ldb_message *msg; - struct ldb_request *req; - int flags = LDB_FLAG_MOD_REPLACE; - int ret; - - if (!sysdb_handle_check_running(handle)) { - DEBUG(2, ("Invalid request! Not running at this time.\n")); - return EINVAL; + dn = ldb_dn_get_linearized(member_dn); + if (!dn) { + ERROR_OUT(ret, EINVAL, fail); } - /* Validate that the target GID is within the domain range */ - if((gid < domain->id_min) || - (domain->id_max && (gid > domain->id_max))) { - DEBUG(2, ("Invalid request. Domain ID out of range")); - return EDOM; + ret = ldb_msg_add_fmt(msg, SYSDB_MEMBER, "%s", dn); + if (ret != LDB_SUCCESS) { + ERROR_OUT(ret, EINVAL, fail); } - group_ctx = talloc(handle, struct group_add_ctx); - if (!group_ctx) return ENOMEM; - - group_ctx->cbctx = talloc_zero(group_ctx, struct sysdb_cb_ctx); - if (!group_ctx->cbctx) return ENOMEM; - - group_ctx->handle = handle; - group_ctx->domain = domain; - group_ctx->cbctx->fn = fn; - group_ctx->cbctx->pvt = pvt; - group_ctx->name = name; - group_ctx->gid = gid; + 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); + } - sysdb = sysdb_handle_get_ctx(group_ctx->handle); + 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); - msg = ldb_msg_new(group_ctx); - if (!msg) return ENOMEM; + return req; - msg->dn = sysdb_group_dn(sysdb, msg, - group_ctx->domain->name, - group_ctx->name); - if (!msg->dn) return ENOMEM; +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} - ret = add_ulong(msg, flags, SYSDB_GIDNUM, - (unsigned long)(group_ctx->gid)); +int sysdb_mod_group_member_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} - ret = ldb_build_mod_req(&req, sysdb->ldb, group_ctx, msg, NULL, - group_ctx->cbctx, sysdb_op_callback, NULL); - if (ret == LDB_SUCCESS) { - ret = ldb_request(sysdb->ldb, req); - } - if (ret != LDB_SUCCESS) { - return sysdb_error_to_errno(ret); - } - return EOK; -} +/* =Store-Users-(Native/Legacy)-(replaces-existing-data)================== */ -/* "sysdb_legacy_" functions - * the set of functions named sysdb_legacy_* are used by modules - * that only have access to strictly posix like databases where - * user and groups names are retrieved as strings, groups can't - * be nested and can't reference foreign sources */ +/* if one of the basic attributes is empty ("") as opposed to NULL, + * this will just remove it */ -struct legacy_user_ctx { +struct sysdb_store_user_state { + struct tevent_context *ev; struct sysdb_handle *handle; - struct sysdb_cb_ctx *cbctx; struct sss_domain_info *domain; - struct ldb_dn *dn; - const char *name; - const char *pwd; uid_t uid; gid_t gid; const char *gecos; const char *homedir; const char *shell; - - struct ldb_result *res; + struct sysdb_attrs *attrs; }; -static int legacy_user_callback(struct ldb_request *req, - struct ldb_reply *rep); - -int sysdb_legacy_store_user(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, - sysdb_callback_t fn, void *pvt) +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) { - static const char *attrs[] = { SYSDB_NAME, NULL }; - struct legacy_user_ctx *user_ctx; - struct sysdb_ctx *ctx; - struct ldb_request *req; + struct tevent_req *req, *subreq; + struct sysdb_store_user_state *state; int ret; - if (!sysdb_handle_check_running(handle)) { - DEBUG(2, ("Invalid request! Not running at this time.\n")); - return EINVAL; - } - - ctx = sysdb_handle_get_ctx(handle); - - user_ctx = talloc(handle, struct legacy_user_ctx); - if (!user_ctx) return ENOMEM; - - user_ctx->cbctx = talloc_zero(user_ctx, struct sysdb_cb_ctx); - if (!user_ctx->cbctx) return ENOMEM; + req = tevent_req_create(mem_ctx, &state, struct sysdb_store_user_state); + if (!req) return NULL; - user_ctx->dn = sysdb_user_dn(ctx, user_ctx, domain->name, name); - if (!user_ctx->dn) return ENOMEM; + 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 = NULL; - user_ctx->handle = handle; - user_ctx->cbctx->fn = fn; - user_ctx->cbctx->pvt = pvt; - user_ctx->domain = domain; - user_ctx->name = name; - user_ctx->pwd = pwd; - user_ctx->uid = uid; - user_ctx->gid = gid; - user_ctx->gecos = gecos; - user_ctx->homedir = homedir; - user_ctx->shell = shell; - - user_ctx->res = talloc_zero(user_ctx, struct ldb_result); - if (!user_ctx->res) return ENOMEM; + if (pwd && (domain->legacy_passwords || !*pwd)) { + ret = sysdb_attrs_add_string(state->attrs, SYSDB_PWD, pwd); + if (ret) goto fail; + } - ret = ldb_build_search_req(&req, ctx->ldb, user_ctx, - user_ctx->dn, LDB_SCOPE_BASE, - SYSDB_PWENT_FILTER, attrs, NULL, - user_ctx, legacy_user_callback, NULL); - if (ret != LDB_SUCCESS) { - DEBUG(1, ("Failed to build search request: %s(%d)[%s]\n", - ldb_strerror(ret), ret, ldb_errstring(ctx->ldb))); - return sysdb_error_to_errno(ret); + subreq = sysdb_search_user_by_name_send(state, ev, handle, domain, name); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); } + tevent_req_set_callback(subreq, sysdb_store_user_check, req); - ret = ldb_request(ctx->ldb, req); - if (ret != LDB_SUCCESS) return sysdb_error_to_errno(ret); + return req; - return EOK; +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; } -static int legacy_user_callback(struct ldb_request *req, - struct ldb_reply *rep) +static void sysdb_store_user_check(struct tevent_req *subreq) { - struct legacy_user_ctx *user_ctx; - struct sysdb_cb_ctx *cbctx; - struct sysdb_ctx *ctx; + 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; - struct ldb_request *ureq; - struct ldb_result *res; - int flags; - int ret, err; - - user_ctx = talloc_get_type(req->context, struct legacy_user_ctx); - ctx = sysdb_handle_get_ctx(user_ctx->handle); - cbctx = user_ctx->cbctx; - res = user_ctx->res; + int ret; - if (!rep) { - return sysdb_ret_error(cbctx, EIO, LDB_ERR_OPERATIONS_ERROR); - } - if (rep->error != LDB_SUCCESS) { - err = sysdb_error_to_errno(rep->error); - return sysdb_ret_error(cbctx, err, rep->error); + ret = sysdb_search_user_recv(subreq, state, &msg); + talloc_zfree(subreq); + if (ret && ret != ENOENT) { + tevent_req_error(req, ret); + return; } - switch (rep->type) { - case LDB_REPLY_ENTRY: - res->msgs = talloc_realloc(res, res->msgs, - struct ldb_message *, - res->count + 2); - if (!res->msgs) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); + 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); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; } + tevent_req_set_callback(subreq, sysdb_store_user_add_done, req); + return; + } - res->msgs[res->count + 1] = NULL; - - res->msgs[res->count] = talloc_steal(res->msgs, rep->message); - res->count++; - - break; - - case LDB_REPLY_DONE: - - msg = ldb_msg_new(cbctx); - if (!msg) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); + /* the user exists, let's just replace attributes when set */ + if (!state->attrs) { + state->attrs = sysdb_new_attrs(state); + if (!state->attrs) { + tevent_req_error(req, ENOMEM); + return; } - msg->dn = user_ctx->dn; - - switch (res->count) { - case 0: - flags = LDB_FLAG_MOD_ADD; - break; - case 1: - flags = LDB_FLAG_MOD_REPLACE; - break; - default: - DEBUG(0, ("Cache DB corrupted, base search returned %d results\n", - res->count)); - - return sysdb_ret_error(cbctx, EFAULT, LDB_ERR_OPERATIONS_ERROR); - } - - talloc_free(res); - user_ctx->res = res = NULL; - - if (flags == LDB_FLAG_MOD_ADD) { - ret = add_string(msg, flags, "objectClass", SYSDB_USER_CLASS); - if (ret != LDB_SUCCESS) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); - } + } - ret = add_string(msg, flags, SYSDB_NAME, user_ctx->name); - if (ret != LDB_SUCCESS) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); - } + if (state->uid) { + ret = sysdb_attrs_add_uint32(state->attrs, SYSDB_UIDNUM, state->uid); + if (ret) { + tevent_req_error(req, ret); + return; } + } - if (user_ctx->domain->legacy_passwords && - user_ctx->pwd && *user_ctx->pwd) { - ret = add_string(msg, flags, SYSDB_PWD, user_ctx->pwd); - } else { - ret = ldb_msg_add_empty(msg, SYSDB_PWD, - LDB_FLAG_MOD_DELETE, NULL); - } - if (ret != LDB_SUCCESS) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); + if (state->gid) { + ret = sysdb_attrs_add_uint32(state->attrs, SYSDB_GIDNUM, state->gid); + if (ret) { + tevent_req_error(req, ret); + return; } + } - if (user_ctx->uid) { - ret = add_ulong(msg, flags, SYSDB_UIDNUM, - (unsigned long)(user_ctx->uid)); - if (ret != LDB_SUCCESS) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); - } - } else { - DEBUG(0, ("Cached users can't have UID == 0\n")); - return sysdb_ret_error(cbctx, EINVAL, LDB_ERR_OPERATIONS_ERROR); + if (state->uid && !state->gid && state->domain->mpg) { + ret = sysdb_attrs_add_uint32(state->attrs, SYSDB_GIDNUM, state->uid); + if (ret) { + tevent_req_error(req, ret); + return; } + } - if (user_ctx->gid) { - ret = add_ulong(msg, flags, SYSDB_GIDNUM, - (unsigned long)(user_ctx->gid)); - if (ret != LDB_SUCCESS) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); - } - } else { - DEBUG(0, ("Cached users can't have GID == 0\n")); - return sysdb_ret_error(cbctx, EINVAL, LDB_ERR_OPERATIONS_ERROR); + if (state->gecos) { + ret = sysdb_attrs_add_string(state->attrs, SYSDB_GECOS, state->gecos); + if (ret) { + tevent_req_error(req, ret); + return; } + } - if (user_ctx->gecos && *user_ctx->gecos) { - ret = add_string(msg, flags, SYSDB_GECOS, user_ctx->gecos); - } else { - ret = ldb_msg_add_empty(msg, SYSDB_GECOS, - LDB_FLAG_MOD_DELETE, NULL); - } - if (ret != LDB_SUCCESS) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); + if (state->homedir) { + ret = sysdb_attrs_add_string(state->attrs, + SYSDB_HOMEDIR, state->homedir); + if (ret) { + tevent_req_error(req, ret); + return; } + } - if (user_ctx->homedir && *user_ctx->homedir) { - ret = add_string(msg, flags, SYSDB_HOMEDIR, user_ctx->homedir); - } else { - ret = ldb_msg_add_empty(msg, SYSDB_HOMEDIR, - LDB_FLAG_MOD_DELETE, NULL); - } - if (ret != LDB_SUCCESS) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); + if (state->shell) { + ret = sysdb_attrs_add_string(state->attrs, SYSDB_SHELL, state->shell); + if (ret) { + tevent_req_error(req, ret); + return; } + } - if (user_ctx->shell && *user_ctx->shell) { - ret = add_string(msg, flags, SYSDB_SHELL, user_ctx->shell); - } else { - ret = ldb_msg_add_empty(msg, SYSDB_SHELL, - LDB_FLAG_MOD_DELETE, NULL); - } - if (ret != LDB_SUCCESS) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); - } + ret = sysdb_attrs_add_time_t(state->attrs, SYSDB_LAST_UPDATE, time(NULL)); + if (ret) { + tevent_req_error(req, ret); + return; + } - /* modification time */ - ret = add_ulong(msg, flags, SYSDB_LAST_UPDATE, - (unsigned long)time(NULL)); - if (ret != LDB_SUCCESS) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); - } + subreq = sysdb_set_user_attr_send(state, state->ev, + state->handle, state->domain, + state->name, state->attrs, + SYSDB_MOD_REP); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_store_user_attr_done, req); +} - if (flags == LDB_FLAG_MOD_ADD) { - ret = ldb_build_add_req(&ureq, ctx->ldb, cbctx, msg, NULL, - cbctx, sysdb_op_callback, NULL); - } else { - ret = ldb_build_mod_req(&ureq, ctx->ldb, cbctx, msg, NULL, - cbctx, sysdb_op_callback, NULL); - } - if (ret == LDB_SUCCESS) { - ret = ldb_request(ctx->ldb, ureq); - } - if (ret != LDB_SUCCESS) { - err = sysdb_error_to_errno(ret); - return sysdb_ret_error(cbctx, err, ret); - } - break; +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; - default: - return sysdb_ret_error(cbctx, EINVAL, LDB_ERR_OPERATIONS_ERROR); + ret = sysdb_add_user_recv(subreq); + talloc_zfree(subreq); + if (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) { + tevent_req_error(req, ret); + return; } - talloc_free(rep); - return LDB_SUCCESS; + 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 legacy_group_ctx { +struct sysdb_store_group_state { + struct tevent_context *ev; struct sysdb_handle *handle; - struct sysdb_cb_ctx *cbctx; struct sss_domain_info *domain; - struct ldb_dn *dn; - const char *name; gid_t gid; const char **members; - struct ldb_result *res; + struct sysdb_attrs *attrs; }; -static int legacy_group_callback(struct ldb_request *req, - struct ldb_reply *rep); - -int sysdb_legacy_store_group(struct sysdb_handle *handle, - struct sss_domain_info *domain, - const char *name, gid_t gid, - const char **members, - sysdb_callback_t fn, void *pvt) +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, + const char **members) { - static const char *attrs[] = { SYSDB_NAME, NULL }; - struct legacy_group_ctx *group_ctx; - struct sysdb_ctx *ctx; - struct ldb_request *req; - int ret; + struct tevent_req *req, *subreq; + struct sysdb_store_group_state *state; + int ret, i; + + 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->members = members; + state->attrs = NULL; + + if (state->members) { + state->attrs = sysdb_new_attrs(state); + if (!state->attrs) { + ERROR_OUT(ret, ENOMEM, fail); + } + + for (i = 0; state->members[i]; i++) { + if (domain->legacy) { +/* + const char *member; + + member = talloc_asprintf(state, SYSDB_TMPL_USER, + domain->name, state->members[i]); + if (!member) { + ERROR_OUT(ret, ENOMEM, fail); + } +*/ + ret = sysdb_attrs_add_string(state->attrs, SYSDB_LEGACY_MEMBER, + state->members[i]); + if (ret) goto fail; + } else { + ret = sysdb_attrs_add_string(state->attrs, SYSDB_MEMBER, + state->members[i]); + if (ret) goto fail; + } + } + + state->members = NULL; + } - if (!sysdb_handle_check_running(handle)) { - DEBUG(2, ("Invalid request! Not running at this time.\n")); - return EINVAL; + subreq = sysdb_search_group_by_name_send(state, ev, handle, domain, name); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); } + tevent_req_set_callback(subreq, sysdb_store_group_check, req); - ctx = sysdb_handle_get_ctx(handle); + return req; - group_ctx = talloc(handle, struct legacy_group_ctx); - if (!group_ctx) return ENOMEM; +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} - group_ctx->cbctx = talloc_zero(group_ctx, struct sysdb_cb_ctx); - if (!group_ctx->cbctx) return ENOMEM; +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; + int ret; - group_ctx->dn = sysdb_group_dn(ctx, group_ctx, domain->name, name); - if (!group_ctx->dn) return ENOMEM; + ret = sysdb_search_group_recv(subreq, state, &msg); + talloc_zfree(subreq); + if (ret && ret != ENOENT) { + tevent_req_error(req, ret); + return; + } - group_ctx->handle = handle; - group_ctx->cbctx->fn = fn; - group_ctx->cbctx->pvt = pvt; - group_ctx->domain = domain; - group_ctx->name = name; - group_ctx->gid = gid; - group_ctx->members = members; + if (ret == ENOENT) { + /* groups 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); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_store_group_add_done, req); + return; + } - group_ctx->res = talloc_zero(group_ctx, struct ldb_result); - if (!group_ctx->res) return ENOMEM; + /* the group exists, let's just replace attributes when set */ + if (!state->attrs) { + state->attrs = sysdb_new_attrs(state); + if (!state->attrs) { + tevent_req_error(req, ENOMEM); + return; + } + } - ret = ldb_build_search_req(&req, ctx->ldb, group_ctx, - group_ctx->dn, LDB_SCOPE_BASE, - SYSDB_GRENT_FILTER, attrs, NULL, - group_ctx, legacy_group_callback, NULL); - if (ret != LDB_SUCCESS) { - DEBUG(1, ("Failed to build search request: %s(%d)[%s]\n", - ldb_strerror(ret), ret, ldb_errstring(ctx->ldb))); - return sysdb_error_to_errno(ret); + if (state->gid) { + ret = sysdb_attrs_add_uint32(state->attrs, SYSDB_GIDNUM, state->gid); + if (ret) { + tevent_req_error(req, ret); + return; + } } - ret = ldb_request(ctx->ldb, req); - if (ret != LDB_SUCCESS) return sysdb_error_to_errno(ret); + /* FIXME: handle non legacy groups */ - return EOK; + ret = sysdb_attrs_add_time_t(state->attrs, SYSDB_LAST_UPDATE, time(NULL)); + if (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) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_store_group_attr_done, req); } -static int legacy_group_callback(struct ldb_request *req, - struct ldb_reply *rep) +static void sysdb_store_group_add_done(struct tevent_req *subreq) { - struct legacy_group_ctx *group_ctx; - struct sysdb_cb_ctx *cbctx; - struct sysdb_ctx *ctx; - struct ldb_message *msg; - struct ldb_request *greq; - struct ldb_result *res; - int flags; - int i, ret, err; - - group_ctx = talloc_get_type(req->context, struct legacy_group_ctx); - ctx = sysdb_handle_get_ctx(group_ctx->handle); - cbctx = group_ctx->cbctx; - res = group_ctx->res; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; - if (!rep) { - return sysdb_ret_error(cbctx, EIO, LDB_ERR_OPERATIONS_ERROR); + ret = sysdb_add_group_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; } - if (rep->error != LDB_SUCCESS) { - err = sysdb_error_to_errno(rep->error); - return sysdb_ret_error(cbctx, err, rep->error); + + 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) { + tevent_req_error(req, ret); + return; } - switch (rep->type) { - case LDB_REPLY_ENTRY: - res->msgs = talloc_realloc(res, res->msgs, - struct ldb_message *, - res->count + 2); - if (!res->msgs) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); - } + tevent_req_done(req); +} - res->msgs[res->count + 1] = NULL; +int sysdb_store_group_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} - res->msgs[res->count] = talloc_steal(res->msgs, rep->message); - res->count++; - break; +/* =Add-User-to-Group(Native/Legacy)====================================== */ - case LDB_REPLY_DONE: +static void sysdb_add_group_member_done(struct tevent_req *subreq); +static void sysdb_add_group_member_l_done(struct tevent_req *subreq); - msg = ldb_msg_new(cbctx); - if (!msg) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); - } - msg->dn = group_ctx->dn; - - switch (res->count) { - case 0: - flags = LDB_FLAG_MOD_ADD; - break; - case 1: - flags = LDB_FLAG_MOD_REPLACE; - break; - default: - DEBUG(0, ("Cache DB corrupted, base search returned %d results\n", - res->count)); - - return sysdb_ret_error(cbctx, EFAULT, LDB_ERR_OPERATIONS_ERROR); - } +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; + struct sysdb_attrs *attrs; + int ret; - talloc_free(res); - group_ctx->res = res = NULL; + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; - if (flags == LDB_FLAG_MOD_ADD) { - ret = add_string(msg, flags, "objectClass", SYSDB_GROUP_CLASS); - if (ret != LDB_SUCCESS) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); - } + state->ev = ev; + state->handle = handle; + state->ignore_not_found = false; + state->ldbreply = NULL; - ret = add_string(msg, flags, SYSDB_NAME, group_ctx->name); - if (ret != LDB_SUCCESS) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); - } + if (domain->legacy) { + attrs = sysdb_new_attrs(state); + if (!attrs) { + ERROR_OUT(ret, ENOMEM, fail); } - if (group_ctx->gid) { - ret = add_ulong(msg, flags, SYSDB_GIDNUM, - (unsigned long)(group_ctx->gid)); - if (ret != LDB_SUCCESS) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); - } - } else { - DEBUG(0, ("Cached groups can't have GID == 0\n")); - return sysdb_ret_error(cbctx, EINVAL, LDB_ERR_OPERATIONS_ERROR); - } + ret = sysdb_attrs_add_string(attrs, SYSDB_LEGACY_MEMBER, user); + if (ret) goto fail; - /* members */ - if (group_ctx->members && group_ctx->members[0]) { - ret = ldb_msg_add_empty(msg, SYSDB_LEGACY_MEMBER, flags, NULL); - if (ret != LDB_SUCCESS) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); - } - for (i = 0; group_ctx->members[i]; i++) { - ret = ldb_msg_add_string(msg, SYSDB_LEGACY_MEMBER, - group_ctx->members[i]); - if (ret != LDB_SUCCESS) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); - } - } + subreq = sysdb_set_group_attr_send(state, ev, handle, + domain, group, attrs, + SYSDB_MOD_ADD); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); } + tevent_req_set_callback(subreq, sysdb_add_group_member_l_done, req); - /* modification time */ - ret = add_ulong(msg, flags, SYSDB_LAST_UPDATE, - (unsigned long)time(NULL)); - if (ret != LDB_SUCCESS) { - return sysdb_ret_error(cbctx, ENOMEM, LDB_ERR_OPERATIONS_ERROR); + } else { + group_dn = sysdb_group_dn(handle->ctx, state, domain->name, group); + if (!group_dn) { + ERROR_OUT(ret, ENOMEM, fail); } - if (flags == LDB_FLAG_MOD_ADD) { - ret = ldb_build_add_req(&greq, ctx->ldb, cbctx, msg, NULL, - cbctx, sysdb_op_callback, NULL); - } else { - ret = ldb_build_mod_req(&greq, ctx->ldb, cbctx, msg, NULL, - cbctx, sysdb_op_callback, NULL); - } - if (ret == LDB_SUCCESS) { - ret = ldb_request(ctx->ldb, greq); + user_dn = sysdb_user_dn(handle->ctx, state, domain->name, user); + if (!user_dn) { + ERROR_OUT(ret, ENOMEM, fail); } - if (ret != LDB_SUCCESS) { - err = sysdb_error_to_errno(ret); - return sysdb_ret_error(cbctx, err, ret); + + subreq = sysdb_mod_group_member_send(state, ev, handle, + user_dn, group_dn, + SYSDB_MOD_ADD); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); } - break; + tevent_req_set_callback(subreq, sysdb_add_group_member_done, req); + } - default: - return sysdb_ret_error(cbctx, EINVAL, LDB_ERR_OPERATIONS_ERROR); + return req; + +fail: + 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) { + tevent_req_error(req, ret); + return; } - talloc_free(rep); - return LDB_SUCCESS; + tevent_req_done(req); } -int sysdb_legacy_add_group_member(struct sysdb_handle *handle, - struct sss_domain_info *domain, - const char *group, - const char *member, - sysdb_callback_t fn, void *pvt) +static void sysdb_add_group_member_l_done(struct tevent_req *subreq) { - struct sysdb_ctx *ctx; - struct sysdb_cb_ctx *cbctx; - struct ldb_request *req; - struct ldb_message *msg; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); int ret; - if (!sysdb_handle_check_running(handle)) { - DEBUG(2, ("Invalid request! Not running at this time.\n")); - return EINVAL; + ret = sysdb_set_group_attr_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; } - ctx = sysdb_handle_get_ctx(handle); + tevent_req_done(req); +} + +int sysdb_add_group_member_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} - cbctx = talloc_zero(handle, struct sysdb_cb_ctx); - if (!cbctx) return ENOMEM; - cbctx->fn = fn; - cbctx->pvt = pvt; +/* =Remove-member-from-Group(Native/Legacy)=============================== */ - /* Add the member_dn as a member of the group */ - msg = ldb_msg_new(cbctx); - if(msg == NULL) return ENOMEM; +static void sysdb_remove_group_member_done(struct tevent_req *subreq); +static void sysdb_remove_group_member_l_done(struct tevent_req *subreq); - msg->dn = sysdb_group_dn(ctx, cbctx, domain->name, group); - if (!msg->dn) return ENOMEM; +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; + struct sysdb_attrs *attrs; + int ret; - ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_LEGACY_MEMBER, member); - if (ret != LDB_SUCCESS) return ENOMEM; + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; - ret = ldb_build_mod_req(&req, ctx->ldb, cbctx, msg, - NULL, cbctx, sysdb_op_callback, NULL); - if (ret != LDB_SUCCESS) { - DEBUG(1, ("Failed to build modify request: %s(%d)[%s]\n", - ldb_strerror(ret), ret, ldb_errstring(ctx->ldb))); - return sysdb_error_to_errno(ret); + state->ev = ev; + state->handle = handle; + state->ignore_not_found = false; + state->ldbreply = NULL; + + if (domain->legacy) { + attrs = sysdb_new_attrs(state); + if (!attrs) { + ERROR_OUT(ret, ENOMEM, fail); + } + + ret = sysdb_attrs_add_string(attrs, SYSDB_LEGACY_MEMBER, user); + if (ret) goto fail; + + subreq = sysdb_set_group_attr_send(state, ev, handle, + domain, group, attrs, + SYSDB_MOD_DEL); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_remove_group_member_l_done, req); + + } else { + 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); } - ret = ldb_request(ctx->ldb, req); - if (ret != LDB_SUCCESS) return sysdb_error_to_errno(ret); + return req; - return EOK; +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; } -int sysdb_legacy_remove_group_member(struct sysdb_handle *handle, - struct sss_domain_info *domain, - const char *group, - const char *member, - sysdb_callback_t fn, void *pvt) +static void sysdb_remove_group_member_done(struct tevent_req *subreq) { - struct sysdb_ctx *ctx; - struct sysdb_cb_ctx *cbctx; - struct ldb_request *req; - struct ldb_message *msg; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); int ret; - if (!sysdb_handle_check_running(handle)) { - DEBUG(2, ("Invalid request! Not running at this time.\n")); - return EINVAL; + ret = sysdb_mod_group_member_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; } - ctx = sysdb_handle_get_ctx(handle); - - cbctx = talloc_zero(handle, struct sysdb_cb_ctx); - if (!cbctx) return ENOMEM; + tevent_req_done(req); +} - cbctx->fn = fn; - cbctx->pvt = pvt; +static void sysdb_remove_group_member_l_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; - /* Add the member_dn as a member of the group */ - msg = ldb_msg_new(cbctx); - if(msg == NULL) return ENOMEM; + ret = sysdb_set_group_attr_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } - msg->dn = sysdb_group_dn(ctx, cbctx, domain->name, group); - if (!msg->dn) return ENOMEM; + tevent_req_done(req); +} - ret = add_string(msg, LDB_FLAG_MOD_DELETE, SYSDB_LEGACY_MEMBER, member); - if (ret != LDB_SUCCESS) return ENOMEM; +int sysdb_remove_group_member_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} - ret = ldb_build_mod_req(&req, ctx->ldb, cbctx, msg, - NULL, cbctx, sysdb_op_callback, NULL); - if (ret != LDB_SUCCESS) { - DEBUG(1, ("Failed to build modify request: %s(%d)[%s]\n", - ldb_strerror(ret), ret, ldb_errstring(ctx->ldb))); - return sysdb_error_to_errno(ret); - } - ret = ldb_request(ctx->ldb, req); - if (ret != LDB_SUCCESS) return sysdb_error_to_errno(ret); +/* ======================================================================= */ - return EOK; -} +static void sysdb_set_cached_password_done(struct tevent_req *subreq); -int sysdb_set_cached_password(struct sysdb_handle *handle, - struct sss_domain_info *domain, - const char *user, - const char *password, - sysdb_callback_t fn, void *pvt) +struct tevent_req *sysdb_set_cached_password_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *user, + const char *password) { - struct sysdb_ctx *ctx; + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; struct sysdb_attrs *attrs; char *hash = NULL; char *salt; int ret; - ctx = sysdb_handle_get_ctx(handle); - if (!ctx) return EFAULT; + 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; - ret = s3crypt_gen_salt(handle, &salt); + ret = s3crypt_gen_salt(state, &salt); if (ret) { DEBUG(4, ("Failed to generate random salt.\n")); - return ret; + goto fail; } - ret = s3crypt_sha512(handle, password, salt, &hash); + ret = s3crypt_sha512(state, password, salt, &hash); if (ret) { DEBUG(4, ("Failed to create password hash.\n")); - return ret; + goto fail; } - attrs = sysdb_new_attrs(handle); + attrs = sysdb_new_attrs(state); if (!attrs) { - return ENOMEM; + ERROR_OUT(ret, ENOMEM, fail); } ret = sysdb_attrs_add_string(attrs, SYSDB_CACHEDPWD, hash); - if (ret) return ret; + if (ret) goto fail; /* FIXME: should we use a different attribute for chache passwords ?? */ ret = sysdb_attrs_add_long(attrs, "lastCachedPasswordChange", (long)time(NULL)); - if (ret) return ret; + if (ret) goto fail; - ret = sysdb_set_user_attr(handle, domain, user, attrs, fn, pvt); - if (ret) return ret; + subreq = sysdb_set_user_attr_send(state, ev, handle, domain, + user, attrs, SYSDB_MOD_REP); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_set_cached_password_done, req); - return EOK; + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_set_cached_password_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) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); } + +int sysdb_set_cached_password_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + -- cgit