summaryrefslogtreecommitdiffstats
path: root/src/db
diff options
context:
space:
mode:
authorStephen Gallagher <sgallagh@redhat.com>2010-02-18 07:49:04 -0500
committerStephen Gallagher <sgallagh@redhat.com>2010-02-18 13:48:45 -0500
commit1c48b5a62f73234ed26bb20f0ab345ab61cda0ab (patch)
tree0b6cddd567a862e1a7b5df23764869782a62ca78 /src/db
parent8c56df3176f528fe0260974b3bf934173c4651ea (diff)
downloadsssd_unused-1c48b5a62f73234ed26bb20f0ab345ab61cda0ab.tar.gz
sssd_unused-1c48b5a62f73234ed26bb20f0ab345ab61cda0ab.tar.xz
sssd_unused-1c48b5a62f73234ed26bb20f0ab345ab61cda0ab.zip
Rename server/ directory to src/
Also update BUILD.txt
Diffstat (limited to 'src/db')
-rw-r--r--src/db/sysdb.c1883
-rw-r--r--src/db/sysdb.h650
-rw-r--r--src/db/sysdb_ops.c5059
-rw-r--r--src/db/sysdb_private.h107
-rw-r--r--src/db/sysdb_search.c691
5 files changed, 8390 insertions, 0 deletions
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 <ssorce@redhat.com> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "util/util.h"
+#include "db/sysdb_private.h"
+#include "confdb/confdb.h"
+#include <time.h>
+
+
+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 <ssorce@redhat.com> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __SYS_DB_H__
+#define __SYS_DB_H__
+
+#include "util/util.h"
+#include "confdb/confdb.h"
+#include <tevent.h>
+
+#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 <ssorce@redhat.com> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "util/util.h"
+#include "db/sysdb_private.h"
+#include "util/sha512crypt.h"
+#include <time.h>
+
+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 <ssorce@redhat.com> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <ssorce@redhat.com> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "util/util.h"
+#include "db/sysdb_private.h"
+#include "confdb/confdb.h"
+#include <time.h>
+
+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;
+}