/* SSSD System Database Copyright (C) Simo Sorce 2008 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "util/util.h" #include "db/sysdb_private.h" #include "util/nss_sha512crypt.h" #include static int add_string(struct ldb_message *msg, int flags, const char *attr, const char *value) { int ret; ret = ldb_msg_add_empty(msg, attr, flags, NULL); if (ret == LDB_SUCCESS) { ret = ldb_msg_add_string(msg, attr, value); if (ret == LDB_SUCCESS) return EOK; } return ENOMEM; } static int add_ulong(struct ldb_message *msg, int flags, const char *attr, unsigned long value) { int ret; ret = ldb_msg_add_empty(msg, attr, flags, NULL); if (ret == LDB_SUCCESS) { ret = ldb_msg_add_fmt(msg, attr, "%lu", value); if (ret == LDB_SUCCESS) return EOK; } return ENOMEM; } static uint32_t get_attr_as_uint32(struct ldb_message *msg, const char *attr) { const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr); long long int l; if (!v || !v->data) { return 0; } errno = 0; l = strtoll((const char *)v->data, NULL, 0); if (errno) { return (uint32_t)-1; } if (l < 0 || l > ((uint32_t)(-1))) { return (uint32_t)-1; } return l; } #define ERROR_OUT(v, r, l) do { v = r; goto l; } while(0); /* =LDB-Request-(tevent_req-style)======================================== */ struct sldb_request_state { struct tevent_context *ev; struct ldb_context *ldbctx; struct ldb_request *ldbreq; struct ldb_reply *ldbreply; }; static void sldb_request_wakeup(struct tevent_req *subreq); static int sldb_request_callback(struct ldb_request *ldbreq, struct ldb_reply *ldbreply); static struct tevent_req *sldb_request_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct ldb_context *ldbctx, struct ldb_request *ldbreq) { struct tevent_req *req, *subreq; struct sldb_request_state *state; struct timeval tv = { 0, 0 }; req = tevent_req_create(mem_ctx, &state, struct sldb_request_state); if (!req) return NULL; state->ev = ev; state->ldbctx = ldbctx; state->ldbreq = ldbreq; state->ldbreply = NULL; subreq = tevent_wakeup_send(state, ev, tv); if (!subreq) { DEBUG(1, ("Failed to add critical timer to run next ldb operation!\n")); talloc_zfree(req); return NULL; } tevent_req_set_callback(subreq, sldb_request_wakeup, req); return req; } static void sldb_request_wakeup(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sldb_request_state *state = tevent_req_data(req, struct sldb_request_state); int ret; if (!tevent_wakeup_recv(subreq)) return; talloc_zfree(subreq); state->ldbreq->callback = sldb_request_callback; state->ldbreq->context = req; ret = ldb_request(state->ldbctx, state->ldbreq); if (ret != LDB_SUCCESS) { int err = sysdb_error_to_errno(ret); DEBUG(6, ("Error: %d (%s)\n", err, strerror(err))); tevent_req_error(req, err); } } static int sldb_request_callback(struct ldb_request *ldbreq, struct ldb_reply *ldbreply) { struct tevent_req *req = talloc_get_type(ldbreq->context, struct tevent_req); struct sldb_request_state *state = tevent_req_data(req, struct sldb_request_state); int err; if (!ldbreply) { DEBUG(6, ("Error: Missing ldbreply")); ERROR_OUT(err, EIO, fail); } state->ldbreply = talloc_steal(state, ldbreply); if (ldbreply->error != LDB_SUCCESS) { DEBUG(6, ("LDB Error: %d (%s)\n", ldbreply->error, ldb_errstring(state->ldbctx))); ERROR_OUT(err, sysdb_error_to_errno(ldbreply->error), fail); } if (ldbreply->type == LDB_REPLY_DONE) { tevent_req_done(req); return EOK; } tevent_req_notify_callback(req); return EOK; fail: tevent_req_error(req, err); return EOK; } static int sldb_request_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, struct ldb_reply **ldbreply) { struct sldb_request_state *state = tevent_req_data(req, struct sldb_request_state); enum tevent_req_state tstate; uint64_t err = 0; if (state->ldbreply) { *ldbreply = talloc_move(mem_ctx, &state->ldbreply); } if (tevent_req_is_error(req, &tstate, &err)) { if (err != 0) return err; if (tstate == TEVENT_REQ_IN_PROGRESS) return EOK; return EIO; } return EOK; } /* =Standard-Sysdb-Operations-utility-functions=========================== */ struct sysdb_op_state { struct tevent_context *ev; struct sysdb_handle *handle; bool ignore_not_found; struct ldb_reply *ldbreply; }; static void sysdb_op_default_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_op_state *state = tevent_req_data(req, struct sysdb_op_state); int ret; ret = sldb_request_recv(subreq, state, &state->ldbreply); talloc_zfree(subreq); if (ret) { if (state->ignore_not_found && ret == ENOENT) { goto done; } DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } if (state->ldbreply->type != LDB_REPLY_DONE) { DEBUG(6, ("Error: %d (%s)\n", EIO, strerror(EIO))); tevent_req_error(req, EIO); return; } done: tevent_req_done(req); } static int sysdb_op_default_recv(struct tevent_req *req) { enum tevent_req_state tstate; uint64_t err; if (tevent_req_is_error(req, &tstate, &err)) { return err; } return EOK; } /* =Remove-Entry-From-Sysdb=============================================== */ struct tevent_req *sysdb_delete_entry_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_handle *handle, struct ldb_dn *dn, bool ignore_not_found) { struct tevent_req *req, *subreq; struct sysdb_op_state *state; struct ldb_request *ldbreq; int ret; req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); if (!req) return NULL; state->ev = ev; state->handle = handle; state->ignore_not_found = ignore_not_found; state->ldbreply = NULL; ret = ldb_build_del_req(&ldbreq, handle->ctx->ldb, state, dn, NULL, NULL, NULL, NULL); if (ret != LDB_SUCCESS) { DEBUG(1, ("LDB Error: %s(%d)\nError Message: [%s]\n", ldb_strerror(ret), ret, ldb_errstring(handle->ctx->ldb))); ERROR_OUT(ret, sysdb_error_to_errno(ret), fail); } subreq = sldb_request_send(state, ev, handle->ctx->ldb, ldbreq); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_op_default_done, req); return req; fail: DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } int sysdb_delete_entry_recv(struct tevent_req *req) { return sysdb_op_default_recv(req); } /* =Search-Entry========================================================== */ static void sysdb_search_entry_done(struct tevent_req *subreq); struct tevent_req *sysdb_search_entry_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_handle *handle, struct ldb_dn *base_dn, int scope, const char *filter, const char **attrs) { struct tevent_req *req, *subreq; struct sysdb_op_state *state; struct ldb_request *ldbreq; int ret; req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); if (!req) return NULL; state->ev = ev; state->handle = handle; state->ignore_not_found = false; state->ldbreply = NULL; ret = ldb_build_search_req(&ldbreq, handle->ctx->ldb, state, base_dn, scope, filter, attrs, NULL, NULL, NULL, NULL); if (ret != LDB_SUCCESS) { DEBUG(1, ("Failed to build search request: %s(%d)[%s]\n", ldb_strerror(ret), ret, ldb_errstring(handle->ctx->ldb))); ERROR_OUT(ret, sysdb_error_to_errno(ret), fail); } subreq = sldb_request_send(state, ev, handle->ctx->ldb, ldbreq); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_search_entry_done, req); return req; fail: DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } static void sysdb_search_entry_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_op_state *state = tevent_req_data(req, struct sysdb_op_state); struct ldb_reply *ldbreply; int ret; ret = sldb_request_recv(subreq, state, &ldbreply); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } switch (ldbreply->type) { case LDB_REPLY_ENTRY: if (state->ldbreply) { DEBUG(1, ("More than one reply for a base search ?! " "DB seems corrupted, aborting.")); tevent_req_error(req, EFAULT); return; } /* save the entry so that it can be retrieved by the caller */ state->ldbreply = ldbreply; /* just return, wait for a LDB_REPLY_DONE entry */ return; case LDB_REPLY_DONE: if (!state->ldbreply) { talloc_zfree(ldbreply); DEBUG(6, ("Error: Entry not Found!\n")); tevent_req_error(req, ENOENT); return; } talloc_zfree(ldbreply); return tevent_req_done(req); default: /* unexpected stuff */ talloc_zfree(ldbreply); DEBUG(6, ("Error: Unknown error!\n")); tevent_req_error(req, EIO); return; } } int sysdb_search_entry_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, 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; if (tevent_req_is_error(req, &tstate, &err)) { return err; } *msg = talloc_move(mem_ctx, &state->ldbreply->message); return EOK; } /* =Search-User-by-[UID/NAME]============================================= */ struct sysdb_search_user_state { struct tevent_context *ev; struct sysdb_handle *handle; struct ldb_dn *basedn; const char **attrs; const char *filter; int scope; struct ldb_message *msg; }; static void sysdb_search_user_cont(struct tevent_req *subreq); static void sysdb_search_user_done(struct tevent_req *subreq); struct tevent_req *sysdb_search_user_by_name_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_ctx *sysdb, struct sysdb_handle *handle, struct sss_domain_info *domain, const char *name, const char **attrs) { struct tevent_req *req, *subreq; struct sysdb_search_user_state *state; static const char *def_attrs[] = { SYSDB_NAME, SYSDB_UIDNUM, NULL }; int ret; if (!sysdb && !handle) return NULL; req = tevent_req_create(mem_ctx, &state, struct sysdb_search_user_state); if (!req) return NULL; state->ev = ev; state->handle = handle; state->msg = NULL; state->attrs = attrs ? attrs : def_attrs; state->filter = NULL; state->scope = LDB_SCOPE_BASE; if (!sysdb) sysdb = handle->ctx; state->basedn = sysdb_user_dn(sysdb, state, domain->name, name); if (!state->basedn) { ERROR_OUT(ret, ENOMEM, fail); } if (!handle) { subreq = sysdb_operation_send(state, state->ev, sysdb); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_search_user_cont, req); } else { subreq = sysdb_search_entry_send(state, state->ev, state->handle, state->basedn, state->scope, state->filter, state->attrs); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_search_user_done, req); } return req; fail: DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } struct tevent_req *sysdb_search_user_by_uid_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_ctx *sysdb, struct sysdb_handle *handle, struct sss_domain_info *domain, uid_t uid, const char **attrs) { struct tevent_req *req, *subreq; struct sysdb_search_user_state *state; static const char *def_attrs[] = { SYSDB_NAME, SYSDB_UIDNUM, NULL }; int ret; if (!sysdb && !handle) return NULL; req = tevent_req_create(mem_ctx, &state, struct sysdb_search_user_state); if (!req) return NULL; state->ev = ev; state->handle = handle; state->msg = NULL; state->attrs = attrs ? attrs : def_attrs; if (!sysdb) sysdb = handle->ctx; state->basedn = ldb_dn_new_fmt(state, sysdb->ldb, SYSDB_TMPL_USER_BASE, domain->name); if (!state->basedn) { ERROR_OUT(ret, ENOMEM, fail); } state->filter = talloc_asprintf(state, SYSDB_PWUID_FILTER, (unsigned long)uid); if (!state->filter) { ERROR_OUT(ret, ENOMEM, fail); } state->scope = LDB_SCOPE_ONELEVEL; if (!handle) { subreq = sysdb_operation_send(state, state->ev, sysdb); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_search_user_cont, req); } else { subreq = sysdb_search_entry_send(state, state->ev, state->handle, state->basedn, state->scope, state->filter, state->attrs); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_search_user_done, req); } return req; fail: DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } static void sysdb_search_user_cont(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_search_user_state *state = tevent_req_data(req, struct sysdb_search_user_state); int ret; ret = sysdb_operation_recv(subreq, state, &state->handle); talloc_zfree(subreq); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } subreq = sysdb_search_entry_send(state, state->ev, state->handle, state->basedn, state->scope, state->filter, state->attrs); if (!subreq) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_search_user_done, req); } static void sysdb_search_user_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_search_user_state *state = tevent_req_data(req, struct sysdb_search_user_state); int ret; ret = sysdb_search_entry_recv(subreq, state, &state->msg); talloc_zfree(subreq); if (ret) { tevent_req_error(req, ret); return; } tevent_req_done(req); } int sysdb_search_user_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, struct ldb_message **msg) { struct sysdb_search_user_state *state = tevent_req_data(req, struct sysdb_search_user_state); enum tevent_req_state tstate; uint64_t err; if (tevent_req_is_error(req, &tstate, &err)) { return err; } *msg = talloc_move(mem_ctx, &state->msg); return EOK; } /* =Delete-User-by-UID==================================================== */ static void sysdb_delete_user_by_uid_found(struct tevent_req *subreq); static void sysdb_delete_user_by_uid_done(struct tevent_req *subreq); 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, bool ignore_not_found) { struct tevent_req *req, *subreq; struct sysdb_op_state *state; req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); if (!req) return NULL; state->ev = ev; state->handle = handle; state->ignore_not_found = ignore_not_found; state->ldbreply = NULL; subreq = sysdb_search_user_by_uid_send(state, ev, NULL, handle, domain, uid, NULL); if (!subreq) { talloc_zfree(req); return NULL; } tevent_req_set_callback(subreq, sysdb_delete_user_by_uid_found, req); return 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; } subreq = sysdb_delete_entry_send(state, state->ev, state->handle, msg->dn, state->ignore_not_found); if (!subreq) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_delete_user_by_uid_done, req); } 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); } int sysdb_delete_user_by_uid_recv(struct tevent_req *req) { return sysdb_op_default_recv(req); } /* =Search-Group-by-[GID/NAME]============================================ */ struct sysdb_search_group_state { struct tevent_context *ev; struct sysdb_handle *handle; struct ldb_dn *basedn; const char **attrs; const char *filter; int scope; struct ldb_message *msg; }; static void sysdb_search_group_cont(struct tevent_req *subreq); static void sysdb_search_group_done(struct tevent_req *subreq); struct tevent_req *sysdb_search_group_by_name_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_ctx *sysdb, struct sysdb_handle *handle, struct sss_domain_info *domain, const char *name, const char **attrs) { struct tevent_req *req, *subreq; struct sysdb_search_group_state *state; static const char *def_attrs[] = { SYSDB_NAME, SYSDB_GIDNUM, NULL }; int ret; if (!sysdb && !handle) return NULL; req = tevent_req_create(mem_ctx, &state, struct sysdb_search_group_state); if (!req) return NULL; state->ev = ev; state->handle = handle; state->msg = NULL; state->attrs = attrs ? attrs : def_attrs; state->filter = NULL; state->scope = LDB_SCOPE_BASE; if (!sysdb) sysdb = handle->ctx; state->basedn = sysdb_group_dn(sysdb, state, domain->name, name); if (!state->basedn) { ERROR_OUT(ret, ENOMEM, fail); } if (!handle) { subreq = sysdb_operation_send(state, state->ev, sysdb); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_search_group_cont, req); } else { subreq = sysdb_search_entry_send(state, state->ev, state->handle, state->basedn, state->scope, state->filter, state->attrs); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_search_group_done, req); } return req; fail: DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } struct tevent_req *sysdb_search_group_by_gid_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_ctx *sysdb, struct sysdb_handle *handle, struct sss_domain_info *domain, gid_t gid, const char **attrs) { struct tevent_req *req, *subreq; struct sysdb_search_group_state *state; static const char *def_attrs[] = { SYSDB_NAME, SYSDB_GIDNUM, NULL }; int ret; if (!sysdb && !handle) return NULL; req = tevent_req_create(mem_ctx, &state, struct sysdb_search_group_state); if (!req) return NULL; state->ev = ev; state->handle = handle; state->msg = NULL; state->attrs = attrs ? attrs : def_attrs; if (!sysdb) sysdb = handle->ctx; state->basedn = ldb_dn_new_fmt(state, sysdb->ldb, SYSDB_TMPL_GROUP_BASE, domain->name); if (!state->basedn) { ERROR_OUT(ret, ENOMEM, fail); } state->filter = talloc_asprintf(state, SYSDB_GRGID_FILTER, (unsigned long)gid); if (!state->filter) { ERROR_OUT(ret, ENOMEM, fail); } state->scope = LDB_SCOPE_ONELEVEL; if (!handle) { subreq = sysdb_operation_send(state, state->ev, sysdb); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_search_group_cont, req); } else { subreq = sysdb_search_entry_send(state, state->ev, state->handle, state->basedn, state->scope, state->filter, state->attrs); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_search_group_done, req); } return req; fail: DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } static void sysdb_search_group_cont(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_search_group_state *state = tevent_req_data(req, struct sysdb_search_group_state); int ret; ret = sysdb_operation_recv(subreq, state, &state->handle); talloc_zfree(subreq); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } subreq = sysdb_search_entry_send(state, state->ev, state->handle, state->basedn, state->scope, state->filter, state->attrs); if (!subreq) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_search_group_done, req); } static void sysdb_search_group_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_search_group_state *state = tevent_req_data(req, struct sysdb_search_group_state); int ret; ret = sysdb_search_entry_recv(subreq, state, &state->msg); talloc_zfree(subreq); if (ret) { tevent_req_error(req, ret); return; } tevent_req_done(req); } int sysdb_search_group_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, struct ldb_message **msg) { struct sysdb_search_group_state *state = tevent_req_data(req, struct sysdb_search_group_state); enum tevent_req_state tstate; uint64_t err; if (tevent_req_is_error(req, &tstate, &err)) { return err; } *msg = talloc_move(mem_ctx, &state->msg); return EOK; } /* =Delete-Group-by-GID=================================================== */ static void sysdb_delete_group_by_gid_found(struct tevent_req *subreq); static void sysdb_delete_group_by_gid_done(struct tevent_req *subreq); 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, bool ignore_not_found) { struct tevent_req *req, *subreq; struct sysdb_op_state *state; req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); if (!req) return NULL; state->ev = ev; state->handle = handle; state->ignore_not_found = ignore_not_found; state->ldbreply = NULL; subreq = sysdb_search_group_by_gid_send(state, ev, NULL, handle, domain, gid, NULL); if (!subreq) { talloc_zfree(req); return NULL; } tevent_req_set_callback(subreq, sysdb_delete_group_by_gid_found, req); return req; } 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; 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; } subreq = sysdb_delete_entry_send(state, state->ev, state->handle, msg->dn, state->ignore_not_found); if (!subreq) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_delete_group_by_gid_done, req); } static void sysdb_delete_group_by_gid_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); int ret; ret = sysdb_delete_entry_recv(subreq); talloc_zfree(subreq); if (ret) { tevent_req_error(req, ret); return; } tevent_req_done(req); } int sysdb_delete_group_by_gid_recv(struct tevent_req *req) { return sysdb_op_default_recv(req); } /* =Replace-Attributes-On-Entry=========================================== */ struct tevent_req *sysdb_set_entry_attr_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_handle *handle, struct ldb_dn *entry_dn, struct sysdb_attrs *attrs, int mod_op) { struct tevent_req *req, *subreq; struct sysdb_op_state *state; struct ldb_request *ldbreq; struct ldb_message *msg; int i, ret; req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); if (!req) return NULL; state->ev = ev; state->handle = handle; state->ignore_not_found = false; state->ldbreply = NULL; if (!entry_dn) { ERROR_OUT(ret, EINVAL, fail); } if (attrs->num == 0) { ERROR_OUT(ret, EINVAL, fail); } msg = ldb_msg_new(state); if (!msg) { ERROR_OUT(ret, ENOMEM, fail); } msg->dn = entry_dn; msg->elements = talloc_array(msg, struct ldb_message_element, attrs->num); if (!msg->elements) { ERROR_OUT(ret, ENOMEM, fail); } for (i = 0; i < attrs->num; i++) { msg->elements[i] = attrs->a[i]; msg->elements[i].flags = mod_op; } msg->num_elements = attrs->num; ret = ldb_build_mod_req(&ldbreq, handle->ctx->ldb, state, msg, NULL, NULL, NULL, NULL); if (ret != LDB_SUCCESS) { DEBUG(1, ("Failed to build modify request: %s(%d)[%s]\n", ldb_strerror(ret), ret, ldb_errstring(handle->ctx->ldb))); ERROR_OUT(ret, sysdb_error_to_errno(ret), fail); } subreq = sldb_request_send(state, ev, handle->ctx->ldb, ldbreq); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_op_default_done, req); return req; fail: DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } int sysdb_set_entry_attr_recv(struct tevent_req *req) { return sysdb_op_default_recv(req); } /* =Replace-Attributes-On-User============================================ */ static void sysdb_set_user_attr_done(struct tevent_req *subreq); struct tevent_req *sysdb_set_user_attr_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_handle *handle, struct sss_domain_info *domain, const char *name, struct sysdb_attrs *attrs, int mod_op) { struct tevent_req *req, *subreq; struct sysdb_op_state *state; struct ldb_dn *dn; int ret; req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); if (!req) return NULL; state->ev = ev; state->handle = handle; state->ignore_not_found = false; state->ldbreply = NULL; dn = sysdb_user_dn(handle->ctx, state, domain->name, name); if (!dn) { ERROR_OUT(ret, ENOMEM, fail); } subreq = sysdb_set_entry_attr_send(state, ev, handle, dn, attrs, mod_op); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_set_user_attr_done, req); return req; fail: DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } static void sysdb_set_user_attr_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); int ret; ret = sysdb_set_entry_attr_recv(subreq); talloc_zfree(subreq); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } tevent_req_done(req); } int sysdb_set_user_attr_recv(struct tevent_req *req) { return sysdb_op_default_recv(req); } /* =Replace-Attributes-On-Group=========================================== */ static void sysdb_set_group_attr_done(struct tevent_req *subreq); struct tevent_req *sysdb_set_group_attr_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_handle *handle, struct sss_domain_info *domain, const char *name, struct sysdb_attrs *attrs, int mod_op) { struct tevent_req *req, *subreq; struct sysdb_op_state *state; struct ldb_dn *dn; int ret; req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); if (!req) return NULL; state->ev = ev; state->handle = handle; state->ignore_not_found = false; state->ldbreply = NULL; dn = sysdb_group_dn(handle->ctx, state, domain->name, name); if (!dn) { ERROR_OUT(ret, ENOMEM, fail); } subreq = sysdb_set_entry_attr_send(state, ev, handle, dn, attrs, mod_op); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_set_group_attr_done, req); return req; fail: DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } static void sysdb_set_group_attr_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); int ret; ret = sysdb_set_entry_attr_recv(subreq); talloc_zfree(subreq); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } tevent_req_done(req); } int sysdb_set_group_attr_recv(struct tevent_req *req) { return sysdb_op_default_recv(req); } /* =Get-New-ID============================================================ */ struct sysdb_get_new_id_state { struct tevent_context *ev; struct sysdb_handle *handle; struct sss_domain_info *domain; struct ldb_dn *base_dn; struct ldb_message *base; struct ldb_message **v_msgs; int v_count; uint32_t new_id; }; static void sysdb_get_new_id_base(struct tevent_req *subreq); static void sysdb_get_new_id_verify(struct tevent_req *subreq); static void sysdb_get_new_id_done(struct tevent_req *subreq); struct tevent_req *sysdb_get_new_id_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_handle *handle, struct sss_domain_info *domain) { struct tevent_req *req, *subreq; struct sysdb_get_new_id_state *state; static const char *attrs[] = { SYSDB_NEXTID, NULL }; struct ldb_request *ldbreq; int ret; req = tevent_req_create(mem_ctx, &state, struct sysdb_get_new_id_state); if (!req) return NULL; state->ev = ev; state->handle = handle; state->domain = domain; state->base = NULL; state->v_msgs = NULL; state->v_count = 0; state->new_id = 0; state->base_dn = sysdb_domain_dn(handle->ctx, state, domain->name); if (!state->base_dn) { ERROR_OUT(ret, ENOMEM, fail); } ret = ldb_build_search_req(&ldbreq, handle->ctx->ldb, state, state->base_dn, LDB_SCOPE_BASE, SYSDB_NEXTID_FILTER, attrs, NULL, NULL, NULL, NULL); if (ret != LDB_SUCCESS) { DEBUG(1, ("Failed to build search request: %s(%d)[%s]\n", ldb_strerror(ret), ret, ldb_errstring(handle->ctx->ldb))); ERROR_OUT(ret, sysdb_error_to_errno(ret), fail); } subreq = sldb_request_send(state, ev, handle->ctx->ldb, ldbreq); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_get_new_id_base, req); return req; fail: DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } static void sysdb_get_new_id_base(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_get_new_id_state *state = tevent_req_data(req, struct sysdb_get_new_id_state); static const char *attrs[] = { SYSDB_UIDNUM, SYSDB_GIDNUM, NULL }; struct ldb_reply *ldbreply; struct ldb_request *ldbreq; char *filter; int ret; ret = sldb_request_recv(subreq, state, &ldbreply); if (ret) { talloc_zfree(subreq); DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } switch (ldbreply->type) { case LDB_REPLY_ENTRY: if (state->base) { DEBUG(1, ("More than one reply for a base search ?! " "DB seems corrupted, aborting.")); tevent_req_error(req, EFAULT); return; } state->base = talloc_move(state, &ldbreply->message); if (!state->base) { DEBUG(6, ("Error: Out of memory!\n")); tevent_req_error(req, ENOMEM); return; } /* just return, wait for a LDB_REPLY_DONE entry */ talloc_zfree(ldbreply); return; case LDB_REPLY_DONE: break; default: /* unexpected stuff */ DEBUG(6, ("Error: Unknown error\n")); tevent_req_error(req, EIO); talloc_zfree(ldbreply); return; } talloc_zfree(subreq); if (state->base) { state->new_id = get_attr_as_uint32(state->base, SYSDB_NEXTID); if (state->new_id == (uint32_t)(-1)) { DEBUG(1, ("Invalid Next ID in domain %s\n", state->domain->name)); tevent_req_error(req, ERANGE); return; } if (state->new_id < state->domain->id_min) { state->new_id = state->domain->id_min; } if ((state->domain->id_max != 0) && (state->new_id > state->domain->id_max)) { DEBUG(0, ("Failed to allocate new id, out of range (%u/%u)\n", state->new_id, state->domain->id_max)); tevent_req_error(req, ERANGE); return; } } else { /* looks like the domain is not initialized yet, use min_id */ state->new_id = state->domain->id_min; } /* verify the id is actually really free. * search all entries with id >= new_id and < max_id */ if (state->domain->id_max) { filter = talloc_asprintf(state, "(|(&(%s>=%u)(%s<=%u))(&(%s>=%u)(%s<=%u)))", SYSDB_UIDNUM, state->new_id, SYSDB_UIDNUM, state->domain->id_max, SYSDB_GIDNUM, state->new_id, SYSDB_GIDNUM, state->domain->id_max); } else { filter = talloc_asprintf(state, "(|(%s>=%u)(%s>=%u))", SYSDB_UIDNUM, state->new_id, SYSDB_GIDNUM, state->new_id); } if (!filter) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } ret = ldb_build_search_req(&ldbreq, state->handle->ctx->ldb, state, state->base_dn, LDB_SCOPE_SUBTREE, filter, attrs, NULL, NULL, NULL, NULL); if (ret != LDB_SUCCESS) { DEBUG(1, ("Failed to build search request: %s(%d)[%s]\n", ldb_strerror(ret), ret, ldb_errstring(state->handle->ctx->ldb))); tevent_req_error(req, sysdb_error_to_errno(ret)); return; } subreq = sldb_request_send(state, state->ev, state->handle->ctx->ldb, ldbreq); if (!subreq) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_get_new_id_verify, req); return; } static void sysdb_get_new_id_verify(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_get_new_id_state *state = tevent_req_data(req, struct sysdb_get_new_id_state); struct ldb_reply *ldbreply; struct ldb_request *ldbreq; struct ldb_message *msg; int ret, i; ret = sldb_request_recv(subreq, state, &ldbreply); if (ret) { talloc_zfree(subreq); DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } switch (ldbreply->type) { case LDB_REPLY_ENTRY: state->v_msgs = talloc_realloc(state, state->v_msgs, struct ldb_message *, state->v_count + 2); if (!state->v_msgs) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } state->v_msgs[state->v_count] = talloc_move(state, &ldbreply->message); if (!state->v_msgs[state->v_count]) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } state->v_count++; /* just return, wait for a LDB_REPLY_DONE entry */ talloc_zfree(ldbreply); return; case LDB_REPLY_DONE: break; default: /* unexpected stuff */ DEBUG(6, ("Error: Unknown error\n")); tevent_req_error(req, EIO); talloc_zfree(ldbreply); return; } talloc_zfree(subreq); /* if anything was found, find the maximum and increment past it */ if (state->v_count) { uint32_t id; for (i = 0; i < state->v_count; i++) { id = get_attr_as_uint32(state->v_msgs[i], SYSDB_UIDNUM); if (id != (uint32_t)(-1)) { if (id > state->new_id) state->new_id = id; } id = get_attr_as_uint32(state->v_msgs[i], SYSDB_GIDNUM); if (id != (uint32_t)(-1)) { if (id > state->new_id) state->new_id = id; } } state->new_id++; /* check again we are not falling out of range */ if ((state->domain->id_max != 0) && (state->new_id > state->domain->id_max)) { DEBUG(0, ("Failed to allocate new id, out of range (%u/%u)\n", state->new_id, state->domain->id_max)); tevent_req_error(req, ERANGE); return; } talloc_zfree(state->v_msgs); state->v_count = 0; } /* finally store the new next id */ msg = ldb_msg_new(state); if (!msg) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } msg->dn = state->base_dn; ret = add_ulong(msg, LDB_FLAG_MOD_REPLACE, SYSDB_NEXTID, state->new_id + 1); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } ret = ldb_build_mod_req(&ldbreq, state->handle->ctx->ldb, state, msg, NULL, NULL, NULL, NULL); if (ret != LDB_SUCCESS) { DEBUG(1, ("Failed to build modify request: %s(%d)[%s]\n", ldb_strerror(ret), ret, ldb_errstring(state->handle->ctx->ldb))); tevent_req_error(req, ret); return; } subreq = sldb_request_send(state, state->ev, state->handle->ctx->ldb, ldbreq); if (!subreq) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_get_new_id_done, req); } static void sysdb_get_new_id_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_get_new_id_state *state = tevent_req_data(req, struct sysdb_get_new_id_state); struct ldb_reply *ldbreply; int ret; ret = sldb_request_recv(subreq, state, &ldbreply); talloc_zfree(subreq); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } if (ldbreply->type != LDB_REPLY_DONE) { DEBUG(6, ("Error: %d (%s)\n", EIO, strerror(EIO))); tevent_req_error(req, EIO); return; } tevent_req_done(req); } int sysdb_get_new_id_recv(struct tevent_req *req, uint32_t *id) { struct sysdb_get_new_id_state *state = tevent_req_data(req, struct sysdb_get_new_id_state); 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; } if (shell && *shell) { ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_SHELL, shell); if (ret) goto fail; } /* creation time */ ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_CREATE_TIME, (unsigned long)time(NULL)); if (ret) goto fail; ret = ldb_build_add_req(&ldbreq, handle->ctx->ldb, state, msg, NULL, NULL, NULL, NULL); if (ret != LDB_SUCCESS) { DEBUG(1, ("Failed to build modify request: %s(%d)[%s]\n", ldb_strerror(ret), ret, ldb_errstring(handle->ctx->ldb))); ERROR_OUT(ret, sysdb_error_to_errno(ret), fail); } subreq = sldb_request_send(state, ev, handle->ctx->ldb, ldbreq); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_op_default_done, req); return req; fail: DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } int sysdb_add_basic_user_recv(struct tevent_req *req) { return sysdb_op_default_recv(req); } /* =Add-User-Function===================================================== */ struct sysdb_add_user_state { struct tevent_context *ev; struct sysdb_handle *handle; struct sss_domain_info *domain; const char *name; uid_t uid; gid_t gid; const char *gecos; const char *homedir; const char *shell; struct sysdb_attrs *attrs; }; 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); 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 tevent_req *req, *subreq; struct sysdb_add_user_state *state; int ret; req = tevent_req_create(mem_ctx, &state, struct sysdb_add_user_state); if (!req) return NULL; state->ev = ev; state->handle = handle; state->domain = domain; state->name = name; state->uid = uid; state->gid = gid; state->gecos = gecos; state->homedir = homedir; state->shell = shell; state->attrs = attrs; if (handle->ctx->mpg) { if (gid != 0) { DEBUG(0, ("Cannot add user with arbitrary GID in MPG domain!\n")); ERROR_OUT(ret, EINVAL, fail); } state->gid = state->uid; } if (domain->id_max != 0 && uid != 0 && (uid < domain->id_min || uid > domain->id_max)) { DEBUG(2, ("Supplied uid [%d] is not in the allowed range [%d-%d].\n", uid, domain->id_min, domain->id_max)); ERROR_OUT(ret, ERANGE, fail); } if (domain->id_max != 0 && gid != 0 && (gid < domain->id_min || gid > domain->id_max)) { DEBUG(2, ("Supplied gid [%d] is not in the allowed range [%d-%d].\n", gid, domain->id_min, domain->id_max)); ERROR_OUT(ret, ERANGE, fail); } if (handle->ctx->mpg) { /* In MPG domains you can't have groups with the same name as users, * search if a group with the same name exists. * Don't worry about users, if we try to add a user with the same * name the operation will fail */ subreq = sysdb_search_group_by_name_send(state, ev, NULL, handle, domain, name, NULL); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_add_user_group_check, req); return req; } /* check no other user with the same uid exist */ if (state->uid != 0) { subreq = sysdb_search_user_by_uid_send(state, ev, NULL, handle, domain, uid, NULL); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_add_user_uid_check, req); return req; } /* try to add the user */ subreq = sysdb_add_basic_user_send(state, ev, handle, domain, name, uid, gid, gecos, homedir, shell); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_add_user_basic_done, req); return req; fail: DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } static void sysdb_add_user_group_check(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_add_user_state *state = tevent_req_data(req, struct sysdb_add_user_state); struct ldb_message *msg; int ret; /* We can succeed only if we get an ENOENT error, which means no groups * with the same name exist. * If any other error is returned fail as well. */ ret = sysdb_search_group_recv(subreq, state, &msg); talloc_zfree(subreq); if (ret != ENOENT) { if (ret == EOK) ret = EEXIST; tevent_req_error(req, ret); return; } /* check no other user with the same uid exist */ if (state->uid != 0) { subreq = sysdb_search_user_by_uid_send(state, state->ev, NULL, state->handle, state->domain, state->uid, NULL); if (!subreq) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_add_user_uid_check, req); return; } /* try to add the user */ subreq = sysdb_add_basic_user_send(state, state->ev, state->handle, state->domain, state->name, state->uid, state->gid, state->gecos, state->homedir, state->shell); if (!subreq) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_add_user_basic_done, req); } static void sysdb_add_user_uid_check(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_add_user_state *state = tevent_req_data(req, struct sysdb_add_user_state); struct ldb_message *msg; int ret; /* We can succeed only if we get an ENOENT error, which means no user * with the same uid exist. * If any other error is returned fail as well. */ ret = sysdb_search_user_recv(subreq, state, &msg); talloc_zfree(subreq); if (ret != ENOENT) { if (ret == EOK) ret = EEXIST; tevent_req_error(req, ret); return; } /* try to add the user */ subreq = sysdb_add_basic_user_send(state, state->ev, state->handle, state->domain, state->name, state->uid, state->gid, state->gecos, state->homedir, state->shell); if (!subreq) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_add_user_basic_done, req); } static void sysdb_add_user_basic_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_add_user_state *state = tevent_req_data(req, struct sysdb_add_user_state); int ret; ret = sysdb_add_basic_user_recv(subreq); talloc_zfree(subreq); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } if (state->uid == 0) { subreq = sysdb_get_new_id_send(state, state->ev, state->handle, state->domain); if (!subreq) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_add_user_get_id_done, req); return; } 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) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_add_user_set_attrs_done, req); return; } tevent_req_done(req); } static void sysdb_add_user_get_id_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_add_user_state *state = tevent_req_data(req, struct sysdb_add_user_state); struct sysdb_attrs *id_attrs; uint32_t id; int ret; ret = sysdb_get_new_id_recv(subreq, &id); talloc_zfree(subreq); if (ret) { tevent_req_error(req, ret); return; } if (state->uid == 0) { id_attrs = sysdb_new_attrs(state); if (!id_attrs) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } ret = sysdb_attrs_add_uint32(id_attrs, SYSDB_UIDNUM, id); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } if (state->handle->ctx->mpg) { ret = sysdb_attrs_add_uint32(id_attrs, SYSDB_GIDNUM, id); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } } subreq = sysdb_set_user_attr_send(state, state->ev, state->handle, state->domain, state->name, id_attrs, SYSDB_MOD_REP); if (!subreq) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_add_user_set_id_done, req); return; } 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) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_add_user_set_attrs_done, req); return; } 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; } 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) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_add_user_set_attrs_done, req); return; } tevent_req_done(req); } static void sysdb_add_user_set_attrs_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); int ret; ret = sysdb_set_user_attr_recv(subreq); talloc_zfree(subreq); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } tevent_req_done(req); } int sysdb_add_user_recv(struct tevent_req *req) { return sysdb_op_default_recv(req); } /* =Add-Basic-Group-NO-CHECKS============================================= */ struct tevent_req *sysdb_add_basic_group_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_handle *handle, struct sss_domain_info *domain, const char *name, gid_t gid) { struct tevent_req *req, *subreq; struct sysdb_op_state *state; struct ldb_request *ldbreq; struct ldb_message *msg; int ret; req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); if (!req) return NULL; state->ev = ev; state->handle = handle; state->ignore_not_found = false; state->ldbreply = NULL; msg = ldb_msg_new(state); if (!msg) { ERROR_OUT(ret, ENOMEM, fail); } /* user dn */ msg->dn = sysdb_group_dn(handle->ctx, msg, domain->name, name); if (!msg->dn) { ERROR_OUT(ret, ENOMEM, fail); } ret = add_string(msg, LDB_FLAG_MOD_ADD, "objectClass", SYSDB_GROUP_CLASS); if (ret) goto fail; ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_NAME, name); if (ret) goto fail; ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_GIDNUM, (unsigned long)gid); if (ret) goto fail; /* creation time */ ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_CREATE_TIME, (unsigned long)time(NULL)); if (ret) goto fail; ret = ldb_build_add_req(&ldbreq, handle->ctx->ldb, state, msg, NULL, NULL, NULL, NULL); if (ret != LDB_SUCCESS) { DEBUG(1, ("Failed to build modify request: %s(%d)[%s]\n", ldb_strerror(ret), ret, ldb_errstring(handle->ctx->ldb))); ERROR_OUT(ret, sysdb_error_to_errno(ret), fail); } subreq = sldb_request_send(state, ev, handle->ctx->ldb, ldbreq); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_op_default_done, req); return req; fail: DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } int sysdb_add_basic_group_recv(struct tevent_req *req) { return sysdb_op_default_recv(req); } /* =Add-Group-Function==================================================== */ struct sysdb_add_group_state { struct tevent_context *ev; struct sysdb_handle *handle; struct sss_domain_info *domain; const char *name; gid_t gid; struct sysdb_attrs *attrs; }; 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 tevent_req *req, *subreq; struct sysdb_add_group_state *state; int ret; req = tevent_req_create(mem_ctx, &state, struct sysdb_add_group_state); if (!req) return NULL; state->ev = ev; state->handle = handle; state->domain = domain; state->name = name; state->gid = gid; state->attrs = attrs; if (domain->id_max != 0 && gid != 0 && (gid < domain->id_min || gid > domain->id_max)) { DEBUG(2, ("Supplied gid [%d] is not in the allowed range [%d-%d].\n", gid, domain->id_min, domain->id_max)); ERROR_OUT(ret, ERANGE, fail); } if (handle->ctx->mpg) { /* In MPG domains you can't have groups with the same name as users, * search if a group with the same name exists. * Don't worry about users, if we try to add a user with the same * name the operation will fail */ subreq = sysdb_search_user_by_name_send(state, ev, NULL, handle, domain, name, NULL); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_add_group_user_check, req); return req; } /* check no other groups with the same gid exist */ if (state->gid != 0) { subreq = sysdb_search_group_by_gid_send(state, ev, NULL, handle, domain, gid, NULL); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_add_group_gid_check, req); return req; } /* try to add the group */ subreq = sysdb_add_basic_group_send(state, ev, handle, domain, name, gid); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_add_group_basic_done, req); return req; fail: DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } static void sysdb_add_group_user_check(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_add_group_state *state = tevent_req_data(req, struct sysdb_add_group_state); struct ldb_message *msg; int ret; /* We can succeed only if we get an ENOENT error, which means no users * with the same name exist. * If any other error is returned fail as well. */ ret = sysdb_search_user_recv(subreq, state, &msg); talloc_zfree(subreq); if (ret != ENOENT) { if (ret == EOK) ret = EEXIST; tevent_req_error(req, ret); return; } /* check no other group with the same gid exist */ if (state->gid != 0) { subreq = sysdb_search_group_by_gid_send(state, state->ev, NULL, state->handle, state->domain, state->gid, NULL); if (!subreq) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_add_group_gid_check, req); return; } /* try to add the group */ subreq = sysdb_add_basic_group_send(state, state->ev, state->handle, state->domain, state->name, state->gid); if (!subreq) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_add_group_basic_done, req); } static void sysdb_add_group_gid_check(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_add_group_state *state = tevent_req_data(req, struct sysdb_add_group_state); struct ldb_message *msg; int ret; /* We can succeed only if we get an ENOENT error, which means no group * with the same gid exist. * If any other error is returned fail as well. */ ret = sysdb_search_group_recv(subreq, state, &msg); talloc_zfree(subreq); if (ret != ENOENT) { if (ret == EOK) ret = EEXIST; tevent_req_error(req, ret); return; } /* try to add the group */ subreq = sysdb_add_basic_group_send(state, state->ev, state->handle, state->domain, state->name, state->gid); if (!subreq) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_add_group_basic_done, req); } static void sysdb_add_group_basic_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_add_group_state *state = tevent_req_data(req, struct sysdb_add_group_state); int ret; ret = sysdb_add_basic_group_recv(subreq); talloc_zfree(subreq); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } if (state->gid == 0) { subreq = sysdb_get_new_id_send(state, state->ev, state->handle, state->domain); if (!subreq) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_add_group_get_id_done, req); return; } 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) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_add_group_set_attrs_done, req); return; } tevent_req_done(req); } static void sysdb_add_group_get_id_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_add_group_state *state = tevent_req_data(req, struct sysdb_add_group_state); struct sysdb_attrs *id_attrs; uint32_t id; int ret; ret = sysdb_get_new_id_recv(subreq, &id); talloc_zfree(subreq); if (ret) { tevent_req_error(req, ret); return; } if (state->gid == 0) { id_attrs = sysdb_new_attrs(state); if (!id_attrs) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } ret = sysdb_attrs_add_uint32(id_attrs, SYSDB_GIDNUM, id); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } subreq = sysdb_set_group_attr_send(state, state->ev, state->handle, state->domain, state->name, id_attrs, SYSDB_MOD_REP); if (!subreq) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_add_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) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_add_group_set_attrs_done, req); 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) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(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) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_add_group_set_attrs_done, req); 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) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } tevent_req_done(req); } int sysdb_add_group_recv(struct tevent_req *req) { return sysdb_op_default_recv(req); } /* =Add-Or-Remove-Group-Memeber=========================================== */ /* mod_op must be either SYSDB_MOD_ADD or SYSDB_MOD_DEL */ struct tevent_req *sysdb_mod_group_member_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_handle *handle, struct ldb_dn *member_dn, struct ldb_dn *group_dn, int mod_op) { struct tevent_req *req, *subreq; struct sysdb_op_state *state; struct ldb_request *ldbreq; struct ldb_message *msg; const char *dn; int ret; req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); if (!req) return NULL; state->ev = ev; state->handle = handle; state->ignore_not_found = false; state->ldbreply = NULL; msg = ldb_msg_new(state); if (!msg) { ERROR_OUT(ret, ENOMEM, fail); } msg->dn = group_dn; ret = ldb_msg_add_empty(msg, SYSDB_MEMBER, mod_op, NULL); if (ret != LDB_SUCCESS) { ERROR_OUT(ret, ENOMEM, fail); } dn = ldb_dn_get_linearized(member_dn); if (!dn) { ERROR_OUT(ret, EINVAL, fail); } ret = ldb_msg_add_fmt(msg, SYSDB_MEMBER, "%s", dn); if (ret != LDB_SUCCESS) { ERROR_OUT(ret, EINVAL, fail); } ret = ldb_build_mod_req(&ldbreq, handle->ctx->ldb, state, msg, NULL, NULL, NULL, NULL); if (ret != LDB_SUCCESS) { DEBUG(1, ("Failed to build modify request: %s(%d)[%s]\n", ldb_strerror(ret), ret, ldb_errstring(handle->ctx->ldb))); ERROR_OUT(ret, sysdb_error_to_errno(ret), fail); } subreq = sldb_request_send(state, ev, handle->ctx->ldb, ldbreq); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_op_default_done, req); return req; fail: DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } int sysdb_mod_group_member_recv(struct tevent_req *req) { return sysdb_op_default_recv(req); } /* =Store-Users-(Native/Legacy)-(replaces-existing-data)================== */ /* if one of the basic attributes is empty ("") as opposed to NULL, * this will just remove it */ struct sysdb_store_user_state { struct tevent_context *ev; struct sysdb_handle *handle; struct sss_domain_info *domain; const char *name; uid_t uid; gid_t gid; const char *gecos; const char *homedir; const char *shell; struct sysdb_attrs *attrs; uint64_t cache_timeout; }; static void sysdb_store_user_check(struct tevent_req *subreq); static void sysdb_store_user_add_done(struct tevent_req *subreq); static void sysdb_store_user_attr_done(struct tevent_req *subreq); struct tevent_req *sysdb_store_user_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_handle *handle, struct sss_domain_info *domain, const char *name, const char *pwd, uid_t uid, gid_t gid, const char *gecos, const char *homedir, const char *shell, struct sysdb_attrs *attrs, uint64_t cache_timeout) { struct tevent_req *req, *subreq; struct sysdb_store_user_state *state; int ret; req = tevent_req_create(mem_ctx, &state, struct sysdb_store_user_state); if (!req) return NULL; state->ev = ev; state->handle = handle; state->domain = domain; state->name = name; state->uid = uid; state->gid = gid; state->gecos = gecos; state->homedir = homedir; state->shell = shell; state->attrs = attrs; state->cache_timeout = cache_timeout; if (pwd && (domain->legacy_passwords || !*pwd)) { ret = sysdb_attrs_add_string(state->attrs, SYSDB_PWD, pwd); if (ret) goto fail; } subreq = sysdb_search_user_by_name_send(state, ev, NULL, handle, domain, name, NULL); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_store_user_check, req); return req; fail: DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } static void sysdb_store_user_check(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_store_user_state *state = tevent_req_data(req, struct sysdb_store_user_state); struct ldb_message *msg; time_t now = time(NULL); int ret; ret = sysdb_search_user_recv(subreq, state, &msg); talloc_zfree(subreq); if (ret && ret != ENOENT) { tevent_req_error(req, ret); return; } if (ret == ENOENT) { /* users doesn't exist, turn into adding a user */ subreq = sysdb_add_user_send(state, state->ev, state->handle, state->domain, state->name, state->uid, state->gid, state->gecos, state->homedir, state->shell, state->attrs); if (!subreq) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_store_user_add_done, req); return; } /* the user exists, let's just replace attributes when set */ if (!state->attrs) { state->attrs = sysdb_new_attrs(state); if (!state->attrs) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } } if (state->uid) { ret = sysdb_attrs_add_uint32(state->attrs, SYSDB_UIDNUM, state->uid); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } } if (state->gid) { ret = sysdb_attrs_add_uint32(state->attrs, SYSDB_GIDNUM, state->gid); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } } if (state->uid && !state->gid && state->handle->ctx->mpg) { ret = sysdb_attrs_add_uint32(state->attrs, SYSDB_GIDNUM, state->uid); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } } if (state->gecos) { ret = sysdb_attrs_add_string(state->attrs, SYSDB_GECOS, state->gecos); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } } if (state->homedir) { ret = sysdb_attrs_add_string(state->attrs, SYSDB_HOMEDIR, state->homedir); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } } if (state->shell) { ret = sysdb_attrs_add_string(state->attrs, SYSDB_SHELL, state->shell); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } } ret = sysdb_attrs_add_time_t(state->attrs, SYSDB_LAST_UPDATE, now); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } ret = sysdb_attrs_add_time_t(state->attrs, SYSDB_CACHE_EXPIRE, now + state->cache_timeout); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } subreq = sysdb_set_user_attr_send(state, state->ev, state->handle, state->domain, state->name, state->attrs, SYSDB_MOD_REP); if (!subreq) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_store_user_attr_done, req); } static void sysdb_store_user_add_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); int ret; ret = sysdb_add_user_recv(subreq); talloc_zfree(subreq); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } tevent_req_done(req); } static void sysdb_store_user_attr_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); int ret; ret = sysdb_set_user_attr_recv(subreq); talloc_zfree(subreq); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } tevent_req_done(req); } int sysdb_store_user_recv(struct tevent_req *req) { return sysdb_op_default_recv(req); } /* =Store-Group-(Native/Legacy)-(replaces-existing-data)================== */ /* this function does not check that all user members are actually present */ struct sysdb_store_group_state { struct tevent_context *ev; struct sysdb_handle *handle; struct sss_domain_info *domain; const char *name; gid_t gid; const char **member_users; const char **member_groups; struct sysdb_attrs *attrs; uint64_t cache_timeout; }; static void sysdb_store_group_check(struct tevent_req *subreq); static void sysdb_store_group_add_done(struct tevent_req *subreq); static void sysdb_store_group_attr_done(struct tevent_req *subreq); struct tevent_req *sysdb_store_group_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_handle *handle, struct sss_domain_info *domain, const char *name, gid_t gid, const char **member_users, const char **member_groups, struct sysdb_attrs *attrs, uint64_t cache_timeout) { struct tevent_req *req, *subreq; struct sysdb_store_group_state *state; static const char *src_attrs[] = { SYSDB_NAME, SYSDB_GIDNUM, SYSDB_ORIG_MODSTAMP, NULL }; int ret; req = tevent_req_create(mem_ctx, &state, struct sysdb_store_group_state); if (!req) return NULL; state->ev = ev; state->handle = handle; state->domain = domain; state->name = name; state->gid = gid; state->member_users = member_users; state->member_groups = member_groups; state->attrs = attrs; state->cache_timeout = cache_timeout; subreq = sysdb_search_group_by_name_send(state, ev, NULL, handle, domain, name, src_attrs); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_store_group_check, req); return req; fail: DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } static void sysdb_store_group_check(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_store_group_state *state = tevent_req_data(req, struct sysdb_store_group_state); struct ldb_message *msg; time_t now = time(NULL); bool new_group = false; int ret, i; ret = sysdb_search_group_recv(subreq, state, &msg); talloc_zfree(subreq); if (ret && ret != ENOENT) { tevent_req_error(req, ret); return; } if (ret == ENOENT) { new_group = true; } /* FIXME: use the remote modification timestamp to know if the * group needs any update */ if (state->member_users || state->member_groups) { if (!state->attrs) { state->attrs = sysdb_new_attrs(state); if (!state->attrs) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } } for (i = 0; state->member_users && state->member_users[i]; i++) { char *member; member = talloc_asprintf(state, SYSDB_TMPL_USER, state->member_users[i], state->domain->name); if (!member) { DEBUG(4, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } DEBUG(9, ("adding member: %s to group %s\n", member, state->name)); ret = sysdb_attrs_steal_string(state->attrs, SYSDB_MEMBER, member); if (ret) { DEBUG(4, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } } for (i = 0; state->member_groups && state->member_groups[i]; i++) { char *member; member = talloc_asprintf(state, SYSDB_TMPL_GROUP, state->member_groups[i], state->domain->name); if (!member) { DEBUG(4, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } DEBUG(9, ("adding member: %s to group %s\n", member, state->name)); ret = sysdb_attrs_steal_string(state->attrs, SYSDB_MEMBER, member); if (ret) { DEBUG(4, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } } } if (new_group) { /* group doesn't exist, turn into adding a group */ subreq = sysdb_add_group_send(state, state->ev, state->handle, state->domain, state->name, state->gid, state->attrs); if (!subreq) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_store_group_add_done, req); return; } /* the group exists, let's just replace attributes when set */ if (!state->attrs) { state->attrs = sysdb_new_attrs(state); if (!state->attrs) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } } if (state->gid) { ret = sysdb_attrs_add_uint32(state->attrs, SYSDB_GIDNUM, state->gid); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } } ret = sysdb_attrs_add_time_t(state->attrs, SYSDB_LAST_UPDATE, now); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } ret = sysdb_attrs_add_time_t(state->attrs, SYSDB_CACHE_EXPIRE, now + state->cache_timeout); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } subreq = sysdb_set_group_attr_send(state, state->ev, state->handle, state->domain, state->name, state->attrs, SYSDB_MOD_REP); if (!subreq) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_store_group_attr_done, req); } static void sysdb_store_group_add_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); int ret; ret = sysdb_add_group_recv(subreq); talloc_zfree(subreq); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } tevent_req_done(req); } static void sysdb_store_group_attr_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); int ret; ret = sysdb_set_group_attr_recv(subreq); talloc_zfree(subreq); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } tevent_req_done(req); } int sysdb_store_group_recv(struct tevent_req *req) { return sysdb_op_default_recv(req); } /* =Add-User-to-Group(Native/Legacy)====================================== */ static void sysdb_add_group_member_done(struct tevent_req *subreq); struct tevent_req *sysdb_add_group_member_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_handle *handle, struct sss_domain_info *domain, const char *group, const char *user) { struct tevent_req *req, *subreq; struct sysdb_op_state *state; struct ldb_dn *group_dn, *user_dn; int ret; req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); if (!req) return NULL; state->ev = ev; state->handle = handle; state->ignore_not_found = false; state->ldbreply = NULL; group_dn = sysdb_group_dn(handle->ctx, state, domain->name, group); if (!group_dn) { ERROR_OUT(ret, ENOMEM, fail); } user_dn = sysdb_user_dn(handle->ctx, state, domain->name, user); if (!user_dn) { ERROR_OUT(ret, ENOMEM, fail); } subreq = sysdb_mod_group_member_send(state, ev, handle, user_dn, group_dn, SYSDB_MOD_ADD); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_add_group_member_done, req); return req; fail: DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } static void sysdb_add_group_member_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); int ret; ret = sysdb_mod_group_member_recv(subreq); talloc_zfree(subreq); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } tevent_req_done(req); } int sysdb_add_group_member_recv(struct tevent_req *req) { return sysdb_op_default_recv(req); } /* =Remove-member-from-Group(Native/Legacy)=============================== */ static void sysdb_remove_group_member_done(struct tevent_req *subreq); struct tevent_req *sysdb_remove_group_member_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_handle *handle, struct sss_domain_info *domain, const char *group, const char *user) { struct tevent_req *req, *subreq; struct sysdb_op_state *state; struct ldb_dn *group_dn, *user_dn; int ret; req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); if (!req) return NULL; state->ev = ev; state->handle = handle; state->ignore_not_found = false; state->ldbreply = NULL; group_dn = sysdb_group_dn(handle->ctx, state, domain->name, group); if (!group_dn) { ERROR_OUT(ret, ENOMEM, fail); } user_dn = sysdb_user_dn(handle->ctx, state, domain->name, user); if (!user_dn) { ERROR_OUT(ret, ENOMEM, fail); } subreq = sysdb_mod_group_member_send(state, ev, handle, user_dn, group_dn, SYSDB_MOD_DEL); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_remove_group_member_done, req); return req; fail: DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } static void sysdb_remove_group_member_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); int ret; ret = sysdb_mod_group_member_recv(subreq); talloc_zfree(subreq); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } tevent_req_done(req); } int sysdb_remove_group_member_recv(struct tevent_req *req) { return sysdb_op_default_recv(req); } /* =Password-Caching====================================================== */ struct sysdb_cache_pw_state { struct tevent_context *ev; struct sss_domain_info *domain; const char *username; struct sysdb_attrs *attrs; struct sysdb_handle *handle; bool commit; }; static void sysdb_cache_password_trans(struct tevent_req *subreq); static void sysdb_cache_password_done(struct tevent_req *subreq); struct tevent_req *sysdb_cache_password_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_ctx *sysdb, struct sysdb_handle *handle, struct sss_domain_info *domain, const char *username, const char *password) { struct tevent_req *req, *subreq; struct sysdb_cache_pw_state *state; char *hash = NULL; char *salt; int ret; req = tevent_req_create(mem_ctx, &state, struct sysdb_cache_pw_state); if (!req) return NULL; state->ev = ev; state->domain = domain; state->username = username; ret = s3crypt_gen_salt(state, &salt); if (ret) { DEBUG(4, ("Failed to generate random salt.\n")); goto fail; } ret = s3crypt_sha512(state, password, salt, &hash); if (ret) { DEBUG(4, ("Failed to create password hash.\n")); goto fail; } state->attrs = sysdb_new_attrs(state); if (!state->attrs) { ERROR_OUT(ret, ENOMEM, fail); } ret = sysdb_attrs_add_string(state->attrs, SYSDB_CACHEDPWD, hash); if (ret) goto fail; /* FIXME: should we use a different attribute for chache passwords ?? */ ret = sysdb_attrs_add_long(state->attrs, "lastCachedPasswordChange", (long)time(NULL)); if (ret) goto fail; state->handle = NULL; if (handle) { state->handle = handle; state->commit = false; subreq = sysdb_set_user_attr_send(state, state->ev, state->handle, state->domain, state->username, state->attrs, SYSDB_MOD_REP); if (!subreq) { ERROR_OUT(ret, ENOMEM, fail); } tevent_req_set_callback(subreq, sysdb_cache_password_done, req); } else { state->commit = true; subreq = sysdb_transaction_send(state, state->ev, sysdb); if (!subreq) { ret = ENOMEM; goto fail; } tevent_req_set_callback(subreq, sysdb_cache_password_trans, req); } return req; fail: DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } static void sysdb_cache_password_trans(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_cache_pw_state *state = tevent_req_data(req, struct sysdb_cache_pw_state); int ret; ret = sysdb_transaction_recv(subreq, state, &state->handle); talloc_zfree(subreq); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } subreq = sysdb_set_user_attr_send(state, state->ev, state->handle, state->domain, state->username, state->attrs, SYSDB_MOD_REP); if (!subreq) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_cache_password_done, req); } static void sysdb_cache_password_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_cache_pw_state *state = tevent_req_data(req, struct sysdb_cache_pw_state); int ret; ret = sysdb_set_user_attr_recv(subreq); talloc_zfree(subreq); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } if (state->commit) { subreq = sysdb_transaction_commit_send(state, state->ev, state->handle); if (!subreq) { DEBUG(6, ("Error: Out of memory\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_transaction_complete, req); return; } tevent_req_done(req); } int sysdb_cache_password_recv(struct tevent_req *req) { return sysdb_op_default_recv(req); } /* = sysdb_check_handle ================== */ struct sysdb_check_handle_state { struct tevent_context *ev; struct sysdb_handle *handle; }; static void sysdb_check_handle_done(struct tevent_req *subreq); struct tevent_req *sysdb_check_handle_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_ctx *sysdb, struct sysdb_handle *handle) { struct tevent_req *req; struct tevent_req *subreq; struct sysdb_check_handle_state *state; if (sysdb == NULL && handle == NULL) { DEBUG(1, ("Sysdb context not available.\n")); return NULL; } req = tevent_req_create(mem_ctx, &state, struct sysdb_check_handle_state); if (req == NULL) { DEBUG(1, ("tevent_req_create failed.\n")); return NULL; } state->ev = ev; if (handle != NULL) { state->handle = talloc_memdup(state, handle, sizeof(struct sysdb_handle)); tevent_req_done(req); tevent_req_post(req, ev); return req; } state->handle = NULL; subreq = sysdb_operation_send(state, state->ev, sysdb); if (!subreq) { DEBUG(1, ("sysdb_operation_send failed.\n")); tevent_req_error(req, ENOMEM); tevent_req_post(req, ev); return req; } tevent_req_set_callback(subreq, sysdb_check_handle_done, req); return req; } static void sysdb_check_handle_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_check_handle_state *state = tevent_req_data(req, struct sysdb_check_handle_state); int ret; ret = sysdb_operation_recv(subreq, state, &state->handle); talloc_zfree(subreq); if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } tevent_req_done(req); return; } int sysdb_check_handle_recv(struct tevent_req *req, TALLOC_CTX *memctx, struct sysdb_handle **handle) { struct sysdb_check_handle_state *state = tevent_req_data(req, struct sysdb_check_handle_state); enum tevent_req_state tstate; uint64_t err; if (tevent_req_is_error(req, &tstate, &err)) { return err; } *handle = talloc_move(memctx, &state->handle); return EOK; } /* =Custom Search================== */ struct sysdb_search_custom_state { struct tevent_context *ev; struct sysdb_handle *handle; struct ldb_dn *basedn; const char **attrs; const char *filter; int scope; struct ldb_message *msg; }; static void sysdb_search_custom_check_handle_done(struct tevent_req *subreq); static void sysdb_search_custom_done(struct tevent_req *subreq); struct tevent_req *sysdb_search_custom_by_name_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_ctx *sysdb, struct sysdb_handle *handle, struct sss_domain_info *domain, const char *object_name, const char *subtree_name, const char **attrs) { struct tevent_req *req, *subreq; struct sysdb_search_custom_state *state; int ret; if (sysdb == NULL && handle == NULL) return NULL; if (object_name == NULL || subtree_name == NULL) return NULL; req = tevent_req_create(mem_ctx, &state, struct sysdb_search_custom_state); if (req == NULL) { DEBUG(1, ("tevent_req_create failed.\n")); return NULL; } state->ev = ev; state->handle = handle; state->attrs = attrs; state->filter = NULL; state->scope = LDB_SCOPE_BASE; state->msg = NULL; if (sysdb == NULL) { sysdb = handle->ctx; } state->basedn = sysdb_custom_dn(sysdb, state, domain->name, object_name, subtree_name); if (state->basedn == NULL) { DEBUG(1, ("sysdb_custom_dn failed.\n")); ret = ENOMEM; goto fail; } if (!ldb_dn_validate(state->basedn)) { DEBUG(1, ("Failed to create DN.\n")); ret = EINVAL; goto fail; } subreq = sysdb_check_handle_send(state, state->ev, sysdb, state->handle); if (!subreq) { DEBUG(1, ("sysdb_check_handle_send failed.\n")); ret = ENOMEM; goto fail; } tevent_req_set_callback(subreq, sysdb_search_custom_check_handle_done, req); return req; fail: tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } static void sysdb_search_custom_check_handle_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_search_custom_state *state = tevent_req_data(req, struct sysdb_search_custom_state); int ret; ret = sysdb_check_handle_recv(subreq, state, &state->handle); talloc_zfree(subreq); if (ret != EOK) { tevent_req_error(req, ret); return; } subreq = sysdb_search_entry_send(state, state->ev, state->handle, state->basedn, state->scope, state->filter, state->attrs); if (!subreq) { DEBUG(1, ("sysdb_search_entry_send failed.\n")); tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_search_custom_done, req); return; } static void sysdb_search_custom_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_search_custom_state *state = tevent_req_data(req, struct sysdb_search_custom_state); int ret; ret = sysdb_search_entry_recv(subreq, state, &state->msg); talloc_zfree(subreq); if (ret) { tevent_req_error(req, ret); return; } tevent_req_done(req); } int sysdb_search_custom_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, struct ldb_message **msg) { struct sysdb_search_custom_state *state = tevent_req_data(req, struct sysdb_search_custom_state); enum tevent_req_state tstate; uint64_t err; if (tevent_req_is_error(req, &tstate, &err)) { return err; } *msg = talloc_move(mem_ctx, &state->msg); return EOK; } /* =Custom Store (replaces-existing-data)================== */ struct sysdb_store_custom_state { struct tevent_context *ev; struct sysdb_handle *handle; struct sss_domain_info *domain; const char *object_name; const char *subtree_name; struct ldb_dn *dn; struct sysdb_attrs *attrs; struct ldb_message *msg; }; static void sysdb_store_custom_check_done(struct tevent_req *subreq); static void sysdb_store_custom_done(struct tevent_req *subreq); struct tevent_req *sysdb_store_custom_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_handle *handle, struct sss_domain_info *domain, const char *object_name, const char *subtree_name, struct sysdb_attrs *attrs) { struct tevent_req *req, *subreq; struct sysdb_store_custom_state *state; int ret; const char **search_attrs; if (object_name == NULL || subtree_name == NULL) return NULL; if (handle == NULL) { DEBUG(1, ("Sysdb context not available.\n")); return NULL; } req = tevent_req_create(mem_ctx, &state, struct sysdb_store_custom_state); if (req == NULL) { DEBUG(1, ("tevent_req_create failed.\n")); return NULL; } state->ev = ev; state->handle = handle; state->domain = domain; state->object_name = object_name; state->subtree_name = subtree_name; state->attrs = attrs; state->msg = NULL; state->dn = sysdb_custom_dn(handle->ctx, state, domain->name, object_name, subtree_name); if (state->dn == NULL) { DEBUG(1, ("sysdb_custom_dn failed.\n")); ret = ENOMEM; goto fail; } search_attrs = talloc_array(state, const char *, 2); if (search_attrs == NULL) { DEBUG(1, ("talloc_array failed.\n")); ret = ENOMEM; goto fail; } search_attrs[0] = "*"; search_attrs[1] = NULL; subreq = sysdb_search_custom_by_name_send(state, state->ev, NULL, state->handle, state->domain, state->object_name, state->subtree_name, search_attrs); if (!subreq) { DEBUG(1, ("sysdb_search_custom_by_name_send failed.\n")); ret = ENOMEM; goto fail; } tevent_req_set_callback(subreq, sysdb_store_custom_check_done, req); return req; fail: DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } static void sysdb_store_custom_check_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_store_custom_state *state = tevent_req_data(req, struct sysdb_store_custom_state); int ret; int i; struct ldb_message *resp; struct ldb_message *msg; struct ldb_request *ldbreq; struct ldb_message_element *el; bool add_object = false; ret = sysdb_search_custom_recv(subreq, state, &resp); talloc_zfree(subreq); if (ret != EOK && ret != ENOENT) { tevent_req_error(req, ret); return; } if (ret == ENOENT) { add_object = true; } msg = ldb_msg_new(state); if (msg == NULL) { tevent_req_error(req, ENOMEM); return; } msg->dn = state->dn; msg->elements = talloc_array(msg, struct ldb_message_element, state->attrs->num); if (!msg->elements) { tevent_req_error(req, ENOMEM); return; } for (i = 0; i < state->attrs->num; i++) { msg->elements[i] = state->attrs->a[i]; if (add_object) { msg->elements[i].flags = LDB_FLAG_MOD_ADD; } else { el = ldb_msg_find_element(resp, state->attrs->a[i].name); if (el == NULL) { msg->elements[i].flags = LDB_FLAG_MOD_ADD; } else { msg->elements[i].flags = LDB_FLAG_MOD_REPLACE; } } } msg->num_elements = state->attrs->num; if (add_object) { ret = ldb_build_add_req(&ldbreq, state->handle->ctx->ldb, state, msg, NULL, NULL, NULL, NULL); } else { ret = ldb_build_mod_req(&ldbreq, state->handle->ctx->ldb, state, msg, NULL, NULL, NULL, NULL); } if (ret != LDB_SUCCESS) { DEBUG(1, ("Failed to build request: %s(%d)[%s]\n", ldb_strerror(ret), ret, ldb_errstring(state->handle->ctx->ldb))); tevent_req_error(req, sysdb_error_to_errno(ret)); return; } subreq = sldb_request_send(state, state->ev, state->handle->ctx->ldb, ldbreq); if (!subreq) { tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, sysdb_store_custom_done, req); return; } static void sysdb_store_custom_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); int ret; ret = sysdb_op_default_recv(subreq); talloc_zfree(subreq); if (ret != EOK) { tevent_req_error(req, ret); return; } tevent_req_done(req); return; } int sysdb_store_custom_recv(struct tevent_req *req) { enum tevent_req_state tstate; uint64_t err; if (tevent_req_is_error(req, &tstate, &err)) { return err; } return EOK; } /* = Custom Delete======================================= */ struct sysdb_delete_custom_state { struct tevent_context *ev; struct sysdb_handle *handle; struct sss_domain_info *domain; const char *object_name; const char *subtree_name; struct ldb_dn *dn; }; static void sysdb_delete_custom_done(struct tevent_req *subreq); struct tevent_req *sysdb_delete_custom_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_handle *handle, struct sss_domain_info *domain, const char *object_name, const char *subtree_name) { struct tevent_req *req, *subreq; struct sysdb_delete_custom_state *state; int ret; if (object_name == NULL || subtree_name == NULL) return NULL; if (handle == NULL) { DEBUG(1, ("Sysdb context not available.\n")); return NULL; } req = tevent_req_create(mem_ctx, &state, struct sysdb_store_custom_state); if (req == NULL) { DEBUG(1, ("tevent_req_create failed.\n")); return NULL; } state->ev = ev; state->handle = handle; state->domain = domain; state->object_name = object_name; state->subtree_name = subtree_name; state->dn = sysdb_custom_dn(handle->ctx, state, domain->name, object_name, subtree_name); if (state->dn == NULL) { DEBUG(1, ("sysdb_custom_dn failed.\n")); ret = ENOMEM; goto fail; } subreq = sysdb_delete_entry_send(state, state->ev, state->handle, state->dn, true); if (!subreq) { DEBUG(1, ("sysdb_delete_entry_send failed.\n")); ret = ENOMEM; goto fail; } tevent_req_set_callback(subreq, sysdb_delete_custom_done, req); return req; fail: DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } static void sysdb_delete_custom_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); int ret; ret = sysdb_delete_entry_recv(subreq); talloc_zfree(subreq); if (ret != EOK) { tevent_req_error(req, ret); return; } tevent_req_done(req); return; } int sysdb_delete_custom_recv(struct tevent_req *req) { enum tevent_req_state tstate; uint64_t err; if (tevent_req_is_error(req, &tstate, &err)) { return err; } return EOK; } /* = ASQ search request ======================================== */ struct sysdb_asq_search_state { struct tevent_context *ev; struct sysdb_ctx *sysdb; struct sysdb_handle *handle; struct sss_domain_info *domain; struct ldb_dn *base_dn; const char *asq_attribute; const char **attrs; const char *expression; int msgs_count; struct ldb_message **msgs; }; void sysdb_asq_search_check_handle_done(struct tevent_req *subreq); static void sysdb_asq_search_done(struct tevent_req *subreq); struct tevent_req *sysdb_asq_search_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_ctx *sysdb, struct sysdb_handle *handle, struct sss_domain_info *domain, struct ldb_dn *base_dn, const char *expression, const char *asq_attribute, const char **attrs) { struct tevent_req *req; struct tevent_req *subreq; struct sysdb_asq_search_state *state; int ret; if (sysdb == NULL && handle == NULL) { DEBUG(1, ("Sysdb context not available.\n")); return NULL; } req = tevent_req_create(mem_ctx, &state, struct sysdb_asq_search_state); if (req == NULL) { DEBUG(1, ("tevent_req_create failed.\n")); return NULL; } state->ev = ev; state->sysdb = (sysdb == NULL) ? handle->ctx : sysdb; state->handle = handle; state->domain = domain; state->base_dn = base_dn; state->expression = expression; state->asq_attribute = asq_attribute; state->attrs = attrs; state->msgs_count = 0; state->msgs = NULL; subreq = sysdb_check_handle_send(state, state->ev, state->sysdb, state->handle); if (!subreq) { DEBUG(1, ("sysdb_check_handle_send failed.\n")); ret = ENOMEM; goto fail; } tevent_req_set_callback(subreq, sysdb_asq_search_check_handle_done, req); return req; fail: tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } void sysdb_asq_search_check_handle_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_asq_search_state *state = tevent_req_data(req, struct sysdb_asq_search_state); struct ldb_request *ldb_req; struct ldb_control **ctrl; struct ldb_asq_control *asq_control; int ret; ret = sysdb_check_handle_recv(subreq, state, &state->handle); talloc_zfree(subreq); if (ret != EOK) { tevent_req_error(req, ret); return; } ctrl = talloc_array(state, struct ldb_control *, 2); if (ctrl == NULL) { ret = ENOMEM; goto fail; } ctrl[0] = talloc(ctrl, struct ldb_control); if (ctrl[0] == NULL) { ret = ENOMEM; goto fail; } ctrl[1] = NULL; ctrl[0]->oid = LDB_CONTROL_ASQ_OID; ctrl[0]->critical = 1; asq_control = talloc(ctrl[0], struct ldb_asq_control); if (asq_control == NULL) { ret = ENOMEM; goto fail; } asq_control->request = 1; asq_control->source_attribute = talloc_strdup(asq_control, state->asq_attribute); if (asq_control->source_attribute == NULL) { ret = ENOMEM; goto fail; } asq_control->src_attr_len = strlen(asq_control->source_attribute); ctrl[0]->data = asq_control; ret = ldb_build_search_req(&ldb_req, state->handle->ctx->ldb, state, state->base_dn, LDB_SCOPE_BASE, state->expression, state->attrs, ctrl, NULL, NULL, NULL); if (ret != LDB_SUCCESS) { ret = sysdb_error_to_errno(ret); goto fail; } subreq = sldb_request_send(state, state->ev, state->handle->ctx->ldb, ldb_req); if (!subreq) { ret = ENOMEM; goto fail; } tevent_req_set_callback(subreq, sysdb_asq_search_done, req); return; fail: tevent_req_error(req, ret); return; } static void sysdb_asq_search_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sysdb_asq_search_state *state = tevent_req_data(req, struct sysdb_asq_search_state); struct ldb_reply *ldbreply; int ret; ret = sldb_request_recv(subreq, state, &ldbreply); if (ret != EOK) { talloc_free(subreq); DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); tevent_req_error(req, ret); return; } switch (ldbreply->type) { case LDB_REPLY_ENTRY: state->msgs = talloc_realloc(state, state->msgs, struct ldb_message *, state->msgs_count + 2); if (state->msgs == NULL) { tevent_req_error(req, ENOMEM); return; } state->msgs[state->msgs_count + 1] = NULL; state->msgs[state->msgs_count] = talloc_steal(state->msgs, ldbreply->message); state->msgs_count++; talloc_zfree(ldbreply); return; case LDB_REPLY_DONE: break; default: DEBUG(1, ("Unknown ldb reply type [%d].\n", ldbreply->type)); tevent_req_error(req, EINVAL); return; } talloc_zfree(subreq); tevent_req_done(req); } int sysdb_asq_search_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, size_t *msgs_count, struct ldb_message ***msgs) { struct sysdb_asq_search_state *state = tevent_req_data(req, struct sysdb_asq_search_state); enum tevent_req_state tstate; uint64_t err; int i; if (tevent_req_is_error(req, &tstate, &err)) { return err; } *msgs_count = state->msgs_count; for (i = 0; i < state->msgs_count; i++) { talloc_steal(mem_ctx, state->msgs[i]); } *msgs = talloc_move(mem_ctx, &state->msgs); return EOK; }