summaryrefslogtreecommitdiffstats
path: root/src/db/sysdb.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/db/sysdb.c')
-rw-r--r--src/db/sysdb.c1883
1 files changed, 1883 insertions, 0 deletions
diff --git a/src/db/sysdb.c b/src/db/sysdb.c
new file mode 100644
index 000000000..b3f81a083
--- /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;
+}