From 1c48b5a62f73234ed26bb20f0ab345ab61cda0ab Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Thu, 18 Feb 2010 07:49:04 -0500 Subject: Rename server/ directory to src/ Also update BUILD.txt --- src/db/sysdb.c | 1883 ++++++++++++++++++ src/db/sysdb.h | 650 +++++++ src/db/sysdb_ops.c | 5059 ++++++++++++++++++++++++++++++++++++++++++++++++ src/db/sysdb_private.h | 107 + src/db/sysdb_search.c | 691 +++++++ 5 files changed, 8390 insertions(+) create mode 100644 src/db/sysdb.c create mode 100644 src/db/sysdb.h create mode 100644 src/db/sysdb_ops.c create mode 100644 src/db/sysdb_private.h create mode 100644 src/db/sysdb_search.c (limited to 'src/db') diff --git a/src/db/sysdb.c b/src/db/sysdb.c new file mode 100644 index 00000000..b3f81a08 --- /dev/null +++ b/src/db/sysdb.c @@ -0,0 +1,1883 @@ +/* + 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 "confdb/confdb.h" +#include + + +struct ldb_dn *sysdb_custom_subtree_dn(struct sysdb_ctx *ctx, void *memctx, + const char *domain, + const char *subtree_name) +{ + return ldb_dn_new_fmt(memctx, ctx->ldb, SYSDB_TMPL_CUSTOM_SUBTREE, + subtree_name, domain); +} +struct ldb_dn *sysdb_custom_dn(struct sysdb_ctx *ctx, void *memctx, + const char *domain, const char *object_name, + const char *subtree_name) +{ + return ldb_dn_new_fmt(memctx, ctx->ldb, SYSDB_TMPL_CUSTOM, object_name, + subtree_name, domain); +} + +struct ldb_dn *sysdb_user_dn(struct sysdb_ctx *ctx, void *memctx, + const char *domain, const char *name) +{ + return ldb_dn_new_fmt(memctx, ctx->ldb, SYSDB_TMPL_USER, name, domain); +} + +struct ldb_dn *sysdb_group_dn(struct sysdb_ctx *ctx, void *memctx, + const char *domain, const char *name) +{ + return ldb_dn_new_fmt(memctx, ctx->ldb, SYSDB_TMPL_GROUP, name, domain); +} + +struct ldb_dn *sysdb_domain_dn(struct sysdb_ctx *ctx, void *memctx, + const char *domain) +{ + return ldb_dn_new_fmt(memctx, ctx->ldb, SYSDB_DOM_BASE, domain); +} + +struct ldb_context *sysdb_ctx_get_ldb(struct sysdb_ctx *ctx) +{ + return ctx->ldb; +} + +struct ldb_context *sysdb_handle_get_ldb(struct sysdb_handle *handle) +{ + return handle->ctx->ldb; +} + +struct sysdb_ctx *sysdb_handle_get_ctx(struct sysdb_handle *handle) +{ + return handle->ctx; +} + +struct sysdb_attrs *sysdb_new_attrs(TALLOC_CTX *memctx) +{ + return talloc_zero(memctx, struct sysdb_attrs); +} + +static int sysdb_attrs_get_el_int(struct sysdb_attrs *attrs, const char *name, + bool alloc, struct ldb_message_element **el) +{ + struct ldb_message_element *e = NULL; + int i; + + for (i = 0; i < attrs->num; i++) { + if (strcasecmp(name, attrs->a[i].name) == 0) + e = &(attrs->a[i]); + } + + if (!e && alloc) { + e = talloc_realloc(attrs, attrs->a, + struct ldb_message_element, attrs->num+1); + if (!e) return ENOMEM; + attrs->a = e; + + e[attrs->num].name = talloc_strdup(e, name); + if (!e[attrs->num].name) return ENOMEM; + + e[attrs->num].num_values = 0; + e[attrs->num].values = NULL; + e[attrs->num].flags = 0; + + e = &(attrs->a[attrs->num]); + attrs->num++; + } + + if (!e) { + return ENOENT; + } + + *el = e; + + return EOK; +} + +int sysdb_attrs_get_el(struct sysdb_attrs *attrs, const char *name, + struct ldb_message_element **el) +{ + return sysdb_attrs_get_el_int(attrs, name, true, el); +} + +int sysdb_attrs_get_string(struct sysdb_attrs *attrs, const char *name, + const char **string) +{ + struct ldb_message_element *el; + int ret; + + ret = sysdb_attrs_get_el_int(attrs, name, false, &el); + if (ret) { + return ret; + } + + if (el->num_values != 1) { + return ERANGE; + } + + *string = (const char *)el->values[0].data; + return EOK; +} + +int sysdb_attrs_add_val(struct sysdb_attrs *attrs, + const char *name, const struct ldb_val *val) +{ + struct ldb_message_element *el = NULL; + struct ldb_val *vals; + int ret; + + ret = sysdb_attrs_get_el(attrs, name, &el); + + vals = talloc_realloc(attrs->a, el->values, + struct ldb_val, el->num_values+1); + if (!vals) return ENOMEM; + + vals[el->num_values] = ldb_val_dup(vals, val); + if (vals[el->num_values].data == NULL && + vals[el->num_values].length != 0) { + return ENOMEM; + } + + el->values = vals; + el->num_values++; + + return EOK; +} + +int sysdb_attrs_add_string(struct sysdb_attrs *attrs, + const char *name, const char *str) +{ + struct ldb_val v; + + v.data = (uint8_t *)discard_const(str); + v.length = strlen(str); + + return sysdb_attrs_add_val(attrs, name, &v); +} + +int sysdb_attrs_steal_string(struct sysdb_attrs *attrs, + const char *name, char *str) +{ + struct ldb_message_element *el = NULL; + struct ldb_val *vals; + int ret; + + ret = sysdb_attrs_get_el(attrs, name, &el); + + vals = talloc_realloc(attrs->a, el->values, + struct ldb_val, el->num_values+1); + if (!vals) return ENOMEM; + el->values = vals; + + /* now steal and assign the string */ + talloc_steal(el->values, str); + + el->values[el->num_values].data = (uint8_t *)str; + el->values[el->num_values].length = strlen(str); + el->num_values++; + + return EOK; +} + +int sysdb_attrs_add_long(struct sysdb_attrs *attrs, + const char *name, long value) +{ + struct ldb_val v; + char *str; + int ret; + + str = talloc_asprintf(attrs, "%ld", value); + if (!str) return ENOMEM; + + v.data = (uint8_t *)str; + v.length = strlen(str); + + ret = sysdb_attrs_add_val(attrs, name, &v); + talloc_free(str); + + return ret; +} + +int sysdb_attrs_add_uint32(struct sysdb_attrs *attrs, + const char *name, uint32_t value) +{ + unsigned long val = value; + struct ldb_val v; + char *str; + int ret; + + str = talloc_asprintf(attrs, "%lu", val); + if (!str) return ENOMEM; + + v.data = (uint8_t *)str; + v.length = strlen(str); + + ret = sysdb_attrs_add_val(attrs, name, &v); + talloc_free(str); + + return ret; +} + +int sysdb_attrs_add_time_t(struct sysdb_attrs *attrs, + const char *name, time_t value) +{ + long long val = value; + struct ldb_val v; + char *str; + int ret; + + str = talloc_asprintf(attrs, "%lld", val); + if (!str) return ENOMEM; + + v.data = (uint8_t *)str; + v.length = strlen(str); + + ret = sysdb_attrs_add_val(attrs, name, &v); + talloc_free(str); + + return ret; +} + +int sysdb_attrs_users_from_str_list(struct sysdb_attrs *attrs, + const char *attr_name, + const char *domain, + const char **list) +{ + struct ldb_message_element *el = NULL; + struct ldb_val *vals; + int i, j, num; + char *member; + int ret; + + ret = sysdb_attrs_get_el(attrs, attr_name, &el); + if (ret) { + return ret; + } + + for (num = 0; list[num]; num++) /* count */ ; + + vals = talloc_realloc(attrs->a, el->values, + struct ldb_val, el->num_values + num); + if (!vals) { + return ENOMEM; + } + el->values = vals; + + DEBUG(9, ("Adding %d members to existing %d ones\n", + num, el->num_values)); + + for (i = 0, j = el->num_values; i < num; i++) { + + member = sysdb_user_strdn(el->values, domain, list[i]); + if (!member) { + DEBUG(4, ("Failed to get user dn for [%s]\n", list[i])); + continue; + } + el->values[j].data = (uint8_t *)member; + el->values[j].length = strlen(member); + j++; + + DEBUG(7, (" member #%d: [%s]\n", i, member)); + } + el->num_values = j; + + return EOK; +} + +int sysdb_attrs_users_from_ldb_vals(struct sysdb_attrs *attrs, + const char *attr_name, + const char *domain, + struct ldb_val *values, + int num_values) +{ + struct ldb_message_element *el = NULL; + struct ldb_val *vals; + int i, j; + char *member; + int ret; + + ret = sysdb_attrs_get_el(attrs, attr_name, &el); + if (ret) { + return ret; + } + + vals = talloc_realloc(attrs->a, el->values, struct ldb_val, + el->num_values + num_values); + if (!vals) { + return ENOMEM; + } + el->values = vals; + + DEBUG(9, ("Adding %d members to existing %d ones\n", + num_values, el->num_values)); + + for (i = 0, j = el->num_values; i < num_values; i++) { + member = sysdb_user_strdn(el->values, domain, + (char *)values[i].data); + if (!member) { + DEBUG(4, ("Failed to get user dn for [%s]\n", + (char *)values[i].data)); + return ENOMEM; + } + el->values[j].data = (uint8_t *)member; + el->values[j].length = strlen(member); + j++; + + DEBUG(7, (" member #%d: [%s]\n", i, member)); + } + el->num_values = j; + + return EOK; +} + +static char *build_dom_dn_str_escape(TALLOC_CTX *memctx, const char *template, + const char *domain, const char *name) +{ + char *ret; + int l; + + l = strcspn(name, ",=\n+<>#;\\\""); + if (name[l] != '\0') { + struct ldb_val v; + char *tmp; + + v.data = discard_const_p(uint8_t, name); + v.length = strlen(name); + + tmp = ldb_dn_escape_value(memctx, v); + if (!tmp) { + return NULL; + } + + ret = talloc_asprintf(memctx, template, tmp, domain); + talloc_zfree(tmp); + if (!ret) { + return NULL; + } + + return ret; + } + + ret = talloc_asprintf(memctx, template, name, domain); + if (!ret) { + return NULL; + } + + return ret; +} + +char *sysdb_user_strdn(TALLOC_CTX *memctx, + const char *domain, const char *name) +{ + return build_dom_dn_str_escape(memctx, SYSDB_TMPL_USER, domain, name); +} + +char *sysdb_group_strdn(TALLOC_CTX *memctx, + const char *domain, const char *name) +{ + return build_dom_dn_str_escape(memctx, SYSDB_TMPL_GROUP, domain, name); +} + +/* TODO: make a more complete and precise mapping */ +int sysdb_error_to_errno(int ldberr) +{ + switch (ldberr) { + case LDB_SUCCESS: + return EOK; + case LDB_ERR_OPERATIONS_ERROR: + return EIO; + case LDB_ERR_NO_SUCH_OBJECT: + return ENOENT; + case LDB_ERR_BUSY: + return EBUSY; + case LDB_ERR_ENTRY_ALREADY_EXISTS: + return EEXIST; + default: + return EFAULT; + } +} + +/* =Internal-Operations-Queue============================================= */ + +static void sysdb_run_operation(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval tv, void *pvt) +{ + struct sysdb_handle *handle = talloc_get_type(pvt, struct sysdb_handle); + + tevent_req_done(handle->subreq); +} + +static void sysdb_schedule_operation(struct sysdb_handle *handle) +{ + struct timeval tv = { 0, 0 }; + struct tevent_timer *te; + + te = tevent_add_timer(handle->ctx->ev, handle, tv, + sysdb_run_operation, handle); + if (!te) { + DEBUG(1, ("Failed to add critical timer to run next handle!\n")); + } +} + +static int sysdb_handle_destructor(void *mem) +{ + struct sysdb_handle *handle = talloc_get_type(mem, struct sysdb_handle); + bool start_next = false; + int ret; + + /* if this was the current op start next */ + if (handle->ctx->queue == handle) { + start_next = true; + } + + DLIST_REMOVE(handle->ctx->queue, handle); + + if (start_next && handle->ctx->queue) { + /* run next */ + sysdb_schedule_operation(handle->ctx->queue); + } + + if (handle->transaction_active) { + ret = ldb_transaction_cancel(handle->ctx->ldb); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to cancel ldb transaction! (%d)\n", ret)); + } + /* FIXME: abort() ? */ + handle->transaction_active = false; + } + + return 0; +} + +struct sysdb_get_handle_state { + struct tevent_context *ev; + struct sysdb_ctx *ctx; + + struct sysdb_handle *handle; +}; + +struct tevent_req *sysdb_get_handle_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *ctx) +{ + struct tevent_req *req; + struct sysdb_get_handle_state *state; + struct sysdb_handle *handle; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_get_handle_state); + if (!req) return NULL; + + state->ev = ev; + state->ctx = ctx; + + handle = talloc_zero(state, struct sysdb_handle); + if (!handle) { + talloc_zfree(req); + return NULL; + } + + handle->ctx = ctx; + handle->subreq = req; + + talloc_set_destructor((TALLOC_CTX *)handle, sysdb_handle_destructor); + + DLIST_ADD_END(ctx->queue, handle, struct sysdb_handle *); + + if (ctx->queue == handle) { + /* this is the first in the queue, schedule an immediate run */ + sysdb_schedule_operation(handle); + } + + state->handle = handle; + + return req; +} + +static int sysdb_get_handle_recv(struct tevent_req *req, TALLOC_CTX *memctx, + struct sysdb_handle **handle) +{ + struct sysdb_get_handle_state *state = tevent_req_data(req, + struct sysdb_get_handle_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *handle = talloc_steal(memctx, state->handle); + if (!*handle) return ENOMEM; + + return EOK; +} + +/* =Transactions========================================================== */ + +struct sysdb_transaction_state { + struct tevent_context *ev; + struct sysdb_ctx *ctx; + + struct sysdb_handle *handle; +}; + +static void sysdb_transaction_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_transaction_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *ctx) +{ + struct tevent_req *req, *subreq; + struct sysdb_transaction_state *state; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_transaction_state); + if (!req) return NULL; + + state->ev = ev; + state->ctx = ctx; + + subreq = sysdb_get_handle_send(state, ev, ctx); + if (!subreq) { + talloc_zfree(req); + return NULL; + } + + tevent_req_set_callback(subreq, sysdb_transaction_done, req); + + return req; +} + +static void sysdb_transaction_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_transaction_state *state = tevent_req_data(req, + struct sysdb_transaction_state); + int ret; + + ret = sysdb_get_handle_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + ret = ldb_transaction_start(state->ctx->ldb); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to start ldb transaction! (%d)\n", ret)); + tevent_req_error(req, sysdb_error_to_errno(ret)); + return; + } + state->handle->transaction_active = true; + + tevent_req_done(req); +} + +int sysdb_transaction_recv(struct tevent_req *req, TALLOC_CTX *memctx, + struct sysdb_handle **handle) +{ + struct sysdb_transaction_state *state = tevent_req_data(req, + struct sysdb_transaction_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *handle = talloc_steal(memctx, state->handle); + if (!*handle) return ENOMEM; + + return EOK; +} + +struct tevent_req *sysdb_transaction_commit_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle) +{ + struct tevent_req *req; + struct sysdb_transaction_state *state; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_transaction_state); + if (!req) return NULL; + + state->ev = ev; + state->ctx = handle->ctx; + state->handle = handle; + + ret = ldb_transaction_commit(handle->ctx->ldb); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to commit ldb transaction! (%d)\n", ret)); + tevent_req_error(req, sysdb_error_to_errno(ret)); + } + handle->transaction_active = false; + + /* the following may seem weird but it is actually fine. + * _done() will not actually call the callback as it will not be set + * until we return. But it will mark the request as done. + * _post() will trigger the callback as it schedules after we returned + * and actually set the callback */ + tevent_req_done(req); + tevent_req_post(req, ev); + return req; +} + +int sysdb_transaction_commit_recv(struct tevent_req *req) +{ + struct sysdb_transaction_state *state = tevent_req_data(req, + struct sysdb_transaction_state); + + /* finally free handle + * this will also trigger the next transaction in the queue if any */ + talloc_zfree(state->handle); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +/* default transaction commit receive function. + * This function does not use the request state so it is safe to use + * from any caller */ +void sysdb_transaction_complete(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_transaction_commit_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + + +/* =Operations============================================================ */ + +struct sysdb_operation_state { + struct tevent_context *ev; + struct sysdb_ctx *ctx; + + struct sysdb_handle *handle; +}; + +static void sysdb_operation_process(struct tevent_req *subreq); + +struct tevent_req *sysdb_operation_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *ctx) +{ + struct tevent_req *req, *subreq; + struct sysdb_operation_state *state; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_operation_state); + if (!req) return NULL; + + state->ev = ev; + state->ctx = ctx; + + subreq = sysdb_get_handle_send(state, ev, ctx); + if (!subreq) { + talloc_zfree(req); + return NULL; + } + + tevent_req_set_callback(subreq, sysdb_operation_process, req); + + return req; +} + +static void sysdb_operation_process(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_operation_state *state = tevent_req_data(req, + struct sysdb_operation_state); + int ret; + + ret = sysdb_get_handle_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_operation_recv(struct tevent_req *req, TALLOC_CTX *memctx, + struct sysdb_handle **handle) +{ + struct sysdb_operation_state *state = tevent_req_data(req, + struct sysdb_operation_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *handle = talloc_steal(memctx, state->handle); + if (!*handle) return ENOMEM; + + return EOK; +} + +void sysdb_operation_done(struct sysdb_handle *handle) +{ + talloc_free(handle); +} + +/* =Initialization======================================================== */ + +static int sysdb_domain_init_internal(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sss_domain_info *domain, + const char *db_path, + bool allow_upgrade, + struct sysdb_ctx **_ctx); + +static int sysdb_get_db_file(TALLOC_CTX *mem_ctx, + const char *provider, const char *name, + const char *base_path, char **_ldb_file) +{ + char *ldb_file; + + /* special case for the local domain */ + if (strcasecmp(provider, "local") == 0) { + ldb_file = talloc_asprintf(mem_ctx, "%s/"LOCAL_SYSDB_FILE, + base_path); + } else { + ldb_file = talloc_asprintf(mem_ctx, "%s/"CACHE_SYSDB_FILE, + base_path, name); + } + if (!ldb_file) { + return ENOMEM; + } + + *_ldb_file = ldb_file; + return EOK; +} + +/* serach all groups that have a memberUid attribute. + * change it into a member attribute for a user of same domain. + * remove the memberUid attribute + * add the new member attribute + * finally stop indexing memberUid + * upgrade version to 0.2 + */ +static int sysdb_upgrade_01(TALLOC_CTX *mem_ctx, + struct ldb_context *ldb, + const char **ver) +{ + struct ldb_message_element *el; + struct ldb_result *res; + struct ldb_dn *basedn; + struct ldb_dn *mem_dn; + struct ldb_message *msg; + const struct ldb_val *val; + const char *filter = "(&(memberUid=*)(objectclass=group))"; + const char *attrs[] = { "memberUid", NULL }; + const char *mdn; + char *domain; + int ret, i, j; + + basedn = ldb_dn_new(mem_ctx, ldb, "cn=sysdb"); + if (!basedn) { + ret = EIO; + goto done; + } + + ret = ldb_search(ldb, mem_ctx, &res, + basedn, LDB_SCOPE_SUBTREE, + attrs, filter); + if (ret != LDB_SUCCESS) { + ret = EIO; + goto done; + } + + ret = ldb_transaction_start(ldb); + if (ret != LDB_SUCCESS) { + ret = EIO; + goto done; + } + + for (i = 0; i < res->count; i++) { + el = ldb_msg_find_element(res->msgs[i], "memberUid"); + if (!el) { + DEBUG(1, ("memberUid is missing from message [%s], skipping\n", + ldb_dn_get_linearized(res->msgs[i]->dn))); + continue; + } + + /* create modification message */ + msg = ldb_msg_new(mem_ctx); + if (!msg) { + ret = ENOMEM; + goto done; + } + msg->dn = res->msgs[i]->dn; + + ret = ldb_msg_add_empty(msg, "memberUid", LDB_FLAG_MOD_DELETE, NULL); + if (ret != LDB_SUCCESS) { + ret = ENOMEM; + goto done; + } + + ret = ldb_msg_add_empty(msg, SYSDB_MEMBER, LDB_FLAG_MOD_ADD, NULL); + if (ret != LDB_SUCCESS) { + ret = ENOMEM; + goto done; + } + + /* get domain name component value */ + val = ldb_dn_get_component_val(res->msgs[i]->dn, 2); + domain = talloc_strndup(mem_ctx, (const char *)val->data, val->length); + if (!domain) { + ret = ENOMEM; + goto done; + } + + for (j = 0; j < el->num_values; j++) { + mem_dn = ldb_dn_new_fmt(mem_ctx, ldb, SYSDB_TMPL_USER, + (const char *)el->values[j].data, domain); + if (!mem_dn) { + ret = ENOMEM; + goto done; + } + + mdn = talloc_strdup(msg, ldb_dn_get_linearized(mem_dn)); + if (!mdn) { + ret = ENOMEM; + goto done; + } + ret = ldb_msg_add_string(msg, SYSDB_MEMBER, mdn); + if (ret != LDB_SUCCESS) { + ret = ENOMEM; + goto done; + } + + talloc_zfree(mem_dn); + } + + /* ok now we are ready to modify the entry */ + ret = ldb_modify(ldb, msg); + if (ret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(ret); + goto done; + } + + talloc_zfree(msg); + } + + /* conversion done, upgrade version number */ + msg = ldb_msg_new(mem_ctx); + if (!msg) { + ret = ENOMEM; + goto done; + } + msg->dn = ldb_dn_new(mem_ctx, ldb, "cn=sysdb"); + if (!msg->dn) { + ret = ENOMEM; + goto done; + } + + ret = ldb_msg_add_empty(msg, "version", LDB_FLAG_MOD_REPLACE, NULL); + if (ret != LDB_SUCCESS) { + ret = ENOMEM; + goto done; + } + ret = ldb_msg_add_string(msg, "version", SYSDB_VERSION_0_2); + if (ret != LDB_SUCCESS) { + ret = ENOMEM; + goto done; + } + + ret = ldb_modify(ldb, msg); + if (ret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(ret); + goto done; + } + + ret = EOK; + +done: + if (ret != EOK) { + ldb_transaction_cancel(ldb); + } else { + ret = ldb_transaction_commit(ldb); + if (ret != LDB_SUCCESS) { + return EIO; + } + + *ver = SYSDB_VERSION_0_2; + } + + return ret; +} + +static int sysdb_check_upgrade_02(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sss_domain_info *domains, + const char *db_path) +{ + TALLOC_CTX *tmp_ctx = NULL; + struct ldb_context *ldb; + char *ldb_file; + struct sysdb_ctx *ctx; + struct sss_domain_info *dom; + struct ldb_message_element *el; + struct ldb_message *msg; + struct ldb_result *res; + struct ldb_dn *verdn; + const char *version = NULL; + bool do_02_upgrade = false; + bool ctx_trans = false; + int ret; + + tmp_ctx = talloc_new(mem_ctx); + if (!tmp_ctx) { + return ENOMEM; + } + + ret = sysdb_get_db_file(mem_ctx, + "local", "UPGRADE", + db_path, &ldb_file); + if (ret != EOK) { + goto exit; + } + + ldb = ldb_init(tmp_ctx, ev); + if (!ldb) { + ret = EIO; + goto exit; + } + + ret = ldb_set_debug(ldb, ldb_debug_messages, NULL); + if (ret != LDB_SUCCESS) { + ret = EIO; + goto exit; + } + +#ifdef SYSDB_TEST + ldb_set_modules_dir(ldb, "./.libs"); +#endif + + ret = ldb_connect(ldb, ldb_file, 0, NULL); + if (ret != LDB_SUCCESS) { + ret = EIO; + goto exit; + } + + verdn = ldb_dn_new(tmp_ctx, ldb, "cn=sysdb"); + if (!verdn) { + ret = EIO; + goto exit; + } + + ret = ldb_search(ldb, tmp_ctx, &res, + verdn, LDB_SCOPE_BASE, + NULL, NULL); + if (ret != LDB_SUCCESS) { + ret = EIO; + goto exit; + } + if (res->count > 1) { + ret = EIO; + goto exit; + } + + if (res->count == 1) { + el = ldb_msg_find_element(res->msgs[0], "version"); + if (el) { + if (el->num_values != 1) { + ret = EINVAL; + goto exit; + } + version = talloc_strndup(tmp_ctx, + (char *)(el->values[0].data), + el->values[0].length); + if (!version) { + ret = ENOMEM; + goto exit; + } + + if (strcmp(version, SYSDB_VERSION) == 0) { + /* all fine, return */ + ret = EOK; + goto exit; + } + + DEBUG(4, ("Upgrading DB from version: %s\n", version)); + + if (strcmp(version, SYSDB_VERSION_0_1) == 0) { + /* convert database */ + ret = sysdb_upgrade_01(tmp_ctx, ldb, &version); + if (ret != EOK) goto exit; + } + + if (strcmp(version, SYSDB_VERSION_0_2) == 0) { + /* need to convert database to split files */ + do_02_upgrade = true; + } + + } + } + + if (!do_02_upgrade) { + /* not a v2 upgrade, return and let the normal code take over any + * further upgrade */ + ret = EOK; + goto exit; + } + + /* == V2->V3 UPGRADE == */ + + DEBUG(0, ("UPGRADING DB TO VERSION %s\n", SYSDB_VERSION_0_3)); + + /* ldb uses posix locks, + * posix is stupid and kills all locks when you close *any* file + * descriptor associated to the same file. + * Therefore we must close and reopen the ldb file here */ + + /* == Backup and reopen ldb == */ + + /* close */ + talloc_zfree(ldb); + + /* backup*/ + ret = backup_file(ldb_file, 0); + if (ret != EOK) { + goto exit; + } + + /* reopen */ + ldb = ldb_init(tmp_ctx, ev); + if (!ldb) { + ret = EIO; + goto exit; + } + + ret = ldb_set_debug(ldb, ldb_debug_messages, NULL); + if (ret != LDB_SUCCESS) { + ret = EIO; + goto exit; + } + + ret = ldb_connect(ldb, ldb_file, 0, NULL); + if (ret != LDB_SUCCESS) { + ret = EIO; + goto exit; + } + + /* open a transaction */ + ret = ldb_transaction_start(ldb); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to start ldb transaction! (%d)\n", ret)); + ret = EIO; + goto exit; + } + + /* == Upgrade contents == */ + + for (dom = domains; dom; dom = dom->next) { + struct ldb_dn *domain_dn; + struct ldb_dn *users_dn; + struct ldb_dn *groups_dn; + int i; + + /* skip local */ + if (strcasecmp(dom->provider, "local") == 0) { + continue; + } + + /* create new dom db */ + ret = sysdb_domain_init_internal(tmp_ctx, ev, dom, + db_path, false, &ctx); + if (ret != EOK) { + goto done; + } + + ret = ldb_transaction_start(ctx->ldb); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to start ldb transaction! (%d)\n", ret)); + ret = EIO; + goto done; + } + ctx_trans = true; + + /* search all entries for this domain in local, + * copy them all in the new database, + * then remove them from local */ + + domain_dn = ldb_dn_new_fmt(tmp_ctx, ctx->ldb, + SYSDB_DOM_BASE, ctx->domain->name); + if (!domain_dn) { + ret = ENOMEM; + goto done; + } + + ret = ldb_search(ldb, tmp_ctx, &res, + domain_dn, LDB_SCOPE_SUBTREE, + NULL, NULL); + if (ret != LDB_SUCCESS) { + ret = EIO; + goto done; + } + + users_dn = ldb_dn_new_fmt(tmp_ctx, ctx->ldb, + SYSDB_TMPL_USER_BASE, ctx->domain->name); + if (!users_dn) { + ret = ENOMEM; + goto done; + } + groups_dn = ldb_dn_new_fmt(tmp_ctx, ctx->ldb, + SYSDB_TMPL_GROUP_BASE, ctx->domain->name); + if (!groups_dn) { + ret = ENOMEM; + goto done; + } + + for (i = 0; i < res->count; i++) { + + struct ldb_dn *orig_dn; + + msg = res->msgs[i]; + + /* skip pre-created congtainers */ + if ((ldb_dn_compare(msg->dn, domain_dn) == 0) || + (ldb_dn_compare(msg->dn, users_dn) == 0) || + (ldb_dn_compare(msg->dn, groups_dn) == 0)) { + continue; + } + + /* regenerate the DN against the new ldb as it may have different + * casefolding rules (example: name changing from case insensitive + * to case sensitive) */ + orig_dn = msg->dn; + msg->dn = ldb_dn_new(msg, ctx->ldb, + ldb_dn_get_linearized(orig_dn)); + if (!msg->dn) { + ret = ENOMEM; + goto done; + } + + ret = ldb_add(ctx->ldb, msg); + if (ret != LDB_SUCCESS) { + DEBUG(0, ("WARNING: Could not add entry %s," + " to new ldb file! (%d [%s])\n", + ldb_dn_get_linearized(msg->dn), + ret, ldb_errstring(ctx->ldb))); + } + + ret = ldb_delete(ldb, orig_dn); + if (ret != LDB_SUCCESS) { + DEBUG(0, ("WARNING: Could not remove entry %s," + " from old ldb file! (%d [%s])\n", + ldb_dn_get_linearized(orig_dn), + ret, ldb_errstring(ldb))); + } + } + + /* now remove the basic containers from local */ + /* these were optional so debug at level 9 in case + * of failure just for tracing */ + ret = ldb_delete(ldb, groups_dn); + if (ret != LDB_SUCCESS) { + DEBUG(9, ("WARNING: Could not remove entry %s," + " from old ldb file! (%d [%s])\n", + ldb_dn_get_linearized(groups_dn), + ret, ldb_errstring(ldb))); + } + ret = ldb_delete(ldb, users_dn); + if (ret != LDB_SUCCESS) { + DEBUG(9, ("WARNING: Could not remove entry %s," + " from old ldb file! (%d [%s])\n", + ldb_dn_get_linearized(users_dn), + ret, ldb_errstring(ldb))); + } + ret = ldb_delete(ldb, domain_dn); + if (ret != LDB_SUCCESS) { + DEBUG(9, ("WARNING: Could not remove entry %s," + " from old ldb file! (%d [%s])\n", + ldb_dn_get_linearized(domain_dn), + ret, ldb_errstring(ldb))); + } + + ret = ldb_transaction_commit(ctx->ldb); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to commit ldb transaction! (%d)\n", ret)); + ret = EIO; + goto done; + } + ctx_trans = false; + + talloc_zfree(domain_dn); + talloc_zfree(groups_dn); + talloc_zfree(users_dn); + talloc_zfree(res); + } + + /* conversion done, upgrade version number */ + msg = ldb_msg_new(tmp_ctx); + if (!msg) { + ret = ENOMEM; + goto done; + } + msg->dn = ldb_dn_new(tmp_ctx, ldb, "cn=sysdb"); + if (!msg->dn) { + ret = ENOMEM; + goto done; + } + + ret = ldb_msg_add_empty(msg, "version", LDB_FLAG_MOD_REPLACE, NULL); + if (ret != LDB_SUCCESS) { + ret = ENOMEM; + goto done; + } + ret = ldb_msg_add_string(msg, "version", SYSDB_VERSION_0_3); + if (ret != LDB_SUCCESS) { + ret = ENOMEM; + goto done; + } + + ret = ldb_modify(ldb, msg); + if (ret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(ret); + goto done; + } + + ret = ldb_transaction_commit(ldb); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to commit ldb transaction! (%d)\n", ret)); + ret = EIO; + goto exit; + } + + ret = EOK; + +done: + if (ret != EOK) { + if (ctx_trans) { + ret = ldb_transaction_cancel(ctx->ldb); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to cancel ldb transaction! (%d)\n", ret)); + } + } + ret = ldb_transaction_cancel(ldb); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to cancel ldb transaction! (%d)\n", ret)); + } + } + +exit: + talloc_free(tmp_ctx); + return ret; +} + +static int sysdb_upgrade_03(struct sysdb_ctx *ctx, const char **ver) +{ + TALLOC_CTX *tmp_ctx; + int ret; + struct ldb_message *msg; + + tmp_ctx = talloc_new(ctx); + if (!tmp_ctx) { + return ENOMEM; + } + + DEBUG(0, ("UPGRADING DB TO VERSION %s\n", SYSDB_VERSION_0_4)); + + ret = ldb_transaction_start(ctx->ldb); + if (ret != LDB_SUCCESS) { + ret = EIO; + goto done; + } + + /* Make this database case-sensitive */ + msg = ldb_msg_new(tmp_ctx); + if (!msg) { + ret = ENOMEM; + goto done; + } + msg->dn = ldb_dn_new(tmp_ctx, ctx->ldb, "@ATTRIBUTES"); + if (!msg->dn) { + ret = ENOMEM; + goto done; + } + + ret = ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_DELETE, NULL); + if (ret != LDB_SUCCESS) { + ret = ENOMEM; + goto done; + } + + ret = ldb_modify(ctx->ldb, msg); + if (ret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(ret); + goto done; + } + + /* conversion done, upgrade version number */ + msg = ldb_msg_new(tmp_ctx); + if (!msg) { + ret = ENOMEM; + goto done; + } + msg->dn = ldb_dn_new(tmp_ctx, ctx->ldb, "cn=sysdb"); + if (!msg->dn) { + ret = ENOMEM; + goto done; + } + + ret = ldb_msg_add_empty(msg, "version", LDB_FLAG_MOD_REPLACE, NULL); + if (ret != LDB_SUCCESS) { + ret = ENOMEM; + goto done; + } + ret = ldb_msg_add_string(msg, "version", SYSDB_VERSION_0_4); + if (ret != LDB_SUCCESS) { + ret = ENOMEM; + goto done; + } + + ret = ldb_modify(ctx->ldb, msg); + if (ret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(ret); + goto done; + } + + ret = EOK; + +done: + talloc_zfree(tmp_ctx); + + if (ret != EOK) { + ret = ldb_transaction_cancel(ctx->ldb); + } else { + ret = ldb_transaction_commit(ctx->ldb); + *ver = SYSDB_VERSION_0_4; + } + if (ret != LDB_SUCCESS) { + ret = EIO; + } + + return ret; +} + +static int sysdb_upgrade_04(struct sysdb_ctx *ctx, const char **ver) +{ + TALLOC_CTX *tmp_ctx; + int ret; + struct ldb_message *msg; + + tmp_ctx = talloc_new(ctx); + if (!tmp_ctx) { + return ENOMEM; + } + + DEBUG(0, ("UPGRADING DB TO VERSION %s\n", SYSDB_VERSION_0_5)); + + ret = ldb_transaction_start(ctx->ldb); + if (ret != LDB_SUCCESS) { + ret = EIO; + goto done; + } + + /* Add new index */ + msg = ldb_msg_new(tmp_ctx); + if (!msg) { + ret = ENOMEM; + goto done; + } + msg->dn = ldb_dn_new(tmp_ctx, ctx->ldb, "@INDEXLIST"); + if (!msg->dn) { + ret = ENOMEM; + goto done; + } + + ret = ldb_msg_add_empty(msg, "@IDXATTR", LDB_FLAG_MOD_ADD, NULL); + if (ret != LDB_SUCCESS) { + ret = ENOMEM; + goto done; + } + ret = ldb_msg_add_string(msg, "@IDXATTR", "originalDN"); + if (ret != LDB_SUCCESS) { + ret = ENOMEM; + goto done; + } + + ret = ldb_modify(ctx->ldb, msg); + if (ret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(ret); + goto done; + } + + /* Rebuild memberuid and memberoif attributes */ + msg = ldb_msg_new(tmp_ctx); + if (!msg) { + ret = ENOMEM; + goto done; + } + msg->dn = ldb_dn_new(tmp_ctx, ctx->ldb, "@MEMBEROF-REBUILD"); + if (!msg->dn) { + ret = ENOMEM; + goto done; + } + + ret = ldb_add(ctx->ldb, msg); + if (ret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(ret); + goto done; + } + + /* conversion done, upgrade version number */ + msg = ldb_msg_new(tmp_ctx); + if (!msg) { + ret = ENOMEM; + goto done; + } + msg->dn = ldb_dn_new(tmp_ctx, ctx->ldb, "cn=sysdb"); + if (!msg->dn) { + ret = ENOMEM; + goto done; + } + + ret = ldb_msg_add_empty(msg, "version", LDB_FLAG_MOD_REPLACE, NULL); + if (ret != LDB_SUCCESS) { + ret = ENOMEM; + goto done; + } + ret = ldb_msg_add_string(msg, "version", SYSDB_VERSION_0_5); + if (ret != LDB_SUCCESS) { + ret = ENOMEM; + goto done; + } + + ret = ldb_modify(ctx->ldb, msg); + if (ret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(ret); + goto done; + } + + ret = EOK; + +done: + talloc_zfree(tmp_ctx); + + if (ret != EOK) { + ret = ldb_transaction_cancel(ctx->ldb); + } else { + ret = ldb_transaction_commit(ctx->ldb); + *ver = SYSDB_VERSION_0_5; + } + if (ret != LDB_SUCCESS) { + ret = EIO; + } + + return ret; +} + +static int sysdb_domain_init_internal(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sss_domain_info *domain, + const char *db_path, + bool allow_upgrade, + struct sysdb_ctx **_ctx) +{ + TALLOC_CTX *tmp_ctx = NULL; + struct sysdb_ctx *ctx; + const char *base_ldif; + struct ldb_ldif *ldif; + struct ldb_message *msg; + struct ldb_message_element *el; + struct ldb_result *res; + struct ldb_dn *verdn; + const char *version = NULL; + int ret; + + ctx = talloc_zero(mem_ctx, struct sysdb_ctx); + if (!ctx) { + return ENOMEM; + } + ctx->ev = ev; + ctx->domain = domain; + + /* The local provider s the only true MPG, + * for the other domains, the provider actually unrolls MPGs */ + if (strcasecmp(domain->provider, "local") == 0) { + ctx->mpg = true; + } + + ret = sysdb_get_db_file(ctx, domain->provider, + domain->name, db_path, + &ctx->ldb_file); + if (ret != EOK) { + return ret; + } + DEBUG(5, ("DB File for %s: %s\n", domain->name, ctx->ldb_file)); + + ctx->ldb = ldb_init(ctx, ev); + if (!ctx->ldb) { + return EIO; + } + + ret = ldb_set_debug(ctx->ldb, ldb_debug_messages, NULL); + if (ret != LDB_SUCCESS) { + return EIO; + } + +#ifdef SYSDB_TEST + ldb_set_modules_dir(ctx->ldb, "./.libs"); +#endif + + ret = ldb_connect(ctx->ldb, ctx->ldb_file, 0, NULL); + if (ret != LDB_SUCCESS) { + return EIO; + } + + tmp_ctx = talloc_new(ctx); + if (!tmp_ctx) { + return ENOMEM; + } + + verdn = ldb_dn_new(tmp_ctx, ctx->ldb, "cn=sysdb"); + if (!verdn) { + ret = EIO; + goto done; + } + + ret = ldb_search(ctx->ldb, tmp_ctx, &res, + verdn, LDB_SCOPE_BASE, + NULL, NULL); + if (ret != LDB_SUCCESS) { + ret = EIO; + goto done; + } + if (res->count > 1) { + ret = EIO; + goto done; + } + + if (res->count == 1) { + el = ldb_msg_find_element(res->msgs[0], "version"); + if (el) { + if (el->num_values != 1) { + ret = EINVAL; + goto done; + } + version = talloc_strndup(tmp_ctx, + (char *)(el->values[0].data), + el->values[0].length); + if (!version) { + ret = ENOMEM; + goto done; + } + + if (strcmp(version, SYSDB_VERSION) == 0) { + /* all fine, return */ + ret = EOK; + goto done; + } + + if (!allow_upgrade) { + DEBUG(0, ("Wrong DB version (got %s expected %s)\n", + version, SYSDB_VERSION)); + ret = EINVAL; + goto done; + } + + DEBUG(4, ("Upgrading DB [%s] from version: %s\n", + domain->name, version)); + + if (strcmp(version, SYSDB_VERSION_0_3) == 0) { + ret = sysdb_upgrade_03(ctx, &version); + if (ret != EOK) { + goto done; + } + } + + if (strcmp(version, SYSDB_VERSION_0_4) == 0) { + ret = sysdb_upgrade_04(ctx, &version); + goto done; + } + } + + DEBUG(0,("Unknown DB version [%s], expected [%s] for domain %s!\n", + version?version:"not found", SYSDB_VERSION, domain->name)); + ret = EINVAL; + goto done; + } + + /* cn=sysdb does not exists, means db is empty, populate */ + + base_ldif = SYSDB_BASE_LDIF; + while ((ldif = ldb_ldif_read_string(ctx->ldb, &base_ldif))) { + ret = ldb_add(ctx->ldb, ldif->msg); + if (ret != LDB_SUCCESS) { + DEBUG(0, ("Failed to initialize DB (%d, [%s]) for domain %s!", + ret, ldb_errstring(ctx->ldb), domain->name)); + ret = EIO; + goto done; + } + ldb_ldif_read_free(ctx->ldb, ldif); + } + + /* == create base domain object == */ + + msg = ldb_msg_new(tmp_ctx); + if (!msg) { + ret = ENOMEM; + goto done; + } + msg->dn = ldb_dn_new_fmt(msg, ctx->ldb, SYSDB_DOM_BASE, domain->name); + if (!msg->dn) { + ret = ENOMEM; + goto done; + } + ret = ldb_msg_add_fmt(msg, "cn", "%s", domain->name); + if (ret != LDB_SUCCESS) { + ret = EIO; + goto done; + } + /* do a synchronous add */ + ret = ldb_add(ctx->ldb, msg); + if (ret != LDB_SUCCESS) { + DEBUG(0, ("Failed to initialize DB (%d, [%s]) for domain %s!", + ret, ldb_errstring(ctx->ldb), domain->name)); + ret = EIO; + goto done; + } + talloc_zfree(msg); + + /* == create Users tree == */ + + msg = ldb_msg_new(tmp_ctx); + if (!msg) { + ret = ENOMEM; + goto done; + } + msg->dn = ldb_dn_new_fmt(msg, ctx->ldb, + SYSDB_TMPL_USER_BASE, domain->name); + if (!msg->dn) { + ret = ENOMEM; + goto done; + } + ret = ldb_msg_add_fmt(msg, "cn", "Users"); + if (ret != LDB_SUCCESS) { + ret = EIO; + goto done; + } + /* do a synchronous add */ + ret = ldb_add(ctx->ldb, msg); + if (ret != LDB_SUCCESS) { + DEBUG(0, ("Failed to initialize DB (%d, [%s]) for domain %s!", + ret, ldb_errstring(ctx->ldb), domain->name)); + ret = EIO; + goto done; + } + talloc_zfree(msg); + + /* == create Groups tree == */ + + msg = ldb_msg_new(tmp_ctx); + if (!msg) { + ret = ENOMEM; + goto done; + } + msg->dn = ldb_dn_new_fmt(msg, ctx->ldb, + SYSDB_TMPL_GROUP_BASE, domain->name); + if (!msg->dn) { + ret = ENOMEM; + goto done; + } + ret = ldb_msg_add_fmt(msg, "cn", "Groups"); + if (ret != LDB_SUCCESS) { + ret = EIO; + goto done; + } + /* do a synchronous add */ + ret = ldb_add(ctx->ldb, msg); + if (ret != LDB_SUCCESS) { + DEBUG(0, ("Failed to initialize DB (%d, [%s]) for domain %s!", + ret, ldb_errstring(ctx->ldb), domain->name)); + ret = EIO; + goto done; + } + talloc_zfree(msg); + + ret = EOK; + +done: + if (ret == EOK) { + *_ctx = ctx; + } + talloc_free(tmp_ctx); + return ret; +} + +int sysdb_init(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct confdb_ctx *cdb, + const char *alt_db_path, + bool allow_upgrade, + struct sysdb_ctx_list **_ctx_list) +{ + struct sysdb_ctx_list *ctx_list; + struct sss_domain_info *domains, *dom; + struct sysdb_ctx *ctx; + int ret; + + if (!ev) return EINVAL; + + ctx_list = talloc_zero(mem_ctx, struct sysdb_ctx_list); + if (!ctx_list) { + return ENOMEM; + } + + if (alt_db_path) { + ctx_list->db_path = talloc_strdup(ctx_list, alt_db_path); + } else { + ctx_list->db_path = talloc_strdup(ctx_list, DB_PATH); + } + if (!ctx_list->db_path) { + talloc_zfree(ctx_list); + return ENOMEM; + } + + /* open a db for each backend */ + ret = confdb_get_domains(cdb, &domains); + if (ret != EOK) { + talloc_zfree(ctx_list); + return ret; + } + + if (allow_upgrade) { + /* check if we have an old sssd.ldb to upgrade */ + ret = sysdb_check_upgrade_02(ctx_list, ev, domains, + ctx_list->db_path); + if (ret != EOK) { + talloc_zfree(ctx_list); + return ret; + } + } + + for (dom = domains; dom; dom = dom->next) { + + ctx_list->dbs = talloc_realloc(ctx_list, ctx_list->dbs, + struct sysdb_ctx *, + ctx_list->num_dbs + 1); + if (!ctx_list->dbs) { + talloc_zfree(ctx_list); + return ENOMEM; + } + + ret = sysdb_domain_init_internal(ctx_list, ev, dom, + ctx_list->db_path, + allow_upgrade, &ctx); + if (ret != EOK) { + talloc_zfree(ctx_list); + return ret; + } + + ctx_list->dbs[ctx_list->num_dbs] = ctx; + ctx_list->num_dbs++; + } + if (ctx_list->num_dbs == 0) { + /* what? .. */ + talloc_zfree(ctx_list); + return ENOENT; + } + + *_ctx_list = ctx_list; + + return EOK; +} + +int sysdb_domain_init(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sss_domain_info *domain, + const char *db_path, + struct sysdb_ctx **_ctx) +{ + return sysdb_domain_init_internal(mem_ctx, ev, domain, + db_path, false, _ctx); +} + +int sysdb_get_ctx_from_list(struct sysdb_ctx_list *ctx_list, + struct sss_domain_info *domain, + struct sysdb_ctx **ctx) +{ + int i; + + for (i = 0; i < ctx_list->num_dbs; i++) { + if (ctx_list->dbs[i]->domain == domain) { + *ctx = ctx_list->dbs[i]; + return EOK; + } + if (strcasecmp(ctx_list->dbs[i]->domain->name, domain->name) == 0) { + *ctx = ctx_list->dbs[i]; + return EOK; + } + } + /* definitely not found */ + return ENOENT; +} + + +int compare_ldb_dn_comp_num(const void *m1, const void *m2) +{ + struct ldb_message *msg1 = talloc_get_type(*(void **) discard_const(m1), + struct ldb_message); + struct ldb_message *msg2 = talloc_get_type(*(void **) discard_const(m2), + struct ldb_message); + + return ldb_dn_get_comp_num(msg2->dn) - ldb_dn_get_comp_num(msg1->dn); +} + +int sysdb_attrs_replace_name(struct sysdb_attrs *attrs, const char *oldname, + const char *newname) +{ + struct ldb_message_element *e = NULL; + int i; + const char *dummy; + + if (attrs == NULL || oldname == NULL || newname == NULL) return EINVAL; + + for (i = 0; i < attrs->num; i++) { + if (strcasecmp(oldname, attrs->a[i].name) == 0) { + e = &(attrs->a[i]); + } + if (strcasecmp(newname, attrs->a[i].name) == 0) { + DEBUG(3, ("New attribute name [%s] already exists.\n", newname)); + return EEXIST; + } + } + + if (e != NULL) { + dummy = talloc_strdup(attrs, newname); + if (dummy == NULL) { + DEBUG(1, ("talloc_strdup failed.\n")); + return ENOMEM; + } + + talloc_free(discard_const(e->name)); + e->name = dummy; + } + + return EOK; +} diff --git a/src/db/sysdb.h b/src/db/sysdb.h new file mode 100644 index 00000000..cf97ed62 --- /dev/null +++ b/src/db/sysdb.h @@ -0,0 +1,650 @@ +/* + SSSD + + System Database Header + + 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 . +*/ + +#ifndef __SYS_DB_H__ +#define __SYS_DB_H__ + +#include "util/util.h" +#include "confdb/confdb.h" +#include + +#define SYSDB_CONF_SECTION "config/sysdb" +#define CACHE_SYSDB_FILE "cache_%s.ldb" +#define LOCAL_SYSDB_FILE "sssd.ldb" + +#define SYSDB_BASE "cn=sysdb" +#define SYSDB_DOM_BASE "cn=%s,cn=sysdb" +#define SYSDB_USERS_CONTAINER "cn=users" +#define SYSDB_GROUPS_CONTAINER "cn=groups" +#define SYSDB_CUSTOM_CONTAINER "cn=custom" +#define SYSDB_TMPL_USER_BASE SYSDB_USERS_CONTAINER",cn=%s,"SYSDB_BASE +#define SYSDB_TMPL_GROUP_BASE SYSDB_GROUPS_CONTAINER",cn=%s,"SYSDB_BASE +#define SYSDB_TMPL_CUSTOM_BASE SYSDB_CUSTOM_CONTAINER",cn=%s,"SYSDB_BASE + +#define SYSDB_USER_CLASS "user" +#define SYSDB_GROUP_CLASS "group" + +#define SYSDB_NAME "name" + +#define SYSDB_NEXTID "nextID" +#define SYSDB_UIDNUM "uidNumber" +#define SYSDB_GIDNUM "gidNumber" +#define SYSDB_CREATE_TIME "createTimestamp" + +#define SYSDB_PWD "userPassword" +#define SYSDB_FULLNAME "fullName" +#define SYSDB_HOMEDIR "homeDirectory" +#define SYSDB_SHELL "loginShell" +#define SYSDB_MEMBEROF "memberOf" +#define SYSDB_DISABLED "disabled" + +#define SYSDB_MEMBER "member" +#define SYSDB_MEMBERUID "memberUid" + +#define SYSDB_DEFAULTGROUP "defaultGroup" +#define SYSDB_GECOS "gecos" +#define SYSDB_LOCALE "locale" +#define SYSDB_KEYBOARD "keyboard" +#define SYSDB_SESSION "session" +#define SYSDB_LAST_LOGIN "lastLogin" +#define SYSDB_LAST_ONLINE_AUTH "lastOnlineAuth" +#define SYSDB_USERPIC "userPicture" +#define SYSDB_LAST_FAILED_LOGIN "lastFailedLogin" +#define SYSDB_FAILED_LOGIN_ATTEMPTS "failedLoginAttempts" + +#define SYSDB_LAST_UPDATE "lastUpdate" +#define SYSDB_CACHE_EXPIRE "dataExpireTimestamp" +#define SYSDB_INITGR_EXPIRE "initgrExpireTimestamp" + +#define SYSDB_CACHEDPWD "cachedPassword" + +#define SYSDB_UUID "uniqueID" +#define SYSDB_UPN "userPrincipalName" +#define SYSDB_CCACHE_FILE "ccacheFile" + +#define SYSDB_ORIG_DN "originalDN" +#define SYSDB_ORIG_MODSTAMP "originalModifyTimestamp" +#define SYSDB_ORIG_MEMBEROF "originalMemberOf" + +#define SYSDB_USN "entryUSN" +#define SYSDB_HIGH_USN "highestUSN" + +#define SYSDB_NEXTID_FILTER "("SYSDB_NEXTID"=*)" + +#define SYSDB_UC "objectclass="SYSDB_USER_CLASS +#define SYSDB_GC "objectclass="SYSDB_GROUP_CLASS +#define SYSDB_MPGC "|("SYSDB_UC")("SYSDB_GC")" + +#define SYSDB_PWNAM_FILTER "(&("SYSDB_UC")("SYSDB_NAME"=%s))" +#define SYSDB_PWUID_FILTER "(&("SYSDB_UC")("SYSDB_UIDNUM"=%lu))" +#define SYSDB_PWENT_FILTER "("SYSDB_UC")" + +#define SYSDB_GRNAM_FILTER "(&("SYSDB_GC")("SYSDB_NAME"=%s))" +#define SYSDB_GRNA2_FILTER "(&("SYSDB_UC")("SYSDB_MEMBEROF"=%s))" +#define SYSDB_GRGID_FILTER "(&("SYSDB_GC")("SYSDB_GIDNUM"=%lu))" +#define SYSDB_GRENT_FILTER "("SYSDB_GC")" +#define SYSDB_GRNAM_MPG_FILTER "(&("SYSDB_MPGC")("SYSDB_NAME"=%s))" +#define SYSDB_GRGID_MPG_FILTER "(&("SYSDB_MPGC")("SYSDB_GIDNUM"=%lu))" +#define SYSDB_GRENT_MPG_FILTER "("SYSDB_MPGC")" + +#define SYSDB_INITGR_FILTER "(&("SYSDB_GC")("SYSDB_GIDNUM"=*))" + +#define SYSDB_GETCACHED_FILTER "(&"SYSDB_UC")("SYSDB_LAST_LOGIN">=%lu))" + +#define SYSDB_DEFAULT_ATTRS SYSDB_LAST_UPDATE, \ + SYSDB_CACHE_EXPIRE, \ + SYSDB_INITGR_EXPIRE, \ + "objectClass" + +#define SYSDB_PW_ATTRS {SYSDB_NAME, SYSDB_UIDNUM, \ + SYSDB_GIDNUM, SYSDB_GECOS, \ + SYSDB_HOMEDIR, SYSDB_SHELL, \ + SYSDB_DEFAULT_ATTRS, \ + NULL} +#define SYSDB_GRSRC_ATTRS {SYSDB_NAME, SYSDB_GIDNUM, \ + SYSDB_MEMBERUID, \ + SYSDB_DEFAULT_ATTRS, \ + NULL} +#define SYSDB_GRPW_ATTRS {SYSDB_NAME, SYSDB_UIDNUM, \ + SYSDB_DEFAULT_ATTRS, \ + NULL} +#define SYSDB_GRENT_ATTRS {SYSDB_NAME, SYSDB_UIDNUM, SYSDB_MEMBEROF, \ + SYSDB_DEFAULT_ATTRS, \ + NULL} + +#define SYSDB_INITGR_ATTR SYSDB_MEMBEROF +#define SYSDB_INITGR_ATTRS {SYSDB_GIDNUM, \ + SYSDB_DEFAULT_ATTRS, \ + NULL} + +#define SYSDB_TMPL_USER SYSDB_NAME"=%s,"SYSDB_TMPL_USER_BASE +#define SYSDB_TMPL_GROUP SYSDB_NAME"=%s,"SYSDB_TMPL_GROUP_BASE +#define SYSDB_TMPL_CUSTOM_SUBTREE "cn=%s,"SYSDB_TMPL_CUSTOM_BASE +#define SYSDB_TMPL_CUSTOM SYSDB_NAME"=%s,cn=%s,"SYSDB_TMPL_CUSTOM_BASE + +#define SYSDB_MOD_ADD LDB_FLAG_MOD_ADD +#define SYSDB_MOD_DEL LDB_FLAG_MOD_DELETE +#define SYSDB_MOD_REP LDB_FLAG_MOD_REPLACE + +struct confdb_ctx; +struct sysdb_ctx_list; +struct sysdb_ctx; +struct sysdb_handle; + +struct sysdb_attrs { + int num; + struct ldb_message_element *a; +}; + +/* sysdb_attrs helper functions */ +struct sysdb_attrs *sysdb_new_attrs(TALLOC_CTX *memctx); + +/* values are copied in the structure, allocated on "attrs" */ +int sysdb_attrs_add_val(struct sysdb_attrs *attrs, + const char *name, const struct ldb_val *val); +int sysdb_attrs_add_string(struct sysdb_attrs *attrs, + const char *name, const char *str); +int sysdb_attrs_add_long(struct sysdb_attrs *attrs, + const char *name, long value); +int sysdb_attrs_add_uint32(struct sysdb_attrs *attrs, + const char *name, uint32_t value); +int sysdb_attrs_add_time_t(struct sysdb_attrs *attrs, + const char *name, time_t value); +int sysdb_attrs_get_el(struct sysdb_attrs *attrs, const char *name, + struct ldb_message_element **el); +int sysdb_attrs_steal_string(struct sysdb_attrs *attrs, + const char *name, char *str); +int sysdb_attrs_get_string(struct sysdb_attrs *attrs, const char *name, + const char **string); + +int sysdb_attrs_replace_name(struct sysdb_attrs *attrs, const char *oldname, + const char *newname); + +int sysdb_attrs_users_from_str_list(struct sysdb_attrs *attrs, + const char *attr_name, + const char *domain, + const char **list); +int sysdb_attrs_users_from_ldb_vals(struct sysdb_attrs *attrs, + const char *attr_name, + const char *domain, + struct ldb_val *values, + int num_values); + +/* convert an ldb error into an errno error */ +int sysdb_error_to_errno(int ldberr); + +/* DNs related helper functions */ +struct ldb_dn *sysdb_user_dn(struct sysdb_ctx *ctx, void *memctx, + const char *domain, const char *name); +struct ldb_dn *sysdb_group_dn(struct sysdb_ctx *ctx, void *memctx, + const char *domain, const char *name); +struct ldb_dn *sysdb_domain_dn(struct sysdb_ctx *ctx, void *memctx, + const char *domain); +struct ldb_dn *sysdb_custom_dn(struct sysdb_ctx *ctx, void *memctx, + const char *domain, const char *object_name, + const char *subtree_name); +struct ldb_dn *sysdb_custom_subtree_dn(struct sysdb_ctx *ctx, void *memctx, + const char *domain, + const char *subtree_name); + +char *sysdb_user_strdn(TALLOC_CTX *memctx, + const char *domain, const char *name); +char *sysdb_group_strdn(TALLOC_CTX *memctx, + const char *domain, const char *name); + + +struct ldb_context *sysdb_ctx_get_ldb(struct sysdb_ctx *ctx); +struct ldb_context *sysdb_handle_get_ldb(struct sysdb_handle *handle); +struct sysdb_ctx *sysdb_handle_get_ctx(struct sysdb_handle *handle); + +int compare_ldb_dn_comp_num(const void *m1, const void *m2); + +/* function to start and finish a transaction + * sysdb_transaction_send() will queue a request for a transaction + * when it is done it will call the tevent_req callback, which must + * retrieve the transaction handle using sysdb_transaction_recv() + * + * A transaction must be completed either by sending a commit: + * sysdb_transaction_commit_send()/sysdb_transaction_commit_recv() + * or by freeing the transaction handle (this will implicitly cause + * a transaction cancelation). + * + * Transactions are serialized, no other transaction or operation can be + * performed while a transaction is active. Multiple transaction request + * are queued internally and served in order. + */ + +struct tevent_req *sysdb_transaction_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *ctx); +int sysdb_transaction_recv(struct tevent_req *req, TALLOC_CTX *memctx, + struct sysdb_handle **handle); + +struct tevent_req *sysdb_transaction_commit_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle); +int sysdb_transaction_commit_recv(struct tevent_req *req); + + +/* default transaction commit receive function. + * This function does not use the request state so it is safe to use + * from any caller */ +void sysdb_transaction_complete(struct tevent_req *subreq); + + +/* Sysdb initialization. + * call this function *only* once to initialize the database and get + * the sysdb ctx */ +int sysdb_init(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct confdb_ctx *cdb, + const char *alt_db_path, + bool allow_upgrade, + struct sysdb_ctx_list **_ctx_list); +/* used to initialize only one domain database. + * Do NOT use if sysdb_init has already been called */ +int sysdb_domain_init(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sss_domain_info *domain, + const char *db_path, + struct sysdb_ctx **_ctx); + +int sysdb_get_ctx_from_list(struct sysdb_ctx_list *ctx_list, + struct sss_domain_info *domain, + struct sysdb_ctx **_ctx); + +/* FIXME: REMOVE */ +typedef void (*sysdb_callback_t)(void *, int, struct ldb_result *); + +/* functions to retrieve information from sysdb + * These functions automatically starts an operation + * therefore they cannot be called within a transaction */ +int sysdb_getpwnam(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *ctx, + struct sss_domain_info *domain, + const char *name, + sysdb_callback_t fn, void *ptr); + +int sysdb_getpwuid(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *ctx, + struct sss_domain_info *domain, + uid_t uid, + sysdb_callback_t fn, void *ptr); + +int sysdb_enumpwent(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *ctx, + struct sss_domain_info *domain, + const char *expression, + sysdb_callback_t fn, void *ptr); + +int sysdb_getgrnam(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *ctx, + struct sss_domain_info *domain, + const char *name, + sysdb_callback_t fn, void *ptr); + +int sysdb_getgrgid(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *ctx, + struct sss_domain_info *domain, + gid_t gid, + sysdb_callback_t fn, void *ptr); + +int sysdb_enumgrent(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *ctx, + struct sss_domain_info *domain, + sysdb_callback_t fn, void *ptr); + +int sysdb_initgroups(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *ctx, + struct sss_domain_info *domain, + const char *name, + sysdb_callback_t fn, void *ptr); + +int sysdb_get_user_attr(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *ctx, + struct sss_domain_info *domain, + const char *name, + const char **attributes, + sysdb_callback_t fn, void *ptr); + + +/* functions that modify the databse + * they have to be called within a transaction + * See sysdb_transaction_send()/_recv() */ + +/* Delete Entry */ +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); +int sysdb_delete_entry_recv(struct tevent_req *req); + + +struct tevent_req *sysdb_delete_recursive_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct ldb_dn *dn, + bool ignore_not_found); +int sysdb_delete_recursive_recv(struct tevent_req *req); + +/* Search Entry */ +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); +int sysdb_search_entry_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *msgs_size, + struct ldb_message ***msgs); + +/* Search User (by uid or name) */ +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 *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); +int sysdb_search_user_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct ldb_message **msg); + +/* Search Group (gy gid or name) */ +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 *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); +int sysdb_search_group_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct ldb_message **msg); + +/* Replace entry attrs */ +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); +int sysdb_set_entry_attr_recv(struct tevent_req *req); + +/* Replace user attrs */ +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); +int sysdb_set_user_attr_recv(struct tevent_req *req); + +/* Replace group attrs */ +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); +int sysdb_set_group_attr_recv(struct tevent_req *req); + +/* Allocate a new id */ +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); +int sysdb_get_new_id_recv(struct tevent_req *req, uint32_t *id); + +/* Add user (only basic attrs and w/o 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); +int sysdb_add_basic_user_recv(struct tevent_req *req); + +/* Add user (all checks) */ +struct tevent_req *sysdb_add_user_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, + uid_t uid, gid_t gid, + const char *gecos, + const char *homedir, + const char *shell, + struct sysdb_attrs *attrs, + int cache_timeout); +int sysdb_add_user_recv(struct tevent_req *req); + +/* Add group (only basic attrs and w/o 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); +int sysdb_add_basic_group_recv(struct tevent_req *req); + +/* Add group (all checks) */ +struct tevent_req *sysdb_add_group_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, gid_t gid, + struct sysdb_attrs *attrs, + int cache_timeout); +int sysdb_add_group_recv(struct tevent_req *req); + +/* mod_op must be either LDB_FLAG_MOD_ADD or LDB_FLAG_MOD_DELETE */ +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); +int sysdb_mod_group_member_recv(struct tevent_req *req); + +int sysdb_set_group_gid(struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, gid_t gid, + sysdb_callback_t fn, void *pvt); + +struct 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); +int sysdb_store_user_recv(struct tevent_req *req); + +struct tevent_req *sysdb_store_group_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, + gid_t gid, + struct sysdb_attrs *attrs, + uint64_t cache_timeout); +int sysdb_store_group_recv(struct tevent_req *req); + +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 *member); +int sysdb_add_group_member_recv(struct tevent_req *req); + +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 *member); +int sysdb_remove_group_member_recv(struct tevent_req *req); + +/* Password caching function. + * If you are in a transaction ignore sysdb and pass in the handle. + * If you are not in a transaction pass NULL in handle and provide sysdb, + * in this case a transaction will be automatically started and the + * function will be completely wrapped in it's own sysdb transaction */ +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); +int sysdb_cache_password_recv(struct tevent_req *req); + + +errno_t check_failed_login_attempts(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb, + struct ldb_message *ldb_msg, + uint32_t *failed_login_attempts, + time_t *delayed_until); +struct tevent_req *sysdb_cache_auth_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *name, + const uint8_t *authtok, + size_t authtok_size, + struct confdb_ctx *cdb); +int sysdb_cache_auth_recv(struct tevent_req *req, time_t *expire_date, + time_t *delayed_until); + +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); +int sysdb_store_custom_recv(struct tevent_req *req); + +struct tevent_req *sysdb_search_custom_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *filter, + const char *subtree_name, + const char **attrs); +struct tevent_req *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); +int sysdb_search_custom_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *msgs_count, + struct ldb_message ***msg); + +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); +int sysdb_delete_custom_recv(struct tevent_req *req); + +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); +int sysdb_asq_search_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + size_t *msgs_count, struct ldb_message ***msgs); + +struct tevent_req *sysdb_search_users_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *sub_filter, + const char **attrs); +int sysdb_search_users_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + size_t *msgs_count, struct ldb_message ***msgs); + +struct tevent_req *sysdb_delete_user_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, uid_t uid); +int sysdb_delete_user_recv(struct tevent_req *req); + +struct tevent_req *sysdb_search_groups_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *sub_filter, + const char **attrs); +int sysdb_search_groups_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + size_t *msgs_count, struct ldb_message ***msgs); + +struct tevent_req *sysdb_delete_group_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, gid_t gid); +int sysdb_delete_group_recv(struct tevent_req *req); + +#endif /* __SYS_DB_H__ */ diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c new file mode 100644 index 00000000..33cfd91f --- /dev/null +++ b/src/db/sysdb_ops.c @@ -0,0 +1,5059 @@ +/* + SSSD + + System Database + + Copyright (C) Simo Sorce 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "util/util.h" +#include "db/sysdb_private.h" +#include "util/sha512crypt.h" +#include + +static int add_string(struct ldb_message *msg, int flags, + const char *attr, const char *value) +{ + int ret; + + ret = ldb_msg_add_empty(msg, attr, flags, NULL); + if (ret == LDB_SUCCESS) { + ret = ldb_msg_add_string(msg, attr, value); + if (ret == LDB_SUCCESS) return EOK; + } + return ENOMEM; +} + +static int add_ulong(struct ldb_message *msg, int flags, + const char *attr, unsigned long value) +{ + int ret; + + ret = ldb_msg_add_empty(msg, attr, flags, NULL); + if (ret == LDB_SUCCESS) { + ret = ldb_msg_add_fmt(msg, attr, "%lu", value); + if (ret == LDB_SUCCESS) return EOK; + } + return ENOMEM; +} + +static uint32_t get_attr_as_uint32(struct ldb_message *msg, const char *attr) +{ + const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr); + long long int l; + + if (!v || !v->data) { + return 0; + } + + errno = 0; + l = strtoll((const char *)v->data, NULL, 0); + if (errno) { + return (uint32_t)-1; + } + + if (l < 0 || l > ((uint32_t)(-1))) { + return (uint32_t)-1; + } + + return l; +} + +#define ERROR_OUT(v, r, l) do { v = r; goto l; } while(0); + +/* =LDB-Request-(tevent_req-style)======================================== */ + +struct sldb_request_state { + struct tevent_context *ev; + struct ldb_context *ldbctx; + struct ldb_request *ldbreq; + struct ldb_reply *ldbreply; +}; + +static void sldb_request_wakeup(struct tevent_req *subreq); +static int sldb_request_callback(struct ldb_request *ldbreq, + struct ldb_reply *ldbreply); + +static struct tevent_req *sldb_request_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ldb_context *ldbctx, + struct ldb_request *ldbreq) +{ + struct tevent_req *req, *subreq; + struct sldb_request_state *state; + struct timeval tv = { 0, 0 }; + + req = tevent_req_create(mem_ctx, &state, struct sldb_request_state); + if (!req) return NULL; + + state->ev = ev; + state->ldbctx = ldbctx; + state->ldbreq = ldbreq; + state->ldbreply = NULL; + + subreq = tevent_wakeup_send(state, ev, tv); + if (!subreq) { + DEBUG(1, ("Failed to add critical timer to run next ldb operation!\n")); + talloc_zfree(req); + return NULL; + } + tevent_req_set_callback(subreq, sldb_request_wakeup, req); + + return req; +} + +static void sldb_request_wakeup(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sldb_request_state *state = tevent_req_data(req, + struct sldb_request_state); + int ret; + + if (!tevent_wakeup_recv(subreq)) return; + talloc_zfree(subreq); + + state->ldbreq->callback = sldb_request_callback; + state->ldbreq->context = req; + + ret = ldb_request(state->ldbctx, state->ldbreq); + if (ret != LDB_SUCCESS) { + int err = sysdb_error_to_errno(ret); + DEBUG(6, ("Error: %d (%s)\n", err, strerror(err))); + tevent_req_error(req, err); + } +} + +static int sldb_request_callback(struct ldb_request *ldbreq, + struct ldb_reply *ldbreply) +{ + struct tevent_req *req = talloc_get_type(ldbreq->context, + struct tevent_req); + struct sldb_request_state *state = tevent_req_data(req, + struct sldb_request_state); + int err; + + if (!ldbreply) { + DEBUG(6, ("Error: Missing ldbreply")); + ERROR_OUT(err, EIO, fail); + } + + state->ldbreply = talloc_steal(state, ldbreply); + + if (ldbreply->error != LDB_SUCCESS) { + DEBUG(6, ("LDB Error: %d (%s)\n", + ldbreply->error, ldb_errstring(state->ldbctx))); + ERROR_OUT(err, sysdb_error_to_errno(ldbreply->error), fail); + } + + if (ldbreply->type == LDB_REPLY_DONE) { + tevent_req_done(req); + return EOK; + } + + tevent_req_notify_callback(req); + return EOK; + +fail: + tevent_req_error(req, err); + return EOK; +} + +static int sldb_request_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct ldb_reply **ldbreply) +{ + struct sldb_request_state *state = tevent_req_data(req, + struct sldb_request_state); + enum tevent_req_state tstate; + uint64_t err = 0; + + if (state->ldbreply) { + *ldbreply = talloc_move(mem_ctx, &state->ldbreply); + } + + if (tevent_req_is_error(req, &tstate, &err)) { + switch (tstate) { + case TEVENT_REQ_USER_ERROR: + return err; + case TEVENT_REQ_IN_PROGRESS: + return EOK; + default: + return EIO; + } + } + + return EOK; +} + +/* =Standard-Sysdb-Operations-utility-functions=========================== */ + +struct sysdb_op_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + + bool ignore_not_found; + + struct ldb_reply *ldbreply; + size_t msgs_count; + struct ldb_message **msgs; +}; + +static void sysdb_op_default_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_op_state *state = tevent_req_data(req, + struct sysdb_op_state); + int ret; + + ret = sldb_request_recv(subreq, state, &state->ldbreply); + talloc_zfree(subreq); + if (ret) { + if (state->ignore_not_found && ret == ENOENT) { + goto done; + } + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + if (state->ldbreply->type != LDB_REPLY_DONE) { + DEBUG(6, ("Error: %d (%s)\n", EIO, strerror(EIO))); + tevent_req_error(req, EIO); + return; + } + +done: + tevent_req_done(req); +} + +static int sysdb_op_default_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + + +/* =Remove-Entry-From-Sysdb=============================================== */ + +struct tevent_req *sysdb_delete_entry_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct ldb_dn *dn, + bool ignore_not_found) +{ + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; + struct ldb_request *ldbreq; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->ignore_not_found = ignore_not_found; + state->ldbreply = NULL; + + ret = ldb_build_del_req(&ldbreq, handle->ctx->ldb, state, dn, + NULL, NULL, NULL, NULL); + + if (ret != LDB_SUCCESS) { + DEBUG(1, ("LDB Error: %s(%d)\nError Message: [%s]\n", + ldb_strerror(ret), ret, ldb_errstring(handle->ctx->ldb))); + ERROR_OUT(ret, sysdb_error_to_errno(ret), fail); + } + + subreq = sldb_request_send(state, ev, handle->ctx->ldb, ldbreq); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_op_default_done, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +int sysdb_delete_entry_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Remove-Subentries-From-Sysdb=============================================== */ + +struct sysdb_delete_recursive_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + + bool ignore_not_found; + + struct ldb_reply *ldbreply; + size_t msgs_count; + struct ldb_message **msgs; + size_t current_item; +}; + +static void sysdb_delete_search_done(struct tevent_req *subreq); +static void sysdb_delete_recursive_prepare_op(struct tevent_req *req); +static void sysdb_delete_recursive_op_done(struct tevent_req *req); + +struct tevent_req *sysdb_delete_recursive_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct ldb_dn *dn, + bool ignore_not_found) +{ + struct tevent_req *req, *subreq; + struct sysdb_delete_recursive_state *state; + int ret; + const char **no_attrs; + + req = tevent_req_create(mem_ctx, &state, + struct sysdb_delete_recursive_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->ignore_not_found = ignore_not_found; + state->ldbreply = NULL; + state->msgs_count = 0; + state->msgs = NULL; + state->current_item = 0; + + no_attrs = talloc_array(state, const char *, 1); + if (no_attrs == NULL) { + ERROR_OUT(ret, ENOMEM, fail); + } + no_attrs[0] = NULL; + + subreq = sysdb_search_entry_send(state, ev, handle, dn, LDB_SCOPE_SUBTREE, + "(distinguishedName=*)", no_attrs); + + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_delete_search_done, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_delete_search_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_delete_recursive_state *state = tevent_req_data(req, + struct sysdb_delete_recursive_state); + int ret; + + ret = sysdb_search_entry_recv(subreq, state, &state->msgs_count, + &state->msgs); + talloc_zfree(subreq); + if (ret) { + if (state->ignore_not_found && ret == ENOENT) { + tevent_req_done(req); + return; + } + DEBUG(6, ("Search error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + DEBUG(9, ("Found [%d] items to delete.\n", state->msgs_count)); + + qsort(state->msgs, state->msgs_count, sizeof(struct ldb_message *), + compare_ldb_dn_comp_num); + + state->current_item = 0; + sysdb_delete_recursive_prepare_op(req); +} + +static void sysdb_delete_recursive_prepare_op(struct tevent_req *req) +{ + struct sysdb_delete_recursive_state *state = tevent_req_data(req, + struct sysdb_delete_recursive_state); + struct tevent_req *subreq; + int ret; + struct ldb_request *ldbreq; + + if (state->current_item < state->msgs_count) { + DEBUG(9 ,("Trying to delete [%s].\n", + ldb_dn_canonical_string(state, + state->msgs[state->current_item]->dn))); + ret = ldb_build_del_req(&ldbreq, state->handle->ctx->ldb, state, + state->msgs[state->current_item]->dn, NULL, + NULL, NULL, NULL); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("LDB Error: %s(%d)\nError Message: [%s]\n", + ldb_strerror(ret), ret, + ldb_errstring(state->handle->ctx->ldb))); + ret = sysdb_error_to_errno(ret); + goto fail; + } + + subreq = sldb_request_send(state, state->ev, state->handle->ctx->ldb, + ldbreq); + if (!subreq) { + ret = ENOMEM; + goto fail; + } + + state->current_item++; + tevent_req_set_callback(subreq, sysdb_delete_recursive_op_done, req); + return; + } + + tevent_req_done(req); + return; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); +} + +static void sysdb_delete_recursive_op_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_op_default_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Delete error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + sysdb_delete_recursive_prepare_op(req); +} + +int sysdb_delete_recursive_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Search-Entry========================================================== */ + +static void sysdb_search_entry_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_search_entry_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct ldb_dn *base_dn, + int scope, + const char *filter, + const char **attrs) +{ + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; + struct ldb_request *ldbreq; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->ignore_not_found = false; + state->ldbreply = NULL; + state->msgs_count = 0; + state->msgs = NULL; + + ret = ldb_build_search_req(&ldbreq, handle->ctx->ldb, state, + base_dn, scope, filter, attrs, + NULL, NULL, NULL, NULL); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to build search request: %s(%d)[%s]\n", + ldb_strerror(ret), ret, ldb_errstring(handle->ctx->ldb))); + ERROR_OUT(ret, sysdb_error_to_errno(ret), fail); + } + + subreq = sldb_request_send(state, ev, handle->ctx->ldb, ldbreq); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_search_entry_done, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_search_entry_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_op_state *state = tevent_req_data(req, + struct sysdb_op_state); + struct ldb_reply *ldbreply; + struct ldb_message **dummy; + int ret; + + ret = sldb_request_recv(subreq, state, &ldbreply); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + switch (ldbreply->type) { + case LDB_REPLY_ENTRY: + dummy = talloc_realloc(state, state->msgs, + struct ldb_message *, + state->msgs_count + 2); + if (dummy == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + state->msgs = dummy; + + state->msgs[state->msgs_count + 1] = NULL; + + state->msgs[state->msgs_count] = talloc_steal(state->msgs, + ldbreply->message); + state->msgs_count++; + + talloc_zfree(ldbreply); + return; + + case LDB_REPLY_DONE: + talloc_zfree(subreq); + talloc_zfree(ldbreply); + if (state->msgs_count == 0) { + DEBUG(6, ("Error: Entry not Found!\n")); + tevent_req_error(req, ENOENT); + return; + } + return tevent_req_done(req); + + default: + /* unexpected stuff */ + talloc_zfree(ldbreply); + DEBUG(6, ("Error: Unknown error!\n")); + tevent_req_error(req, EIO); + return; + } +} + +int sysdb_search_entry_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *msgs_count, + struct ldb_message ***msgs) +{ + struct sysdb_op_state *state = tevent_req_data(req, + struct sysdb_op_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *msgs_count = state->msgs_count; + *msgs = talloc_move(mem_ctx, &state->msgs); + + return EOK; +} + + +/* =Search-User-by-[UID/NAME]============================================= */ + +struct sysdb_search_user_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + + struct ldb_dn *basedn; + const char **attrs; + const char *filter; + int scope; + + size_t msgs_count; + struct ldb_message **msgs; +}; + +static void sysdb_search_user_cont(struct tevent_req *subreq); +static void sysdb_search_user_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_search_user_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, + const char **attrs) +{ + struct tevent_req *req, *subreq; + struct sysdb_search_user_state *state; + static const char *def_attrs[] = { SYSDB_NAME, SYSDB_UIDNUM, NULL }; + int ret; + + if (!sysdb && !handle) return NULL; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_search_user_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->msgs_count = 0; + state->msgs = NULL; + + state->attrs = attrs ? attrs : def_attrs; + state->filter = NULL; + state->scope = LDB_SCOPE_BASE; + + if (!sysdb) sysdb = handle->ctx; + + state->basedn = sysdb_user_dn(sysdb, state, domain->name, name); + if (!state->basedn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + if (!handle) { + subreq = sysdb_operation_send(state, state->ev, sysdb); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_search_user_cont, req); + } + else { + subreq = sysdb_search_entry_send(state, state->ev, state->handle, + state->basedn, state->scope, + state->filter, state->attrs); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_search_user_done, req); + } + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +struct tevent_req *sysdb_search_user_by_uid_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + uid_t uid, + const char **attrs) +{ + struct tevent_req *req, *subreq; + struct sysdb_search_user_state *state; + static const char *def_attrs[] = { SYSDB_NAME, SYSDB_UIDNUM, NULL }; + int ret; + + if (!sysdb && !handle) return NULL; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_search_user_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->msgs_count = 0; + state->msgs = NULL; + state->attrs = attrs ? attrs : def_attrs; + + if (!sysdb) sysdb = handle->ctx; + + state->basedn = ldb_dn_new_fmt(state, sysdb->ldb, + SYSDB_TMPL_USER_BASE, domain->name); + if (!state->basedn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + state->filter = talloc_asprintf(state, SYSDB_PWUID_FILTER, + (unsigned long)uid); + if (!state->filter) { + ERROR_OUT(ret, ENOMEM, fail); + } + + state->scope = LDB_SCOPE_ONELEVEL; + + if (!handle) { + subreq = sysdb_operation_send(state, state->ev, sysdb); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_search_user_cont, req); + } + else { + subreq = sysdb_search_entry_send(state, state->ev, state->handle, + state->basedn, state->scope, + state->filter, state->attrs); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_search_user_done, req); + } + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_search_user_cont(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_search_user_state *state = tevent_req_data(req, + struct sysdb_search_user_state); + int ret; + + ret = sysdb_operation_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + subreq = sysdb_search_entry_send(state, state->ev, state->handle, + state->basedn, state->scope, + state->filter, state->attrs); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_search_user_done, req); +} + +static void sysdb_search_user_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_search_user_state *state = tevent_req_data(req, + struct sysdb_search_user_state); + int ret; + + ret = sysdb_search_entry_recv(subreq, state, &state->msgs_count, + &state->msgs); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_search_user_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct ldb_message **msg) +{ + struct sysdb_search_user_state *state = tevent_req_data(req, + struct sysdb_search_user_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + if (state->msgs_count > 1) { + DEBUG(1, ("More than one result found.\n")); + return EFAULT; + } + + *msg = talloc_move(mem_ctx, &state->msgs[0]); + + return EOK; +} + + +/* =Search-Group-by-[GID/NAME]============================================ */ + +struct sysdb_search_group_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + + struct ldb_dn *basedn; + const char **attrs; + const char *filter; + int scope; + + size_t msgs_count; + struct ldb_message **msgs; +}; + +static void sysdb_search_group_cont(struct tevent_req *subreq); +static void sysdb_search_group_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_search_group_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, + const char **attrs) +{ + struct tevent_req *req, *subreq; + struct sysdb_search_group_state *state; + static const char *def_attrs[] = { SYSDB_NAME, SYSDB_GIDNUM, NULL }; + int ret; + + if (!sysdb && !handle) return NULL; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_search_group_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->msgs_count = 0; + state->msgs = NULL; + + state->attrs = attrs ? attrs : def_attrs; + state->filter = NULL; + state->scope = LDB_SCOPE_BASE; + + if (!sysdb) sysdb = handle->ctx; + + state->basedn = sysdb_group_dn(sysdb, state, domain->name, name); + if (!state->basedn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + if (!handle) { + subreq = sysdb_operation_send(state, state->ev, sysdb); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_search_group_cont, req); + } + else { + subreq = sysdb_search_entry_send(state, state->ev, state->handle, + state->basedn, state->scope, + state->filter, state->attrs); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_search_group_done, req); + } + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +struct tevent_req *sysdb_search_group_by_gid_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + gid_t gid, + const char **attrs) +{ + struct tevent_req *req, *subreq; + struct sysdb_search_group_state *state; + static const char *def_attrs[] = { SYSDB_NAME, SYSDB_GIDNUM, NULL }; + int ret; + + if (!sysdb && !handle) return NULL; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_search_group_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->msgs_count = 0; + state->msgs = NULL; + state->attrs = attrs ? attrs : def_attrs; + + if (!sysdb) sysdb = handle->ctx; + + state->basedn = ldb_dn_new_fmt(state, sysdb->ldb, + SYSDB_TMPL_GROUP_BASE, domain->name); + if (!state->basedn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + state->filter = talloc_asprintf(state, SYSDB_GRGID_FILTER, + (unsigned long)gid); + if (!state->filter) { + ERROR_OUT(ret, ENOMEM, fail); + } + + state->scope = LDB_SCOPE_ONELEVEL; + + if (!handle) { + subreq = sysdb_operation_send(state, state->ev, sysdb); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_search_group_cont, req); + } + else { + subreq = sysdb_search_entry_send(state, state->ev, state->handle, + state->basedn, state->scope, + state->filter, state->attrs); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_search_group_done, req); + } + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_search_group_cont(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_search_group_state *state = tevent_req_data(req, + struct sysdb_search_group_state); + int ret; + + ret = sysdb_operation_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + subreq = sysdb_search_entry_send(state, state->ev, state->handle, + state->basedn, state->scope, + state->filter, state->attrs); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_search_group_done, req); +} + +static void sysdb_search_group_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_search_group_state *state = tevent_req_data(req, + struct sysdb_search_group_state); + int ret; + + ret = sysdb_search_entry_recv(subreq, state, &state->msgs_count, + &state->msgs); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_search_group_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct ldb_message **msg) +{ + struct sysdb_search_group_state *state = tevent_req_data(req, + struct sysdb_search_group_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + if (state->msgs_count > 1) { + DEBUG(1, ("More than one result found.\n")); + return EFAULT; + } + + *msg = talloc_move(mem_ctx, &state->msgs[0]); + + return EOK; +} + + +/* =Replace-Attributes-On-Entry=========================================== */ + +struct tevent_req *sysdb_set_entry_attr_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct ldb_dn *entry_dn, + struct sysdb_attrs *attrs, + int mod_op) +{ + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; + struct ldb_request *ldbreq; + struct ldb_message *msg; + int i, ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->ignore_not_found = false; + state->ldbreply = NULL; + + if (!entry_dn) { + ERROR_OUT(ret, EINVAL, fail); + } + + if (attrs->num == 0) { + ERROR_OUT(ret, EINVAL, fail); + } + + msg = ldb_msg_new(state); + if (!msg) { + ERROR_OUT(ret, ENOMEM, fail); + } + + msg->dn = entry_dn; + + msg->elements = talloc_array(msg, struct ldb_message_element, attrs->num); + if (!msg->elements) { + ERROR_OUT(ret, ENOMEM, fail); + } + + for (i = 0; i < attrs->num; i++) { + msg->elements[i] = attrs->a[i]; + msg->elements[i].flags = mod_op; + } + + msg->num_elements = attrs->num; + + ret = ldb_build_mod_req(&ldbreq, handle->ctx->ldb, state, msg, + NULL, NULL, NULL, NULL); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to build modify request: %s(%d)[%s]\n", + ldb_strerror(ret), ret, ldb_errstring(handle->ctx->ldb))); + ERROR_OUT(ret, sysdb_error_to_errno(ret), fail); + } + + subreq = sldb_request_send(state, ev, handle->ctx->ldb, ldbreq); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_op_default_done, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +int sysdb_set_entry_attr_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Replace-Attributes-On-User============================================ */ + +static void sysdb_set_user_attr_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_set_user_attr_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, + struct sysdb_attrs *attrs, + int mod_op) +{ + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; + struct ldb_dn *dn; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->ignore_not_found = false; + state->ldbreply = NULL; + + dn = sysdb_user_dn(handle->ctx, state, domain->name, name); + if (!dn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + subreq = sysdb_set_entry_attr_send(state, ev, handle, dn, attrs, mod_op); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_set_user_attr_done, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_set_user_attr_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_set_entry_attr_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_set_user_attr_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Replace-Attributes-On-Group=========================================== */ + +static void sysdb_set_group_attr_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_set_group_attr_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, + struct sysdb_attrs *attrs, + int mod_op) +{ + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; + struct ldb_dn *dn; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->ignore_not_found = false; + state->ldbreply = NULL; + + dn = sysdb_group_dn(handle->ctx, state, domain->name, name); + if (!dn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + subreq = sysdb_set_entry_attr_send(state, ev, handle, dn, attrs, mod_op); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_set_group_attr_done, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_set_group_attr_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_set_entry_attr_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_set_group_attr_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Get-New-ID============================================================ */ + +struct sysdb_get_new_id_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + struct sss_domain_info *domain; + + struct ldb_dn *base_dn; + struct ldb_message *base; + + struct ldb_message **v_msgs; + int v_count; + + uint32_t new_id; +}; + +static void sysdb_get_new_id_base(struct tevent_req *subreq); +static void sysdb_get_new_id_verify(struct tevent_req *subreq); +static void sysdb_get_new_id_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_get_new_id_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain) +{ + struct tevent_req *req, *subreq; + struct sysdb_get_new_id_state *state; + static const char *attrs[] = { SYSDB_NEXTID, NULL }; + struct ldb_request *ldbreq; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_get_new_id_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->domain = domain; + state->base = NULL; + state->v_msgs = NULL; + state->v_count = 0; + state->new_id = 0; + + state->base_dn = sysdb_domain_dn(handle->ctx, state, domain->name); + if (!state->base_dn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + ret = ldb_build_search_req(&ldbreq, handle->ctx->ldb, state, + state->base_dn, LDB_SCOPE_BASE, + SYSDB_NEXTID_FILTER, attrs, + NULL, NULL, NULL, NULL); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to build search request: %s(%d)[%s]\n", + ldb_strerror(ret), ret, ldb_errstring(handle->ctx->ldb))); + ERROR_OUT(ret, sysdb_error_to_errno(ret), fail); + } + + subreq = sldb_request_send(state, ev, handle->ctx->ldb, ldbreq); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_get_new_id_base, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_get_new_id_base(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_get_new_id_state *state = tevent_req_data(req, + struct sysdb_get_new_id_state); + static const char *attrs[] = { SYSDB_UIDNUM, SYSDB_GIDNUM, NULL }; + struct ldb_reply *ldbreply; + struct ldb_request *ldbreq; + char *filter; + int ret; + + ret = sldb_request_recv(subreq, state, &ldbreply); + if (ret) { + talloc_zfree(subreq); + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + switch (ldbreply->type) { + case LDB_REPLY_ENTRY: + if (state->base) { + DEBUG(1, ("More than one reply for a base search ?! " + "DB seems corrupted, aborting.")); + tevent_req_error(req, EFAULT); + return; + } + + state->base = talloc_move(state, &ldbreply->message); + if (!state->base) { + DEBUG(6, ("Error: Out of memory!\n")); + tevent_req_error(req, ENOMEM); + return; + } + + /* just return, wait for a LDB_REPLY_DONE entry */ + talloc_zfree(ldbreply); + return; + + case LDB_REPLY_DONE: + break; + + default: + /* unexpected stuff */ + DEBUG(6, ("Error: Unknown error\n")); + tevent_req_error(req, EIO); + talloc_zfree(ldbreply); + return; + } + + talloc_zfree(subreq); + + if (state->base) { + state->new_id = get_attr_as_uint32(state->base, SYSDB_NEXTID); + if (state->new_id == (uint32_t)(-1)) { + DEBUG(1, ("Invalid Next ID in domain %s\n", state->domain->name)); + tevent_req_error(req, ERANGE); + return; + } + + if (state->new_id < state->domain->id_min) { + state->new_id = state->domain->id_min; + } + + if ((state->domain->id_max != 0) && + (state->new_id > state->domain->id_max)) { + DEBUG(0, ("Failed to allocate new id, out of range (%u/%u)\n", + state->new_id, state->domain->id_max)); + tevent_req_error(req, ERANGE); + return; + } + + } else { + /* looks like the domain is not initialized yet, use min_id */ + state->new_id = state->domain->id_min; + } + + /* verify the id is actually really free. + * search all entries with id >= new_id and < max_id */ + if (state->domain->id_max) { + filter = talloc_asprintf(state, + "(|(&(%s>=%u)(%s<=%u))(&(%s>=%u)(%s<=%u)))", + SYSDB_UIDNUM, state->new_id, + SYSDB_UIDNUM, state->domain->id_max, + SYSDB_GIDNUM, state->new_id, + SYSDB_GIDNUM, state->domain->id_max); + } + else { + filter = talloc_asprintf(state, + "(|(%s>=%u)(%s>=%u))", + SYSDB_UIDNUM, state->new_id, + SYSDB_GIDNUM, state->new_id); + } + if (!filter) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + + ret = ldb_build_search_req(&ldbreq, state->handle->ctx->ldb, state, + state->base_dn, LDB_SCOPE_SUBTREE, + filter, attrs, + NULL, NULL, NULL, NULL); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to build search request: %s(%d)[%s]\n", + ldb_strerror(ret), ret, + ldb_errstring(state->handle->ctx->ldb))); + tevent_req_error(req, sysdb_error_to_errno(ret)); + return; + } + + subreq = sldb_request_send(state, state->ev, + state->handle->ctx->ldb, ldbreq); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_get_new_id_verify, req); + + return; +} + +static void sysdb_get_new_id_verify(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_get_new_id_state *state = tevent_req_data(req, + struct sysdb_get_new_id_state); + struct ldb_reply *ldbreply; + struct ldb_request *ldbreq; + struct ldb_message *msg; + int ret, i; + + ret = sldb_request_recv(subreq, state, &ldbreply); + if (ret) { + talloc_zfree(subreq); + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + switch (ldbreply->type) { + case LDB_REPLY_ENTRY: + state->v_msgs = talloc_realloc(state, state->v_msgs, + struct ldb_message *, + state->v_count + 2); + if (!state->v_msgs) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + + state->v_msgs[state->v_count] = talloc_move(state, &ldbreply->message); + if (!state->v_msgs[state->v_count]) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + state->v_count++; + + /* just return, wait for a LDB_REPLY_DONE entry */ + talloc_zfree(ldbreply); + return; + + case LDB_REPLY_DONE: + break; + + default: + /* unexpected stuff */ + DEBUG(6, ("Error: Unknown error\n")); + tevent_req_error(req, EIO); + talloc_zfree(ldbreply); + return; + } + + talloc_zfree(subreq); + + /* if anything was found, find the maximum and increment past it */ + if (state->v_count) { + uint32_t id; + + for (i = 0; i < state->v_count; i++) { + id = get_attr_as_uint32(state->v_msgs[i], SYSDB_UIDNUM); + if (id != (uint32_t)(-1)) { + if (id > state->new_id) state->new_id = id; + } + id = get_attr_as_uint32(state->v_msgs[i], SYSDB_GIDNUM); + if (id != (uint32_t)(-1)) { + if (id > state->new_id) state->new_id = id; + } + } + + state->new_id++; + + /* check again we are not falling out of range */ + if ((state->domain->id_max != 0) && + (state->new_id > state->domain->id_max)) { + DEBUG(0, ("Failed to allocate new id, out of range (%u/%u)\n", + state->new_id, state->domain->id_max)); + tevent_req_error(req, ERANGE); + return; + } + + talloc_zfree(state->v_msgs); + state->v_count = 0; + } + + /* finally store the new next id */ + msg = ldb_msg_new(state); + if (!msg) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + msg->dn = state->base_dn; + + ret = add_ulong(msg, LDB_FLAG_MOD_REPLACE, + SYSDB_NEXTID, state->new_id + 1); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + ret = ldb_build_mod_req(&ldbreq, state->handle->ctx->ldb, state, msg, + NULL, NULL, NULL, NULL); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to build modify request: %s(%d)[%s]\n", + ldb_strerror(ret), ret, + ldb_errstring(state->handle->ctx->ldb))); + tevent_req_error(req, ret); + return; + } + + subreq = sldb_request_send(state, state->ev, + state->handle->ctx->ldb, ldbreq); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_get_new_id_done, req); +} + +static void sysdb_get_new_id_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_get_new_id_state *state = tevent_req_data(req, + struct sysdb_get_new_id_state); + struct ldb_reply *ldbreply; + int ret; + + ret = sldb_request_recv(subreq, state, &ldbreply); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + if (ldbreply->type != LDB_REPLY_DONE) { + DEBUG(6, ("Error: %d (%s)\n", EIO, strerror(EIO))); + tevent_req_error(req, EIO); + return; + } + + tevent_req_done(req); +} + +int sysdb_get_new_id_recv(struct tevent_req *req, uint32_t *id) +{ + struct sysdb_get_new_id_state *state = tevent_req_data(req, + struct sysdb_get_new_id_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *id = state->new_id; + + return EOK; +} + + +/* =Add-Basic-User-NO-CHECKS============================================== */ + +struct tevent_req *sysdb_add_basic_user_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, + uid_t uid, gid_t gid, + const char *gecos, + const char *homedir, + const char *shell) +{ + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; + struct ldb_request *ldbreq; + struct ldb_message *msg; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->ignore_not_found = false; + state->ldbreply = NULL; + + msg = ldb_msg_new(state); + if (!msg) { + ERROR_OUT(ret, ENOMEM, fail); + } + + /* user dn */ + msg->dn = sysdb_user_dn(handle->ctx, msg, domain->name, name); + if (!msg->dn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + ret = add_string(msg, LDB_FLAG_MOD_ADD, "objectClass", SYSDB_USER_CLASS); + if (ret) goto fail; + + ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_NAME, name); + if (ret) goto fail; + + ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_UIDNUM, (unsigned long)uid); + if (ret) goto fail; + + ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_GIDNUM, (unsigned long)gid); + if (ret) goto fail; + + /* We set gecos to be the same as fullname on user creation, + * But we will not enforce coherency after that, it's up to + * admins to decide if they want to keep it in sync if they change + * one of the 2 */ + if (gecos && *gecos) { + ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_FULLNAME, gecos); + if (ret) goto fail; + ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_GECOS, gecos); + if (ret) goto fail; + } + + if (homedir && *homedir) { + ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_HOMEDIR, homedir); + if (ret) goto fail; + } + + if (shell && *shell) { + ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_SHELL, shell); + if (ret) goto fail; + } + + /* creation time */ + ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_CREATE_TIME, + (unsigned long)time(NULL)); + if (ret) goto fail; + + + ret = ldb_build_add_req(&ldbreq, handle->ctx->ldb, state, msg, + NULL, NULL, NULL, NULL); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to build modify request: %s(%d)[%s]\n", + ldb_strerror(ret), ret, ldb_errstring(handle->ctx->ldb))); + ERROR_OUT(ret, sysdb_error_to_errno(ret), fail); + } + + subreq = sldb_request_send(state, ev, handle->ctx->ldb, ldbreq); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_op_default_done, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +int sysdb_add_basic_user_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Add-User-Function===================================================== */ + +struct sysdb_add_user_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + struct sss_domain_info *domain; + + const char *name; + uid_t uid; + gid_t gid; + const char *gecos; + const char *homedir; + const char *shell; + struct sysdb_attrs *attrs; + + int cache_timeout; +}; + +static void sysdb_add_user_group_check(struct tevent_req *subreq); +static void sysdb_add_user_uid_check(struct tevent_req *subreq); +static void sysdb_add_user_basic_done(struct tevent_req *subreq); +static void sysdb_add_user_get_id_done(struct tevent_req *subreq); +static void sysdb_add_user_set_id_done(struct tevent_req *subreq); +static void sysdb_add_user_set_attrs(struct tevent_req *req); +static void sysdb_add_user_set_attrs_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_add_user_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, + uid_t uid, gid_t gid, + const char *gecos, + const char *homedir, + const char *shell, + struct sysdb_attrs *attrs, + int cache_timeout) +{ + struct tevent_req *req, *subreq; + struct sysdb_add_user_state *state; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_add_user_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->domain = domain; + state->name = name; + state->uid = uid; + state->gid = gid; + state->gecos = gecos; + state->homedir = homedir; + state->shell = shell; + state->attrs = attrs; + state->cache_timeout = cache_timeout; + + if (handle->ctx->mpg) { + if (gid != 0) { + DEBUG(0, ("Cannot add user with arbitrary GID in MPG domain!\n")); + ERROR_OUT(ret, EINVAL, fail); + } + state->gid = state->uid; + } + + if (domain->id_max != 0 && uid != 0 && + (uid < domain->id_min || uid > domain->id_max)) { + DEBUG(2, ("Supplied uid [%d] is not in the allowed range [%d-%d].\n", + uid, domain->id_min, domain->id_max)); + ERROR_OUT(ret, ERANGE, fail); + } + + if (domain->id_max != 0 && gid != 0 && + (gid < domain->id_min || gid > domain->id_max)) { + DEBUG(2, ("Supplied gid [%d] is not in the allowed range [%d-%d].\n", + gid, domain->id_min, domain->id_max)); + ERROR_OUT(ret, ERANGE, fail); + } + + if (handle->ctx->mpg) { + /* In MPG domains you can't have groups with the same name as users, + * search if a group with the same name exists. + * Don't worry about users, if we try to add a user with the same + * name the operation will fail */ + + subreq = sysdb_search_group_by_name_send(state, ev, NULL, handle, + domain, name, NULL); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_add_user_group_check, req); + return req; + } + + /* check no other user with the same uid exist */ + if (state->uid != 0) { + subreq = sysdb_search_user_by_uid_send(state, ev, NULL, handle, + domain, uid, NULL); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_add_user_uid_check, req); + return req; + } + + /* try to add the user */ + subreq = sysdb_add_basic_user_send(state, ev, handle, + domain, name, uid, gid, + gecos, homedir, shell); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_add_user_basic_done, req); + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_add_user_group_check(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_add_user_state *state = tevent_req_data(req, + struct sysdb_add_user_state); + struct ldb_message *msg; + int ret; + + /* We can succeed only if we get an ENOENT error, which means no groups + * with the same name exist. + * If any other error is returned fail as well. */ + ret = sysdb_search_group_recv(subreq, state, &msg); + talloc_zfree(subreq); + if (ret != ENOENT) { + if (ret == EOK) ret = EEXIST; + tevent_req_error(req, ret); + return; + } + + /* check no other user with the same uid exist */ + if (state->uid != 0) { + subreq = sysdb_search_user_by_uid_send(state, state->ev, + NULL, state->handle, + state->domain, state->uid, + NULL); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_user_uid_check, req); + return; + } + + /* try to add the user */ + subreq = sysdb_add_basic_user_send(state, state->ev, state->handle, + state->domain, state->name, + state->uid, state->gid, + state->gecos, + state->homedir, + state->shell); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_user_basic_done, req); +} + +static void sysdb_add_user_uid_check(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_add_user_state *state = tevent_req_data(req, + struct sysdb_add_user_state); + struct ldb_message *msg; + int ret; + + /* We can succeed only if we get an ENOENT error, which means no user + * with the same uid exist. + * If any other error is returned fail as well. */ + ret = sysdb_search_user_recv(subreq, state, &msg); + talloc_zfree(subreq); + if (ret != ENOENT) { + if (ret == EOK) ret = EEXIST; + tevent_req_error(req, ret); + return; + } + + /* try to add the user */ + subreq = sysdb_add_basic_user_send(state, state->ev, state->handle, + state->domain, state->name, + state->uid, state->gid, + state->gecos, + state->homedir, + state->shell); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_user_basic_done, req); +} + +static void sysdb_add_user_basic_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_add_user_state *state = tevent_req_data(req, + struct sysdb_add_user_state); + int ret; + + ret = sysdb_add_basic_user_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + if (state->uid == 0) { + subreq = sysdb_get_new_id_send(state, + state->ev, state->handle, + state->domain); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_user_get_id_done, req); + return; + } + + sysdb_add_user_set_attrs(req); +} + +static void sysdb_add_user_get_id_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_add_user_state *state = tevent_req_data(req, + struct sysdb_add_user_state); + struct sysdb_attrs *id_attrs; + uint32_t id; + int ret; + + ret = sysdb_get_new_id_recv(subreq, &id); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + if (state->uid == 0) { + id_attrs = sysdb_new_attrs(state); + if (!id_attrs) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + ret = sysdb_attrs_add_uint32(id_attrs, SYSDB_UIDNUM, id); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + if (state->handle->ctx->mpg) { + ret = sysdb_attrs_add_uint32(id_attrs, SYSDB_GIDNUM, id); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + } + + subreq = sysdb_set_user_attr_send(state, state->ev, state->handle, + state->domain, state->name, + id_attrs, SYSDB_MOD_REP); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_user_set_id_done, req); + return; + } + + sysdb_add_user_set_attrs(req); +} + +static void sysdb_add_user_set_id_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_add_user_state *state = tevent_req_data(req, + struct sysdb_add_user_state); + int ret; + + ret = sysdb_set_user_attr_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + if (state->attrs) { + } + + tevent_req_done(req); +} + +static void sysdb_add_user_set_attrs(struct tevent_req *req) +{ + struct sysdb_add_user_state *state = tevent_req_data(req, + struct sysdb_add_user_state); + struct tevent_req *subreq; + time_t now = time(NULL); + int ret; + + if (!state->attrs) { + state->attrs = sysdb_new_attrs(state); + if (!state->attrs) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + } + + ret = sysdb_attrs_add_time_t(state->attrs, SYSDB_LAST_UPDATE, now); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + ret = sysdb_attrs_add_time_t(state->attrs, SYSDB_CACHE_EXPIRE, + ((state->cache_timeout) ? + (now + state->cache_timeout) : 0)); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + subreq = sysdb_set_user_attr_send(state, state->ev, + state->handle, state->domain, + state->name, state->attrs, + SYSDB_MOD_REP); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_user_set_attrs_done, req); +} + +static void sysdb_add_user_set_attrs_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_set_user_attr_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_add_user_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Add-Basic-Group-NO-CHECKS============================================= */ + +struct tevent_req *sysdb_add_basic_group_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, gid_t gid) +{ + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; + struct ldb_request *ldbreq; + struct ldb_message *msg; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->ignore_not_found = false; + state->ldbreply = NULL; + + msg = ldb_msg_new(state); + if (!msg) { + ERROR_OUT(ret, ENOMEM, fail); + } + + /* user dn */ + msg->dn = sysdb_group_dn(handle->ctx, msg, domain->name, name); + if (!msg->dn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + ret = add_string(msg, LDB_FLAG_MOD_ADD, "objectClass", SYSDB_GROUP_CLASS); + if (ret) goto fail; + + ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_NAME, name); + if (ret) goto fail; + + ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_GIDNUM, (unsigned long)gid); + if (ret) goto fail; + + /* creation time */ + ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_CREATE_TIME, + (unsigned long)time(NULL)); + if (ret) goto fail; + + + ret = ldb_build_add_req(&ldbreq, handle->ctx->ldb, state, msg, + NULL, NULL, NULL, NULL); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to build modify request: %s(%d)[%s]\n", + ldb_strerror(ret), ret, ldb_errstring(handle->ctx->ldb))); + ERROR_OUT(ret, sysdb_error_to_errno(ret), fail); + } + + subreq = sldb_request_send(state, ev, handle->ctx->ldb, ldbreq); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_op_default_done, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +int sysdb_add_basic_group_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Add-Group-Function==================================================== */ + +struct sysdb_add_group_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + struct sss_domain_info *domain; + + const char *name; + gid_t gid; + struct sysdb_attrs *attrs; + + int cache_timeout; +}; + +static void sysdb_add_group_user_check(struct tevent_req *subreq); +static void sysdb_add_group_gid_check(struct tevent_req *subreq); +static void sysdb_add_group_basic_done(struct tevent_req *subreq); +static void sysdb_add_group_get_id_done(struct tevent_req *subreq); +static void sysdb_add_group_set_attrs(struct tevent_req *req); +static void sysdb_add_group_set_attrs_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_add_group_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, gid_t gid, + struct sysdb_attrs *attrs, + int cache_timeout) +{ + struct tevent_req *req, *subreq; + struct sysdb_add_group_state *state; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_add_group_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->domain = domain; + state->name = name; + state->gid = gid; + state->attrs = attrs; + state->cache_timeout = cache_timeout; + + if (domain->id_max != 0 && gid != 0 && + (gid < domain->id_min || gid > domain->id_max)) { + DEBUG(2, ("Supplied gid [%d] is not in the allowed range [%d-%d].\n", + gid, domain->id_min, domain->id_max)); + ERROR_OUT(ret, ERANGE, fail); + } + + if (handle->ctx->mpg) { + /* In MPG domains you can't have groups with the same name as users, + * search if a group with the same name exists. + * Don't worry about users, if we try to add a user with the same + * name the operation will fail */ + + subreq = sysdb_search_user_by_name_send(state, ev, NULL, handle, + domain, name, NULL); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_add_group_user_check, req); + return req; + } + + /* check no other groups with the same gid exist */ + if (state->gid != 0) { + subreq = sysdb_search_group_by_gid_send(state, ev, NULL, handle, + domain, gid, NULL); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_add_group_gid_check, req); + return req; + } + + /* try to add the group */ + subreq = sysdb_add_basic_group_send(state, ev, handle, + domain, name, gid); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_add_group_basic_done, req); + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_add_group_user_check(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_add_group_state *state = tevent_req_data(req, + struct sysdb_add_group_state); + struct ldb_message *msg; + int ret; + + /* We can succeed only if we get an ENOENT error, which means no users + * with the same name exist. + * If any other error is returned fail as well. */ + ret = sysdb_search_user_recv(subreq, state, &msg); + talloc_zfree(subreq); + if (ret != ENOENT) { + if (ret == EOK) ret = EEXIST; + tevent_req_error(req, ret); + return; + } + + /* check no other group with the same gid exist */ + if (state->gid != 0) { + subreq = sysdb_search_group_by_gid_send(state, state->ev, + NULL, state->handle, + state->domain, state->gid, + NULL); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_group_gid_check, req); + return; + } + + /* try to add the group */ + subreq = sysdb_add_basic_group_send(state, state->ev, + state->handle, state->domain, + state->name, state->gid); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_group_basic_done, req); +} + +static void sysdb_add_group_gid_check(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_add_group_state *state = tevent_req_data(req, + struct sysdb_add_group_state); + struct ldb_message *msg; + int ret; + + /* We can succeed only if we get an ENOENT error, which means no group + * with the same gid exist. + * If any other error is returned fail as well. */ + ret = sysdb_search_group_recv(subreq, state, &msg); + talloc_zfree(subreq); + if (ret != ENOENT) { + if (ret == EOK) ret = EEXIST; + tevent_req_error(req, ret); + return; + } + + /* try to add the group */ + subreq = sysdb_add_basic_group_send(state, state->ev, + state->handle, state->domain, + state->name, state->gid); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_group_basic_done, req); +} + +static void sysdb_add_group_basic_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_add_group_state *state = tevent_req_data(req, + struct sysdb_add_group_state); + int ret; + + ret = sysdb_add_basic_group_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + if (state->gid == 0) { + subreq = sysdb_get_new_id_send(state, + state->ev, state->handle, + state->domain); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_group_get_id_done, req); + return; + } + + sysdb_add_group_set_attrs(req); +} + +static void sysdb_add_group_get_id_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_add_group_state *state = tevent_req_data(req, + struct sysdb_add_group_state); + uint32_t id; + int ret; + + ret = sysdb_get_new_id_recv(subreq, &id); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + if (state->gid == 0) { + if (!state->attrs) { + state->attrs = sysdb_new_attrs(state); + if (!state->attrs) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + } + + ret = sysdb_attrs_add_uint32(state->attrs, SYSDB_GIDNUM, id); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + } + + sysdb_add_group_set_attrs(req); +} + +static void sysdb_add_group_set_attrs(struct tevent_req *req) +{ + struct sysdb_add_group_state *state = tevent_req_data(req, + struct sysdb_add_group_state); + struct tevent_req *subreq; + time_t now = time(NULL); + int ret; + + if (!state->attrs) { + state->attrs = sysdb_new_attrs(state); + if (!state->attrs) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + } + + ret = sysdb_attrs_add_time_t(state->attrs, SYSDB_LAST_UPDATE, now); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + ret = sysdb_attrs_add_time_t(state->attrs, SYSDB_CACHE_EXPIRE, + ((state->cache_timeout) ? + (now + state->cache_timeout) : 0)); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + subreq = sysdb_set_group_attr_send(state, state->ev, + state->handle, state->domain, + state->name, state->attrs, + SYSDB_MOD_REP); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_add_group_set_attrs_done, req); +} + +static void sysdb_add_group_set_attrs_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_set_group_attr_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_add_group_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Add-Or-Remove-Group-Memeber=========================================== */ + +/* mod_op must be either SYSDB_MOD_ADD or SYSDB_MOD_DEL */ +struct tevent_req *sysdb_mod_group_member_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct ldb_dn *member_dn, + struct ldb_dn *group_dn, + int mod_op) +{ + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; + struct ldb_request *ldbreq; + struct ldb_message *msg; + const char *dn; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->ignore_not_found = false; + state->ldbreply = NULL; + + msg = ldb_msg_new(state); + if (!msg) { + ERROR_OUT(ret, ENOMEM, fail); + } + + msg->dn = group_dn; + ret = ldb_msg_add_empty(msg, SYSDB_MEMBER, mod_op, NULL); + if (ret != LDB_SUCCESS) { + ERROR_OUT(ret, ENOMEM, fail); + } + + dn = ldb_dn_get_linearized(member_dn); + if (!dn) { + ERROR_OUT(ret, EINVAL, fail); + } + + ret = ldb_msg_add_fmt(msg, SYSDB_MEMBER, "%s", dn); + if (ret != LDB_SUCCESS) { + ERROR_OUT(ret, EINVAL, fail); + } + + ret = ldb_build_mod_req(&ldbreq, handle->ctx->ldb, state, msg, + NULL, NULL, NULL, NULL); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to build modify request: %s(%d)[%s]\n", + ldb_strerror(ret), ret, ldb_errstring(handle->ctx->ldb))); + ERROR_OUT(ret, sysdb_error_to_errno(ret), fail); + } + + subreq = sldb_request_send(state, ev, handle->ctx->ldb, ldbreq); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_op_default_done, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +int sysdb_mod_group_member_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Store-Users-(Native/Legacy)-(replaces-existing-data)================== */ + +/* if one of the basic attributes is empty ("") as opposed to NULL, + * this will just remove it */ + +struct sysdb_store_user_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + struct sss_domain_info *domain; + + const char *name; + uid_t uid; + gid_t gid; + const char *gecos; + const char *homedir; + const char *shell; + struct sysdb_attrs *attrs; + + uint64_t cache_timeout; +}; + +static void sysdb_store_user_check(struct tevent_req *subreq); +static void sysdb_store_user_add_done(struct tevent_req *subreq); +static void sysdb_store_user_attr_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_store_user_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, + const char *pwd, + uid_t uid, gid_t gid, + const char *gecos, + const char *homedir, + const char *shell, + struct sysdb_attrs *attrs, + uint64_t cache_timeout) +{ + struct tevent_req *req, *subreq; + struct sysdb_store_user_state *state; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_store_user_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->domain = domain; + state->name = name; + state->uid = uid; + state->gid = gid; + state->gecos = gecos; + state->homedir = homedir; + state->shell = shell; + state->attrs = attrs; + state->cache_timeout = cache_timeout; + + if (pwd && (domain->legacy_passwords || !*pwd)) { + ret = sysdb_attrs_add_string(state->attrs, SYSDB_PWD, pwd); + if (ret) goto fail; + } + + subreq = sysdb_search_user_by_name_send(state, ev, NULL, handle, + domain, name, NULL); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_store_user_check, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_store_user_check(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_store_user_state *state = tevent_req_data(req, + struct sysdb_store_user_state); + struct ldb_message *msg; + time_t now = time(NULL); + int ret; + + ret = sysdb_search_user_recv(subreq, state, &msg); + talloc_zfree(subreq); + if (ret && ret != ENOENT) { + tevent_req_error(req, ret); + return; + } + + if (ret == ENOENT) { + /* users doesn't exist, turn into adding a user */ + subreq = sysdb_add_user_send(state, state->ev, state->handle, + state->domain, state->name, + state->uid, state->gid, + state->gecos, state->homedir, + state->shell, state->attrs, + state->cache_timeout); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_store_user_add_done, req); + return; + } + + /* the user exists, let's just replace attributes when set */ + if (!state->attrs) { + state->attrs = sysdb_new_attrs(state); + if (!state->attrs) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + } + + if (state->uid) { + ret = sysdb_attrs_add_uint32(state->attrs, SYSDB_UIDNUM, state->uid); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + } + + if (state->gid) { + ret = sysdb_attrs_add_uint32(state->attrs, SYSDB_GIDNUM, state->gid); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + } + + if (state->uid && !state->gid && state->handle->ctx->mpg) { + ret = sysdb_attrs_add_uint32(state->attrs, SYSDB_GIDNUM, state->uid); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + } + + if (state->gecos) { + ret = sysdb_attrs_add_string(state->attrs, SYSDB_GECOS, state->gecos); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + } + + if (state->homedir) { + ret = sysdb_attrs_add_string(state->attrs, + SYSDB_HOMEDIR, state->homedir); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + } + + if (state->shell) { + ret = sysdb_attrs_add_string(state->attrs, SYSDB_SHELL, state->shell); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + } + + ret = sysdb_attrs_add_time_t(state->attrs, SYSDB_LAST_UPDATE, now); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + ret = sysdb_attrs_add_time_t(state->attrs, SYSDB_CACHE_EXPIRE, + ((state->cache_timeout) ? + (now + state->cache_timeout) : 0)); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + subreq = sysdb_set_user_attr_send(state, state->ev, + state->handle, state->domain, + state->name, state->attrs, + SYSDB_MOD_REP); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_store_user_attr_done, req); +} + +static void sysdb_store_user_add_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_add_user_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +static void sysdb_store_user_attr_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_set_user_attr_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_store_user_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + +/* =Store-Group-(Native/Legacy)-(replaces-existing-data)================== */ + +/* this function does not check that all user members are actually present */ + +struct sysdb_store_group_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + struct sss_domain_info *domain; + + const char *name; + gid_t gid; + + struct sysdb_attrs *attrs; + + uint64_t cache_timeout; +}; + +static void sysdb_store_group_check(struct tevent_req *subreq); +static void sysdb_store_group_add_done(struct tevent_req *subreq); +static void sysdb_store_group_attr_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_store_group_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, + gid_t gid, + struct sysdb_attrs *attrs, + uint64_t cache_timeout) +{ + struct tevent_req *req, *subreq; + struct sysdb_store_group_state *state; + static const char *src_attrs[] = { SYSDB_NAME, SYSDB_GIDNUM, + SYSDB_ORIG_MODSTAMP, NULL }; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_store_group_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->domain = domain; + state->name = name; + state->gid = gid; + state->attrs = attrs; + state->cache_timeout = cache_timeout; + + subreq = sysdb_search_group_by_name_send(state, ev, NULL, handle, + domain, name, src_attrs); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_store_group_check, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_store_group_check(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_store_group_state *state = tevent_req_data(req, + struct sysdb_store_group_state); + struct ldb_message *msg; + time_t now = time(NULL); + bool new_group = false; + int ret; + + ret = sysdb_search_group_recv(subreq, state, &msg); + talloc_zfree(subreq); + if (ret && ret != ENOENT) { + tevent_req_error(req, ret); + return; + } + if (ret == ENOENT) { + new_group = true; + } + + /* FIXME: use the remote modification timestamp to know if the + * group needs any update */ + + if (new_group) { + /* group doesn't exist, turn into adding a group */ + subreq = sysdb_add_group_send(state, state->ev, state->handle, + state->domain, state->name, + state->gid, state->attrs, + state->cache_timeout); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_store_group_add_done, req); + + return; + } + + /* the group exists, let's just replace attributes when set */ + + if (!state->attrs) { + state->attrs = sysdb_new_attrs(state); + if (!state->attrs) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + } + + if (state->gid) { + ret = sysdb_attrs_add_uint32(state->attrs, SYSDB_GIDNUM, state->gid); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + } + + ret = sysdb_attrs_add_time_t(state->attrs, SYSDB_LAST_UPDATE, now); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + ret = sysdb_attrs_add_time_t(state->attrs, SYSDB_CACHE_EXPIRE, + ((state->cache_timeout) ? + (now + state->cache_timeout) : 0)); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + subreq = sysdb_set_group_attr_send(state, state->ev, + state->handle, state->domain, + state->name, state->attrs, + SYSDB_MOD_REP); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_store_group_attr_done, req); +} + +static void sysdb_store_group_add_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_add_group_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +static void sysdb_store_group_attr_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_set_group_attr_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_store_group_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Add-User-to-Group(Native/Legacy)====================================== */ + +static void sysdb_add_group_member_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_add_group_member_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *group, + const char *user) +{ + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; + struct ldb_dn *group_dn, *user_dn; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->ignore_not_found = false; + state->ldbreply = NULL; + + group_dn = sysdb_group_dn(handle->ctx, state, domain->name, group); + if (!group_dn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + user_dn = sysdb_user_dn(handle->ctx, state, domain->name, user); + if (!user_dn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + subreq = sysdb_mod_group_member_send(state, ev, handle, + user_dn, group_dn, + SYSDB_MOD_ADD); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_add_group_member_done, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_add_group_member_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_mod_group_member_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_add_group_member_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Remove-member-from-Group(Native/Legacy)=============================== */ + +static void sysdb_remove_group_member_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_remove_group_member_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *group, + const char *user) +{ + struct tevent_req *req, *subreq; + struct sysdb_op_state *state; + struct ldb_dn *group_dn, *user_dn; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->ignore_not_found = false; + state->ldbreply = NULL; + + group_dn = sysdb_group_dn(handle->ctx, state, domain->name, group); + if (!group_dn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + user_dn = sysdb_user_dn(handle->ctx, state, domain->name, user); + if (!user_dn) { + ERROR_OUT(ret, ENOMEM, fail); + } + + subreq = sysdb_mod_group_member_send(state, ev, handle, + user_dn, group_dn, + SYSDB_MOD_DEL); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_remove_group_member_done, req); + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_remove_group_member_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_mod_group_member_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_remove_group_member_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Password-Caching====================================================== */ + +struct sysdb_cache_pw_state { + struct tevent_context *ev; + struct sss_domain_info *domain; + + const char *username; + struct sysdb_attrs *attrs; + + struct sysdb_handle *handle; + bool commit; +}; + +static void sysdb_cache_password_trans(struct tevent_req *subreq); +static void sysdb_cache_password_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_cache_password_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *username, + const char *password) +{ + struct tevent_req *req, *subreq; + struct sysdb_cache_pw_state *state; + char *hash = NULL; + char *salt; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_cache_pw_state); + if (!req) return NULL; + + state->ev = ev; + state->domain = domain; + state->username = username; + + ret = s3crypt_gen_salt(state, &salt); + if (ret) { + DEBUG(4, ("Failed to generate random salt.\n")); + goto fail; + } + + ret = s3crypt_sha512(state, password, salt, &hash); + if (ret) { + DEBUG(4, ("Failed to create password hash.\n")); + goto fail; + } + + state->attrs = sysdb_new_attrs(state); + if (!state->attrs) { + ERROR_OUT(ret, ENOMEM, fail); + } + + ret = sysdb_attrs_add_string(state->attrs, SYSDB_CACHEDPWD, hash); + if (ret) goto fail; + + /* FIXME: should we use a different attribute for chache passwords ?? */ + ret = sysdb_attrs_add_long(state->attrs, "lastCachedPasswordChange", + (long)time(NULL)); + if (ret) goto fail; + + ret = sysdb_attrs_add_uint32(state->attrs, SYSDB_FAILED_LOGIN_ATTEMPTS, 0U); + if (ret) goto fail; + + state->handle = NULL; + + if (handle) { + state->handle = handle; + state->commit = false; + + subreq = sysdb_set_user_attr_send(state, state->ev, state->handle, + state->domain, state->username, + state->attrs, SYSDB_MOD_REP); + if (!subreq) { + ERROR_OUT(ret, ENOMEM, fail); + } + tevent_req_set_callback(subreq, sysdb_cache_password_done, req); + } else { + state->commit = true; + + subreq = sysdb_transaction_send(state, state->ev, sysdb); + if (!subreq) { + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sysdb_cache_password_trans, req); + } + + return req; + +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_cache_password_trans(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_cache_pw_state *state = tevent_req_data(req, + struct sysdb_cache_pw_state); + int ret; + + ret = sysdb_transaction_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + subreq = sysdb_set_user_attr_send(state, state->ev, state->handle, + state->domain, state->username, + state->attrs, SYSDB_MOD_REP); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_cache_password_done, req); +} + +static void sysdb_cache_password_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_cache_pw_state *state = tevent_req_data(req, + struct sysdb_cache_pw_state); + int ret; + + ret = sysdb_set_user_attr_recv(subreq); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + if (state->commit) { + subreq = sysdb_transaction_commit_send(state, state->ev, + state->handle); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_transaction_complete, req); + return; + } + + tevent_req_done(req); +} + +int sysdb_cache_password_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + +/* = sysdb_check_handle ================== */ +struct sysdb_check_handle_state { + struct tevent_context *ev; + struct sysdb_handle *handle; +}; + +static void sysdb_check_handle_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_check_handle_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle) +{ + struct tevent_req *req; + struct tevent_req *subreq; + struct sysdb_check_handle_state *state; + + if (sysdb == NULL && handle == NULL) { + DEBUG(1, ("Sysdb context not available.\n")); + return NULL; + } + + req = tevent_req_create(mem_ctx, &state, struct sysdb_check_handle_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ev = ev; + + if (handle != NULL) { + state->handle = talloc_memdup(state, handle, sizeof(struct sysdb_handle)); + if (state->handle == NULL) { + DEBUG(1, ("talloc_memdup failed.\n")); + tevent_req_error(req, ENOMEM); + } else { + tevent_req_done(req); + } + tevent_req_post(req, ev); + return req; + } + + state->handle = NULL; + + subreq = sysdb_operation_send(state, state->ev, sysdb); + if (!subreq) { + DEBUG(1, ("sysdb_operation_send failed.\n")); + tevent_req_error(req, ENOMEM); + tevent_req_post(req, ev); + return req; + } + tevent_req_set_callback(subreq, sysdb_check_handle_done, req); + + return req; +} + +static void sysdb_check_handle_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_check_handle_state *state = tevent_req_data(req, + struct sysdb_check_handle_state); + int ret; + + ret = sysdb_operation_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); + return; +} + +int sysdb_check_handle_recv(struct tevent_req *req, TALLOC_CTX *memctx, + struct sysdb_handle **handle) +{ + struct sysdb_check_handle_state *state = tevent_req_data(req, + struct sysdb_check_handle_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *handle = talloc_move(memctx, &state->handle); + + return EOK; + +} + +/* =Custom Search================== */ +struct sysdb_search_custom_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + + struct ldb_dn *basedn; + const char **attrs; + const char *filter; + int scope; + bool expect_not_more_than_one; + + size_t msgs_count; + struct ldb_message **msgs; +}; + +static void sysdb_search_custom_check_handle_done(struct tevent_req *subreq); +static void sysdb_search_custom_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_search_custom_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *filter, + const char *subtree_name, + const char **attrs) +{ + struct tevent_req *req, *subreq; + struct sysdb_search_custom_state *state; + int ret; + + if (sysdb == NULL && handle == NULL) return NULL; + + if (filter == NULL || subtree_name == NULL) return NULL; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_search_custom_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ev = ev; + state->handle = handle; + state->attrs = attrs; + state->filter = filter; + state->scope = LDB_SCOPE_SUBTREE; + state->expect_not_more_than_one = false; + state->msgs_count = 0; + state->msgs = NULL; + + if (sysdb == NULL) { + sysdb = handle->ctx; + } + state->basedn = sysdb_custom_subtree_dn(sysdb, state, domain->name, + subtree_name); + if (state->basedn == NULL) { + DEBUG(1, ("sysdb_custom_subtree_dn failed.\n")); + ret = ENOMEM; + goto fail; + } + if (!ldb_dn_validate(state->basedn)) { + DEBUG(1, ("Failed to create DN.\n")); + ret = EINVAL; + goto fail; + } + + subreq = sysdb_check_handle_send(state, state->ev, sysdb, state->handle); + if (!subreq) { + DEBUG(1, ("sysdb_check_handle_send failed.\n")); + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sysdb_search_custom_check_handle_done, req); + + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +struct tevent_req *sysdb_search_custom_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *object_name, + const char *subtree_name, + const char **attrs) +{ + struct tevent_req *req, *subreq; + struct sysdb_search_custom_state *state; + int ret; + + if (sysdb == NULL && handle == NULL) return NULL; + + if (object_name == NULL || subtree_name == NULL) return NULL; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_search_custom_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ev = ev; + state->handle = handle; + state->attrs = attrs; + state->filter = NULL; + state->scope = LDB_SCOPE_BASE; + state->expect_not_more_than_one = true; + state->msgs_count = 0; + state->msgs = NULL; + + if (sysdb == NULL) { + sysdb = handle->ctx; + } + state->basedn = sysdb_custom_dn(sysdb, state, domain->name, object_name, + subtree_name); + if (state->basedn == NULL) { + DEBUG(1, ("sysdb_custom_dn failed.\n")); + ret = ENOMEM; + goto fail; + } + if (!ldb_dn_validate(state->basedn)) { + DEBUG(1, ("Failed to create DN.\n")); + ret = EINVAL; + goto fail; + } + + subreq = sysdb_check_handle_send(state, state->ev, sysdb, state->handle); + if (!subreq) { + DEBUG(1, ("sysdb_check_handle_send failed.\n")); + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sysdb_search_custom_check_handle_done, req); + + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_search_custom_check_handle_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_search_custom_state *state = tevent_req_data(req, + struct sysdb_search_custom_state); + int ret; + + ret = sysdb_check_handle_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + subreq = sysdb_search_entry_send(state, state->ev, state->handle, + state->basedn, state->scope, + state->filter, state->attrs); + if (!subreq) { + DEBUG(1, ("sysdb_search_entry_send failed.\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_search_custom_done, req); + return; +} + +static void sysdb_search_custom_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_search_custom_state *state = tevent_req_data(req, + struct sysdb_search_custom_state); + int ret; + + ret = sysdb_search_entry_recv(subreq, state, &state->msgs_count, + &state->msgs); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + if (state->expect_not_more_than_one && state->msgs_count > 1) { + DEBUG(1, ("More than one result found.\n")); + tevent_req_error(req, EFAULT); + return; + } + + tevent_req_done(req); +} + +int sysdb_search_custom_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *msgs_count, + struct ldb_message ***msgs) +{ + struct sysdb_search_custom_state *state = tevent_req_data(req, + struct sysdb_search_custom_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *msgs_count = state->msgs_count; + *msgs = talloc_move(mem_ctx, &state->msgs); + + return EOK; +} + + +/* =Custom Store (replaces-existing-data)================== */ + +struct sysdb_store_custom_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + struct sss_domain_info *domain; + + const char *object_name; + const char *subtree_name; + struct ldb_dn *dn; + struct sysdb_attrs *attrs; + struct ldb_message *msg; +}; + +static void sysdb_store_custom_check_done(struct tevent_req *subreq); +static void sysdb_store_custom_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_store_custom_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *object_name, + const char *subtree_name, + struct sysdb_attrs *attrs) +{ + struct tevent_req *req, *subreq; + struct sysdb_store_custom_state *state; + int ret; + const char **search_attrs; + + if (object_name == NULL || subtree_name == NULL) return NULL; + + if (handle == NULL) { + DEBUG(1, ("Sysdb context not available.\n")); + return NULL; + } + + req = tevent_req_create(mem_ctx, &state, struct sysdb_store_custom_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ev = ev; + state->handle = handle; + state->domain = domain; + state->object_name = object_name; + state->subtree_name = subtree_name; + state->attrs = attrs; + state->msg = NULL; + state->dn = sysdb_custom_dn(handle->ctx, state, domain->name, object_name, + subtree_name); + if (state->dn == NULL) { + DEBUG(1, ("sysdb_custom_dn failed.\n")); + ret = ENOMEM; + goto fail; + } + + search_attrs = talloc_array(state, const char *, 2); + if (search_attrs == NULL) { + DEBUG(1, ("talloc_array failed.\n")); + ret = ENOMEM; + goto fail; + } + search_attrs[0] = "*"; + search_attrs[1] = NULL; + + subreq = sysdb_search_custom_by_name_send(state, state->ev, NULL, + state->handle, + state->domain, + state->object_name, + state->subtree_name, + search_attrs); + if (!subreq) { + DEBUG(1, ("sysdb_search_custom_by_name_send failed.\n")); + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sysdb_store_custom_check_done, req); + + return req; +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_store_custom_check_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_store_custom_state *state = tevent_req_data(req, + struct sysdb_store_custom_state); + int ret; + int i; + size_t resp_count = 0; + struct ldb_message **resp; + struct ldb_message *msg; + struct ldb_request *ldbreq; + struct ldb_message_element *el; + bool add_object = false; + + ret = sysdb_search_custom_recv(subreq, state, &resp_count, &resp); + talloc_zfree(subreq); + if (ret != EOK && ret != ENOENT) { + tevent_req_error(req, ret); + return; + } + + if (ret == ENOENT) { + add_object = true; + } + + msg = ldb_msg_new(state); + if (msg == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + + msg->dn = state->dn; + + msg->elements = talloc_array(msg, struct ldb_message_element, + state->attrs->num); + if (!msg->elements) { + tevent_req_error(req, ENOMEM); + return; + } + + for (i = 0; i < state->attrs->num; i++) { + msg->elements[i] = state->attrs->a[i]; + if (add_object) { + msg->elements[i].flags = LDB_FLAG_MOD_ADD; + } else { + el = ldb_msg_find_element(resp[0], state->attrs->a[i].name); + if (el == NULL) { + msg->elements[i].flags = LDB_FLAG_MOD_ADD; + } else { + msg->elements[i].flags = LDB_FLAG_MOD_REPLACE; + } + } + } + msg->num_elements = state->attrs->num; + + if (add_object) { + ret = ldb_build_add_req(&ldbreq, state->handle->ctx->ldb, state, msg, + NULL, NULL, NULL, NULL); + } else { + ret = ldb_build_mod_req(&ldbreq, state->handle->ctx->ldb, state, msg, + NULL, NULL, NULL, NULL); + } + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to build request: %s(%d)[%s]\n", + ldb_strerror(ret), ret, + ldb_errstring(state->handle->ctx->ldb))); + tevent_req_error(req, sysdb_error_to_errno(ret)); + return; + } + + subreq = sldb_request_send(state, state->ev, state->handle->ctx->ldb, + ldbreq); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_store_custom_done, req); + return; +} + +static void sysdb_store_custom_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_op_default_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); + return; +} + +int sysdb_store_custom_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +/* = Custom Delete======================================= */ + +struct sysdb_delete_custom_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + struct sss_domain_info *domain; + + const char *object_name; + const char *subtree_name; + struct ldb_dn *dn; +}; +static void sysdb_delete_custom_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_delete_custom_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *object_name, + const char *subtree_name) +{ + struct tevent_req *req, *subreq; + struct sysdb_delete_custom_state *state; + int ret; + + if (object_name == NULL || subtree_name == NULL) return NULL; + + if (handle == NULL) { + DEBUG(1, ("Sysdb context not available.\n")); + return NULL; + } + + req = tevent_req_create(mem_ctx, &state, struct sysdb_store_custom_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ev = ev; + state->handle = handle; + state->domain = domain; + state->object_name = object_name; + state->subtree_name = subtree_name; + state->dn = sysdb_custom_dn(handle->ctx, state, domain->name, object_name, + subtree_name); + if (state->dn == NULL) { + DEBUG(1, ("sysdb_custom_dn failed.\n")); + ret = ENOMEM; + goto fail; + } + + subreq = sysdb_delete_entry_send(state, state->ev, state->handle, + state->dn, true); + if (!subreq) { + DEBUG(1, ("sysdb_delete_entry_send failed.\n")); + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sysdb_delete_custom_done, req); + + return req; +fail: + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void sysdb_delete_custom_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_delete_entry_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); + return; +} + +int sysdb_delete_custom_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +/* = ASQ search request ======================================== */ +struct sysdb_asq_search_state { + struct tevent_context *ev; + struct sysdb_ctx *sysdb; + struct sysdb_handle *handle; + struct sss_domain_info *domain; + struct ldb_dn *base_dn; + const char *asq_attribute; + const char **attrs; + const char *expression; + + int msgs_count; + struct ldb_message **msgs; +}; + +void sysdb_asq_search_check_handle_done(struct tevent_req *subreq); +static void sysdb_asq_search_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_asq_search_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + struct ldb_dn *base_dn, + const char *expression, + const char *asq_attribute, + const char **attrs) +{ + struct tevent_req *req; + struct tevent_req *subreq; + struct sysdb_asq_search_state *state; + int ret; + + if (sysdb == NULL && handle == NULL) { + DEBUG(1, ("Sysdb context not available.\n")); + return NULL; + } + + req = tevent_req_create(mem_ctx, &state, struct sysdb_asq_search_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ev = ev; + state->sysdb = (sysdb == NULL) ? handle->ctx : sysdb; + state->handle = handle; + state->domain = domain; + state->base_dn = base_dn; + state->expression = expression; + state->asq_attribute = asq_attribute; + state->attrs = attrs; + + state->msgs_count = 0; + state->msgs = NULL; + + subreq = sysdb_check_handle_send(state, state->ev, state->sysdb, + state->handle); + if (!subreq) { + DEBUG(1, ("sysdb_check_handle_send failed.\n")); + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sysdb_asq_search_check_handle_done, req); + + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +void sysdb_asq_search_check_handle_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_asq_search_state *state = tevent_req_data(req, + struct sysdb_asq_search_state); + struct ldb_request *ldb_req; + struct ldb_control **ctrl; + struct ldb_asq_control *asq_control; + int ret; + + ret = sysdb_check_handle_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + ctrl = talloc_array(state, struct ldb_control *, 2); + if (ctrl == NULL) { + ret = ENOMEM; + goto fail; + } + + ctrl[0] = talloc(ctrl, struct ldb_control); + if (ctrl[0] == NULL) { + ret = ENOMEM; + goto fail; + } + ctrl[1] = NULL; + + ctrl[0]->oid = LDB_CONTROL_ASQ_OID; + ctrl[0]->critical = 1; + + asq_control = talloc(ctrl[0], struct ldb_asq_control); + if (asq_control == NULL) { + ret = ENOMEM; + goto fail; + } + + asq_control->request = 1; + asq_control->source_attribute = talloc_strdup(asq_control, + state->asq_attribute); + if (asq_control->source_attribute == NULL) { + ret = ENOMEM; + goto fail; + } + asq_control->src_attr_len = strlen(asq_control->source_attribute); + ctrl[0]->data = asq_control; + + ret = ldb_build_search_req(&ldb_req, state->handle->ctx->ldb, state, + state->base_dn, LDB_SCOPE_BASE, + state->expression, state->attrs, ctrl, + NULL, NULL, NULL); + if (ret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(ret); + goto fail; + } + + subreq = sldb_request_send(state, state->ev, state->handle->ctx->ldb, + ldb_req); + if (!subreq) { + ret = ENOMEM; + goto fail; + } + + tevent_req_set_callback(subreq, sysdb_asq_search_done, req); + return; + +fail: + tevent_req_error(req, ret); + return; +} + +static void sysdb_asq_search_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_asq_search_state *state = tevent_req_data(req, + struct sysdb_asq_search_state); + struct ldb_reply *ldbreply; + int ret; + + ret = sldb_request_recv(subreq, state, &ldbreply); + /* DO NOT free the subreq here, the subrequest search is not + * finished until we get an ldbreply of type LDB_REPLY_DONE */ + if (ret != EOK) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + switch (ldbreply->type) { + case LDB_REPLY_ENTRY: + state->msgs = talloc_realloc(state, state->msgs, + struct ldb_message *, + state->msgs_count + 2); + if (state->msgs == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + + state->msgs[state->msgs_count + 1] = NULL; + + state->msgs[state->msgs_count] = talloc_steal(state->msgs, + ldbreply->message); + state->msgs_count++; + + talloc_zfree(ldbreply); + return; + + case LDB_REPLY_DONE: + /* now it is safe to free the subrequest, the search is complete */ + talloc_zfree(subreq); + break; + + default: + DEBUG(1, ("Unknown ldb reply type [%d].\n", ldbreply->type)); + tevent_req_error(req, EINVAL); + return; + } + + tevent_req_done(req); +} + +int sysdb_asq_search_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + size_t *msgs_count, struct ldb_message ***msgs) +{ + struct sysdb_asq_search_state *state = tevent_req_data(req, + struct sysdb_asq_search_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *msgs_count = state->msgs_count; + *msgs = talloc_move(mem_ctx, &state->msgs); + + return EOK; +} + +/* =Search-Users-with-Custom-Filter====================================== */ + +struct sysdb_search_users_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + struct sss_domain_info *domain; + const char *sub_filter; + const char **attrs; + + struct ldb_message **msgs; + size_t msgs_count; +}; + +void sysdb_search_users_check_handle(struct tevent_req *subreq); +static void sysdb_search_users_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_search_users_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *sub_filter, + const char **attrs) +{ + struct tevent_req *req, *subreq; + struct sysdb_search_users_state *state; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_search_users_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ev = ev; + state->handle = handle; + state->domain = domain; + state->sub_filter = sub_filter; + state->attrs = attrs; + + state->msgs_count = 0; + state->msgs = NULL; + + subreq = sysdb_check_handle_send(state, ev, sysdb, handle); + if (!subreq) { + DEBUG(1, ("sysdb_check_handle_send failed.\n")); + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sysdb_search_users_check_handle, req); + + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +void sysdb_search_users_check_handle(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_search_users_state *state = tevent_req_data(req, + struct sysdb_search_users_state); + struct ldb_dn *basedn; + char *filter; + int ret; + + ret = sysdb_check_handle_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + basedn = ldb_dn_new_fmt(state, state->handle->ctx->ldb, + SYSDB_TMPL_USER_BASE, state->domain->name); + if (!basedn) { + DEBUG(2, ("Failed to build base dn\n")); + tevent_req_error(req, ENOMEM); + return; + } + + filter = talloc_asprintf(state, "(&(%s)%s)", + SYSDB_UC, state->sub_filter); + if (!filter) { + DEBUG(2, ("Failed to build filter\n")); + tevent_req_error(req, ENOMEM); + return; + } + + DEBUG(6, ("Search users with filter: %s\n", filter)); + + subreq = sysdb_search_entry_send(state, state->ev, state->handle, + basedn, LDB_SCOPE_SUBTREE, + filter, state->attrs); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_search_users_done, req); +} + +static void sysdb_search_users_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_search_users_state *state = tevent_req_data(req, + struct sysdb_search_users_state); + int ret; + + ret = sysdb_search_entry_recv(subreq, state, + &state->msgs_count, &state->msgs); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_search_users_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + size_t *msgs_count, struct ldb_message ***msgs) +{ + struct sysdb_search_users_state *state = tevent_req_data(req, + struct sysdb_search_users_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *msgs_count = state->msgs_count; + *msgs = talloc_move(mem_ctx, &state->msgs); + + return EOK; +} + +/* =Delete-User-by-Name-OR-uid============================================ */ + +struct sysdb_delete_user_state { + struct tevent_context *ev; + struct sss_domain_info *domain; + + const char *name; + uid_t uid; + + struct sysdb_handle *handle; +}; + +void sysdb_delete_user_check_handle(struct tevent_req *subreq); +static void sysdb_delete_user_found(struct tevent_req *subreq); +static void sysdb_delete_user_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_delete_user_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, uid_t uid) +{ + struct tevent_req *req, *subreq; + struct sysdb_delete_user_state *state; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_delete_user_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->domain = domain; + state->name = name; + state->uid = uid; + + subreq = sysdb_check_handle_send(state, ev, sysdb, handle); + if (!subreq) { + DEBUG(1, ("sysdb_check_handle_send failed.\n")); + tevent_req_error(req, ENOMEM); + tevent_req_post(req, ev); + return req; + } + tevent_req_set_callback(subreq, sysdb_delete_user_check_handle, req); + + return req; +} + +void sysdb_delete_user_check_handle(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_delete_user_state *state = tevent_req_data(req, + struct sysdb_delete_user_state); + static const char *attrs[] = { SYSDB_NAME, SYSDB_UIDNUM, NULL }; + int ret; + + ret = sysdb_check_handle_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + if (state->name) { + subreq = sysdb_search_user_by_name_send(state, state->ev, NULL, + state->handle, state->domain, + state->name, attrs); + } else { + subreq = sysdb_search_user_by_uid_send(state, state->ev, NULL, + state->handle, state->domain, + state->uid, NULL); + } + + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_delete_user_found, req); +} + +static void sysdb_delete_user_found(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_delete_user_state *state = tevent_req_data(req, + struct sysdb_delete_user_state); + struct ldb_message *msg; + int ret; + + ret = sysdb_search_user_recv(subreq, state, &msg); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + if (state->name && state->uid) { + /* verify name/gid match */ + const char *name; + uint64_t uid; + + name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL); + uid = ldb_msg_find_attr_as_uint64(msg, SYSDB_UIDNUM, 0); + if (name == NULL || uid == 0) { + DEBUG(2, ("Attribute is missing but this should never happen!\n")); + tevent_req_error(req, EFAULT); + return; + } + if (strcmp(state->name, name) || state->uid != uid) { + /* this is not the entry we are looking for */ + tevent_req_error(req, EINVAL); + return; + } + } + + subreq = sysdb_delete_entry_send(state, state->ev, + state->handle, msg->dn, false); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_delete_user_done, req); +} + +static void sysdb_delete_user_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_delete_entry_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_delete_user_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + + +/* =Search-Groups-with-Custom-Filter===================================== */ + +struct sysdb_search_groups_state { + struct tevent_context *ev; + struct sysdb_handle *handle; + struct sss_domain_info *domain; + const char *sub_filter; + const char **attrs; + + struct ldb_message **msgs; + size_t msgs_count; +}; + +void sysdb_search_groups_check_handle(struct tevent_req *subreq); +static void sysdb_search_groups_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_search_groups_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *sub_filter, + const char **attrs) +{ + struct tevent_req *req, *subreq; + struct sysdb_search_groups_state *state; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_search_groups_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ev = ev; + state->handle = handle; + state->domain = domain; + state->sub_filter = sub_filter; + state->attrs = attrs; + + state->msgs_count = 0; + state->msgs = NULL; + + subreq = sysdb_check_handle_send(state, ev, sysdb, handle); + if (!subreq) { + DEBUG(1, ("sysdb_check_handle_send failed.\n")); + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sysdb_search_groups_check_handle, req); + + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +void sysdb_search_groups_check_handle(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_search_groups_state *state = tevent_req_data(req, + struct sysdb_search_groups_state); + struct ldb_dn *basedn; + char *filter; + int ret; + + ret = sysdb_check_handle_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + basedn = ldb_dn_new_fmt(state, state->handle->ctx->ldb, + SYSDB_TMPL_GROUP_BASE, state->domain->name); + if (!basedn) { + DEBUG(2, ("Failed to build base dn\n")); + tevent_req_error(req, ENOMEM); + return; + } + + filter = talloc_asprintf(state, "(&(%s)%s)", + SYSDB_GC, state->sub_filter); + if (!filter) { + DEBUG(2, ("Failed to build filter\n")); + tevent_req_error(req, ENOMEM); + return; + } + + DEBUG(6, ("Search groups with filter: %s\n", filter)); + + subreq = sysdb_search_entry_send(state, state->ev, state->handle, + basedn, LDB_SCOPE_SUBTREE, + filter, state->attrs); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_search_groups_done, req); +} + +static void sysdb_search_groups_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_search_groups_state *state = tevent_req_data(req, + struct sysdb_search_groups_state); + int ret; + + ret = sysdb_search_entry_recv(subreq, state, + &state->msgs_count, &state->msgs); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_search_groups_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + size_t *msgs_count, struct ldb_message ***msgs) +{ + struct sysdb_search_groups_state *state = tevent_req_data(req, + struct sysdb_search_groups_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *msgs_count = state->msgs_count; + *msgs = talloc_move(mem_ctx, &state->msgs); + + return EOK; +} + +/* =Delete-Group-by-Name-OR-gid=========================================== */ + +struct sysdb_delete_group_state { + struct tevent_context *ev; + struct sss_domain_info *domain; + + const char *name; + gid_t gid; + + struct sysdb_handle *handle; +}; + +void sysdb_delete_group_check_handle(struct tevent_req *subreq); +static void sysdb_delete_group_found(struct tevent_req *subreq); +static void sysdb_delete_group_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_delete_group_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sysdb_handle *handle, + struct sss_domain_info *domain, + const char *name, gid_t gid) +{ + struct tevent_req *req, *subreq; + struct sysdb_delete_group_state *state; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_delete_group_state); + if (!req) return NULL; + + state->ev = ev; + state->handle = handle; + state->domain = domain; + state->name = name; + state->gid = gid; + + subreq = sysdb_check_handle_send(state, ev, sysdb, handle); + if (!subreq) { + DEBUG(1, ("sysdb_check_handle_send failed.\n")); + tevent_req_error(req, ENOMEM); + tevent_req_post(req, ev); + return req; + } + tevent_req_set_callback(subreq, sysdb_delete_group_check_handle, req); + + return req; +} + +void sysdb_delete_group_check_handle(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_delete_group_state *state = tevent_req_data(req, + struct sysdb_delete_group_state); + static const char *attrs[] = { SYSDB_NAME, SYSDB_GIDNUM, NULL }; + int ret; + + ret = sysdb_check_handle_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + if (state->name) { + subreq = sysdb_search_group_by_name_send(state, state->ev, NULL, + state->handle, state->domain, + state->name, attrs); + } else { + subreq = sysdb_search_group_by_gid_send(state, state->ev, NULL, + state->handle, state->domain, + state->gid, NULL); + } + + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_delete_group_found, req); +} + +static void sysdb_delete_group_found(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sysdb_delete_group_state *state = tevent_req_data(req, + struct sysdb_delete_group_state); + struct ldb_message *msg; + int ret; + + ret = sysdb_search_group_recv(subreq, state, &msg); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + if (state->name && state->gid) { + /* verify name/gid match */ + const char *name; + uint64_t gid; + + name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL); + gid = ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0); + if (name == NULL || gid == 0) { + DEBUG(2, ("Attribute is missing but this should never happen!\n")); + tevent_req_error(req, EFAULT); + return; + } + if (strcmp(state->name, name) || state->gid != gid) { + /* this is not the entry we are looking for */ + tevent_req_error(req, EINVAL); + return; + } + } + + subreq = sysdb_delete_entry_send(state, state->ev, + state->handle, msg->dn, false); + if (!subreq) { + DEBUG(6, ("Error: Out of memory\n")); + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sysdb_delete_group_done, req); +} + +static void sysdb_delete_group_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + + ret = sysdb_delete_entry_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sysdb_delete_group_recv(struct tevent_req *req) +{ + return sysdb_op_default_recv(req); +} + +/* ========= Authentication against cached password ============ */ + +struct sysdb_cache_auth_state { + struct tevent_context *ev; + const char *name; + const uint8_t *authtok; + size_t authtok_size; + struct sss_domain_info *domain; + struct sysdb_ctx *sysdb; + struct confdb_ctx *cdb; + struct sysdb_attrs *update_attrs; + bool authentication_successful; + struct sysdb_handle *handle; + time_t expire_date; + time_t delayed_until; +}; + +errno_t check_failed_login_attempts(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb, + struct ldb_message *ldb_msg, + uint32_t *failed_login_attempts, + time_t *delayed_until) +{ + int ret; + int allowed_failed_login_attempts; + int failed_login_delay; + time_t last_failed_login; + time_t end; + + *delayed_until = -1; + *failed_login_attempts = ldb_msg_find_attr_as_uint(ldb_msg, + SYSDB_FAILED_LOGIN_ATTEMPTS, 0); + last_failed_login = (time_t) ldb_msg_find_attr_as_int64(ldb_msg, + SYSDB_LAST_FAILED_LOGIN, 0); + ret = confdb_get_int(cdb, mem_ctx, CONFDB_PAM_CONF_ENTRY, + CONFDB_PAM_FAILED_LOGIN_ATTEMPTS, + CONFDB_DEFAULT_PAM_FAILED_LOGIN_ATTEMPTS, + &allowed_failed_login_attempts); + if (ret != EOK) { + DEBUG(1, ("Failed to read the number of allowed failed login " + "attempts.\n")); + return EIO; + } + ret = confdb_get_int(cdb, mem_ctx, CONFDB_PAM_CONF_ENTRY, + CONFDB_PAM_FAILED_LOGIN_DELAY, + CONFDB_DEFAULT_PAM_FAILED_LOGIN_DELAY, + &failed_login_delay); + if (ret != EOK) { + DEBUG(1, ("Failed to read the failed login delay.\n")); + return EIO; + } + DEBUG(9, ("Failed login attempts [%d], allowed failed login attempts [%d], " + "failed login delay [%d].\n", *failed_login_attempts, + allowed_failed_login_attempts, failed_login_delay)); + + if (allowed_failed_login_attempts) { + if (*failed_login_attempts >= allowed_failed_login_attempts) { + if (failed_login_delay) { + end = last_failed_login + (failed_login_delay * 60); + if (end < time(NULL)) { + DEBUG(7, ("failed_login_delay has passed, " + "resetting failed_login_attempts.\n")); + *failed_login_attempts = 0; + } else { + DEBUG(7, ("login delayed until %lld.\n", (long long) end)); + *delayed_until = end; + return EACCES; + } + } else { + DEBUG(4, ("Too many failed logins.\n")); + return EACCES; + } + } + } + + return EOK; +} + +static void sysdb_cache_auth_get_attrs_done(struct tevent_req *subreq); +static void sysdb_cache_auth_transaction_start_done(struct tevent_req *subreq); +static void sysdb_cache_auth_attr_update_done(struct tevent_req *subreq); +static void sysdb_cache_auth_done(struct tevent_req *subreq); + +struct tevent_req *sysdb_cache_auth_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *name, + const uint8_t *authtok, + size_t authtok_size, + struct confdb_ctx *cdb) +{ + struct tevent_req *req; + struct tevent_req *subreq; + struct sysdb_cache_auth_state *state; + + if (name == NULL || *name == '\0') { + DEBUG(1, ("Missing user name.\n")); + return NULL; + } + + if (cdb == NULL) { + DEBUG(1, ("Missing config db context.\n")); + return NULL; + } + + if (sysdb == NULL) { + DEBUG(1, ("Missing sysdb db context.\n")); + return NULL; + } + + if (!domain->cache_credentials) { + DEBUG(3, ("Cached credentials not available.\n")); + return NULL; + } + + static const char *attrs[] = {SYSDB_NAME, + SYSDB_CACHEDPWD, + SYSDB_DISABLED, + SYSDB_LAST_LOGIN, + SYSDB_LAST_ONLINE_AUTH, + "lastCachedPasswordChange", + "accountExpires", + SYSDB_FAILED_LOGIN_ATTEMPTS, + SYSDB_LAST_FAILED_LOGIN, + NULL}; + + req = tevent_req_create(mem_ctx, &state, struct sysdb_cache_auth_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ev = ev; + state->name = name; + state->authtok = authtok; + state->authtok_size = authtok_size; + state->domain = domain; + state->sysdb = sysdb; + state->cdb = cdb; + state->update_attrs = NULL; + state->authentication_successful = false; + state->handle = NULL; + state->expire_date = -1; + state->delayed_until = -1; + + subreq = sysdb_search_user_by_name_send(state, ev, sysdb, NULL, domain, + name, attrs); + if (subreq == NULL) { + DEBUG(1, ("sysdb_search_user_by_name_send failed.\n")); + talloc_zfree(req); + return NULL; + } + tevent_req_set_callback(subreq, sysdb_cache_auth_get_attrs_done, req); + + return req; +} + +static void sysdb_cache_auth_get_attrs_done(struct tevent_req *subreq) +{ + struct ldb_message *ldb_msg; + const char *userhash; + char *comphash; + char *password = NULL; + int i; + int ret; + uint64_t lastLogin = 0; + int cred_expiration; + uint32_t failed_login_attempts = 0; + + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + + struct sysdb_cache_auth_state *state = tevent_req_data(req, + struct sysdb_cache_auth_state); + + ret = sysdb_search_user_recv(subreq, state, &ldb_msg); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(1, ("sysdb_search_user_by_name_send failed [%d][%s].\n", + ret, strerror(ret))); + tevent_req_error(req, ENOENT); + return; + } + + /* Check offline_auth_cache_timeout */ + lastLogin = ldb_msg_find_attr_as_uint64(ldb_msg, + SYSDB_LAST_ONLINE_AUTH, + 0); + + ret = confdb_get_int(state->cdb, state, CONFDB_PAM_CONF_ENTRY, + CONFDB_PAM_CRED_TIMEOUT, 0, &cred_expiration); + if (ret != EOK) { + DEBUG(1, ("Failed to read expiration time of offline credentials.\n")); + ret = EACCES; + goto done; + } + DEBUG(9, ("Offline credentials expiration is [%d] days.\n", + cred_expiration)); + + if (cred_expiration) { + state->expire_date = lastLogin + (cred_expiration * 86400); + if (state->expire_date < time(NULL)) { + DEBUG(4, ("Cached user entry is too old.\n")); + state->expire_date = 0; + ret = EACCES; + goto done; + } + } else { + state->expire_date = 0; + } + + ret = check_failed_login_attempts(state, state->cdb, ldb_msg, + &failed_login_attempts, + &state->delayed_until); + if (ret != EOK) { + goto done; + } + + /* TODO: verify user account (disabled, expired ...) */ + + password = talloc_strndup(state, (const char *) state->authtok, + state->authtok_size); + if (password == NULL) { + DEBUG(1, ("talloc_strndup failed.\n")); + ret = ENOMEM; + goto done; + } + + userhash = ldb_msg_find_attr_as_string(ldb_msg, SYSDB_CACHEDPWD, NULL); + if (userhash == NULL || *userhash == '\0') { + DEBUG(4, ("Cached credentials not available.\n")); + ret = ENOENT; + goto done; + } + + ret = s3crypt_sha512(state, password, userhash, &comphash); + if (ret) { + DEBUG(4, ("Failed to create password hash.\n")); + ret = EFAULT; + goto done; + } + + state->update_attrs = sysdb_new_attrs(state); + if (state->update_attrs == NULL) { + DEBUG(1, ("sysdb_new_attrs failed.\n")); + goto done; + } + + if (strcmp(userhash, comphash) == 0) { + /* TODO: probable good point for audit logging */ + DEBUG(4, ("Hashes do match!\n")); + state->authentication_successful = true; + + ret = sysdb_attrs_add_time_t(state->update_attrs, SYSDB_LAST_LOGIN, + time(NULL)); + if (ret != EOK) { + DEBUG(3, ("sysdb_attrs_add_time_t failed, " + "but authentication is successful.\n")); + ret = EOK; + goto done; + } + + ret = sysdb_attrs_add_uint32(state->update_attrs, + SYSDB_FAILED_LOGIN_ATTEMPTS, 0U); + if (ret != EOK) { + DEBUG(3, ("sysdb_attrs_add_uint32 failed, " + "but authentication is successful.\n")); + ret = EOK; + goto done; + } + + + } else { + DEBUG(4, ("Authentication failed.\n")); + state->authentication_successful = false; + + ret = sysdb_attrs_add_time_t(state->update_attrs, + SYSDB_LAST_FAILED_LOGIN, + time(NULL)); + if (ret != EOK) { + DEBUG(3, ("sysdb_attrs_add_time_t failed\n.")); + ret = EINVAL; + goto done; + } + + ret = sysdb_attrs_add_uint32(state->update_attrs, + SYSDB_FAILED_LOGIN_ATTEMPTS, + ++failed_login_attempts); + if (ret != EOK) { + DEBUG(3, ("sysdb_attrs_add_uint32 failed.\n")); + ret = EINVAL; + goto done; + } + } + + subreq = sysdb_transaction_send(state, state->ev, state->sysdb); + if (subreq == NULL) { + DEBUG(1, ("sysdb_transaction_send failed.\n")); + goto done; + } + tevent_req_set_callback(subreq, sysdb_cache_auth_transaction_start_done, + req); + return; + +done: + if (password) for (i = 0; password[i]; i++) password[i] = 0; + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + return; +} + +static void sysdb_cache_auth_transaction_start_done(struct tevent_req *subreq) +{ + int ret; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + + struct sysdb_cache_auth_state *state = tevent_req_data(req, + struct sysdb_cache_auth_state); + + ret = sysdb_transaction_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(1, ("sysdb_transaction_send failed [%d][%s].\n", + ret, strerror(ret))); + goto done; + } + + + subreq = sysdb_set_user_attr_send(state, state->ev, state->handle, + state->domain, state->name, + state->update_attrs, + LDB_FLAG_MOD_REPLACE); + if (subreq == NULL) { + DEBUG(1, ("sysdb_set_user_attr_send failed.\n")); + goto done; + } + tevent_req_set_callback(subreq, sysdb_cache_auth_attr_update_done, + req); + return; + +done: + if (state->authentication_successful) { + tevent_req_done(req); + } else { + tevent_req_error(req, EINVAL); + } + return; +} + +static void sysdb_cache_auth_attr_update_done(struct tevent_req *subreq) +{ + int ret; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + + struct sysdb_cache_auth_state *state = tevent_req_data(req, + struct sysdb_cache_auth_state); + + ret = sysdb_set_user_attr_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(1, ("sysdb_set_user_attr request failed [%d][%s].\n", + ret, strerror(ret))); + goto done; + } + + subreq = sysdb_transaction_commit_send(state, state->ev, state->handle); + if (subreq == NULL) { + DEBUG(1, ("sysdb_transaction_commit_send failed.\n")); + goto done; + } + tevent_req_set_callback(subreq, sysdb_cache_auth_done, req); + return; + +done: + if (state->authentication_successful) { + tevent_req_done(req); + } else { + tevent_req_error(req, EINVAL); + } + return; +} + +static void sysdb_cache_auth_done(struct tevent_req *subreq) +{ + int ret; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + + struct sysdb_cache_auth_state *state = tevent_req_data(req, + struct sysdb_cache_auth_state); + + ret = sysdb_transaction_commit_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(1, ("sysdb_transaction_commit_send failed [%d][%s].\n", + ret, strerror(ret))); + } + + if (state->authentication_successful) { + tevent_req_done(req); + } else { + tevent_req_error(req, EINVAL); + } + return; +} + +int sysdb_cache_auth_recv(struct tevent_req *req, time_t *expire_date, + time_t *delayed_until) { + struct sysdb_cache_auth_state *state = tevent_req_data(req, + struct sysdb_cache_auth_state); + *expire_date = state->expire_date; + *delayed_until = state->delayed_until; + + TEVENT_REQ_RETURN_ON_ERROR(req); + + return (state->authentication_successful ? EOK : EINVAL); +} diff --git a/src/db/sysdb_private.h b/src/db/sysdb_private.h new file mode 100644 index 00000000..270cf360 --- /dev/null +++ b/src/db/sysdb_private.h @@ -0,0 +1,107 @@ + +/* + SSSD + + Private System Database Header + + 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 . +*/ + +#ifndef __INT_SYS_DB_H__ +#define __INT_SYS_DB_H__ + +#define SYSDB_VERSION_0_5 "0.5" +#define SYSDB_VERSION_0_4 "0.4" +#define SYSDB_VERSION_0_3 "0.3" +#define SYSDB_VERSION_0_2 "0.2" +#define SYSDB_VERSION_0_1 "0.1" + +#define SYSDB_VERSION SYSDB_VERSION_0_5 + +#define SYSDB_BASE_LDIF \ + "dn: @ATTRIBUTES\n" \ + "userPrincipalName: CASE_INSENSITIVE\n" \ + "cn: CASE_INSENSITIVE\n" \ + "dc: CASE_INSENSITIVE\n" \ + "dn: CASE_INSENSITIVE\n" \ + "objectclass: CASE_INSENSITIVE\n" \ + "\n" \ + "dn: @INDEXLIST\n" \ + "@IDXATTR: cn\n" \ + "@IDXATTR: objectclass\n" \ + "@IDXATTR: member\n" \ + "@IDXATTR: memberof\n" \ + "@IDXATTR: name\n" \ + "@IDXATTR: uidNumber\n" \ + "@IDXATTR: gidNumber\n" \ + "@IDXATTR: lastUpdate\n" \ + "@IDXATTR: originalDN\n" \ + "\n" \ + "dn: @MODULES\n" \ + "@LIST: asq,memberof\n" \ + "\n" \ + "dn: cn=sysdb\n" \ + "cn: sysdb\n" \ + "version: " SYSDB_VERSION "\n" \ + "description: base object\n" \ + "\n" + +#include "db/sysdb.h" + +struct sysdb_handle { + struct sysdb_handle *prev; + struct sysdb_handle *next; + + struct sysdb_ctx *ctx; + struct tevent_req *subreq; + + bool transaction_active; +}; + +struct sysdb_ctx { + struct tevent_context *ev; + + struct sss_domain_info *domain; + bool mpg; + + struct ldb_context *ldb; + char *ldb_file; + + struct sysdb_handle *queue; +}; + +struct sysdb_ctx_list { + struct sysdb_ctx **dbs; + size_t num_dbs; + + char *db_path; +}; + +/* An operation blocks the transaction queue as well, but does not + * start a transaction, normally useful only for search type calls. + * do *NOT* call within a transaction you'll deadlock sysdb. + * Also make sure to free the handle as soon as the operation is + * finished to avoid stalling or potentially deadlocking sysdb */ + +struct tevent_req *sysdb_operation_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *ctx); +int sysdb_operation_recv(struct tevent_req *req, TALLOC_CTX *memctx, + struct sysdb_handle **handle); + +void sysdb_operation_done(struct sysdb_handle *handle); + +#endif /* __INT_SYS_DB_H__ */ diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c new file mode 100644 index 00000000..4b9470ba --- /dev/null +++ b/src/db/sysdb_search.c @@ -0,0 +1,691 @@ +/* + 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 "confdb/confdb.h" +#include + +struct sysdb_search_ctx; + +typedef void (*gen_callback)(struct sysdb_search_ctx *); + +struct sysdb_search_ctx { + struct tevent_context *ev; + struct sysdb_ctx *ctx; + struct sysdb_handle *handle; + + struct sss_domain_info *domain; + + const char *expression; + + sysdb_callback_t callback; + void *ptr; + + gen_callback gen_aux_fn; + bool gen_conv_mpg_users; + + struct ldb_result *res; + + const char **attrs; + + int error; +}; + +static struct sysdb_search_ctx *init_src_ctx(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + struct sysdb_ctx *ctx, + sysdb_callback_t fn, + void *ptr) +{ + struct sysdb_search_ctx *sctx; + + sctx = talloc_zero(mem_ctx, struct sysdb_search_ctx); + if (!sctx) { + return NULL; + } + sctx->ctx = ctx; + sctx->ev = ctx->ev; + sctx->callback = fn; + sctx->ptr = ptr; + sctx->res = talloc_zero(sctx, struct ldb_result); + if (!sctx->res) { + talloc_free(sctx); + return NULL; + } + sctx->domain = domain; + + return sctx; +} + +static void request_ldberror(struct sysdb_search_ctx *sctx, int error) +{ + sysdb_operation_done(sctx->handle); + sctx->callback(sctx->ptr, sysdb_error_to_errno(error), NULL); +} + +static void request_error(struct sysdb_search_ctx *sctx, int error) +{ + sysdb_operation_done(sctx->handle); + sctx->callback(sctx->ptr, error, NULL); +} + +static void request_done(struct sysdb_search_ctx *sctx) +{ + sysdb_operation_done(sctx->handle); + sctx->callback(sctx->ptr, EOK, sctx->res); +} + +static int mpg_convert(struct ldb_message *msg); + +static int get_gen_callback(struct ldb_request *req, + struct ldb_reply *rep) +{ + struct sysdb_search_ctx *sctx; + struct ldb_result *res; + int n, ret; + + sctx = talloc_get_type(req->context, struct sysdb_search_ctx); + res = sctx->res; + + if (!rep) { + request_ldberror(sctx, LDB_ERR_OPERATIONS_ERROR); + return LDB_ERR_OPERATIONS_ERROR; + } + if (rep->error != LDB_SUCCESS) { + request_ldberror(sctx, rep->error); + return rep->error; + } + + switch (rep->type) { + case LDB_REPLY_ENTRY: + + if (sctx->gen_conv_mpg_users) { + ret = mpg_convert(rep->message); + if (ret != EOK) { + request_ldberror(sctx, LDB_ERR_OPERATIONS_ERROR); + return LDB_ERR_OPERATIONS_ERROR; + } + } + + res->msgs = talloc_realloc(res, res->msgs, + struct ldb_message *, + res->count + 2); + if (!res->msgs) { + request_ldberror(sctx, LDB_ERR_OPERATIONS_ERROR); + return LDB_ERR_OPERATIONS_ERROR; + } + + res->msgs[res->count + 1] = NULL; + + res->msgs[res->count] = talloc_steal(res->msgs, rep->message); + res->count++; + break; + + case LDB_REPLY_REFERRAL: + if (res->refs) { + for (n = 0; res->refs[n]; n++) /*noop*/ ; + } else { + n = 0; + } + + res->refs = talloc_realloc(res, res->refs, char *, n + 2); + if (! res->refs) { + request_ldberror(sctx, LDB_ERR_OPERATIONS_ERROR); + return LDB_ERR_OPERATIONS_ERROR; + } + + res->refs[n] = talloc_steal(res->refs, rep->referral); + res->refs[n + 1] = NULL; + break; + + case LDB_REPLY_DONE: + res->controls = talloc_steal(res, rep->controls); + + /* check if we need to call any aux function */ + if (sctx->gen_aux_fn) { + sctx->gen_aux_fn(sctx); + } else { + /* no aux functions, this means the request is done */ + request_done(sctx); + } + return LDB_SUCCESS; + } + + talloc_free(rep); + return LDB_SUCCESS; +} + +/* users */ + +static void user_search(struct tevent_req *treq) +{ + struct sysdb_search_ctx *sctx; + struct ldb_request *req; + struct ldb_dn *base_dn; + int ret; + + sctx = tevent_req_callback_data(treq, struct sysdb_search_ctx); + + ret = sysdb_operation_recv(treq, sctx, &sctx->handle); + if (ret) { + return request_error(sctx, ret); + } + + base_dn = ldb_dn_new_fmt(sctx, sctx->ctx->ldb, + SYSDB_TMPL_USER_BASE, sctx->domain->name); + if (!base_dn) { + return request_error(sctx, ENOMEM); + } + + ret = ldb_build_search_req(&req, sctx->ctx->ldb, sctx, + base_dn, LDB_SCOPE_SUBTREE, + sctx->expression, sctx->attrs, NULL, + sctx, get_gen_callback, + NULL); + if (ret != LDB_SUCCESS) { + return request_ldberror(sctx, ret); + } + + ret = ldb_request(sctx->ctx->ldb, req); + if (ret != LDB_SUCCESS) { + return request_ldberror(sctx, ret); + } +} + +int sysdb_getpwnam(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *ctx, + struct sss_domain_info *domain, + const char *name, + sysdb_callback_t fn, void *ptr) +{ + static const char *attrs[] = SYSDB_PW_ATTRS; + struct sysdb_search_ctx *sctx; + struct tevent_req *req; + + if (!domain) { + return EINVAL; + } + + sctx = init_src_ctx(mem_ctx, domain, ctx, fn, ptr); + if (!sctx) { + return ENOMEM; + } + + sctx->expression = talloc_asprintf(sctx, SYSDB_PWNAM_FILTER, name); + if (!sctx->expression) { + talloc_free(sctx); + return ENOMEM; + } + + sctx->attrs = attrs; + + req = sysdb_operation_send(mem_ctx, ctx->ev, ctx); + if (!req) { + talloc_free(sctx); + return ENOMEM; + } + + tevent_req_set_callback(req, user_search, sctx); + + return EOK; +} + +int sysdb_getpwuid(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *ctx, + struct sss_domain_info *domain, + uid_t uid, + sysdb_callback_t fn, void *ptr) +{ + static const char *attrs[] = SYSDB_PW_ATTRS; + struct sysdb_search_ctx *sctx; + unsigned long int filter_uid = uid; + struct tevent_req *req; + + if (!domain) { + return EINVAL; + } + + sctx = init_src_ctx(mem_ctx, domain, ctx, fn, ptr); + if (!sctx) { + return ENOMEM; + } + + sctx->expression = talloc_asprintf(sctx, SYSDB_PWUID_FILTER, filter_uid); + if (!sctx->expression) { + talloc_free(sctx); + return ENOMEM; + } + + sctx->attrs = attrs; + + req = sysdb_operation_send(mem_ctx, ctx->ev, ctx); + if (!req) { + talloc_free(sctx); + return ENOMEM; + } + + tevent_req_set_callback(req, user_search, sctx); + + return EOK; +} + +int sysdb_enumpwent(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *ctx, + struct sss_domain_info *domain, + const char *expression, + sysdb_callback_t fn, void *ptr) +{ + static const char *attrs[] = SYSDB_PW_ATTRS; + struct sysdb_search_ctx *sctx; + struct tevent_req *req; + + if (!domain) { + return EINVAL; + } + + sctx = init_src_ctx(mem_ctx, domain, ctx, fn, ptr); + if (!sctx) { + return ENOMEM; + } + + if (expression) + sctx->expression = expression; + else + sctx->expression = SYSDB_PWENT_FILTER; + + sctx->attrs = attrs; + + req = sysdb_operation_send(mem_ctx, ctx->ev, ctx); + if (!req) { + talloc_free(sctx); + return ENOMEM; + } + + tevent_req_set_callback(req, user_search, sctx); + + return EOK; +} + +/* groups */ + +static int mpg_convert(struct ldb_message *msg) +{ + struct ldb_message_element *el; + struct ldb_val *val; + int i; + + el = ldb_msg_find_element(msg, "objectClass"); + if (!el) return EINVAL; + + /* see if this is a user to convert to a group */ + for (i = 0; i < el->num_values; i++) { + val = &(el->values[i]); + if (strncasecmp(SYSDB_USER_CLASS, + (char *)val->data, val->length) == 0) { + break; + } + } + /* no, leave as is */ + if (i == el->num_values) return EOK; + + /* yes, convert */ + val->data = (uint8_t *)talloc_strdup(msg, SYSDB_GROUP_CLASS); + if (val->data == NULL) return ENOMEM; + val->length = strlen(SYSDB_GROUP_CLASS); + + return EOK; +} + +static void grp_search(struct tevent_req *treq) +{ + struct sysdb_search_ctx *sctx; + static const char *attrs[] = SYSDB_GRSRC_ATTRS; + struct ldb_request *req; + struct ldb_dn *base_dn; + int ret; + + sctx = tevent_req_callback_data(treq, struct sysdb_search_ctx); + + ret = sysdb_operation_recv(treq, sctx, &sctx->handle); + if (ret) { + return request_error(sctx, ret); + } + + if (sctx->gen_conv_mpg_users) { + base_dn = ldb_dn_new_fmt(sctx, sctx->ctx->ldb, + SYSDB_DOM_BASE, sctx->domain->name); + } else { + base_dn = ldb_dn_new_fmt(sctx, sctx->ctx->ldb, + SYSDB_TMPL_GROUP_BASE, sctx->domain->name); + } + if (!base_dn) { + return request_error(sctx, ENOMEM); + } + + ret = ldb_build_search_req(&req, sctx->ctx->ldb, sctx, + base_dn, LDB_SCOPE_SUBTREE, + sctx->expression, attrs, NULL, + sctx, get_gen_callback, + NULL); + if (ret != LDB_SUCCESS) { + return request_ldberror(sctx, ret); + } + + ret = ldb_request(sctx->ctx->ldb, req); + if (ret != LDB_SUCCESS) { + return request_ldberror(sctx, ret); + } +} + +int sysdb_getgrnam(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *ctx, + struct sss_domain_info *domain, + const char *name, + sysdb_callback_t fn, void *ptr) +{ + struct sysdb_search_ctx *sctx; + struct tevent_req *req; + + if (!domain) { + return EINVAL; + } + + sctx = init_src_ctx(mem_ctx, domain, ctx, fn, ptr); + if (!sctx) { + return ENOMEM; + } + + if (ctx->mpg) { + sctx->gen_conv_mpg_users = true; + sctx->expression = talloc_asprintf(sctx, SYSDB_GRNAM_MPG_FILTER, name); + } else { + sctx->expression = talloc_asprintf(sctx, SYSDB_GRNAM_FILTER, name); + } + if (!sctx->expression) { + talloc_free(sctx); + return ENOMEM; + } + + req = sysdb_operation_send(mem_ctx, ctx->ev, ctx); + if (!req) { + talloc_free(sctx); + return ENOMEM; + } + + tevent_req_set_callback(req, grp_search, sctx); + + return EOK; +} + +int sysdb_getgrgid(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *ctx, + struct sss_domain_info *domain, + gid_t gid, + sysdb_callback_t fn, void *ptr) +{ + struct sysdb_search_ctx *sctx; + struct tevent_req *req; + + if (!domain) { + return EINVAL; + } + + sctx = init_src_ctx(mem_ctx, domain, ctx, fn, ptr); + if (!sctx) { + return ENOMEM; + } + + if (ctx->mpg) { + sctx->gen_conv_mpg_users = true; + sctx->expression = talloc_asprintf(sctx, + SYSDB_GRGID_MPG_FILTER, + (unsigned long int)gid); + } else { + sctx->expression = talloc_asprintf(sctx, + SYSDB_GRGID_FILTER, + (unsigned long int)gid); + } + if (!sctx->expression) { + talloc_free(sctx); + return ENOMEM; + } + + req = sysdb_operation_send(mem_ctx, ctx->ev, ctx); + if (!req) { + talloc_free(sctx); + return ENOMEM; + } + + tevent_req_set_callback(req, grp_search, sctx); + + return EOK; +} + +int sysdb_enumgrent(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *ctx, + struct sss_domain_info *domain, + sysdb_callback_t fn, void *ptr) +{ + struct sysdb_search_ctx *sctx; + struct tevent_req *req; + + if (!domain) { + return EINVAL; + } + + sctx = init_src_ctx(mem_ctx, domain, ctx, fn, ptr); + if (!sctx) { + return ENOMEM; + } + + if (ctx->mpg) { + sctx->gen_conv_mpg_users = true; + sctx->expression = SYSDB_GRENT_MPG_FILTER; + } else { + sctx->expression = SYSDB_GRENT_FILTER; + } + + req = sysdb_operation_send(mem_ctx, ctx->ev, ctx); + if (!req) { + talloc_free(sctx); + return ENOMEM; + } + + tevent_req_set_callback(req, grp_search, sctx); + + return EOK; +} + +static void initgr_mem_search(struct sysdb_search_ctx *sctx) +{ + struct sysdb_ctx *ctx = sctx->ctx; + struct ldb_result *res = sctx->res; + struct ldb_request *req; + struct ldb_control **ctrl; + struct ldb_asq_control *control; + static const char *attrs[] = SYSDB_INITGR_ATTRS; + int ret; + + if (res->count == 0) { + return request_done(sctx); + } + if (res->count > 1) { + return request_ldberror(sctx, LDB_ERR_OPERATIONS_ERROR); + } + + /* make sure we don't loop with get_gen_callback() */ + sctx->gen_aux_fn = NULL; + + sctx->expression = talloc_asprintf(sctx, SYSDB_INITGR_FILTER); + if (!sctx->expression) { + return request_ldberror(sctx, LDB_ERR_OPERATIONS_ERROR); + } + + ctrl = talloc_array(sctx, struct ldb_control *, 2); + if (!ctrl) { + return request_ldberror(sctx, LDB_ERR_OPERATIONS_ERROR); + } + ctrl[1] = NULL; + ctrl[0] = talloc(ctrl, struct ldb_control); + if (!ctrl[0]) { + return request_ldberror(sctx, LDB_ERR_OPERATIONS_ERROR); + } + ctrl[0]->oid = LDB_CONTROL_ASQ_OID; + ctrl[0]->critical = 1; + control = talloc(ctrl[0], struct ldb_asq_control); + if (!control) { + return request_ldberror(sctx, LDB_ERR_OPERATIONS_ERROR); + } + control->request = 1; + control->source_attribute = talloc_strdup(control, SYSDB_INITGR_ATTR); + if (!control->source_attribute) { + return request_ldberror(sctx, LDB_ERR_OPERATIONS_ERROR); + } + control->src_attr_len = strlen(control->source_attribute); + ctrl[0]->data = control; + + ret = ldb_build_search_req(&req, ctx->ldb, sctx, + res->msgs[0]->dn, + LDB_SCOPE_BASE, + sctx->expression, attrs, ctrl, + sctx, get_gen_callback, + NULL); + if (ret != LDB_SUCCESS) { + return request_ldberror(sctx, ret); + } + + ret = ldb_request(ctx->ldb, req); + if (ret != LDB_SUCCESS) { + return request_ldberror(sctx, ret); + } +} + +static void initgr_search(struct tevent_req *treq) +{ + struct sysdb_search_ctx *sctx; + static const char *attrs[] = SYSDB_PW_ATTRS; + struct ldb_request *req; + struct ldb_dn *base_dn; + int ret; + + sctx = tevent_req_callback_data(treq, struct sysdb_search_ctx); + + ret = sysdb_operation_recv(treq, sctx, &sctx->handle); + if (ret) { + return request_error(sctx, ret); + } + + sctx->gen_aux_fn = initgr_mem_search; + + base_dn = ldb_dn_new_fmt(sctx, sctx->ctx->ldb, + SYSDB_TMPL_USER_BASE, sctx->domain->name); + if (!base_dn) { + return request_error(sctx, ENOMEM); + } + + ret = ldb_build_search_req(&req, sctx->ctx->ldb, sctx, + base_dn, LDB_SCOPE_SUBTREE, + sctx->expression, attrs, NULL, + sctx, get_gen_callback, + NULL); + if (ret != LDB_SUCCESS) { + return request_ldberror(sctx, ret); + } + + ret = ldb_request(sctx->ctx->ldb, req); + if (ret != LDB_SUCCESS) { + return request_ldberror(sctx, ret); + } +} + +int sysdb_initgroups(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *ctx, + struct sss_domain_info *domain, + const char *name, + sysdb_callback_t fn, void *ptr) +{ + struct sysdb_search_ctx *sctx; + struct tevent_req *req; + + if (!domain) { + return EINVAL; + } + + sctx = init_src_ctx(mem_ctx, domain, ctx, fn, ptr); + if (!sctx) { + return ENOMEM; + } + + sctx->expression = talloc_asprintf(sctx, SYSDB_PWNAM_FILTER, name); + if (!sctx->expression) { + talloc_free(sctx); + return ENOMEM; + } + + req = sysdb_operation_send(mem_ctx, ctx->ev, ctx); + if (!req) { + talloc_free(sctx); + return ENOMEM; + } + + tevent_req_set_callback(req, initgr_search, sctx); + + return EOK; +} + +int sysdb_get_user_attr(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *ctx, + struct sss_domain_info *domain, + const char *name, + const char **attributes, + sysdb_callback_t fn, void *ptr) +{ + struct sysdb_search_ctx *sctx; + struct tevent_req *req; + + if (!domain) { + return EINVAL; + } + + sctx = init_src_ctx(mem_ctx, domain, ctx, fn, ptr); + if (!sctx) { + return ENOMEM; + } + + sctx->expression = talloc_asprintf(sctx, SYSDB_PWNAM_FILTER, name); + if (!sctx->expression) { + talloc_free(sctx); + return ENOMEM; + } + + sctx->attrs = attributes; + + req = sysdb_operation_send(mem_ctx, ctx->ev, ctx); + if (!req) { + talloc_free(sctx); + return ENOMEM; + } + + tevent_req_set_callback(req, user_search, sctx); + + return EOK; +} -- cgit