summaryrefslogtreecommitdiffstats
path: root/src/db
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
commit74aa0bc6779af38018a03fd2cf4419fe85917904 (patch)
tree9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/db
parentInitial commit. (diff)
downloadsssd-74aa0bc6779af38018a03fd2cf4419fe85917904.tar.xz
sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.zip
Adding upstream version 2.9.4.upstream/2.9.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/db')
-rw-r--r--src/db/sysdb.c1977
-rw-r--r--src/db/sysdb.h1584
-rw-r--r--src/db/sysdb_autofs.c787
-rw-r--r--src/db/sysdb_autofs.h110
-rw-r--r--src/db/sysdb_certmap.c502
-rw-r--r--src/db/sysdb_domain_resolution_order.c169
-rw-r--r--src/db/sysdb_domain_resolution_order.h37
-rw-r--r--src/db/sysdb_gpo.c685
-rw-r--r--src/db/sysdb_idmap.c316
-rw-r--r--src/db/sysdb_init.c1102
-rw-r--r--src/db/sysdb_iphosts.c875
-rw-r--r--src/db/sysdb_iphosts.h88
-rw-r--r--src/db/sysdb_ipnetworks.c869
-rw-r--r--src/db/sysdb_ipnetworks.h88
-rw-r--r--src/db/sysdb_ops.c5854
-rw-r--r--src/db/sysdb_passkey_user_verification.c236
-rw-r--r--src/db/sysdb_passkey_user_verification.h49
-rw-r--r--src/db/sysdb_private.h317
-rw-r--r--src/db/sysdb_ranges.c405
-rw-r--r--src/db/sysdb_search.c2782
-rw-r--r--src/db/sysdb_selinux.c328
-rw-r--r--src/db/sysdb_selinux.h59
-rw-r--r--src/db/sysdb_services.c837
-rw-r--r--src/db/sysdb_services.h112
-rw-r--r--src/db/sysdb_ssh.c401
-rw-r--r--src/db/sysdb_ssh.h79
-rw-r--r--src/db/sysdb_subdomains.c1801
-rw-r--r--src/db/sysdb_subid.c163
-rw-r--r--src/db/sysdb_subid.h39
-rw-r--r--src/db/sysdb_sudo.c1175
-rw-r--r--src/db/sysdb_sudo.h157
-rw-r--r--src/db/sysdb_upgrade.c2791
-rw-r--r--src/db/sysdb_views.c1845
33 files changed, 28619 insertions, 0 deletions
diff --git a/src/db/sysdb.c b/src/db/sysdb.c
new file mode 100644
index 0000000..1faa11b
--- /dev/null
+++ b/src/db/sysdb.c
@@ -0,0 +1,1977 @@
+/*
+ SSSD
+
+ System Database
+
+ Copyright (C) 2008-2011 Simo Sorce <ssorce@redhat.com>
+ Copyright (C) 2008-2011 Stephen Gallagher <ssorce@redhat.com>
+
+ 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 "util/strtonum.h"
+#include "util/sss_utf8.h"
+#include "util/crypto/sss_crypto.h"
+#include "db/sysdb_private.h"
+#include "confdb/confdb.h"
+#include "util/probes.h"
+#include <time.h>
+
+errno_t sysdb_dn_sanitize(TALLOC_CTX *mem_ctx, const char *input,
+ char **sanitized)
+{
+ struct ldb_val val;
+ errno_t ret = EOK;
+
+ val.data = (uint8_t *)talloc_strdup(mem_ctx, input);
+ if (!val.data) {
+ return ENOMEM;
+ }
+
+ /* We can't include the trailing NULL because it would
+ * be escaped and result in an unterminated string
+ */
+ val.length = strlen(input);
+
+ *sanitized = ldb_dn_escape_value(mem_ctx, val);
+ if (!*sanitized) {
+ ret = ENOMEM;
+ }
+
+ talloc_free(val.data);
+ return ret;
+}
+
+struct ldb_dn *sysdb_custom_subtree_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ const char *subtree_name)
+{
+ errno_t ret;
+ char *clean_subtree;
+ struct ldb_dn *dn = NULL;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return NULL;
+
+ ret = sysdb_dn_sanitize(tmp_ctx, subtree_name, &clean_subtree);
+ if (ret != EOK) {
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ dn = ldb_dn_new_fmt(tmp_ctx, dom->sysdb->ldb, SYSDB_TMPL_CUSTOM_SUBTREE,
+ clean_subtree, dom->name);
+ if (dn) {
+ talloc_steal(mem_ctx, dn);
+ }
+ talloc_free(tmp_ctx);
+
+ return dn;
+}
+
+struct ldb_dn *sysdb_custom_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ const char *object_name,
+ const char *subtree_name)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ char *clean_name;
+ char *clean_subtree;
+ struct ldb_dn *dn = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return NULL;
+ }
+
+ ret = sysdb_dn_sanitize(tmp_ctx, object_name, &clean_name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_dn_sanitize(tmp_ctx, subtree_name, &clean_subtree);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ dn = ldb_dn_new_fmt(mem_ctx, dom->sysdb->ldb, SYSDB_TMPL_CUSTOM, clean_name,
+ clean_subtree, dom->name);
+
+done:
+ talloc_free(tmp_ctx);
+ return dn;
+}
+
+struct ldb_dn *sysdb_user_dn(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
+ const char *name)
+{
+ errno_t ret;
+ char *clean_name;
+ struct ldb_dn *dn;
+
+ ret = sysdb_dn_sanitize(NULL, name, &clean_name);
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ dn = ldb_dn_new_fmt(mem_ctx, dom->sysdb->ldb, SYSDB_TMPL_USER,
+ clean_name, dom->name);
+ talloc_free(clean_name);
+
+ return dn;
+}
+
+struct ldb_dn *sysdb_user_base_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom)
+{
+ return ldb_dn_new_fmt(mem_ctx, dom->sysdb->ldb,
+ SYSDB_TMPL_USER_BASE, dom->name);
+}
+
+struct ldb_dn *sysdb_group_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom, const char *name)
+{
+ errno_t ret;
+ char *clean_name;
+ struct ldb_dn *dn;
+
+ ret = sysdb_dn_sanitize(NULL, name, &clean_name);
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ dn = ldb_dn_new_fmt(mem_ctx, dom->sysdb->ldb, SYSDB_TMPL_GROUP,
+ clean_name, dom->name);
+ talloc_free(clean_name);
+
+ return dn;
+}
+
+struct ldb_dn *sysdb_group_base_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom)
+{
+ return ldb_dn_new_fmt(mem_ctx, dom->sysdb->ldb,
+ SYSDB_TMPL_GROUP_BASE, dom->name);
+}
+
+
+struct ldb_dn *sysdb_netgroup_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom, const char *name)
+{
+ errno_t ret;
+ char *clean_name;
+ struct ldb_dn *dn;
+
+ ret = sysdb_dn_sanitize(NULL, name, &clean_name);
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ dn = ldb_dn_new_fmt(mem_ctx, dom->sysdb->ldb, SYSDB_TMPL_NETGROUP,
+ clean_name, dom->name);
+ talloc_free(clean_name);
+
+ return dn;
+}
+
+struct ldb_dn *sysdb_netgroup_base_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom)
+{
+ return ldb_dn_new_fmt(mem_ctx, dom->sysdb->ldb,
+ SYSDB_TMPL_NETGROUP_BASE, dom->name);
+}
+
+errno_t sysdb_get_rdn(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx,
+ const char *dn, char **_name, char **_val)
+{
+ errno_t ret;
+ struct ldb_dn *ldb_dn;
+ const char *attr_name = NULL;
+ const struct ldb_val *val;
+ TALLOC_CTX *tmp_ctx;
+
+ /* We have to create a tmp_ctx here because
+ * ldb_dn_new_fmt() fails if mem_ctx is NULL
+ */
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ldb_dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, "%s", dn);
+ if (ldb_dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (_name) {
+ attr_name = ldb_dn_get_rdn_name(ldb_dn);
+ if (attr_name == NULL) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ *_name = talloc_strdup(mem_ctx, attr_name);
+ if (!*_name) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ val = ldb_dn_get_rdn_val(ldb_dn);
+ if (val == NULL) {
+ ret = EINVAL;
+ if (_name) talloc_free(*_name);
+ goto done;
+ }
+
+ *_val = talloc_strndup(mem_ctx, (char *) val->data, val->length);
+ if (!*_val) {
+ ret = ENOMEM;
+ if (_name) talloc_free(*_name);
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_group_dn_name(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx,
+ const char *dn, char **_name)
+{
+ return sysdb_get_rdn(sysdb, mem_ctx, dn, NULL, _name);
+}
+
+struct ldb_dn *sysdb_domain_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom)
+{
+ return ldb_dn_new_fmt(mem_ctx, dom->sysdb->ldb, SYSDB_DOM_BASE, dom->name);
+}
+
+struct ldb_dn *sysdb_base_dn(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx)
+{
+ return ldb_dn_new(mem_ctx, sysdb->ldb, SYSDB_BASE);
+}
+
+struct ldb_context *sysdb_ctx_get_ldb(struct sysdb_ctx *sysdb)
+{
+ return sysdb->ldb;
+}
+
+struct sysdb_attrs *sysdb_new_attrs(TALLOC_CTX *mem_ctx)
+{
+ return talloc_zero(mem_ctx, struct sysdb_attrs);
+}
+
+int sysdb_attrs_get_el_ext(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_ext(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_ext(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_get_int32_t(struct sysdb_attrs *attrs, const char *name,
+ int32_t *value)
+{
+ struct ldb_message_element *el;
+ int ret;
+ char *endptr;
+ int32_t val;
+
+ ret = sysdb_attrs_get_el_ext(attrs, name, false, &el);
+ if (ret) {
+ return ret;
+ }
+
+ if (el->num_values != 1) {
+ return ERANGE;
+ }
+
+ val = strtoint32((const char *) el->values[0].data, &endptr, 10);
+ if (errno != 0) return errno;
+ if (*endptr) return EINVAL;
+
+ *value = val;
+ return EOK;
+}
+
+int sysdb_attrs_get_uint32_t(struct sysdb_attrs *attrs, const char *name,
+ uint32_t *value)
+{
+ struct ldb_message_element *el;
+ int ret;
+ char *endptr;
+ uint32_t val;
+
+ ret = sysdb_attrs_get_el_ext(attrs, name, false, &el);
+ if (ret) {
+ return ret;
+ }
+
+ if (el->num_values != 1) {
+ return ERANGE;
+ }
+
+ val = strtouint32((const char *) el->values[0].data, &endptr, 10);
+ if (errno != 0) return errno;
+ if (*endptr) return EINVAL;
+
+ *value = val;
+ return EOK;
+}
+
+int sysdb_attrs_get_uint16_t(struct sysdb_attrs *attrs, const char *name,
+ uint16_t *value)
+{
+ struct ldb_message_element *el;
+ int ret;
+ char *endptr;
+ uint16_t val;
+
+ ret = sysdb_attrs_get_el_ext(attrs, name, false, &el);
+ if (ret) {
+ return ret;
+ }
+
+ if (el->num_values != 1) {
+ return ERANGE;
+ }
+
+ val = strtouint16((const char *) el->values[0].data, &endptr, 10);
+ if (errno != 0) return errno;
+ if (*endptr) return EINVAL;
+
+ *value = val;
+ return EOK;
+}
+
+errno_t sysdb_attrs_get_bool(struct sysdb_attrs *attrs, const char *name,
+ bool *value)
+{
+ struct ldb_message_element *el;
+ int ret;
+
+ ret = sysdb_attrs_get_el_ext(attrs, name, false, &el);
+ if (ret) {
+ return ret;
+ }
+
+ if (el->num_values != 1) {
+ return ERANGE;
+ }
+
+ if (strcmp((const char *)el->values[0].data, "TRUE") == 0)
+ *value = true;
+ else
+ *value = false;
+ return EOK;
+}
+
+const char **sss_ldb_el_to_string_list(TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *el)
+{
+ unsigned int u;
+ const char **a;
+
+ a = talloc_zero_array(mem_ctx, const char *, el->num_values + 1);
+ if (a == NULL) {
+ return NULL;
+ }
+
+ for (u = 0; u < el->num_values; u++) {
+ a[u] = talloc_strndup(a, (const char *)el->values[u].data,
+ el->values[u].length);
+ if (a[u] == NULL) {
+ talloc_free(a);
+ return NULL;
+ }
+ }
+
+ return a;
+}
+
+int sysdb_attrs_get_string_array(struct sysdb_attrs *attrs, const char *name,
+ TALLOC_CTX *mem_ctx, const char ***string)
+{
+ struct ldb_message_element *el;
+ int ret;
+ const char **a;
+
+ ret = sysdb_attrs_get_el_ext(attrs, name, false, &el);
+ if (ret) {
+ return ret;
+ }
+
+ a = sss_ldb_el_to_string_list(mem_ctx, el);
+ if (a == NULL) {
+ return ENOMEM;
+ }
+
+ *string = a;
+ return EOK;
+}
+
+
+static int sysdb_attrs_add_val_int(struct sysdb_attrs *attrs,
+ const char *name, bool check_values,
+ const struct ldb_val *val)
+{
+ struct ldb_message_element *el = NULL;
+ struct ldb_val *vals;
+ int ret;
+ size_t c;
+
+ ret = sysdb_attrs_get_el(attrs, name, &el);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ if (check_values) {
+ for (c = 0; c < el->num_values; c++) {
+ if (val->length == el->values[c].length
+ && memcmp(val->data, el->values[c].data,
+ val->length) == 0) {
+ return EOK;
+ }
+ }
+ }
+
+ 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_empty(struct sysdb_attrs *attrs, const char *name)
+{
+ struct ldb_message_element *el;
+
+ /* Calling this will create the element if it does not exist. */
+ return sysdb_attrs_get_el_ext(attrs, name, true, &el);
+}
+
+int sysdb_attrs_add_val(struct sysdb_attrs *attrs,
+ const char *name, const struct ldb_val *val)
+{
+ return sysdb_attrs_add_val_int(attrs, name, false, val);
+}
+
+/* Check if the same value already exists. */
+int sysdb_attrs_add_val_safe(struct sysdb_attrs *attrs,
+ const char *name, const struct ldb_val *val)
+{
+ return sysdb_attrs_add_val_int(attrs, name, true, val);
+}
+
+int sysdb_attrs_add_string_safe(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_safe(attrs, name, &v);
+}
+
+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_add_lower_case_string(struct sysdb_attrs *attrs, bool safe,
+ const char *name, const char *str)
+{
+ char *lc_str;
+ int ret;
+
+ if (attrs == NULL || str == NULL) {
+ return EINVAL;
+ }
+
+ lc_str = sss_tc_utf8_str_tolower(attrs, str);
+ if (lc_str == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot convert name to lowercase.\n");
+ return ENOMEM;
+ }
+
+ if (safe) {
+ ret = sysdb_attrs_add_string_safe(attrs, name, lc_str);
+ } else {
+ ret = sysdb_attrs_add_string(attrs, name, lc_str);
+ }
+ talloc_free(lc_str);
+
+ return ret;
+}
+
+int sysdb_attrs_add_mem(struct sysdb_attrs *attrs, const char *name,
+ const void *mem, size_t size)
+{
+ struct ldb_val v;
+
+ v.data = discard_const(mem);
+ v.length = size;
+ return sysdb_attrs_add_val(attrs, name, &v);
+}
+
+int sysdb_attrs_add_base64_blob(struct sysdb_attrs *attrs, const char *name,
+ const char *base64_str)
+{
+ struct ldb_val v;
+ int ret;
+
+ if (base64_str == NULL) {
+ return EINVAL;
+ }
+
+ v.data = sss_base64_decode(attrs, base64_str, &v.length);
+ if (v.data == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_base64_decode failed.\n");
+ return ENOMEM;
+ }
+
+ ret = sysdb_attrs_add_val(attrs, name, &v);
+ talloc_free(v.data);
+ return ret;
+}
+
+int sysdb_attrs_add_bool(struct sysdb_attrs *attrs,
+ const char *name, bool value)
+{
+ if(value) {
+ return sysdb_attrs_add_string(attrs, name, "TRUE");
+ }
+
+ return sysdb_attrs_add_string(attrs, name, "FALSE");
+}
+
+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);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ 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_add_lc_name_alias(struct sysdb_attrs *attrs,
+ const char *value)
+{
+ return sysdb_attrs_add_lower_case_string(attrs, false, SYSDB_NAME_ALIAS,
+ value);
+}
+
+int sysdb_attrs_add_lc_name_alias_safe(struct sysdb_attrs *attrs,
+ const char *value)
+{
+ return sysdb_attrs_add_lower_case_string(attrs, true, SYSDB_NAME_ALIAS,
+ value);
+}
+
+int sysdb_attrs_copy_values(struct sysdb_attrs *src,
+ struct sysdb_attrs *dst,
+ const char *name)
+{
+ int ret = EOK;
+ int i;
+ struct ldb_message_element *src_el;
+
+ ret = sysdb_attrs_get_el(src, name, &src_el);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ for (i = 0; i < src_el->num_values; i++) {
+ ret = sysdb_attrs_add_val(dst, name, &src_el->values[i]);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+done:
+ return ret;
+}
+
+errno_t sysdb_attrs_copy(struct sysdb_attrs *src, struct sysdb_attrs *dst)
+{
+ int ret;
+ size_t c;
+ size_t d;
+
+ if (src == NULL || dst == NULL) {
+ return EINVAL;
+ }
+
+ for (c = 0; c < src->num; c++) {
+ for (d = 0; d < src->a[c].num_values; d++) {
+ ret = sysdb_attrs_add_val_safe(dst, src->a[c].name,
+ &src->a[c].values[d]);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_val failed.\n");
+ return ret;
+ }
+ }
+ }
+
+ return EOK;
+}
+
+int sysdb_attrs_users_from_str_list(struct sysdb_attrs *attrs,
+ const char *attr_name,
+ const char *domain,
+ const char *const *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(SSSDBG_TRACE_ALL, "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(SSSDBG_CONF_SETTINGS,
+ "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(SSSDBG_TRACE_LIBS, " member #%d: [%s]\n", i, member);
+ }
+ el->num_values = j;
+
+ return EOK;
+}
+
+static char *build_dom_dn_str_escape(TALLOC_CTX *mem_ctx, 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(mem_ctx, v);
+ if (!tmp) {
+ return NULL;
+ }
+
+ ret = talloc_asprintf(mem_ctx, template, tmp, domain);
+ talloc_zfree(tmp);
+ if (!ret) {
+ return NULL;
+ }
+
+ return ret;
+ }
+
+ ret = talloc_asprintf(mem_ctx, template, name, domain);
+ if (!ret) {
+ return NULL;
+ }
+
+ return ret;
+}
+
+char *sysdb_user_strdn(TALLOC_CTX *mem_ctx,
+ const char *domain, const char *name)
+{
+ return build_dom_dn_str_escape(mem_ctx, SYSDB_TMPL_USER, domain, name);
+}
+
+char *sysdb_group_strdn(TALLOC_CTX *mem_ctx,
+ const char *domain, const char *name)
+{
+ return build_dom_dn_str_escape(mem_ctx, SYSDB_TMPL_GROUP, domain, name);
+}
+
+/* =Transactions========================================================== */
+
+int sysdb_transaction_start(struct sysdb_ctx *sysdb)
+{
+ int ret;
+
+ ret = ldb_transaction_start(sysdb->ldb);
+ if (ret == LDB_SUCCESS) {
+ PROBE(SYSDB_TRANSACTION_START, sysdb->transaction_nesting);
+ sysdb->transaction_nesting++;
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to start ldb transaction! (%d)\n", ret);
+ }
+ return sysdb_error_to_errno(ret);
+}
+
+int sysdb_transaction_commit(struct sysdb_ctx *sysdb)
+{
+ int ret;
+#ifdef HAVE_SYSTEMTAP
+ int commit_nesting = sysdb->transaction_nesting-1;
+#endif
+
+ PROBE(SYSDB_TRANSACTION_COMMIT_BEFORE, commit_nesting);
+ ret = ldb_transaction_commit(sysdb->ldb);
+ if (ret == LDB_SUCCESS) {
+ sysdb->transaction_nesting--;
+ PROBE(SYSDB_TRANSACTION_COMMIT_AFTER, sysdb->transaction_nesting);
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to commit ldb transaction! (%d)\n", ret);
+ }
+ return sysdb_error_to_errno(ret);
+}
+
+int sysdb_transaction_cancel(struct sysdb_ctx *sysdb)
+{
+ int ret;
+
+ ret = ldb_transaction_cancel(sysdb->ldb);
+ if (ret == LDB_SUCCESS) {
+ sysdb->transaction_nesting--;
+ PROBE(SYSDB_TRANSACTION_CANCEL, sysdb->transaction_nesting);
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to cancel ldb transaction! (%d)\n", ret);
+ }
+ return sysdb_error_to_errno(ret);
+}
+
+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(SSSDBG_MINOR_FAILURE,
+ "New attribute name [%s] already exists.\n", newname);
+ return EEXIST;
+ }
+ }
+
+ if (e != NULL) {
+ dummy = talloc_strdup(attrs, newname);
+ if (dummy == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n");
+ return ENOMEM;
+ }
+
+ talloc_free(discard_const(e->name));
+ e->name = dummy;
+ }
+
+ return EOK;
+}
+
+/* Search for all incidences of attr_name in a list of
+ * sysdb_attrs and add their value to a list
+ *
+ * TODO: Currently only works for single-valued
+ * attributes. Multi-valued attributes will return
+ * only the first entry
+ */
+errno_t sysdb_attrs_to_list(TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs **attrs,
+ int attr_count,
+ const char *attr_name,
+ char ***_list)
+{
+ int attr_idx;
+ int i;
+ char **list;
+ char **tmp_list;
+ int list_idx;
+
+ *_list = NULL;
+
+ /* Assume that every attrs entry contains the attr_name
+ * This may waste a little memory if some entries don't
+ * have the attribute, but it will save us the trouble
+ * of continuously resizing the array.
+ */
+ list = talloc_array(mem_ctx, char *, attr_count+1);
+ if (!list) {
+ return ENOMEM;
+ }
+
+ list_idx = 0;
+ /* Loop through all entries in attrs */
+ for (attr_idx = 0; attr_idx < attr_count; attr_idx++) {
+ /* Examine each attribute within the entry */
+ for (i = 0; i < attrs[attr_idx]->num; i++) {
+ if (strcasecmp(attrs[attr_idx]->a[i].name, attr_name) == 0) {
+ /* Attribute name matches the requested name
+ * Copy it to the output list
+ */
+ list[list_idx] = talloc_strdup(
+ list,
+ (const char *)attrs[attr_idx]->a[i].values[0].data);
+ if (!list[list_idx]) {
+ talloc_free(list);
+ return ENOMEM;
+ }
+ list_idx++;
+
+ /* We only support single-valued attributes
+ * Break here and go on to the next entry
+ */
+ break;
+ }
+ }
+ }
+
+ list[list_idx] = NULL;
+
+ /* if list_idx < attr_count, do a realloc to
+ * reclaim unused memory
+ */
+ if (list_idx < attr_count) {
+ tmp_list = talloc_realloc(mem_ctx, list, char *, list_idx+1);
+ if (!tmp_list) {
+ talloc_zfree(list);
+ return ENOMEM;
+ }
+ list = tmp_list;
+ }
+
+ *_list = list;
+ return EOK;
+}
+
+errno_t sysdb_get_bool(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char *attr_name,
+ bool *value)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ errno_t ret;
+ int lret;
+ const char *attrs[2] = {attr_name, NULL};
+ struct ldb_message_element *el;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ lret = ldb_search(sysdb->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
+ attrs, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ if (res->count == 0) {
+ /* This entry has not been populated in LDB
+ * This is a common case, as unlike LDAP,
+ * LDB does not need to have all of its parent
+ * objects actually exist.
+ * This object in the sysdb exists mostly just
+ * to contain this attribute.
+ */
+ *value = false;
+ ret = ENOENT;
+ goto done;
+ } else if (res->count != 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Got more than one reply for base search!\n");
+ ret = EIO;
+ goto done;
+ }
+
+ el = ldb_msg_find_element(res->msgs[0], attr_name);
+ if (el == NULL || el->num_values == 0) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ *value = ldb_msg_find_attr_as_bool(res->msgs[0], attr_name, false);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_set_bool(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char *cn_value,
+ const char *attr_name,
+ bool value)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct ldb_message *msg = NULL;
+ struct ldb_result *res = NULL;
+ errno_t ret;
+ int lret;
+
+ if (dn == NULL || attr_name == NULL) {
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ lret = ldb_search(sysdb->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
+ NULL, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ msg->dn = dn;
+
+ if (res->count == 0) {
+ if (cn_value == NULL) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ lret = ldb_msg_add_string(msg, "cn", cn_value);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ } else if (res->count != 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Got more than one reply for base search!\n");
+ ret = EIO;
+ goto done;
+ } else {
+ lret = ldb_msg_add_empty(msg, attr_name, LDB_FLAG_MOD_REPLACE, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ }
+
+ lret = ldb_msg_add_string(msg, attr_name, value ? "TRUE" : "FALSE");
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ if (res->count) {
+ lret = ldb_modify(sysdb->ldb, msg);
+ } else {
+ lret = ldb_add(sysdb->ldb, msg);
+ }
+
+ if (lret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "ldb operation failed: [%s](%d)[%s]\n",
+ ldb_strerror(lret), lret, ldb_errstring(sysdb->ldb));
+ }
+ ret = sysdb_error_to_errno(lret);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_get_uint(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char *attr_name,
+ uint32_t *value)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ errno_t ret;
+ int lret;
+ const char *attrs[2] = {attr_name, NULL};
+ struct ldb_message_element *el;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ lret = ldb_search(sysdb->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
+ attrs, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ if (res->count == 0) {
+ /* This entry has not been populated in LDB
+ * This is a common case, as unlike LDAP,
+ * LDB does not need to have all of its parent
+ * objects actually exist.
+ * This object in the sysdb exists mostly just
+ * to contain this attribute.
+ */
+ *value = false;
+ ret = ENOENT;
+ goto done;
+ } else if (res->count != 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Got more than one reply for base search!\n");
+ ret = EIO;
+ goto done;
+ }
+
+ el = ldb_msg_find_element(res->msgs[0], attr_name);
+ if (el == NULL || el->num_values == 0) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ *value = ldb_msg_find_attr_as_uint(res->msgs[0], attr_name, false);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_set_uint(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char *cn_value,
+ const char *attr_name,
+ uint32_t value)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct ldb_message *msg = NULL;
+ struct ldb_result *res = NULL;
+ errno_t ret;
+ int lret;
+
+ if (dn == NULL || attr_name == NULL) {
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ lret = ldb_search(sysdb->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
+ NULL, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ msg->dn = dn;
+
+ if (res->count == 0) {
+ if (cn_value == NULL) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ lret = ldb_msg_add_string(msg, "cn", cn_value);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ } else if (res->count != 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Got more than one reply for base search!\n");
+ ret = EIO;
+ goto done;
+ } else {
+ lret = ldb_msg_add_empty(msg, attr_name, LDB_FLAG_MOD_REPLACE, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ }
+
+ lret = ldb_msg_add_fmt(msg, attr_name, "%u", value);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ if (res->count) {
+ lret = ldb_modify(sysdb->ldb, msg);
+ } else {
+ lret = ldb_add(sysdb->ldb, msg);
+ }
+
+ if (lret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "ldb operation failed: [%s](%d)[%s]\n",
+ ldb_strerror(lret), lret, ldb_errstring(sysdb->ldb));
+ }
+ ret = sysdb_error_to_errno(lret);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_has_enumerated(struct sss_domain_info *domain,
+ uint32_t provider,
+ bool *has_enumerated)
+{
+ errno_t ret;
+ struct ldb_dn *dn;
+ TALLOC_CTX *tmp_ctx;
+ uint32_t enumerated;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ dn = sysdb_domain_dn(tmp_ctx, domain);
+ if (!dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_get_uint(domain->sysdb, dn, SYSDB_HAS_ENUMERATED,
+ &enumerated);
+
+ if (ret != EOK) {
+ return ret;
+ }
+
+ *has_enumerated = (enumerated & provider);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_set_enumerated(struct sss_domain_info *domain,
+ uint32_t provider,
+ bool has_enumerated)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *dn;
+ uint32_t enumerated = 0;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ dn = sysdb_domain_dn(tmp_ctx, domain);
+ if (!dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_get_uint(domain->sysdb, dn, SYSDB_HAS_ENUMERATED,
+ &enumerated);
+
+ if (ret != EOK && ret != ENOENT) {
+ return ret;
+ }
+
+ if (has_enumerated) {
+ enumerated |= provider;
+ } else {
+ enumerated &= ~provider;
+ }
+
+ ret = sysdb_set_uint(domain->sysdb, dn, domain->name,
+ SYSDB_HAS_ENUMERATED, enumerated);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/*
+ * An entity with multiple names would have multiple SYSDB_NAME attributes
+ * after being translated into sysdb names using a map.
+ * Given a primary name returned by sysdb_attrs_primary_name(), this function
+ * returns the other SYSDB_NAME attribute values so they can be saved as
+ * SYSDB_NAME_ALIAS into cache.
+ *
+ * If lowercase is set, all aliases are duplicated in lowercase as well.
+ */
+errno_t sysdb_attrs_get_aliases(TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs *attrs,
+ const char *primary,
+ bool lowercase,
+ const char ***_aliases)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct ldb_message_element *sysdb_name_el;
+ size_t i, j, ai;
+ errno_t ret;
+ const char **aliases = NULL;
+ const char *name;
+ char *lower;
+
+ if (_aliases == NULL) return EINVAL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_attrs_get_el(attrs,
+ SYSDB_NAME,
+ &sysdb_name_el);
+ if (ret != EOK || sysdb_name_el->num_values == 0) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ aliases = talloc_array(tmp_ctx, const char *,
+ sysdb_name_el->num_values + 1);
+ if (!aliases) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (lowercase) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Domain is case-insensitive; will add lowercased aliases\n");
+ }
+
+ ai = 0;
+ for (i=0; i < sysdb_name_el->num_values; i++) {
+ name = (const char *)sysdb_name_el->values[i].data;
+
+ if (lowercase) {
+ /* Domain is case-insensitive. Save the lower-cased version */
+ lower = sss_tc_utf8_str_tolower(tmp_ctx, name);
+ if (!lower) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (j=0; j < ai; j++) {
+ if (sss_utf8_case_eq((const uint8_t *) aliases[j],
+ (const uint8_t *) lower) == ENOMATCH) {
+ break;
+ }
+ }
+
+ if (ai == 0 || j < ai) {
+ aliases[ai] = talloc_strdup(aliases, lower);
+ if (!aliases[ai]) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ai++;
+ }
+ } else {
+ /* Domain is case-sensitive. Save it as-is */
+ if (strcmp(primary, name) != 0) {
+ aliases[ai] = talloc_strdup(aliases, name);
+ if (!aliases[ai]) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ai++;
+ }
+ }
+ }
+
+ aliases[ai] = NULL;
+
+ ret = EOK;
+
+done:
+ *_aliases = talloc_steal(mem_ctx, aliases);
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_msg2attrs(TALLOC_CTX *mem_ctx, size_t count,
+ struct ldb_message **msgs,
+ struct sysdb_attrs ***attrs)
+{
+ int i;
+ struct sysdb_attrs **a;
+
+ a = talloc_array(mem_ctx, struct sysdb_attrs *, count);
+ if (a == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_array failed.\n");
+ return ENOMEM;
+ }
+
+ for (i = 0; i < count; i++) {
+ a[i] = talloc(a, struct sysdb_attrs);
+ if (a[i] == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n");
+ talloc_free(a);
+ return ENOMEM;
+ }
+ a[i]->num = msgs[i]->num_elements;
+ a[i]->a = talloc_steal(a[i], msgs[i]->elements);
+ }
+
+ *attrs = a;
+
+ return EOK;
+}
+
+struct ldb_message *sysdb_attrs2msg(TALLOC_CTX *mem_ctx,
+ struct ldb_dn *entry_dn,
+ struct sysdb_attrs *attrs,
+ int mod_op)
+{
+ struct ldb_message *msg;
+ errno_t ret;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = entry_dn;
+
+ msg->elements = talloc_array(msg, struct ldb_message_element, attrs->num);
+ if (msg->elements == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (int i = 0; i < attrs->num; i++) {
+ msg->elements[i] = attrs->a[i];
+ msg->elements[i].flags = mod_op;
+ }
+ msg->num_elements = attrs->num;
+
+ ret = EOK;
+done:
+ if (ret != EOK) {
+ talloc_zfree(msg);
+ }
+ return msg;
+}
+
+int sysdb_compare_usn(const char *a, const char *b)
+{
+ size_t len_a;
+ size_t len_b;
+
+ if (a == NULL) {
+ return -1;
+ }
+
+ if (b == NULL) {
+ return 1;
+ }
+
+ len_a = strlen(a);
+ len_b = strlen(b);
+
+ /* trim leading zeros */
+ while (len_a > 0 && *a == '0') {
+ a++;
+ len_a--;
+ }
+
+ while (len_b > 0 && *b == '0') {
+ b++;
+ len_b--;
+ }
+
+ /* less digits means lower number */
+ if (len_a < len_b) {
+ return -1;
+ }
+
+ /* more digits means bigger number */
+ if (len_a > len_b) {
+ return 1;
+ }
+
+ /* now we can compare digits since alphabetical order is the same
+ * as numeric order */
+ return strcmp(a, b);
+}
+
+errno_t sysdb_get_highest_usn(TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs **attrs,
+ size_t num_attrs,
+ char **_usn)
+{
+ const char *highest = NULL;
+ const char *current = NULL;
+ char *usn;
+ errno_t ret;
+ size_t i;
+
+ if (num_attrs == 0 || attrs == NULL) {
+ goto done;
+ }
+
+ for (i = 0; i < num_attrs; i++) {
+ ret = sysdb_attrs_get_string(attrs[i], SYSDB_USN, &current);
+ if (ret == ENOENT) {
+ /* USN value is not present, assuming zero. */
+ current = "0";
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Failed to retrieve USN value "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+
+ return ret;
+ }
+
+ if (current == NULL) {
+ continue;
+ }
+
+ if (highest == NULL) {
+ highest = current;
+ continue;
+ }
+
+ if (sysdb_compare_usn(current, highest) > 0 ) {
+ highest = current;
+ }
+ }
+
+done:
+ if (highest == NULL) {
+ usn = talloc_strdup(mem_ctx, "0");
+ } else {
+ usn = talloc_strdup(mem_ctx, highest);
+ }
+
+ if (usn == NULL) {
+ return ENOMEM;
+ }
+
+ *_usn = usn;
+ return EOK;
+}
+
+static int sysdb_ldb_msg_string_helper(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;
+}
+
+int sysdb_add_string(struct ldb_message *msg,
+ const char *attr, const char *value)
+{
+ return sysdb_ldb_msg_string_helper(msg, LDB_FLAG_MOD_ADD, attr, value);
+}
+
+int sysdb_replace_string(struct ldb_message *msg,
+ const char *attr, const char *value)
+{
+ return sysdb_ldb_msg_string_helper(msg, LDB_FLAG_MOD_REPLACE, attr, value);
+}
+
+int sysdb_delete_string(struct ldb_message *msg,
+ const char *attr, const char *value)
+{
+ return sysdb_ldb_msg_string_helper(msg, LDB_FLAG_MOD_DELETE, attr, value);
+}
+
+static int sysdb_ldb_msg_ulong_helper(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;
+}
+
+int sysdb_add_ulong(struct ldb_message *msg,
+ const char *attr, unsigned long value)
+{
+ return sysdb_ldb_msg_ulong_helper(msg, LDB_FLAG_MOD_ADD, attr, value);
+}
+
+int sysdb_replace_ulong(struct ldb_message *msg,
+ const char *attr, unsigned long value)
+{
+ return sysdb_ldb_msg_ulong_helper(msg, LDB_FLAG_MOD_REPLACE, attr, value);
+}
+
+int sysdb_delete_ulong(struct ldb_message *msg,
+ const char *attr, unsigned long value)
+{
+ return sysdb_ldb_msg_ulong_helper(msg, LDB_FLAG_MOD_DELETE, attr, value);
+}
+
+bool is_ts_ldb_dn(struct ldb_dn *dn)
+{
+ const char *sysdb_comp_name = NULL;
+ const struct ldb_val *sysdb_comp_val = NULL;
+
+ if (dn == NULL) {
+ return false;
+ }
+
+ sysdb_comp_name = ldb_dn_get_component_name(dn, 1);
+ if (strcasecmp("cn", sysdb_comp_name) != 0) {
+ /* The second component name is not "cn" */
+ return false;
+ }
+
+ sysdb_comp_val = ldb_dn_get_component_val(dn, 1);
+ if (strncasecmp("users",
+ (const char *) sysdb_comp_val->data,
+ sysdb_comp_val->length) == 0) {
+ return true;
+ }
+
+ sysdb_comp_val = ldb_dn_get_component_val(dn, 1);
+ if (strncasecmp("groups",
+ (const char *) sysdb_comp_val->data,
+ sysdb_comp_val->length) == 0) {
+ return true;
+ }
+
+ return false;
+}
+
+bool sysdb_msg_attrs_modts_differs(struct ldb_message *old_entry,
+ struct sysdb_attrs *new_entry)
+{
+ const char *old_entry_ts_attr = NULL;
+ const char *new_entry_ts_attr = NULL;
+ errno_t ret;
+
+ old_entry_ts_attr = ldb_msg_find_attr_as_string(old_entry,
+ SYSDB_ORIG_MODSTAMP,
+ NULL);
+ if (old_entry_ts_attr == NULL) {
+ /* we didn't know the originalModifyTimestamp earlier. Regardless
+ * of whether the new_entry has the timestamp, we should do
+ * a comparison of the attributes
+ */
+ return true;
+ }
+
+ if (new_entry == NULL) {
+ return false;
+ }
+
+ ret = sysdb_attrs_get_string(new_entry, SYSDB_ORIG_MODSTAMP,
+ &new_entry_ts_attr);
+ if (ret != EOK) {
+ /* Nothing to compare against in the new entry either, do
+ * a comparison of the attributes
+ */
+ return true;
+ }
+
+ if (old_entry_ts_attr != NULL
+ && new_entry_ts_attr != NULL
+ && strcmp(old_entry_ts_attr, new_entry_ts_attr) == 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool sysdb_ldb_msg_difference(struct ldb_dn *entry_dn,
+ struct ldb_message *db_msg,
+ struct ldb_message *mod_msg)
+{
+ struct ldb_message_element *mod_msg_el;
+ struct ldb_message_element *db_msg_el;
+ int el_differs;
+
+ for (unsigned i = 0; i < mod_msg->num_elements; i++) {
+ mod_msg_el = &mod_msg->elements[i];
+
+ switch (mod_msg_el->flags) {
+ case 0:
+ /* Unspecified flags are internally converted to SYSDB_MOD_REP in
+ * sysdb_set_group_attr, do the same here
+ */
+ case SYSDB_MOD_ADD:
+ case SYSDB_MOD_REP:
+ db_msg_el = ldb_msg_find_element(db_msg, mod_msg_el->name);
+ if (db_msg_el == NULL) {
+ /* The attribute to be added does not exist in the target
+ * message, this is a modification. Special-case adding
+ * empty elements which also do not exist in the target
+ * message. This is how sysdb callers ensure a particular
+ * element is not present in the database.
+ */
+ if (mod_msg_el->num_values > 0) {
+ /* We can ignore additions of timestamp attributes */
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Added attr [%s] to entry [%s]\n",
+ mod_msg_el->name, ldb_dn_get_linearized(entry_dn));
+ return true;
+ }
+ break;
+ }
+
+ el_differs = ldb_msg_element_compare(db_msg_el, mod_msg_el);
+ if (el_differs) {
+ /* We are replacing or extending element, there is a difference.
+ * If some values already exist and ldb_add is not permissive,
+ * ldb will throw an error, but that's not our job to check..
+ */
+ if (is_ts_cache_attr(mod_msg_el->name) == false) {
+ /* We can ignore changes to timestamp attributes */
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Replaced/extended attr [%s] of entry [%s]\n",
+ mod_msg_el->name, ldb_dn_get_linearized(entry_dn));
+ return true;
+ }
+ }
+ break;
+ case SYSDB_MOD_DEL:
+ db_msg_el = ldb_msg_find_element(db_msg, mod_msg_el->name);
+ if (db_msg_el != NULL) {
+ /* We are deleting a valid element, there is a difference */
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Deleted attr [%s] of entry [%s].\n",
+ mod_msg_el->name, ldb_dn_get_linearized(entry_dn));
+ return true;
+ }
+ break;
+ }
+ }
+
+ return false;
+}
+
+bool sysdb_entry_attrs_diff(struct sysdb_ctx *sysdb,
+ struct ldb_dn *entry_dn,
+ struct sysdb_attrs *attrs,
+ int mod_op)
+{
+ struct ldb_message *new_entry_msg = NULL;
+ TALLOC_CTX *tmp_ctx;
+ bool differs = true;
+ int lret;
+ struct ldb_result *res;
+ const char *attrnames[attrs->num+1];
+
+ if (sysdb->ldb_ts == NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Entry [%s] differs, reason: there is no ts_cache yet.\n",
+ ldb_dn_get_linearized(entry_dn));
+ return true;
+ }
+
+ if (is_ts_ldb_dn(entry_dn) == false) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Entry [%s] differs, reason: ts_cache doesn't trace this type of entry.\n",
+ ldb_dn_get_linearized(entry_dn));
+ return true;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ goto done;
+ }
+
+ new_entry_msg = sysdb_attrs2msg(tmp_ctx, entry_dn,
+ attrs, mod_op);
+ if (new_entry_msg == NULL) {
+ goto done;
+ }
+
+ for (int i = 0; i < attrs->num; i++) {
+ attrnames[i] = attrs->a[i].name;
+ }
+ attrnames[attrs->num] = NULL;
+
+ lret = ldb_search(sysdb->ldb, tmp_ctx, &res, entry_dn, LDB_SCOPE_BASE,
+ attrnames, NULL);
+ if (lret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot search sysdb: %d\n",
+ sysdb_error_to_errno(lret));
+ goto done;
+ }
+
+ if (res->count == 0) {
+ return true;
+ } else if (res->count != 1) {
+ goto done;
+ }
+
+ differs = sysdb_ldb_msg_difference(entry_dn, res->msgs[0], new_entry_msg);
+done:
+ talloc_free(tmp_ctx);
+ return differs;
+}
+
+void ldb_debug_messages(void *context, enum ldb_debug_level level,
+ const char *fmt, va_list ap)
+{
+ int loglevel = SSSDBG_UNRESOLVED;
+
+ switch(level) {
+ case LDB_DEBUG_FATAL:
+ loglevel = SSSDBG_FATAL_FAILURE;
+ break;
+ case LDB_DEBUG_ERROR:
+ loglevel = SSSDBG_CRIT_FAILURE;
+ break;
+ case LDB_DEBUG_WARNING:
+ loglevel = SSSDBG_TRACE_FUNC;
+ break;
+ case LDB_DEBUG_TRACE:
+ loglevel = SSSDBG_TRACE_LDB;
+ break;
+ }
+
+ sss_vdebug_fn(__FILE__, __LINE__, "ldb", loglevel, APPEND_LINE_FEED,
+ fmt, ap);
+}
+
+struct sss_domain_info *find_domain_by_msg(struct sss_domain_info *dom,
+ struct ldb_message *msg)
+{
+ const char *name;
+ struct sss_domain_info *obj_dom = NULL;
+
+ name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+ if (name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Object does not have a name attribute.\n");
+ return dom;
+ }
+
+ obj_dom = find_domain_by_object_name(get_domains_head(dom), name);
+ if (obj_dom == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "No domain found for [%s].\n", name);
+ return dom;
+ }
+
+ return obj_dom;
+}
diff --git a/src/db/sysdb.h b/src/db/sysdb.h
new file mode 100644
index 0000000..55c6437
--- /dev/null
+++ b/src/db/sysdb.h
@@ -0,0 +1,1584 @@
+/*
+ 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 "sss_client/sss_cli.h"
+#include <ldb.h>
+#include <tevent.h>
+
+#define CACHE_SYSDB_FILE "cache_%s.ldb"
+#define CACHE_TIMESTAMPS_FILE "timestamps_%s.ldb"
+#define LOCAL_SYSDB_FILE "sssd.ldb"
+
+#define SYSDB_INDEXES "@INDEXLIST"
+#define SYSDB_IDXATTR "@IDXATTR"
+
+#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_NETGROUP_CONTAINER "cn=Netgroups"
+#define SYSDB_RANGE_CONTAINER "cn=ranges"
+#define SYSDB_VIEW_CONTAINER "cn=views"
+#define SYSDB_CERTMAP_CONTAINER "cn=certmap"
+#define SYSDB_TMPL_USER_BASE SYSDB_USERS_CONTAINER","SYSDB_DOM_BASE
+#define SYSDB_TMPL_GROUP_BASE SYSDB_GROUPS_CONTAINER","SYSDB_DOM_BASE
+#define SYSDB_TMPL_CUSTOM_BASE SYSDB_CUSTOM_CONTAINER","SYSDB_DOM_BASE
+#define SYSDB_TMPL_NETGROUP_BASE SYSDB_NETGROUP_CONTAINER","SYSDB_DOM_BASE
+#define SYSDB_TMPL_RANGE_BASE SYSDB_RANGE_CONTAINER","SYSDB_BASE
+#define SYSDB_TMPL_VIEW_BASE SYSDB_VIEW_CONTAINER","SYSDB_BASE
+#define SYSDB_TMPL_VIEW_SEARCH_BASE "cn=%s,"SYSDB_TMPL_VIEW_BASE
+#define SYSDB_TMPL_CERTMAP_BASE SYSDB_CERTMAP_CONTAINER","SYSDB_BASE
+
+#define SYSDB_SUBDOMAIN_CLASS "subdomain"
+#define SYSDB_USER_CLASS "user"
+#define SYSDB_GROUP_CLASS "group"
+#define SYSDB_NETGROUP_CLASS "netgroup"
+#define SYSDB_HOST_CLASS "host"
+#define SYSDB_HOSTGROUP_CLASS "hostgroup"
+#define SYSDB_SELINUX_USERMAP_CLASS "selinuxusermap"
+#define SYSDB_SELINUX_CLASS "selinux"
+#define SYSDB_ID_RANGE_CLASS "idRange"
+#define SYSDB_DOMAIN_ID_RANGE_CLASS "domainIDRange"
+#define SYSDB_TRUSTED_AD_DOMAIN_RANGE_CLASS "TrustedADDomainRange"
+#define SYSDB_CERTMAP_CLASS "certificateMappingRule"
+
+#define SYSDB_DN "dn"
+#define SYSDB_NAME "name"
+#define SYSDB_NAME_ALIAS "nameAlias"
+#define SYSDB_OBJECTCLASS "objectClass"
+
+#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_GHOST "ghost"
+#define SYSDB_POSIX "isPosix"
+#define SYSDB_USER_CATEGORY "userCategory"
+#define SYSDB_HOST_CATEGORY "hostCategory"
+#define SYSDB_GROUP_TYPE "groupType"
+#define SYSDB_EXTERNAL_MEMBER "externalMember"
+
+#define SYSDB_GECOS "gecos"
+#define SYSDB_LAST_LOGIN "lastLogin"
+#define SYSDB_LAST_ONLINE_AUTH "lastOnlineAuth"
+#define SYSDB_LAST_FAILED_LOGIN "lastFailedLogin"
+#define SYSDB_FAILED_LOGIN_ATTEMPTS "failedLoginAttempts"
+#define SYSDB_LAST_ONLINE_AUTH_WITH_CURR_TOKEN "lastOnlineAuthWithCurrentToken"
+
+#define SYSDB_LAST_UPDATE "lastUpdate"
+#define SYSDB_CACHE_EXPIRE "dataExpireTimestamp"
+#define SYSDB_INITGR_EXPIRE "initgrExpireTimestamp"
+#define SYSDB_ENUM_EXPIRE "enumerationExpireTimestamp"
+#define SYSDB_IFP_CACHED "ifpCached"
+
+#define SYSDB_AUTHORIZED_SERVICE "authorizedService"
+#define SYSDB_AUTHORIZED_HOST "authorizedHost"
+#define SYSDB_AUTHORIZED_RHOST "authorizedRHost"
+
+#define SYSDB_NETGROUP_TRIPLE "netgroupTriple"
+#define SYSDB_ORIG_NETGROUP_MEMBER "originalMemberNisNetgroup"
+#define SYSDB_ORIG_NETGROUP_EXTERNAL_HOST "originalExternalHost"
+#define SYSDB_NETGROUP_DOMAIN "nisDomain"
+#define SYSDB_NETGROUP_MEMBER "memberNisNetgroup"
+#define SYSDB_DESCRIPTION "description"
+
+#define SYSDB_FQDN "fqdn"
+#define SYSDB_SERVERHOSTNAME "serverHostname"
+
+#define SYSDB_CACHEDPWD "cachedPassword"
+#define SYSDB_CACHEDPWD_TYPE "cachedPasswordType"
+#define SYSDB_CACHEDPWD_FA2_LEN "cachedPasswordSecondFactorLen"
+
+#define SYSDB_UUID "uniqueID"
+#define SYSDB_SID "objectSID"
+#define SYSDB_PRIMARY_GROUP "ADPrimaryGroupID"
+#define SYSDB_PRIMARY_GROUP_GIDNUM "origPrimaryGroupGidNumber"
+#define SYSDB_SID_STR "objectSIDString"
+#define SYSDB_PAC_BLOB "pacBlob"
+#define SYSDB_PAC_BLOB_EXPIRE "pacBlobExpireTimestamp"
+#define SYSDB_UPN "userPrincipalName"
+#define SYSDB_CANONICAL_UPN "canonicalUserPrincipalName"
+#define SYSDB_CCACHE_FILE "ccacheFile"
+#define SYSDB_DN_FOR_MEMBER_HASH_TABLE "dnForMemberHashTable"
+
+#define SYSDB_ORIG_DN "originalDN"
+#define SYSDB_ORIG_OBJECTCLASS "originalObjectClass"
+#define SYSDB_ORIG_MODSTAMP "originalModifyTimestamp"
+#define SYSDB_ORIG_MEMBEROF "originalMemberOf"
+#define SYSDB_ORIG_MEMBER "orig_member"
+#define SYSDB_ORIG_MEMBER_USER "originalMemberUser"
+#define SYSDB_ORIG_MEMBER_HOST "originalMemberHost"
+
+#define SYSDB_USN "entryUSN"
+#define SYSDB_HIGH_USN "highestUSN"
+
+#define SYSDB_SSH_PUBKEY "sshPublicKey"
+
+#define SYSDB_SUBID_UID_COUND "subUidCount"
+#define SYSDB_SUBID_GID_COUNT "subGidCount"
+#define SYSDB_SUBID_UID_NUMBER "subUidNumber"
+#define SYSDB_SUBID_GID_NUMBER "subGidNumber"
+#define SYSDB_SUBID_OWNER "subidOwner"
+
+#define SYSDB_AUTH_TYPE "authType"
+#define SYSDB_USER_CERT "userCertificate"
+#define SYSDB_USER_MAPPED_CERT "userMappedCertificate"
+#define SYSDB_USER_EMAIL "mail"
+
+#define SYSDB_USER_PASSKEY "userPasskey"
+
+/* Local auth types */
+#define SYSDB_LOCAL_SMARTCARD_AUTH "localSmartcardAuth"
+#define SYSDB_LOCAL_PASSKEY_AUTH "localPasskeyAuth"
+
+#define SYSDB_SUBDOMAIN_REALM "realmName"
+#define SYSDB_SUBDOMAIN_FLAT "flatName"
+#define SYSDB_SUBDOMAIN_DNS "dnsName"
+#define SYSDB_SUBDOMAIN_ID "domainID"
+#define SYSDB_SUBDOMAIN_MPG "mpg"
+#define SYSDB_SUBDOMAIN_ENUM "enumerate"
+#define SYSDB_SUBDOMAIN_FOREST "memberOfForest"
+#define SYSDB_SUBDOMAIN_TRUST_DIRECTION "trustDirection"
+#define SYSDB_UPN_SUFFIXES "upnSuffixes"
+#define SYSDB_SITE "site"
+#define SYSDB_ENABLED "enabled"
+
+#define SYSDB_BASE_ID "baseID"
+#define SYSDB_ID_RANGE_SIZE "idRangeSize"
+#define SYSDB_BASE_RID "baseRID"
+#define SYSDB_SECONDARY_BASE_RID "secondaryBaseRID"
+#define SYSDB_DOMAIN_ID "domainID"
+#define SYSDB_ID_RANGE_TYPE "idRangeType"
+#define SYSDB_ID_RANGE_MPG "idRangeMPG"
+
+#define SYSDB_CERTMAP_PRIORITY "priority"
+#define SYSDB_CERTMAP_MATCHING_RULE "matchingRule"
+#define SYSDB_CERTMAP_MAPPING_RULE "mappingRule"
+#define SYSDB_CERTMAP_DOMAINS "domains"
+#define SYSDB_CERTMAP_USER_NAME_HINT "userNameHint"
+
+#define ORIGINALAD_PREFIX "originalAD"
+#define OVERRIDE_PREFIX "override"
+#define SYSDB_DEFAULT_OVERRIDE_NAME "defaultOverrideName"
+
+#define SYSDB_ORIG_AD_GID_NUMBER "originalADgidNumber"
+
+#define SYSDB_AD_ACCOUNT_EXPIRES "adAccountExpires"
+#define SYSDB_AD_USER_ACCOUNT_CONTROL "adUserAccountControl"
+
+#define SYSDB_DEFAULT_VIEW_NAME "default"
+#define SYSDB_LOCAL_VIEW_NAME "LOCAL" /* reserved for client-side overrides */
+#define SYSDB_VIEW_CLASS "view"
+#define SYSDB_VIEW_NAME "viewName"
+#define SYSDB_OVERRIDE_CLASS "override"
+#define SYSDB_OVERRIDE_ANCHOR_UUID "overrideAnchorUUID"
+#define SYSDB_OVERRIDE_USER_CLASS "userOverride"
+#define SYSDB_OVERRIDE_GROUP_CLASS "groupOverride"
+#define SYSDB_OVERRIDE_DN "overrideDN"
+#define SYSDB_OVERRIDE_OBJECT_DN "overrideObjectDN"
+#define SYSDB_USE_DOMAIN_RESOLUTION_ORDER "useDomainResolutionOrder"
+#define SYSDB_DOMAIN_RESOLUTION_ORDER "domainResolutionOrder"
+#define SYSDB_PASSKEY_USER_VERIFICATION "passkeyUserVerification"
+#define SYSDB_SESSION_RECORDING "sessionRecording"
+
+#define SYSDB_NEXTID_FILTER "("SYSDB_NEXTID"=*)"
+
+#define SYSDB_OBJECTCATEGORY "objectCategory"
+#define SYSDB_UC SYSDB_OBJECTCATEGORY"="SYSDB_USER_CLASS
+#define SYSDB_GC SYSDB_OBJECTCATEGORY"="SYSDB_GROUP_CLASS
+#define SYSDB_NC SYSDB_OBJECTCLASS"="SYSDB_NETGROUP_CLASS
+#define SYSDB_MPGC "|("SYSDB_UC")("SYSDB_GC")"
+
+#define SYSDB_PWNAM_FILTER "(&("SYSDB_UC")(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
+#define SYSDB_PWUID_FILTER "(&("SYSDB_UC")("SYSDB_UIDNUM"=%lu))"
+#define SYSDB_PWSID_FILTER "(&("SYSDB_UC")("SYSDB_SID_STR"=%s))"
+#define SYSDB_PWUPN_FILTER "(&("SYSDB_UC")(|("SYSDB_UPN"=%s)("SYSDB_CANONICAL_UPN"=%s)("SYSDB_USER_EMAIL"=%s)))"
+#define SYSDB_PWENT_FILTER "("SYSDB_UC")"
+
+#define SYSDB_GRNAM_FILTER "(&("SYSDB_GC")(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
+#define SYSDB_GRGID_FILTER "(&("SYSDB_GC")("SYSDB_GIDNUM"=%lu))"
+#define SYSDB_GRORIGGID_FILTER "(&("SYSDB_GC")("ORIGINALAD_PREFIX SYSDB_GIDNUM"=%lu))"
+#define SYSDB_GRSID_FILTER "(&("SYSDB_GC")("SYSDB_SID_STR"=%s))"
+#define SYSDB_GRENT_FILTER "("SYSDB_GC")"
+#define SYSDB_GRNAM_MPG_FILTER "(&("SYSDB_MPGC")(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
+#define SYSDB_GRGID_MPG_FILTER "(|(&("SYSDB_GC")("SYSDB_GIDNUM"=%lu))(&("SYSDB_UC")("SYSDB_GIDNUM"=%lu)("SYSDB_UIDNUM"=%lu)))"
+#define SYSDB_GRENT_MPG_FILTER "("SYSDB_MPGC")"
+
+#define SYSDB_INITGR_FILTER "(&("SYSDB_GC")("SYSDB_GIDNUM"=*))"
+
+#define SYSDB_NETGR_FILTER "(&("SYSDB_NC")(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
+#define SYSDB_NETGR_TRIPLES_FILTER "(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)("SYSDB_NAME_ALIAS"=%s)("SYSDB_MEMBEROF"=%s))"
+
+#define SYSDB_SID_FILTER "(&(|("SYSDB_UC")("SYSDB_GC"))("SYSDB_SID_STR"=%s))"
+#define SYSDB_UUID_FILTER "(&(|("SYSDB_UC")("SYSDB_GC"))("SYSDB_UUID"=%s))"
+#define SYSDB_NAME_FILTER "(&(|("SYSDB_UC")("SYSDB_GC"))(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
+#define SYSDB_ID_FILTER "(|(&("SYSDB_UC")("SYSDB_UIDNUM"=%u))(&("SYSDB_GC")("SYSDB_GIDNUM"=%u)))"
+#define SYSDB_USER_CERT_FILTER "(&("SYSDB_UC")%s)"
+
+#define SYSDB_HAS_ENUMERATED "has_enumerated"
+#define SYSDB_HAS_ENUMERATED_ID 0x00000001
+#define SYSDB_HAS_ENUMERATED_RESOLVER 0x00000002
+
+#define SYSDB_DEFAULT_ATTRS SYSDB_LAST_UPDATE, \
+ SYSDB_CACHE_EXPIRE, \
+ SYSDB_INITGR_EXPIRE, \
+ SYSDB_OBJECTCLASS, \
+ SYSDB_OBJECTCATEGORY
+
+#define SYSDB_PW_ATTRS {SYSDB_NAME, SYSDB_UIDNUM, \
+ SYSDB_GIDNUM, SYSDB_GECOS, \
+ SYSDB_HOMEDIR, SYSDB_SHELL, \
+ SYSDB_DEFAULT_ATTRS, \
+ SYSDB_PRIMARY_GROUP_GIDNUM, \
+ SYSDB_SID_STR, \
+ SYSDB_UPN, \
+ SYSDB_USER_CERT, \
+ SYSDB_USER_EMAIL, \
+ SYSDB_OVERRIDE_DN, \
+ SYSDB_OVERRIDE_OBJECT_DN, \
+ SYSDB_DEFAULT_OVERRIDE_NAME, \
+ SYSDB_SESSION_RECORDING, \
+ SYSDB_UUID, \
+ SYSDB_ORIG_DN, \
+ NULL}
+
+#define SYSDB_GRSRC_ATTRS {SYSDB_NAME, SYSDB_GIDNUM, \
+ SYSDB_MEMBERUID, \
+ SYSDB_MEMBER, \
+ SYSDB_GHOST, \
+ SYSDB_DEFAULT_ATTRS, \
+ SYSDB_SID_STR, \
+ SYSDB_OVERRIDE_DN, \
+ SYSDB_OVERRIDE_OBJECT_DN, \
+ SYSDB_DEFAULT_OVERRIDE_NAME, \
+ SYSDB_UUID, \
+ ORIGINALAD_PREFIX SYSDB_NAME, \
+ ORIGINALAD_PREFIX SYSDB_GIDNUM, \
+ NULL}
+
+#define SYSDB_NETGR_ATTRS {SYSDB_NAME, SYSDB_NETGROUP_TRIPLE, \
+ SYSDB_NETGROUP_MEMBER, \
+ SYSDB_DEFAULT_ATTRS, \
+ NULL}
+
+#define SYSDB_INITGR_ATTR SYSDB_MEMBEROF
+#define SYSDB_INITGR_ATTRS {SYSDB_GIDNUM, SYSDB_POSIX, \
+ SYSDB_DEFAULT_ATTRS, \
+ SYSDB_ORIG_DN, \
+ SYSDB_SID_STR, \
+ SYSDB_NAME, \
+ SYSDB_OVERRIDE_DN, \
+ 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_NETGROUP SYSDB_NAME"=%s,"SYSDB_TMPL_NETGROUP_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_TMPL_RANGE SYSDB_NAME"=%s,"SYSDB_TMPL_RANGE_BASE
+#define SYSDB_TMPL_OVERRIDE SYSDB_OVERRIDE_ANCHOR_UUID"=%s,"SYSDB_TMPL_VIEW_SEARCH_BASE
+#define SYSDB_TMPL_CERTMAP SYSDB_NAME"=%s,"SYSDB_TMPL_CERTMAP_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
+
+/* sysdb version check macros */
+#define SYSDB_VERSION_ERROR_HINT \
+ ERROR("Removing cache files in "DB_PATH" should fix the issue, " \
+ "but note that removing cache files will also remove all of your " \
+ "cached credentials.\n")
+
+#define SYSDB_VERSION_LOWER_ERROR(ret) do { \
+ if (ret == ERR_SYSDB_VERSION_TOO_NEW) { \
+ ERROR("Lower version of database is expected!\n"); \
+ SYSDB_VERSION_ERROR_HINT; \
+ } \
+} while(0)
+
+#define SYSDB_VERSION_HIGHER_ERROR(ret) do { \
+ if (ret == ERR_SYSDB_VERSION_TOO_OLD) { \
+ ERROR("Higher version of database is expected!\n"); \
+ ERROR("In order to upgrade the database, you must run SSSD.\n"); \
+ SYSDB_VERSION_ERROR_HINT; \
+ } \
+} while(0)
+
+/* use this in daemons */
+#define SYSDB_VERSION_ERROR_DAEMON(ret) \
+ SYSDB_VERSION_LOWER_ERROR(ret)
+
+/* use this in tools */
+#define SYSDB_VERSION_ERROR(ret) \
+ SYSDB_VERSION_LOWER_ERROR(ret); \
+ SYSDB_VERSION_HIGHER_ERROR(ret)
+
+struct confdb_ctx;
+struct sysdb_ctx;
+
+struct sysdb_attrs {
+ int num;
+ struct ldb_message_element *a;
+};
+
+/* sysdb_attrs helper functions */
+struct sysdb_attrs *sysdb_new_attrs(TALLOC_CTX *mem_ctx);
+
+struct range_info {
+ char *name;
+ uint32_t base_id;
+ uint32_t id_range_size;
+ uint32_t base_rid;
+ uint32_t secondary_base_rid;
+ char *trusted_dom_sid;
+ char *range_type;
+ enum sss_domain_mpg_mode mpg_mode;
+};
+
+struct certmap_info {
+ char *name;
+ uint32_t priority;
+ char *match_rule;
+ char *map_rule;
+ const char **domains;
+};
+
+enum sysdb_member_type {
+ SYSDB_MEMBER_USER,
+ SYSDB_MEMBER_GROUP,
+ SYSDB_MEMBER_NETGROUP,
+ SYSDB_MEMBER_SERVICE,
+ SYSDB_MEMBER_HOST,
+ SYSDB_MEMBER_IP_NETWORK,
+};
+
+enum sysdb_index_actions {
+ SYSDB_IDX_CREATE,
+ SYSDB_IDX_DELETE,
+ SYSDB_IDX_LIST
+};
+
+enum sysdb_obj_type {
+ SYSDB_UNKNOWN = 0,
+ SYSDB_USER,
+ SYSDB_GROUP
+};
+
+/* These attributes are stored in the timestamp cache */
+extern const char *sysdb_ts_cache_attrs[];
+
+/* values are copied in the structure, allocated on "attrs" */
+int sysdb_attrs_add_empty(struct sysdb_attrs *attrs, const char *name);
+int sysdb_attrs_add_val(struct sysdb_attrs *attrs,
+ const char *name, const struct ldb_val *val);
+int sysdb_attrs_add_val_safe(struct sysdb_attrs *attrs,
+ const char *name, const struct ldb_val *val);
+int sysdb_attrs_add_string_safe(struct sysdb_attrs *attrs,
+ const char *name, const char *str);
+int sysdb_attrs_add_string(struct sysdb_attrs *attrs,
+ const char *name, const char *str);
+int sysdb_attrs_add_lower_case_string(struct sysdb_attrs *attrs, bool safe,
+ const char *name, const char *str);
+int sysdb_attrs_add_mem(struct sysdb_attrs *attrs, const char *name,
+ const void *mem, size_t size);
+int sysdb_attrs_add_base64_blob(struct sysdb_attrs *attrs, const char *name,
+ const char *base64_str);
+int sysdb_attrs_add_bool(struct sysdb_attrs *attrs,
+ const char *name, bool value);
+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_add_lc_name_alias(struct sysdb_attrs *attrs,
+ const char *value);
+int sysdb_attrs_add_lc_name_alias_safe(struct sysdb_attrs *attrs,
+ const char *value);
+int sysdb_attrs_copy_values(struct sysdb_attrs *src,
+ struct sysdb_attrs *dst,
+ const char *name);
+errno_t sysdb_attrs_copy(struct sysdb_attrs *src, struct sysdb_attrs *dst);
+int sysdb_attrs_get_el(struct sysdb_attrs *attrs, const char *name,
+ struct ldb_message_element **el);
+int sysdb_attrs_get_el_ext(struct sysdb_attrs *attrs, const char *name,
+ bool alloc, 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);
+const char **sss_ldb_el_to_string_list(TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *el);
+int sysdb_attrs_get_string_array(struct sysdb_attrs *attrs, const char *name,
+ TALLOC_CTX *mem_ctx, const char ***string);
+errno_t sysdb_attrs_get_bool(struct sysdb_attrs *attrs, const char *name,
+ bool *value);
+int sysdb_attrs_get_uint16_t(struct sysdb_attrs *attrs, const char *name,
+ uint16_t *value);
+int sysdb_attrs_get_int32_t(struct sysdb_attrs *attrs, const char *name,
+ int32_t *value);
+int sysdb_attrs_get_uint32_t(struct sysdb_attrs *attrs, const char *name,
+ uint32_t *value);
+
+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 *const *list);
+errno_t sysdb_attrs_get_aliases(TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs *attrs,
+ const char *primary,
+ bool lowercase,
+ const char ***_aliases);
+errno_t sysdb_get_real_name(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name_or_upn,
+ const char **_cname);
+
+errno_t sysdb_msg2attrs(TALLOC_CTX *mem_ctx, size_t count,
+ struct ldb_message **msgs,
+ struct sysdb_attrs ***attrs);
+
+int sysdb_compare_usn(const char *a, const char *b);
+
+errno_t sysdb_get_highest_usn(TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs **attrs,
+ size_t num_attrs,
+ char **_usn);
+
+/* DNs related helper functions */
+errno_t sysdb_get_rdn(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx,
+ const char *dn, char **_name, char **_val);
+struct ldb_dn *sysdb_user_dn(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
+ const char *name);
+struct ldb_dn *sysdb_user_base_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom);
+struct ldb_dn *sysdb_group_dn(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
+ const char *name);
+struct ldb_dn *sysdb_group_base_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom);
+struct ldb_dn *sysdb_netgroup_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom, const char *name);
+struct ldb_dn *sysdb_netgroup_base_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom);
+errno_t sysdb_group_dn_name(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx,
+ const char *dn_str, char **name);
+struct ldb_dn *sysdb_domain_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom);
+struct ldb_dn *sysdb_base_dn(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx);
+struct ldb_dn *sysdb_custom_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ const char *object_name,
+ const char *subtree_name);
+struct ldb_dn *sysdb_custom_subtree_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ const char *subtree_name);
+
+char *sysdb_user_strdn(TALLOC_CTX *mem_ctx,
+ const char *domain, const char *name);
+char *sysdb_group_strdn(TALLOC_CTX *mem_ctx,
+ const char *domain, const char *name);
+
+
+struct ldb_context *sysdb_ctx_get_ldb(struct sysdb_ctx *sysdb);
+
+int compare_ldb_dn_comp_num(const void *m1, const void *m2);
+
+/* functions to start and finish transactions */
+int sysdb_transaction_start(struct sysdb_ctx *sysdb);
+int sysdb_transaction_commit(struct sysdb_ctx *sysdb);
+int sysdb_transaction_cancel(struct sysdb_ctx *sysdb);
+
+/* functions related to subdomains */
+errno_t sysdb_domain_create(struct sysdb_ctx *sysdb, const char *domain_name);
+
+errno_t sysdb_domain_get_domain_resolution_order(
+ TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ const char *domain_name,
+ const char **_domain_resolution_order);
+
+errno_t sysdb_domain_update_domain_resolution_order(
+ struct sysdb_ctx *sysdb,
+ const char *domain_name,
+ const char *domain_resolution_order);
+
+
+errno_t
+sysdb_get_site(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ const char **_site);
+
+errno_t
+sysdb_set_site(struct sss_domain_info *dom,
+ const char *site);
+
+errno_t
+sysdb_domain_set_enabled(struct sysdb_ctx *sysdb,
+ const char *name,
+ bool enabled);
+
+errno_t
+sysdb_list_subdomains(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ const char ***_names);
+
+errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
+ const char *name, const char *realm,
+ const char *flat_name, const char *dns_name,
+ const char *domain_id,
+ enum sss_domain_mpg_mode mpg_mode,
+ bool enumerate, const char *forest,
+ uint32_t trust_direction,
+ struct ldb_message_element *upn_suffixes);
+
+errno_t sysdb_update_subdomains(struct sss_domain_info *domain,
+ struct confdb_ctx *confdb);
+
+errno_t sysdb_master_domain_update(struct sss_domain_info *domain);
+
+errno_t sysdb_master_domain_add_info(struct sss_domain_info *domain,
+ const char *realm,
+ const char *flat,
+ const char *dns,
+ const char *id,
+ const char *forest,
+ struct ldb_message_element *alt_dom_suf);
+
+errno_t sysdb_subdomain_delete(struct sysdb_ctx *sysdb, const char *name);
+
+errno_t sysdb_subdomain_content_delete(struct sysdb_ctx *sysdb,
+ const char *name);
+
+errno_t
+sysdb_subdomain_get_id_by_name(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ const char *name,
+ const char **_id);
+
+/* The utility function to create a subdomain sss_domain_info object is handy
+ * for unit tests, so it should be available in a headerr.
+ */
+struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *parent,
+ const char *name,
+ const char *realm,
+ const char *flat_name,
+ const char *dns_name,
+ const char *id,
+ enum sss_domain_mpg_mode mpg_mode,
+ bool enumerate,
+ const char *forest,
+ const char **upn_suffixes,
+ uint32_t trust_direction,
+ struct confdb_ctx *confdb,
+ bool enabled);
+
+
+errno_t sysdb_get_ranges(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
+ size_t *range_count,
+ struct range_info ***range_list);
+errno_t sysdb_get_range(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ const char *forest,
+ struct range_info **_range);
+errno_t sysdb_range_create(struct sysdb_ctx *sysdb, struct range_info *range);
+errno_t sysdb_update_ranges(struct sysdb_ctx *sysdb,
+ struct range_info **ranges);
+
+errno_t sysdb_update_view_name(struct sysdb_ctx *sysdb, const char *view_name);
+
+errno_t sysdb_get_view_name(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
+ char **view_name);
+
+errno_t sysdb_update_view_domain_resolution_order(
+ struct sysdb_ctx *sysdb,
+ const char *domain_resolution_order);
+
+errno_t sysdb_get_view_domain_resolution_order(
+ TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ const char **_domain_resolution_order);
+
+static inline bool is_default_view(const char *view_name)
+{
+ /* NULL is treated as default */
+ if (view_name == NULL
+ || strcmp(view_name, SYSDB_DEFAULT_VIEW_NAME) == 0) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static inline bool is_local_view(const char *view_name)
+{
+ /* NULL is treated as default */
+ if (view_name != NULL
+ && strcmp(view_name, SYSDB_LOCAL_VIEW_NAME) == 0) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+errno_t sysdb_delete_view_tree(struct sysdb_ctx *sysdb, const char *view_name);
+
+errno_t sysdb_invalidate_overrides(struct sysdb_ctx *sysdb);
+
+errno_t sysdb_apply_default_override(struct sss_domain_info *domain,
+ struct sysdb_attrs *override_attrs,
+ struct ldb_dn *obj_dn);
+
+errno_t sysdb_search_by_orig_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ enum sysdb_member_type type,
+ const char *member_dn,
+ const char **attrs,
+ size_t *msgs_counts,
+ struct ldb_message ***msgs);
+
+#define sysdb_search_users_by_orig_dn(mem_ctx, domain, member_dn, attrs, msgs_counts, msgs) \
+ sysdb_search_by_orig_dn(mem_ctx, domain, SYSDB_MEMBER_USER, member_dn, attrs, msgs_counts, msgs);
+
+#define sysdb_search_groups_by_orig_dn(mem_ctx, domain, member_dn, attrs, msgs_counts, msgs) \
+ sysdb_search_by_orig_dn(mem_ctx, domain, SYSDB_MEMBER_GROUP, member_dn, attrs, msgs_counts, msgs);
+
+errno_t sysdb_search_user_override_attrs_by_name(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char **attrs,
+ struct ldb_result **override_obj,
+ struct ldb_result **orig_obj);
+
+errno_t sysdb_search_group_override_attrs_by_name(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char **attrs,
+ struct ldb_result **override_obj,
+ struct ldb_result **orig_obj);
+
+errno_t sysdb_search_user_override_by_name(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **override_obj,
+ struct ldb_result **orig_obj);
+
+errno_t sysdb_search_group_override_by_name(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **override_obj,
+ struct ldb_result **orig_obj);
+
+errno_t sysdb_search_user_override_by_uid(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ uid_t uid,
+ struct ldb_result **override_obj,
+ struct ldb_result **orig_obj);
+
+errno_t sysdb_search_group_override_by_gid(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ gid_t gid,
+ struct ldb_result **override_obj,
+ struct ldb_result **orig_obj);
+
+errno_t sysdb_search_override_by_cert(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *cert,
+ const char **attrs,
+ struct ldb_result **override_obj,
+ struct ldb_result **orig_obj);
+
+errno_t sysdb_add_overrides_to_object(struct sss_domain_info *domain,
+ struct ldb_message *obj,
+ struct ldb_message *override_obj,
+ const char **req_attrs);
+
+errno_t sysdb_add_group_member_overrides(struct sss_domain_info *domain,
+ struct ldb_message *obj,
+ bool expect_override_dn);
+
+errno_t sysdb_getpwnam_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **res);
+
+errno_t sysdb_getpwuid_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ uid_t uid,
+ struct ldb_result **res);
+
+int sysdb_getgrnam_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **res);
+
+int sysdb_getgrgid_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ gid_t gid,
+ struct ldb_result **res);
+
+struct ldb_message_element *
+sss_view_ldb_msg_find_element(struct sss_domain_info *dom,
+ const struct ldb_message *msg,
+ const char *attr_name);
+
+const char *sss_view_ldb_msg_find_attr_as_string_ex(struct sss_domain_info *dom,
+ const struct ldb_message *msg,
+ const char *attr_name,
+ const char *default_value,
+ bool *is_override);
+
+const char *sss_view_ldb_msg_find_attr_as_string(struct sss_domain_info *dom,
+ const struct ldb_message *msg,
+ const char *attr_name,
+ const char * default_value);
+
+uint64_t sss_view_ldb_msg_find_attr_as_uint64(struct sss_domain_info *dom,
+ const struct ldb_message *msg,
+ const char *attr_name,
+ uint64_t default_value);
+
+errno_t sysdb_update_certmap(struct sysdb_ctx *sysdb,
+ struct certmap_info **certmaps,
+ bool user_name_hint);
+
+errno_t sysdb_ldb_msg_attr_to_certmap_info(TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ const char **attr_map,
+ struct certmap_info **certmap);
+
+errno_t sysdb_get_certmap(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
+ struct certmap_info ***certmaps,
+ bool *user_name_hint);
+
+/* Sysdb initialization.
+ * call this function *only* once to initialize the database and get
+ * the sysdb ctx */
+int sysdb_init(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domains);
+
+/* Same as sysdb_init, but additionally allows to change
+ * file ownership of the sysdb databases and allow the
+ * upgrade via passing a context. */
+struct sysdb_upgrade_ctx {
+ struct confdb_ctx *cdb;
+};
+
+int sysdb_init_ext(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domains,
+ struct sysdb_upgrade_ctx *upgrade_ctx,
+ bool chown_dbfile,
+ uid_t uid, gid_t gid);
+
+/* 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 sss_domain_info *domain,
+ const char *db_path,
+ struct sysdb_ctx **_ctx);
+
+/* 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 sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **res);
+
+int sysdb_getpwuid(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ uid_t uid,
+ struct ldb_result **res);
+
+int sysdb_getpwupn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ bool domain_scope,
+ const char *upn,
+ struct ldb_result **res);
+
+int sysdb_enumpwent(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result **res);
+
+int sysdb_enumpwent_filter(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *attr,
+ const char *attr_filter,
+ const char *addtl_filter,
+ struct ldb_result **res);
+
+int sysdb_enumpwent_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result **res);
+
+int sysdb_enumpwent_filter_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *attr,
+ const char *attr_filter,
+ const char *addtl_filter,
+ struct ldb_result **res);
+
+int sysdb_getgrnam(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **res);
+
+int sysdb_getgrgid(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ gid_t gid,
+ struct ldb_result **res);
+
+int sysdb_getgrgid_attrs(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ gid_t gid,
+ const char **attrs,
+ struct ldb_result **res);
+
+int sysdb_enumgrent(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result **res);
+
+int sysdb_enumgrent_filter(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name_filter,
+ const char *addtl_filter,
+ struct ldb_result **res);
+
+int sysdb_enumgrent_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result **res);
+
+int sysdb_enumgrent_filter_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name_filter,
+ const char *addtl_filter,
+ struct ldb_result **res);
+
+struct sysdb_netgroup_ctx {
+ enum {SYSDB_NETGROUP_TRIPLE_VAL, SYSDB_NETGROUP_GROUP_VAL} type;
+ union {
+ struct {
+ char *hostname;
+ char *username;
+ char *domainname;
+ } triple;
+ char *groupname;
+ } value;
+};
+
+errno_t sysdb_getnetgr(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *netgroup,
+ struct ldb_result **res);
+
+int sysdb_initgroups(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **res);
+
+int sysdb_initgroups_by_upn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *upn,
+ struct ldb_result **res);
+
+int sysdb_initgroups_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **res);
+
+int sysdb_get_user_attr(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char **attributes,
+ struct ldb_result **res);
+
+int sysdb_get_user_attr_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char **attributes,
+ struct ldb_result **res);
+
+int sysdb_search_user_by_cert_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *cert,
+ struct ldb_result **res);
+
+int sysdb_get_netgroup_attr(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *netgrname,
+ const char **attributes,
+ struct ldb_result **res);
+
+/* functions that modify the database
+ * they have to be called within a transaction
+ * See sysdb_transaction_send()/_recv() */
+
+/* Permissive modify */
+int sss_ldb_modify_permissive(struct ldb_context *ldb,
+ struct ldb_message *msg);
+
+/* Delete Entry */
+int sysdb_delete_entry(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ bool ignore_not_found);
+
+int sysdb_delete_recursive(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ bool ignore_not_found);
+
+int sysdb_delete_recursive_with_filter(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ bool ignore_not_found,
+ const char *filter);
+
+/* Mark entry as expired */
+errno_t sysdb_mark_entry_as_expired_ldb_dn(struct sss_domain_info *dom,
+ struct ldb_dn *ldbdn);
+errno_t sysdb_mark_entry_as_expired_ldb_val(struct sss_domain_info *dom,
+ struct ldb_val *dn_val);
+
+/* Search Entry */
+int sysdb_search_entry(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct ldb_dn *base_dn,
+ enum ldb_scope scope,
+ const char *filter,
+ const char **attrs,
+ size_t *_msgs_count,
+ struct ldb_message ***_msgs);
+
+#define SSS_LDB_SEARCH(ret, ldb, mem_ctx, _result, base, scope, attrs, \
+ exp_fmt, ...) do { \
+ int _sls_lret; \
+ \
+ _sls_lret = ldb_search(ldb, mem_ctx, _result, base, scope, attrs, \
+ exp_fmt, ##__VA_ARGS__); \
+ ret = sysdb_error_to_errno(_sls_lret); \
+ if (ret == EOK && (*_result)->count == 0) { \
+ ret = ENOENT; \
+ } \
+} while(0)
+
+/* Search User (by uid, sid or name) */
+int sysdb_search_user_by_name(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char **attrs,
+ struct ldb_message **msg);
+
+int sysdb_search_user_by_uid(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ uid_t uid,
+ const char **attrs,
+ struct ldb_message **msg);
+
+int sysdb_search_user_by_sid_str(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sid_str,
+ const char **attrs,
+ struct ldb_message **msg);
+
+int sysdb_search_user_by_upn_res(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ bool domain_scope,
+ const char *upn,
+ const char **attrs,
+ struct ldb_result **out_res);
+
+int sysdb_search_user_by_upn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ bool domain_scope,
+ const char *sid_str,
+ const char **attrs,
+ struct ldb_message **msg);
+
+/* Search Group (by gid, sid or name) */
+int sysdb_search_group_by_name(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char **attrs,
+ struct ldb_message **msg);
+
+int sysdb_search_group_by_gid(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ gid_t gid,
+ const char **attrs,
+ struct ldb_message **msg);
+
+int sysdb_search_group_by_origgid(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ gid_t gid,
+ const char **attrs,
+ struct ldb_message **msg);
+
+int sysdb_search_group_by_sid_str(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sid_str,
+ const char **attrs,
+ struct ldb_message **msg);
+
+/* Search Netgroup (by name) */
+int sysdb_search_netgroup_by_name(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char **attrs,
+ struct ldb_message **msg);
+
+/* Replace entry attrs */
+int sysdb_set_entry_attr(struct sysdb_ctx *sysdb,
+ struct ldb_dn *entry_dn,
+ struct sysdb_attrs *attrs,
+ int mod_op);
+
+/* User/group invalidation of cache by direct writing to persistent cache
+ * WARNING: This function can cause performance issue!!
+ * is_user = true --> user invalidation
+ * is_user = false --> group invalidation
+ */
+int sysdb_invalidate_cache_entry(struct sss_domain_info *domain,
+ const char *name,
+ bool is_user);
+
+/* Replace user attrs */
+int sysdb_set_user_attr(struct sss_domain_info *domain,
+ const char *name,
+ struct sysdb_attrs *attrs,
+ int mod_op);
+
+errno_t sysdb_update_user_shadow_last_change(struct sss_domain_info *domain,
+ const char *name,
+ const char *attrname);
+
+/* Replace group attrs */
+int sysdb_set_group_attr(struct sss_domain_info *domain,
+ const char *name,
+ struct sysdb_attrs *attrs,
+ int mod_op);
+
+/* Replace netgroup attrs */
+int sysdb_set_netgroup_attr(struct sss_domain_info *domain,
+ const char *name,
+ struct sysdb_attrs *attrs,
+ int mod_op);
+
+/* Add user (only basic attrs and w/o checks) */
+int sysdb_add_basic_user(struct sss_domain_info *domain,
+ const char *name,
+ uid_t uid, gid_t gid,
+ const char *gecos,
+ const char *homedir,
+ const char *shell);
+
+/* Add user (all checks) */
+int sysdb_add_user(struct sss_domain_info *domain,
+ const char *name,
+ uid_t uid, gid_t gid,
+ const char *gecos,
+ const char *homedir,
+ const char *shell,
+ const char *orig_dn,
+ struct sysdb_attrs *attrs,
+ int cache_timeout,
+ time_t now);
+
+/* Add group (only basic attrs and w/o checks) */
+int sysdb_add_basic_group(struct sss_domain_info *domain,
+ const char *name, gid_t gid);
+
+/* Add group (all checks) */
+int sysdb_add_group(struct sss_domain_info *domain,
+ const char *name, gid_t gid,
+ struct sysdb_attrs *attrs,
+ int cache_timeout,
+ time_t now);
+
+int sysdb_add_incomplete_group(struct sss_domain_info *domain,
+ const char *name,
+ gid_t gid,
+ const char *original_dn,
+ const char *sid_str,
+ const char *uuid,
+ bool posix,
+ time_t now);
+
+/* Add netgroup (only basic attrs and w/o checks) */
+int sysdb_add_basic_netgroup(struct sss_domain_info *domain,
+ const char *name, const char *description);
+
+int sysdb_add_netgroup(struct sss_domain_info *domain,
+ const char *name,
+ const char *description,
+ struct sysdb_attrs *attrs,
+ char **missing,
+ int cache_timeout,
+ time_t now);
+
+/* mod_op must be either LDB_FLAG_MOD_ADD or LDB_FLAG_MOD_DELETE */
+int sysdb_mod_group_member(struct sss_domain_info *domain,
+ struct ldb_dn *member_dn,
+ struct ldb_dn *group_dn,
+ int mod_op);
+
+int sysdb_store_user(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,
+ const char *orig_dn,
+ struct sysdb_attrs *attrs,
+ char **remove_attrs,
+ uint64_t cache_timeout,
+ time_t now);
+
+int sysdb_store_group(struct sss_domain_info *domain,
+ const char *name,
+ gid_t gid,
+ struct sysdb_attrs *attrs,
+ uint64_t cache_timeout,
+ time_t now);
+
+int sysdb_add_group_member(struct sss_domain_info *domain,
+ const char *group,
+ const char *member,
+ enum sysdb_member_type type,
+ bool is_dn);
+
+int sysdb_remove_group_member(struct sss_domain_info *domain,
+ const char *group,
+ const char *member,
+ enum sysdb_member_type type,
+ bool is_dn);
+
+errno_t sysdb_update_members(struct sss_domain_info *domain,
+ const char *member,
+ enum sysdb_member_type type,
+ const char *const *add_groups,
+ const char *const *del_groups);
+
+errno_t sysdb_update_members_dn(struct sss_domain_info *member_domain,
+ const char *member,
+ enum sysdb_member_type type,
+ const char *const *add_groups,
+ const char *const *del_groups);
+
+errno_t sysdb_store_override(struct sss_domain_info *domain,
+ const char *view_name,
+ enum sysdb_member_type type,
+ struct sysdb_attrs *attrs, struct ldb_dn *obj_dn);
+
+/*
+ * Cache the time of last initgroups invocation. Typically this is not done when
+ * the provider-specific request itself finishes, because currently the request
+ * might hand over to other requests from a different provider (e.g. an AD user
+ * from a trusted domain might need to also call an IPA request to fetch the
+ * external groups). Instead, the caller of the initgroups request, typically
+ * the DP or the periodical refresh task sets the timestamp.
+ */
+errno_t sysdb_set_initgr_expire_timestamp(struct sss_domain_info *domain,
+ const char *name_or_upn_or_sid);
+
+/* 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 */
+int sysdb_cache_password(struct sss_domain_info *domain,
+ const char *username,
+ const char *password);
+
+int sysdb_cache_password_ex(struct sss_domain_info *domain,
+ const char *username,
+ const char *password,
+ enum sss_authtok_type authtok_type,
+ size_t second_factor_size);
+
+errno_t check_failed_login_attempts(struct confdb_ctx *cdb,
+ struct ldb_message *ldb_msg,
+ uint32_t *failed_login_attempts,
+ time_t *delayed_until);
+int sysdb_cache_auth(struct sss_domain_info *domain,
+ const char *name,
+ const char *password,
+ struct confdb_ctx *cdb,
+ bool just_check,
+ time_t *_expire_date,
+ time_t *_delayed_until);
+
+int sysdb_store_custom(struct sss_domain_info *domain,
+ const char *object_name,
+ const char *subtree_name,
+ struct sysdb_attrs *attrs);
+
+int sysdb_search_custom(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *filter,
+ const char *subtree_name,
+ const char **attrs,
+ size_t *msgs_count,
+ struct ldb_message ***msgs);
+
+int sysdb_search_custom_by_name(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *object_name,
+ const char *subtree_name,
+ const char **attrs,
+ size_t *_count,
+ struct ldb_message ***_msgs);
+
+int sysdb_delete_custom(struct sss_domain_info *domain,
+ const char *object_name,
+ const char *subtree_name);
+
+int sysdb_asq_search(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_dn *base_dn,
+ const char *expression,
+ const char *asq_attribute,
+ const char **attrs,
+ size_t *msgs_count,
+ struct ldb_message ***msgs);
+
+int sysdb_search_users(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter,
+ const char **attrs,
+ size_t *msgs_count,
+ struct ldb_message ***msgs);
+
+enum sysdb_cache_type {
+ SYSDB_CACHE_TYPE_NONE,
+ SYSDB_CACHE_TYPE_TIMESTAMP,
+ SYSDB_CACHE_TYPE_PERSISTENT
+};
+
+errno_t sysdb_search_with_ts_attr(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_dn *base_dn,
+ enum ldb_scope scope,
+ enum sysdb_cache_type search_cache,
+ const char *filter,
+ const char *attrs[],
+ struct ldb_result **_result);
+
+int sysdb_search_users_by_timestamp(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter,
+ const char *ts_sub_filter,
+ const char **attrs,
+ size_t *_msgs_count,
+ struct ldb_message ***_msgs);
+
+int sysdb_delete_user(struct sss_domain_info *domain,
+ const char *name, uid_t uid);
+
+int sysdb_search_groups(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter,
+ const char **attrs,
+ size_t *msgs_count,
+ struct ldb_message ***msgs);
+
+int sysdb_search_groups_by_timestamp(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter,
+ const char *ts_sub_filter,
+ const char **attrs,
+ size_t *_msgs_count,
+ struct ldb_message ***_msgs);
+
+int sysdb_delete_group(struct sss_domain_info *domain,
+ const char *name, gid_t gid);
+
+int sysdb_search_netgroups(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter,
+ const char **attrs,
+ size_t *msgs_count,
+ struct ldb_message ***msgs);
+
+int sysdb_delete_netgroup(struct sss_domain_info *domain,
+ const char *name);
+
+int sysdb_delete_by_sid(struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ const char *sid_str);
+
+errno_t sysdb_attrs_to_list(TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs **attrs,
+ int attr_count,
+ const char *attr_name,
+ char ***_list);
+
+errno_t sysdb_netgr_to_entries(TALLOC_CTX *mem_ctx,
+ struct ldb_result *res,
+ struct sysdb_netgroup_ctx ***entries,
+ size_t *netgroup_count);
+
+errno_t sysdb_dn_sanitize(TALLOC_CTX *mem_ctx, const char *input,
+ char **sanitized);
+
+errno_t sysdb_get_bool(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char *attr_name,
+ bool *value);
+
+errno_t sysdb_set_bool(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char *cn_value,
+ const char *attr_name,
+ bool value);
+
+errno_t sysdb_get_uint(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char *attr_name,
+ uint32_t *value);
+
+errno_t sysdb_set_uint(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char *cn_value,
+ const char *attr_name,
+ uint32_t value);
+
+errno_t sysdb_has_enumerated(struct sss_domain_info *domain,
+ uint32_t provider,
+ bool *has_enumerated);
+
+errno_t sysdb_set_enumerated(struct sss_domain_info *domain,
+ uint32_t provider,
+ bool has_enumerated);
+
+errno_t sysdb_remove_attrs(struct sss_domain_info *domain,
+ const char *name,
+ enum sysdb_member_type type,
+ char **remove_attrs);
+
+/**
+ * @brief Return direct parents of an object in the cache
+ *
+ * @param[in] mem_ctx Memory context the result should be allocated
+ * on
+ * @param[in] dom domain the object is in
+ * @param[in] parent_dom domain which should be searched for direct
+ * parents if NULL all domains in the given cache
+ * are searched
+ * @param[in] mtype Type of the object, SYSDB_MEMBER_USER or
+ * SYSDB_MEMBER_GROUP
+ * @param[in] name Name of the object
+ * @param[out] _direct_parents List of names of the direct parent groups
+ *
+ *
+ * @return
+ * - EOK: success
+ * - EINVAL: wrong mtype
+ * - ENOMEM: Memory allocation failed
+ */
+errno_t sysdb_get_direct_parents(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ struct sss_domain_info *parent_dom,
+ enum sysdb_member_type mtype,
+ const char *name,
+ char ***_direct_parents);
+
+/* === Functions related to ID-mapping === */
+
+#define SYSDB_IDMAP_CONTAINER "cn=id_mappings"
+
+#define SYSDB_IDMAP_SUBTREE "idmap"
+#define SYSDB_IDMAP_MAPPING_OC "id_mapping"
+#define SYSDB_IDMAP_FILTER "(objectClass="SYSDB_IDMAP_MAPPING_OC")"
+#define SYSDB_IDMAP_SID_ATTR "objectSID"
+#define SYSDB_IDMAP_SLICE_ATTR "slice"
+
+#define SYSDB_IDMAP_ATTRS { \
+ SYSDB_NAME, \
+ SYSDB_IDMAP_SID_ATTR, \
+ SYSDB_IDMAP_SLICE_ATTR, \
+ NULL }
+
+#define SYSDB_TMPL_IDMAP_BASE SYSDB_IDMAP_CONTAINER",cn=%s,"SYSDB_BASE
+#define SYSDB_TMPL_IDMAP SYSDB_IDMAP_SID_ATTR"=%s,"SYSDB_TMPL_IDMAP_BASE
+
+errno_t sysdb_idmap_store_mapping(struct sss_domain_info *domain,
+ const char *dom_name,
+ const char *dom_sid,
+ id_t slice_num);
+
+errno_t sysdb_idmap_get_mappings(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result);
+
+errno_t sysdb_search_object_by_id(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ uint32_t id,
+ const char **attrs,
+ struct ldb_result **res);
+
+errno_t sysdb_search_object_by_name(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char **attrs,
+ struct ldb_result **res);
+
+errno_t sysdb_search_object_by_sid(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sid_str,
+ const char **attrs,
+ struct ldb_result **res);
+
+errno_t sysdb_search_object_by_uuid(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *uuid_str,
+ const char **attrs,
+ struct ldb_result **res);
+
+errno_t sysdb_search_object_by_cert(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *cert,
+ const char **attrs,
+ struct ldb_result **res);
+
+errno_t sysdb_search_user_by_cert(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *cert,
+ struct ldb_result **res);
+
+errno_t sysdb_remove_cert(struct sss_domain_info *domain,
+ const char *cert);
+
+errno_t sysdb_remove_mapped_data(struct sss_domain_info *domain,
+ struct sysdb_attrs *mapped_attr);
+
+/* === Functions related to GPOs === */
+
+#define SYSDB_GPO_CONTAINER "cn=gpos,cn=ad,cn=custom"
+
+/* === Functions related to GPO entries === */
+
+#define SYSDB_GPO_OC "gpo"
+#define SYSDB_GPO_FILTER "(objectClass="SYSDB_GPO_OC")"
+#define SYSDB_GPO_GUID_FILTER "(&(objectClass="SYSDB_GPO_OC")("SYSDB_GPO_GUID_ATTR"=%s))"
+#define SYSDB_GPO_GUID_ATTR "gpoGUID"
+#define SYSDB_GPO_VERSION_ATTR "gpoVersion"
+#define SYSDB_GPO_TIMEOUT_ATTR "gpoPolicyFileTimeout"
+
+#define SYSDB_TMPL_GPO_BASE SYSDB_GPO_CONTAINER","SYSDB_DOM_BASE
+#define SYSDB_TMPL_GPO SYSDB_GPO_GUID_ATTR"=%s,"SYSDB_TMPL_GPO_BASE
+
+#define SYSDB_GPO_ATTRS { \
+ SYSDB_NAME, \
+ SYSDB_GPO_GUID_ATTR, \
+ SYSDB_GPO_VERSION_ATTR, \
+ SYSDB_GPO_TIMEOUT_ATTR, \
+ NULL }
+
+errno_t sysdb_gpo_store_gpo(struct sss_domain_info *domain,
+ const char *gpo_guid,
+ int gpo_version,
+ int cache_timeout,
+ time_t now);
+
+errno_t sysdb_gpo_get_gpo_by_guid(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *gpo_guid,
+ struct ldb_result **_result);
+
+errno_t sysdb_gpo_get_gpos(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result);
+
+/* === Functions related to GPO Result object === */
+
+#define SYSDB_GPO_RESULT_OC "gpo_result"
+#define SYSDB_GPO_RESULT_FILTER "(objectClass="SYSDB_GPO_RESULT_OC")"
+
+#define SYSDB_TMPL_GPO_RESULT_BASE SYSDB_GPO_CONTAINER","SYSDB_DOM_BASE
+#define SYSDB_TMPL_GPO_RESULT "cn=%s,"SYSDB_TMPL_GPO_RESULT_BASE
+
+errno_t sysdb_gpo_delete_gpo_result_object(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain);
+
+errno_t sysdb_gpo_store_gpo_result_setting(struct sss_domain_info *domain,
+ const char *policy_setting_key,
+ const char *policy_setting_value);
+
+errno_t sysdb_gpo_get_gpo_result_setting(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *policy_setting_key,
+ const char **policy_setting_value);
+
+errno_t sysdb_get_sids_of_members(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ const char *group_name,
+ const char ***_sids,
+ const char ***_dns,
+ size_t *_n);
+
+errno_t sysdb_get_user_members_recursively(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ struct ldb_dn *group_dn,
+ struct ldb_result **members);
+
+errno_t sysdb_handle_original_uuid(const char *orig_name,
+ struct sysdb_attrs *src_attrs,
+ const char *src_name,
+ struct sysdb_attrs *dest_attrs,
+ const char *dest_name);
+
+errno_t sysdb_cert_derb64_to_ldap_filter(TALLOC_CTX *mem_ctx,
+ const char *derb64,
+ const char *attr_name,
+ char **ldap_filter);
+
+/* define old name for backward compatibility */
+#define sysdb_error_to_errno(ldberr) sss_ldb_error_to_errno(ldberr)
+
+void ldb_debug_messages(void *context, enum ldb_debug_level level,
+ const char *fmt, va_list ap);
+
+/* Try to detect the object domain from the object's SYSDB_NAME attribute and
+ * return the matching sss_domain_info. This should work reliable with user
+ * and group objects since fully-qualified names are used here. If the proper
+ * domain cannot be detected the given domain is returned. */
+struct sss_domain_info *find_domain_by_msg(struct sss_domain_info *dom,
+ struct ldb_message *msg);
+
+#endif /* __SYS_DB_H__ */
diff --git a/src/db/sysdb_autofs.c b/src/db/sysdb_autofs.c
new file mode 100644
index 0000000..1febdae
--- /dev/null
+++ b/src/db/sysdb_autofs.c
@@ -0,0 +1,787 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) 2012 Red Hat
+
+ 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 <talloc.h>
+
+#include "db/sysdb.h"
+#include "db/sysdb_private.h"
+#include "db/sysdb_autofs.h"
+
+#define SYSDB_TMPL_AUTOFS_ENTRY SYSDB_NAME"=%s,"SYSDB_TMPL_CUSTOM
+
+static struct ldb_dn *
+sysdb_autofsmap_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *map_name)
+{
+ return sysdb_custom_dn(mem_ctx, domain, map_name, AUTOFS_MAP_SUBDIR);
+}
+
+static struct ldb_dn *
+sysdb_autofsentry_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *map_name,
+ const char *entry_name,
+ const char *entry_value)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ char *clean_name;
+ char *clean_value;
+ const char *rdn;
+ struct ldb_dn *dn = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return NULL;
+ }
+
+ ret = sysdb_dn_sanitize(tmp_ctx, entry_name, &clean_name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_dn_sanitize(tmp_ctx, entry_value, &clean_value);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ rdn = talloc_asprintf(tmp_ctx, "%s%s", clean_name, clean_value);
+ if (!rdn) {
+ goto done;
+ }
+
+ dn = ldb_dn_new_fmt(mem_ctx, domain->sysdb->ldb, SYSDB_TMPL_AUTOFS_ENTRY,
+ rdn, map_name, AUTOFS_MAP_SUBDIR, domain->name);
+
+done:
+ talloc_free(tmp_ctx);
+ return dn;
+}
+
+char *
+sysdb_autofsentry_strdn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *map_name,
+ const char *entry_name,
+ const char *entry_value)
+{
+ struct ldb_dn *dn;
+ char *strdn;
+
+ dn = sysdb_autofsentry_dn(mem_ctx, domain,
+ map_name, entry_name, entry_value);
+ if (!dn) return NULL;
+
+ strdn = talloc_strdup(mem_ctx, ldb_dn_get_linearized(dn));
+ talloc_free(dn);
+ return strdn;
+}
+
+errno_t
+sysdb_save_autofsmap(struct sss_domain_info *domain,
+ const char *name,
+ const char *autofsmapname,
+ const char *origdn,
+ struct sysdb_attrs *attrs,
+ int cache_timeout,
+ time_t now,
+ bool enumerated)
+{
+ time_t expiration = cache_timeout ? now + cache_timeout : 0;
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Adding autofs map %s\n", autofsmapname);
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ if (!attrs) {
+ attrs = sysdb_new_attrs(tmp_ctx);
+ if (!attrs) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = sysdb_attrs_add_string(attrs, SYSDB_OBJECTCLASS,
+ SYSDB_AUTOFS_MAP_OC);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set map object class [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_string(attrs, SYSDB_AUTOFS_MAP_NAME, autofsmapname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set map name [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_string(attrs, SYSDB_ORIG_DN, origdn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set origdn [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_string(attrs, SYSDB_NAME, name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set name attribute [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_UPDATE, now);
+ if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set sysdb lastUpdate [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE, expiration);
+ if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set sysdb cache expire [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ if (enumerated) {
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_ENUM_EXPIRE, expiration);
+ if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set sysdb enum expire [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+ }
+
+ ret = sysdb_store_custom(domain, name, AUTOFS_MAP_SUBDIR, attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_store_custom failed [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_delete_autofsmap(struct sss_domain_info *domain,
+ const char *name)
+{
+ DEBUG(SSSDBG_TRACE_FUNC, "Deleting autofs map %s\n", name);
+ return sysdb_delete_custom(domain, name, AUTOFS_MAP_SUBDIR);
+}
+
+errno_t
+sysdb_get_map_byname(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *map_name,
+ struct ldb_message **_map)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ const char *filter;
+ char *safe_map_name;
+ size_t count;
+ struct ldb_message **msgs;
+ const char *attrs[] = { SYSDB_OBJECTCLASS,
+ SYSDB_ORIG_DN,
+ SYSDB_CACHE_EXPIRE,
+ SYSDB_LAST_UPDATE,
+ SYSDB_AUTOFS_MAP_NAME,
+ SYSDB_MEMBER,
+ NULL };
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ ret = sss_filter_sanitize(tmp_ctx, map_name, &safe_map_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot sanitize map [%s] error [%d]: %s\n",
+ map_name, ret, strerror(ret));
+ goto done;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, "(&(objectclass=%s)(%s=%s))",
+ SYSDB_AUTOFS_MAP_OC, SYSDB_NAME, safe_map_name);
+ if (!filter) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_custom(tmp_ctx, domain, filter,
+ AUTOFS_MAP_SUBDIR, attrs,
+ &count, &msgs);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Error looking up autofs map [%s]\n", safe_map_name);
+ goto done;
+ } else if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No such map [%s]\n", safe_map_name);
+ *_map = NULL;
+ goto done;
+ }
+
+ if (count != 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "More than one map named [%s]\n", safe_map_name);
+ goto done;
+ }
+
+ *_map = talloc_steal(mem_ctx, msgs[0]);
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_save_autofsentry(struct sss_domain_info *domain,
+ const char *map,
+ const char *key,
+ const char *value,
+ struct sysdb_attrs *attrs,
+ int cache_timeout,
+ time_t now)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ struct ldb_dn *dn;
+ const char *name;
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Adding autofs entry [%s] - [%s]\n", key, value);
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ if (!attrs) {
+ attrs = sysdb_new_attrs(tmp_ctx);
+ if (!attrs) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = sysdb_attrs_add_string(attrs, SYSDB_OBJECTCLASS,
+ SYSDB_AUTOFS_ENTRY_OC);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set entry object class [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_string(attrs, SYSDB_AUTOFS_ENTRY_KEY, key);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set entry key [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_string(attrs, SYSDB_AUTOFS_ENTRY_VALUE, value);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set entry key [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ name = talloc_asprintf(tmp_ctx, "%s%s", key, value);
+ if (!name) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_string(attrs, SYSDB_NAME, name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set name attribute [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE,
+ ((cache_timeout) ?
+ (now + cache_timeout) : 0));
+ if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set sysdb cache expire [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ dn = sysdb_autofsentry_dn(tmp_ctx, domain, map, key, value);
+ if (!dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = dn;
+ msg->elements = attrs->a;
+ msg->num_elements = attrs->num;
+
+ ret = ldb_add(domain->sysdb->ldb, msg);
+ ret = sysdb_error_to_errno(ret);
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_get_autofsentry(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *map_name,
+ const char *entry_name,
+ struct ldb_message **_entry)
+{
+ TALLOC_CTX *tmp_ctx;
+ char *safe_entryname;
+ char *filter;
+ struct ldb_dn *mapdn;
+ size_t count;
+ struct ldb_message **msgs;
+ errno_t ret;
+ const char *attrs[] = { SYSDB_AUTOFS_ENTRY_KEY,
+ SYSDB_AUTOFS_ENTRY_VALUE,
+ SYSDB_CACHE_EXPIRE,
+ NULL };
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ ret = sss_filter_sanitize(tmp_ctx, entry_name, &safe_entryname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot sanitize map [%s] error [%d]: %s\n",
+ map_name, ret, strerror(ret));
+ goto done;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, "(&(objectclass=%s)(%s=%s))",
+ SYSDB_AUTOFS_ENTRY_OC, SYSDB_AUTOFS_ENTRY_KEY,
+ safe_entryname);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ mapdn = sysdb_autofsmap_dn(tmp_ctx, domain, map_name);
+ if (!mapdn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_entry(tmp_ctx, domain->sysdb, mapdn, LDB_SCOPE_SUBTREE,
+ filter, attrs, &count, &msgs);
+ if (ret == ENOENT) {
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb search failed: %d\n", ret);
+ goto done;
+ }
+
+ if (count != 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "More than one entry %s:%s found\n",
+ map_name, entry_name);
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ *_entry = talloc_steal(mem_ctx, msgs[0]);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+sysdb_del_autofsentry(struct sss_domain_info *domain,
+ const char *entry_dn)
+{
+ struct ldb_dn *dn;
+ errno_t ret;
+
+ dn = ldb_dn_new(NULL, sysdb_ctx_get_ldb(domain->sysdb), entry_dn);
+ if (!dn) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_delete_entry(domain->sysdb, dn, true);
+ talloc_free(dn);
+ return ret;
+}
+
+errno_t
+sysdb_del_autofsentry_by_key(struct sss_domain_info *domain,
+ const char *map_name,
+ const char *entry_key)
+{
+ struct ldb_message *entry;
+ errno_t ret;
+
+ ret = sysdb_get_autofsentry(NULL, domain, map_name, entry_key, &entry);
+ if (ret == ENOENT) {
+ return EOK;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to get autofs entry [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ ret = sysdb_delete_entry(domain->sysdb, entry->dn, true);
+ talloc_free(entry);
+ return ret;
+}
+
+errno_t
+sysdb_autofs_entries_by_map(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *mapname,
+ size_t *_count,
+ struct ldb_message ***_entries)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ char *filter;
+ const char *attrs[] = { SYSDB_AUTOFS_ENTRY_KEY,
+ SYSDB_AUTOFS_ENTRY_VALUE,
+ NULL };
+ size_t count;
+ struct ldb_message **msgs;
+ struct ldb_dn *mapdn;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Getting entries for map %s\n", mapname);
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ mapdn = sysdb_autofsmap_dn(tmp_ctx, domain, mapname);
+ if (!mapdn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, "(objectclass=%s)",
+ SYSDB_AUTOFS_ENTRY_OC);
+ if (!filter) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_entry(tmp_ctx, domain->sysdb, mapdn, LDB_SCOPE_SUBTREE,
+ filter, attrs, &count, &msgs);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb search failed: %d\n", ret);
+ goto done;
+ } else if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No entries for the map\n");
+ *_count = 0;
+ *_entries = NULL;
+ goto done;
+ }
+
+ *_count = count;
+ *_entries = talloc_steal(mem_ctx, msgs);
+ ret = EOK;
+ DEBUG(SSSDBG_TRACE_INTERNAL, "found %zu entries for map %s\n",
+ count, mapname);
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_set_autofsentry_attr(struct sss_domain_info *domain,
+ const char *mapname,
+ const char *key,
+ const char *value,
+ struct sysdb_attrs *attrs,
+ int mod_op)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *dn;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ dn = sysdb_autofsentry_dn(tmp_ctx, domain, mapname, key, value);
+ if (dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_set_entry_attr(domain->sysdb, dn, attrs, mod_op);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_set_autofsmap_attr(struct sss_domain_info *domain,
+ const char *name,
+ struct sysdb_attrs *attrs,
+ int mod_op)
+{
+ errno_t ret;
+ struct ldb_dn *dn;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ dn = sysdb_autofsmap_dn(tmp_ctx, domain, name);
+ if (!dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_set_entry_attr(domain->sysdb, dn, attrs, mod_op);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_invalidate_autofs_entries(struct sss_domain_info *domain,
+ const char *mapname)
+{
+ TALLOC_CTX *tmp_ctx;
+ bool in_transaction = false;
+ struct ldb_message **entries;
+ struct sysdb_attrs *attrs;
+ const char *value;
+ const char *key;
+ size_t count;
+ errno_t ret;
+ size_t i;
+ int sret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ ret = sysdb_autofs_entries_by_map(tmp_ctx, domain, mapname,
+ &count, &entries);
+ if (ret == ENOENT) {
+ ret = EOK;
+ goto done;
+ } else if (ret != EOK) {
+ goto done;
+ }
+
+ attrs = sysdb_new_attrs(tmp_ctx);
+ if (attrs == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE, 1);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_transaction_start(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+ goto done;
+ }
+ in_transaction = true;
+
+ for (i = 0; i < count; i++) {
+ key = ldb_msg_find_attr_as_string(entries[i], SYSDB_AUTOFS_ENTRY_KEY,
+ NULL);
+ if (key == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "An entry with no key?\n");
+ continue;
+ }
+
+ value = ldb_msg_find_attr_as_string(entries[i],
+ SYSDB_AUTOFS_ENTRY_VALUE,
+ NULL);
+ if (value == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "An entry with no value?\n");
+ continue;
+ }
+
+ ret = sysdb_set_autofsentry_attr(domain, mapname, key, value,
+ attrs, SYSDB_MOD_REP);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Could not expire entry %s\n", key);
+ continue;
+ }
+ }
+
+ ret = sysdb_transaction_commit(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not commit transaction\n");
+ goto done;
+ }
+ in_transaction = false;
+
+ ret = EOK;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(domain->sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not cancel transaction\n");
+ }
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_invalidate_autofs_maps(struct sss_domain_info *domain)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ const char *filter;
+ struct sysdb_attrs *sys_attrs = NULL;
+ const char *attrs[] = { SYSDB_OBJECTCLASS,
+ SYSDB_NAME,
+ SYSDB_CACHE_EXPIRE,
+ NULL };
+ size_t count;
+ struct ldb_message **msgs;
+ const char *name;
+ bool in_transaction = false;
+ int sret;
+ int i;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ filter = talloc_asprintf(tmp_ctx, "(&(objectclass=%s)(%s=*))",
+ SYSDB_AUTOFS_MAP_OC, SYSDB_NAME);
+ if (!filter) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_custom(tmp_ctx, domain, filter,
+ AUTOFS_MAP_SUBDIR, attrs,
+ &count, &msgs);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Error looking up autofs maps\n");
+ goto done;
+ } else if (ret == ENOENT) {
+ ret = EOK;
+ goto done;
+ }
+
+ sys_attrs = sysdb_new_attrs(tmp_ctx);
+ if (!sys_attrs) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_time_t(sys_attrs, SYSDB_CACHE_EXPIRE, 1);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_time_t(sys_attrs, SYSDB_ENUM_EXPIRE, 1);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_transaction_start(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+ goto done;
+ }
+ in_transaction = true;
+
+ for (i = 0; i < count; i++) {
+ name = ldb_msg_find_attr_as_string(msgs[i], SYSDB_NAME, NULL);
+ if (!name) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "A map with no name?\n");
+ continue;
+ }
+
+ ret = sysdb_set_autofsmap_attr(domain, name,
+ sys_attrs, SYSDB_MOD_REP);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Could not expire map %s\n", name);
+ continue;
+ }
+
+ ret = sysdb_invalidate_autofs_entries(domain, name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Could not expire map entries %s\n",
+ name);
+ continue;
+ }
+ }
+
+ ret = sysdb_transaction_commit(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not commit transaction\n");
+ goto done;
+ }
+ in_transaction = false;
+
+ ret = EOK;
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(domain->sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not cancel transaction\n");
+ }
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
diff --git a/src/db/sysdb_autofs.h b/src/db/sysdb_autofs.h
new file mode 100644
index 0000000..37489f2
--- /dev/null
+++ b/src/db/sysdb_autofs.h
@@ -0,0 +1,110 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) 2012 Red Hat
+
+ 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 _SYSDB_AUTOFS_H_
+#define _SYSDB_AUTOFS_H_
+
+#include "db/sysdb.h"
+
+/* subdirs in cn=custom in sysdb. We don't store autofs stuff in sysdb directly
+ * b/c it's not name-service-switch data */
+#define AUTOFS_MAP_SUBDIR "autofsmaps"
+#define AUTOFS_ENTRY_SUBDIR "autofsentries"
+
+#define SYSDB_AUTOFS_MAP_OC "automountMap"
+#define SYSDB_AUTOFS_MAP_NAME "automountMapName"
+
+#define SYSDB_AUTOFS_ENTRY_OC "automount"
+#define SYSDB_AUTOFS_ENTRY_KEY "automountKey"
+#define SYSDB_AUTOFS_ENTRY_VALUE "automountInformation"
+
+errno_t
+sysdb_save_autofsmap(struct sss_domain_info *domain,
+ const char *name,
+ const char *autofsmapname,
+ const char *origdn,
+ struct sysdb_attrs *attrs,
+ int cache_timeout,
+ time_t now,
+ bool enumerated);
+
+errno_t
+sysdb_get_map_byname(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *map_name,
+ struct ldb_message **map);
+
+errno_t
+sysdb_delete_autofsmap(struct sss_domain_info *domain,
+ const char *name);
+
+errno_t
+sysdb_save_autofsentry(struct sss_domain_info *domain,
+ const char *map,
+ const char *key,
+ const char *value,
+ struct sysdb_attrs *attrs,
+ int cache_timeout,
+ time_t now);
+
+errno_t
+sysdb_get_autofsentry(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *map_name,
+ const char *entry_name,
+ struct ldb_message **_entry);
+
+errno_t
+sysdb_del_autofsentry(struct sss_domain_info *domain,
+ const char *entry_dn);
+
+errno_t
+sysdb_del_autofsentry_by_key(struct sss_domain_info *domain,
+ const char *map_name,
+ const char *entry_key);
+
+errno_t
+sysdb_autofs_entries_by_map(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *mapname,
+ size_t *_count,
+ struct ldb_message ***_entries);
+
+errno_t
+sysdb_set_autofsmap_attr(struct sss_domain_info *domain,
+ const char *name,
+ struct sysdb_attrs *attrs,
+ int mod_op);
+
+errno_t
+sysdb_invalidate_autofs_entries(struct sss_domain_info *domain,
+ const char *mapname);
+
+errno_t
+sysdb_invalidate_autofs_maps(struct sss_domain_info *domain);
+
+char *
+sysdb_autofsentry_strdn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *map_name,
+ const char *entry_name,
+ const char *entry_value);
+
+#endif /* _SYSDB_AUTOFS_H_ */
diff --git a/src/db/sysdb_certmap.c b/src/db/sysdb_certmap.c
new file mode 100644
index 0000000..4cc5b5b
--- /dev/null
+++ b/src/db/sysdb_certmap.c
@@ -0,0 +1,502 @@
+/*
+ SSSD
+
+ System Database - certificate mapping rules related calls
+
+ Copyright (C) 2017 Sumit Bose <sbose@redhat.com>
+
+ 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 "lib/certmap/sss_certmap.h"
+
+static errno_t sysdb_create_certmap_container(struct sysdb_ctx *sysdb,
+ bool user_name_hint)
+{
+ struct ldb_message *msg = NULL;
+ errno_t ret;
+
+ msg = ldb_msg_new(sysdb);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ msg->dn = ldb_dn_new(msg, sysdb->ldb, SYSDB_TMPL_CERTMAP_BASE);
+ if (msg->dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = ldb_msg_add_string(msg, "cn", "certmap");
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, SYSDB_CERTMAP_USER_NAME_HINT,
+ user_name_hint ? "TRUE" : "FALSE");
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ /* do a synchronous add */
+ ret = ldb_add(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to add certmap container (%d, [%s])!\n",
+ ret, ldb_errstring(sysdb->ldb));
+ ret = EIO;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(msg);
+
+ return ret;
+}
+
+static struct ldb_dn *sysdb_certmap_dn(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ const char *name)
+{
+ int ret;
+ char *clean_name;
+ struct ldb_dn *dn = NULL;
+
+ ret = sysdb_dn_sanitize(mem_ctx, name, &clean_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_dn_sanitize failed.\n");
+ return NULL;
+ }
+
+ dn = ldb_dn_new_fmt(mem_ctx, sysdb->ldb, SYSDB_TMPL_CERTMAP, clean_name);
+ talloc_free(clean_name);
+ if (dn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new_fmt failed.\n");
+ return NULL;
+ }
+
+ return dn;
+}
+
+static errno_t sysdb_certmap_add(struct sysdb_ctx *sysdb,
+ struct certmap_info *certmap)
+{
+ struct ldb_message *msg;
+ struct ldb_message_element *el;
+ int ret;
+ TALLOC_CTX *tmp_ctx;
+ size_t c;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed");
+ return ENOMEM;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = sysdb_certmap_dn(tmp_ctx, sysdb, certmap->name);
+ if (msg->dn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_certmap_dn failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_add_string(msg, SYSDB_OBJECTCLASS, SYSDB_CERTMAP_CLASS);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_string failed.\n");
+ goto done;
+ }
+
+ ret = sysdb_add_string(msg, SYSDB_NAME, certmap->name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_string failed.\n");
+ goto done;
+ }
+
+ if (certmap->map_rule != NULL) {
+ ret = sysdb_add_string(msg, SYSDB_CERTMAP_MAPPING_RULE,
+ certmap->map_rule);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_string failed.\n");
+ goto done;
+ }
+ }
+
+ if (certmap->match_rule != NULL) {
+ ret = sysdb_add_string(msg, SYSDB_CERTMAP_MATCHING_RULE,
+ certmap->match_rule);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_string failed.\n");
+ goto done;
+ }
+ }
+
+ if (certmap->domains != NULL && certmap->domains[0] != NULL) {
+ for (c = 0; certmap->domains[c] != NULL; c++);
+ el = talloc_zero(tmp_ctx, struct ldb_message_element);
+ if (el == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ el->name = talloc_strdup(el, SYSDB_CERTMAP_DOMAINS);
+ if(el->name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ el->num_values = c;
+ el->values = talloc_zero_array(el, struct ldb_val, c + 1);
+ if (el->values == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (c = 0; certmap->domains[c] != NULL; c++) {
+ el->values[c].data = (uint8_t *) talloc_strdup(el->values,
+ certmap->domains[c]);
+ if (el->values[c].data == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ el->values[c].length = strlen(certmap->domains[c]);
+ }
+
+ ret = ldb_msg_add(msg, el, LDB_FLAG_MOD_ADD);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add failed.\n");
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+
+ ret = sysdb_add_ulong(msg, SYSDB_CERTMAP_PRIORITY,
+ (unsigned long)certmap->priority);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_ulong failed.\n");
+ goto done;
+ }
+
+ ret = ldb_add(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_add failed.\n");
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, sss_strerror(ret));
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_update_certmap(struct sysdb_ctx *sysdb,
+ struct certmap_info **certmaps,
+ bool user_name_hint)
+{
+ size_t c;
+ struct ldb_dn *container_dn = NULL;
+ bool in_transaction = false;
+ int ret;
+ int sret;
+
+ if (certmaps == NULL) {
+ return EINVAL;
+ }
+
+ container_dn = ldb_dn_new(sysdb, sysdb->ldb, SYSDB_TMPL_CERTMAP_BASE);
+ if (container_dn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n");
+ return ENOMEM;
+ }
+
+ ret = sysdb_transaction_start(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_transaction_start failed.\n");
+ goto done;
+ }
+ in_transaction = true;
+
+ ret = sysdb_delete_recursive(sysdb, container_dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_delete_recursive failed.\n");
+ goto done;
+ }
+ ret = sysdb_create_certmap_container(sysdb, user_name_hint);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_create_certmap_container failed.\n");
+ goto done;
+ }
+
+ for (c = 0; certmaps[c] != NULL; c++) {
+ ret = sysdb_certmap_add(sysdb, certmaps[c]);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_certmap_add failed.\n");
+ goto done;
+ }
+ }
+
+ ret = sysdb_transaction_commit(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_transaction_commit failed.\n");
+ goto done;
+ }
+ in_transaction = false;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction.\n");
+ }
+ }
+
+ talloc_free(container_dn);
+
+ return ret;
+}
+
+enum certmap_info_member {
+ SSS_CMIM_NAME = 0,
+ SSS_CMIM_MAPPING_RULE,
+ SSS_CMIM_MATCHING_RULE,
+ SSS_CMIM_PRIORITY,
+ SSS_CMIM_DOMAINS,
+
+ SSS_CMIM_SENTINEL
+};
+
+errno_t sysdb_ldb_msg_attr_to_certmap_info(TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ const char **attr_map,
+ struct certmap_info **certmap)
+{
+ int ret;
+ size_t d;
+ size_t num_values;
+ struct certmap_info *map = NULL;
+ const char *tmp_str;
+ uint64_t tmp_uint;
+ struct ldb_message_element *tmp_el;
+
+ if (msg == NULL || attr_map == NULL || certmap == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid input.\n");
+ return EINVAL;
+ }
+
+ for (d = 0; d < SSS_CMIM_SENTINEL; d++) {
+ if (attr_map[d] == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid attribute map");
+ return EINVAL;
+ }
+ }
+
+ map = talloc_zero(mem_ctx, struct certmap_info);
+ if (map == NULL) {
+ return ENOMEM;
+ }
+
+ tmp_str = ldb_msg_find_attr_as_string(msg, attr_map[SSS_CMIM_NAME], NULL);
+ if (tmp_str == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "The object [%s] doesn't have a name.\n",
+ ldb_dn_get_linearized(msg->dn));
+ ret = EINVAL;
+ goto done;
+ }
+
+ map->name = talloc_strdup(map, tmp_str);
+ if (map->name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tmp_str = ldb_msg_find_attr_as_string(msg, attr_map[SSS_CMIM_MAPPING_RULE],
+ NULL);
+ if (tmp_str != NULL) {
+ map->map_rule = talloc_strdup(map, tmp_str);
+ if (map->map_rule == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ tmp_str = ldb_msg_find_attr_as_string(msg, attr_map[SSS_CMIM_MATCHING_RULE],
+ NULL);
+ if (tmp_str != NULL) {
+ map->match_rule = talloc_strdup(map, tmp_str);
+ if (map->match_rule == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ tmp_uint = ldb_msg_find_attr_as_uint64(msg, attr_map[SSS_CMIM_PRIORITY],
+ (uint64_t) -1);
+ if (tmp_uint != (uint64_t) -1) {
+ if (tmp_uint > UINT32_MAX) {
+ DEBUG(SSSDBG_OP_FAILURE, "Priority value [%lu] too large.\n",
+ (unsigned long) tmp_uint);
+ ret = EINVAL;
+ goto done;
+ }
+
+ map->priority = (uint32_t) tmp_uint;
+ } else {
+ map->priority = SSS_CERTMAP_MIN_PRIO;
+ }
+
+ tmp_el = ldb_msg_find_element(msg, attr_map[SSS_CMIM_DOMAINS]);
+ if (tmp_el != NULL) {
+ num_values = tmp_el->num_values;
+ } else {
+ num_values = 0;
+ }
+
+ map->domains = talloc_zero_array(map, const char *, num_values + 1);
+ if (map->domains == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (d = 0; d < num_values; d++) {
+ map->domains[d] = talloc_strndup(map->domains,
+ (char *) tmp_el->values[d].data,
+ tmp_el->values[d].length);
+ if (map->domains[d] == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ *certmap = map;
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(map);
+ }
+
+ return ret;
+}
+
+errno_t sysdb_get_certmap(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
+ struct certmap_info ***certmaps, bool *user_name_hint)
+{
+ size_t c;
+ struct ldb_dn *container_dn = NULL;
+ int ret;
+ struct certmap_info **maps = NULL;
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct ldb_result *res;
+ const char *attrs[] = {SYSDB_NAME,
+ SYSDB_CERTMAP_MAPPING_RULE,
+ SYSDB_CERTMAP_MATCHING_RULE,
+ SYSDB_CERTMAP_PRIORITY,
+ SYSDB_CERTMAP_DOMAINS,
+ NULL};
+ const char *config_attrs[] = {SYSDB_CERTMAP_USER_NAME_HINT,
+ NULL};
+ bool hint = false;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ container_dn = ldb_dn_new(tmp_ctx, sysdb->ldb, SYSDB_TMPL_CERTMAP_BASE);
+ if (container_dn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_search(sysdb->ldb, tmp_ctx, &res, container_dn, LDB_SCOPE_BASE,
+ config_attrs, SYSDB_CERTMAP_USER_NAME_HINT"=*");
+ if (ret != LDB_SUCCESS || res->count != 1) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Failed to read certmap config, skipping.\n");
+ } else {
+ hint = ldb_msg_find_attr_as_bool(res->msgs[0],
+ SYSDB_CERTMAP_USER_NAME_HINT, false);
+ }
+
+ ret = ldb_search(sysdb->ldb, tmp_ctx, &res,
+ container_dn, LDB_SCOPE_SUBTREE,
+ attrs, "objectclass=%s", SYSDB_CERTMAP_CLASS);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_search failed.\n");
+ ret = EIO;
+ goto done;
+ }
+
+ if (res->count == 0) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No certificate maps found.\n");
+ ret = EOK;
+ goto done;
+ }
+
+ maps = talloc_zero_array(tmp_ctx, struct certmap_info *, res->count + 1);
+ if (maps == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (c = 0; c < res->count; c++) {
+ ret = sysdb_ldb_msg_attr_to_certmap_info(maps, res->msgs[c], attrs,
+ &maps[c]);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_ldb_msg_attr_to_certmap_info failed.\n");
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ *certmaps = talloc_steal(mem_ctx, maps);
+ *user_name_hint = hint;
+ }
+
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
diff --git a/src/db/sysdb_domain_resolution_order.c b/src/db/sysdb_domain_resolution_order.c
new file mode 100644
index 0000000..6377446
--- /dev/null
+++ b/src/db/sysdb_domain_resolution_order.c
@@ -0,0 +1,169 @@
+/*
+ Authors:
+ Fabiano Fidêncio <fidencio@redhat.com>
+
+ Copyright (C) 2017 Red Hat
+
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "db/sysdb_private.h"
+
+static errno_t
+sysdb_get_domain_resolution_order_string_attr(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char *const *attrs,
+ const char **_attr)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ const char *attr;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = ldb_search(sysdb->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = EIO;
+ goto done;
+ }
+
+ if (res->count > 1) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Base search returned [%d] results, expected 1.\n", res->count);
+ ret = EINVAL;
+ goto done;
+ } else if (res->count == 0) {
+ ret = ENOENT;
+ goto done;
+ } else {
+ /* res->count == 1 */
+ attr = ldb_msg_find_attr_as_string(res->msgs[0], attrs[0], NULL);
+ if (attr == NULL) {
+ ret = ENOENT;
+ goto done;
+ }
+ }
+
+ *_attr = talloc_steal(mem_ctx, attr);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_get_domain_resolution_order(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char **_domain_resolution_order)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *domain_resolution_order = NULL;
+ const char *attrs[] = { SYSDB_DOMAIN_RESOLUTION_ORDER, NULL };
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_get_domain_resolution_order_string_attr(
+ tmp_ctx, sysdb, dn, attrs, &domain_resolution_order);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_get_domain_resolution_order_string_attr() failed "
+ "[%d]: [%s]",
+ ret, sss_strerror(ret));
+ goto done;
+ } else if (ret == ENOENT) {
+ *_domain_resolution_order = NULL;
+ goto done;
+ } else {
+ /* ret == EOK */
+ *_domain_resolution_order = talloc_steal(mem_ctx,
+ domain_resolution_order);
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_update_domain_resolution_order(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char *domain_resolution_order)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = dn;
+
+ ret = ldb_msg_add_empty(msg, SYSDB_DOMAIN_RESOLUTION_ORDER,
+ LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ if (domain_resolution_order != NULL) {
+ ret = ldb_msg_add_string(msg, SYSDB_DOMAIN_RESOLUTION_ORDER,
+ domain_resolution_order);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+
+ ret = ldb_modify(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "ldb_modify()_failed: [%s][%d][%s]\n",
+ ldb_strerror(ret), ret, ldb_errstring(sysdb->ldb));
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
diff --git a/src/db/sysdb_domain_resolution_order.h b/src/db/sysdb_domain_resolution_order.h
new file mode 100644
index 0000000..45d2ea6
--- /dev/null
+++ b/src/db/sysdb_domain_resolution_order.h
@@ -0,0 +1,37 @@
+/*
+ Authors:
+ Fabiano Fidêncio <fidencio@redhat.com>
+
+ Copyright (C) 2017 Red Hat
+
+ 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 _SYSDB_DOMAIN_RESOLUTION_ORDER_H_
+#define _SYSDB_DOMAIN_RESOLUTION_ORDER_H_
+
+#include "db/sysdb.h"
+
+errno_t
+sysdb_get_domain_resolution_order(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char **_domain_resolution_order);
+
+errno_t
+sysdb_update_domain_resolution_order(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char *domain_resolution_order);
+
+#endif /* _SYSDB_DOMAIN_RESOLUTION_ORDER_H_ */
diff --git a/src/db/sysdb_gpo.c b/src/db/sysdb_gpo.c
new file mode 100644
index 0000000..e5af91b
--- /dev/null
+++ b/src/db/sysdb_gpo.c
@@ -0,0 +1,685 @@
+/*
+ SSSD
+
+ Authors:
+ Yassir Elley <yelley@redhat.com>
+
+ Copyright (C) 2014 Red Hat
+
+ 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 "db/sysdb.h"
+#include "db/sysdb_private.h"
+
+static struct ldb_dn *
+sysdb_gpo_dn(TALLOC_CTX *mem_ctx, struct sss_domain_info *domain,
+ const char *gpo_guid)
+{
+ errno_t ret;
+ char *clean_gpo_guid;
+ struct ldb_dn *dn;
+
+ ret = sysdb_dn_sanitize(NULL, gpo_guid, &clean_gpo_guid);
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, SYSDB_TMPL_GPO"\n", clean_gpo_guid, domain->name);
+
+ dn = ldb_dn_new_fmt(mem_ctx, domain->sysdb->ldb, SYSDB_TMPL_GPO,
+ clean_gpo_guid, domain->name);
+ talloc_free(clean_gpo_guid);
+
+ return dn;
+}
+
+errno_t
+sysdb_gpo_store_gpo(struct sss_domain_info *domain,
+ const char *gpo_guid,
+ int gpo_version,
+ int cache_timeout,
+ time_t now)
+{
+ errno_t ret, sret;
+ int lret;
+ struct ldb_message *update_msg;
+ struct ldb_message **msgs;
+ static const char *attrs[] = SYSDB_GPO_ATTRS;
+ size_t count;
+ bool in_transaction = false;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ update_msg = ldb_msg_new(tmp_ctx);
+ if (!update_msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ update_msg->dn = sysdb_gpo_dn(update_msg, domain, gpo_guid);
+ if (!update_msg->dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_transaction_start(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+ goto done;
+ }
+
+ if (!now) {
+ now = time(NULL);
+ }
+
+ in_transaction = true;
+
+ /* Check for an existing gpo_guid entry */
+ ret = sysdb_search_entry(tmp_ctx, domain->sysdb, update_msg->dn,
+ LDB_SCOPE_BASE, NULL, attrs, &count, &msgs);
+
+ if (ret == ENOENT) {
+ /* Create new GPO */
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Adding new GPO [gpo_guid:%s][gpo_version:%d]\n",
+ gpo_guid, gpo_version);
+
+ /* Add the objectClass */
+ lret = ldb_msg_add_empty(update_msg, SYSDB_OBJECTCLASS,
+ LDB_FLAG_MOD_ADD,
+ NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ lret = ldb_msg_add_string(update_msg, SYSDB_OBJECTCLASS,
+ SYSDB_GPO_OC);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ /* Add the GPO GUID */
+ lret = ldb_msg_add_empty(update_msg, SYSDB_GPO_GUID_ATTR,
+ LDB_FLAG_MOD_ADD,
+ NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ lret = ldb_msg_add_string(update_msg, SYSDB_GPO_GUID_ATTR, gpo_guid);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ /* Add the Version */
+ lret = ldb_msg_add_empty(update_msg, SYSDB_GPO_VERSION_ATTR,
+ LDB_FLAG_MOD_ADD,
+ NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ lret = ldb_msg_add_fmt(update_msg, SYSDB_GPO_VERSION_ATTR,
+ "%d", gpo_version);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ /* Add the Policy File Timeout */
+ lret = ldb_msg_add_empty(update_msg, SYSDB_GPO_TIMEOUT_ATTR,
+ LDB_FLAG_MOD_ADD, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ lret = ldb_msg_add_fmt(update_msg, SYSDB_GPO_TIMEOUT_ATTR, "%lu",
+ ((cache_timeout) ? (now + cache_timeout) : 0));
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ lret = ldb_add(domain->sysdb->ldb, update_msg);
+ if (lret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to add GPO: [%s]\n",
+ ldb_strerror(lret));
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ } else if (ret == EOK && count == 1) {
+ /* Update the existing GPO */
+
+ DEBUG(SSSDBG_TRACE_ALL, "Updating new GPO [%s][%s]\n", domain->name, gpo_guid);
+
+ /* Add the Version */
+ lret = ldb_msg_add_empty(update_msg, SYSDB_GPO_VERSION_ATTR,
+ LDB_FLAG_MOD_REPLACE,
+ NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ lret = ldb_msg_add_fmt(update_msg, SYSDB_GPO_VERSION_ATTR,
+ "%d", gpo_version);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ /* Add the Policy File Timeout */
+ lret = ldb_msg_add_empty(update_msg, SYSDB_GPO_TIMEOUT_ATTR,
+ LDB_FLAG_MOD_REPLACE, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ lret = ldb_msg_add_fmt(update_msg, SYSDB_GPO_TIMEOUT_ATTR, "%lu",
+ ((cache_timeout) ? (now + cache_timeout) : 0));
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ lret = ldb_modify(domain->sysdb->ldb, update_msg);
+ if (lret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to modify GPO: [%s](%d)[%s]\n",
+ ldb_strerror(lret), lret, ldb_errstring(domain->sysdb->ldb));
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ } else {
+ ret = EIO;
+ goto done;
+ }
+
+ ret = sysdb_transaction_commit(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not commit transaction: [%s]\n", strerror(ret));
+ goto done;
+ }
+ in_transaction = false;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(domain->sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
+ }
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_gpo_get_gpo_by_guid(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *gpo_guid,
+ struct ldb_result **_result)
+{
+ errno_t ret;
+ int lret;
+ struct ldb_dn *base_dn;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+
+ const char *attrs[] = SYSDB_GPO_ATTRS;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ DEBUG(SSSDBG_TRACE_ALL, SYSDB_TMPL_GPO_BASE"\n", domain->name);
+
+ base_dn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb,
+ SYSDB_TMPL_GPO_BASE,
+ domain->name);
+ if (!base_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ lret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, SYSDB_GPO_GUID_FILTER, gpo_guid);
+ if (lret) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not locate GPO: [%s]\n",
+ ldb_strerror(lret));
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ if (res->count > 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Search for GUID [%s] returned more than " \
+ "one object.\n", gpo_guid);
+ ret = EINVAL;
+ goto done;
+ } else if (res->count == 0) {
+ ret = ENOENT;
+ goto done;
+ }
+ *_result = talloc_steal(mem_ctx, res);
+ ret = EOK;
+done:
+
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_ALL, "No such entry.\n");
+ } else if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_gpo_get_gpos(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ errno_t ret;
+ int lret;
+ struct ldb_dn *base_dn;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+
+ const char *attrs[] = SYSDB_GPO_ATTRS;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ DEBUG(SSSDBG_TRACE_ALL, SYSDB_TMPL_GPO_BASE"\n", domain->name);
+
+ base_dn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb,
+ SYSDB_TMPL_GPO_BASE,
+ domain->name);
+ if (!base_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ lret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, SYSDB_GPO_FILTER);
+ if (lret) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not locate GPOs: [%s]\n",
+ ldb_strerror(lret));
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ if (res->count == 0) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ *_result = talloc_steal(mem_ctx, res);
+ ret = EOK;
+
+done:
+
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_ALL, "No GPO entries.\n");
+ } else if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/* GPO Result */
+
+static struct ldb_dn *
+sysdb_gpo_result_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *result_name)
+{
+ errno_t ret;
+ char *clean_result_name;
+ struct ldb_dn *dn;
+
+ ret = sysdb_dn_sanitize(NULL, result_name, &clean_result_name);
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, SYSDB_TMPL_GPO_RESULT"\n",
+ clean_result_name, domain->name);
+
+ dn = ldb_dn_new_fmt(mem_ctx, domain->sysdb->ldb, SYSDB_TMPL_GPO_RESULT,
+ clean_result_name, domain->name);
+ talloc_free(clean_result_name);
+
+ return dn;
+}
+
+errno_t
+sysdb_gpo_store_gpo_result_setting(struct sss_domain_info *domain,
+ const char *ini_key,
+ const char *ini_value)
+{
+ errno_t ret, sret;
+ int lret;
+ struct ldb_message *update_msg;
+ struct ldb_message **msgs;
+ size_t count;
+ bool in_transaction = false;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ update_msg = ldb_msg_new(tmp_ctx);
+ if (!update_msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ update_msg->dn = sysdb_gpo_result_dn(update_msg, domain, "gpo_result");
+ if (!update_msg->dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_transaction_start(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+ goto done;
+ }
+
+ in_transaction = true;
+
+ /* Check for an existing GPO Result object */
+ ret = sysdb_search_entry(tmp_ctx, domain->sysdb, update_msg->dn,
+ LDB_SCOPE_BASE, NULL, NULL, &count, &msgs);
+
+ if (ret == ENOENT) {
+ /* Create new GPO Result object */
+ DEBUG(SSSDBG_TRACE_FUNC, "Storing setting: key [%s] value [%s]\n",
+ ini_key, ini_value);
+
+ /* Add the objectClass */
+ lret = ldb_msg_add_empty(update_msg, SYSDB_OBJECTCLASS,
+ LDB_FLAG_MOD_ADD,
+ NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ lret = ldb_msg_add_string(update_msg, SYSDB_OBJECTCLASS,
+ SYSDB_GPO_RESULT_OC);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ /* Store the policy_setting if it is non-NULL */
+ if (ini_value) {
+ lret = ldb_msg_add_empty(update_msg, ini_key,
+ LDB_FLAG_MOD_ADD,
+ NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ lret = ldb_msg_add_string(update_msg, ini_key, ini_value);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ }
+
+ lret = ldb_add(domain->sysdb->ldb, update_msg);
+ if (lret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to add GPO Result: [%s]\n",
+ ldb_strerror(lret));
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ } else if (ret == EOK && count == 1) {
+ /* Update existing GPO Result object*/
+ if (ini_value) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Updating setting: key [%s] value [%s]\n",
+ ini_key, ini_value);
+ /* Update the policy setting */
+ lret = ldb_msg_add_empty(update_msg, ini_key,
+ LDB_FLAG_MOD_REPLACE,
+ NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ lret = ldb_msg_add_fmt(update_msg, ini_key, "%s", ini_value);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ } else {
+ /* If the value is NULL, we need to remove it from the cache */
+ DEBUG(SSSDBG_TRACE_FUNC, "Removing setting: key [%s]\n", ini_key);
+
+ /* Update the policy setting */
+ lret = ldb_msg_add_empty(update_msg, ini_key,
+ LDB_FLAG_MOD_DELETE,
+ NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ }
+
+ lret = ldb_modify(domain->sysdb->ldb, update_msg);
+ if (lret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to modify GPO Result: [%s](%d)[%s]\n",
+ ldb_strerror(lret), lret, ldb_errstring(domain->sysdb->ldb));
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ } else {
+ ret = EIO;
+ goto done;
+ }
+
+ ret = sysdb_transaction_commit(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not commit transaction: [%s]\n", strerror(ret));
+ goto done;
+ }
+ in_transaction = false;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(domain->sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
+ }
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t
+sysdb_gpo_get_gpo_result_object(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char **attrs,
+ struct ldb_result **_result)
+{
+ errno_t ret;
+ int lret;
+ struct ldb_dn *base_dn;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ DEBUG(SSSDBG_TRACE_ALL, SYSDB_TMPL_GPO_RESULT_BASE"\n", domain->name);
+
+ base_dn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb,
+ SYSDB_TMPL_GPO_RESULT_BASE,
+ domain->name);
+ if (!base_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ lret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, SYSDB_GPO_RESULT_FILTER);
+ if (lret) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not locate GPO Result object: [%s]\n",
+ ldb_strerror(lret));
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ if (res->count == 0) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ *_result = talloc_steal(mem_ctx, res);
+ ret = EOK;
+
+done:
+
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_ALL, "No GPO Result object.\n");
+ } else if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+
+errno_t
+sysdb_gpo_get_gpo_result_setting(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *ini_key,
+ const char **_ini_value)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ const char *ini_value;
+
+ const char *attrs[] = {ini_key, NULL};
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ ret = sysdb_gpo_get_gpo_result_object(tmp_ctx, domain, attrs, &res);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ini_value = ldb_msg_find_attr_as_string(res->msgs[0],
+ ini_key,
+ NULL);
+ DEBUG(SSSDBG_TRACE_FUNC, "key [%s] value [%s]\n", ini_key, ini_value);
+
+ *_ini_value = talloc_strdup(mem_ctx, ini_value);
+ if (!*_ini_value && ini_value) {
+ /* If ini_value was NULL, this is expected to also be NULL */
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_ALL, "No setting for key [%s].\n", ini_key);
+ } else if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+
+errno_t sysdb_gpo_delete_gpo_result_object(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain)
+{
+ struct ldb_result *res;
+ errno_t ret, sret;
+ bool in_transaction = false;
+
+ ret = sysdb_transaction_start(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+ goto done;
+ }
+
+ in_transaction = true;
+
+ ret = sysdb_gpo_get_gpo_result_object(mem_ctx, domain, NULL, &res);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not delete GPO result object: %d\n", ret);
+ goto done;
+ } else if (ret != ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Deleting GPO Result object\n");
+
+ ret = sysdb_delete_entry(domain->sysdb, res->msgs[0]->dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not delete GPO Result cache entry\n");
+ goto done;
+ }
+ }
+
+ ret = sysdb_transaction_commit(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not commit transaction: [%s]\n", strerror(ret));
+ goto done;
+ }
+ in_transaction = false;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(domain->sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
+ }
+ }
+ return ret;
+
+}
diff --git a/src/db/sysdb_idmap.c b/src/db/sysdb_idmap.c
new file mode 100644
index 0000000..2aa00ef
--- /dev/null
+++ b/src/db/sysdb_idmap.c
@@ -0,0 +1,316 @@
+/*
+ SSSD
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2012 Red Hat
+
+ 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 "db/sysdb.h"
+#include "db/sysdb_private.h"
+
+static struct ldb_dn *
+sysdb_idmap_dn(TALLOC_CTX *mem_ctx, struct sss_domain_info *domain,
+ const char *object_sid)
+{
+ errno_t ret;
+ char *clean_sid;
+ struct ldb_dn *dn;
+
+ ret = sysdb_dn_sanitize(NULL, object_sid, &clean_sid);
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, SYSDB_TMPL_IDMAP"\n", clean_sid, domain->name);
+
+ dn = ldb_dn_new_fmt(mem_ctx, domain->sysdb->ldb, SYSDB_TMPL_IDMAP,
+ clean_sid, domain->name);
+ talloc_free(clean_sid);
+
+ return dn;
+}
+
+errno_t
+sysdb_idmap_store_mapping(struct sss_domain_info *domain,
+ const char *dom_name,
+ const char *dom_sid,
+ id_t slice_num)
+{
+ errno_t ret, sret;
+ int lret;
+ bool in_transaction = false;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *dn;
+ static const char *attrs[] = SYSDB_IDMAP_ATTRS;
+ size_t count;
+ struct ldb_message *update_msg;
+ struct ldb_message **msgs;
+ const char *old_name;
+ id_t old_slice;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ dn = sysdb_idmap_dn(tmp_ctx, domain, dom_sid);
+ if (!dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ update_msg = ldb_msg_new(tmp_ctx);
+ if (!update_msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+ update_msg->dn = dn;
+
+ ret = sysdb_transaction_start(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+ goto done;
+ }
+
+ in_transaction = true;
+
+
+ /* Check for an existing mapping */
+ ret = sysdb_search_entry(tmp_ctx, domain->sysdb, dn, LDB_SCOPE_BASE,
+ NULL, attrs, &count, &msgs);
+ if (ret != EOK && ret != ENOENT) goto done;
+
+ if (ret == EOK && count != 1) {
+ /* More than one reply for a base search? */
+ ret = EIO;
+ goto done;
+ } else if (ret == ENOENT) {
+ /* Create a new mapping */
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "Adding new ID mapping [%s][%s][%lu]\n",
+ dom_name, dom_sid, (unsigned long)slice_num);
+
+ /* Add the objectClass */
+ lret = ldb_msg_add_empty(update_msg, SYSDB_OBJECTCLASS,
+ LDB_FLAG_MOD_ADD,
+ NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ lret = ldb_msg_add_string(update_msg, SYSDB_OBJECTCLASS,
+ SYSDB_IDMAP_MAPPING_OC);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ /* Add the domain objectSID */
+ lret = ldb_msg_add_empty(update_msg, SYSDB_IDMAP_SID_ATTR,
+ LDB_FLAG_MOD_ADD,
+ NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ lret = ldb_msg_add_string(update_msg, SYSDB_IDMAP_SID_ATTR, dom_sid);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ /* Add the domain name */
+ lret = ldb_msg_add_empty(update_msg, SYSDB_NAME,
+ LDB_FLAG_MOD_ADD,
+ NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ lret = ldb_msg_add_string(update_msg, SYSDB_NAME, dom_name);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ /* Add the slice number */
+ lret = ldb_msg_add_empty(update_msg, SYSDB_IDMAP_SLICE_ATTR,
+ LDB_FLAG_MOD_ADD,
+ NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ lret = ldb_msg_add_fmt(update_msg, SYSDB_IDMAP_SLICE_ATTR,
+ "%lu", (unsigned long)slice_num);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ lret = ldb_add(domain->sysdb->ldb, update_msg);
+ if (lret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to add mapping: [%s]\n",
+ ldb_strerror(lret));
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ } else {
+ /* Update the existing mapping */
+
+ /* Check whether the slice has changed
+ * This should never happen, and it's a recipe for
+ * disaster. We'll throw an error if it does.
+ */
+ old_slice = ldb_msg_find_attr_as_int(msgs[0],
+ SYSDB_IDMAP_SLICE_ATTR,
+ -1);
+ if (old_slice == -1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not identify original slice for SID [%s]\n",
+ dom_sid);
+ ret = ENOENT;
+ goto done;
+ }
+
+ if (slice_num != old_slice) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Detected attempt to change slice value for sid [%s] "
+ "This will break existing users. Refusing to perform.\n",
+ dom_sid);
+ ret = EINVAL;
+ goto done;
+ }
+
+ /* Check whether the name has changed. This may happen
+ * if we're told the real name of a domain and want to
+ * replace the SID as placeholder.
+ */
+ old_name = ldb_msg_find_attr_as_string(msgs[0], SYSDB_NAME, NULL);
+ if (!old_name) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not identify original domain name of SID [%s]\n",
+ dom_sid);
+ ret = ENOENT;
+ goto done;
+ }
+
+ if (strcmp(old_name, dom_name) == 0) {
+ /* There's nothing to be done. We don't need to
+ * make any changes here. Just return success.
+ */
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "No changes needed, canceling transaction\n");
+ ret = EOK;
+ goto done;
+ } else {
+ /* The name has changed. Replace it */
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "Changing domain name of SID [%s] from [%s] to [%s]\n",
+ dom_sid, old_name, dom_name);
+
+ /* Set the new name */
+ lret = ldb_msg_add_empty(update_msg, SYSDB_NAME,
+ LDB_FLAG_MOD_REPLACE,
+ NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ lret = ldb_msg_add_string(update_msg, SYSDB_NAME, dom_name);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ }
+
+ lret = ldb_modify(domain->sysdb->ldb, update_msg);
+ if (lret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to update mapping: [%s](%d)[%s]\n",
+ ldb_strerror(lret), lret, ldb_errstring(domain->sysdb->ldb));
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ }
+
+ ret = sysdb_transaction_commit(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not commit transaction: [%s]\n", strerror(ret));
+ goto done;
+ }
+ in_transaction = false;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(domain->sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not cancel transaction\n");
+ }
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_idmap_get_mappings(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ errno_t ret;
+ struct ldb_dn *base_dn;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ static const char *attrs[] = SYSDB_IDMAP_ATTRS;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ DEBUG(SSSDBG_TRACE_ALL, SYSDB_TMPL_IDMAP_BASE"\n", domain->name);
+
+ base_dn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb,
+ SYSDB_TMPL_IDMAP_BASE, domain->name);
+ if (!base_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ SSS_LDB_SEARCH(ret, domain->sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, SYSDB_IDMAP_FILTER);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not locate ID mappings: [%s]\n",
+ sss_strerror(ret));
+ goto done;
+ }
+
+ *_result = talloc_steal(mem_ctx, res);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
diff --git a/src/db/sysdb_init.c b/src/db/sysdb_init.c
new file mode 100644
index 0000000..c2ea6c3
--- /dev/null
+++ b/src/db/sysdb_init.c
@@ -0,0 +1,1102 @@
+/*
+ SSSD
+
+ System Database - initialization
+
+ Copyright (C) 2008-2011 Simo Sorce <ssorce@redhat.com>
+ Copyright (C) 2008-2011 Stephen Gallagher <ssorce@redhat.com>
+
+ 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 "util/strtonum.h"
+#include "util/sss_utf8.h"
+#include "db/sysdb_private.h"
+#include "confdb/confdb.h"
+#include "util/probes.h"
+#include <time.h>
+
+#define LDB_MODULES_PATH "LDB_MODULES_PATH"
+
+/* If an entry differs only in these attributes, they are written to
+ * the timestamp cache only. In addition, objectclass/objectcategory is added
+ * so that we can distinguish between users and groups.
+ */
+const char *sysdb_ts_cache_attrs[] = {
+ SYSDB_OBJECTCLASS,
+ SYSDB_OBJECTCATEGORY,
+ SYSDB_LAST_UPDATE,
+ SYSDB_CACHE_EXPIRE,
+ SYSDB_ORIG_MODSTAMP,
+ SYSDB_INITGR_EXPIRE,
+ SYSDB_USN,
+
+ NULL,
+};
+
+errno_t sysdb_ldb_connect(TALLOC_CTX *mem_ctx,
+ const char *filename,
+ int flags,
+ struct ldb_context **_ldb)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ errno_t ret;
+ struct ldb_context *ldb;
+ char *mod_path = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (_ldb == NULL) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ ldb = ldb_init(mem_ctx, NULL);
+ if (!ldb) {
+ ret = EIO;
+ goto done;
+ }
+
+ ret = ldb_set_debug(ldb, ldb_debug_messages, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = EIO;
+ goto done;
+ }
+
+ ret = sss_getenv(tmp_ctx, LDB_MODULES_PATH, NULL, &mod_path);
+ if (ret == EOK) {
+ DEBUG(SSSDBG_TRACE_ALL, "Setting ldb module path to [%s].\n", mod_path);
+ ldb_set_modules_dir(ldb, mod_path);
+ } else if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_ALL, "No ldb module path set in env\n");
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_getenv() failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = ldb_connect(ldb, filename, flags, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = EIO;
+ goto done;
+ }
+
+ *_ldb = ldb;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t sysdb_ldb_reconnect(TALLOC_CTX *mem_ctx,
+ const char *ldb_file,
+ int flags,
+ struct ldb_context **ldb)
+{
+ errno_t ret;
+
+ talloc_zfree(*ldb);
+ ret = sysdb_ldb_connect(mem_ctx, ldb_file, flags, ldb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_ldb_connect failed.\n");
+ }
+
+ return ret;
+}
+
+static errno_t sysdb_chown_db_files(struct sysdb_ctx *sysdb,
+ uid_t uid, gid_t gid)
+{
+ errno_t ret;
+
+ ret = chown(sysdb->ldb_file, uid, gid);
+ if (ret != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot set sysdb ownership of %s to %"SPRIuid":%"SPRIgid"\n",
+ sysdb->ldb_file, uid, gid);
+ return ret;
+ }
+
+ if (sysdb->ldb_ts_file != NULL) {
+ ret = chown(sysdb->ldb_ts_file, uid, gid);
+ if (ret != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot set sysdb ownership of %s to %"SPRIuid":%"SPRIgid"\n",
+ sysdb->ldb_ts_file, uid, gid);
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+int sysdb_get_db_file(TALLOC_CTX *mem_ctx,
+ const char *provider,
+ const char *name,
+ const char *base_path,
+ char **_ldb_file,
+ char **_ts_file)
+{
+ char *ldb_file = NULL;
+ char *ts_file = NULL;
+
+ if (_ldb_file != NULL) {
+ ldb_file = talloc_asprintf(mem_ctx, "%s/"CACHE_SYSDB_FILE,
+ base_path, name);
+ if (!ldb_file) {
+ return ENOMEM;
+ }
+ }
+ if (_ts_file != NULL) {
+ ts_file = talloc_asprintf(mem_ctx, "%s/"CACHE_TIMESTAMPS_FILE,
+ base_path, name);
+ if (!ts_file) {
+ talloc_free(ldb_file);
+ return ENOMEM;
+ }
+ }
+
+ if (_ldb_file != NULL) {
+ *_ldb_file = ldb_file;
+ }
+ if (_ts_file != NULL) {
+ *_ts_file = ts_file;
+ }
+
+ return EOK;
+}
+
+static errno_t sysdb_domain_create_int(struct ldb_context *ldb,
+ const char *domain_name)
+{
+ struct ldb_message *msg;
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* == create base domain object == */
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+ msg->dn = ldb_dn_new_fmt(msg, ldb, SYSDB_DOM_BASE, domain_name);
+ if (!msg->dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = ldb_msg_add_string(msg, "cn", domain_name);
+ if (ret != LDB_SUCCESS) {
+ ret = EIO;
+ goto done;
+ }
+ /* do a synchronous add */
+ ret = ldb_add(ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to initialize DB (%d, [%s]) "
+ "for domain %s!\n",
+ ret, ldb_errstring(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, ldb,
+ SYSDB_TMPL_USER_BASE, domain_name);
+ if (!msg->dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = ldb_msg_add_string(msg, "cn", "Users");
+ if (ret != LDB_SUCCESS) {
+ ret = EIO;
+ goto done;
+ }
+ /* do a synchronous add */
+ ret = ldb_add(ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to initialize DB (%d, [%s]) "
+ "for domain %s!\n",
+ ret, ldb_errstring(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, ldb,
+ SYSDB_TMPL_GROUP_BASE, domain_name);
+ if (!msg->dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = ldb_msg_add_string(msg, "cn", "Groups");
+ if (ret != LDB_SUCCESS) {
+ ret = EIO;
+ goto done;
+ }
+ /* do a synchronous add */
+ ret = ldb_add(ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to initialize DB (%d, [%s]) for "
+ "domain %s!\n",
+ ret, ldb_errstring(ldb),
+ domain_name);
+ ret = EIO;
+ goto done;
+ }
+ talloc_zfree(msg);
+
+ ret = EOK;
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_domain_create(struct sysdb_ctx *sysdb, const char *domain_name)
+{
+ return sysdb_domain_create_int(sysdb->ldb, domain_name);
+}
+
+/* Compare versions of sysdb, returns ERRNO accordingly */
+static errno_t
+sysdb_version_check(const char *expected,
+ const char *received)
+{
+ int ret;
+ unsigned int exp_major, exp_minor, recv_major, recv_minor;
+
+ if (strcmp(received, expected) == 0) {
+ return EOK;
+ }
+
+ ret = sscanf(expected, "%u.%u", &exp_major, &exp_minor);
+ if (ret != 2) {
+ return EINVAL;
+ }
+ ret = sscanf(received, "%u.%u", &recv_major, &recv_minor);
+ if (ret != 2) {
+ return EINVAL;
+ }
+
+ if (recv_major > exp_major) {
+ return ERR_SYSDB_VERSION_TOO_NEW;
+ } else if (recv_major < exp_major) {
+ return ERR_SYSDB_VERSION_TOO_OLD;
+ }
+
+ if (recv_minor > exp_minor) {
+ return ERR_SYSDB_VERSION_TOO_NEW;
+ } else if (recv_minor < exp_minor) {
+ return ERR_SYSDB_VERSION_TOO_OLD;
+ }
+
+ return EOK;
+}
+
+static errno_t sysdb_cache_add_base_ldif(struct ldb_context *ldb,
+ const char *base_ldif,
+ const char *domain_name)
+{
+ int ret;
+ struct ldb_ldif *ldif;
+
+ while ((ldif = ldb_ldif_read_string(ldb, &base_ldif))) {
+ ret = ldb_add(ldb, ldif->msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to initialize DB (%d, [%s]) for domain %s!\n",
+ ret, ldb_errstring(ldb), domain_name);
+ return EIO;
+ }
+ ldb_ldif_read_free(ldb, ldif);
+ }
+
+ return EOK;
+}
+
+static errno_t sysdb_cache_create_empty(struct ldb_context *ldb,
+ const char *base_ldif,
+ struct sss_domain_info *domain)
+{
+ int ret;
+
+ ret = sysdb_cache_add_base_ldif(ldb, base_ldif, domain->name);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sysdb_domain_create_int(ldb, domain->name);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+static errno_t sysdb_ts_cache_upgrade(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct ldb_context *ldb,
+ struct sss_domain_info *domain,
+ const char *cur_version,
+ const char **_new_version)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ const char *version;
+ struct ldb_context *save_ldb;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ /* The upgrade process depends on having ldb around, yet the upgrade
+ * function shouldn't set the ldb pointer, only the connect function
+ * should after it's successful. To avoid hard refactoring, save the
+ * ldb pointer here and restore in the 'done' handler
+ */
+ save_ldb = sysdb->ldb;
+ sysdb->ldb = ldb;
+
+ version = talloc_strdup(tmp_ctx, cur_version);
+ if (version == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "Upgrading timstamp cache of DB [%s] from version: %s\n",
+ domain->name, version);
+
+ if (strcmp(version, SYSDB_TS_VERSION_0_1) == 0) {
+ ret = sysdb_ts_upgrade_01(sysdb, &version);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ sysdb->ldb = save_ldb;
+ *_new_version = version;
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t sysdb_domain_cache_upgrade(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct sysdb_dom_upgrade_ctx *upgrade_ctx,
+ struct ldb_context *ldb,
+ struct sss_domain_info *domain,
+ const char *cur_version,
+ const char **_new_version)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ const char *version;
+ struct ldb_context *save_ldb;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ /* The upgrade process depends on having ldb around, yet the upgrade
+ * function shouldn't set the ldb pointer, only the connect function
+ * should after it's successful. To avoid hard refactoring, save the
+ * ldb pointer here and restore in the 'done' handler
+ */
+ save_ldb = sysdb->ldb;
+ sysdb->ldb = ldb;
+
+ version = talloc_strdup(tmp_ctx, cur_version);
+ if (version == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "Upgrading DB [%s] from version: %s\n",
+ domain->name, version);
+
+ if (strcmp(version, SYSDB_VERSION_0_3) == 0) {
+ ret = sysdb_upgrade_03(sysdb, &version);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (strcmp(version, SYSDB_VERSION_0_4) == 0) {
+ ret = sysdb_upgrade_04(sysdb, &version);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (strcmp(version, SYSDB_VERSION_0_5) == 0) {
+ ret = sysdb_upgrade_05(sysdb, &version);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (strcmp(version, SYSDB_VERSION_0_6) == 0) {
+ ret = sysdb_upgrade_06(sysdb, &version);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (strcmp(version, SYSDB_VERSION_0_7) == 0) {
+ ret = sysdb_upgrade_07(sysdb, &version);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (strcmp(version, SYSDB_VERSION_0_8) == 0) {
+ ret = sysdb_upgrade_08(sysdb, &version);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (strcmp(version, SYSDB_VERSION_0_9) == 0) {
+ ret = sysdb_upgrade_09(sysdb, &version);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (strcmp(version, SYSDB_VERSION_0_10) == 0) {
+ ret = sysdb_upgrade_10(sysdb, domain, &version);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (strcmp(version, SYSDB_VERSION_0_11) == 0) {
+ ret = sysdb_upgrade_11(sysdb, domain, &version);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (strcmp(version, SYSDB_VERSION_0_12) == 0) {
+ ret = sysdb_upgrade_12(sysdb, &version);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (strcmp(version, SYSDB_VERSION_0_13) == 0) {
+ ret = sysdb_upgrade_13(sysdb, &version);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (strcmp(version, SYSDB_VERSION_0_14) == 0) {
+ ret = sysdb_upgrade_14(sysdb, &version);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (strcmp(version, SYSDB_VERSION_0_15) == 0) {
+ ret = sysdb_upgrade_15(sysdb, &version);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (strcmp(version, SYSDB_VERSION_0_16) == 0) {
+ ret = sysdb_upgrade_16(sysdb, &version);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (strcmp(version, SYSDB_VERSION_0_17) == 0) {
+ ret = sysdb_upgrade_17(sysdb, upgrade_ctx, &version);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (strcmp(version, SYSDB_VERSION_0_18) == 0) {
+ ret = sysdb_upgrade_18(sysdb, &version);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (strcmp(version, SYSDB_VERSION_0_19) == 0) {
+ ret = sysdb_upgrade_19(sysdb, &version);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (strcmp(version, SYSDB_VERSION_0_20) == 0) {
+ ret = sysdb_upgrade_20(sysdb, &version);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (strcmp(version, SYSDB_VERSION_0_21) == 0) {
+ ret = sysdb_upgrade_21(sysdb, &version);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (strcmp(version, SYSDB_VERSION_0_22) == 0) {
+ ret = sysdb_upgrade_22(sysdb, &version);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ ret = EOK;
+done:
+ sysdb->ldb = save_ldb;
+ *_new_version = version;
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t remove_ts_cache(struct sysdb_ctx *sysdb)
+{
+ errno_t ret;
+
+ if (sysdb->ldb_ts_file == NULL) {
+ return EOK;
+ }
+
+ ret = unlink(sysdb->ldb_ts_file);
+ if (ret != EOK && errno != ENOENT) {
+ return errno;
+ }
+
+ return EOK;
+}
+
+static errno_t sysdb_cache_connect_helper(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *ldb_file,
+ int flags,
+ const char *exp_version,
+ const char *base_ldif,
+ bool *_newly_created,
+ struct ldb_context **_ldb,
+ const char **_version)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct ldb_message_element *el;
+ struct ldb_result *res;
+ struct ldb_dn *verdn;
+ const char *version = NULL;
+ int ret;
+ struct ldb_context *ldb;
+ bool newly_created = false;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_ldb_connect(tmp_ctx, ldb_file, flags, &ldb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_ldb_connect failed.\n");
+ goto done;
+ }
+
+ verdn = ldb_dn_new(tmp_ctx, ldb, SYSDB_BASE);
+ if (!verdn) {
+ ret = EIO;
+ goto done;
+ }
+
+ ret = ldb_search(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) {
+ ret = EIO;
+ goto done;
+ }
+
+ 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;
+ }
+
+ ret = sysdb_version_check(exp_version, version);
+ /* This is not the latest version. Return what version it is
+ * and appropriate error
+ */
+ *_ldb = talloc_steal(mem_ctx, ldb);
+ *_version = talloc_steal(mem_ctx, version);
+ goto done;
+ }
+
+ /* SYSDB_BASE does not exists, means db is empty, populate */
+ ret = sysdb_cache_create_empty(ldb, base_ldif, domain);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ newly_created = true;
+
+ /* We need to reopen the LDB to ensure that
+ * all of the special values take effect
+ * (such as enabling the memberOf plugin and
+ * the various indexes).
+ */
+ ret = sysdb_ldb_reconnect(tmp_ctx, ldb_file, flags, &ldb);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* If we connect to a new database, then the version is the
+ * latest one
+ */
+ *_version = talloc_strdup(mem_ctx, exp_version);
+ if (*_version == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+done:
+ if (ret == EOK) {
+ if (_newly_created != NULL) {
+ *_newly_created = newly_created;
+ }
+ *_ldb = talloc_steal(mem_ctx, ldb);
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t sysdb_cache_connect(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ struct ldb_context **ldb,
+ const char **version)
+{
+ bool newly_created;
+ bool ldb_file_exists;
+ errno_t ret;
+
+ ldb_file_exists = !(access(sysdb->ldb_file, F_OK) == -1 && errno == ENOENT);
+
+ ret = sysdb_cache_connect_helper(mem_ctx, domain, sysdb->ldb_file,
+ 0, SYSDB_VERSION, SYSDB_BASE_LDIF,
+ &newly_created, ldb, version);
+
+ /* The cache has been newly created. */
+ if (ret == EOK && newly_created && !ldb_file_exists) {
+ ret = remove_ts_cache(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not delete the timestamp ldb file (%d) (%s)\n",
+ ret, sss_strerror(ret));
+ }
+ }
+
+ return ret;
+}
+
+static errno_t sysdb_ts_cache_connect(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ struct ldb_context **ldb,
+ const char **version)
+{
+ return sysdb_cache_connect_helper(mem_ctx, domain, sysdb->ldb_ts_file,
+ LDB_FLG_NOSYNC, SYSDB_TS_VERSION,
+ SYSDB_TS_BASE_LDIF, NULL,
+ ldb, version);
+}
+
+static int sysdb_domain_cache_connect(struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ struct sysdb_dom_upgrade_ctx *upgrade_ctx)
+{
+ errno_t ret;
+ const char *version = NULL;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_context *ldb;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_cache_connect(tmp_ctx, sysdb, domain, &ldb, &version);
+ switch (ret) {
+ case ERR_SYSDB_VERSION_TOO_OLD:
+ if (upgrade_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "DB version too old [%s], expected [%s] for domain %s!\n",
+ version, SYSDB_VERSION, domain->name);
+ goto done;
+ }
+
+ ret = sysdb_domain_cache_upgrade(tmp_ctx, sysdb, upgrade_ctx,
+ ldb, domain, version, &version);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* To be on the safe side, nuke the timestamp cache on upgrades.
+ * This is just a one-time performance hit after an upgrade
+ */
+ ret = remove_ts_cache(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not delete the timestamp ldb file (%d) (%s)\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+
+ /* The version should now match SYSDB_VERSION.
+ * If not, it means we didn't match any of the
+ * known older versions. The DB might be
+ * corrupt or generated by a newer version of
+ * SSSD.
+ */
+ ret = sysdb_version_check(SYSDB_VERSION, version);
+ if (ret == EOK) {
+ /* The cache has been upgraded.
+ * We need to reopen the LDB to ensure that
+ * any changes made above take effect.
+ */
+ ret = sysdb_ldb_reconnect(tmp_ctx, sysdb->ldb_file, 0, &ldb);
+ goto done;
+ }
+ break;
+ case ERR_SYSDB_VERSION_TOO_NEW:
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "DB version too new [%s], expected [%s] for domain %s!\n",
+ version, SYSDB_VERSION, domain->name);
+ break;
+ default:
+ break;
+ }
+
+done:
+ if (ret == EOK) {
+ sysdb->ldb = talloc_steal(sysdb, ldb);
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static int sysdb_timestamp_cache_connect(struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ struct sysdb_dom_upgrade_ctx *upgrade_ctx)
+{
+ errno_t ret;
+ const char *version;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_context *ldb;
+
+ if (sysdb->ldb_ts_file == NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No timestamp cache for %s\n", domain->name);
+ return EOK;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_ts_cache_connect(tmp_ctx, sysdb, domain, &ldb, &version);
+ switch (ret) {
+ case ERR_SYSDB_VERSION_TOO_OLD:
+ if (upgrade_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "DB version too old [%s], expected [%s] for domain %s!\n",
+ version, SYSDB_VERSION, domain->name);
+ break;
+ }
+
+ ret = sysdb_ts_cache_upgrade(tmp_ctx, sysdb, ldb, domain, version,
+ &version);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not upgrade the timestamp ldb file (%d) (%s)\n",
+ ret, sss_strerror(ret));
+ break;
+ }
+
+ /* The version should now match SYSDB_VERSION.
+ * If not, it means we didn't match any of the
+ * known older versions. The DB might be
+ * corrupt or generated by a newer version of
+ * SSSD.
+ */
+ ret = sysdb_version_check(SYSDB_TS_VERSION, version);
+ if (ret == EOK) {
+ /* The cache has been upgraded.
+ * We need to reopen the LDB to ensure that
+ * any changes made above take effect.
+ */
+ ret = sysdb_ldb_reconnect(tmp_ctx,
+ sysdb->ldb_ts_file,
+ LDB_FLG_NOSYNC,
+ &ldb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not reopen the timestamp ldb file (%d) (%s)\n",
+ ret, sss_strerror(ret));
+ }
+ }
+ break;
+ case ERR_SYSDB_VERSION_TOO_NEW:
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "DB version too new [%s], expected [%s] for domain %s!\n",
+ version, SYSDB_TS_VERSION, domain->name);
+ break;
+ default:
+ break;
+ }
+
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "The timestamps cache could not be opened. "
+ "Throwing away the database and opening a new one\n");
+
+ ret = remove_ts_cache(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not delete the timestamp ldb file (%d) (%s)\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ /* Now the connect must succeed because the previous cache doesn't
+ * exist anymore.
+ */
+ ret = sysdb_ts_cache_connect(tmp_ctx, sysdb, domain, &ldb, &version);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not delete the timestamp ldb file (%d) (%s)\n",
+ ret, sss_strerror(ret));
+ }
+ }
+
+ if (ret == EOK) {
+ sysdb->ldb_ts = talloc_steal(sysdb, ldb);
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_domain_init_internal(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *db_path,
+ struct sysdb_dom_upgrade_ctx *upgrade_ctx,
+ struct sysdb_ctx **_ctx)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct sysdb_ctx *sysdb;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ sysdb = talloc_zero(mem_ctx, struct sysdb_ctx);
+ if (!sysdb) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_get_db_file(sysdb, domain->provider, domain->name, db_path,
+ &sysdb->ldb_file, &sysdb->ldb_ts_file);
+ if (ret != EOK) {
+ goto done;
+ }
+ DEBUG(SSSDBG_FUNC_DATA,
+ "DB File for %s: %s\n", domain->name, sysdb->ldb_file);
+ if (sysdb->ldb_ts_file) {
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Timestamp file for %s: %s\n", domain->name, sysdb->ldb_ts_file);
+ }
+
+ ret = sysdb_domain_cache_connect(sysdb, domain, upgrade_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not open the sysdb cache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_timestamp_cache_connect(sysdb, domain, upgrade_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not open the timestamp cache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+done:
+ if (ret == EOK) {
+ *_ctx = talloc_steal(mem_ctx, sysdb);
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_init(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domains)
+{
+ return sysdb_init_ext(mem_ctx, domains, NULL, false, 0, 0);
+}
+
+int sysdb_init_ext(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domains,
+ struct sysdb_upgrade_ctx *upgrade_ctx,
+ bool chown_dbfile,
+ uid_t uid,
+ gid_t gid)
+{
+ struct sss_domain_info *dom;
+ struct sysdb_ctx *sysdb;
+ int ret;
+ TALLOC_CTX *tmp_ctx;
+ struct sysdb_dom_upgrade_ctx *dom_upgrade_ctx;
+
+ if (upgrade_ctx != NULL) {
+ /* check if we have an old sssd.ldb to upgrade */
+ ret = sysdb_check_upgrade_02(domains, DB_PATH);
+ if (ret != EOK) {
+ return ret;
+ }
+ }
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ /* open a db for each domain */
+ for (dom = domains; dom; dom = dom->next) {
+ if (upgrade_ctx) {
+ dom_upgrade_ctx = talloc_zero(tmp_ctx,
+ struct sysdb_dom_upgrade_ctx);
+
+ ret = sss_names_init(tmp_ctx,
+ upgrade_ctx->cdb,
+ dom->name,
+ &dom_upgrade_ctx->names);
+ if (ret != EOK) {
+ goto done;
+ }
+ } else {
+ dom_upgrade_ctx = NULL;
+ }
+
+ ret = sysdb_domain_init_internal(tmp_ctx, dom, DB_PATH,
+ dom_upgrade_ctx, &sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot connect to database for %s: [%d]: %s\n",
+ dom->name, ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (chown_dbfile) {
+ ret = sysdb_chown_db_files(sysdb, uid, gid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot chown databases for %s: [%d]: %s\n",
+ dom->name, ret, sss_strerror(ret));
+ goto done;
+ }
+ }
+
+ dom->sysdb = talloc_move(dom, &sysdb);
+ }
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_domain_init(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *db_path,
+ struct sysdb_ctx **_ctx)
+{
+ return sysdb_domain_init_internal(mem_ctx, domain,
+ db_path, false, _ctx);
+}
diff --git a/src/db/sysdb_iphosts.c b/src/db/sysdb_iphosts.c
new file mode 100644
index 0000000..d3ee8f1
--- /dev/null
+++ b/src/db/sysdb_iphosts.c
@@ -0,0 +1,875 @@
+/*
+ SSSD
+
+ Authors:
+ Samuel Cabrero <scabrero@suse.com>
+
+ Copyright (C) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+
+ 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 <arpa/inet.h>
+
+#include "db/sysdb.h"
+#include "db/sysdb_private.h"
+#include "db/sysdb_iphosts.h"
+
+static errno_t
+sysdb_host_update(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char **aliases,
+ const char **addresses);
+
+static errno_t
+sysdb_host_remove_alias(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char *alias);
+
+errno_t
+sysdb_gethostbyname(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **_res)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = {
+ SYSDB_NAME,
+ SYSDB_NAME_ALIAS,
+ SYSDB_IP_HOST_ATTR_ADDRESS,
+ SYSDB_DEFAULT_ATTRS,
+ NULL,
+ };
+ char *sanitized_name;
+ char *subfilter;
+ struct ldb_result *res = NULL;
+ struct ldb_message **msgs;
+ size_t msgs_count;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Searching host by name [%s] in domain [%s]\n",
+ name, domain->name);
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_filter_sanitize(tmp_ctx, name, &sanitized_name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ subfilter = talloc_asprintf(tmp_ctx, SYSDB_IP_HOST_BYNAME_SUBFILTER,
+ sanitized_name, sanitized_name);
+ if (subfilter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_hosts(mem_ctx, domain, subfilter,
+ attrs, &msgs_count, &msgs);
+ if (ret == EOK) {
+ res = talloc_zero(mem_ctx, struct ldb_result);
+ if (res == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ res->count = msgs_count;
+ res->msgs = talloc_steal(res, msgs);
+ }
+
+ *_res = res;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t sysdb_gethostbyaddr(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *address,
+ struct ldb_result **_res)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = {
+ SYSDB_NAME,
+ SYSDB_NAME_ALIAS,
+ SYSDB_IP_HOST_ATTR_ADDRESS,
+ NULL,
+ };
+ char *sanitized_address;
+ char *subfilter;
+ struct ldb_result *res = NULL;
+ struct ldb_message **msgs;
+ size_t msgs_count;
+ char *canonical_address;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ /* IP addresses are stored in canonical form, canonicalize the given
+ * address before search */
+ ret = sss_canonicalize_ip_address(tmp_ctx, address, &canonical_address);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Searching host by address [%s] in domain [%s]\n",
+ canonical_address, domain->name);
+
+ ret = sss_filter_sanitize(tmp_ctx, canonical_address, &sanitized_address);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ subfilter = talloc_asprintf(tmp_ctx, SYSDB_IP_HOST_BYADDR_SUBFILTER,
+ sanitized_address);
+ if (subfilter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_hosts(mem_ctx, domain, subfilter,
+ attrs, &msgs_count, &msgs);
+ if (ret == EOK) {
+ res = talloc_zero(mem_ctx, struct ldb_result);
+ if (res == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ res->count = msgs_count;
+ res->msgs = talloc_steal(res, msgs);
+ }
+
+ *_res = res;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+sysdb_store_host(struct sss_domain_info *domain,
+ const char *primary_name,
+ const char **aliases,
+ const char **addresses,
+ struct sysdb_attrs *extra_attrs,
+ char **remove_attrs,
+ uint64_t cache_timeout,
+ time_t now)
+{
+ errno_t ret;
+ errno_t sret;
+ TALLOC_CTX *tmp_ctx;
+ bool in_transaction = false;
+ struct ldb_result *res = NULL;
+ const char *name;
+ unsigned int i, j;
+ struct ldb_dn *update_dn = NULL;
+ struct sysdb_attrs *attrs;
+ struct sysdb_ctx *sysdb;
+ size_t len;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Storing host [%s] into cache, domain [%s]\n",
+ primary_name, domain->name);
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ sysdb = domain->sysdb;
+
+ ret = sysdb_transaction_start(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+ goto done;
+ }
+
+ in_transaction = true;
+
+ /* Check that the addresses are unique. If the address appears for any
+ * host other than the one matching the primary_name, we need to
+ * remove them so that gethostbyaddr() can work properly.
+ * Last entry saved to the cache should always "win".
+ */
+ len = talloc_array_length(addresses);
+ for (i = 0; i < len && addresses[i] != NULL; i++) {
+ ret = sysdb_gethostbyaddr(tmp_ctx, domain, addresses[i], &res);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ } else if (ret != ENOENT) {
+ if (res->count != 1) {
+ /* Somehow the cache has multiple entries with the same
+ * address. This is corrupted. We'll delete them all to
+ * sort it out.
+ */
+ for (j = 0; j < res->count; j++) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Corrupt cache entry [%s] detected. Deleting\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[j]->dn));
+
+ ret = sysdb_delete_entry(sysdb, res->msgs[j]->dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not delete corrupt cache entry [%s]\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[j]->dn));
+ goto done;
+ }
+ }
+ } else {
+ /* Check whether this is the same name as we're currently
+ * saving to the cache.
+ */
+ name = ldb_msg_find_attr_as_string(res->msgs[0],
+ SYSDB_NAME,
+ NULL);
+ if (name == NULL || strcmp(name, primary_name) != 0) {
+ if (name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "A host with no name?\n");
+ /* Corrupted */
+ }
+
+ /* Either this is a corrupt entry or it's another host
+ * claiming ownership of this address. In order to account
+ * for address reassignments, we need to delete the old
+ * entry.
+ */
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Corrupt or replaced cache entry [%s] detected. "
+ "Deleting\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[0]->dn));
+
+ ret = sysdb_delete_entry(sysdb, res->msgs[0]->dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not delete cache entry [%s]\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[0]->dn));
+ }
+ }
+ }
+ }
+ talloc_zfree(res);
+ }
+
+ /* Ok, addresses should now be unique. Now look the host up by name
+ * to determine if we need to update existing entries or modify aliases.
+ */
+ ret = sysdb_gethostbyname(tmp_ctx, domain, primary_name, &res);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ } else if (ret != ENOENT) { /* Found entries */
+ for (i = 0; i < res->count; i++) {
+ /* Check whether this is the same name as we're currently
+ * saving to the cache.
+ */
+ name = ldb_msg_find_attr_as_string(res->msgs[i],
+ SYSDB_NAME,
+ NULL);
+ if (name == NULL) {
+ /* Corrupted */
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "A host with no name?\n");
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Corrupt cache entry [%s] detected. Deleting\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[i]->dn));
+
+ ret = sysdb_delete_entry(sysdb, res->msgs[i]->dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not delete corrupt cache entry [%s]\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[i]->dn));
+ goto done;
+ }
+ } else if (strcmp(name, primary_name) == 0) {
+ /* This is the same host name, so we need
+ * to update this entry with the values
+ * provided.
+ */
+ if (update_dn) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Two existing hosts with the same name: [%s]? "
+ "Deleting both.\n",
+ primary_name);
+
+ /* Delete the entry from the previous pass */
+ ret = sysdb_delete_entry(sysdb, update_dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not delete cache entry [%s]\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ update_dn));
+ goto done;
+ }
+
+ /* Delete the new entry as well */
+ ret = sysdb_delete_entry(sysdb, res->msgs[i]->dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not delete cache entry [%s]\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[i]->dn));
+ goto done;
+ }
+
+ update_dn = NULL;
+ } else {
+ update_dn = talloc_steal(tmp_ctx, res->msgs[i]->dn);
+ }
+ } else {
+ /* Another host is claiming this name as an alias.
+ * In order to account for aliases being promoted to
+ * primary names, we need to make sure to remove the
+ * old alias entry.
+ */
+ ret = sysdb_host_remove_alias(sysdb,
+ res->msgs[i]->dn,
+ primary_name);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+ }
+ talloc_zfree(res);
+ }
+
+ if (update_dn) {
+ /* Update the existing entry */
+ ret = sysdb_host_update(sysdb, update_dn, aliases, addresses);
+ } else {
+ /* Add a new entry */
+ ret = sysdb_host_add(tmp_ctx, domain, primary_name,
+ aliases, addresses, &update_dn);
+ }
+
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (extra_attrs == NULL) {
+ attrs = sysdb_new_attrs(tmp_ctx);
+ if (attrs == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ } else {
+ attrs = extra_attrs;
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_UPDATE, now);
+ if (ret) {
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE,
+ ((cache_timeout) ?
+ (now + cache_timeout) : 0));
+ if (ret) {
+ goto done;
+ }
+
+ ret = sysdb_set_entry_attr(sysdb, update_dn, attrs, SYSDB_MOD_REP);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (remove_attrs) {
+ ret = sysdb_remove_attrs(domain, primary_name,
+ SYSDB_MEMBER_HOST,
+ remove_attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not remove missing attributes: [%s]\n",
+ strerror(ret));
+ goto done;
+ }
+ }
+
+ ret = sysdb_transaction_commit(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
+ goto done;
+ }
+ in_transaction = false;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
+ }
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+struct ldb_dn *sysdb_host_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name)
+{
+ errno_t ret;
+ char *clean_name;
+ struct ldb_dn *dn;
+
+ ret = sysdb_dn_sanitize(NULL, name, &clean_name);
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ dn = ldb_dn_new_fmt(mem_ctx, domain->sysdb->ldb, SYSDB_TMPL_IP_HOST,
+ clean_name, domain->name);
+ talloc_free(clean_name);
+
+ return dn;
+}
+
+errno_t sysdb_host_add(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *primary_name,
+ const char **aliases,
+ const char **addresses,
+ struct ldb_dn **dn)
+{
+ errno_t ret;
+ int lret;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ unsigned long i;
+ size_t len;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Adding host [%s] to domain [%s]\n",
+ primary_name, domain->name);
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* host dn */
+ msg->dn = sysdb_host_dn(msg, domain, primary_name);
+ if (msg->dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Objectclass */
+ ret = sysdb_add_string(msg, SYSDB_OBJECTCLASS, SYSDB_HOST_CLASS);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* Set the primary name */
+ ret = sysdb_add_string(msg, SYSDB_NAME, primary_name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* If this iphost has any aliases, include them */
+ if (aliases != NULL && aliases[0] != NULL) {
+ /* Set the name aliases */
+ lret = ldb_msg_add_empty(msg, SYSDB_NAME_ALIAS,
+ LDB_FLAG_MOD_ADD, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ len = talloc_array_length(aliases);
+ for (i = 0; i < len && aliases[i] != NULL ; i++) {
+ lret = ldb_msg_add_string(msg, SYSDB_NAME_ALIAS, aliases[i]);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ }
+ }
+
+ /* Set the addresses */
+ lret = ldb_msg_add_empty(msg, SYSDB_IP_HOST_ATTR_ADDRESS,
+ LDB_FLAG_MOD_ADD, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ len = talloc_array_length(addresses);
+ for (i = 0; i < len && addresses[i] != NULL; i++) {
+ char *addr = NULL;
+
+ ret = sss_canonicalize_ip_address(msg, addresses[i], &addr);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to canonicalize address [%s]: %s\n",
+ addresses[i], sss_strerror(ret));
+ goto done;
+ }
+
+ lret = ldb_msg_add_string(msg, SYSDB_IP_HOST_ATTR_ADDRESS, addr);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ }
+
+ /* creation time */
+ ret = sysdb_add_ulong(msg, SYSDB_CREATE_TIME, (unsigned long)time(NULL));
+ if (ret != 0) {
+ goto done;
+ }
+
+ lret = ldb_add(domain->sysdb->ldb, msg);
+ ret = sysdb_error_to_errno(lret);
+
+ if (ret == EOK && dn != NULL) {
+ *dn = talloc_steal(mem_ctx, msg->dn);
+ }
+
+done:
+ if (ret != 0) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t
+sysdb_host_update(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char **aliases,
+ const char **addresses)
+{
+ errno_t ret;
+ struct ldb_message *msg;
+ int lret;
+ unsigned int i;
+ size_t len;
+
+ if (dn == NULL || addresses[0] == NULL) {
+ return EINVAL;
+ }
+
+ msg = ldb_msg_new(NULL);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = dn;
+
+ if (aliases != NULL && aliases[0] != NULL) {
+ /* Update the aliases */
+ lret = ldb_msg_add_empty(msg, SYSDB_NAME_ALIAS, SYSDB_MOD_REP, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ len = talloc_array_length(aliases);
+ for (i = 0; i < len && aliases[i] != NULL; i++) {
+ lret = ldb_msg_add_string(msg, SYSDB_NAME_ALIAS, aliases[i]);
+ if (lret != LDB_SUCCESS) {
+ ret = EINVAL;
+ goto done;
+ }
+ }
+ }
+
+ /* Update the addresses */
+ lret = ldb_msg_add_empty(msg, SYSDB_IP_HOST_ATTR_ADDRESS,
+ SYSDB_MOD_REP, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ len = talloc_array_length(addresses);
+ for (i = 0; i < len && addresses[i] != NULL; i++) {
+ lret = ldb_msg_add_string(msg, SYSDB_IP_HOST_ATTR_ADDRESS,
+ addresses[i]);
+ if (lret != LDB_SUCCESS) {
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ lret = ldb_modify(sysdb->ldb, msg);
+ if (lret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "ldb_modify failed: [%s](%d)[%s]\n",
+ ldb_strerror(lret), lret, ldb_errstring(sysdb->ldb));
+ }
+ ret = sysdb_error_to_errno(lret);
+
+done:
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_free(msg);
+ return ret;
+}
+
+
+errno_t
+sysdb_host_remove_alias(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char *alias)
+{
+ errno_t ret;
+ struct ldb_message *msg;
+ int lret;
+
+ msg = ldb_msg_new(NULL);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = dn;
+
+ ret = sysdb_delete_string(msg, SYSDB_NAME_ALIAS, alias);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ lret = ldb_modify(sysdb->ldb, msg);
+ if (lret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "ldb_modify failed: [%s](%d)[%s]\n",
+ ldb_strerror(lret), lret, ldb_errstring(sysdb->ldb));
+ }
+ ret = sysdb_error_to_errno(lret);
+
+done:
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_zfree(msg);
+ return ret;
+}
+
+
+errno_t sysdb_host_delete(struct sss_domain_info *domain,
+ const char *name,
+ const char *address)
+{
+ errno_t ret, sret;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ unsigned int i;
+ bool in_transaction = false;
+ struct sysdb_ctx *sysdb = domain->sysdb;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Deleting host [%s] - [%s] from domain [%s]\n",
+ name, address, domain->name);
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_transaction_start(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+ goto done;
+ }
+
+ in_transaction = true;
+
+ if (name != NULL) {
+ ret = sysdb_gethostbyname(tmp_ctx, domain, name, &res);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+
+ if (ret == ENOENT) {
+ /* Doesn't exist in the DB. Nothing to do */
+ ret = EOK;
+ goto done;
+ }
+ } else if (address != NULL) {
+ ret = sysdb_gethostbyaddr(tmp_ctx, domain, address, &res);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+
+ if (ret == ENOENT) {
+ /* Doesn't exist in the DB. Nothing to do */
+ ret = EOK;
+ goto done;
+ }
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Host name or address needed\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ /* There should only be one matching entry,
+ * but if there are multiple, we should delete
+ * them all to de-corrupt the DB.
+ */
+ for (i = 0; i < res->count; i++) {
+ ret = sysdb_delete_entry(sysdb, res->msgs[i]->dn, false);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ ret = sysdb_transaction_commit(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
+ goto done;
+ }
+ in_transaction = false;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not cancel transaction\n");
+ }
+ }
+
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_search_hosts(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter,
+ const char **attrs,
+ size_t *msgs_count,
+ struct ldb_message ***msgs)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *basedn;
+ char *filter;
+ int ret;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Searching hosts with subfilter [%s] in "
+ "domain [%s]\n", sub_filter, domain->name);
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to allocate memory\n");
+ return ENOMEM;
+ }
+
+ basedn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb,
+ SYSDB_TMPL_IP_HOST_BASE, domain->name);
+ if (basedn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to build base dn\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, "(&(%s)%s)", SYSDB_IP_HOST_CLASS_FILTER,
+ sub_filter);
+ if (filter == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to build filter\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Searching hosts with filter [%s] in "
+ "domain [%s]\n", filter, domain->name);
+
+ ret = sysdb_search_entry(mem_ctx, domain->sysdb, basedn,
+ LDB_SCOPE_SUBTREE, filter, attrs,
+ msgs_count, msgs);
+ if (ret) {
+ goto fail;
+ }
+
+ talloc_free(tmp_ctx);
+
+ return EOK;
+
+fail:
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "No such entry\n");
+ }
+ else if (ret) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+sysdb_enumhostent(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result **_res)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = {
+ SYSDB_NAME,
+ SYSDB_NAME_ALIAS,
+ SYSDB_IP_HOST_ATTR_ADDRESS,
+ NULL,
+ };
+ struct ldb_result *res = NULL;
+ struct ldb_message **msgs;
+ size_t msgs_count;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_search_hosts(mem_ctx, domain, "",
+ attrs, &msgs_count, &msgs);
+ if (ret == EOK) {
+ res = talloc_zero(mem_ctx, struct ldb_result);
+ if (res == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ res->count = msgs_count;
+ res->msgs = talloc_steal(res, msgs);
+ }
+
+ *_res = res;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
diff --git a/src/db/sysdb_iphosts.h b/src/db/sysdb_iphosts.h
new file mode 100644
index 0000000..7edf7d4
--- /dev/null
+++ b/src/db/sysdb_iphosts.h
@@ -0,0 +1,88 @@
+/*
+ SSSD
+
+ Authors:
+ Samuel Cabrero <scabrero@suse.com>
+
+ Copyright (C) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+
+ 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 SYSDB_IP_HOSTS_H_
+#define SYSDB_IP_HOSTS_H_
+
+#include "db/sysdb.h"
+
+#define SYSDB_IP_HOST_CLASS "host"
+#define SYSDB_IP_HOST_CONTAINER "cn=hosts"
+
+#define SYSDB_IP_HOST_CLASS_FILTER "objectclass="SYSDB_IP_HOST_CLASS
+#define SYSDB_IP_HOST_ATTR_ADDRESS "ipHostNumber"
+
+#define SYSDB_TMPL_IP_HOST_BASE SYSDB_IP_HOST_CONTAINER",cn=%s,"SYSDB_BASE
+#define SYSDB_TMPL_IP_HOST SYSDB_NAME"=%s,"SYSDB_TMPL_IP_HOST_BASE
+
+#define SYSDB_IP_HOST_BYNAME_SUBFILTER \
+ "(|("SYSDB_NAME"=%s)("SYSDB_NAME_ALIAS"=%s))"
+#define SYSDB_IP_HOST_BYADDR_SUBFILTER \
+ "("SYSDB_IP_HOST_ATTR_ADDRESS"=%s)"
+
+errno_t sysdb_gethostbyname(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **_res);
+
+errno_t sysdb_gethostbyaddr(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *address,
+ struct ldb_result **_res);
+
+errno_t
+sysdb_store_host(struct sss_domain_info *domain,
+ const char *primary_name,
+ const char **aliases,
+ const char **addresses,
+ struct sysdb_attrs *extra_attrs,
+ char **remove_attrs,
+ uint64_t cache_timeout,
+ time_t now);
+
+struct ldb_dn *sysdb_host_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name);
+
+errno_t sysdb_host_add(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *primary_name,
+ const char **aliases,
+ const char **addresses,
+ struct ldb_dn **dn);
+
+errno_t sysdb_host_delete(struct sss_domain_info *domain,
+ const char *name,
+ const char *address);
+
+errno_t sysdb_search_hosts(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter,
+ const char **attrs,
+ size_t *msgs_count,
+ struct ldb_message ***msgs);
+
+errno_t sysdb_enumhostent(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result **_res);
+
+#endif /* SYSDB_IP_HOSTS_H_ */
diff --git a/src/db/sysdb_ipnetworks.c b/src/db/sysdb_ipnetworks.c
new file mode 100644
index 0000000..9da4d9b
--- /dev/null
+++ b/src/db/sysdb_ipnetworks.c
@@ -0,0 +1,869 @@
+/*
+ SSSD
+
+ Authors:
+ Samuel Cabrero <scabrero@suse.com>
+
+ Copyright (C) 2020 SUSE LINUX GmbH, Nuernberg, Germany.
+
+ 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 <arpa/inet.h>
+
+#include "db/sysdb.h"
+#include "db/sysdb_private.h"
+#include "db/sysdb_ipnetworks.h"
+
+static errno_t
+sysdb_ipnetwork_update(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char **aliases,
+ const char *address);
+
+static errno_t
+sysdb_ipnetwork_remove_alias(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char *alias);
+
+errno_t sysdb_getipnetworkbyname(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **_res)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = {
+ SYSDB_NAME,
+ SYSDB_NAME_ALIAS,
+ SYSDB_IP_NETWORK_ATTR_NUMBER,
+ SYSDB_DEFAULT_ATTRS,
+ NULL,
+ };
+ char *sanitized_name;
+ char *subfilter;
+ struct ldb_result *res = NULL;
+ struct ldb_message **msgs;
+ size_t msgs_count;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Searching network by name [%s] in domain [%s]\n",
+ name, domain->name);
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_filter_sanitize(tmp_ctx, name, &sanitized_name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ subfilter = talloc_asprintf(tmp_ctx, SYSDB_IP_NETWORK_BYNAME_SUBFILTER,
+ sanitized_name, sanitized_name);
+ if (subfilter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_ipnetworks(tmp_ctx, domain, subfilter, attrs,
+ &msgs_count, &msgs);
+ if (ret == EOK) {
+ res = talloc_zero(mem_ctx, struct ldb_result);
+ if (res == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ res->count = msgs_count;
+ res->msgs = talloc_steal(res, msgs);
+ }
+
+ *_res = res;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+
+}
+
+errno_t sysdb_getipnetworkbyaddr(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *address,
+ struct ldb_result **_res)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = {
+ SYSDB_NAME,
+ SYSDB_NAME_ALIAS,
+ SYSDB_IP_NETWORK_ATTR_NUMBER,
+ NULL,
+ };
+ char *sanitized_address;
+ char *subfilter;
+ struct ldb_result *res = NULL;
+ struct ldb_message **msgs;
+ size_t msgs_count;
+ char *canonical_address;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ /* IP addresses are stored in canonical form, canonicalize the given
+ * address before search */
+ ret = sss_canonicalize_ip_address(tmp_ctx, address, &canonical_address);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Searching network by address [%s] in domain [%s]\n",
+ canonical_address, domain->name);
+
+ ret = sss_filter_sanitize(tmp_ctx, canonical_address, &sanitized_address);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ subfilter = talloc_asprintf(tmp_ctx, SYSDB_IP_NETWORK_BYADDR_SUBFILTER,
+ sanitized_address);
+ if (subfilter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_ipnetworks(tmp_ctx, domain, subfilter, attrs,
+ &msgs_count, &msgs);
+ if (ret == EOK) {
+ res = talloc_zero(mem_ctx, struct ldb_result);
+ if (res == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ res->count = msgs_count;
+ res->msgs = talloc_steal(res, msgs);
+ }
+
+ *_res = res;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+
+}
+
+errno_t
+sysdb_store_ipnetwork(struct sss_domain_info *domain,
+ const char *primary_name,
+ const char **aliases,
+ const char *address,
+ struct sysdb_attrs *extra_attrs,
+ char **remove_attrs,
+ uint64_t cache_timeout,
+ time_t now)
+{
+ errno_t ret;
+ errno_t sret;
+ TALLOC_CTX *tmp_ctx;
+ bool in_transaction = false;
+ struct ldb_result *res = NULL;
+ const char *name;
+ unsigned int i, j;
+ struct ldb_dn *update_dn = NULL;
+ struct sysdb_attrs *attrs;
+ struct sysdb_ctx *sysdb;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Storing network [%s] into cache, domain [%s]\n",
+ primary_name, domain->name);
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ sysdb = domain->sysdb;
+
+ ret = sysdb_transaction_start(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+ goto done;
+ }
+
+ in_transaction = true;
+
+ /* Check that the address is unique. If the address appears for any
+ * network other than the one matching the primary_name, we need to
+ * remove it so that getnetbyaddr() can work properly.
+ * Last entry saved to the cache should always "win".
+ */
+ ret = sysdb_getipnetworkbyaddr(tmp_ctx, domain, address, &res);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ } else if (ret != ENOENT) {
+ if (res->count != 1) {
+ /* Somehow the cache has multiple entries with the same
+ * address. This is corrupted. We'll delete them all to
+ * sort it out.
+ */
+ for (j = 0; j < res->count; j++) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Corrupt cache entry [%s] detected. Deleting\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[j]->dn));
+
+ ret = sysdb_delete_entry(sysdb, res->msgs[j]->dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not delete corrupt cache entry [%s]\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[j]->dn));
+ goto done;
+ }
+ }
+ } else {
+ /* Check whether this is the same name as we're currently
+ * saving to the cache.
+ */
+ name = ldb_msg_find_attr_as_string(res->msgs[0],
+ SYSDB_NAME,
+ NULL);
+ if (name == NULL || strcmp(name, primary_name) != 0) {
+ if (name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "A network with no name?\n");
+ /* Corrupted */
+ }
+
+ /* Either this is a corrupt entry or it's another network
+ * claiming ownership of this address. In order to account
+ * for address reassignments, we need to delete the old
+ * entry.
+ */
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Corrupt or replaced cache entry [%s] detected. "
+ "Deleting\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[0]->dn));
+
+ ret = sysdb_delete_entry(sysdb, res->msgs[0]->dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not delete cache entry [%s]\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[0]->dn));
+ }
+ }
+ }
+ }
+ talloc_zfree(res);
+
+ /* Address is now unique. look the network up by name to determine if
+ * we need to update existing entries or modify aliases.
+ */
+ ret = sysdb_getipnetworkbyname(tmp_ctx, domain, primary_name, &res);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ } else if (ret != ENOENT) {
+ for (i = 0; i < res->count; i++) {
+ /* Check whether this is the same name as we're currently
+ * saving to the cache.
+ */
+ name = ldb_msg_find_attr_as_string(res->msgs[i],
+ SYSDB_NAME,
+ NULL);
+ if (name == NULL) {
+ /* Corrupted */
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "A network with no name?\n");
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Corrupt cache entry [%s] detected. Deleting\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[i]->dn));
+
+ ret = sysdb_delete_entry(sysdb, res->msgs[i]->dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not delete corrupt cache entry [%s]\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[i]->dn));
+ goto done;
+ }
+ } else if (strcmp(name, primary_name) == 0) {
+ /* This is the same network name, so we need
+ * to update this entry with the values provided.
+ */
+ if (update_dn) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Two existing networks with the same name: [%s]? "
+ "Deleting both.\n",
+ primary_name);
+
+ /* Delete the entry from the previous pass */
+ ret = sysdb_delete_entry(sysdb, update_dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not delete cache entry [%s]\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ update_dn));
+ goto done;
+ }
+
+ /* Delete the new entry as well */
+ ret = sysdb_delete_entry(sysdb, res->msgs[i]->dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not delete cache entry [%s]\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[i]->dn));
+ goto done;
+ }
+
+ update_dn = NULL;
+ } else {
+ update_dn = talloc_steal(tmp_ctx, res->msgs[i]->dn);
+ }
+ } else {
+ /* Another network is claiming this name as an alias.
+ * In order to account for aliases being promoted to
+ * primary names, we need to make sure to remove the
+ * old alias entry.
+ */
+ ret = sysdb_ipnetwork_remove_alias(sysdb,
+ res->msgs[i]->dn,
+ primary_name);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+ }
+ talloc_zfree(res);
+ }
+
+ if (update_dn) {
+ /* Update the existing entry */
+ ret = sysdb_ipnetwork_update(sysdb, update_dn, aliases, address);
+ } else {
+ /* Add a new entry */
+ ret = sysdb_ipnetwork_add(tmp_ctx, domain, primary_name,
+ aliases, address, &update_dn);
+ }
+
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (extra_attrs == NULL) {
+ attrs = sysdb_new_attrs(tmp_ctx);
+ if (attrs == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ } else {
+ attrs = extra_attrs;
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_UPDATE, now);
+ if (ret) {
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE,
+ ((cache_timeout) ?
+ (now + cache_timeout) : 0));
+ if (ret) {
+ goto done;
+ }
+
+ ret = sysdb_set_entry_attr(sysdb, update_dn, attrs, SYSDB_MOD_REP);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (remove_attrs) {
+ ret = sysdb_remove_attrs(domain, primary_name,
+ SYSDB_MEMBER_IP_NETWORK,
+ remove_attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not remove missing attributes: [%s]\n",
+ strerror(ret));
+ goto done;
+ }
+ }
+
+ ret = sysdb_transaction_commit(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
+ goto done;
+ }
+ in_transaction = false;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
+ }
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+struct ldb_dn *sysdb_ipnetwork_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name)
+{
+ errno_t ret;
+ char *clean_name;
+ struct ldb_dn *dn;
+
+ ret = sysdb_dn_sanitize(NULL, name, &clean_name);
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ dn = ldb_dn_new_fmt(mem_ctx, domain->sysdb->ldb, SYSDB_TMPL_IP_NETWORK,
+ clean_name, domain->name);
+ talloc_free(clean_name);
+
+ return dn;
+}
+
+errno_t sysdb_ipnetwork_add(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *primary_name,
+ const char **aliases,
+ const char *address,
+ struct ldb_dn **dn)
+{
+ errno_t ret;
+ int lret;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ unsigned long i;
+ size_t len;
+ char *canonical_addr = NULL;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Adding network [%s] to domain [%s]\n",
+ primary_name, domain->name);
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* network dn */
+ msg->dn = sysdb_ipnetwork_dn(msg, domain, primary_name);
+ if (msg->dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Objectclass */
+ ret = sysdb_add_string(msg, SYSDB_OBJECTCLASS, SYSDB_IP_NETWORK_CLASS);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* Set the primary name */
+ ret = sysdb_add_string(msg, SYSDB_NAME, primary_name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* If this network has any aliases, include them */
+ if (aliases != NULL && aliases[0] != NULL) {
+ /* Set the name aliases */
+ lret = ldb_msg_add_empty(msg, SYSDB_NAME_ALIAS,
+ LDB_FLAG_MOD_ADD, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ len = talloc_array_length(aliases);
+ for (i = 0; i < len && aliases[i] != NULL ; i++) {
+ lret = ldb_msg_add_string(msg, SYSDB_NAME_ALIAS, aliases[i]);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ }
+ }
+
+ /* Set the address */
+ ret = sss_canonicalize_ip_address(msg, address, &canonical_addr);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to canonicalize network address [%s]: %s\n",
+ address, sss_strerror(ret));
+ goto done;
+ }
+
+ lret = ldb_msg_add_string(msg, SYSDB_IP_NETWORK_ATTR_NUMBER,
+ canonical_addr);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ /* creation time */
+ ret = sysdb_add_ulong(msg, SYSDB_CREATE_TIME, (unsigned long)time(NULL));
+ if (ret != 0) {
+ goto done;
+ }
+
+ lret = ldb_add(domain->sysdb->ldb, msg);
+ ret = sysdb_error_to_errno(lret);
+
+ if (ret == EOK && dn != NULL) {
+ *dn = talloc_steal(mem_ctx, msg->dn);
+ }
+
+done:
+ if (ret != 0) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_ipnetwork_delete(struct sss_domain_info *domain,
+ const char *name,
+ const char *address)
+{
+ errno_t ret, sret;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ unsigned int i;
+ bool in_transaction = false;
+ struct sysdb_ctx *sysdb = domain->sysdb;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Deleting network [%s] - [%s] from domain [%s]\n",
+ name, address, domain->name);
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_transaction_start(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+ goto done;
+ }
+
+ in_transaction = true;
+
+ if (name != NULL) {
+ ret = sysdb_getipnetworkbyname(tmp_ctx, domain, name, &res);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+
+ if (ret == ENOENT) {
+ /* Doesn't exist in the DB. Nothing to do */
+ ret = EOK;
+ goto done;
+ }
+ } else if (address != NULL) {
+ ret = sysdb_getipnetworkbyaddr(tmp_ctx, domain, address, &res);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+
+ if (ret == ENOENT) {
+ /* Doesn't exist in the DB. Nothing to do */
+ ret = EOK;
+ goto done;
+ }
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Deleting network failed. Network name or address needed\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ /* There should only be one matching entry,
+ * but if there are multiple, we should delete
+ * them all to de-corrupt the DB.
+ */
+ for (i = 0; i < res->count; i++) {
+ ret = sysdb_delete_entry(sysdb, res->msgs[i]->dn, false);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ ret = sysdb_transaction_commit(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
+ goto done;
+ }
+ in_transaction = false;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not cancel transaction\n");
+ }
+ }
+
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_search_ipnetworks(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter,
+ const char **attrs,
+ size_t *msgs_count,
+ struct ldb_message ***msgs)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *basedn;
+ char *filter;
+ int ret;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Searching networks with subfilter [%s] in "
+ "domain [%s]\n", sub_filter, domain->name);
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to allocate memory\n");
+ return ENOMEM;
+ }
+
+ basedn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb,
+ SYSDB_TMPL_IP_NETWORK_BASE, domain->name);
+ if (basedn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to build base dn\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, "(&(%s)%s)",
+ SYSDB_IP_NETWORK_CLASS_FILTER,
+ sub_filter);
+ if (filter == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to build filter\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Searching networks with filter [%s] in "
+ "domain [%s]\n", filter, domain->name);
+
+ ret = sysdb_search_entry(mem_ctx, domain->sysdb, basedn,
+ LDB_SCOPE_SUBTREE, filter, attrs,
+ msgs_count, msgs);
+ if (ret) {
+ goto fail;
+ }
+
+ talloc_free(tmp_ctx);
+
+ return EOK;
+
+fail:
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "No such entry\n");
+ }
+ else if (ret) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sysdb_ipnetwork_update(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char **aliases,
+ const char *address)
+{
+ errno_t ret;
+ struct ldb_message *msg;
+ int lret;
+ unsigned int i;
+ size_t len;
+ char *canonical_addr = NULL;
+
+ if (dn == NULL || address == NULL) {
+ return EINVAL;
+ }
+
+ msg = ldb_msg_new(NULL);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = dn;
+
+ if (aliases != NULL && aliases[0] != NULL) {
+ /* Update the aliases */
+ lret = ldb_msg_add_empty(msg, SYSDB_NAME_ALIAS, SYSDB_MOD_REP, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ len = talloc_array_length(aliases);
+ for (i = 0; i < len && aliases[i] != NULL; i++) {
+ lret = ldb_msg_add_string(msg, SYSDB_NAME_ALIAS, aliases[i]);
+ if (lret != LDB_SUCCESS) {
+ ret = EINVAL;
+ goto done;
+ }
+ }
+ }
+
+ /* Update the addresses */
+ ret = sss_canonicalize_ip_address(msg, address, &canonical_addr);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to canonicalize network address [%s]: %s\n",
+ address, sss_strerror(ret));
+ goto done;
+ }
+
+ lret = ldb_msg_add_empty(msg, SYSDB_IP_NETWORK_ATTR_NUMBER,
+ SYSDB_MOD_REP, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ lret = ldb_msg_add_string(msg, SYSDB_IP_NETWORK_ATTR_NUMBER,
+ canonical_addr);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ lret = ldb_modify(sysdb->ldb, msg);
+ if (lret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "ldb_modify failed: [%s](%d)[%s]\n",
+ ldb_strerror(lret), lret, ldb_errstring(sysdb->ldb));
+ }
+ ret = sysdb_error_to_errno(lret);
+
+done:
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_free(msg);
+ return ret;
+}
+
+errno_t
+sysdb_ipnetwork_remove_alias(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char *alias)
+{
+ errno_t ret;
+ struct ldb_message *msg;
+ int lret;
+
+ msg = ldb_msg_new(NULL);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = dn;
+
+ ret = sysdb_delete_string(msg, SYSDB_NAME_ALIAS, alias);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ lret = ldb_modify(sysdb->ldb, msg);
+ if (lret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "ldb_modify failed: [%s](%d)[%s]\n",
+ ldb_strerror(lret), lret, ldb_errstring(sysdb->ldb));
+ }
+ ret = sysdb_error_to_errno(lret);
+
+done:
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_zfree(msg);
+ return ret;
+}
+
+errno_t
+sysdb_enumnetent(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result **_res)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = {
+ SYSDB_NAME,
+ SYSDB_NAME_ALIAS,
+ SYSDB_IP_NETWORK_ATTR_NUMBER,
+ NULL,
+ };
+ struct ldb_result *res = NULL;
+ struct ldb_message **msgs;
+ size_t msgs_count;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_search_ipnetworks(tmp_ctx, domain, "", attrs,
+ &msgs_count, &msgs);
+ if (ret == EOK) {
+ res = talloc_zero(mem_ctx, struct ldb_result);
+ if (res == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ res->count = msgs_count;
+ res->msgs = talloc_steal(res, msgs);
+ }
+
+ *_res = res;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
diff --git a/src/db/sysdb_ipnetworks.h b/src/db/sysdb_ipnetworks.h
new file mode 100644
index 0000000..e2f9527
--- /dev/null
+++ b/src/db/sysdb_ipnetworks.h
@@ -0,0 +1,88 @@
+/*
+ SSSD
+
+ Authors:
+ Samuel Cabrero <scabrero@suse.com>
+
+ Copyright (C) 2020 SUSE LINUX GmbH, Nuernberg, Germany.
+
+ 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 SYSDB_IP_NETWORKS_H_
+#define SYSDB_IP_NETWORKS_H_
+
+#include "db/sysdb.h"
+
+#define SYSDB_IP_NETWORK_CLASS "network"
+#define SYSDB_IP_NETWORK_CONTAINER "cn=networks"
+
+#define SYSDB_IP_NETWORK_CLASS_FILTER "objectclass="SYSDB_IP_NETWORK_CLASS
+#define SYSDB_IP_NETWORK_ATTR_NUMBER "ipNetworkNumber"
+
+#define SYSDB_TMPL_IP_NETWORK_BASE SYSDB_IP_NETWORK_CONTAINER",cn=%s,"SYSDB_BASE
+#define SYSDB_TMPL_IP_NETWORK SYSDB_NAME"=%s,"SYSDB_TMPL_IP_NETWORK_BASE
+
+#define SYSDB_IP_NETWORK_BYNAME_SUBFILTER \
+ "(|("SYSDB_NAME"=%s)("SYSDB_NAME_ALIAS"=%s))"
+#define SYSDB_IP_NETWORK_BYADDR_SUBFILTER \
+ "("SYSDB_IP_NETWORK_ATTR_NUMBER"=%s)"
+
+errno_t sysdb_getipnetworkbyname(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **_res);
+
+errno_t sysdb_getipnetworkbyaddr(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *address,
+ struct ldb_result **_res);
+
+errno_t
+sysdb_store_ipnetwork(struct sss_domain_info *domain,
+ const char *primary_name,
+ const char **aliases,
+ const char *address,
+ struct sysdb_attrs *extra_attrs,
+ char **remove_attrs,
+ uint64_t cache_timeout,
+ time_t now);
+
+struct ldb_dn *sysdb_ipnetwork_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name);
+
+errno_t sysdb_ipnetwork_add(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *primary_name,
+ const char **aliases,
+ const char *address,
+ struct ldb_dn **dn);
+
+errno_t sysdb_ipnetwork_delete(struct sss_domain_info *domain,
+ const char *name,
+ const char *address);
+
+errno_t sysdb_search_ipnetworks(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter,
+ const char **attrs,
+ size_t *msgs_count,
+ struct ldb_message ***msgs);
+
+errno_t sysdb_enumnetent(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result **_res);
+
+#endif /* SYSDB_IP_NETWORKS_H_ */
diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
new file mode 100644
index 0000000..3331d46
--- /dev/null
+++ b/src/db/sysdb_ops.c
@@ -0,0 +1,5854 @@
+/*
+ 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 <stdbool.h>
+
+#include "util/util.h"
+#include "db/sysdb_private.h"
+#include "db/sysdb_services.h"
+#include "db/sysdb_autofs.h"
+#include "db/sysdb_iphosts.h"
+#include "db/sysdb_ipnetworks.h"
+#include "util/crypto/sss_crypto.h"
+#include "util/cert.h"
+#include <time.h>
+
+#define SSS_SYSDB_NO_CACHE 0x0
+#define SSS_SYSDB_CACHE 0x1
+#define SSS_SYSDB_TS_CACHE 0x2
+#define SSS_SYSDB_BOTH_CACHE (SSS_SYSDB_CACHE | SSS_SYSDB_TS_CACHE)
+
+/*
+ * The wrapper around ldb_modify that optionally uses
+ * LDB_CONTROL_PERMISSIVE_MODIFY_OID so that on adds entries that already
+ * exist are skipped and similarly entries that are missing are ignored
+ * on deletes.
+ *
+ * Please note this function returns LDB error codes, not sysdb error
+ * codes on purpose, see usage in callers!
+ */
+int sss_ldb_modify(struct ldb_context *ldb,
+ struct ldb_message *msg,
+ bool permissive)
+{
+ struct ldb_request *req;
+ int ret;
+ int cancel_ret;
+ bool in_transaction = false;
+
+ ret = ldb_build_mod_req(&req, ldb, ldb,
+ msg,
+ NULL,
+ NULL,
+ ldb_op_default_callback,
+ NULL);
+
+ if (ret != LDB_SUCCESS) return ret;
+
+ if (permissive) {
+ ret = ldb_request_add_control(req, LDB_CONTROL_PERMISSIVE_MODIFY_OID,
+ false, NULL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(req);
+ return ret;
+ }
+ }
+
+ ret = ldb_transaction_start(ldb);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to start ldb transaction [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ in_transaction = true;
+
+ ret = ldb_request(ldb, req);
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ if (ret != LDB_SUCCESS) {
+ goto done;
+ }
+ }
+
+ ret = ldb_transaction_commit(ldb);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to commit ldb transaction [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ in_transaction = false;
+
+ ret = LDB_SUCCESS;
+
+done:
+ if (in_transaction) {
+ cancel_ret = ldb_transaction_cancel(ldb);
+ if (cancel_ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to cancel ldb transaction [%d]: %s\n",
+ cancel_ret, sss_strerror(cancel_ret));
+ }
+ }
+
+ talloc_free(req);
+
+ /* Please note this function returns LDB error codes, not sysdb error
+ * codes on purpose, see usage in callers!
+ */
+ return ret;
+}
+
+int sss_ldb_modify_permissive(struct ldb_context *ldb,
+ struct ldb_message *msg)
+{
+ return sss_ldb_modify(ldb, msg, true);
+}
+
+#define ERROR_OUT(v, r, l) do { v = r; goto l; } while(0)
+
+
+/* =Remove-Entry-From-Sysdb=============================================== */
+static int sysdb_delete_cache_entry(struct ldb_context *ldb,
+ struct ldb_dn *dn,
+ bool ignore_not_found)
+{
+ int ret;
+
+ ret = ldb_delete(ldb, dn);
+ switch (ret) {
+ case LDB_SUCCESS:
+ return EOK;
+ case LDB_ERR_NO_SUCH_OBJECT:
+ if (ignore_not_found) {
+ return EOK;
+ }
+ /* fall through */
+ SSS_ATTRIBUTE_FALLTHROUGH;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "LDB Error: %s (%d); error message: [%s]\n",
+ ldb_strerror(ret), ret, ldb_errstring(ldb));
+ return sysdb_error_to_errno(ret);
+ }
+}
+
+static int sysdb_delete_ts_entry(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn)
+{
+ if (sysdb->ldb_ts == NULL) {
+ return EOK;
+ }
+
+ return sysdb_delete_cache_entry(sysdb->ldb_ts, dn, true);
+}
+
+int sysdb_delete_entry(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ bool ignore_not_found)
+{
+ errno_t ret;
+ errno_t tret;
+
+ ret = sysdb_delete_cache_entry(sysdb->ldb, dn, ignore_not_found);
+ if (ret == EOK) {
+ tret = sysdb_delete_ts_entry(sysdb, dn);
+ if (tret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "sysdb_delete_ts_entry failed: %d\n", tret);
+ /* Not fatal */
+ }
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_delete_cache_entry failed: %d\n", ret);
+ }
+
+ return ret;
+}
+
+/* =Remove-Subentries-From-Sysdb=========================================== */
+
+int sysdb_delete_recursive_with_filter(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ bool ignore_not_found,
+ const char *filter)
+{
+ const char *no_attrs[] = { NULL };
+ struct ldb_message **msgs;
+ size_t msgs_count;
+ int ret;
+ int i;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = ldb_transaction_start(sysdb->ldb);
+ if (ret) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = sysdb_search_entry(tmp_ctx, sysdb, dn,
+ LDB_SCOPE_SUBTREE, filter,
+ no_attrs, &msgs_count, &msgs);
+ if (ret) {
+ if (ignore_not_found && ret == ENOENT) {
+ ret = EOK;
+ }
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Search error: %d (%s)\n",
+ ret, strerror(ret));
+ }
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "Found [%zu] items to delete.\n", msgs_count);
+
+ qsort(msgs, msgs_count,
+ sizeof(struct ldb_message *), compare_ldb_dn_comp_num);
+
+ for (i = 0; i < msgs_count; i++) {
+ DEBUG(SSSDBG_TRACE_ALL, "Trying to delete [%s].\n",
+ ldb_dn_get_linearized(msgs[i]->dn));
+
+ ret = sysdb_delete_entry(sysdb, msgs[i]->dn, false);
+ if (ret) {
+ goto done;
+ }
+ }
+
+done:
+ if (ret == EOK) {
+ ret = ldb_transaction_commit(sysdb->ldb);
+ ret = sysdb_error_to_errno(ret);
+ } else {
+ ldb_transaction_cancel(sysdb->ldb);
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_delete_recursive(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ bool ignore_not_found)
+{
+ return sysdb_delete_recursive_with_filter(sysdb, dn, ignore_not_found,
+ "(distinguishedName=*)");
+}
+
+
+/* =Search-Entry========================================================== */
+
+int sysdb_cache_search_entry(TALLOC_CTX *mem_ctx,
+ struct ldb_context *ldb,
+ struct ldb_dn *base_dn,
+ enum ldb_scope scope,
+ const char *filter,
+ const char **attrs,
+ size_t *_msgs_count,
+ struct ldb_message ***_msgs)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_search(ldb, tmp_ctx, &res,
+ base_dn, scope, attrs,
+ filter?"%s":NULL, filter);
+ if (ret != EOK) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ *_msgs_count = res->count;
+ *_msgs = talloc_steal(mem_ctx, res->msgs);
+
+ if (res->count == 0) {
+ ret = ENOENT;
+ goto done;
+ }
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_search_entry(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct ldb_dn *base_dn,
+ enum ldb_scope scope,
+ const char *filter,
+ const char **attrs,
+ size_t *_msgs_count,
+ struct ldb_message ***_msgs)
+{
+ errno_t ret;
+
+ ret = sysdb_cache_search_entry(mem_ctx, sysdb->ldb, base_dn, scope,
+ filter, attrs, _msgs_count, _msgs);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return sysdb_merge_msg_list_ts_attrs(sysdb, *_msgs_count, *_msgs,
+ attrs);
+}
+
+int sysdb_search_ts_entry(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct ldb_dn *base_dn,
+ enum ldb_scope scope,
+ const char *filter,
+ const char **attrs,
+ size_t *_msgs_count,
+ struct ldb_message ***_msgs)
+{
+ if (sysdb->ldb_ts == NULL) {
+ if (_msgs_count != NULL) {
+ *_msgs_count = 0;
+ }
+ if (_msgs != NULL) {
+ *_msgs = NULL;
+ }
+
+ return EOK;
+ }
+
+ return sysdb_cache_search_entry(mem_ctx, sysdb->ldb_ts, base_dn, scope,
+ filter, attrs, _msgs_count, _msgs);
+}
+
+/* =Search-Entry-by-SID-string============================================ */
+
+int sysdb_search_entry_by_sid_str(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *search_base,
+ const char *filter_str,
+ const char *sid_str,
+ const char **attrs,
+ struct ldb_message **msg)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *def_attrs[] = { SYSDB_NAME, SYSDB_SID_STR, NULL };
+ struct ldb_message **msgs = NULL;
+ struct ldb_dn *basedn;
+ size_t msgs_count = 0;
+ char *filter;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ basedn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb,
+ search_base, domain->name);
+ if (!basedn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, filter_str, sid_str);
+ if (!filter) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_entry(tmp_ctx, domain->sysdb, basedn, LDB_SCOPE_SUBTREE,
+ filter, attrs?attrs:def_attrs, &msgs_count,
+ &msgs);
+ if (ret) {
+ goto done;
+ }
+
+ *msg = talloc_steal(mem_ctx, msgs[0]);
+
+done:
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No such entry\n");
+ }
+ else if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+/* =Search-User-by-[UID/SID/NAME]============================================= */
+
+static errno_t cleanup_dn_filter(TALLOC_CTX *mem_ctx,
+ struct ldb_result *ts_res,
+ const char *object_class,
+ const char *filter,
+ char **_dn_filter)
+{
+ TALLOC_CTX *tmp_ctx;
+ char *dn_filter;
+ char *sanitized_linearized_dn = NULL;
+ errno_t ret;
+
+ if (ts_res->count == 0) {
+ *_dn_filter = NULL;
+ return EOK;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ dn_filter = talloc_asprintf(tmp_ctx, "(&(%s)%s(|", object_class, filter);
+ if (dn_filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (size_t i = 0; i < ts_res->count; i++) {
+ ret = sss_filter_sanitize(tmp_ctx,
+ ldb_dn_get_linearized(ts_res->msgs[i]->dn),
+ &sanitized_linearized_dn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sss_filter_sanitize() failed: (%s) [%d]\n",
+ sss_strerror(ret), ret);
+ goto done;
+ }
+ dn_filter = talloc_asprintf_append(
+ dn_filter,
+ "(%s=%s)",
+ SYSDB_DN,
+ sanitized_linearized_dn);
+ if (dn_filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ dn_filter = talloc_asprintf_append(dn_filter, "))");
+ if (dn_filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ *_dn_filter = talloc_steal(mem_ctx, dn_filter);
+ ret = EOK;
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static int sysdb_search_by_name(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ enum sysdb_obj_type type,
+ const char **attrs,
+ struct ldb_message **msg)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *def_attrs[] = { SYSDB_NAME, NULL, NULL };
+ const char *filter_tmpl = NULL;
+ struct ldb_message **msgs = NULL;
+ struct ldb_dn *basedn;
+ size_t msgs_count = 0;
+ char *sanitized_name;
+ char *lc_sanitized_name;
+ char *filter;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ switch (type) {
+ case SYSDB_USER:
+ def_attrs[1] = SYSDB_UIDNUM;
+ filter_tmpl = SYSDB_PWNAM_FILTER;
+ basedn = sysdb_user_base_dn(tmp_ctx, domain);
+ break;
+ case SYSDB_GROUP:
+ def_attrs[1] = SYSDB_GIDNUM;
+ if (sss_domain_is_mpg(domain)) {
+ /* When searching a group by name in a MPG domain, we also
+ * need to search the user space in order to be able to match
+ * a user private group/
+ */
+ filter_tmpl = SYSDB_GRNAM_MPG_FILTER;
+ basedn = sysdb_domain_dn(tmp_ctx, domain);
+ } else {
+ filter_tmpl = SYSDB_GRNAM_FILTER;
+ basedn = sysdb_group_base_dn(tmp_ctx, domain);
+ }
+ break;
+ default:
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (!basedn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_filter_sanitize_for_dom(tmp_ctx, name, domain, &sanitized_name,
+ &lc_sanitized_name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, filter_tmpl, lc_sanitized_name,
+ sanitized_name, sanitized_name);
+ if (!filter) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_entry(tmp_ctx, domain->sysdb, basedn, LDB_SCOPE_SUBTREE,
+ filter, attrs?attrs:def_attrs,
+ &msgs_count, &msgs);
+ if (ret) {
+ goto done;
+ }
+
+ ret = sysdb_merge_msg_list_ts_attrs(domain->sysdb, msgs_count, msgs, attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot retrieve timestamp attributes\n");
+ }
+
+ *msg = talloc_steal(mem_ctx, msgs[0]);
+
+done:
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No such entry\n");
+ }
+ else if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_search_user_by_name(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char **attrs,
+ struct ldb_message **msg)
+{
+ return sysdb_search_by_name(mem_ctx, domain, name, SYSDB_USER, attrs, msg);
+}
+
+int sysdb_search_user_by_uid(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ uid_t uid,
+ const char **attrs,
+ struct ldb_message **msg)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *def_attrs[] = { SYSDB_NAME, SYSDB_UIDNUM, NULL };
+ struct ldb_message **msgs = NULL;
+ struct ldb_dn *basedn;
+ size_t msgs_count = 0;
+ char *filter;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ basedn = sysdb_user_base_dn(tmp_ctx, domain);
+ if (!basedn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, SYSDB_PWUID_FILTER, (unsigned long)uid);
+ if (!filter) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Use SUBTREE scope here, not ONELEVEL
+ * There is a bug in LDB that makes ONELEVEL searches extremely
+ * slow (it ignores indexing)
+ */
+ ret = sysdb_search_entry(tmp_ctx, domain->sysdb, basedn,
+ LDB_SCOPE_SUBTREE, filter,
+ attrs?attrs:def_attrs, &msgs_count, &msgs);
+ if (ret) {
+ goto done;
+ }
+
+ *msg = talloc_steal(mem_ctx, msgs[0]);
+
+done:
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No such entry\n");
+ }
+ else if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_search_user_by_sid_str(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sid_str,
+ const char **attrs,
+ struct ldb_message **msg)
+{
+
+ return sysdb_search_entry_by_sid_str(mem_ctx, domain,
+ SYSDB_TMPL_USER_BASE,
+ SYSDB_PWSID_FILTER,
+ sid_str, attrs, msg);
+}
+
+int sysdb_search_user_by_upn_res(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ bool domain_scope,
+ const char *upn,
+ const char **attrs,
+ struct ldb_result **out_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ struct ldb_dn *base_dn;
+ int ret;
+ const char *def_attrs[] = { SYSDB_NAME, SYSDB_UPN, SYSDB_CANONICAL_UPN,
+ SYSDB_USER_EMAIL, NULL };
+ char *sanitized;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_filter_sanitize(tmp_ctx, upn, &sanitized);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_filter_sanitize failed.\n");
+ goto done;
+ }
+
+ if (domain_scope == true) {
+ base_dn = sysdb_user_base_dn(tmp_ctx, domain);
+ } else {
+ base_dn = sysdb_base_dn(domain->sysdb, tmp_ctx);
+ }
+ if (base_dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res,
+ base_dn, LDB_SCOPE_SUBTREE, attrs ? attrs : def_attrs,
+ SYSDB_PWUPN_FILTER, sanitized, sanitized, sanitized);
+ if (ret != EOK) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ if (res->count == 0) {
+ /* set result anyway */
+ *out_res = talloc_steal(mem_ctx, res);
+ ret = ENOENT;
+ goto done;
+ } else if (res->count > 1) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Search for upn [%s] returns more than one result. One of the "
+ "possible reasons can be that several users share the same "
+ "email address.\n", upn);
+ ret = EINVAL;
+ goto done;
+ }
+
+ /* Merge in the timestamps from the fast ts db */
+ ret = sysdb_merge_res_ts_attrs(domain->sysdb, res,
+ attrs ? attrs : def_attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n");
+ /* non-fatal */
+ }
+
+ *out_res = talloc_steal(mem_ctx, res);
+ ret = EOK;
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_search_user_by_upn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ bool domain_scope,
+ const char *upn,
+ const char **attrs,
+ struct ldb_message **msg)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_user_by_upn_res(tmp_ctx, domain, domain_scope, upn, attrs, &res);
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No entry with upn [%s] found.\n", upn);
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ goto done;
+ }
+
+ *msg = talloc_steal(mem_ctx, res->msgs[0]);
+
+ ret = EOK;
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+/* =Search-Group-by-[GID/SID/NAME]============================================ */
+
+int sysdb_search_group_by_name(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char **attrs,
+ struct ldb_message **msg)
+{
+ return sysdb_search_by_name(mem_ctx, domain, name, SYSDB_GROUP, attrs, msg);
+}
+
+static int
+sysdb_search_group_by_id(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *filterfmt,
+ gid_t gid,
+ const char **attrs,
+ struct ldb_message **msg)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *def_attrs[] = { SYSDB_NAME, SYSDB_GIDNUM, NULL };
+ struct ldb_message **msgs = NULL;
+ struct ldb_dn *basedn;
+ size_t msgs_count = 0;
+ char *filter;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ basedn = sysdb_group_base_dn(tmp_ctx, domain);
+ if (!basedn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, filterfmt, (unsigned long)gid);
+ if (!filter) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Use SUBTREE scope here, not ONELEVEL
+ * There is a bug in LDB that makes ONELEVEL searches extremely
+ * slow (it ignores indexing)
+ */
+ ret = sysdb_search_entry(tmp_ctx, domain->sysdb, basedn, LDB_SCOPE_SUBTREE,
+ filter, attrs?attrs:def_attrs,
+ &msgs_count, &msgs);
+ if (ret) {
+ goto done;
+ }
+
+ *msg = talloc_steal(mem_ctx, msgs[0]);
+
+done:
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No such entry\n");
+ }
+ else if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+/* Please note that sysdb_search_group_by_gid() is not aware of MPGs. If MPG
+ * support is needed either the caller must handle it or sysdb_getgrgid() or
+ * sysdb_getgrgid_attrs() should be used. */
+int sysdb_search_group_by_gid(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ gid_t gid,
+ const char **attrs,
+ struct ldb_message **msg)
+{
+ return sysdb_search_group_by_id(mem_ctx, domain, SYSDB_GRGID_FILTER,
+ gid, attrs, msg);
+}
+
+int sysdb_search_group_by_origgid(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ gid_t gid,
+ const char **attrs,
+ struct ldb_message **msg)
+{
+ return sysdb_search_group_by_id(mem_ctx, domain, SYSDB_GRORIGGID_FILTER,
+ gid, attrs, msg);
+}
+
+int sysdb_search_group_by_sid_str(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sid_str,
+ const char **attrs,
+ struct ldb_message **msg)
+{
+
+ return sysdb_search_entry_by_sid_str(mem_ctx, domain,
+ SYSDB_TMPL_GROUP_BASE,
+ SYSDB_GRSID_FILTER,
+ sid_str, attrs, msg);
+}
+
+/* =Search-Group-by-Name============================================ */
+
+int sysdb_search_netgroup_by_name(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char **attrs,
+ struct ldb_message **msg)
+{
+ TALLOC_CTX *tmp_ctx;
+ static const char *def_attrs[] = { SYSDB_NAME, NULL };
+ struct ldb_message **msgs = NULL;
+ struct ldb_dn *basedn;
+ size_t msgs_count = 0;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ basedn = sysdb_netgroup_dn(tmp_ctx, domain, name);
+ if (!basedn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_entry(tmp_ctx, domain->sysdb, basedn, LDB_SCOPE_BASE,
+ NULL, attrs?attrs:def_attrs, &msgs_count,
+ &msgs);
+ if (ret) {
+ goto done;
+ }
+
+ *msg = talloc_steal(mem_ctx, msgs[0]);
+
+done:
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No such entry\n");
+ }
+ else if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+/* =Timestamp-cache-functions==============================================*/
+
+/* If modifyTimestamp is the same in the TS cache, return EOK. Return ERR_NO_TS
+ * if there is no timestamps cache for this domain and ERR_TS_CACHE_MISS if
+ * the entry had changed and the caller needs to update the sysdb cache as well.
+ */
+static errno_t sysdb_check_ts_cache(struct sss_domain_info *domain,
+ struct ldb_dn *entry_dn,
+ struct sysdb_attrs *entry)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ size_t msgs_count;
+ struct ldb_message **msgs;
+ bool mod_ts_differs;
+
+ if (domain->sysdb->ldb_ts == NULL) {
+ return ERR_NO_TS;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ /* Check if the entry is in the timestamp cache */
+ ret = sysdb_search_ts_entry(tmp_ctx,
+ domain->sysdb,
+ entry_dn,
+ LDB_SCOPE_BASE,
+ NULL,
+ sysdb_ts_cache_attrs,
+ &msgs_count,
+ &msgs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Cannot find TS cache entry for [%s]: [%d]: %s\n",
+ ldb_dn_get_linearized(entry_dn), ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (msgs_count != 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Expected 1 result for base search, got %zu\n", msgs_count);
+ return EIO;
+ }
+
+ mod_ts_differs = sysdb_msg_attrs_modts_differs(msgs[0], entry);
+ if (mod_ts_differs == true) {
+ ret = ERR_TS_CACHE_MISS;
+ goto done;
+ }
+
+ ret = EOK;
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static int sysdb_set_ts_entry_attr(struct sysdb_ctx *sysdb,
+ struct ldb_dn *entry_dn,
+ struct sysdb_attrs *attrs,
+ int mod_op);
+
+static errno_t sysdb_create_ts_entry(struct sysdb_ctx *sysdb,
+ struct ldb_dn *entry_dn,
+ struct sysdb_attrs *attrs)
+{
+ struct ldb_message *msg;
+ errno_t ret;
+ int lret;
+ TALLOC_CTX *tmp_ctx;
+
+ if (sysdb->ldb_ts == NULL || attrs->num == 0) {
+ return EOK;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ if (entry_dn == NULL) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ msg = sysdb_attrs2msg(tmp_ctx, entry_dn, attrs, 0);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ lret = ldb_add(sysdb->ldb_ts, msg);
+ if (lret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "ldb_add failed: [%s](%d)[%s]\n",
+ ldb_strerror(lret), lret, ldb_errstring(sysdb->ldb_ts));
+ }
+
+ ret = sysdb_error_to_errno(lret);
+done:
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No such entry\n");
+ } else if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static struct sysdb_attrs *ts_obj_attrs(TALLOC_CTX *mem_ctx,
+ enum sysdb_obj_type obj_type)
+{
+ struct sysdb_attrs *attrs;
+ const char *oc;
+ errno_t ret;
+
+ switch (obj_type) {
+ case SYSDB_USER:
+ oc = SYSDB_USER_CLASS;
+ break;
+ case SYSDB_GROUP:
+ oc = SYSDB_GROUP_CLASS;
+ break;
+ default:
+ return NULL;
+ }
+
+ attrs = sysdb_new_attrs(mem_ctx);
+ if (attrs == NULL) {
+ return NULL;
+ }
+
+ ret = sysdb_attrs_add_string(attrs, SYSDB_OBJECTCATEGORY, oc);
+ if (ret != EOK) {
+ talloc_free(attrs);
+ return NULL;
+ }
+
+ return attrs;
+}
+
+static errno_t sysdb_update_ts_cache(struct sss_domain_info *domain,
+ struct ldb_dn *entry_dn,
+ struct sysdb_attrs *entry_attrs,
+ struct sysdb_attrs *ts_attrs,
+ int mod_op,
+ uint64_t cache_timeout,
+ time_t now)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ const char *modstamp;
+
+ if (domain->sysdb->ldb_ts == NULL) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "No timestamp cache for this domain\n");
+ return EOK;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ if (ts_attrs == NULL) {
+ ts_attrs = sysdb_new_attrs(tmp_ctx);
+ if (ts_attrs == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = sysdb_attrs_add_time_t(ts_attrs, SYSDB_LAST_UPDATE, now);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to add %s to tsdb\n", SYSDB_LAST_UPDATE);
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_time_t(ts_attrs, SYSDB_CACHE_EXPIRE,
+ ((cache_timeout) ?
+ (now + cache_timeout) : 0));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to add %s to tsdb\n", SYSDB_CACHE_EXPIRE);
+ goto done;
+ }
+
+ if (entry_attrs != NULL) {
+ ret = sysdb_attrs_get_string(entry_attrs, SYSDB_ORIG_MODSTAMP,
+ &modstamp);
+ if (ret == EOK) {
+ ret = sysdb_attrs_add_string(ts_attrs,
+ SYSDB_ORIG_MODSTAMP, modstamp);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to add %s to tsdb\n", SYSDB_ORIG_MODSTAMP);
+ goto done;
+ }
+ }
+ }
+
+ ret = sysdb_set_ts_entry_attr(domain->sysdb, entry_dn,
+ ts_attrs, mod_op);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot set ts attrs for group %s\n",
+ ldb_dn_get_linearized(entry_dn));
+ /* Not fatal */
+ }
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t sysdb_check_and_update_ts_cache(struct sss_domain_info *domain,
+ struct ldb_dn *entry_dn,
+ struct sysdb_attrs *attrs,
+ uint64_t cache_timeout,
+ time_t now)
+{
+ errno_t ret;
+
+ ret = sysdb_check_ts_cache(domain, entry_dn, attrs);
+ switch (ret) {
+ case ENOENT:
+ DEBUG(SSSDBG_TRACE_INTERNAL, "No timestamps entry\n");
+ break;
+ case EOK:
+ /* The entry's timestamp was the same. Just update the ts cache */
+ ret = sysdb_update_ts_cache(domain, entry_dn, attrs, NULL,
+ SYSDB_MOD_REP, cache_timeout, now);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot update the timestamps cache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ }
+ break;
+ case ERR_TS_CACHE_MISS:
+ case ERR_NO_TS:
+ /* Either there is no cache or the cache is up-do-date. Just report
+ * what's up
+ */
+ break;
+ default:
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Error checking the timestamps cache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ break;
+ }
+
+ return ret;
+}
+
+static errno_t get_sysdb_obj_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ enum sysdb_obj_type obj_type,
+ const char *obj_name,
+ struct ldb_dn **_obj_dn)
+{
+ struct ldb_dn *obj_dn;
+
+ switch (obj_type) {
+ case SYSDB_USER:
+ obj_dn = sysdb_user_dn(mem_ctx, domain, obj_name);
+ break;
+ case SYSDB_GROUP:
+ obj_dn = sysdb_group_dn(mem_ctx, domain, obj_name);
+ break;
+ default:
+ return EINVAL;
+ }
+
+ if (obj_dn == NULL) {
+ return ENOMEM;
+ }
+
+ *_obj_dn = obj_dn;
+ return EOK;
+}
+
+static errno_t sysdb_check_and_update_ts_obj(struct sss_domain_info *domain,
+ enum sysdb_obj_type obj_type,
+ const char *obj_name,
+ struct sysdb_attrs *attrs,
+ uint64_t cache_timeout,
+ time_t now)
+{
+ struct ldb_dn *entry_dn;
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = get_sysdb_obj_dn(tmp_ctx, domain, obj_type, obj_name, &entry_dn);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_check_and_update_ts_cache(domain, entry_dn, attrs,
+ cache_timeout, now);
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static errno_t sysdb_create_ts_obj(struct sss_domain_info *domain,
+ enum sysdb_obj_type obj_type,
+ const char *obj_name,
+ uint64_t cache_timeout,
+ time_t now)
+{
+ struct ldb_dn *entry_dn;
+ struct sysdb_attrs *ts_attrs = NULL;
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+
+ if (domain->sysdb->ldb_ts == NULL) {
+ return EOK;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = get_sysdb_obj_dn(tmp_ctx, domain, obj_type, obj_name, &entry_dn);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ts_attrs = ts_obj_attrs(tmp_ctx, obj_type);
+ if (ts_attrs == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_update_ts_cache(domain, entry_dn, NULL, ts_attrs,
+ SYSDB_MOD_ADD, cache_timeout, now);
+ if (ret != EOK) {
+ goto done;
+ }
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static errno_t sysdb_check_and_update_ts_grp(struct sss_domain_info *domain,
+ const char *grp_name,
+ struct sysdb_attrs *attrs,
+ uint64_t cache_timeout,
+ time_t now)
+{
+ return sysdb_check_and_update_ts_obj(domain, SYSDB_GROUP, grp_name,
+ attrs, cache_timeout, now);
+}
+
+static errno_t sysdb_create_ts_grp(struct sss_domain_info *domain,
+ const char *grp_name,
+ uint64_t cache_timeout,
+ time_t now)
+{
+ return sysdb_create_ts_obj(domain, SYSDB_GROUP, grp_name,
+ cache_timeout, now);
+}
+
+static errno_t sysdb_create_ts_usr(struct sss_domain_info *domain,
+ const char *usr_name,
+ uint64_t cache_timeout,
+ time_t now)
+{
+ return sysdb_create_ts_obj(domain, SYSDB_USER, usr_name,
+ cache_timeout, now);
+}
+
+/* =Replace-Attributes-On-Entry=========================================== */
+static int sysdb_set_cache_entry_attr(struct ldb_context *ldb,
+ struct ldb_dn *entry_dn,
+ struct sysdb_attrs *attrs,
+ int mod_op)
+{
+ struct ldb_message *msg;
+ int ret;
+ int lret;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ if (entry_dn == NULL || attrs->num == 0) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ msg = sysdb_attrs2msg(tmp_ctx, entry_dn, attrs, mod_op);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ lret = ldb_modify(ldb, msg);
+ if (lret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "ldb_modify failed: [%s](%d)[%s]\n",
+ ldb_strerror(lret), lret, ldb_errstring(ldb));
+ }
+
+ ret = sysdb_error_to_errno(lret);
+
+done:
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No such entry\n");
+ }
+ else if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static const char *get_attr_storage(int state_mask)
+{
+ const char *storage = "";
+
+ if (state_mask == SSS_SYSDB_BOTH_CACHE) {
+ storage = "cache, ts_cache";
+ } else if (state_mask == SSS_SYSDB_TS_CACHE) {
+ storage = "ts_cache";
+ } else if (state_mask == SSS_SYSDB_CACHE) {
+ storage = "cache";
+ }
+
+ return storage;
+}
+
+int sysdb_set_entry_attr(struct sysdb_ctx *sysdb,
+ struct ldb_dn *entry_dn,
+ struct sysdb_attrs *attrs,
+ int mod_op)
+{
+ bool sysdb_write = true;
+ errno_t ret = EOK;
+ errno_t tret = EOK;
+ int state_mask = SSS_SYSDB_NO_CACHE;
+
+ sysdb_write = sysdb_entry_attrs_diff(sysdb, entry_dn, attrs, mod_op);
+ if (sysdb_write == true) {
+ ret = sysdb_set_cache_entry_attr(sysdb->ldb, entry_dn, attrs, mod_op);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot set attrs for %s, %d [%s]\n",
+ ldb_dn_get_linearized(entry_dn), ret, sss_strerror(ret));
+ } else {
+ state_mask |= SSS_SYSDB_CACHE;
+ }
+ }
+
+ if (ret == EOK && is_ts_ldb_dn(entry_dn)) {
+ tret = sysdb_set_ts_entry_attr(sysdb, entry_dn, attrs, mod_op);
+ if (tret == ENOENT && mod_op == SYSDB_MOD_REP) {
+ /* Update failed because TS does non exist. Create missing TS */
+ tret = sysdb_set_ts_entry_attr(sysdb, entry_dn, attrs,
+ SYSDB_MOD_ADD);
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "The TS value for %s does not exist, trying to create it\n",
+ ldb_dn_get_linearized(entry_dn));
+ }
+ if (tret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot set TS attrs for %s\n", ldb_dn_get_linearized(entry_dn));
+ /* Not fatal */
+ } else {
+ state_mask |= SSS_SYSDB_TS_CACHE;
+ }
+ }
+
+ if (state_mask != SSS_SYSDB_NO_CACHE) {
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Entry [%s] has set [%s] attrs.\n",
+ ldb_dn_get_linearized(entry_dn),
+ get_attr_storage(state_mask));
+ }
+
+ return ret;
+}
+
+static int sysdb_rep_ts_entry_attr(struct sysdb_ctx *sysdb,
+ struct ldb_dn *entry_dn,
+ struct sysdb_attrs *attrs)
+{
+ if (sysdb->ldb_ts == NULL || attrs->num == 0) {
+ return EOK;
+ }
+
+ return sysdb_set_cache_entry_attr(sysdb->ldb_ts, entry_dn,
+ attrs, SYSDB_MOD_REP);
+}
+
+static int sysdb_set_ts_entry_attr(struct sysdb_ctx *sysdb,
+ struct ldb_dn *entry_dn,
+ struct sysdb_attrs *attrs,
+ int mod_op)
+{
+ struct sysdb_attrs *ts_attrs;
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+
+ if (sysdb->ldb_ts == NULL) {
+ return EOK;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ts_attrs = sysdb_filter_ts_attrs(tmp_ctx, attrs);
+ if (ts_attrs == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ switch (mod_op) {
+ case SYSDB_MOD_REP:
+ ret = sysdb_rep_ts_entry_attr(sysdb, entry_dn, ts_attrs);
+ break;
+ case SYSDB_MOD_ADD:
+ ret = sysdb_create_ts_entry(sysdb, entry_dn, ts_attrs);
+ break;
+ default:
+ ret = EINVAL;
+ break;
+ }
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+/* =Replace-Attributes-On-User============================================ */
+
+int sysdb_set_user_attr(struct sss_domain_info *domain,
+ const char *name,
+ struct sysdb_attrs *attrs,
+ int mod_op)
+{
+ struct ldb_dn *dn;
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ dn = sysdb_user_dn(tmp_ctx, domain, name);
+ if (!dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_set_entry_attr(domain->sysdb, dn, attrs, mod_op);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = EOK;
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_update_user_shadow_last_change(struct sss_domain_info *domain,
+ const char *name,
+ const char *attrname)
+{
+ struct sysdb_attrs *attrs;
+ char *value;
+ errno_t ret;
+
+ attrs = sysdb_new_attrs(NULL);
+ if (attrs == NULL) {
+ return ENOMEM;
+ }
+
+ /* The attribute contains number of days since the epoch */
+ value = talloc_asprintf(attrs, "%ld", (long)time(NULL)/86400);
+ if (value == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_string(attrs, attrname, value);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_set_user_attr(domain, name, attrs, SYSDB_MOD_REP);
+
+done:
+ talloc_free(attrs);
+ return ret;
+}
+
+/* =Replace-Attributes-On-Group=========================================== */
+
+int sysdb_set_group_attr(struct sss_domain_info *domain,
+ const char *name,
+ struct sysdb_attrs *attrs,
+ int mod_op)
+{
+ struct ldb_dn *dn;
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ dn = sysdb_group_dn(tmp_ctx, domain, name);
+ if (!dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_set_entry_attr(domain->sysdb, dn, attrs, mod_op);
+ if (ret) {
+ goto done;
+ }
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/* =Replace-Attributes-On-Netgroup=========================================== */
+
+int sysdb_set_netgroup_attr(struct sss_domain_info *domain,
+ const char *name,
+ struct sysdb_attrs *attrs,
+ int mod_op)
+{
+ errno_t ret;
+ struct ldb_dn *dn;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ dn = sysdb_netgroup_dn(tmp_ctx, domain, name);
+ if (!dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_set_entry_attr(domain->sysdb, dn, attrs, mod_op);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/* =Add-Basic-User-NO-CHECKS============================================== */
+
+int sysdb_add_basic_user(struct sss_domain_info *domain,
+ const char *name,
+ uid_t uid, gid_t gid,
+ const char *gecos,
+ const char *homedir,
+ const char *shell)
+{
+ struct ldb_message *msg;
+ int ret;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* user dn */
+ msg->dn = sysdb_user_dn(msg, domain, name);
+ if (!msg->dn) {
+ ERROR_OUT(ret, ENOMEM, done);
+ }
+
+ ret = sysdb_add_string(msg, SYSDB_OBJECTCATEGORY, SYSDB_USER_CLASS);
+ if (ret) goto done;
+
+ ret = sysdb_add_string(msg, SYSDB_NAME, name);
+ if (ret) goto done;
+
+ ret = sysdb_add_ulong(msg, SYSDB_UIDNUM, (unsigned long)uid);
+ if (ret) goto done;
+
+ ret = sysdb_add_ulong(msg, SYSDB_GIDNUM, (unsigned long)gid);
+ if (ret) goto done;
+
+ /* 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 = sysdb_add_string(msg, SYSDB_FULLNAME, gecos);
+ if (ret) goto done;
+ ret = sysdb_add_string(msg, SYSDB_GECOS, gecos);
+ if (ret) goto done;
+ }
+
+ if (homedir && *homedir) {
+ ret = sysdb_add_string(msg, SYSDB_HOMEDIR, homedir);
+ if (ret) goto done;
+ }
+
+ if (shell && *shell) {
+ ret = sysdb_add_string(msg, SYSDB_SHELL, shell);
+ if (ret) goto done;
+ }
+
+ /* creation time */
+ ret = sysdb_add_ulong(msg, SYSDB_CREATE_TIME, (unsigned long)time(NULL));
+ if (ret) goto done;
+
+ ret = ldb_add(domain->sysdb->ldb, msg);
+ ret = sysdb_error_to_errno(ret);
+
+done:
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static errno_t
+sysdb_remove_ghost_from_group(struct sss_domain_info *dom,
+ struct ldb_message *group,
+ struct ldb_message_element *alias_el,
+ const char *name,
+ const char *orig_dn,
+ const char *userdn)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ struct ldb_message_element *orig_members;
+ bool add_member = false;
+ errno_t ret = EOK;
+ int i;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOENT;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ ERROR_OUT(ret, ENOMEM, done);
+ }
+
+ msg->dn = group->dn;
+
+ if (orig_dn == NULL) {
+ /* We have no way of telling which groups this user belongs to.
+ * Add it to all that reference it in the ghost attribute */
+ add_member = true;
+ } else {
+ add_member = false;
+ orig_members = ldb_msg_find_element(group, SYSDB_ORIG_MEMBER);
+ if (orig_members) {
+ for (i = 0; i < orig_members->num_values; i++) {
+ if (strcmp((const char *) orig_members->values[i].data,
+ orig_dn) == 0) {
+ /* This is a direct member. Add the member attribute */
+ add_member = true;
+ }
+ }
+ } else {
+ /* Nothing to compare the originalDN with. Let's rely on the
+ * memberof plugin to do the right thing during initgroups..
+ */
+ add_member = true;
+ }
+ }
+
+ if (add_member) {
+ ret = sysdb_add_string(msg, SYSDB_MEMBER, userdn);
+ if (ret) goto done;
+ }
+
+ ret = sysdb_delete_string(msg, SYSDB_GHOST, name);
+ if (ret) goto done;
+
+ /* Delete aliases from the ghost attribute as well */
+ for (i = 0; i < alias_el->num_values; i++) {
+ if (strcmp((const char *)alias_el->values[i].data, name) == 0) {
+ continue;
+ }
+ ret = ldb_msg_add_string(msg, SYSDB_GHOST,
+ (char *) alias_el->values[i].data);
+ if (ret != LDB_SUCCESS) {
+ ERROR_OUT(ret, EINVAL, done);
+ }
+ }
+
+
+ ret = sss_ldb_modify_permissive(dom->sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "sss_ldb_modify_permissive failed: [%s](%d)[%s]\n",
+ ldb_strerror(ret), ret, ldb_errstring(dom->sysdb->ldb));
+ }
+
+ ret = sysdb_error_to_errno(ret);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ talloc_zfree(msg);
+
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t
+sysdb_remove_ghostattr_from_groups(struct sss_domain_info *domain,
+ const char *orig_dn,
+ struct sysdb_attrs *attrs,
+ const char *name)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message **groups;
+ struct ldb_message_element *alias_el;
+ struct ldb_dn *tmpdn;
+ const char *group_attrs[] = {SYSDB_NAME, SYSDB_GHOST, SYSDB_ORIG_MEMBER, NULL};
+ const char *userdn;
+ char *sanitized_name;
+ char *filter;
+ errno_t ret = EOK;
+ size_t group_count = 0;
+ int i;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOENT;
+ }
+
+ ret = sss_filter_sanitize(tmp_ctx, name, &sanitized_name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, "(|(%s=%s)",
+ SYSDB_GHOST, sanitized_name);
+ if (!filter) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = sysdb_attrs_get_el(attrs, SYSDB_NAME_ALIAS, &alias_el);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ for (i = 0; i < alias_el->num_values; i++) {
+ if (strcmp((const char *)alias_el->values[i].data, name) == 0) {
+ continue;
+ }
+ filter = talloc_asprintf_append(filter, "(%s=%s)",
+ SYSDB_GHOST, alias_el->values[i].data);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ filter = talloc_asprintf_append(filter, ")");
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tmpdn = sysdb_user_dn(tmp_ctx, domain, name);
+ if (!tmpdn) {
+ ERROR_OUT(ret, ENOMEM, done);
+ }
+
+ userdn = ldb_dn_get_linearized(tmpdn);
+ if (!userdn) {
+ ERROR_OUT(ret, EINVAL, done);
+ }
+
+ /* To cover cross-domain group-membership we must search in all
+ * sub-domains. */
+ tmpdn = ldb_dn_new(tmp_ctx, domain->sysdb->ldb, SYSDB_BASE);
+ if (!tmpdn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* We need to find all groups that contain this object as a ghost user
+ * and replace the ghost user by actual member record in direct parents.
+ * Note that this object can be referred to either by its name or any
+ * of its aliases
+ */
+ ret = sysdb_search_entry(tmp_ctx, domain->sysdb, tmpdn, LDB_SCOPE_SUBTREE,
+ filter, group_attrs, &group_count, &groups);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+
+ for (i = 0; i < group_count; i++) {
+ sysdb_remove_ghost_from_group(domain, groups[i], alias_el, name,
+ orig_dn, userdn);
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/* =Add-User-Function===================================================== */
+
+int sysdb_add_user(struct sss_domain_info *domain,
+ const char *name,
+ uid_t uid, gid_t gid,
+ const char *gecos,
+ const char *homedir,
+ const char *shell,
+ const char *orig_dn,
+ struct sysdb_attrs *attrs,
+ int cache_timeout,
+ time_t now)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ struct ldb_message_element *el = NULL;
+ int ret;
+ bool posix;
+
+ if (sss_domain_is_mpg(domain)) {
+ if (gid != 0) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Cannot add user with arbitrary GID in MPG domain!\n");
+ return EINVAL;
+ }
+ gid = uid;
+ }
+
+ if (domain->id_max != 0 && uid != 0 &&
+ (uid < domain->id_min || uid > domain->id_max)) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Supplied uid [%"SPRIuid"] is not in the allowed range "
+ "[%d-%d].\n", uid, domain->id_min, domain->id_max);
+ return ERANGE;
+ }
+
+ if (domain->id_max != 0 && gid != 0 &&
+ (gid < domain->id_min || gid > domain->id_max)) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Supplied gid [%"SPRIgid"] is not in the allowed range "
+ "[%d-%d].\n", gid, domain->id_min, domain->id_max);
+ return ERANGE;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = ldb_transaction_start(domain->sysdb->ldb);
+ if (ret) {
+ ret = sysdb_error_to_errno(ret);
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ if (sss_domain_is_mpg(domain)) {
+ /* In MPG domains you can't have groups with the same name or GID
+ * 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 */
+
+ ret = sysdb_search_group_by_name(tmp_ctx, domain, name, NULL, &msg);
+ if (ret != ENOENT) {
+ if (ret == EOK) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Group named %s already exists in an MPG domain\n",
+ name);
+ ret = EEXIST;
+ }
+ goto done;
+ }
+
+ ret = sysdb_search_group_by_gid(tmp_ctx, domain, uid, NULL, &msg);
+ if (ret != ENOENT) {
+ if (ret == EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Group with GID [%"SPRIgid"] already exists in an "
+ "MPG domain\n", gid);
+ ret = EEXIST;
+ }
+ goto done;
+ }
+ }
+
+ /* check no other user with the same uid exist */
+ if (uid != 0) {
+ ret = sysdb_search_user_by_uid(tmp_ctx, domain, uid, NULL, &msg);
+ if (ret != ENOENT) {
+ if (ret == EOK) ret = EEXIST;
+ goto done;
+ }
+ }
+
+ /* try to add the user */
+ ret = sysdb_add_basic_user(domain, name, uid, gid, gecos, homedir, shell);
+ if (ret) goto done;
+
+ ret = sysdb_create_ts_usr(domain, name, cache_timeout, now);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot create user timestamp entry\n");
+ /* Not fatal */
+ }
+
+ if (!attrs) {
+ attrs = sysdb_new_attrs(tmp_ctx);
+ if (!attrs) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = sysdb_attrs_get_bool(attrs, SYSDB_POSIX, &posix);
+ if (ret == ENOENT) {
+ posix = true;
+ ret = sysdb_attrs_add_bool(attrs, SYSDB_POSIX, true);
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_LIBS, "Failed to add posix attribute.\n");
+ goto done;
+ }
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_LIBS, "Failed to get posix attribute.\n");
+ goto done;
+ }
+
+ if (uid == 0 && posix == true) {
+ DEBUG(SSSDBG_OP_FAILURE, "Can't store posix user with uid=0.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (!now) {
+ now = time(NULL);
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_UPDATE, now);
+ if (ret) goto done;
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE,
+ ((cache_timeout) ?
+ (now + cache_timeout) : 0));
+ if (ret) goto done;
+
+ ret = sysdb_attrs_get_el_ext(attrs, SYSDB_INITGR_EXPIRE, false, &el);
+ if (ret == ENOENT) {
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_INITGR_EXPIRE, 0);
+ if (ret) goto done;
+ }
+
+ ret = sysdb_set_user_attr(domain, name, attrs, SYSDB_MOD_REP);
+ if (ret) goto done;
+
+ if (domain->enumerate == false) {
+ /* If we're not enumerating, previous getgr{nam,gid} calls might
+ * have stored ghost users into the cache, so we need to link them
+ * with the newly-created user entry
+ */
+ ret = sysdb_remove_ghostattr_from_groups(domain, orig_dn, attrs,
+ name);
+ if (ret) goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ ret = ldb_transaction_commit(domain->sysdb->ldb);
+ ret = sysdb_error_to_errno(ret);
+ } else {
+ DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, strerror(ret));
+ ldb_transaction_cancel(domain->sysdb->ldb);
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+/* =Add-Basic-Group-NO-CHECKS============================================= */
+
+int sysdb_add_basic_group(struct sss_domain_info *domain,
+ const char *name, gid_t gid)
+{
+ struct ldb_message *msg;
+ int ret;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* group dn */
+ msg->dn = sysdb_group_dn(msg, domain, name);
+ if (!msg->dn) {
+ ERROR_OUT(ret, ENOMEM, done);
+ }
+
+ ret = sysdb_add_string(msg, SYSDB_OBJECTCATEGORY, SYSDB_GROUP_CLASS);
+ if (ret) goto done;
+
+ ret = sysdb_add_string(msg, SYSDB_NAME, name);
+ if (ret) goto done;
+
+ ret = sysdb_add_ulong(msg, SYSDB_GIDNUM, (unsigned long)gid);
+ if (ret) goto done;
+
+ /* creation time */
+ ret = sysdb_add_ulong(msg, SYSDB_CREATE_TIME, (unsigned long)time(NULL));
+ if (ret) goto done;
+
+ ret = ldb_add(domain->sysdb->ldb, msg);
+ ret = sysdb_error_to_errno(ret);
+
+done:
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+
+/* =Add-Group-Function==================================================== */
+
+int sysdb_add_group(struct sss_domain_info *domain,
+ const char *name, gid_t gid,
+ struct sysdb_attrs *attrs,
+ int cache_timeout,
+ time_t now)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ int ret;
+ bool posix;
+
+ if (domain->id_max != 0 && gid != 0 &&
+ (gid < domain->id_min || gid > domain->id_max)) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Supplied gid [%"SPRIgid"] is not in the allowed range "
+ "[%d-%d].\n", gid, domain->id_min, domain->id_max);
+ return ERANGE;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = ldb_transaction_start(domain->sysdb->ldb);
+ if (ret) {
+ ret = sysdb_error_to_errno(ret);
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ if (sss_domain_is_mpg(domain)) {
+ /* 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 */
+
+ ret = sysdb_search_user_by_name(tmp_ctx, domain, name, NULL, &msg);
+ if (ret != ENOENT) {
+ if (ret == EOK) {
+ DEBUG(SSSDBG_TRACE_LIBS, "MPG domain contains a user "
+ "with the same name - %s.\n", name);
+ ret = EEXIST;
+ } else {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "sysdb_search_user_by_name failed for user %s.\n", name);
+ }
+ goto done;
+ }
+
+ ret = sysdb_search_user_by_uid(tmp_ctx, domain, gid, NULL, &msg);
+ if (ret != ENOENT) {
+ if (ret == EOK) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "User with the same UID exists in MPG domain: "
+ "[%"SPRIgid"].\n", gid);
+ ret = EEXIST;
+ } else {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "sysdb_search_user_by_uid failed for gid: "
+ "[%"SPRIgid"].\n", gid);
+ }
+ goto done;
+ }
+ }
+
+ /* check no other groups with the same gid exist */
+ if (gid != 0) {
+ ret = sysdb_search_group_by_gid(tmp_ctx, domain, gid, NULL, &msg);
+ if (ret != ENOENT) {
+ if (ret == EOK) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "Group with the same gid exists: [%"SPRIgid"].\n", gid);
+ ret = EEXIST;
+ } else {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "sysdb_search_group_by_gid failed for gid: "
+ "[%"SPRIgid"].\n", gid);
+ }
+ goto done;
+ }
+ }
+
+ /* try to add the group */
+ ret = sysdb_add_basic_group(domain, name, gid);
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "sysdb_add_basic_group failed for: %s with gid: "
+ "[%"SPRIgid"].\n", name, gid);
+ goto done;
+ }
+
+ ret = sysdb_create_ts_grp(domain, name, cache_timeout, now);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot set timestamp cache attributes for a group\n");
+ /* Not fatal */
+ }
+
+ if (!attrs) {
+ attrs = sysdb_new_attrs(tmp_ctx);
+ if (!attrs) {
+ DEBUG(SSSDBG_TRACE_LIBS, "sysdb_new_attrs failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = sysdb_attrs_get_bool(attrs, SYSDB_POSIX, &posix);
+ if (ret == ENOENT) {
+ posix = true;
+ ret = sysdb_attrs_add_bool(attrs, SYSDB_POSIX, true);
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_LIBS, "Failed to add posix attribute.\n");
+ goto done;
+ }
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_LIBS, "Failed to get posix attribute.\n");
+ goto done;
+ }
+
+ if (posix && gid == 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "Can't store posix user with gid=0.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (!now) {
+ now = time(NULL);
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_UPDATE, now);
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_LIBS, "Failed to add sysdb-last-update.\n");
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE,
+ ((cache_timeout) ?
+ (now + cache_timeout) : 0));
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_LIBS, "Failed to add sysdb-cache-expire.\n");
+ goto done;
+ }
+
+ ret = sysdb_set_group_attr(domain, name, attrs, SYSDB_MOD_REP);
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_LIBS, "sysdb_set_group_attr failed.\n");
+ goto done;
+ }
+
+done:
+ if (ret == EOK) {
+ ret = ldb_transaction_commit(domain->sysdb->ldb);
+ ret = sysdb_error_to_errno(ret);
+ } else {
+ DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, strerror(ret));
+ ldb_transaction_cancel(domain->sysdb->ldb);
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_add_incomplete_group(struct sss_domain_info *domain,
+ const char *name,
+ gid_t gid,
+ const char *original_dn,
+ const char *sid_str,
+ const char *uuid,
+ bool posix,
+ time_t now)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ struct sysdb_attrs *attrs;
+ struct ldb_message *msg;
+ const char *previous = NULL;
+ const char *group_attrs[] = { SYSDB_SID_STR, SYSDB_UUID, SYSDB_ORIG_DN, NULL };
+ const char *values[] = { sid_str, uuid, original_dn, NULL };
+ bool same = false;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ if (posix) {
+ ret = sysdb_search_group_by_gid(tmp_ctx, domain, gid, group_attrs, &msg);
+ if (ret == EOK) {
+ for (int i = 0; !same && group_attrs[i] != NULL; i++) {
+ previous = ldb_msg_find_attr_as_string(msg,
+ group_attrs[i],
+ NULL);
+ if (previous != NULL && values[i] != NULL) {
+ same = strcmp(previous, values[i]) == 0;
+ }
+ }
+
+ if (same == true) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "The group with GID [%"SPRIgid"] was renamed\n", gid);
+ ret = ERR_GID_DUPLICATED;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Another group with GID [%"SPRIgid"] already exists\n", gid);
+ ret = ERR_GID_DUPLICATED;
+ goto done;
+ }
+ }
+
+ /* try to add the group */
+ ret = sysdb_add_basic_group(domain, name, gid);
+ if (ret) goto done;
+
+ if (!now) {
+ now = time(NULL);
+ }
+
+ ret = sysdb_create_ts_grp(domain, name, now-1, now);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot set timestamp cache attributes for a group\n");
+ /* Not fatal */
+ }
+
+ attrs = sysdb_new_attrs(tmp_ctx);
+ if (!attrs) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_UPDATE, now);
+ if (ret) goto done;
+
+ /* in case (ignore_group_members == true) group is actually complete */
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE,
+ domain->ignore_group_members ?
+ (now + domain->group_timeout) : (now-1));
+ if (ret) goto done;
+
+ ret = sysdb_attrs_add_bool(attrs, SYSDB_POSIX, posix);
+ if (ret) goto done;
+
+ if (original_dn) {
+ ret = sysdb_attrs_add_string(attrs, SYSDB_ORIG_DN, original_dn);
+ if (ret) goto done;
+ }
+
+ if (sid_str) {
+ ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, sid_str);
+ if (ret) goto done;
+ }
+
+ if (uuid) {
+ ret = sysdb_attrs_add_string(attrs, SYSDB_UUID, uuid);
+ if (ret) goto done;
+ }
+
+ ret = sysdb_set_group_attr(domain, name, attrs, SYSDB_MOD_REP);
+
+done:
+ if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, sss_strerror(ret));
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+/* =Add-Or-Remove-Group-Memeber=========================================== */
+
+/* mod_op must be either SYSDB_MOD_ADD or SYSDB_MOD_DEL */
+int sysdb_mod_group_member(struct sss_domain_info *domain,
+ struct ldb_dn *member_dn,
+ struct ldb_dn *group_dn,
+ int mod_op)
+{
+ struct ldb_message *msg;
+ const char *dn;
+ int ret;
+
+ msg = ldb_msg_new(NULL);
+ 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_string(msg, SYSDB_MEMBER, dn);
+ if (ret != LDB_SUCCESS) {
+ ERROR_OUT(ret, EINVAL, fail);
+ }
+
+ ret = ldb_modify(domain->sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "ldb_modify failed: [%s](%d)[%s]\n",
+ ldb_strerror(ret), ret, ldb_errstring(domain->sysdb->ldb));
+ }
+ ret = sysdb_error_to_errno(ret);
+
+fail:
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_zfree(msg);
+ return ret;
+}
+
+/* =Add-Basic-Netgroup-NO-CHECKS============================================= */
+
+int sysdb_add_basic_netgroup(struct sss_domain_info *domain,
+ const char *name, const char *description)
+{
+ struct ldb_message *msg;
+ int ret;
+
+ msg = ldb_msg_new(NULL);
+ if (!msg) {
+ return ENOMEM;
+ }
+
+ /* netgroup dn */
+ msg->dn = sysdb_netgroup_dn(msg, domain, name);
+ if (!msg->dn) {
+ ERROR_OUT(ret, ENOMEM, done);
+ }
+
+ ret = sysdb_add_string(msg, SYSDB_OBJECTCLASS, SYSDB_NETGROUP_CLASS);
+ if (ret) goto done;
+
+ ret = sysdb_add_string(msg, SYSDB_NAME, name);
+ if (ret) goto done;
+
+ if (description && *description) {
+ ret = sysdb_add_string(msg, SYSDB_DESCRIPTION, description);
+ if (ret) goto done;
+ }
+
+ /* creation time */
+ ret = sysdb_add_ulong(msg, SYSDB_CREATE_TIME, (unsigned long) time(NULL));
+ if (ret) goto done;
+
+ ret = ldb_add(domain->sysdb->ldb, msg);
+ ret = sysdb_error_to_errno(ret);
+
+done:
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_zfree(msg);
+ return ret;
+}
+
+
+/* =Add-Netgroup-Function==================================================== */
+
+int sysdb_add_netgroup(struct sss_domain_info *domain,
+ const char *name,
+ const char *description,
+ struct sysdb_attrs *attrs,
+ char **missing,
+ int cache_timeout,
+ time_t now)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = ldb_transaction_start(domain->sysdb->ldb);
+ if (ret) {
+ ret = sysdb_error_to_errno(ret);
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ /* try to add the netgroup */
+ ret = sysdb_add_basic_netgroup(domain, name, description);
+ if (ret && ret != EEXIST) goto done;
+
+ if (!attrs) {
+ attrs = sysdb_new_attrs(tmp_ctx);
+ if (!attrs) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (!now) {
+ now = time(NULL);
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_UPDATE, now);
+ if (ret) goto done;
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE,
+ ((cache_timeout) ?
+ (now + cache_timeout) : 0));
+ if (ret) goto done;
+
+ ret = sysdb_set_netgroup_attr(domain, name, attrs, SYSDB_MOD_REP);
+
+ if (missing) {
+ ret = sysdb_remove_attrs(domain, name,
+ SYSDB_MEMBER_NETGROUP,
+ missing);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Could not remove missing attributes\n");
+ }
+ }
+
+done:
+ if (ret == EOK) {
+ ret = ldb_transaction_commit(domain->sysdb->ldb);
+ ret = sysdb_error_to_errno(ret);
+ }
+
+ if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, strerror(ret));
+ ldb_transaction_cancel(domain->sysdb->ldb);
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+/* =Store-Users-(Native/Legacy)-(replaces-existing-data)================== */
+
+static errno_t sysdb_store_new_user(struct sss_domain_info *domain,
+ const char *name,
+ uid_t uid,
+ gid_t gid,
+ const char *gecos,
+ const char *homedir,
+ const char *shell,
+ const char *orig_dn,
+ struct sysdb_attrs *attrs,
+ uint64_t cache_timeout,
+ time_t now);
+
+
+static errno_t sysdb_store_user_attrs(struct sss_domain_info *domain,
+ const char *name,
+ uid_t uid,
+ gid_t gid,
+ const char *gecos,
+ const char *homedir,
+ const char *shell,
+ const char *orig_dn,
+ struct sysdb_attrs *attrs,
+ char **remove_attrs,
+ uint64_t cache_timeout,
+ time_t now);
+
+/* if one of the basic attributes is empty ("") as opposed to NULL,
+ * this will just remove it */
+
+int sysdb_store_user(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,
+ const char *orig_dn,
+ struct sysdb_attrs *attrs,
+ char **remove_attrs,
+ uint64_t cache_timeout,
+ time_t now)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ int ret;
+ errno_t sret = EOK;
+ bool in_transaction = false;
+
+ /* get transaction timestamp */
+ if (now == 0) {
+ now = time(NULL);
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ if (!attrs) {
+ attrs = sysdb_new_attrs(tmp_ctx);
+ if (!attrs) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (pwd && !*pwd) {
+ ret = sysdb_attrs_add_string(attrs, SYSDB_PWD, pwd);
+ if (ret) goto done;
+ }
+
+ ret = sysdb_transaction_start(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+ goto done;
+ }
+
+ in_transaction = true;
+
+ ret = sysdb_search_user_by_name(tmp_ctx, domain, name, NULL, &msg);
+ if (ret && ret != ENOENT) {
+ goto done;
+ }
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_LIBS, "User %s does not exist.\n", name);
+ }
+
+ if (ret == ENOENT) {
+ /* the user doesn't exist, turn into adding a user */
+ ret = sysdb_store_new_user(domain, name, uid, gid, gecos, homedir,
+ shell, orig_dn, attrs, cache_timeout, now);
+ if (ret != EOK) {
+ if ((ret == EEXIST) && sss_domain_is_mpg(domain)) {
+ /* ipa_s2n_save_objects() will handle this later */
+ DEBUG(SSSDBG_TRACE_FUNC, "sysdb_store_new_user() failed: conflict in MPG domain\n");
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_store_new_user() failed: %d\n", ret);
+ }
+ }
+ } else {
+ /* the user exists, let's just replace attributes when set */
+ ret = sysdb_store_user_attrs(domain, name, uid, gid, gecos, homedir,
+ shell, orig_dn, attrs, remove_attrs,
+ cache_timeout, now);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_store_user_attrs() failed: %d\n", ret);
+ }
+ }
+ if (ret != EOK) {
+ goto done;
+ }
+
+ sret = sysdb_transaction_commit(domain->sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
+ ret = EIO;
+ goto done;
+ }
+ in_transaction = false;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(domain->sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
+ }
+ }
+
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, strerror(ret));
+ } else {
+ DEBUG(SSSDBG_TRACE_FUNC, "User \"%s\" has been stored\n", name);
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static errno_t sysdb_store_new_user(struct sss_domain_info *domain,
+ const char *name,
+ uid_t uid,
+ gid_t gid,
+ const char *gecos,
+ const char *homedir,
+ const char *shell,
+ const char *orig_dn,
+ struct sysdb_attrs *attrs,
+ uint64_t cache_timeout,
+ time_t now)
+{
+ errno_t ret;
+
+ ret = sysdb_add_user(domain, name, uid, gid, gecos, homedir,
+ shell, orig_dn, attrs, cache_timeout, now);
+ if (ret == EEXIST) {
+ /* This may be a user rename. If there is a user with the
+ * same UID, remove it and try to add the basic user again
+ */
+ ret = sysdb_delete_user(domain, NULL, uid);
+ if (ret == ENOENT) {
+ /* Not found by UID, return the original EEXIST,
+ * this may be a conflict in MPG domain or something
+ * else */
+ return EEXIST;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_delete_user failed.\n");
+ return ret;
+ }
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "A user with the same UID [%llu] was removed from the "
+ "cache\n", (unsigned long long) uid);
+ ret = sysdb_add_user(domain, name, uid, gid, gecos, homedir,
+ shell, orig_dn, attrs, cache_timeout, now);
+ if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_add_user failed (while renaming user) for: "
+ "%s [%"SPRIgid"].\n", name, gid);
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+static errno_t sysdb_store_user_attrs(struct sss_domain_info *domain,
+ const char *name,
+ uid_t uid,
+ gid_t gid,
+ const char *gecos,
+ const char *homedir,
+ const char *shell,
+ const char *orig_dn,
+ struct sysdb_attrs *attrs,
+ char **remove_attrs,
+ uint64_t cache_timeout,
+ time_t now)
+{
+ errno_t ret;
+
+ if (uid) {
+ ret = sysdb_attrs_add_uint32(attrs, SYSDB_UIDNUM, uid);
+ if (ret) return ret;
+ }
+
+ if (gid) {
+ ret = sysdb_attrs_add_uint32(attrs, SYSDB_GIDNUM, gid);
+ if (ret) return ret;
+ }
+
+ if (uid && !gid && sss_domain_is_mpg(domain)) {
+ ret = sysdb_attrs_add_uint32(attrs, SYSDB_GIDNUM, uid);
+ if (ret) return ret;
+ }
+
+ if (gecos) {
+ ret = sysdb_attrs_add_string(attrs, SYSDB_GECOS, gecos);
+ if (ret) return ret;
+ }
+
+ if (homedir) {
+ ret = sysdb_attrs_add_string(attrs, SYSDB_HOMEDIR, homedir);
+ if (ret) return ret;
+ }
+
+ if (shell) {
+ ret = sysdb_attrs_add_string(attrs, SYSDB_SHELL, shell);
+ if (ret) return ret;
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_UPDATE, now);
+ if (ret) return ret;
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE,
+ ((cache_timeout) ?
+ (now + cache_timeout) : 0));
+ if (ret) return ret;
+
+ ret = sysdb_set_user_attr(domain, name, attrs, SYSDB_MOD_REP);
+ if (ret) return ret;
+
+ if (remove_attrs) {
+ ret = sysdb_remove_attrs(domain, name,
+ SYSDB_MEMBER_USER,
+ remove_attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "Could not remove missing attributes\n");
+ }
+ }
+
+ return EOK;
+}
+
+/* =Store-Group-(Native/Legacy)-(replaces-existing-data)================== */
+
+/* this function does not check that all user members are actually present */
+
+static errno_t sysdb_store_new_group(struct sss_domain_info *domain,
+ const char *name,
+ gid_t gid,
+ struct sysdb_attrs *attrs,
+ uint64_t cache_timeout,
+ time_t now);
+
+static errno_t sysdb_store_group_attrs(struct sss_domain_info *domain,
+ const char *name,
+ gid_t gid,
+ struct sysdb_attrs *attrs,
+ uint64_t cache_timeout,
+ time_t now);
+
+int sysdb_store_group(struct sss_domain_info *domain,
+ const char *name,
+ gid_t gid,
+ struct sysdb_attrs *attrs,
+ uint64_t cache_timeout,
+ time_t now)
+{
+ TALLOC_CTX *tmp_ctx;
+ static const char *src_attrs[] = { "*", NULL };
+ struct ldb_message *msg;
+ bool new_group = false;
+ int ret;
+ errno_t sret = EOK;
+ bool in_transaction = false;
+
+ /* get transaction timestamp */
+ if (!now) {
+ now = time(NULL);
+ }
+
+ ret = sysdb_check_and_update_ts_grp(domain, name, attrs,
+ cache_timeout, now);
+ if (ret == EOK) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "The group record of %s did not change, only updated "
+ "the timestamp cache\n", name);
+ return EOK;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_transaction_start(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+ goto done;
+ }
+ in_transaction = true;
+
+ ret = sysdb_search_group_by_name(tmp_ctx, domain, name, src_attrs, &msg);
+ if (ret && ret != ENOENT) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "sysdb_search_group_by_name failed for %s with: [%d][%s].\n",
+ name, ret, strerror(ret));
+ goto done;
+ }
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_LIBS, "Group %s does not exist.\n", name);
+ new_group = true;
+ }
+
+ if (!attrs) {
+ attrs = sysdb_new_attrs(tmp_ctx);
+ if (!attrs) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (new_group) {
+ ret = sysdb_store_new_group(domain, name, gid, attrs,
+ cache_timeout, now);
+ } else {
+ ret = sysdb_store_group_attrs(domain, name, gid, attrs,
+ cache_timeout, now);
+ }
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cache update failed: %d\n", ret);
+ goto done;
+ }
+
+ sret = sysdb_transaction_commit(domain->sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
+ ret = EIO;
+ goto done;
+ }
+ in_transaction = false;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(domain->sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
+ }
+ }
+
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, strerror(ret));
+ } else {
+ DEBUG(SSSDBG_TRACE_FUNC, "Group \"%s\" has been stored\n", name);
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+
+static errno_t sysdb_store_new_group(struct sss_domain_info *domain,
+ const char *name,
+ gid_t gid,
+ struct sysdb_attrs *attrs,
+ uint64_t cache_timeout,
+ time_t now)
+{
+ errno_t ret;
+
+ /* group doesn't exist, turn into adding a group */
+ ret = sysdb_add_group(domain, name, gid, attrs, cache_timeout, now);
+ if (ret == EEXIST) {
+ /* This may be a group rename. If there is a group with the
+ * same GID, remove it and try to add the basic group again
+ */
+ DEBUG(SSSDBG_TRACE_LIBS, "sysdb_add_group failed: [EEXIST].\n");
+ ret = sysdb_delete_group(domain, NULL, gid);
+ if (ret == ENOENT) {
+ /* Not found by GID, return the original EEXIST,
+ * this may be a conflict in MPG domain or something
+ * else */
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_delete_group failed (while renaming group). Not "
+ "found by gid: [%"SPRIgid"].\n", gid);
+ return EEXIST;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_group failed.\n");
+ return ret;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "A group with the same GID [%"SPRIgid"] was removed from "
+ "the cache\n", gid);
+ ret = sysdb_add_group(domain, name, gid, attrs, cache_timeout, now);
+ if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_add_group failed (while renaming group) for: "
+ "%s [%"SPRIgid"].\n", name, gid);
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+static errno_t sysdb_store_group_attrs(struct sss_domain_info *domain,
+ const char *name,
+ gid_t gid,
+ struct sysdb_attrs *attrs,
+ uint64_t cache_timeout,
+ time_t now)
+{
+ errno_t ret;
+
+ /* the group exists, let's just replace attributes when set */
+ if (gid) {
+ ret = sysdb_attrs_add_uint32(attrs, SYSDB_GIDNUM, gid);
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_LIBS, "Failed to add GID.\n");
+ return ret;
+ }
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_UPDATE, now);
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_LIBS, "Failed to add sysdb-last-update.\n");
+ return ret;
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE,
+ ((cache_timeout) ?
+ (now + cache_timeout) : 0));
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_LIBS, "Failed to add sysdb-cache-expire.\n");
+ return ret;
+ }
+
+ ret = sysdb_set_group_attr(domain, name, attrs, SYSDB_MOD_REP);
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_LIBS, "sysdb_set_group_attr failed.\n");
+ return ret;
+ }
+
+ return EOK;
+}
+
+/* =Add-User-to-Group(Native/Legacy)====================================== */
+static int
+sysdb_group_membership_mod(struct sss_domain_info *domain,
+ const char *group,
+ const char *member,
+ enum sysdb_member_type type,
+ int modify_op,
+ bool is_dn)
+{
+ struct ldb_dn *group_dn;
+ struct ldb_dn *member_dn;
+ char *member_domname;
+ struct sss_domain_info *member_dom;
+ struct sss_domain_info *group_dom;
+ int ret;
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sss_parse_internal_fqname(tmp_ctx, member,
+ NULL, &member_domname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to parser internal fqname '%s' [%d]: %s\n",
+ member, ret, sss_strerror(ret));
+ goto done;
+ }
+
+ member_dom = find_domain_by_name(get_domains_head(domain),
+ member_domname, false);
+ if (member_dom == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Domain [%s] was not found\n", member_domname);
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (type == SYSDB_MEMBER_USER) {
+ member_dn = sysdb_user_dn(tmp_ctx, member_dom, member);
+ } else if (type == SYSDB_MEMBER_GROUP) {
+ member_dn = sysdb_group_dn(tmp_ctx, member_dom, member);
+ } else {
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (!member_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (!is_dn) {
+ /* To create a correct DN we have to check if the group belongs to */
+ /* child domain */
+ group_dom = find_domain_by_object_name(domain, group);
+ if (group_dom == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "The right (sub)domain for the group [%s] was not found\n",
+ group);
+ ret = EINVAL;
+ goto done;
+ }
+ group_dn = sysdb_group_dn(tmp_ctx, group_dom, group);
+ } else {
+ group_dn = ldb_dn_new(tmp_ctx, domain->sysdb->ldb, group);
+ }
+
+ if (!group_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_mod_group_member(domain, member_dn, group_dn, modify_op);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_add_group_member(struct sss_domain_info *domain,
+ const char *group,
+ const char *member,
+ enum sysdb_member_type type,
+ bool is_dn)
+{
+ return sysdb_group_membership_mod(domain, group, member, type,
+ SYSDB_MOD_ADD, is_dn);
+}
+
+/* =Remove-member-from-Group(Native/Legacy)=============================== */
+
+
+int sysdb_remove_group_member(struct sss_domain_info *domain,
+ const char *group,
+ const char *member,
+ enum sysdb_member_type type,
+ bool is_dn)
+{
+ return sysdb_group_membership_mod(domain, group, member, type,
+ SYSDB_MOD_DEL, is_dn);
+}
+
+
+/* =Password-Caching====================================================== */
+
+int sysdb_cache_password_ex(struct sss_domain_info *domain,
+ const char *username,
+ const char *password,
+ enum sss_authtok_type authtok_type,
+ size_t second_factor_len)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sysdb_attrs *attrs;
+ char *hash = NULL;
+ char *salt;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = s3crypt_gen_salt(tmp_ctx, &salt);
+ if (ret) {
+ DEBUG(SSSDBG_CONF_SETTINGS, "Failed to generate random salt.\n");
+ goto fail;
+ }
+
+ ret = s3crypt_sha512(tmp_ctx, password, salt, &hash);
+ if (ret) {
+ DEBUG(SSSDBG_CONF_SETTINGS, "Failed to create password hash.\n");
+ goto fail;
+ }
+
+ attrs = sysdb_new_attrs(tmp_ctx);
+ if (!attrs) {
+ ERROR_OUT(ret, ENOMEM, fail);
+ }
+
+ ret = sysdb_attrs_add_string(attrs, SYSDB_CACHEDPWD, hash);
+ if (ret) goto fail;
+
+ ret = sysdb_attrs_add_long(attrs, SYSDB_CACHEDPWD_TYPE, authtok_type);
+ if (ret) goto fail;
+
+ if (authtok_type == SSS_AUTHTOK_TYPE_2FA && second_factor_len > 0) {
+ ret = sysdb_attrs_add_long(attrs, SYSDB_CACHEDPWD_FA2_LEN,
+ second_factor_len);
+ if (ret) goto fail;
+ }
+
+ /* FIXME: should we use a different attribute for cache passwords?? */
+ ret = sysdb_attrs_add_long(attrs, "lastCachedPasswordChange",
+ (long)time(NULL));
+ if (ret) goto fail;
+
+ ret = sysdb_attrs_add_uint32(attrs, SYSDB_FAILED_LOGIN_ATTEMPTS, 0U);
+ if (ret) goto fail;
+
+
+ ret = sysdb_set_user_attr(domain, username, attrs, SYSDB_MOD_REP);
+ if (ret) {
+ goto fail;
+ }
+ talloc_zfree(tmp_ctx);
+ return EOK;
+
+fail:
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_cache_password(struct sss_domain_info *domain,
+ const char *username,
+ const char *password)
+{
+ return sysdb_cache_password_ex(domain, username, password,
+ SSS_AUTHTOK_TYPE_PASSWORD, 0);
+}
+
+static errno_t set_initgroups_expire_attribute(struct sss_domain_info *domain,
+ const char *name)
+{
+ errno_t ret;
+ time_t cache_timeout;
+ struct sysdb_attrs *attrs;
+
+ attrs = sysdb_new_attrs(NULL);
+ if (attrs == NULL) {
+ return ENOMEM;
+ }
+
+ cache_timeout = domain->user_timeout
+ ? time(NULL) + domain->user_timeout
+ : 0;
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_INITGR_EXPIRE, cache_timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not set up attrs\n");
+ goto done;
+ }
+
+ ret = sysdb_set_user_attr(domain, name, attrs, SYSDB_MOD_REP);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to set initgroups expire attribute\n");
+ goto done;
+ }
+
+done:
+ talloc_zfree(attrs);
+ return ret;
+}
+
+errno_t sysdb_set_initgr_expire_timestamp(struct sss_domain_info *domain,
+ const char *name_or_upn_or_sid)
+{
+ const char *cname;
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_get_real_name(tmp_ctx, domain, name_or_upn_or_sid, &cname);
+ if (ret == ENOENT) {
+ /* No point trying to bump timestamp of an entry that does not exist..*/
+ ret = EOK;
+ goto done;
+ } else if (ret != EOK) {
+ cname = name_or_upn_or_sid;
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to canonicalize name, using [%s]\n", name_or_upn_or_sid);
+ }
+
+ ret = set_initgroups_expire_attribute(domain, cname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot set the initgroups expire attribute [%d]: %s\n",
+ ret, sss_strerror(ret));
+ }
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/* =Custom Search================== */
+
+int sysdb_search_custom(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *filter,
+ const char *subtree_name,
+ const char **attrs,
+ size_t *msgs_count,
+ struct ldb_message ***msgs)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *basedn = NULL;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (filter == NULL || subtree_name == NULL) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ basedn = sysdb_custom_subtree_dn(tmp_ctx, domain, subtree_name);
+ if (basedn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_custom_subtree_dn failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ if (!ldb_dn_validate(basedn)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Syntactically invalid subtree DN.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = sysdb_search_entry(mem_ctx, domain->sysdb, basedn,
+ LDB_SCOPE_SUBTREE, filter, attrs,
+ msgs_count, msgs);
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_search_custom_by_name(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *object_name,
+ const char *subtree_name,
+ const char **attrs,
+ size_t *_count,
+ struct ldb_message ***_msgs)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *basedn;
+ struct ldb_message **msgs;
+ size_t count;
+ int ret;
+
+ if (object_name == NULL || subtree_name == NULL) {
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ basedn = sysdb_custom_dn(tmp_ctx, domain, object_name, subtree_name);
+ if (basedn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_custom_dn failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ if (!ldb_dn_validate(basedn)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Syntactically invalid DN.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = sysdb_search_entry(tmp_ctx, domain->sysdb, basedn,
+ LDB_SCOPE_BASE, NULL, attrs, &count, &msgs);
+ if (ret) {
+ goto done;
+ }
+
+ if (count > 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "More than one result found.\n");
+ ret = EFAULT;
+ goto done;
+ }
+
+ *_count = count;
+ *_msgs = talloc_move(mem_ctx, &msgs);
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static int sysdb_cache_search_users(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_context *ldb,
+ const char *sub_filter,
+ const char **attrs,
+ size_t *msgs_count,
+ struct ldb_message ***msgs);
+
+static int sysdb_cache_search_groups(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_context *ldb,
+ const char *sub_filter,
+ const char **attrs,
+ size_t *msgs_count,
+ struct ldb_message ***msgs);
+
+errno_t sysdb_search_by_orig_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ enum sysdb_member_type type,
+ const char *member_dn,
+ const char **attrs,
+ size_t *msgs_count,
+ struct ldb_message ***msgs)
+{
+ TALLOC_CTX *tmp_ctx;
+ char *filter;
+ char *sanitized_dn = NULL;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_filter_sanitize_dn(tmp_ctx, member_dn, &sanitized_dn);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, "(%s=%s)", SYSDB_ORIG_DN, sanitized_dn);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ switch (type) {
+ case SYSDB_MEMBER_USER:
+ ret = sysdb_cache_search_users(mem_ctx, domain, domain->sysdb->ldb,
+ filter, attrs, msgs_count, msgs);
+ break;
+ case SYSDB_MEMBER_GROUP:
+ ret = sysdb_cache_search_groups(mem_ctx, domain, domain->sysdb->ldb,
+ filter, attrs, msgs_count, msgs);
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Trying to perform a search by orig_dn using a "
+ "non-supported type %d\n", type);
+ ret = EINVAL;
+ goto done;
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/* =Custom Store (replaces-existing-data)================== */
+
+int sysdb_store_custom(struct sss_domain_info *domain,
+ const char *object_name,
+ const char *subtree_name,
+ struct sysdb_attrs *attrs)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *search_attrs[] = { "*", NULL };
+ size_t resp_count = 0;
+ struct ldb_message **resp;
+ struct ldb_message *msg;
+ struct ldb_message_element *el;
+ bool add_object = false;
+ int ret;
+ int i;
+
+ if (object_name == NULL || subtree_name == NULL) {
+ return EINVAL;
+ }
+
+ ret = ldb_transaction_start(domain->sysdb->ldb);
+ if (ret) {
+ return sysdb_error_to_errno(ret);
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_custom_by_name(tmp_ctx, domain,
+ object_name, subtree_name,
+ search_attrs, &resp_count, &resp);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+
+ if (ret == ENOENT) {
+ add_object = true;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = sysdb_custom_dn(tmp_ctx, domain, object_name, subtree_name);
+ if (!msg->dn) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_custom_dn failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->elements = talloc_array(msg, struct ldb_message_element, attrs->num);
+ if (!msg->elements) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < attrs->num; i++) {
+ msg->elements[i] = attrs->a[i];
+ if (add_object) {
+ msg->elements[i].flags = LDB_FLAG_MOD_ADD;
+ } else {
+ el = ldb_msg_find_element(resp[0], 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 = attrs->num;
+
+ if (add_object) {
+ ret = ldb_add(domain->sysdb->ldb, msg);
+ } else {
+ ret = ldb_modify(domain->sysdb->ldb, msg);
+ }
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to store custom entry: %s(%d)[%s]\n",
+ ldb_strerror(ret), ret, ldb_errstring(domain->sysdb->ldb));
+ ret = sysdb_error_to_errno(ret);
+ }
+
+done:
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, strerror(ret));
+ ldb_transaction_cancel(domain->sysdb->ldb);
+ } else {
+ ret = ldb_transaction_commit(domain->sysdb->ldb);
+ ret = sysdb_error_to_errno(ret);
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+/* = Custom Delete======================================= */
+
+int sysdb_delete_custom(struct sss_domain_info *domain,
+ const char *object_name,
+ const char *subtree_name)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *dn;
+ int ret;
+
+ if (object_name == NULL || subtree_name == NULL) {
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ dn = sysdb_custom_dn(tmp_ctx, domain, object_name, subtree_name);
+ if (dn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_custom_dn failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_delete(domain->sysdb->ldb, dn);
+
+ switch (ret) {
+ case LDB_SUCCESS:
+ case LDB_ERR_NO_SUCH_OBJECT:
+ ret = EOK;
+ break;
+
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "ldb_delete failed: %s (%d); error Message: [%s]\n",
+ ldb_strerror(ret), ret, ldb_errstring(domain->sysdb->ldb));
+ ret = sysdb_error_to_errno(ret);
+ break;
+ }
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+/* = ASQ search request ======================================== */
+
+int sysdb_asq_search(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_dn *base_dn,
+ const char *expression,
+ const char *asq_attribute,
+ const char **attrs,
+ size_t *msgs_count,
+ struct ldb_message ***msgs)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_request *ldb_req;
+ struct ldb_control **ctrl;
+ struct ldb_asq_control *asq_control;
+ struct ldb_result *res;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ctrl = talloc_array(tmp_ctx, 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, 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;
+
+ res = talloc_zero(tmp_ctx, struct ldb_result);
+ if (!res) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ ret = ldb_build_search_req(&ldb_req, domain->sysdb->ldb, tmp_ctx,
+ base_dn, LDB_SCOPE_BASE,
+ expression, attrs, ctrl,
+ res, ldb_search_default_callback, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto fail;
+ }
+
+ ret = ldb_request(domain->sysdb->ldb, ldb_req);
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(ldb_req->handle, LDB_WAIT_ALL);
+ }
+ if (ret) {
+ ret = sysdb_error_to_errno(ret);
+ goto fail;
+ }
+
+ *msgs_count = res->count;
+ *msgs = talloc_move(mem_ctx, &res->msgs);
+
+ talloc_zfree(tmp_ctx);
+ return EOK;
+
+fail:
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No such entry\n");
+ }
+ else if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+/* =Search-Users-with-Custom-Filter====================================== */
+
+static int sysdb_cache_search_users(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_context *ldb,
+ const char *sub_filter,
+ const char **attrs,
+ size_t *msgs_count,
+ struct ldb_message ***msgs)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *basedn;
+ char *filter;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ basedn = sysdb_user_base_dn(tmp_ctx, domain);
+ if (!basedn) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to build base dn\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, "(&(%s)%s)", SYSDB_UC, sub_filter);
+ if (!filter) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to build filter\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Search users with filter: %s\n", filter);
+
+ ret = sysdb_cache_search_entry(mem_ctx, ldb, basedn,
+ LDB_SCOPE_SUBTREE, filter, attrs,
+ msgs_count, msgs);
+ if (ret) {
+ goto fail;
+ }
+
+ talloc_zfree(tmp_ctx);
+ return EOK;
+
+fail:
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "No such entry\n");
+ }
+ else if (ret) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_search_users(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter,
+ const char **attrs,
+ size_t *msgs_count,
+ struct ldb_message ***msgs)
+{
+ errno_t ret;
+
+ ret = sysdb_cache_search_users(mem_ctx, domain, domain->sysdb->ldb,
+ sub_filter, attrs, msgs_count, msgs);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return sysdb_merge_msg_list_ts_attrs(domain->sysdb, *msgs_count, *msgs,
+ attrs);
+}
+
+int sysdb_search_users_by_timestamp(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter,
+ const char *ts_sub_filter,
+ const char **attrs,
+ size_t *_msgs_count,
+ struct ldb_message ***_msgs)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ struct ldb_result ts_res;
+ struct ldb_message **msgs;
+ size_t msgs_count;
+ char *dn_filter = NULL;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_search_ts_users(tmp_ctx, domain, ts_sub_filter, NULL, &ts_res);
+ if (ret == ERR_NO_TS) {
+ ret = sysdb_cache_search_users(tmp_ctx, domain, domain->sysdb->ldb,
+ ts_sub_filter, attrs, &msgs_count, &msgs);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_merge_msg_list_ts_attrs(domain->sysdb, msgs_count, msgs, attrs);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ goto immediately;
+ } else if (ret != EOK) {
+ goto done;
+ }
+
+ ret = cleanup_dn_filter(tmp_ctx, &ts_res, SYSDB_UC, sub_filter, &dn_filter);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_search_ts_matches(tmp_ctx, domain->sysdb, attrs,
+ &ts_res, dn_filter, &res);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ msgs_count = res->count;
+ msgs = res->msgs;
+
+immediately:
+ *_msgs_count = msgs_count;
+ *_msgs = talloc_steal(mem_ctx, msgs);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_search_ts_users(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter,
+ const char **attrs,
+ struct ldb_result *res)
+{
+ size_t msgs_count;
+ struct ldb_message **msgs;
+ int ret;
+
+ if (res == NULL) {
+ return EINVAL;
+ }
+
+ memset(res, 0, sizeof(*res));
+
+ if (domain->sysdb->ldb_ts == NULL) {
+ return ERR_NO_TS;
+ }
+
+ ret = sysdb_cache_search_users(mem_ctx, domain, domain->sysdb->ldb_ts,
+ sub_filter, attrs, &msgs_count, &msgs);
+ if (ret == EOK) {
+ res->count = (unsigned)msgs_count;
+ res->msgs = msgs;
+ }
+
+ return ret;
+}
+
+/* =Delete-User-by-Name-OR-uid============================================ */
+
+static errno_t sysdb_user_local_override_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_dn *obj_dn,
+ struct ldb_dn **out_dn)
+{
+ struct ldb_context *ldb = sysdb_ctx_get_ldb(domain->sysdb);
+ struct ldb_dn *override_dn;
+ char *anchor;
+ char *dn;
+ errno_t ret;
+
+ ret = sysdb_dn_sanitize(mem_ctx, ldb_dn_get_linearized(obj_dn), &dn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_dn_sanitize() failed\n");
+ return ret;
+ }
+
+ anchor = talloc_asprintf(mem_ctx, ":%s:%s", SYSDB_LOCAL_VIEW_NAME, dn);
+ talloc_free(dn);
+ if (anchor == NULL) {
+ return ENOMEM;
+ }
+
+ override_dn = ldb_dn_new_fmt(mem_ctx, ldb, SYSDB_TMPL_OVERRIDE,
+ anchor, SYSDB_LOCAL_VIEW_NAME);
+ talloc_free(anchor);
+ if (override_dn == NULL) {
+ return ENOMEM;
+ }
+
+ *out_dn = override_dn;
+
+ return EOK;
+}
+
+int sysdb_delete_user(struct sss_domain_info *domain,
+ const char *name, uid_t uid)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *attrs[] = {SYSDB_GHOST, NULL};
+ size_t msg_count;
+ char *filter;
+ struct ldb_message **msgs;
+ struct ldb_message *msg;
+ int ret;
+ int i;
+ char *sanitized_name;
+ struct ldb_dn *override_dn = NULL;
+ bool in_transaction = false;
+ errno_t sret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ if (name) {
+ ret = sysdb_search_user_by_name(tmp_ctx, domain, name, NULL, &msg);
+ } else {
+ ret = sysdb_search_user_by_uid(tmp_ctx, domain, uid, NULL, &msg);
+ }
+ if (ret == EOK) {
+ if (name && uid) {
+ /* verify name/gid match */
+ const char *c_name;
+ uint64_t c_uid;
+
+ c_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+ c_uid = ldb_msg_find_attr_as_uint64(msg, SYSDB_UIDNUM, 0);
+ if (c_name == NULL || c_uid == 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Attribute is missing but this should never happen!\n");
+ ret = EFAULT;
+ goto fail;
+ }
+ if (strcmp(name, c_name) || uid != c_uid) {
+ /* this is not the entry we are looking for */
+ ret = EINVAL;
+ goto fail;
+ }
+ }
+
+ /* If user has a linked userOverride delete it */
+ ret = sysdb_user_local_override_dn(tmp_ctx, domain, msg->dn,
+ &override_dn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to build local override DN: %s\n",
+ strerror(ret));
+ goto fail;
+ }
+
+ ret = sysdb_transaction_start(domain->sysdb);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto fail;
+ }
+ in_transaction = true;
+
+ ret = sysdb_delete_entry(domain->sysdb, override_dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Error deleting linked override DN: %s\n",
+ strerror(ret));
+ goto fail;
+ }
+
+ ret = sysdb_delete_entry(domain->sysdb, msg->dn, false);
+ if (ret) {
+ goto fail;
+ }
+
+ ret = sysdb_transaction_commit(domain->sysdb);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to commit ldb transaction [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto fail;
+ }
+ in_transaction = false;
+
+ } else if (ret == ENOENT && name != NULL) {
+ /* Perhaps a ghost user? */
+ ret = sss_filter_sanitize(tmp_ctx, name, &sanitized_name);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, "(%s=%s)",
+ SYSDB_GHOST, sanitized_name);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ ret = sysdb_search_groups(tmp_ctx, domain, filter, attrs,
+ &msg_count, &msgs);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ for (i = 0; i < msg_count; i++) {
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ ERROR_OUT(ret, ENOMEM, fail);
+ }
+
+ msg->dn = msgs[i]->dn;
+
+ ret = sysdb_delete_string(msg, SYSDB_GHOST, name);
+ if (ret) goto fail;
+
+ ret = ldb_modify(domain->sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "ldb_modify failed: [%s](%d)[%s]\n",
+ ldb_strerror(ret), ret,
+ ldb_errstring(domain->sysdb->ldb));
+ }
+ ret = sysdb_error_to_errno(ret);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ talloc_zfree(msg);
+ }
+ } else {
+ goto fail;
+ }
+
+
+ talloc_zfree(tmp_ctx);
+ return EOK;
+
+fail:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(domain->sysdb);
+ if (sret != LDB_SUCCESS) {
+ sret = sysdb_error_to_errno(sret);
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to cancel ldb transaction [%d]: %s\n",
+ sret, sss_strerror(sret));
+ }
+ }
+ DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, strerror(ret));
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+
+/* =Search-Groups-with-Custom-Filter===================================== */
+
+static int sysdb_cache_search_groups(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_context *ldb,
+ const char *sub_filter,
+ const char **attrs,
+ size_t *msgs_count,
+ struct ldb_message ***msgs)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *basedn;
+ char *filter;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ basedn = sysdb_group_base_dn(tmp_ctx, domain);
+ if (!basedn) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to build base dn\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, "(&(%s)%s)", SYSDB_GC, sub_filter);
+ if (!filter) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to build filter\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Search groups with filter: %s\n", filter);
+
+ ret = sysdb_cache_search_entry(mem_ctx, ldb, basedn,
+ LDB_SCOPE_SUBTREE, filter, attrs,
+ msgs_count, msgs);
+ if (ret) {
+ goto fail;
+ }
+
+ talloc_zfree(tmp_ctx);
+ return EOK;
+
+fail:
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "No such entry\n");
+ }
+ else if (ret) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_search_groups(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter,
+ const char **attrs,
+ size_t *msgs_count,
+ struct ldb_message ***msgs)
+{
+ errno_t ret;
+
+ ret = sysdb_cache_search_groups(mem_ctx, domain, domain->sysdb->ldb,
+ sub_filter, attrs, msgs_count, msgs);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return sysdb_merge_msg_list_ts_attrs(domain->sysdb, *msgs_count, *msgs,
+ attrs);
+}
+
+int sysdb_search_groups_by_timestamp(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter,
+ const char *ts_sub_filter,
+ const char **attrs,
+ size_t *_msgs_count,
+ struct ldb_message ***_msgs)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ struct ldb_result ts_res;
+ struct ldb_message **msgs;
+ size_t msgs_count;
+ char *dn_filter = NULL;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_search_ts_groups(tmp_ctx, domain, ts_sub_filter, NULL, &ts_res);
+ if (ret == ERR_NO_TS) {
+ ret = sysdb_cache_search_groups(tmp_ctx, domain, domain->sysdb->ldb,
+ ts_sub_filter, attrs, &msgs_count, &msgs);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_merge_msg_list_ts_attrs(domain->sysdb, msgs_count, msgs, attrs);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ goto immediately;
+ } else if (ret != EOK) {
+ goto done;
+ }
+
+ ret = cleanup_dn_filter(tmp_ctx, &ts_res, SYSDB_GC, sub_filter, &dn_filter);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_search_ts_matches(tmp_ctx, domain->sysdb, attrs,
+ &ts_res, dn_filter, &res);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ msgs_count = res->count;
+ msgs = res->msgs;
+
+immediately:
+ *_msgs_count = msgs_count;
+ *_msgs = talloc_steal(mem_ctx, msgs);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_search_ts_groups(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter,
+ const char **attrs,
+ struct ldb_result *res)
+{
+ size_t msgs_count;
+ struct ldb_message **msgs;
+ int ret;
+
+ if (res == NULL) {
+ return EINVAL;
+ }
+
+ memset(res, 0, sizeof(*res));
+
+ if (domain->sysdb->ldb_ts == NULL) {
+ return ERR_NO_TS;
+ }
+
+ ret = sysdb_cache_search_groups(mem_ctx, domain, domain->sysdb->ldb_ts,
+ sub_filter, attrs, &msgs_count, &msgs);
+ if (ret == EOK) {
+ res->count = (unsigned)msgs_count;
+ res->msgs = msgs;
+ }
+
+ return ret;
+}
+
+/* =Delete-Group-by-Name-OR-gid=========================================== */
+
+int sysdb_delete_group(struct sss_domain_info *domain,
+ const char *name, gid_t gid)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ if (name) {
+ ret = sysdb_search_group_by_name(tmp_ctx, domain, name, NULL, &msg);
+ } else {
+ ret = sysdb_search_group_by_gid(tmp_ctx, domain, gid, NULL, &msg);
+ }
+ if (ret) {
+ goto fail;
+ }
+
+ if (name && gid) {
+ /* verify name/gid match */
+ const char *c_name;
+ uint64_t c_gid;
+
+ c_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+ c_gid = ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0);
+ if (c_name == NULL || c_gid == 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Attribute is missing but this should never happen!\n");
+ ret = EFAULT;
+ goto fail;
+ }
+ if (strcmp(name, c_name) || gid != c_gid) {
+ /* this is not the entry we are looking for */
+ ret = EINVAL;
+ goto fail;
+ }
+ }
+
+ ret = sysdb_delete_entry(domain->sysdb, msg->dn, false);
+ if (ret) {
+ goto fail;
+ }
+
+ talloc_zfree(tmp_ctx);
+ return EOK;
+
+fail:
+ DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, strerror(ret));
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+/* =Search-Netgroups-with-Custom-Filter===================================== */
+
+int sysdb_search_netgroups(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter,
+ const char **attrs,
+ size_t *msgs_count,
+ struct ldb_message ***msgs)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *basedn;
+ char *filter;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ basedn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb,
+ SYSDB_TMPL_NETGROUP_BASE, domain->name);
+ if (!basedn) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to build base dn\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, "(&(%s)%s)", SYSDB_NC, sub_filter);
+ if (!filter) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to build filter\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Search netgroups with filter: %s\n", filter);
+
+ ret = sysdb_search_entry(mem_ctx, domain->sysdb, basedn,
+ LDB_SCOPE_SUBTREE, filter, attrs,
+ msgs_count, msgs);
+ if (ret) {
+ goto fail;
+ }
+
+ talloc_zfree(tmp_ctx);
+ return EOK;
+
+fail:
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Entry not found\n");
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+/* =Delete-Netgroup-by-Name============================================== */
+
+int sysdb_delete_netgroup(struct sss_domain_info *domain,
+ const char *name)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ int ret;
+
+ if (!name) return EINVAL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_search_netgroup_by_name(tmp_ctx, domain, name, NULL, &msg);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "sysdb_search_netgroup_by_name failed: %d (%s)\n",
+ ret, strerror(ret));
+ goto done;
+ } else if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Netgroup does not exist, nothing to delete\n");
+ ret = EOK;
+ goto done;
+ }
+
+ ret = sysdb_delete_entry(domain->sysdb, msg->dn, false);
+ if (ret != EOK) {
+ goto done;
+ }
+
+done:
+ if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_delete_by_sid(struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ const char *sid_str)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ int ret;
+
+ if (!sid_str) return EINVAL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_search_object_by_sid(tmp_ctx, domain, sid_str, NULL, &res);
+
+ if (ret == ENOENT) {
+ /* No existing entry. Just quit. */
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "search by sid did not return any results.\n");
+ ret = EOK;
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "search by sid failed: %d (%s)\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ if (res->count > 1) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "getbysid call returned more than one " \
+ "result !?!\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = sysdb_delete_entry(sysdb, res->msgs[0]->dn, false);
+ if (ret != EOK) {
+ goto done;
+ }
+
+done:
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/* ========= Authentication against cached password ============ */
+
+
+errno_t check_failed_login_attempts(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;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ *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, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_FAILED_LOGIN_ATTEMPTS,
+ CONFDB_DEFAULT_PAM_FAILED_LOGIN_ATTEMPTS,
+ &allowed_failed_login_attempts);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to read the number of allowed failed login "
+ "attempts.\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+ ret = confdb_get_int(cdb, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_FAILED_LOGIN_DELAY,
+ CONFDB_DEFAULT_PAM_FAILED_LOGIN_DELAY,
+ &failed_login_delay);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to read the failed login delay.\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_ALL,
+ "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(SSSDBG_TRACE_LIBS, "failed_login_delay has passed, "
+ "resetting failed_login_attempts.\n");
+ *failed_login_attempts = 0;
+ } else {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "login delayed until %lld.\n", (long long) end);
+ *delayed_until = end;
+ ret = ERR_AUTH_DENIED;
+ goto done;
+ }
+ } else {
+ DEBUG(SSSDBG_CONF_SETTINGS, "Too many failed logins.\n");
+ ret = ERR_AUTH_DENIED;
+ goto done;
+ }
+ }
+ }
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t check_for_combined_2fa_password(struct sss_domain_info *domain,
+ struct ldb_message *ldb_msg,
+ const char *password,
+ const char *userhash)
+{
+
+ unsigned int cached_authtok_type;
+ unsigned int cached_fa2_len;
+ char *short_pw;
+ char *comphash;
+ size_t pw_len;
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+
+ cached_authtok_type = ldb_msg_find_attr_as_uint(ldb_msg,
+ SYSDB_CACHEDPWD_TYPE,
+ SSS_AUTHTOK_TYPE_EMPTY);
+ if (cached_authtok_type != SSS_AUTHTOK_TYPE_2FA) {
+ DEBUG(SSSDBG_TRACE_LIBS, "Wrong authtok type.\n");
+ return EINVAL;
+ }
+
+ cached_fa2_len = ldb_msg_find_attr_as_uint(ldb_msg, SYSDB_CACHEDPWD_FA2_LEN,
+ 0);
+ if (cached_fa2_len == 0) {
+ DEBUG(SSSDBG_TRACE_LIBS, "Second factor size not available.\n");
+ return EINVAL;
+ }
+
+ pw_len = strlen(password);
+ if (pw_len < cached_fa2_len + domain->cache_credentials_min_ff_length) {
+ DEBUG(SSSDBG_TRACE_LIBS, "Password too short.\n");
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ short_pw = talloc_strndup(tmp_ctx, password, (pw_len - cached_fa2_len));
+ if (short_pw == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ talloc_set_destructor((TALLOC_CTX *)short_pw,
+ sss_erase_talloc_mem_securely);
+
+ ret = s3crypt_sha512(tmp_ctx, short_pw, userhash, &comphash);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CONF_SETTINGS, "Failed to create password hash.\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ if (strcmp(userhash, comphash) != 0) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Hash of shorten password does not match.\n");
+ ret = ERR_AUTH_FAILED;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+int sysdb_cache_auth(struct sss_domain_info *domain,
+ const char *name,
+ const char *password,
+ struct confdb_ctx *cdb,
+ bool just_check,
+ time_t *_expire_date,
+ time_t *_delayed_until)
+{
+ TALLOC_CTX *tmp_ctx;
+ 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, SYSDB_CACHEDPWD_TYPE,
+ SYSDB_CACHEDPWD_FA2_LEN, NULL };
+ struct ldb_message *ldb_msg;
+ const char *userhash;
+ char *comphash;
+ uint64_t lastLogin = 0;
+ int cred_expiration;
+ uint32_t failed_login_attempts = 0;
+ struct sysdb_attrs *update_attrs;
+ bool authentication_successful = false;
+ time_t expire_date = -1;
+ time_t delayed_until = -1;
+ int ret;
+
+ if (name == NULL || *name == '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing user name.\n");
+ return EINVAL;
+ }
+
+ if (cdb == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing config db context.\n");
+ return EINVAL;
+ }
+
+ if (domain->sysdb == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing sysdb db context.\n");
+ return EINVAL;
+ }
+
+ if (!domain->cache_credentials) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cached credentials not available.\n");
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = ldb_transaction_start(domain->sysdb->ldb);
+ if (ret) {
+ talloc_zfree(tmp_ctx);
+ ret = sysdb_error_to_errno(ret);
+ return ret;
+ }
+
+ ret = sysdb_search_user_by_name(tmp_ctx, domain, name, attrs, &ldb_msg);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sysdb_search_user_by_name failed [%d][%s].\n",
+ ret, strerror(ret));
+ if (ret == ENOENT) ret = ERR_ACCOUNT_UNKNOWN;
+ goto done;
+ }
+
+ /* Check offline_auth_cache_timeout */
+ lastLogin = ldb_msg_find_attr_as_uint64(ldb_msg,
+ SYSDB_LAST_ONLINE_AUTH,
+ 0);
+
+ ret = confdb_get_int(cdb, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_CRED_TIMEOUT, 0, &cred_expiration);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to read expiration time of offline credentials.\n");
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_ALL, "Offline credentials expiration is [%d] days.\n",
+ cred_expiration);
+
+ if (cred_expiration) {
+ expire_date = lastLogin + (cred_expiration * 86400);
+ if (expire_date < time(NULL)) {
+ DEBUG(SSSDBG_CONF_SETTINGS, "Cached user entry is too old.\n");
+ expire_date = 0;
+ ret = ERR_CACHED_CREDS_EXPIRED;
+ goto done;
+ }
+ } else {
+ expire_date = 0;
+ }
+
+ ret = check_failed_login_attempts(cdb, ldb_msg, &failed_login_attempts,
+ &delayed_until);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to check login attempts\n");
+ goto done;
+ }
+
+ /* TODO: verify user account (disabled, expired ...) */
+
+ userhash = ldb_msg_find_attr_as_string(ldb_msg, SYSDB_CACHEDPWD, NULL);
+ if (userhash == NULL || *userhash == '\0') {
+ DEBUG(SSSDBG_CONF_SETTINGS, "Cached credentials not available.\n");
+ ret = ERR_NO_CACHED_CREDS;
+ goto done;
+ }
+
+ ret = s3crypt_sha512(tmp_ctx, password, userhash, &comphash);
+ if (ret) {
+ DEBUG(SSSDBG_CONF_SETTINGS, "Failed to create password hash.\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ update_attrs = sysdb_new_attrs(tmp_ctx);
+ if (update_attrs == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_new_attrs failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (strcmp(userhash, comphash) == 0
+ || check_for_combined_2fa_password(domain, ldb_msg,
+ password, userhash) == EOK) {
+ /* TODO: probable good point for audit logging */
+ DEBUG(SSSDBG_CONF_SETTINGS, "Hashes do match!\n");
+ authentication_successful = true;
+
+ if (just_check) {
+ ret = EOK;
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_time_t(update_attrs,
+ SYSDB_LAST_LOGIN, time(NULL));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "sysdb_attrs_add_time_t failed, "
+ "but authentication is successful.\n");
+ ret = EOK;
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_uint32(update_attrs,
+ SYSDB_FAILED_LOGIN_ATTEMPTS, 0U);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "sysdb_attrs_add_uint32 failed, "
+ "but authentication is successful.\n");
+ ret = EOK;
+ goto done;
+ }
+
+ } else {
+ DEBUG(SSSDBG_CONF_SETTINGS, "Authentication failed.\n");
+ authentication_successful = false;
+
+ ret = sysdb_attrs_add_time_t(update_attrs,
+ SYSDB_LAST_FAILED_LOGIN,
+ time(NULL));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "sysdb_attrs_add_time_t failed.\n");
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_uint32(update_attrs,
+ SYSDB_FAILED_LOGIN_ATTEMPTS,
+ ++failed_login_attempts);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "sysdb_attrs_add_uint32 failed.\n");
+ goto done;
+ }
+ }
+
+ ret = sysdb_set_user_attr(domain, name, update_attrs,
+ LDB_FLAG_MOD_REPLACE);
+ if (ret) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to update Login attempt information!\n");
+ }
+
+done:
+ if (_expire_date != NULL) {
+ *_expire_date = expire_date;
+ }
+ if (_delayed_until != NULL) {
+ *_delayed_until = delayed_until;
+ }
+ if (ret) {
+ ldb_transaction_cancel(domain->sysdb->ldb);
+ } else {
+ ret = ldb_transaction_commit(domain->sysdb->ldb);
+ ret = sysdb_error_to_errno(ret);
+ if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to commit transaction!\n");
+ }
+ }
+ if (authentication_successful) {
+ ret = EOK;
+ } else {
+ if (ret == EOK) {
+ ret = ERR_AUTH_FAILED;
+ }
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t sysdb_update_members_ex(struct sss_domain_info *domain,
+ const char *member,
+ enum sysdb_member_type type,
+ const char *const *add_groups,
+ const char *const *del_groups,
+ bool is_dn)
+{
+ errno_t ret;
+ errno_t sret;
+ int i;
+ bool in_transaction = false;
+
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ if(!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_transaction_start(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to start update transaction\n");
+ goto done;
+ }
+
+ in_transaction = true;
+
+ if (add_groups) {
+ /* Add the user to all add_groups */
+ for (i = 0; add_groups[i]; i++) {
+ ret = sysdb_add_group_member(domain, add_groups[i],
+ member, type, is_dn);
+ if (ret != EOK) {
+ if (ret != EEXIST) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not add member [%s] to group [%s]. "
+ "Skipping.\n", member, add_groups[i]);
+ } else {
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Group [%s] already has member [%s]. Skipping.\n",
+ add_groups[i], member);
+ }
+ /* Continue on, we should try to finish the rest */
+ }
+ }
+ }
+
+ if (del_groups) {
+ /* Remove the user from all del_groups */
+ for (i = 0; del_groups[i]; i++) {
+ ret = sysdb_remove_group_member(domain, del_groups[i],
+ member, type, is_dn);
+ if (ret != EOK) {
+ if (ret != ENOENT) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not remove member [%s] from group [%s]. "
+ "Skipping\n", member, del_groups[i]);
+ } else {
+ DEBUG(SSSDBG_FUNC_DATA,
+ "No member [%s] in group [%s]. "
+ "Skipping\n", member, del_groups[i]);
+ }
+ /* Continue on, we should try to finish the rest */
+ }
+ }
+ }
+
+ ret = sysdb_transaction_commit(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
+ goto done;
+ }
+
+ in_transaction = false;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(domain->sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
+ }
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_update_members(struct sss_domain_info *domain,
+ const char *member,
+ enum sysdb_member_type type,
+ const char *const *add_groups,
+ const char *const *del_groups)
+{
+ return sysdb_update_members_ex(domain, member, type,
+ add_groups, del_groups, false);
+}
+
+errno_t sysdb_update_members_dn(struct sss_domain_info *member_domain,
+ const char *member,
+ enum sysdb_member_type type,
+ const char *const *add_groups,
+ const char *const *del_groups)
+{
+ return sysdb_update_members_ex(member_domain, member, type,
+ add_groups, del_groups, true);
+}
+
+errno_t sysdb_remove_attrs(struct sss_domain_info *domain,
+ const char *name,
+ enum sysdb_member_type type,
+ char **remove_attrs)
+{
+ errno_t ret;
+ errno_t sret = EOK;
+ bool in_transaction = false;
+ struct ldb_message *msg;
+ int lret;
+ size_t i;
+
+ msg = ldb_msg_new(NULL);
+ if (!msg) return ENOMEM;
+
+ switch(type) {
+ case SYSDB_MEMBER_USER:
+ msg->dn = sysdb_user_dn(msg, domain, name);
+ break;
+
+ case SYSDB_MEMBER_GROUP:
+ msg->dn = sysdb_group_dn(msg, domain, name);
+ break;
+
+ case SYSDB_MEMBER_NETGROUP:
+ msg->dn = sysdb_netgroup_dn(msg, domain, name);
+ break;
+
+ case SYSDB_MEMBER_SERVICE:
+ msg->dn = sysdb_svc_dn(domain->sysdb, msg, domain->name, name);
+ break;
+ case SYSDB_MEMBER_HOST:
+ msg->dn = sysdb_host_dn(msg, domain, name);
+ break;
+ case SYSDB_MEMBER_IP_NETWORK:
+ msg->dn = sysdb_ipnetwork_dn(msg, domain, name);
+ break;
+ }
+ if (!msg->dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_transaction_start(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+ goto done;
+ }
+
+ in_transaction = true;
+
+ for (i = 0; remove_attrs[i]; i++) {
+ /* SYSDB_MEMBEROF is exclusively handled by the memberof plugin */
+ if (strcasecmp(remove_attrs[i], SYSDB_MEMBEROF) == 0) {
+ continue;
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Removing attribute [%s] from [%s]\n",
+ remove_attrs[i], name);
+ lret = ldb_msg_add_empty(msg, remove_attrs[i],
+ LDB_FLAG_MOD_DELETE, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ /* We need to do individual modifies so that we can
+ * skip unknown attributes. Otherwise, any nonexistent
+ * attribute in the sysdb will cause other removals to
+ * fail.
+ */
+ lret = ldb_modify(domain->sysdb->ldb, msg);
+ if (lret != LDB_SUCCESS && lret != LDB_ERR_NO_SUCH_ATTRIBUTE) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "ldb_modify failed: [%s](%d)[%s]\n",
+ ldb_strerror(lret), lret, ldb_errstring(domain->sysdb->ldb));
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ /* Remove this attribute and move on to the next one */
+ ldb_msg_remove_attr(msg, remove_attrs[i]);
+ }
+
+ ret = sysdb_transaction_commit(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
+ goto done;
+ }
+
+ in_transaction = false;
+
+ ret = EOK;
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(domain->sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
+ }
+ }
+ talloc_free(msg);
+ return ret;
+}
+
+static errno_t sysdb_search_object_attr(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *filter,
+ const char **attrs,
+ bool expect_only_one_result,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *def_attrs[] = { SYSDB_NAME, SYSDB_UIDNUM, SYSDB_GIDNUM,
+ ORIGINALAD_PREFIX SYSDB_NAME,
+ SYSDB_DEFAULT_ATTRS,
+ NULL };
+ struct ldb_dn *basedn;
+ int ret;
+ struct ldb_result *res = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ basedn = sysdb_domain_dn(tmp_ctx, domain);
+ if (basedn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new_fmt failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, basedn,
+ LDB_SCOPE_SUBTREE, attrs ? attrs : def_attrs,
+ "%s", filter);
+ if (ret != EOK) {
+ ret = sysdb_error_to_errno(ret);
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_search failed.\n");
+ goto done;
+ }
+
+ if (res->count > 1 && expect_only_one_result) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Search with filter [%s] returned more than one object.\n",
+ filter);
+ ret = EINVAL;
+ goto done;
+ } else if (res->count == 0) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ /* Merge in the timestamps from the fast ts db */
+ ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n");
+ /* non-fatal */
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No such entry.\n");
+ } else if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static errno_t sysdb_search_object_by_str_attr(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *filter_tmpl,
+ const char *str,
+ const char **attrs,
+ bool expect_only_one_result,
+ struct ldb_result **_res)
+{
+ char *filter = NULL;
+ errno_t ret;
+ char *sanitized = NULL;
+
+ if (str == NULL) {
+ return EINVAL;
+ }
+
+ ret = sss_filter_sanitize(NULL, str, &sanitized);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_filter_sanitize failed.\n");
+ goto done;
+ }
+
+ filter = talloc_asprintf(NULL, filter_tmpl, sanitized);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_object_attr(mem_ctx, domain, filter, attrs,
+ expect_only_one_result, _res);
+
+done:
+ talloc_free(sanitized);
+ talloc_free(filter);
+ return ret;
+}
+
+errno_t sysdb_search_object_by_id(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ uint32_t id,
+ const char **attrs,
+ struct ldb_result **res)
+{
+ char *filter;
+ errno_t ret;
+
+ filter = talloc_asprintf(NULL, SYSDB_ID_FILTER, id, id);
+ if (filter == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_search_object_attr(mem_ctx, domain, filter, attrs, true, res);
+
+ talloc_free(filter);
+ return ret;
+}
+
+errno_t sysdb_search_object_by_name(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char **attrs,
+ struct ldb_result **res)
+{
+ TALLOC_CTX *tmp_ctx;
+ char *filter;
+ char *sanitized_name;
+ char *sanitized_alias_name;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sss_filter_sanitize_for_dom(tmp_ctx, name, domain, &sanitized_name,
+ &sanitized_alias_name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, SYSDB_NAME_FILTER, sanitized_alias_name,
+ sanitized_name);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_object_attr(mem_ctx, domain, filter, attrs, false, res);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_search_object_by_sid(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sid_str,
+ const char **attrs,
+ struct ldb_result **res)
+{
+ return sysdb_search_object_by_str_attr(mem_ctx, domain, SYSDB_SID_FILTER,
+ sid_str, attrs, true, res);
+}
+
+errno_t sysdb_search_object_by_uuid(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *uuid_str,
+ const char **attrs,
+ struct ldb_result **res)
+{
+ return sysdb_search_object_by_str_attr(mem_ctx, domain, SYSDB_UUID_FILTER,
+ uuid_str, attrs, true, res);
+}
+
+errno_t sysdb_cert_derb64_to_ldap_filter(TALLOC_CTX *mem_ctx,
+ const char *derb64,
+ const char *attr_name,
+ char **ldap_filter)
+{
+ int ret;
+ unsigned char *der;
+ size_t der_size;
+ char *val;
+
+ if (derb64 == NULL || attr_name == NULL) {
+ return EINVAL;
+ }
+
+ der = sss_base64_decode(mem_ctx, derb64, &der_size);
+ if (der == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_base64_decode failed.\n");
+ return EINVAL;
+ }
+
+ ret = bin_to_ldap_filter_value(mem_ctx, der, der_size, &val);
+ talloc_free(der);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "bin_to_ldap_filter_value failed.\n");
+ return ret;
+ }
+
+ *ldap_filter = talloc_asprintf(mem_ctx, "(%s=%s)", attr_name, val);
+ talloc_free(val);
+ if (*ldap_filter == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+errno_t sysdb_search_object_by_cert(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *cert,
+ const char **attrs,
+ struct ldb_result **res)
+{
+ int ret;
+ char *user_filter = NULL;
+ char *filter = NULL;
+
+ ret = sysdb_cert_derb64_to_ldap_filter(mem_ctx, cert,
+ SYSDB_USER_MAPPED_CERT,
+ &user_filter);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_cert_derb64_to_ldap_filter failed.\n");
+ return ret;
+ }
+
+ filter = talloc_asprintf(NULL, SYSDB_USER_CERT_FILTER, user_filter);
+ talloc_free(user_filter);
+ if (filter == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_search_object_attr(mem_ctx, domain, filter, attrs, false, res);
+
+ talloc_free(filter);
+
+ return ret;
+}
+
+errno_t sysdb_search_user_by_cert(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *cert,
+ struct ldb_result **res)
+{
+ const char *user_attrs[] = SYSDB_PW_ATTRS;
+
+ return sysdb_search_object_by_cert(mem_ctx, domain, cert, user_attrs, res);
+}
+
+errno_t sysdb_remove_mapped_data(struct sss_domain_info *domain,
+ struct sysdb_attrs *mapped_attr)
+{
+ int ret;
+ char *val;
+ char *filter;
+ const char *attrs[] = {SYSDB_NAME, NULL};
+ struct ldb_result *res = NULL;
+ size_t c;
+ bool all_ok = true;
+
+ if (mapped_attr->num != 1 || mapped_attr->a[0].num_values != 1) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unsupported number of attributes.\n");
+ return EINVAL;
+ }
+
+ ret = bin_to_ldap_filter_value(NULL, mapped_attr->a[0].values[0].data,
+ mapped_attr->a[0].values[0].length, &val);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "bin_to_ldap_filter_value failed.\n");
+ return ret;
+ }
+
+ filter = talloc_asprintf(NULL, "(&("SYSDB_UC")(%s=%s))",
+ mapped_attr->a[0].name, val);
+ talloc_free(val);
+ if (filter == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+ return ENOMEM;
+ }
+
+ ret = sysdb_search_object_attr(NULL, domain, filter, attrs, false, &res);
+ talloc_free(filter);
+ if (ret == ENOENT || res == NULL) {
+ DEBUG(SSSDBG_TRACE_ALL, "Mapped data not found.\n");
+ talloc_free(res);
+ return EOK;
+ } else if (ret != EOK) {
+ talloc_free(res);
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_object_attr failed.\n");
+ return ret;
+ }
+
+ for (c = 0; c < res->count; c++) {
+ DEBUG(SSSDBG_TRACE_ALL, "Removing mapped data from [%s].\n",
+ ldb_dn_get_linearized(res->msgs[c]->dn));
+ /* The timestamp cache is skipped on purpose here. */
+ ret = sysdb_set_cache_entry_attr(domain->sysdb->ldb, res->msgs[c]->dn,
+ mapped_attr, SYSDB_MOD_DEL);
+ if (ret != EOK) {
+ all_ok = false;
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to remove mapped data from [%s], skipping.\n",
+ ldb_dn_get_linearized(res->msgs[c]->dn));
+ }
+ }
+ talloc_free(res);
+
+ return (all_ok ? EOK : EIO);
+}
+
+errno_t sysdb_remove_cert(struct sss_domain_info *domain,
+ const char *cert)
+{
+ struct ldb_message_element el = { 0, SYSDB_USER_MAPPED_CERT, 0, NULL };
+ struct sysdb_attrs del_attrs = { 1, &el };
+ const char *attrs[] = {SYSDB_NAME, NULL};
+ struct ldb_result *res = NULL;
+ unsigned int i;
+ errno_t ret;
+
+ ret = sysdb_search_object_by_cert(NULL, domain, cert, attrs, &res);
+ if (ret == ENOENT || res == NULL) {
+ ret = EOK;
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to lookup object by cert "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ /* Certificate may be found on more objects, remove it from all.
+ * If object contains more then one certificate, we still remove the
+ * whole attribute since it will be downloaded again. */
+ for (i = 0; i < res->count; i++) {
+ ret = sysdb_set_entry_attr(domain->sysdb, res->msgs[0]->dn,
+ &del_attrs, SYSDB_MOD_DEL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to remove certificate "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_mark_entry_as_expired_ldb_dn(domain, res->msgs[0]->dn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to expire object "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ continue;
+ }
+ }
+
+done:
+ talloc_free(res);
+ return ret;
+}
+
+errno_t sysdb_get_sids_of_members(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ const char *group_name,
+ const char ***_sids,
+ const char ***_dns,
+ size_t *_n)
+{
+ errno_t ret;
+ size_t i, m_count;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ struct ldb_message **members;
+ const char *attrs[] = { SYSDB_SID_STR, NULL };
+ const char **sids = NULL, **dns = NULL;
+ size_t n = 0;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_search_group_by_name(tmp_ctx, dom, group_name, NULL, &msg);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* Get sid_str attribute of all elements pointed to by group members */
+ ret = sysdb_asq_search(tmp_ctx, dom, msg->dn, NULL, SYSDB_MEMBER, attrs,
+ &m_count, &members);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ sids = talloc_array(tmp_ctx, const char*, m_count);
+ if (sids == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ dns = talloc_array(tmp_ctx, const char*, m_count);
+ if (dns == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i=0; i < m_count; i++) {
+ const char *sidstr;
+
+ sidstr = ldb_msg_find_attr_as_string(members[i], SYSDB_SID_STR, NULL);
+
+ if (sidstr != NULL) {
+ sids[n] = talloc_steal(sids, sidstr);
+
+ dns[n] = talloc_steal(dns, ldb_dn_get_linearized(members[i]->dn));
+ if (dns[n] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ n++;
+ }
+ }
+
+ if (n == 0) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ *_n = n;
+ *_sids = talloc_steal(mem_ctx, sids);
+ *_dns = talloc_steal(mem_ctx, dns);
+
+ ret = EOK;
+
+done:
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No such entry\n");
+ } else if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_get_user_members_recursively(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ struct ldb_dn *group_dn,
+ struct ldb_result **members)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ size_t count;
+ struct ldb_result *res;
+ struct ldb_dn *base_dn;
+ char *filter;
+ char *sanitized_name;
+ const char *attrs[] = SYSDB_PW_ATTRS;
+ struct ldb_message **msgs;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ base_dn = sysdb_base_dn(dom->sysdb, tmp_ctx);
+ if (base_dn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_base_dn failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_filter_sanitize(tmp_ctx, ldb_dn_get_linearized(group_dn),
+ &sanitized_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to sanitize the given name:'%s'.\n",
+ ldb_dn_get_linearized(group_dn));
+ goto done;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, "(&("SYSDB_UC")("SYSDB_MEMBEROF"=%s))",
+ sanitized_name);
+ if (filter == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_entry(tmp_ctx, dom->sysdb, base_dn, LDB_SCOPE_SUBTREE,
+ filter, attrs, &count, &msgs);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ res = talloc_zero(tmp_ctx, struct ldb_result);
+ if (res == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ res->count = count;
+ res->msgs = talloc_steal(res, msgs);
+
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ *members = talloc_steal(mem_ctx, res);
+ } else if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No such entry\n");
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_handle_original_uuid(const char *orig_name,
+ struct sysdb_attrs *src_attrs,
+ const char *src_name,
+ struct sysdb_attrs *dest_attrs,
+ const char *dest_name)
+{
+ int ret;
+ struct ldb_message_element *el;
+ char guid_str_buf[GUID_STR_BUF_SIZE];
+
+ if (orig_name == NULL) {
+ /* This provider doesn't handle UUIDs */
+ return ENOENT;
+ }
+
+ if (src_attrs == NULL || src_name == NULL
+ || dest_attrs == NULL || dest_name == NULL) {
+ return EINVAL;
+ }
+
+ ret = sysdb_attrs_get_el_ext(src_attrs, src_name, false, &el);
+ if (ret != EOK) {
+ if (ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_el failed.\n");
+ }
+ return ret;
+ }
+
+ if (el->num_values != 1) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Found more than one UUID value, using the first.\n");
+ }
+
+ /* Check if we got a binary AD objectGUID */
+ if (el->values[0].length == GUID_BIN_LENGTH
+ && strcasecmp(orig_name, "objectGUID") == 0) {
+ ret = guid_blob_to_string_buf(el->values[0].data, guid_str_buf,
+ GUID_STR_BUF_SIZE);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "guid_blob_to_string_buf failed.\n");
+ return ret;
+ }
+
+ ret = sysdb_attrs_add_string(dest_attrs, dest_name, guid_str_buf);
+ } else {
+ ret = sysdb_attrs_add_string(dest_attrs, dest_name,
+ (const char *)el->values[0].data);
+ }
+
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n");
+ return ret;
+ }
+
+ return EOK;
+}
+
+/* Mark entry as expired */
+errno_t sysdb_mark_entry_as_expired_ldb_dn(struct sss_domain_info *dom,
+ struct ldb_dn *ldbdn)
+{
+ struct ldb_message *msg;
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = ldbdn;
+
+ ret = ldb_msg_add_empty(msg, SYSDB_CACHE_EXPIRE,
+ LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, SYSDB_CACHE_EXPIRE, "1");
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_msg_add_empty(msg, SYSDB_ORIG_MODSTAMP,
+ LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, SYSDB_ORIG_MODSTAMP, "1");
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_modify(dom->sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ if (dom->sysdb->ldb_ts != NULL) {
+ ret = ldb_modify(dom->sysdb->ldb_ts, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not mark an entry as expired in the timestamp cache\n");
+ /* non-fatal */
+ }
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_mark_entry_as_expired_ldb_val(struct sss_domain_info *dom,
+ struct ldb_val *dn_val)
+{
+ struct ldb_dn *ldbdn;
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ldbdn = ldb_dn_from_ldb_val(tmp_ctx, dom->sysdb->ldb, dn_val);
+ if (ldbdn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_mark_entry_as_expired_ldb_dn(dom, ldbdn);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/* User/group invalidation of cache by direct writing to persistent cache
+ * WARNING: This function can cause performance issue!!
+ * is_user = true --> user invalidation
+ * is_user = false --> group invalidation
+ */
+int sysdb_invalidate_cache_entry(struct sss_domain_info *domain,
+ const char *name,
+ bool is_user)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sysdb_ctx *sysdb = domain->sysdb;
+ struct ldb_dn *entry_dn = NULL;
+ struct sysdb_attrs *attrs = NULL;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ if (is_user == true) {
+ entry_dn = sysdb_user_dn(tmp_ctx, domain, name);
+ } else {
+ entry_dn = sysdb_group_dn(tmp_ctx, domain, name);
+ }
+
+ if (entry_dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ attrs = sysdb_new_attrs(tmp_ctx);
+ if (attrs == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Could not create sysdb attributes\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE, 1);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not add expiration time to attributes\n");
+ goto done;
+ }
+
+ ret = sysdb_set_cache_entry_attr(sysdb->ldb, entry_dn,
+ attrs, SYSDB_MOD_REP);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot set attrs for %s, %d [%s]\n",
+ ldb_dn_get_linearized(entry_dn), ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (sysdb->ldb_ts != NULL) {
+ ret = sysdb_set_cache_entry_attr(sysdb->ldb_ts, entry_dn,
+ attrs, SYSDB_MOD_REP);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot set attrs in the timestamp cache for %s, %d [%s]\n",
+ ldb_dn_get_linearized(entry_dn), ret, sss_strerror(ret));
+ /* non-fatal */
+ }
+ }
+
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Cache entry [%s] has been invalidated.\n",
+ ldb_dn_get_linearized(entry_dn));
+
+ ret = EOK;
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+/* === Operation On Indexes ================================== */
+errno_t sysdb_ldb_list_indexes(TALLOC_CTX *mem_ctx,
+ struct ldb_context *ldb,
+ const char *attribute,
+ const char ***_indexes)
+{
+ errno_t ret;
+ int j;
+ int i;
+ int ldb_ret;
+ unsigned int length;
+ unsigned int attr_length = (attribute == NULL ? 0 : strlen(attribute));
+ char *data;
+ struct ldb_dn *dn;
+ struct ldb_result *res;
+ struct ldb_message_element *el;
+ const char *attrs[] = { SYSDB_IDXATTR, NULL };
+ const char **indexes = NULL;
+
+ dn = ldb_dn_new(mem_ctx, ldb, SYSDB_INDEXES);
+ if (dn == NULL) {
+ ERROR_OUT(ret, EIO, done);
+ }
+
+ ldb_ret = ldb_search(ldb, mem_ctx, &res, dn, LDB_SCOPE_BASE, attrs, NULL);
+ if (ldb_ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_search() failed: %i\n", ldb_ret);
+ ERROR_OUT(ret, EIO, done);
+ }
+ if (res->count != 1) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "ldb_search() returned %u messages. Expected 1.\n", res->count);
+ ERROR_OUT(ret, EIO, done);
+ }
+ if (res->msgs[0]->num_elements != 1) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "ldb_search() returned %u elements. Expected 1.\n",
+ res->msgs[0]->num_elements);
+ ERROR_OUT(ret, EIO, done);
+ }
+
+ el = res->msgs[0]->elements;
+ j = 0;
+ indexes = talloc_zero_array(mem_ctx, const char *, 1);
+ if (indexes == NULL) ERROR_OUT(ret, ENOMEM, done);
+
+ for (i = 0; i < el->num_values; i++) {
+ data = (char *) el->values[i].data;
+ length = (int) el->values[i].length;
+ if (attr_length == 0 ||
+ (attr_length == length && strncmp(attribute, data, length) == 0)) {
+ indexes = talloc_realloc(mem_ctx, indexes, const char *, j + 2);
+ if (indexes == NULL) ERROR_OUT(ret, ENOMEM, done);
+
+ indexes[j] = talloc_asprintf(indexes, "%*s", length, data);
+ if (indexes[j] == NULL) ERROR_OUT(ret, ENOMEM, done);
+
+ indexes[++j] = NULL;
+ }
+ }
+
+ *_indexes = indexes;
+ ret = EOK;
+
+done:
+ talloc_free(dn);
+ if (ret != EOK) {
+ talloc_free(indexes);
+ }
+
+ return ret;
+}
+
+errno_t sysdb_ldb_mod_index(TALLOC_CTX *mem_ctx,
+ enum sysdb_index_actions action,
+ struct ldb_context *ldb,
+ const char *attribute)
+{
+ errno_t ret;
+ int ldb_ret;
+ struct ldb_message *msg;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ ERROR_OUT(ret, ENOMEM, done);
+ }
+
+ msg->dn = ldb_dn_new(msg, ldb, SYSDB_INDEXES);
+ if (msg->dn == NULL) {
+ ERROR_OUT(ret, EIO, done);
+ }
+
+ if (action == SYSDB_IDX_CREATE) {
+ ldb_ret = sysdb_add_string(msg, SYSDB_IDXATTR, attribute);
+ } else if (action == SYSDB_IDX_DELETE) {
+ ldb_ret = sysdb_delete_string(msg, SYSDB_IDXATTR, attribute);
+ } else {
+ ERROR_OUT(ret, EINVAL, done);
+ }
+ if (ldb_ret != LDB_SUCCESS) {
+ ERROR_OUT(ret, EIO, done);
+ }
+
+ ldb_ret = sss_ldb_modify(ldb, msg, false);
+ if (ldb_ret != LDB_SUCCESS) {
+ switch (ldb_ret) {
+ case LDB_ERR_NO_SUCH_ATTRIBUTE:
+ ret = ENOENT;
+ break;
+ case LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS:
+ ret = EEXIST;
+ break;
+ default:
+ ret = EIO;
+ }
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(msg);
+
+ return ret;
+}
+
+errno_t sysdb_manage_index(TALLOC_CTX *mem_ctx, enum sysdb_index_actions action,
+ const char *name, const char *attribute,
+ const char ***_indexes)
+{
+ errno_t ret;
+ struct ldb_context *ldb = NULL;
+
+ ret = sysdb_ldb_connect(mem_ctx, name, LDB_FLG_DONT_CREATE_DB, &ldb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_ldb_connect() failed.\n");
+ goto done;
+ }
+
+ switch (action) {
+ case SYSDB_IDX_CREATE:
+ case SYSDB_IDX_DELETE:
+ ret = sysdb_ldb_mod_index(ldb, action, ldb, attribute);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_ldb_mod_index() failed.\n");
+ goto done;
+ }
+ break;
+ case SYSDB_IDX_LIST:
+ ret = sysdb_ldb_list_indexes(mem_ctx, ldb, attribute, _indexes);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_ldb_list_indexes() failed.\n");
+ goto done;
+ }
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unknown action: %i\n", action);
+ goto done;
+ }
+
+done:
+ talloc_free(ldb);
+
+ return ret;
+}
diff --git a/src/db/sysdb_passkey_user_verification.c b/src/db/sysdb_passkey_user_verification.c
new file mode 100644
index 0000000..428225b
--- /dev/null
+++ b/src/db/sysdb_passkey_user_verification.c
@@ -0,0 +1,236 @@
+/*
+ Authors:
+ Justin Stephenson <jstephen@redhat.com>
+
+ Copyright (C) 2022 Red Hat
+
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "db/sysdb_private.h"
+
+static errno_t
+sysdb_get_passkey_user_verification_string_attr(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char **_attr)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ const char *attr;
+ const char *attrs[] = { SYSDB_PASSKEY_USER_VERIFICATION, NULL };
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = ldb_search(sysdb->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = EIO;
+ goto done;
+ }
+
+ if (res->count > 1) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Base search returned [%d] results, expected 1.\n", res->count);
+ ret = EINVAL;
+ goto done;
+ } else if (res->count == 0) {
+ ret = ENOENT;
+ goto done;
+ } else {
+ /* res->count == 1 */
+ attr = ldb_msg_find_attr_as_string(res->msgs[0],
+ SYSDB_PASSKEY_USER_VERIFICATION,
+ NULL);
+ if (attr == NULL) {
+ ret = ENOENT;
+ goto done;
+ }
+ }
+
+ *_attr = talloc_steal(mem_ctx, attr);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_get_passkey_user_verification(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char **_passkey_user_verification)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *passkey_user_verification = NULL;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_get_passkey_user_verification_string_attr(
+ tmp_ctx, sysdb, dn, &passkey_user_verification);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_get_passkey_user_verification_string_attr() failed "
+ "[%d]: [%s]",
+ ret, sss_strerror(ret));
+ goto done;
+ } else if (ret == ENOENT) {
+ *_passkey_user_verification = NULL;
+ goto done;
+ } else {
+ /* ret == EOK */
+ *_passkey_user_verification = talloc_steal(mem_ctx,
+ passkey_user_verification);
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_update_passkey_user_verification(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char *passkey_user_verification)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = dn;
+
+ ret = ldb_msg_add_empty(msg, SYSDB_PASSKEY_USER_VERIFICATION,
+ LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ if (passkey_user_verification != NULL) {
+ ret = ldb_msg_add_string(msg, SYSDB_PASSKEY_USER_VERIFICATION,
+ passkey_user_verification);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+
+ ret = ldb_modify(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "ldb_modify()_failed: [%s][%d][%s]\n",
+ ldb_strerror(ret), ret, ldb_errstring(sysdb->ldb));
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_domain_get_passkey_user_verification(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ const char *domain_name,
+ const char **_user_verification)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *dn;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_DOM_BASE, domain_name);
+ if (dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_get_passkey_user_verification(mem_ctx, sysdb, dn,
+ _user_verification);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_domain_update_passkey_user_verification(struct sysdb_ctx *sysdb,
+ const char *domain_name,
+ const char *user_verification)
+{
+
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *dn;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_DOM_BASE, domain_name);
+ if (dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_update_passkey_user_verification(sysdb, dn,
+ user_verification);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_update_passkey_user_verification() failed [%d]: [%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
diff --git a/src/db/sysdb_passkey_user_verification.h b/src/db/sysdb_passkey_user_verification.h
new file mode 100644
index 0000000..5644c3c
--- /dev/null
+++ b/src/db/sysdb_passkey_user_verification.h
@@ -0,0 +1,49 @@
+/*
+ Authors:
+ Justin Stephenson <jstephen@redhat.com>
+
+ Copyright (C) 2022 Red Hat
+
+ 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 _SYSDB_PASSKEY_USER_VERIFICATION_H_
+#define _SYSDB_PASSKEY_USER_VERIFICATION_H_
+
+#include "db/sysdb.h"
+
+/* Retrieve passkey user verification value from sysdb */
+errno_t sysdb_get_passkey_user_verification(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char **_user_verification);
+
+/* Replace passkey user verification value in sysdb with
+ * user_verification argument value */
+errno_t sysdb_update_passkey_user_verification(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char *user_verification);
+
+/* For a given domain, retrieve passkey user verification value from sysdb */
+errno_t sysdb_domain_get_passkey_user_verification(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ const char *domain_name,
+ const char **_user_verification);
+
+/* For a given domain, replace passkey user verification value from sysdb
+ * with user_verification argument value */
+errno_t sysdb_domain_update_passkey_user_verification(struct sysdb_ctx *sysdb,
+ const char *domain_name,
+ const char *user_verification);
+#endif /* _SYSDB_PASSKEY_USER_VERIFICATION_H_ */
diff --git a/src/db/sysdb_private.h b/src/db/sysdb_private.h
new file mode 100644
index 0000000..1f55007
--- /dev/null
+++ b/src/db/sysdb_private.h
@@ -0,0 +1,317 @@
+
+/*
+ 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_23 "0.23"
+#define SYSDB_VERSION_0_22 "0.22"
+#define SYSDB_VERSION_0_21 "0.21"
+#define SYSDB_VERSION_0_20 "0.20"
+#define SYSDB_VERSION_0_19 "0.19"
+#define SYSDB_VERSION_0_18 "0.18"
+#define SYSDB_VERSION_0_17 "0.17"
+#define SYSDB_VERSION_0_16 "0.16"
+#define SYSDB_VERSION_0_15 "0.15"
+#define SYSDB_VERSION_0_14 "0.14"
+#define SYSDB_VERSION_0_13 "0.13"
+#define SYSDB_VERSION_0_12 "0.12"
+#define SYSDB_VERSION_0_11 "0.11"
+#define SYSDB_VERSION_0_10 "0.10"
+#define SYSDB_VERSION_0_9 "0.9"
+#define SYSDB_VERSION_0_8 "0.8"
+#define SYSDB_VERSION_0_7 "0.7"
+#define SYSDB_VERSION_0_6 "0.6"
+#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_23
+
+#define SYSDB_BASE_LDIF \
+ "dn: @ATTRIBUTES\n" \
+ "userPrincipalName: CASE_INSENSITIVE\n" \
+ "canonicalUserPrincipalName: CASE_INSENSITIVE\n" \
+ "cn: CASE_INSENSITIVE\n" \
+ "dc: CASE_INSENSITIVE\n" \
+ "dn: CASE_INSENSITIVE\n" \
+ "originalDN: CASE_INSENSITIVE\n" \
+ "objectclass: CASE_INSENSITIVE\n" \
+ "ipHostNumber: CASE_INSENSITIVE\n" \
+ "ipNetworkNumber: 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: dataExpireTimestamp\n" \
+ "@IDXATTR: originalDN\n" \
+ "@IDXATTR: nameAlias\n" \
+ "@IDXATTR: servicePort\n" \
+ "@IDXATTR: serviceProtocol\n" \
+ "@IDXATTR: sudoUser\n" \
+ "@IDXATTR: sshKnownHostsExpire\n" \
+ "@IDXATTR: objectSIDString\n" \
+ "@IDXATTR: ghost\n" \
+ "@IDXATTR: userPrincipalName\n" \
+ "@IDXATTR: canonicalUserPrincipalName\n" \
+ "@IDXATTR: uniqueID\n" \
+ "@IDXATTR: mail\n" \
+ "@IDXATTR: userMappedCertificate\n" \
+ "@IDXATTR: ccacheFile\n" \
+ "@IDXATTR: ipHostNumber\n" \
+ "@IDXATTR: ipNetworkNumber\n" \
+ "@IDXATTR: originalADgidNumber\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" \
+ "dn: cn=ranges,cn=sysdb\n" \
+ "cn: ranges\n" \
+ "\n"
+
+/* The timestamp cache has its own versioning */
+#define SYSDB_TS_VERSION_0_2 "0.2"
+#define SYSDB_TS_VERSION_0_1 "0.1"
+
+#define SYSDB_TS_VERSION SYSDB_TS_VERSION_0_2
+
+#define SYSDB_TS_BASE_LDIF \
+ "dn: @ATTRIBUTES\n" \
+ "dn: CASE_INSENSITIVE\n" \
+ "\n" \
+ "dn: @INDEXLIST\n" \
+ "@IDXATTR: lastUpdate\n" \
+ "@IDXATTR: dataExpireTimestamp\n" \
+ "\n" \
+ "dn: cn=sysdb\n" \
+ "cn: sysdb\n" \
+ "version: " SYSDB_TS_VERSION "\n" \
+ "description: base object\n" \
+ "\n" \
+
+#include "db/sysdb.h"
+
+struct sysdb_ctx {
+ struct ldb_context *ldb;
+ char *ldb_file;
+
+ struct ldb_context *ldb_ts;
+ char *ldb_ts_file;
+
+ int transaction_nesting;
+};
+
+/* Internal utility functions */
+int sysdb_get_db_file(TALLOC_CTX *mem_ctx,
+ const char *provider,
+ const char *name,
+ const char *base_path,
+ char **_ldb_file,
+ char **_ts_file);
+errno_t sysdb_ldb_connect(TALLOC_CTX *mem_ctx,
+ const char *filename,
+ int flags,
+ struct ldb_context **_ldb);
+errno_t sysdb_ldb_mod_index(TALLOC_CTX *mem_ctx,
+ enum sysdb_index_actions action,
+ struct ldb_context *ldb,
+ const char *attribute);
+errno_t sysdb_manage_index(TALLOC_CTX *mem_ctx,
+ enum sysdb_index_actions action,
+ const char *name,
+ const char *attribute,
+ const char ***indexes);
+struct sysdb_dom_upgrade_ctx {
+ struct sss_names_ctx *names; /* upgrade to 0.18 needs to parse names */
+};
+
+int sysdb_domain_init_internal(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *db_path,
+ struct sysdb_dom_upgrade_ctx *upgrade_ctx,
+ struct sysdb_ctx **_ctx);
+
+/* Upgrade routines */
+int sysdb_upgrade_01(struct ldb_context *ldb, const char **ver);
+int sysdb_check_upgrade_02(struct sss_domain_info *domains,
+ const char *db_path);
+int sysdb_upgrade_03(struct sysdb_ctx *sysdb, const char **ver);
+int sysdb_upgrade_04(struct sysdb_ctx *sysdb, const char **ver);
+int sysdb_upgrade_05(struct sysdb_ctx *sysdb, const char **ver);
+int sysdb_upgrade_06(struct sysdb_ctx *sysdb, const char **ver);
+int sysdb_upgrade_07(struct sysdb_ctx *sysdb, const char **ver);
+int sysdb_upgrade_08(struct sysdb_ctx *sysdb, const char **ver);
+int sysdb_upgrade_09(struct sysdb_ctx *sysdb, const char **ver);
+int sysdb_upgrade_10(struct sysdb_ctx *sysdb, struct sss_domain_info *domain,
+ const char **ver);
+int sysdb_upgrade_11(struct sysdb_ctx *sysdb, struct sss_domain_info *domain,
+ const char **ver);
+int sysdb_upgrade_12(struct sysdb_ctx *sysdb, const char **ver);
+int sysdb_upgrade_13(struct sysdb_ctx *sysdb, const char **ver);
+int sysdb_upgrade_14(struct sysdb_ctx *sysdb, const char **ver);
+int sysdb_upgrade_15(struct sysdb_ctx *sysdb, const char **ver);
+int sysdb_upgrade_16(struct sysdb_ctx *sysdb, const char **ver);
+int sysdb_upgrade_17(struct sysdb_ctx *sysdb,
+ struct sysdb_dom_upgrade_ctx *upgrade_ctx,
+ const char **ver);
+int sysdb_upgrade_18(struct sysdb_ctx *sysdb, const char **ver);
+int sysdb_upgrade_19(struct sysdb_ctx *sysdb, const char **ver);
+int sysdb_upgrade_20(struct sysdb_ctx *sysdb, const char **ver);
+int sysdb_upgrade_21(struct sysdb_ctx *sysdb, const char **ver);
+int sysdb_upgrade_22(struct sysdb_ctx *sysdb, const char **ver);
+
+int sysdb_ts_upgrade_01(struct sysdb_ctx *sysdb, const char **ver);
+
+int sysdb_add_string(struct ldb_message *msg,
+ const char *attr, const char *value);
+int sysdb_replace_string(struct ldb_message *msg,
+ const char *attr, const char *value);
+int sysdb_delete_string(struct ldb_message *msg,
+ const char *attr, const char *value);
+int sysdb_add_ulong(struct ldb_message *msg,
+ const char *attr, unsigned long value);
+int sysdb_replace_ulong(struct ldb_message *msg,
+ const char *attr, unsigned long value);
+int sysdb_delete_ulong(struct ldb_message *msg,
+ const char *attr, unsigned long value);
+
+/* Helper functions to deal with the timestamp cache should not be used
+ * outside the sysdb itself. The timestamp cache should be completely
+ * opaque to the sysdb consumers
+ */
+
+/* Returns true if the 'dn' parameter is a user or a group DN, because
+ * at the moment, the timestamps cache only handles users and groups.
+ * Returns false otherwise.
+ */
+bool is_ts_ldb_dn(struct ldb_dn *dn);
+
+/* Returns true if the attrname is an attribute we store to the timestamp
+ * cache, false if it's a sysdb-only attribute
+ */
+bool is_ts_cache_attr(const char *attrname);
+
+/* Returns a subset of attrs that only contains the attributes we store to
+ * the timestamps cache. Useful in generic functions that set some attributes
+ * and we want to mirror that change in the timestamps cache
+ */
+struct sysdb_attrs *sysdb_filter_ts_attrs(TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs *attrs);
+
+/* Given a ldb_result found in the timestamp cache, merge in the
+ * corresponding full attributes from the sysdb cache. The new
+ * attributes are allocated on the messages in the ldb_result.
+ */
+errno_t sysdb_merge_res_ts_attrs(struct sysdb_ctx *ctx,
+ struct ldb_result *res,
+ const char *attrs[]);
+
+/* Given an array of ldb_message structures found in the timestamp cache,
+ * merge in the corresponding full attributes from the sysdb cache. The
+ * new attributes are allocated atop the ldb messages.
+ */
+errno_t sysdb_merge_msg_list_ts_attrs(struct sysdb_ctx *ctx,
+ size_t msgs_count,
+ struct ldb_message **msgs,
+ const char *attrs[]);
+
+/* Merge two sets of ldb_result structures. */
+struct ldb_result *sss_merge_ldb_results(struct ldb_result *res,
+ struct ldb_result *subres);
+
+/* Search Entry in an ldb cache */
+int sysdb_cache_search_entry(TALLOC_CTX *mem_ctx,
+ struct ldb_context *ldb,
+ struct ldb_dn *base_dn,
+ enum ldb_scope scope,
+ const char *filter,
+ const char **attrs,
+ size_t *_msgs_count,
+ struct ldb_message ***_msgs);
+
+/* Search Entry in the timestamp cache */
+int sysdb_search_ts_entry(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct ldb_dn *base_dn,
+ enum ldb_scope scope,
+ const char *filter,
+ const char **attrs,
+ size_t *_msgs_count,
+ struct ldb_message ***_msgs);
+
+int sysdb_search_ts_users(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter,
+ const char **attrs,
+ struct ldb_result *res);
+
+int sysdb_search_ts_groups(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter,
+ const char **attrs,
+ struct ldb_result *res);
+
+errno_t sysdb_search_ts_matches(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ const char *attrs[],
+ struct ldb_result *ts_res,
+ const char *filter,
+ struct ldb_result **_res);
+
+/* Compares the modifyTimestamp attribute between old_entry and
+ * new_entry. Returns true if they differ (or either entry is missing
+ * the attribute) and false if the attribute is the same
+ */
+bool sysdb_msg_attrs_modts_differs(struct ldb_message *old_entry,
+ struct sysdb_attrs *new_entry);
+
+/* Given a sysdb_attrs pointer, returns a corresponding ldb_message */
+struct ldb_message *sysdb_attrs2msg(TALLOC_CTX *mem_ctx,
+ struct ldb_dn *entry_dn,
+ struct sysdb_attrs *attrs,
+ int mod_op);
+
+/* Compares the attributes between the existing attributes of entry_dn and
+ * the new_entry attributes that are about to be set. If the set would
+ * not yield into any differences (and therefore a write to the cache is
+ * not necessary), the function returns false (no diff), otherwise
+ * the function returns true (a difference exists).
+ */
+bool sysdb_entry_attrs_diff(struct sysdb_ctx *sysdb,
+ struct ldb_dn *entry_dn,
+ struct sysdb_attrs *attrs,
+ int mod_op);
+
+#endif /* __INT_SYS_DB_H__ */
diff --git a/src/db/sysdb_ranges.c b/src/db/sysdb_ranges.c
new file mode 100644
index 0000000..3172a64
--- /dev/null
+++ b/src/db/sysdb_ranges.c
@@ -0,0 +1,405 @@
+/*
+ SSSD
+
+ System Database - ID ranges related calls
+
+ Copyright (C) 2012 Sumit Bose <sbose@redhat.com>
+
+ 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"
+
+static errno_t find_attr_as_uint32_t(const struct ldb_message *msg,
+ const char *attr_name, uint32_t *result)
+{
+ uint64_t val;
+
+ val = ldb_msg_find_attr_as_uint64(msg, attr_name, UINT64_MAX);
+
+ if (val == UINT64_MAX) {
+ return ENOENT;
+ } else if (val >= UINT32_MAX) {
+ return EINVAL;
+ }
+
+ *result = val;
+ return EOK;
+}
+
+errno_t sysdb_get_ranges(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
+ size_t *range_count,
+ struct range_info ***range_list)
+{
+ size_t c;
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ const char *attrs[] = {SYSDB_NAME,
+ SYSDB_BASE_ID,
+ SYSDB_ID_RANGE_SIZE,
+ SYSDB_BASE_RID,
+ SYSDB_SECONDARY_BASE_RID,
+ SYSDB_DOMAIN_ID,
+ SYSDB_ID_RANGE_TYPE,
+ SYSDB_ID_RANGE_MPG,
+ NULL};
+ struct range_info **list;
+ struct ldb_dn *basedn;
+ const char *tmp_str;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ basedn = ldb_dn_new(tmp_ctx, sysdb->ldb, SYSDB_TMPL_RANGE_BASE);
+ if (basedn == NULL) {
+ ret = EIO;
+ goto done;
+ }
+ ret = ldb_search(sysdb->ldb, tmp_ctx, &res,
+ basedn, LDB_SCOPE_SUBTREE,
+ attrs, "objectclass=%s", SYSDB_ID_RANGE_CLASS);
+ if (ret != LDB_SUCCESS) {
+ ret = EIO;
+ goto done;
+ }
+
+ list = talloc_zero_array(tmp_ctx, struct range_info *, res->count + 1);
+ if (list == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (c = 0; c < res->count; c++) {
+ list[c] = talloc_zero(list, struct range_info);
+ if (list[c] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ tmp_str = ldb_msg_find_attr_as_string(res->msgs[c], SYSDB_NAME, NULL);
+ if (tmp_str == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "The object [%s] doesn't have a name.\n",
+ ldb_dn_get_linearized(res->msgs[c]->dn));
+ ret = EINVAL;
+ goto done;
+ }
+
+ list[c]->name = talloc_strdup(list, tmp_str);
+ if (list[c]->name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tmp_str = ldb_msg_find_attr_as_string(res->msgs[c], SYSDB_DOMAIN_ID,
+ NULL);
+ if (tmp_str != NULL) {
+ list[c]->trusted_dom_sid = talloc_strdup(list, tmp_str);
+ if (list[c]->trusted_dom_sid == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = find_attr_as_uint32_t(res->msgs[c], SYSDB_BASE_ID,
+ &list[c]->base_id);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "find_attr_as_uint32_t failed.\n");
+ goto done;
+ }
+
+ ret = find_attr_as_uint32_t(res->msgs[c], SYSDB_ID_RANGE_SIZE,
+ &list[c]->id_range_size);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "find_attr_as_uint32_t failed.\n");
+ goto done;
+ }
+
+ ret = find_attr_as_uint32_t(res->msgs[c], SYSDB_BASE_RID,
+ &list[c]->base_rid);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "find_attr_as_uint32_t failed.\n");
+ goto done;
+ }
+
+ ret = find_attr_as_uint32_t(res->msgs[c], SYSDB_SECONDARY_BASE_RID,
+ &list[c]->secondary_base_rid);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "find_attr_as_uint32_t failed.\n");
+ goto done;
+ }
+
+ tmp_str = ldb_msg_find_attr_as_string(res->msgs[c], SYSDB_ID_RANGE_TYPE,
+ NULL);
+ if (tmp_str != NULL) {
+ list[c]->range_type = talloc_strdup(list, tmp_str);
+ if (list[c]->range_type == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ tmp_str = ldb_msg_find_attr_as_string(res->msgs[c], SYSDB_ID_RANGE_MPG,
+ "default");
+ list[c]->mpg_mode = str_to_domain_mpg_mode(tmp_str);
+ }
+ list[res->count] = NULL;
+
+ *range_count = res->count;
+ *range_list = talloc_steal(mem_ctx, list);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_get_range(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ const char *forest,
+ struct range_info **_range)
+{
+ struct range_info **list;
+ struct range_info *range;
+ size_t count;
+ size_t i;
+ errno_t ret;
+
+ ret = sysdb_get_ranges(NULL, sysdb, &count, &list);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ for (i = 0; i < count; i++) {
+ range = list[i];
+ if (range->trusted_dom_sid == NULL) {
+ continue;
+ }
+
+ if (strcmp(range->trusted_dom_sid, forest) != 0) {
+ continue;
+ }
+
+ *_range = talloc_steal(mem_ctx, range);
+ ret = EOK;
+ goto done;
+ }
+
+ ret = ENOENT;
+
+done:
+ talloc_free(list);
+ return ret;
+}
+
+errno_t sysdb_range_create(struct sysdb_ctx *sysdb, struct range_info *range)
+{
+ struct ldb_message *msg;
+ int ret;
+ TALLOC_CTX *tmp_ctx;
+
+ /* if both or none are set, skip */
+ if ((range->trusted_dom_sid == NULL && range->secondary_base_rid == 0) ||
+ (range->trusted_dom_sid != NULL && range->secondary_base_rid != 0)) {
+
+ DEBUG(SSSDBG_OP_FAILURE, "Invalid range, skipping. Expected that "
+ "either the secondary base RID or the SID of the trusted "
+ "domain is set, but not both or none of them.\n");
+ return EOK;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb,
+ SYSDB_TMPL_RANGE, range->name);
+ if (!msg->dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_add_string(msg, SYSDB_OBJECTCLASS, SYSDB_ID_RANGE_CLASS);
+ if (ret) goto done;
+
+ if (range->trusted_dom_sid == NULL && range->secondary_base_rid != 0) {
+ ret = sysdb_add_string(msg, SYSDB_OBJECTCLASS,
+ SYSDB_DOMAIN_ID_RANGE_CLASS);
+ if (ret) goto done;
+
+ ret = sysdb_add_ulong(msg, SYSDB_SECONDARY_BASE_RID,
+ (unsigned long) range->secondary_base_rid);
+ if (ret) goto done;
+ } else if (range->trusted_dom_sid != NULL &&
+ range->secondary_base_rid == 0) {
+ ret = sysdb_add_string(msg, SYSDB_OBJECTCLASS,
+ SYSDB_TRUSTED_AD_DOMAIN_RANGE_CLASS);
+ if (ret) goto done;
+
+ ret = sysdb_add_string(msg, SYSDB_DOMAIN_ID, range->trusted_dom_sid);
+ if (ret) goto done;
+ }
+
+ ret = sysdb_add_string(msg, SYSDB_NAME, range->name);
+ if (ret) goto done;
+
+ ret = sysdb_add_ulong(msg, SYSDB_BASE_ID, (unsigned long) range->base_id);
+ if (ret) goto done;
+
+ ret = sysdb_add_ulong(msg, SYSDB_ID_RANGE_SIZE,
+ (unsigned long) range->id_range_size);
+ if (ret) goto done;
+
+ ret = sysdb_add_ulong(msg, SYSDB_BASE_RID,
+ (unsigned long) range->base_rid);
+ if (ret) goto done;
+
+ ret = sysdb_add_ulong(msg, SYSDB_CREATE_TIME, (unsigned long)time(NULL));
+ if (ret) goto done;
+
+ ret = sysdb_add_string(msg, SYSDB_ID_RANGE_TYPE, range->range_type);
+ if (ret) goto done;
+
+ ret = sysdb_add_string(msg, SYSDB_ID_RANGE_MPG,
+ str_domain_mpg_mode(range->mpg_mode));
+ if (ret) goto done;
+
+ ret = ldb_add(sysdb->ldb, msg);
+ if (ret) goto done;
+
+ ret = sysdb_error_to_errno(ret);
+
+done:
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_update_ranges(struct sysdb_ctx *sysdb,
+ struct range_info **ranges)
+{
+ int ret;
+ int sret;
+ size_t c;
+ size_t d;
+ TALLOC_CTX *tmp_ctx = NULL;
+ size_t cur_range_count;
+ struct range_info **cur_ranges;
+ struct ldb_dn *dn;
+ bool in_transaction = false;
+ bool *keep_range;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Retrieve all ranges that are currently in sysdb */
+ ret = sysdb_get_ranges(tmp_ctx, sysdb, &cur_range_count,
+ &cur_ranges);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_ranges failed.\n");
+ goto done;
+ }
+
+ keep_range = talloc_zero_array(tmp_ctx, bool, cur_range_count);
+ if (keep_range == NULL) {
+ ret = ENOMEM;
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
+ goto done;
+ }
+
+ ret = sysdb_transaction_start(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_transaction_start failed.\n");
+ goto done;
+ }
+ in_transaction = true;
+
+ /* Go through a list of retrieved ranges and:
+ * - if a range already exists in sysdb, mark it for preservation
+ * - if the range doesn't exist in sysdb, create it
+ */
+ for (c = 0; ranges[c] != NULL; c++) {
+ for (d = 0; d < cur_range_count; d++) {
+ if (strcasecmp(ranges[c]->name, cur_ranges[d]->name) == 0) {
+ keep_range[d] = true;
+ /* range already in cache, nothing to do */
+ break;
+ }
+ }
+
+ if (d == cur_range_count) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Adding range [%s].\n", ranges[c]->name);
+ ret = sysdb_range_create(sysdb, ranges[c]);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_range_create failed.\n");
+ goto done;
+ }
+ }
+ }
+
+ /* Now delete all ranges that have been in sysdb prior to
+ * refreshing the list and are not marked for preservation
+ * (i.e. they are not in the new list of ranges)
+ */
+ for (d = 0; d < cur_range_count; d++) {
+ if (!keep_range[d]) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Removing range [%s].\n",
+ cur_ranges[d]->name);
+ dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb,
+ SYSDB_TMPL_RANGE, cur_ranges[d]->name);
+ if (dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_delete_entry(sysdb, dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_delete_entry failed.\n");
+ goto done;
+ }
+ }
+ }
+
+ ret = sysdb_transaction_commit(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not commit transaction\n");
+ goto done;
+ }
+ in_transaction = false;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
+ }
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c
new file mode 100644
index 0000000..e4c53b8
--- /dev/null
+++ b/src/db/sysdb_search.c
@@ -0,0 +1,2782 @@
+/*
+ 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>
+#include <ctype.h>
+
+
+static inline bool is_sysdb_name(const char *attr)
+{
+ return (attr != NULL && strcmp(attr, SYSDB_NAME) == 0);
+}
+
+/* helpers */
+static errno_t merge_ts_attr(struct ldb_message *ts_msg,
+ struct ldb_message *sysdb_msg,
+ const char *ts_attr,
+ const char *want_attrs[])
+{
+ errno_t ret;
+ bool include = true;
+ struct ldb_message_element *ts_el;
+ struct ldb_message_element *sysdb_el;
+
+ if (want_attrs != NULL) {
+ /* Otherwise merge all ts attrs */
+ include = string_in_list(ts_attr, discard_const(want_attrs), true);
+ }
+ if (include == false) {
+ return EOK;
+ }
+
+ ts_el = ldb_msg_find_element(ts_msg, ts_attr);
+ if (ts_el == NULL || ts_el->num_values == 0) {
+ return EOK;
+ }
+
+ if (ts_el->num_values > 1) {
+ return EIO;
+ }
+
+ sysdb_el = ldb_msg_find_element(sysdb_msg, ts_attr);
+ if (sysdb_el == NULL || sysdb_el->num_values == 0) {
+ ret = ldb_msg_add_steal_value(sysdb_msg, ts_attr, &ts_el->values[0]);
+ if (ret != EOK) {
+ return sysdb_error_to_errno(ret);
+ }
+ } else {
+ /* Assumes the timestamps cache only holds single-valued
+ * attributes */
+ sysdb_el->values = talloc_steal(sysdb_el->values, ts_el->values);
+ }
+
+ return EOK;
+}
+
+static errno_t merge_all_ts_attrs(struct ldb_message *ts_msg,
+ struct ldb_message *sysdb_msg,
+ const char *want_attrs[])
+{
+ int ret;
+
+ /* Deliberately start from 2 in order to not merge
+ * objectclass/objectcategory and avoid breaking MPGs where the OC might
+ * be made up
+ */
+ for (size_t c = 2; sysdb_ts_cache_attrs[c]; c++) {
+ ret = merge_ts_attr(ts_msg, sysdb_msg,
+ sysdb_ts_cache_attrs[c], want_attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot merge ts attr %s\n", sysdb_ts_cache_attrs[c]);
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+static errno_t merge_msg_ts_attrs(struct sysdb_ctx *sysdb,
+ struct ldb_message *sysdb_msg,
+ const char *attrs[])
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ size_t msgs_count;
+ struct ldb_message **ts_msgs;
+ bool ts_dn;
+
+ ts_dn = is_ts_ldb_dn(sysdb_msg->dn);
+ if (ts_dn == false) {
+ return ERR_NO_TS;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_search_ts_entry(tmp_ctx, sysdb, sysdb_msg->dn,
+ LDB_SCOPE_BASE,
+ NULL,
+ sysdb_ts_cache_attrs,
+ &msgs_count,
+ &ts_msgs);
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "No such DN in the timestamp cache: %s\n",
+ ldb_dn_get_linearized(sysdb_msg->dn));
+ ret = ERR_TS_CACHE_MISS;
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Cannot find TS cache entry for [%s]: [%d]: %s\n",
+ ldb_dn_get_linearized(sysdb_msg->dn),
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (msgs_count != 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Expected 1 result for base search, got %zu\n", msgs_count);
+ return EIO;
+ }
+
+ ret = merge_all_ts_attrs(ts_msgs[0], sysdb_msg, attrs);
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static errno_t merge_msg_sysdb_attrs(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct ldb_message *ts_msg,
+ struct ldb_message **_sysdb_msg,
+ const char *attrs[])
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ size_t msgs_count;
+ struct ldb_message **sysdb_msgs;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_cache_search_entry(tmp_ctx, sysdb->ldb, ts_msg->dn, LDB_SCOPE_BASE,
+ NULL, attrs, &msgs_count, &sysdb_msgs);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (msgs_count != 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Expected 1 result for base search, got %zu\n", msgs_count);
+ goto done;
+ }
+
+ ret = merge_all_ts_attrs(ts_msg, sysdb_msgs[0], attrs);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_sysdb_msg = talloc_steal(mem_ctx, sysdb_msgs[0]);
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_merge_res_ts_attrs(struct sysdb_ctx *ctx,
+ struct ldb_result *res,
+ const char *attrs[])
+{
+ errno_t ret;
+
+ if (res == NULL || ctx->ldb_ts == NULL) {
+ return EOK;
+ }
+
+ for (size_t c = 0; c < res->count; c++) {
+ ret = merge_msg_ts_attrs(ctx, res->msgs[c], attrs);
+ if (ret == ERR_NO_TS) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "TS cache doesn't handle this DN type, skipping\n");
+ continue;
+ } else if (ret == ERR_TS_CACHE_MISS) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "TS cache doesn't contain this DN, skipping\n");
+ continue;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot merge timestamp cache values for %s\n",
+ ldb_dn_get_linearized(res->msgs[c]->dn));
+ /* non-fatal */
+ continue;
+ }
+ }
+
+ return EOK;
+}
+
+static errno_t merge_res_sysdb_attrs(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *ctx,
+ struct ldb_result *ts_res,
+ struct ldb_result **_ts_cache_res,
+ const char *attrs[])
+{
+ errno_t ret;
+ size_t ts_cache_res_count = 0;
+ struct ldb_result *ts_cache_res = NULL;
+
+ if (ts_res == NULL || ctx->ldb_ts == NULL) {
+ return EOK;
+ }
+
+ ts_cache_res = talloc_zero(mem_ctx, struct ldb_result);
+ if (ts_cache_res == NULL) {
+ return ENOMEM;
+ }
+ ts_cache_res->msgs = talloc_zero_array(ts_cache_res,
+ struct ldb_message *,
+ ts_res->count);
+ if (ts_cache_res->msgs == NULL) {
+ talloc_free(ts_cache_res);
+ return ENOMEM;
+ }
+
+ for (size_t c = 0; c < ts_res->count; c++) {
+ ret = merge_msg_sysdb_attrs(ts_cache_res->msgs,
+ ctx,
+ ts_res->msgs[c],
+ &ts_cache_res->msgs[ts_cache_res_count],
+ attrs);
+ if ((ret != EOK) || (ts_cache_res->msgs[ts_cache_res_count] == NULL)) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot merge sysdb cache values for %s\n",
+ ldb_dn_get_linearized(ts_res->msgs[c]->dn));
+ /* non-fatal, just skip */
+ continue;
+ }
+ ts_cache_res_count += 1;
+ }
+ ts_cache_res->count = ts_cache_res_count;
+
+ *_ts_cache_res = ts_cache_res;
+ return EOK;
+}
+
+errno_t sysdb_merge_msg_list_ts_attrs(struct sysdb_ctx *ctx,
+ size_t msgs_count,
+ struct ldb_message **msgs,
+ const char *attrs[])
+{
+ struct ldb_result res;
+
+ res.count = msgs_count;
+ res.msgs = msgs;
+ return sysdb_merge_res_ts_attrs(ctx, &res, attrs);
+}
+
+/* users */
+
+int sysdb_getpwnam(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = SYSDB_PW_ATTRS;
+ struct ldb_dn *base_dn;
+ struct ldb_result *res;
+ char *sanitized_name;
+ char *lc_sanitized_name;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ base_dn = sysdb_user_base_dn(tmp_ctx, domain);
+ if (!base_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_filter_sanitize_for_dom(tmp_ctx, name, domain,
+ &sanitized_name, &lc_sanitized_name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, SYSDB_PWNAM_FILTER,
+ lc_sanitized_name,
+ sanitized_name, sanitized_name);
+ if (ret) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ if (res->count > 1) {
+ /* We expected either 0 or 1 result for search with
+ * SYSDB_PWNAM_FILTER, but we got more. This error
+ * is handled individually depending on what function
+ * called sysdb_getpwnam, so we just print a message
+ * here and let the caller decide what error code to
+ * propagate based on res->count > 1. */
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Search for [%s] returned multiple results. It can be an email "
+ "address shared among multiple users or an email address of a "
+ "user that conflicts with another user's fully qualified name. "
+ "SSSD will not be able to handle those users properly.\n",
+ sanitized_name);
+ }
+
+ /* Merge in the timestamps from the fast ts db */
+ ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n");
+ /* non-fatal */
+ ret = EOK;
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_getpwnam_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **res)
+{
+ int ret;
+ struct ldb_result *orig_obj = NULL;
+ struct ldb_result *override_obj = NULL;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ /* If there are views we first have to search the overrides for matches */
+ if (DOM_HAS_VIEWS(domain)) {
+ ret = sysdb_search_user_override_by_name(tmp_ctx, domain, name,
+ &override_obj, &orig_obj);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_search_override_by_name failed.\n");
+ goto done;
+ }
+ }
+
+ /* If there are no views or nothing was found in the overrides the
+ * original objects are searched. */
+ if (orig_obj == NULL) {
+ ret = sysdb_getpwnam(tmp_ctx, domain, name, &orig_obj);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_getpwnam failed.\n");
+ goto done;
+ }
+ }
+
+ /* If there are views we have to check if override values must be added to
+ * the original object. */
+ if (DOM_HAS_VIEWS(domain) && orig_obj->count == 1) {
+ ret = sysdb_add_overrides_to_object(domain, orig_obj->msgs[0],
+ override_obj == NULL ? NULL : override_obj->msgs[0],
+ NULL);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_overrides_to_object failed.\n");
+ goto done;
+ }
+
+ if (ret == ENOENT) {
+ *res = talloc_zero(mem_ctx, struct ldb_result);
+ if (*res == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ ret = ENOMEM;
+ } else {
+ ret = EOK;
+ }
+ goto done;
+ }
+ }
+
+ *res = talloc_steal(mem_ctx, orig_obj);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_getpwuid(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ uid_t uid,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ unsigned long int ul_uid = uid;
+ static const char *attrs[] = SYSDB_PW_ATTRS;
+ struct ldb_dn *base_dn;
+ struct ldb_result *res;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ base_dn = sysdb_user_base_dn(tmp_ctx, domain);
+ if (!base_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, SYSDB_PWUID_FILTER, ul_uid);
+ if (ret) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ /* Merge in the timestamps from the fast ts db */
+ ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n");
+ /* non-fatal */
+ ret = EOK;
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_getpwuid_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ uid_t uid,
+ struct ldb_result **res)
+{
+ int ret;
+ struct ldb_result *orig_obj = NULL;
+ struct ldb_result *override_obj = NULL;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ /* If there are views we first have to search the overrides for matches */
+ if (DOM_HAS_VIEWS(domain)) {
+ ret = sysdb_search_user_override_by_uid(tmp_ctx, domain, uid,
+ &override_obj, &orig_obj);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_search_user_override_by_uid failed.\n");
+ goto done;
+ }
+ }
+
+ /* If there are no views or nothing was found in the overrides the
+ * original objects are searched. */
+ if (orig_obj == NULL) {
+ ret = sysdb_getpwuid(tmp_ctx, domain, uid, &orig_obj);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_getpwuid failed.\n");
+ goto done;
+ }
+ }
+
+ /* If there are views we have to check if override values must be added to
+ * the original object. */
+ if (DOM_HAS_VIEWS(domain) && orig_obj->count == 1) {
+ ret = sysdb_add_overrides_to_object(domain, orig_obj->msgs[0],
+ override_obj == NULL ? NULL : override_obj->msgs[0],
+ NULL);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_overrides_to_object failed.\n");
+ goto done;
+ }
+
+ if (ret == ENOENT) {
+ *res = talloc_zero(mem_ctx, struct ldb_result);
+ if (*res == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ ret = ENOMEM;
+ } else {
+ ret = EOK;
+ }
+ goto done;
+ }
+ }
+
+ *res = talloc_steal(mem_ctx, orig_obj);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static char *filter_attribute(TALLOC_CTX *mem_ctx,
+ const char *attr,
+ const char *attr_filter,
+ const char *domain,
+ char *filter)
+{
+ char *new_filter = NULL;
+ const char *value;
+
+ if (filter == NULL) {
+ return NULL;
+ }
+
+ if (domain != NULL && is_sysdb_name(attr)) {
+ value = sss_create_internal_fqname(mem_ctx, attr_filter, domain);
+ } else {
+ value = attr_filter;
+ }
+
+ if (value != NULL) {
+ new_filter = talloc_asprintf_append(filter, "(%s=%s)", attr, value);
+ }
+
+ return new_filter;
+}
+
+static char *enum_filter(TALLOC_CTX *mem_ctx,
+ const char *base_filter,
+ const char *attr,
+ const char *attr_filter,
+ const char *domain,
+ const char *addtl_filter)
+{
+ char *filter;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return NULL;
+ }
+
+ if ((attr == NULL || attr_filter == NULL) && addtl_filter == NULL) {
+ filter = talloc_strdup(tmp_ctx, base_filter);
+ } else {
+ filter = talloc_asprintf(tmp_ctx, "(&%s", base_filter);
+
+ if (filter != NULL && attr != NULL && attr_filter != NULL) {
+ filter = filter_attribute(tmp_ctx, attr, attr_filter, domain, filter);
+ }
+
+ if (filter != NULL && addtl_filter != NULL) {
+ filter = talloc_asprintf_append(filter, "%s", addtl_filter);
+ }
+
+ if (filter != NULL) {
+ filter = talloc_asprintf_append(filter, ")");
+ }
+ }
+
+ if (filter) {
+ talloc_steal(mem_ctx, filter);
+ }
+
+ talloc_free(tmp_ctx);
+ return filter;
+}
+
+int sysdb_getpwupn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ bool domain_scope,
+ const char *upn,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ static const char *attrs[] = SYSDB_PW_ATTRS;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ ret = sysdb_search_user_by_upn_res(tmp_ctx, domain, domain_scope, upn, attrs, &res);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_user_by_upn_res() failed.\n");
+ goto done;
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_search_ts_matches(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ const char *attrs[],
+ struct ldb_result *ts_res,
+ const char *filter,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct ldb_result *res;
+ errno_t ret;
+
+ if (ts_res->count == 0) {
+ *_res = NULL;
+ ret = EOK;
+ goto done;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ res = talloc_zero(tmp_ctx, struct ldb_result);
+ if (res == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_search(sysdb->ldb, tmp_ctx, &res, NULL,
+ LDB_SCOPE_SUBTREE, attrs, "%s", filter);
+ if (ret) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+ ret = EOK;
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_search_with_ts_attr(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_dn *base_dn,
+ enum ldb_scope scope,
+ enum sysdb_cache_type search_cache,
+ const char *filter,
+ const char *attrs[],
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct ldb_result *res;
+ errno_t ret;
+ struct ldb_message **ts_msgs = NULL;
+ struct ldb_result *ts_cache_res = NULL;
+ size_t ts_count;
+
+ if (filter == NULL) {
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ res = talloc_zero(tmp_ctx, struct ldb_result);
+ if (res == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ switch (search_cache) {
+ case SYSDB_CACHE_TYPE_PERSISTENT: {
+ /* We only care about searching the persistent db */
+ ts_cache_res = talloc_zero(tmp_ctx, struct ldb_result);
+ if (ts_cache_res == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ts_cache_res->count = 0;
+ ts_cache_res->msgs = NULL;
+
+ break;
+ }
+
+ case SYSDB_CACHE_TYPE_TIMESTAMP:
+ /* FALLTHOUGH*/
+ SSS_ATTRIBUTE_FALLTHROUGH;
+ default: {
+ /* Because the timestamp database does not contain all the
+ * attributes, we need to search the persistent db for each
+ * of the entries found and merge the results
+ */
+ struct ldb_result ts_res;
+
+ /* We assume that some of the attributes are more up-to-date in
+ * timestamps db and we're supposed to search by them, so let's
+ * first search the timestamp db
+ */
+ ret = sysdb_search_ts_entry(tmp_ctx, domain->sysdb, base_dn,
+ scope, filter, attrs,
+ &ts_count, &ts_msgs);
+ if (ret == ENOENT) {
+ ts_count = 0;
+ } else if (ret != EOK) {
+ goto done;
+ }
+
+ memset(&ts_res, 0, sizeof(struct ldb_result));
+ ts_res.count = ts_count;
+ ts_res.msgs = ts_msgs;
+
+ /* Overlay the results from the main cache with the ts attrs */
+ ret = merge_res_sysdb_attrs(tmp_ctx,
+ domain->sysdb,
+ &ts_res,
+ &ts_cache_res,
+ attrs);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ break;
+ }
+ }
+
+ switch (search_cache) {
+ case SYSDB_CACHE_TYPE_TIMESTAMP: {
+ /* The filter only contains timestamp attrs, no need to search the
+ * persistent db
+ */
+ if (ts_cache_res) {
+ res->count = ts_cache_res->count;
+ res->msgs = talloc_steal(res, ts_cache_res->msgs);
+ }
+
+ break;
+ }
+
+ case SYSDB_CACHE_TYPE_PERSISTENT:
+ /* FALLTHOUGH*/
+ SSS_ATTRIBUTE_FALLTHROUGH;
+ default: {
+ /* Because some of the attributes being searched might exist in the persistent
+ * database only, we also search the persistent db
+ */
+ size_t count;
+
+ ret = sysdb_search_entry(res, domain->sysdb, base_dn, scope,
+ filter, attrs, &count, &res->msgs);
+ if (ret == ENOENT) {
+ res->count = 0;
+ } else if (ret != EOK) {
+ goto done;
+ }
+ res->count = count; /* Just to cleanly assign size_t to unsigned */
+
+ res = sss_merge_ldb_results(res, ts_cache_res);
+ if (res == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ break;
+ }
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+ ret = EOK;
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static errno_t sysdb_enum_dn_filter(TALLOC_CTX *mem_ctx,
+ struct ldb_result *ts_res,
+ const char *name_filter,
+ const char *domain,
+ char **_dn_filter)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ char *dn_filter;
+ const char *fqname;
+ errno_t ret;
+
+ if (ts_res->count == 0) {
+ *_dn_filter = NULL;
+ ret = EOK;
+ goto done;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ if (name_filter == NULL) {
+ dn_filter = talloc_asprintf(tmp_ctx, "(|");
+ } else {
+ fqname = sss_create_internal_fqname(tmp_ctx, name_filter, domain);
+ if (fqname == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ dn_filter = talloc_asprintf(tmp_ctx, "(&(%s=%s)(|", SYSDB_NAME, fqname);
+ }
+ if (dn_filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (size_t i = 0; i < ts_res->count; i++) {
+ dn_filter = talloc_asprintf_append(
+ dn_filter,
+ "(%s=%s)",
+ SYSDB_DN,
+ ldb_dn_get_linearized(ts_res->msgs[i]->dn));
+ if (dn_filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ dn_filter = talloc_asprintf_append(dn_filter, (name_filter == NULL ? ")" : "))"));
+ if (dn_filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ *_dn_filter = talloc_steal(mem_ctx, dn_filter);
+ ret = EOK;
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_enumpwent_filter(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *attr,
+ const char *attr_filter,
+ const char *addtl_filter,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = SYSDB_PW_ATTRS;
+ char *filter = NULL;
+ char *dn_filter = NULL;
+ const char *ts_filter = NULL;
+ struct ldb_dn *base_dn;
+ struct ldb_result *res;
+ struct ldb_result ts_res;
+ struct ldb_result *ts_cache_res = NULL;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ base_dn = sysdb_user_base_dn(tmp_ctx, domain);
+ if (!base_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Do not look for the user's attribute in the timestamp db as it could
+ * not be present. Only look for the name. */
+ if (attr == NULL || is_sysdb_name(attr)) {
+ ts_filter = enum_filter(tmp_ctx, SYSDB_PWENT_FILTER,
+ NULL, NULL, NULL, addtl_filter);
+ if (ts_filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_LIBS, "Searching timestamp cache with [%s]\n",
+ ts_filter);
+
+ ret = sysdb_search_ts_users(tmp_ctx, domain, ts_filter,
+ sysdb_ts_cache_attrs,
+ &ts_res);
+ if (ret == ERR_NO_TS) {
+ ret = ENOENT;
+ }
+
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+
+ ret = sysdb_enum_dn_filter(tmp_ctx, &ts_res, attr_filter, domain->name,
+ &dn_filter);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Searching timestamp entries with [%s]\n",
+ dn_filter);
+
+ ret = sysdb_search_ts_matches(tmp_ctx, domain->sysdb, attrs, &ts_res,
+ dn_filter, &ts_cache_res);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+ }
+
+ filter = enum_filter(tmp_ctx, SYSDB_PWENT_FILTER,
+ attr, attr_filter, domain->name, addtl_filter);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_LIBS, "Searching cache with [%s]\n", filter);
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, "%s", filter);
+ if (ret) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ /* Merge in the timestamps from the fast ts db */
+ ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n");
+ /* non-fatal */
+ ret = EOK;
+ }
+
+ if (ts_cache_res != NULL) {
+ res = sss_merge_ldb_results(res, ts_cache_res);
+ if (res == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_enumpwent(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result **_res)
+{
+ return sysdb_enumpwent_filter(mem_ctx, domain, NULL, NULL, NULL, _res);
+}
+
+int sysdb_enumpwent_filter_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *attr,
+ const char *attr_filter,
+ const char *addtl_filter,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ size_t c;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ ret = sysdb_enumpwent_filter(tmp_ctx, domain, attr, attr_filter,
+ addtl_filter, &res);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_enumpwent failed.\n");
+ goto done;
+ }
+
+ if (DOM_HAS_VIEWS(domain)) {
+ for (c = 0; c < res->count; c++) {
+ ret = sysdb_add_overrides_to_object(domain, res->msgs[c], NULL,
+ NULL);
+ /* enumeration assumes that the cache is up-to-date, hence we do not
+ * need to handle ENOENT separately. */
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_overrides_to_object failed.\n");
+ goto done;
+ }
+ }
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_enumpwent_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result **_res)
+{
+ return sysdb_enumpwent_filter_with_views(mem_ctx, domain, NULL, NULL, NULL, _res);
+}
+
+/* groups */
+
+static int mpg_convert(struct ldb_message *msg)
+{
+ struct ldb_message_element *el;
+ struct ldb_val *val = NULL;
+ int i;
+
+ el = ldb_msg_find_element(msg, SYSDB_OBJECTCATEGORY);
+ 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 int mpg_res_convert(struct ldb_result *res)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < res->count; i++) {
+ ret = mpg_convert(res->msgs[i]);
+ if (ret) {
+ return ret;
+ }
+ }
+ return EOK;
+}
+
+int sysdb_getgrnam_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **res)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ struct ldb_result *orig_obj = NULL;
+ struct ldb_result *override_obj = NULL;
+ struct ldb_message_element *el;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ /* If there are views we first have to search the overrides for matches */
+ if (DOM_HAS_VIEWS(domain)) {
+ ret = sysdb_search_group_override_by_name(tmp_ctx, domain, name,
+ &override_obj, &orig_obj);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_search_group_override_by_name failed.\n");
+ goto done;
+ }
+ }
+
+ /* If there are no views or nothing was found in the overrides the
+ * original objects are searched. */
+ if (orig_obj == NULL) {
+ ret = sysdb_getgrnam(tmp_ctx, domain, name, &orig_obj);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_getgrnam failed.\n");
+ goto done;
+ }
+ }
+
+ /* If there are views we have to check if override values must be added to
+ * the original object. */
+ if (orig_obj->count == 1) {
+ if (DOM_HAS_VIEWS(domain)) {
+ if (!is_local_view(domain->view_name)) {
+ el = ldb_msg_find_element(orig_obj->msgs[0], SYSDB_GHOST);
+ if (el != NULL && el->num_values != 0) {
+ DEBUG(SSSDBG_TRACE_ALL, "Group object [%s], contains ghost "
+ "entries which must be resolved before overrides can be "
+ "applied.\n",
+ ldb_dn_get_linearized(orig_obj->msgs[0]->dn));
+ ret = ENOENT;
+ goto done;
+ }
+ }
+
+ ret = sysdb_add_overrides_to_object(domain, orig_obj->msgs[0],
+ override_obj == NULL ? NULL : override_obj ->msgs[0],
+ NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_overrides_to_object failed.\n");
+ goto done;
+ }
+ }
+
+ /* Must be called even without views to check to
+ * SYSDB_DEFAULT_OVERRIDE_NAME */
+ ret = sysdb_add_group_member_overrides(domain, orig_obj->msgs[0],
+ DOM_HAS_VIEWS(domain));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_add_group_member_overrides failed.\n");
+ goto done;
+ }
+ }
+
+ *res = talloc_steal(mem_ctx, orig_obj);
+ ret = EOK;
+
+done:
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_ALL, "Returning empty result.\n");
+ *res = talloc_zero(mem_ctx, struct ldb_result);
+ if (*res == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ ret = ENOMEM;
+ } else {
+ ret = EOK;
+ }
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_getgrnam(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = SYSDB_GRSRC_ATTRS;
+ const char *fmt_filter;
+ char *sanitized_name;
+ struct ldb_dn *base_dn;
+ struct ldb_result *res = NULL;
+ char *lc_sanitized_name;
+ const char *originalad_sanitized_name;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sss_filter_sanitize_for_dom(tmp_ctx, name, domain,
+ &sanitized_name, &lc_sanitized_name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (sss_domain_is_mpg(domain)) {
+ /* In case the domain supports magic private groups we *must*
+ * check whether the searched name is the very same as the
+ * originalADname attribute.
+ *
+ * In case those are not the same, we're dealing with an
+ * override and in order to return the proper overridden group
+ * we must use the very same search used by a non-mpg domain
+ */
+ fmt_filter = SYSDB_GRNAM_MPG_FILTER;
+ base_dn = sysdb_domain_dn(tmp_ctx, domain);
+ if (base_dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, fmt_filter,
+ lc_sanitized_name, sanitized_name, sanitized_name);
+ if (ret != EOK) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ if (res->count > 0) {
+ originalad_sanitized_name = ldb_msg_find_attr_as_string(
+ res->msgs[0], ORIGINALAD_PREFIX SYSDB_NAME, NULL);
+
+ if (originalad_sanitized_name != NULL
+ && !sss_string_equal(domain->case_sensitive,
+ originalad_sanitized_name,
+ sanitized_name)) {
+ fmt_filter = SYSDB_GRNAM_FILTER;
+ base_dn = sysdb_group_base_dn(tmp_ctx, domain);
+ res = NULL;
+ }
+ }
+ } else {
+ fmt_filter = SYSDB_GRNAM_FILTER;
+ base_dn = sysdb_group_base_dn(tmp_ctx, domain);
+ }
+ if (base_dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* We just do the ldb_search here in case domain is *not* a MPG *or*
+ * it's a MPG and we're dealing with a overridden group, which has to
+ * use the very same filter as a non MPG domain. */
+ if (res == NULL) {
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, fmt_filter,
+ lc_sanitized_name, sanitized_name, sanitized_name);
+ if (ret != EOK) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+
+ ret = mpg_res_convert(res);
+ if (ret) {
+ goto done;
+ }
+
+ ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n");
+ /* non-fatal */
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_getgrgid_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ gid_t gid,
+ struct ldb_result **res)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ struct ldb_result *orig_obj = NULL;
+ struct ldb_result *override_obj = NULL;
+ struct ldb_message_element *el;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ /* If there are views we first have to search the overrides for matches */
+ if (DOM_HAS_VIEWS(domain)) {
+ ret = sysdb_search_group_override_by_gid(tmp_ctx, domain, gid,
+ &override_obj, &orig_obj);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_search_group_override_by_gid failed.\n");
+ goto done;
+ }
+ }
+
+ /* If there are no views or nothing was found in the overrides the
+ * original objects are searched. */
+ if (orig_obj == NULL) {
+ ret = sysdb_getgrgid(tmp_ctx, domain, gid, &orig_obj);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_getgrgid failed.\n");
+ goto done;
+ }
+ }
+
+ /* If there are views we have to check if override values must be added to
+ * the original object. */
+ if (orig_obj->count == 1) {
+ if (DOM_HAS_VIEWS(domain)) {
+ if (!is_local_view(domain->view_name)) {
+ el = ldb_msg_find_element(orig_obj->msgs[0], SYSDB_GHOST);
+ if (el != NULL && el->num_values != 0) {
+ DEBUG(SSSDBG_TRACE_ALL, "Group object [%s], contains ghost "
+ "entries which must be resolved before overrides can be "
+ "applied.\n",
+ ldb_dn_get_linearized(orig_obj->msgs[0]->dn));
+ ret = ENOENT;
+ goto done;
+ }
+ }
+
+ ret = sysdb_add_overrides_to_object(domain, orig_obj->msgs[0],
+ override_obj == NULL ? NULL : override_obj ->msgs[0],
+ NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_overrides_to_object failed.\n");
+ goto done;
+ }
+ }
+
+ /* Must be called even without views to check to
+ * SYSDB_DEFAULT_OVERRIDE_NAME */
+ ret = sysdb_add_group_member_overrides(domain, orig_obj->msgs[0],
+ DOM_HAS_VIEWS(domain));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_add_group_member_overrides failed.\n");
+ goto done;
+ }
+ }
+
+ *res = talloc_steal(mem_ctx, orig_obj);
+ ret = EOK;
+
+done:
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_ALL, "Returning empty result.\n");
+ *res = talloc_zero(mem_ctx, struct ldb_result);
+ if (*res == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ ret = ENOMEM;
+ } else {
+ ret = EOK;
+ }
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_getgrgid_attrs(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ gid_t gid,
+ const char **additional_attrs,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ unsigned long int ul_gid = gid;
+ unsigned long int ul_originalad_gid;
+ const char *fmt_filter;
+ struct ldb_dn *base_dn;
+ struct ldb_result *res = NULL;
+ int ret;
+ static const char *default_attrs[] = SYSDB_GRSRC_ATTRS;
+ const char **attrs = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ if (additional_attrs == NULL) {
+ attrs = default_attrs;
+ } else {
+ ret = add_strings_lists(tmp_ctx, additional_attrs, default_attrs,
+ false, discard_const(&attrs));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "add_strings_lists failed.\n");
+ goto done;
+ }
+ }
+
+ if (sss_domain_is_mpg(domain)) {
+ /* In case the domain supports magic private groups we *must*
+ * check whether the searched gid is the very same as the
+ * originalADgidNumber attribute.
+ *
+ * In case those are not the same, we're dealing with an
+ * override and in order to return the proper overridden group
+ * we must use the very same search used by a non-mpg domain
+ */
+ fmt_filter = SYSDB_GRGID_MPG_FILTER;
+ base_dn = sysdb_domain_dn(tmp_ctx, domain);
+ if (base_dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, fmt_filter, ul_gid, ul_gid, ul_gid);
+ if (ret != EOK) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ if (res->count > 0) {
+ ul_originalad_gid = ldb_msg_find_attr_as_uint64(
+ res->msgs[0], ORIGINALAD_PREFIX SYSDB_GIDNUM, 0);
+
+ if (ul_originalad_gid != 0 && ul_originalad_gid != ul_gid) {
+ fmt_filter = SYSDB_GRGID_FILTER;
+ base_dn = sysdb_group_base_dn(tmp_ctx, domain);
+ res = NULL;
+ }
+ }
+ } else {
+ fmt_filter = SYSDB_GRGID_FILTER;
+ base_dn = sysdb_group_base_dn(tmp_ctx, domain);
+ }
+ if (base_dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* We just do the ldb_search here in case domain is *not* a MPG *or*
+ * it's a MPG and we're dealing with a overridden group, which has to
+ * use the very same filter as a non MPG domain. */
+ if (res == NULL) {
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, fmt_filter, ul_gid);
+ if (ret != EOK) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+
+ ret = mpg_res_convert(res);
+ if (ret) {
+ goto done;
+ }
+
+ ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n");
+ /* non-fatal */
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_getgrgid(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ gid_t gid,
+ struct ldb_result **_res)
+{
+ return sysdb_getgrgid_attrs(mem_ctx, domain, gid, NULL, _res);
+}
+
+int sysdb_enumgrent_filter(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name_filter,
+ const char *addtl_filter,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = SYSDB_GRSRC_ATTRS;
+ const char *filter = NULL;
+ const char *ts_filter = NULL;
+ const char *base_filter;
+ char *dn_filter = NULL;
+ struct ldb_dn *base_dn;
+ struct ldb_result *res;
+ struct ldb_result ts_res;
+ struct ldb_result *ts_cache_res;
+ int ret, lret;
+
+ if (_res == NULL) {
+ return EINVAL;
+ }
+ *_res = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ if (sss_domain_is_mpg(domain)) {
+ base_filter = SYSDB_GRENT_MPG_FILTER;
+ base_dn = sysdb_domain_dn(tmp_ctx, domain);
+ } else {
+ base_filter = SYSDB_GRENT_FILTER;
+ base_dn = sysdb_group_base_dn(tmp_ctx, domain);
+ }
+ if (!base_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ts_filter = enum_filter(tmp_ctx, base_filter,
+ NULL, NULL, NULL, addtl_filter);
+ if (ts_filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_LIBS, "Searching timestamp cache with [%s]\n", ts_filter);
+
+ ret = sysdb_search_ts_groups(tmp_ctx, domain, ts_filter,
+ sysdb_ts_cache_attrs,
+ &ts_res);
+ if (ret == ERR_NO_TS) {
+ ret = ENOENT;
+ }
+
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+
+ ret = sysdb_enum_dn_filter(tmp_ctx, &ts_res, name_filter, domain->name,
+ &dn_filter);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_search_ts_matches(tmp_ctx, domain->sysdb, attrs, &ts_res,
+ dn_filter, &ts_cache_res);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+
+ filter = enum_filter(tmp_ctx, base_filter,
+ SYSDB_NAME, name_filter, domain->name, addtl_filter);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_LIBS, "Searching cache with [%s]\n", filter);
+
+ lret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, "%s", filter);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ ret = mpg_res_convert(res);
+ if (ret) {
+ goto done;
+ }
+
+ /* Merge in the timestamps from the fast ts db */
+ ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n");
+ /* non-fatal */
+ ret = EOK;
+ }
+
+ res = sss_merge_ldb_results(res, ts_cache_res);
+ if (res == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_enumgrent(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result **_res)
+{
+ return sysdb_enumgrent_filter(mem_ctx, domain, NULL, 0, _res);
+}
+
+int sysdb_enumgrent_filter_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name_filter,
+ const char *addtl_filter,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ size_t c;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ ret = sysdb_enumgrent_filter(tmp_ctx, domain, name_filter, addtl_filter, &res);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_enumgrent failed.\n");
+ goto done;
+ }
+
+ for (c = 0; c < res->count; c++) {
+ if (DOM_HAS_VIEWS(domain)) {
+ ret = sysdb_add_overrides_to_object(domain, res->msgs[c], NULL,
+ NULL);
+ /* enumeration assumes that the cache is up-to-date, hence we do not
+ * need to handle ENOENT separately. */
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_overrides_to_object failed.\n");
+ goto done;
+ }
+ }
+
+ ret = sysdb_add_group_member_overrides(domain, res->msgs[c],
+ DOM_HAS_VIEWS(domain));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_add_group_member_overrides failed.\n");
+ goto done;
+ }
+ }
+
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_enumgrent_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result **_res)
+{
+ return sysdb_enumgrent_filter_with_views(mem_ctx, domain, NULL, NULL, _res);
+}
+
+int sysdb_initgroups(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ struct ldb_dn *user_dn;
+ struct ldb_request *req;
+ struct ldb_control **ctrl;
+ struct ldb_asq_control *control;
+ static const char *attrs[] = SYSDB_INITGR_ATTRS;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_getpwnam(tmp_ctx, domain, name, &res);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_getpwnam failed: [%d][%s]\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ if (res->count == 0) {
+ /* User is not cached yet */
+ *_res = talloc_steal(mem_ctx, res);
+ ret = EOK;
+ goto done;
+
+ } else if (res->count != 1) {
+ ret = EIO;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sysdb_getpwnam returned count: [%d]\n", res->count);
+ goto done;
+ }
+
+ /* no need to steal the dn, we are not freeing the result */
+ user_dn = res->msgs[0]->dn;
+
+ /* note we count on the fact that the default search callback
+ * will just keep appending values. This is by design and can't
+ * change so it is ok to already have a result (from the getpwnam)
+ * even before we call the next search */
+
+ ctrl = talloc_array(tmp_ctx, struct ldb_control *, 2);
+ if (!ctrl) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ctrl[1] = NULL;
+ ctrl[0] = talloc(ctrl, struct ldb_control);
+ if (!ctrl[0]) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ctrl[0]->oid = LDB_CONTROL_ASQ_OID;
+ ctrl[0]->critical = 1;
+ control = talloc(ctrl[0], struct ldb_asq_control);
+ if (!control) {
+ ret = ENOMEM;
+ goto done;
+ }
+ control->request = 1;
+ control->source_attribute = talloc_strdup(control, SYSDB_INITGR_ATTR);
+ if (!control->source_attribute) {
+ ret = ENOMEM;
+ goto done;
+ }
+ control->src_attr_len = strlen(control->source_attribute);
+ ctrl[0]->data = control;
+
+ ret = ldb_build_search_req(&req, domain->sysdb->ldb, tmp_ctx,
+ user_dn, LDB_SCOPE_BASE,
+ SYSDB_INITGR_FILTER, attrs, ctrl,
+ res, ldb_search_default_callback,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_request(domain->sysdb->ldb, req);
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_initgroups_by_upn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *upn,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ struct ldb_result *res;
+ const char *sysdb_name;
+ static const char *attrs[] = SYSDB_INITGR_ATTRS;
+ size_t i;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ ret = sysdb_search_user_by_upn(tmp_ctx, domain, false, upn, attrs, &msg);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_user_by_upn() failed.\n");
+ goto done;
+ }
+
+ res = talloc_zero(tmp_ctx, struct ldb_result);
+ if (res == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero() failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (ret == ENOENT) {
+ res->count = 0;
+ res->msgs = NULL;
+ } else {
+ sysdb_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+ if (sysdb_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Sysdb entry does not have a name.\n");
+ return EINVAL;
+ }
+
+ ret = sysdb_initgroups(tmp_ctx, domain, sysdb_name, &res);
+ if (ret == EOK && DOM_HAS_VIEWS(domain)) {
+ for (i = 0; i < res->count; i++) {
+ ret = sysdb_add_overrides_to_object(domain, res->msgs[i],
+ NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_add_overrides_to_object() failed.\n");
+ return ret;
+ }
+ }
+ }
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_initgroups_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ struct ldb_dn *user_dn;
+ struct ldb_request *req;
+ struct ldb_control **ctrl;
+ struct ldb_asq_control *control;
+ static const char *attrs[] = SYSDB_INITGR_ATTRS;
+ int ret;
+ size_t c;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_getpwnam_with_views(tmp_ctx, domain, name, &res);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_getpwnam failed: [%d][%s]\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ if (res->count == 0) {
+ /* User is not cached yet */
+ *_res = talloc_steal(mem_ctx, res);
+ ret = EOK;
+ goto done;
+
+ } else if (res->count != 1) {
+ ret = EIO;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sysdb_getpwnam returned count: [%d]\n", res->count);
+ goto done;
+ }
+
+ /* no need to steal the dn, we are not freeing the result */
+ user_dn = res->msgs[0]->dn;
+
+ /* note we count on the fact that the default search callback
+ * will just keep appending values. This is by design and can't
+ * change so it is ok to already have a result (from the getpwnam)
+ * even before we call the next search */
+
+ ctrl = talloc_array(tmp_ctx, struct ldb_control *, 2);
+ if (!ctrl) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ctrl[1] = NULL;
+ ctrl[0] = talloc(ctrl, struct ldb_control);
+ if (!ctrl[0]) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ctrl[0]->oid = LDB_CONTROL_ASQ_OID;
+ ctrl[0]->critical = 1;
+ control = talloc(ctrl[0], struct ldb_asq_control);
+ if (!control) {
+ ret = ENOMEM;
+ goto done;
+ }
+ control->request = 1;
+ control->source_attribute = talloc_strdup(control, SYSDB_INITGR_ATTR);
+ if (!control->source_attribute) {
+ ret = ENOMEM;
+ goto done;
+ }
+ control->src_attr_len = strlen(control->source_attribute);
+ ctrl[0]->data = control;
+
+ ret = ldb_build_search_req(&req, domain->sysdb->ldb, tmp_ctx,
+ user_dn, LDB_SCOPE_BASE,
+ SYSDB_INITGR_FILTER, attrs, ctrl,
+ res, ldb_search_default_callback,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_request(domain->sysdb->ldb, req);
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ if (DOM_HAS_VIEWS(domain)) {
+ /* Skip user entry because it already has override values added */
+ for (c = 1; c < res->count; c++) {
+ ret = sysdb_add_overrides_to_object(domain, res->msgs[c], NULL,
+ NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_add_overrides_to_object failed.\n");
+ goto done;
+ }
+ }
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_get_user_attr(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char **attributes,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *base_dn;
+ struct ldb_result *res;
+ char *sanitized_name;
+ char *lc_sanitized_name;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ base_dn = sysdb_user_base_dn(tmp_ctx, domain);
+ if (!base_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_filter_sanitize_for_dom(tmp_ctx, name, domain,
+ &sanitized_name, &lc_sanitized_name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attributes,
+ SYSDB_PWNAM_FILTER, lc_sanitized_name, sanitized_name,
+ sanitized_name);
+ if (ret) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ /* Merge in the timestamps from the fast ts db */
+ ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attributes);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n");
+ /* non-fatal */
+ ret = EOK;
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_get_user_attr_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char **attributes,
+ struct ldb_result **_res)
+{
+ int ret;
+ struct ldb_result *orig_obj = NULL;
+ struct ldb_result *override_obj = NULL;
+ const char **attrs = NULL;
+ const char *mandatory_override_attrs[] = {SYSDB_OVERRIDE_DN,
+ SYSDB_OVERRIDE_OBJECT_DN,
+ NULL};
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ attrs = attributes;
+
+ /* If there are views we first have to search the overrides for matches */
+ if (DOM_HAS_VIEWS(domain)) {
+ ret = add_strings_lists(tmp_ctx, attributes, mandatory_override_attrs,
+ false, discard_const(&attrs));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "add_strings_lists failed.\n");
+ goto done;
+ }
+
+ ret = sysdb_search_user_override_attrs_by_name(tmp_ctx, domain, name,
+ attrs, &override_obj, &orig_obj);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_search_user_override_attrs_by_name failed.\n");
+ return ret;
+ }
+ }
+
+ /* If there are no views or nothing was found in the overrides the
+ * original objects are searched. */
+ if (orig_obj == NULL) {
+ ret = sysdb_get_user_attr(tmp_ctx, domain, name, attrs, &orig_obj);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_user_attr failed.\n");
+ return ret;
+ }
+ }
+
+ /* If there are views we have to check if override values must be added to
+ * the original object. */
+ if (DOM_HAS_VIEWS(domain) && orig_obj->count == 1) {
+ ret = sysdb_add_overrides_to_object(domain, orig_obj->msgs[0],
+ override_obj == NULL ? NULL : override_obj ->msgs[0],
+ attrs);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_overrides_to_object failed.\n");
+ return ret;
+ }
+
+ if (ret == ENOENT) {
+ *_res = talloc_zero(mem_ctx, struct ldb_result);
+ if (*_res == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ ret = ENOMEM;
+ } else {
+ ret = EOK;
+ }
+ goto done;
+ }
+ }
+
+ *_res = talloc_steal(mem_ctx, orig_obj);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+
+/* Get string until the first delimiter and strip out
+ * leading and trailing whitespaces.
+ */
+static errno_t sysdb_netgr_split_triple_string(TALLOC_CTX *mem_ctx,
+ const char **in,
+ const char delimiter,
+ char **out)
+{
+ size_t len;
+ const char *p = *in;
+ const char *begin;
+
+ /* Remove any leading whitespace */
+ while (*p && isspace(*p)) p++;
+ begin = p;
+
+ /* Find the delimiter */
+ while (*p && *p != delimiter) p++;
+
+ if (!*p) {
+ /* No delimiter was found: parse error */
+ return EINVAL;
+ }
+
+ len = p - begin;
+ /* Remove trailing spaces */
+ while (len > 0 && isspace(begin[len - 1])) len--;
+
+ *out = NULL;
+ if (len > 0) {
+ /* Copy the output string */
+ *out = talloc_strndup(mem_ctx, begin, len);
+ if (!*out) {
+ return ENOMEM;
+ }
+ }
+ p++;
+
+ *in = p;
+ return EOK;
+}
+
+
+
+/* This function splits a three-tuple into three strings
+ * It strips out any whitespace between the parentheses
+ * and commas. Leading and trailing whitespace is
+ * ignored.
+ *
+ * This behavior is compatible with nss_ldap's
+ * implementation.
+ */
+static errno_t sysdb_netgr_split_triple(TALLOC_CTX *mem_ctx,
+ const char *triple,
+ char **hostname,
+ char **username,
+ char **domainname)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ const char *p = triple;
+
+ char *host = NULL;
+ char *user = NULL;
+ char *domain = NULL;
+
+ /* Pre-set the values to NULL here so if they are not
+ * copied, we don't return garbage below.
+ */
+ *hostname = NULL;
+ *username = NULL;
+ *domainname = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ /* Remove any leading whitespace */
+ while (*p && isspace(*p)) p++;
+
+ if (*p != '(') {
+ /* Triple must start and end with parentheses */
+ ret = EINVAL;
+ goto done;
+ }
+ p++;
+
+ ret = sysdb_netgr_split_triple_string(tmp_ctx, &p, ',', &host);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_netgr_split_triple_string(tmp_ctx, &p, ',', &user);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_netgr_split_triple_string(tmp_ctx, &p, ')', &domain);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* skip trailing whitespace */
+ while (*p && isspace(*p)) p++;
+
+ if (*p) {
+ /* Extra data after the closing parenthesis
+ * is a parse error
+ */
+ ret = EINVAL;
+ goto done;
+ }
+
+ /* Return any non-NULL values */
+ if (host) {
+ *hostname = talloc_steal(mem_ctx, host);
+ }
+
+ if (user) {
+ *username = talloc_steal(mem_ctx, user);
+ }
+
+ if (domain) {
+ *domainname = talloc_steal(mem_ctx, domain);
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_netgr_to_entries(TALLOC_CTX *mem_ctx,
+ struct ldb_result *res,
+ struct sysdb_netgroup_ctx ***entries,
+ size_t *netgroup_count)
+{
+ errno_t ret;
+ size_t size = 0;
+ size_t c = 0;
+ char *triple_str;
+ TALLOC_CTX *tmp_ctx;
+ struct sysdb_netgroup_ctx **tmp_entry = NULL;
+ struct ldb_message_element *el;
+ int i, j;
+
+ if(!res || res->count == 0) {
+ return ENOENT;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ for (i=0; i < res->count; i++) {
+ el = ldb_msg_find_element(res->msgs[i], SYSDB_NETGROUP_TRIPLE);
+ if (el != NULL) {
+ size += el->num_values;
+ }
+ el = ldb_msg_find_element(res->msgs[i], SYSDB_NETGROUP_MEMBER);
+ if (el != NULL) {
+ size += el->num_values;
+ }
+ }
+
+ tmp_entry = talloc_array(tmp_ctx, struct sysdb_netgroup_ctx *, size + 1);
+ if (tmp_entry == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (size != 0) {
+ for (i=0; i < res->count; i++) {
+ el = ldb_msg_find_element(res->msgs[i], SYSDB_NETGROUP_TRIPLE);
+ if (el != NULL) {
+ /* Copy in all of the entries */
+ for(j = 0; j < el->num_values; j++) {
+ triple_str = talloc_strndup(tmp_ctx,
+ (const char *)el->values[j].data,
+ el->values[j].length);
+ if (!triple_str) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tmp_entry[c] = talloc_zero(tmp_entry,
+ struct sysdb_netgroup_ctx);
+ if (!tmp_entry[c]) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tmp_entry[c]->type = SYSDB_NETGROUP_TRIPLE_VAL;
+ ret = sysdb_netgr_split_triple(tmp_entry[c],
+ triple_str,
+ &tmp_entry[c]->value.triple.hostname,
+ &tmp_entry[c]->value.triple.username,
+ &tmp_entry[c]->value.triple.domainname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "Cannot split netgroup triple [%s], "
+ "this attribute will be skipped \n",
+ triple_str);
+ continue;
+ }
+
+ c++;
+ }
+ }
+ el = ldb_msg_find_element(res->msgs[i], SYSDB_NETGROUP_MEMBER);
+ if (el != NULL) {
+ for(j = 0; j < el->num_values; j++) {
+ tmp_entry[c] = talloc_zero(tmp_entry,
+ struct sysdb_netgroup_ctx);
+ if (!tmp_entry[c]) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tmp_entry[c]->type = SYSDB_NETGROUP_GROUP_VAL;
+ tmp_entry[c]->value.groupname = talloc_strndup(tmp_entry[c],
+ (const char *)el->values[j].data,
+ el->values[j].length);
+ if (tmp_entry[c]->value.groupname == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ c++;
+ }
+ }
+ }
+ }
+
+ /* Add NULL terminator */
+ tmp_entry[c] = NULL;
+
+ *entries = talloc_steal(mem_ctx, tmp_entry);
+ *netgroup_count = c;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_getnetgr(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *netgroup,
+ struct ldb_result **res)
+{
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = SYSDB_NETGR_ATTRS;
+ struct ldb_dn *base_dn;
+ struct ldb_result *result = NULL;
+ char *sanitized_netgroup;
+ char *lc_sanitized_netgroup;
+ char *netgroup_dn;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ base_dn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb,
+ SYSDB_TMPL_NETGROUP_BASE,
+ domain->name);
+ if (!base_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_filter_sanitize_for_dom(tmp_ctx, netgroup, domain,
+ &sanitized_netgroup,
+ &lc_sanitized_netgroup);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ netgroup_dn = talloc_asprintf(tmp_ctx, SYSDB_TMPL_NETGROUP,
+ sanitized_netgroup, domain->name);
+ if (!netgroup_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ SSS_LDB_SEARCH(ret, domain->sysdb->ldb, tmp_ctx, &result, base_dn,
+ LDB_SCOPE_SUBTREE, attrs,
+ SYSDB_NETGR_TRIPLES_FILTER, lc_sanitized_netgroup,
+ sanitized_netgroup, sanitized_netgroup,
+ netgroup_dn);
+
+ if (ret == EOK || ret == ENOENT) {
+ *res = talloc_steal(mem_ctx, result);
+ }
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_get_netgroup_attr(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *netgrname,
+ const char **attributes,
+ struct ldb_result **res)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *base_dn;
+ struct ldb_result *result;
+ char *sanitized_netgroup;
+ char *lc_sanitized_netgroup;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ base_dn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb,
+ SYSDB_TMPL_NETGROUP_BASE, domain->name);
+ if (!base_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_filter_sanitize_for_dom(tmp_ctx, netgrname, domain,
+ &sanitized_netgroup,
+ &lc_sanitized_netgroup);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &result, base_dn,
+ LDB_SCOPE_SUBTREE, attributes,
+ SYSDB_NETGR_FILTER,
+ lc_sanitized_netgroup,
+ sanitized_netgroup,
+ sanitized_netgroup);
+ if (ret) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ *res = talloc_steal(mem_ctx, result);
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_get_direct_parents(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ struct sss_domain_info *parent_dom,
+ enum sysdb_member_type mtype,
+ const char *name,
+ char ***_direct_parents)
+{
+ errno_t ret;
+ const char *dn;
+ char *sanitized_dn;
+ struct ldb_dn *basedn;
+ static const char *group_attrs[] = { SYSDB_NAME, NULL };
+ const char *member_filter;
+ size_t direct_sysdb_count = 0;
+ struct ldb_message **direct_sysdb_groups = NULL;
+ char **direct_parents = NULL;
+ TALLOC_CTX *tmp_ctx = NULL;
+ int i, pi;
+ const char *tmp_str;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ if (mtype == SYSDB_MEMBER_USER) {
+ dn = sysdb_user_strdn(tmp_ctx, dom->name, name);
+ } else if (mtype == SYSDB_MEMBER_GROUP) {
+ dn = sysdb_group_strdn(tmp_ctx, dom->name, name);
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unknown member type %d\n", mtype);
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (!dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_filter_sanitize(tmp_ctx, dn, &sanitized_dn);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ member_filter = talloc_asprintf(tmp_ctx, "(&(%s=%s)(%s=%s))",
+ SYSDB_OBJECTCATEGORY, SYSDB_GROUP_CLASS,
+ SYSDB_MEMBER, sanitized_dn);
+ if (!member_filter) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (parent_dom == NULL) {
+ basedn = sysdb_base_dn(dom->sysdb, tmp_ctx);
+ } else {
+ basedn = sysdb_group_base_dn(tmp_ctx, parent_dom);
+ }
+ if (!basedn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "searching sysdb with filter [%s]\n", member_filter);
+
+ ret = sysdb_search_entry(tmp_ctx, dom->sysdb, basedn,
+ LDB_SCOPE_SUBTREE, member_filter, group_attrs,
+ &direct_sysdb_count, &direct_sysdb_groups);
+ if (ret == ENOENT) {
+ direct_sysdb_count = 0;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_entry failed: [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ /* EOK */
+ /* Get the list of sysdb groups by name */
+ direct_parents = talloc_array(tmp_ctx, char *, direct_sysdb_count+1);
+ if (!direct_parents) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ pi = 0;
+ for(i = 0; i < direct_sysdb_count; i++) {
+ tmp_str = ldb_msg_find_attr_as_string(direct_sysdb_groups[i],
+ SYSDB_NAME, NULL);
+ if (!tmp_str) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "A group with no name?\n");
+ /* This should never happen, but if it does, just continue */
+ continue;
+ }
+
+ direct_parents[pi] = talloc_strdup(direct_parents, tmp_str);
+ if (!direct_parents[pi]) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+ ret = EIO;
+ goto done;
+ }
+ pi++;
+ }
+ direct_parents[pi] = NULL;
+
+ DEBUG(SSSDBG_TRACE_LIBS, "%s is a member of %zu sysdb groups\n",
+ name, direct_sysdb_count);
+ *_direct_parents = talloc_steal(mem_ctx, direct_parents);
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_get_real_name(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name_or_upn_or_sid,
+ const char **_cname)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ const char *cname;
+ struct ldb_message *msg;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_getpwnam(tmp_ctx, domain, name_or_upn_or_sid, &res);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot canonicalize username\n");
+ goto done;
+ }
+
+ if (res->count == 0) {
+ ret = sysdb_search_user_by_upn(tmp_ctx, domain, false, name_or_upn_or_sid,
+ NULL, &msg);
+ if (ret == ENOENT) {
+ ret = sysdb_search_user_by_sid_str(tmp_ctx, domain,
+ name_or_upn_or_sid, NULL, &msg);
+ if (ret == ENOENT) {
+ ret = sysdb_search_object_by_uuid(tmp_ctx, domain,
+ name_or_upn_or_sid, NULL,
+ &res);
+ if (ret == EOK && res->count == 1) {
+ msg = res->msgs[0];
+ } else if (ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_search_object_by_uuid failed or returned "
+ "more than one result [%d][%s].\n",
+ ret, sss_strerror(ret));
+ ret = ENOENT;
+ goto done;
+ }
+ }
+ }
+ if (ret != EOK) {
+ /* User cannot be found in cache */
+ if (ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to find user [%s] in cache: %d\n",
+ name_or_upn_or_sid, ret);
+ } else {
+ DEBUG(SSSDBG_TRACE_FUNC, "User [%s] is missing in cache\n",
+ name_or_upn_or_sid);
+ }
+ goto done;
+ }
+ } else if (res->count == 1) {
+ msg = res->msgs[0];
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sysdb_getpwnam returned count: [%d]\n", res->count);
+ ret = EIO;
+ goto done;
+ }
+
+ cname = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+ if (!cname) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "User '%s' without a name?\n", name_or_upn_or_sid);
+ ret = ENOENT;
+ goto done;
+ }
+
+ ret = EOK;
+ *_cname = talloc_steal(mem_ctx, cname);
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_search_user_by_cert_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *cert,
+ struct ldb_result **res)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ struct ldb_result *orig_obj = NULL;
+ struct ldb_result *override_obj = NULL;
+ const char *attrs[] = SYSDB_PW_ATTRS;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ /* If there are views we first have to search the overrides for matches */
+ if (DOM_HAS_VIEWS(domain)) {
+ ret = sysdb_search_override_by_cert(tmp_ctx, domain, cert, attrs,
+ &override_obj, &orig_obj);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_search_override_by_cert failed.\n");
+ goto done;
+ }
+ }
+
+ /* If there are no views or nothing was found in the overrides the
+ * original objects are searched. */
+ if (orig_obj == NULL) {
+ ret = sysdb_search_user_by_cert(tmp_ctx, domain, cert, &orig_obj);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_user_by_cert failed.\n");
+ goto done;
+ }
+ }
+
+ /* If there are views we have to check if override values must be added to
+ * the original object. */
+ if (DOM_HAS_VIEWS(domain) && orig_obj->count == 1) {
+ ret = sysdb_add_overrides_to_object(domain, orig_obj->msgs[0],
+ override_obj == NULL ? NULL : override_obj->msgs[0],
+ NULL);
+ if (ret == ENOENT) {
+ *res = talloc_zero(mem_ctx, struct ldb_result);
+ if (*res == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ ret = ENOMEM;
+ } else {
+ ret = EOK;
+ }
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_overrides_to_object failed.\n");
+ goto done;
+ }
+ }
+
+ *res = talloc_steal(mem_ctx, orig_obj);
+ ret = EOK;
+
+done:
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+struct ldb_result *sss_merge_ldb_results(struct ldb_result *sysdb_res,
+ struct ldb_result *ts_res)
+{
+ size_t i, ii, count, total;
+ int ret;
+
+ if (ts_res == NULL || ts_res->count == 0) {
+ return sysdb_res;
+ }
+
+ total = sysdb_res->count + ts_res->count;
+ sysdb_res->msgs = talloc_realloc(sysdb_res, sysdb_res->msgs,
+ struct ldb_message *,
+ total);
+ if (sysdb_res->msgs == NULL) {
+ return NULL;
+ }
+
+ /* FIXME - this is O(2), so inefficient for large sets! */
+ count = sysdb_res->count;
+ for (i = 0; i < ts_res->count; i++) {
+ for (ii = 0; ii < sysdb_res->count; ii++) {
+ ret = ldb_dn_compare(ts_res->msgs[i]->dn, sysdb_res->msgs[ii]->dn);
+ if (ret == 0) {
+ break;
+ }
+ }
+
+ if (ii < sysdb_res->count) {
+ /* We already have this DN but ts_res might be more up-to-date
+ * wrt timestamps */
+ sysdb_res->msgs[ii] = talloc_steal(sysdb_res, ts_res->msgs[i]);
+ continue;
+ }
+ /* new DN, merge */
+ sysdb_res->msgs[count] = talloc_steal(sysdb_res, ts_res->msgs[i]);
+ count++;
+ }
+
+ if (count < total) {
+ sysdb_res->msgs = talloc_realloc(sysdb_res, sysdb_res->msgs,
+ struct ldb_message *,
+ count);
+ if (sysdb_res->msgs == NULL) {
+ return NULL;
+ }
+ }
+ sysdb_res->count = count;
+ return sysdb_res;
+}
+
+bool is_ts_cache_attr(const char *attrname)
+{
+ size_t i;
+
+ for (i = 0; sysdb_ts_cache_attrs[i] != NULL; i++) {
+ if (strcmp(attrname, sysdb_ts_cache_attrs[i]) == 0) {
+ break;
+ }
+ }
+
+ if (sysdb_ts_cache_attrs[i] == NULL) {
+ return false;
+ }
+
+ return true;
+}
+
+struct sysdb_attrs *sysdb_filter_ts_attrs(TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs *attrs)
+{
+ struct sysdb_attrs *ts_attrs;
+ int ti = 0;
+
+ ts_attrs = sysdb_new_attrs(mem_ctx);
+ if (ts_attrs == NULL) {
+ return NULL;
+ }
+
+ ts_attrs->a = talloc_zero_array(ts_attrs,
+ struct ldb_message_element,
+ attrs->num);
+ if (ts_attrs->a == NULL) {
+ talloc_free(ts_attrs);
+ return NULL;
+ }
+
+ for (int i = 0; i < attrs->num; i++) {
+ if (is_ts_cache_attr(attrs->a[i].name)) {
+ ts_attrs->a[ti] = attrs->a[i];
+ ti++;
+ }
+ }
+ ts_attrs->num = ti;
+
+ return ts_attrs;
+}
diff --git a/src/db/sysdb_selinux.c b/src/db/sysdb_selinux.c
new file mode 100644
index 0000000..5354119
--- /dev/null
+++ b/src/db/sysdb_selinux.c
@@ -0,0 +1,328 @@
+/*
+ SSSD
+
+ System Database - SELinux support
+
+ Copyright (C) Jan Zeleny <jzeleny@redhat.com> 2012
+
+ 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/sss_selinux.h"
+#include "db/sysdb_selinux.h"
+#include "db/sysdb_private.h"
+
+/* Some generic routines */
+enum selinux_entity_type {
+ SELINUX_CONFIG,
+ SELINUX_USER_MAP
+};
+
+static errno_t
+sysdb_add_selinux_entity(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char *objectclass,
+ struct sysdb_attrs *attrs,
+ time_t now)
+{
+ struct ldb_message *msg;
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_string(attrs, SYSDB_OBJECTCLASS, objectclass);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set map object class [%d]: %s\n",
+ ret, strerror(ret));
+ return ret;
+ }
+
+ if (!now) {
+ now = time(NULL);
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_CREATE_TIME, now);
+ if (ret) goto done;
+
+ msg->dn = dn;
+ msg->elements = attrs->a;
+ msg->num_elements = attrs->num;
+
+ ret = ldb_add(sysdb->ldb, msg);
+ ret = sysdb_error_to_errno(ret);
+
+done:
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_LIBS, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static errno_t sysdb_store_selinux_entity(struct sss_domain_info *domain,
+ struct sysdb_attrs *attrs,
+ enum selinux_entity_type type)
+{
+ TALLOC_CTX *tmp_ctx;
+ bool in_transaction = false;
+ const char *objectclass = NULL;
+ const char *name;
+ char *clean_name;
+ struct ldb_dn *dn = NULL;
+ errno_t sret = EOK;
+ errno_t ret;
+ time_t now;
+ struct sysdb_ctx *sysdb = domain->sysdb;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ switch (type) {
+ case SELINUX_USER_MAP:
+ objectclass = SYSDB_SELINUX_USERMAP_CLASS;
+ ret = sysdb_attrs_get_string(attrs, SYSDB_NAME, &name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_dn_sanitize(tmp_ctx, name, &clean_name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_TMPL_SEUSERMAP,
+ clean_name, domain->name);
+ break;
+ case SELINUX_CONFIG:
+ objectclass = SYSDB_SELINUX_CLASS;
+ dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_TMPL_SELINUX_BASE,
+ domain->name);
+ break;
+ }
+
+ if (type != SELINUX_CONFIG && type != SELINUX_USER_MAP) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bad SELinux entity type: [%d]\n", type);
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (!dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_transaction_start(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+ goto done;
+ }
+
+ in_transaction = true;
+
+ now = time(NULL);
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_UPDATE, now);
+ if (ret) goto done;
+
+ ret = sysdb_add_selinux_entity(sysdb, dn, objectclass, attrs, now);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_set_entry_attr(sysdb, dn, attrs, SYSDB_MOD_REP);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_transaction_commit(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
+ goto done;
+ }
+ in_transaction = false;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
+ }
+ }
+
+ if (ret) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_store_selinux_usermap(struct sss_domain_info *domain,
+ struct sysdb_attrs *attrs)
+{
+ return sysdb_store_selinux_entity(domain, attrs, SELINUX_USER_MAP);
+}
+
+errno_t sysdb_store_selinux_config(struct sss_domain_info *domain,
+ const char *default_user,
+ const char *order)
+{
+ errno_t ret;
+ struct sysdb_attrs *attrs;
+
+ if (order == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "The SELinux order is missing\n");
+ return EINVAL;
+ }
+
+ attrs = talloc_zero(NULL, struct sysdb_attrs);
+ if (attrs == NULL) {
+ return ENOMEM;
+ }
+
+ if (default_user) {
+ ret = sysdb_attrs_add_string(attrs, SYSDB_SELINUX_DEFAULT_USER,
+ default_user);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ ret = sysdb_attrs_add_string(attrs, SYSDB_SELINUX_DEFAULT_ORDER,
+ order);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_store_selinux_entity(domain, attrs, SELINUX_CONFIG);
+done:
+ talloc_free(attrs);
+ return ret;
+}
+
+errno_t sysdb_delete_usermaps(struct sss_domain_info *domain)
+{
+ struct ldb_dn *dn = NULL;
+ errno_t ret;
+ struct sysdb_ctx *sysdb = domain->sysdb;
+
+ dn = ldb_dn_new_fmt(sysdb, sysdb->ldb,
+ SYSDB_TMPL_SELINUX_BASE, domain->name);
+ if (!dn) return ENOMEM;
+
+ ret = sysdb_delete_recursive(sysdb, dn, true);
+ talloc_free(dn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_delete_recursive failed.\n");
+ return ret;
+ }
+
+ return EOK;
+}
+
+/* --- SYSDB SELinux search routines --- */
+errno_t
+sysdb_get_selinux_usermaps(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char **attrs,
+ size_t *count,
+ struct ldb_message ***messages)
+{
+ errno_t ret;
+ char *filter;
+ struct ldb_dn *basedn;
+ struct sysdb_ctx *sysdb = domain->sysdb;
+
+ basedn = ldb_dn_new_fmt(mem_ctx, sysdb_ctx_get_ldb(sysdb),
+ SYSDB_TMPL_SELINUX_BASE, domain->name);
+ if (!basedn) {
+ return ENOMEM;
+ }
+
+ filter = talloc_asprintf(mem_ctx, "(%s=%s)",
+ SYSDB_OBJECTCLASS, SYSDB_SELINUX_USERMAP_CLASS);
+ if (filter == NULL) {
+ talloc_free(basedn);
+ return ENOMEM;
+ }
+
+ ret = sysdb_search_entry(mem_ctx, sysdb, basedn, LDB_SCOPE_SUBTREE, filter,
+ attrs, count, messages);
+ talloc_free(basedn);
+ talloc_free(filter);
+ if (ret == ENOENT) {
+ *count = 0;
+ *messages = NULL;
+ } else if (ret) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t sysdb_search_selinux_config(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char **attrs,
+ struct ldb_message **_config)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *def_attrs[] = { SYSDB_SELINUX_DEFAULT_USER,
+ SYSDB_SELINUX_DEFAULT_ORDER,
+ NULL };
+ struct ldb_message **msgs;
+ size_t msgs_count;
+ struct ldb_dn *basedn;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ basedn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb,
+ SYSDB_TMPL_SELINUX_BASE, domain->name);
+ if (!basedn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_entry(tmp_ctx, domain->sysdb, basedn, LDB_SCOPE_BASE,
+ NULL, attrs?attrs:def_attrs, &msgs_count, &msgs);
+ if (ret) {
+ goto done;
+ }
+
+ *_config = talloc_steal(mem_ctx, msgs[0]);
+
+done:
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No SELinux root entry found\n");
+ } else if (ret) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
diff --git a/src/db/sysdb_selinux.h b/src/db/sysdb_selinux.h
new file mode 100644
index 0000000..988f4f8
--- /dev/null
+++ b/src/db/sysdb_selinux.h
@@ -0,0 +1,59 @@
+/*
+ SSSD
+
+ System Database Header - SELinux support
+
+ Copyright (C) Jan Zeleny <jzeleny@redhat.com> 2012
+
+ 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_SELINUX_H__
+#define __SYS_DB_SELINUX_H__
+
+#include "db/sysdb.h"
+
+#define SYSDB_SELINUX_CONTAINER "cn=selinux"
+#define SYSDB_TMPL_SELINUX_BASE SYSDB_SELINUX_CONTAINER",cn=%s,"SYSDB_BASE
+#define SYSDB_TMPL_SEUSERMAP SYSDB_NAME"=%s,"SYSDB_TMPL_SELINUX_BASE
+
+#define SYSDB_SELINUX_NAME "config"
+#define SYSDB_SELINUX_SEEALSO "seeAlso"
+#define SYSDB_SELINUX_USER "selinuxUser"
+#define SYSDB_SELINUX_ENABLED "enabled"
+#define SYSDB_SELINUX_DEFAULT_USER "user"
+#define SYSDB_SELINUX_DEFAULT_ORDER "order"
+#define SYSDB_SELINUX_HOST_PRIORITY "hostPriority"
+
+errno_t sysdb_store_selinux_usermap(struct sss_domain_info *domain,
+ struct sysdb_attrs *attrs);
+
+errno_t sysdb_store_selinux_config(struct sss_domain_info *domain,
+ const char *default_map,
+ const char *order);
+
+errno_t sysdb_get_selinux_usermaps(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char **attrs,
+ size_t *count,
+ struct ldb_message ***messages);
+
+errno_t sysdb_search_selinux_config(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char **attrs,
+ struct ldb_message **_config);
+
+errno_t sysdb_delete_usermaps(struct sss_domain_info *domain);
+
+#endif
diff --git a/src/db/sysdb_services.c b/src/db/sysdb_services.c
new file mode 100644
index 0000000..8129369
--- /dev/null
+++ b/src/db/sysdb_services.c
@@ -0,0 +1,837 @@
+/*
+ SSSD
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2012 Red Hat
+
+ 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.h"
+#include "db/sysdb_private.h"
+#include "db/sysdb_services.h"
+
+static errno_t
+sysdb_svc_update(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ int port,
+ const char **aliases,
+ const char **protocols);
+
+errno_t
+sysdb_svc_remove_alias(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char *alias);
+
+errno_t
+sysdb_getservbyname(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char *proto,
+ struct ldb_result **_res)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = SYSDB_SVC_ATTRS;
+ char *sanitized_name;
+ char *sanitized_proto = NULL;
+ char *subfilter;
+ struct ldb_result *res = NULL;
+ struct ldb_message **msgs;
+ size_t msgs_count;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sss_filter_sanitize(tmp_ctx, name, &sanitized_name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (proto) {
+ ret = sss_filter_sanitize(tmp_ctx, proto, &sanitized_proto);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ subfilter = talloc_asprintf(tmp_ctx, SYSDB_SVC_BYNAME_FILTER,
+ proto ? sanitized_proto : "*",
+ sanitized_name, sanitized_name);
+ if (!subfilter) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_services(mem_ctx, domain, subfilter,
+ attrs, &msgs_count, &msgs);
+ if (ret == EOK) {
+ res = talloc_zero(mem_ctx, struct ldb_result);
+ if (!res) {
+ ret = ENOMEM;
+ goto done;
+ }
+ res->count = msgs_count;
+ res->msgs = talloc_steal(res, msgs);
+ }
+
+ *_res = res;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_getservbyport(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ int port,
+ const char *proto,
+ struct ldb_result **_res)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = SYSDB_SVC_ATTRS;
+ char *sanitized_proto = NULL;
+ char *subfilter;
+ struct ldb_result *res = NULL;
+ struct ldb_message **msgs;
+ size_t msgs_count;
+
+ if (port <= 0) {
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ if (proto) {
+ ret = sss_filter_sanitize(tmp_ctx, proto, &sanitized_proto);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ subfilter = talloc_asprintf(tmp_ctx, SYSDB_SVC_BYPORT_FILTER,
+ proto ? sanitized_proto : "*",
+ (unsigned int) port);
+ if (!subfilter) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_services(mem_ctx, domain, subfilter,
+ attrs, &msgs_count, &msgs);
+ if (ret == EOK) {
+ res = talloc_zero(mem_ctx, struct ldb_result);
+ if (!res) {
+ ret = ENOMEM;
+ goto done;
+ }
+ res->count = msgs_count;
+ res->msgs = talloc_steal(res, msgs);
+ }
+
+ *_res = res;
+
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_store_service(struct sss_domain_info *domain,
+ const char *primary_name,
+ int port,
+ const char **aliases,
+ const char **protocols,
+ struct sysdb_attrs *extra_attrs,
+ char **remove_attrs,
+ uint64_t cache_timeout,
+ time_t now)
+{
+ errno_t ret;
+ errno_t sret;
+ TALLOC_CTX *tmp_ctx;
+ bool in_transaction = false;
+ struct ldb_result *res = NULL;
+ const char *name;
+ unsigned int i;
+ struct ldb_dn *update_dn = NULL;
+ struct sysdb_attrs *attrs;
+ struct sysdb_ctx *sysdb;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ sysdb = domain->sysdb;
+
+ ret = sysdb_transaction_start(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+ goto done;
+ }
+
+ in_transaction = true;
+
+ /* Check that the port is unique
+ * If the port appears for any service other than
+ * the one matching the primary_name, we need to
+ * remove them so that getservbyport() can work
+ * properly. Last entry saved to the cache should
+ * always "win".
+ */
+ ret = sysdb_getservbyport(tmp_ctx, domain, port, NULL, &res);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ } else if (ret != ENOENT) {
+ if (res->count != 1) {
+ /* Somehow the cache has multiple entries with
+ * the same port. This is corrupted. We'll delete
+ * them all to sort it out.
+ */
+ for (i = 0; i < res->count; i++) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Corrupt cache entry [%s] detected. Deleting\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[i]->dn));
+
+ ret = sysdb_delete_entry(sysdb, res->msgs[i]->dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not delete corrupt cache entry [%s]\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[i]->dn));
+ goto done;
+ }
+ }
+ } else {
+ /* Check whether this is the same name as we're currently
+ * saving to the cache.
+ */
+ name = ldb_msg_find_attr_as_string(res->msgs[0],
+ SYSDB_NAME,
+ NULL);
+ if (!name || strcmp(name, primary_name) != 0) {
+
+ if (!name) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "A service with no name?\n");
+ /* Corrupted */
+ }
+
+ /* Either this is a corrupt entry or it's another service
+ * claiming ownership of this port. In order to account
+ * for port reassignments, we need to delete the old entry.
+ */
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Corrupt or replaced cache entry [%s] detected. "
+ "Deleting\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[0]->dn));
+
+ ret = sysdb_delete_entry(sysdb, res->msgs[0]->dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not delete cache entry [%s]\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[0]->dn));
+ }
+ }
+ }
+ }
+ talloc_zfree(res);
+
+ /* Ok, ports should now be unique. Now look
+ * the service up by name to determine if we
+ * need to update existing entries or modify
+ * aliases.
+ */
+ ret = sysdb_getservbyname(tmp_ctx, domain, primary_name, NULL, &res);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ } else if (ret != ENOENT) { /* Found entries */
+ for (i = 0; i < res->count; i++) {
+ /* Check whether this is the same name as we're currently
+ * saving to the cache.
+ */
+ name = ldb_msg_find_attr_as_string(res->msgs[i],
+ SYSDB_NAME,
+ NULL);
+ if (!name) {
+
+ /* Corrupted */
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "A service with no name?\n");
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Corrupt cache entry [%s] detected. Deleting\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[i]->dn));
+
+ ret = sysdb_delete_entry(sysdb, res->msgs[i]->dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not delete corrupt cache entry [%s]\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[i]->dn));
+ goto done;
+ }
+ } else if (strcmp(name, primary_name) == 0) {
+ /* This is the same service name, so we need
+ * to update this entry with the values
+ * provided.
+ */
+ if(update_dn) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Two existing services with the same name: [%s]? "
+ "Deleting both.\n",
+ primary_name);
+
+ /* Delete the entry from the previous pass */
+ ret = sysdb_delete_entry(sysdb, update_dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not delete cache entry [%s]\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ update_dn));
+ goto done;
+ }
+
+ /* Delete the new entry as well */
+ ret = sysdb_delete_entry(sysdb, res->msgs[i]->dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not delete cache entry [%s]\n",
+ ldb_dn_canonical_string(tmp_ctx,
+ res->msgs[i]->dn));
+ goto done;
+ }
+
+ update_dn = NULL;
+ } else {
+ update_dn = talloc_steal(tmp_ctx, res->msgs[i]->dn);
+ }
+ } else {
+ /* Another service is claiming this name as an alias.
+ * In order to account for aliases being promoted to
+ * primary names, we need to make sure to remove the
+ * old alias entry.
+ */
+ ret = sysdb_svc_remove_alias(sysdb,
+ res->msgs[i]->dn,
+ primary_name);
+ if (ret != EOK) goto done;
+ }
+ }
+ talloc_zfree(res);
+ }
+
+ if (update_dn) {
+ /* Update the existing entry */
+ ret = sysdb_svc_update(sysdb, update_dn, port, aliases, protocols);
+ } else {
+ /* Add a new entry */
+ ret = sysdb_svc_add(tmp_ctx, domain, primary_name, port,
+ aliases, protocols, &update_dn);
+ }
+ if (ret != EOK) goto done;
+
+ /* Set the cache timeout */
+ if (!extra_attrs) {
+ attrs = sysdb_new_attrs(tmp_ctx);
+ if (!attrs) {
+ ret = ENOMEM;
+ goto done;
+ }
+ } else {
+ attrs = extra_attrs;
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_UPDATE, now);
+ if (ret) goto done;
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE,
+ ((cache_timeout) ?
+ (now + cache_timeout) : 0));
+ if (ret) goto done;
+
+ ret = sysdb_set_entry_attr(sysdb, update_dn, attrs, SYSDB_MOD_REP);
+ if (ret != EOK) goto done;
+
+ if (remove_attrs) {
+ ret = sysdb_remove_attrs(domain, primary_name,
+ SYSDB_MEMBER_SERVICE,
+ remove_attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not remove missing attributes: [%s]\n",
+ strerror(ret));
+ goto done;
+ }
+ }
+
+ ret = sysdb_transaction_commit(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
+ goto done;
+ }
+ in_transaction = false;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
+ }
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+struct ldb_dn *
+sysdb_svc_dn(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx,
+ const char *domain, const char *name)
+{
+ errno_t ret;
+ char *clean_name;
+ struct ldb_dn *dn;
+
+ ret = sysdb_dn_sanitize(NULL, name, &clean_name);
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ dn = ldb_dn_new_fmt(mem_ctx, sysdb->ldb, SYSDB_TMPL_SVC,
+ clean_name, domain);
+ talloc_free(clean_name);
+
+ return dn;
+}
+
+errno_t
+sysdb_svc_add(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *primary_name,
+ int port,
+ const char **aliases,
+ const char **protocols,
+ struct ldb_dn **dn)
+{
+ errno_t ret;
+ int lret;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ unsigned long i;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* svc dn */
+ msg->dn = sysdb_svc_dn(domain->sysdb, msg, domain->name, primary_name);
+ if (!msg->dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Objectclass */
+ ret = sysdb_add_string(msg, SYSDB_OBJECTCLASS, SYSDB_SVC_CLASS);
+ if (ret != EOK) goto done;
+
+ /* Set the primary name */
+ ret = sysdb_add_string(msg, SYSDB_NAME, primary_name);
+ if (ret != EOK) goto done;
+
+ /* Set the port number */
+ ret = sysdb_add_ulong(msg, SYSDB_SVC_PORT, port);
+ if (ret != EOK) goto done;
+
+ /* If this service has any aliases, include them */
+ if (aliases && aliases[0]) {
+ /* Set the name aliases */
+ lret = ldb_msg_add_empty(msg, SYSDB_NAME_ALIAS,
+ LDB_FLAG_MOD_ADD, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ for (i=0; aliases[i]; i++) {
+ lret = ldb_msg_add_string(msg, SYSDB_NAME_ALIAS, aliases[i]);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ }
+ }
+
+ /* Set the protocols */
+ lret = ldb_msg_add_empty(msg, SYSDB_SVC_PROTO,
+ LDB_FLAG_MOD_ADD, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ for (i=0; protocols[i]; i++) {
+ lret = ldb_msg_add_string(msg, SYSDB_SVC_PROTO, protocols[i]);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ }
+
+ /* creation time */
+ ret = sysdb_add_ulong(msg, SYSDB_CREATE_TIME, (unsigned long)time(NULL));
+ if (ret) goto done;
+
+ lret = ldb_add(domain->sysdb->ldb, msg);
+ ret = sysdb_error_to_errno(lret);
+
+ if (ret == EOK && dn) {
+ *dn = talloc_steal(mem_ctx, msg->dn);
+ }
+
+done:
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t
+sysdb_svc_update(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ int port,
+ const char **aliases,
+ const char **protocols)
+{
+ errno_t ret;
+ struct ldb_message *msg;
+ int lret;
+ unsigned int i;
+
+ if (!dn || !protocols || !protocols[0]) {
+ return EINVAL;
+ }
+
+ msg = ldb_msg_new(NULL);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = dn;
+
+ /* Update the port */
+ ret = sysdb_replace_ulong(msg, SYSDB_SVC_PORT, port);
+ if (ret != EOK) goto done;
+
+ if (aliases && aliases[0]) {
+ /* Update the aliases */
+ lret = ldb_msg_add_empty(msg, SYSDB_NAME_ALIAS, SYSDB_MOD_REP, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; aliases[i]; i++) {
+ lret = ldb_msg_add_string(msg, SYSDB_NAME_ALIAS, aliases[i]);
+ if (lret != LDB_SUCCESS) {
+ ret = EINVAL;
+ goto done;
+ }
+ }
+ }
+
+ /* Update the protocols */
+ lret = ldb_msg_add_empty(msg, SYSDB_SVC_PROTO, SYSDB_MOD_REP, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; protocols[i]; i++) {
+ lret = ldb_msg_add_string(msg, SYSDB_SVC_PROTO, protocols[i]);
+ if (lret != LDB_SUCCESS) {
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ lret = ldb_modify(sysdb->ldb, msg);
+ if (lret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "ldb_modify failed: [%s](%d)[%s]\n",
+ ldb_strerror(lret), lret, ldb_errstring(sysdb->ldb));
+ }
+ ret = sysdb_error_to_errno(lret);
+
+done:
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_free(msg);
+ return ret;
+}
+
+errno_t
+sysdb_svc_remove_alias(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ const char *alias)
+{
+ errno_t ret;
+ struct ldb_message *msg;
+ int lret;
+
+ msg = ldb_msg_new(NULL);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = dn;
+
+ ret = sysdb_delete_string(msg, SYSDB_NAME_ALIAS, alias);
+ if (ret != EOK) goto done;
+
+ lret = ldb_modify(sysdb->ldb, msg);
+ if (lret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "ldb_modify failed: [%s](%d)[%s]\n",
+ ldb_strerror(lret), lret, ldb_errstring(sysdb->ldb));
+ }
+ ret = sysdb_error_to_errno(lret);
+
+done:
+ if (ret) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_zfree(msg);
+ return ret;
+}
+
+errno_t
+sysdb_svc_delete(struct sss_domain_info *domain,
+ const char *name,
+ int port,
+ const char *proto)
+{
+ errno_t ret, sret;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ unsigned int i;
+ bool in_transaction = false;
+ struct sysdb_ctx *sysdb = domain->sysdb;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_transaction_start(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+ goto done;
+ }
+
+ in_transaction = true;
+
+ if (name) {
+ ret = sysdb_getservbyname(tmp_ctx, domain, name, proto, &res);
+ if (ret != EOK && ret != ENOENT) goto done;
+ if (ret == ENOENT) {
+ /* Doesn't exist in the DB. Nothing to do */
+ ret = EOK;
+ goto done;
+ }
+ } else {
+ ret = sysdb_getservbyport(tmp_ctx, domain, port, proto, &res);
+ if (ret != EOK && ret != ENOENT) goto done;
+ if (ret == ENOENT) {
+ /* Doesn't exist in the DB. Nothing to do */
+ ret = EOK;
+ goto done;
+ }
+ }
+
+ /* There should only be one matching entry,
+ * but if there are multiple, we should delete
+ * them all to de-corrupt the DB.
+ */
+ for (i = 0; i < res->count; i++) {
+ ret = sysdb_delete_entry(sysdb, res->msgs[i]->dn, false);
+ if (ret != EOK) goto done;
+ }
+
+ ret = sysdb_transaction_commit(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
+ goto done;
+ }
+ in_transaction = false;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not cancel transaction\n");
+ }
+ }
+
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+
+errno_t
+sysdb_enumservent(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result **_res)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = SYSDB_SVC_ATTRS;
+ struct ldb_result *res = NULL;
+ struct ldb_message **msgs;
+ size_t msgs_count;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_search_services(mem_ctx, domain, "",
+ attrs, &msgs_count, &msgs);
+ if (ret == EOK) {
+ res = talloc_zero(mem_ctx, struct ldb_result);
+ if (!res) {
+ ret = ENOMEM;
+ goto done;
+ }
+ res->count = msgs_count;
+ res->msgs = talloc_steal(res, msgs);
+ }
+
+ *_res = res;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_set_service_attr(struct sss_domain_info *domain,
+ const char *name,
+ struct sysdb_attrs *attrs,
+ int mod_op)
+{
+ errno_t ret;
+ struct ldb_dn *dn;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ dn = sysdb_svc_dn(domain->sysdb, tmp_ctx, domain->name, name);
+ if (!dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_set_entry_attr(domain->sysdb, dn, attrs, mod_op);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_search_services(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter,
+ const char **attrs,
+ size_t *msgs_count,
+ struct ldb_message ***msgs)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *basedn;
+ char *filter;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ basedn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb,
+ SYSDB_TMPL_SVC_BASE, domain->name);
+ if (!basedn) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to build base dn\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, "(&(%s)%s)", SYSDB_SC, sub_filter);
+ if (!filter) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to build filter\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Search services with filter: %s\n", filter);
+
+ ret = sysdb_search_entry(mem_ctx, domain->sysdb, basedn,
+ LDB_SCOPE_SUBTREE, filter, attrs,
+ msgs_count, msgs);
+ if (ret) {
+ goto fail;
+ }
+
+ talloc_zfree(tmp_ctx);
+ return EOK;
+
+fail:
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "No such entry\n");
+ }
+ else if (ret) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
diff --git a/src/db/sysdb_services.h b/src/db/sysdb_services.h
new file mode 100644
index 0000000..c746655
--- /dev/null
+++ b/src/db/sysdb_services.h
@@ -0,0 +1,112 @@
+/*
+ SSSD
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2012 Red Hat
+
+ 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 SYSDB_SERVICES_H_
+#define SYSDB_SERVICES_H_
+
+#include "db/sysdb.h"
+
+#define SYSDB_SVC_CLASS "service"
+#define SYSDB_SVC_CONTAINER "cn=services"
+#define SYSDB_SC "objectclass="SYSDB_SVC_CLASS
+
+#define SYSDB_SVC_PORT "servicePort"
+#define SYSDB_SVC_PROTO "serviceProtocol"
+
+#define SYSDB_TMPL_SVC_BASE SYSDB_SVC_CONTAINER",cn=%s,"SYSDB_BASE
+#define SYSDB_TMPL_SVC SYSDB_NAME"=%s,"SYSDB_TMPL_SVC_BASE
+
+#define SYSDB_SVC_BYNAME_FILTER "(&("SYSDB_SVC_PROTO"=%s)(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
+#define SYSDB_SVC_BYPORT_FILTER "(&("SYSDB_SVC_PROTO"=%s)("SYSDB_SVC_PORT"=%u))"
+
+
+#define SYSDB_SVC_ATTRS { \
+ SYSDB_NAME, \
+ SYSDB_NAME_ALIAS, \
+ SYSDB_SVC_PORT, \
+ SYSDB_SVC_PROTO, \
+ SYSDB_DEFAULT_ATTRS, \
+ NULL }
+
+errno_t
+sysdb_getservbyname(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char *proto,
+ struct ldb_result **_res);
+
+errno_t
+sysdb_getservbyport(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ int port,
+ const char *proto,
+ struct ldb_result **_res);
+
+errno_t
+sysdb_enumservent(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result **_res);
+
+errno_t
+sysdb_store_service(struct sss_domain_info *domain,
+ const char *primary_name,
+ int port,
+ const char **aliases,
+ const char **protocols,
+ struct sysdb_attrs *extra_attrs,
+ char **remove_attrs,
+ uint64_t cache_timeout,
+ time_t now);
+
+struct ldb_dn *
+sysdb_svc_dn(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx,
+ const char *domain, const char *name);
+
+errno_t
+sysdb_svc_add(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *primary_name,
+ int port,
+ const char **aliases,
+ const char **protocols,
+ struct ldb_dn **dn);
+
+errno_t
+sysdb_svc_delete(struct sss_domain_info *domain,
+ const char *name,
+ int port,
+ const char *proto);
+
+errno_t
+sysdb_set_service_attr(struct sss_domain_info *domain,
+ const char *name,
+ struct sysdb_attrs *attrs,
+ int mod_op);
+
+errno_t sysdb_search_services(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter,
+ const char **attrs,
+ size_t *msgs_count,
+ struct ldb_message ***msgs);
+
+#endif /* SYSDB_SERVICES_H_ */
diff --git a/src/db/sysdb_ssh.c b/src/db/sysdb_ssh.c
new file mode 100644
index 0000000..4983dcc
--- /dev/null
+++ b/src/db/sysdb_ssh.c
@@ -0,0 +1,401 @@
+/*
+ Authors:
+ Jan Cholasta <jcholast@redhat.com>
+
+ Copyright (C) 2012 Red Hat
+
+ 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 <talloc.h>
+
+#include "db/sysdb_ssh.h"
+#include "db/sysdb_private.h"
+
+static struct ldb_dn *
+sysdb_ssh_host_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name)
+{
+ return sysdb_custom_dn(mem_ctx, domain, name, SSH_HOSTS_SUBDIR);
+}
+
+static errno_t
+sysdb_update_ssh_host(struct sss_domain_info *domain,
+ const char *name,
+ struct sysdb_attrs *attrs)
+{
+ errno_t ret;
+
+ ret = sysdb_store_custom(domain, name, SSH_HOSTS_SUBDIR,
+ attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Error storing host %s [%d]: %s\n", name, ret, strerror(ret));
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t
+sysdb_store_ssh_host(struct sss_domain_info *domain,
+ const char *name,
+ const char *alias,
+ int cache_timeout,
+ time_t now,
+ struct sysdb_attrs *attrs)
+{
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret, sret;
+ bool in_transaction = false;
+ const char *search_attrs[] = { SYSDB_NAME_ALIAS, NULL };
+ bool new_alias;
+ struct ldb_message *host = NULL;
+ struct ldb_message_element *el;
+ unsigned int i;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Storing host %s\n", name);
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_transaction_start(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+ goto done;
+ }
+
+ in_transaction = true;
+
+ ret = sysdb_get_ssh_host(tmp_ctx, domain, name, search_attrs, &host);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_string(attrs, SYSDB_OBJECTCLASS, SYSDB_SSH_HOST_OC);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not set object class [%d]: %s\n", ret, strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_string(attrs, SYSDB_NAME, name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not set name attribute [%d]: %s\n", ret, strerror(ret));
+ goto done;
+ }
+
+ if (alias) {
+ new_alias = true;
+
+ /* copy aliases from the existing entry */
+ if (host) {
+ el = ldb_msg_find_element(host, SYSDB_NAME_ALIAS);
+
+ if (el) {
+ for (i = 0; i < el->num_values; i++) {
+ if (strcmp((char *)el->values[i].data, alias) == 0) {
+ new_alias = false;
+ }
+
+ ret = sysdb_attrs_add_val(attrs,
+ SYSDB_NAME_ALIAS, &el->values[i]);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not add name alias %s [%d]: %s\n",
+ el->values[i].data, ret, strerror(ret));
+ goto done;
+ }
+ }
+ }
+ }
+
+ /* add alias only if it is not already present */
+ if (new_alias) {
+ ret = sysdb_attrs_add_string(attrs, SYSDB_NAME_ALIAS, alias);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not add name alias %s [%d]: %s\n",
+ alias, ret, strerror(ret));
+ goto done;
+ }
+ }
+ }
+
+ /* make sure sshPublicKey is present when modifying an existing host */
+ if (host) {
+ ret = sysdb_attrs_get_el(attrs, SYSDB_SSH_PUBKEY, &el);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not get sysdb sshPublicKey [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_UPDATE, now);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not set sysdb lastUpdate [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE,
+ cache_timeout ? (now + cache_timeout) : 0);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set sysdb cache expire [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_update_ssh_host(domain, name, attrs);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_transaction_commit(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
+ goto done;
+ }
+
+ in_transaction = false;
+
+ ret = EOK;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(domain->sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
+ }
+ }
+
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+sysdb_set_ssh_host_attr(struct sss_domain_info *domain,
+ const char *name,
+ struct sysdb_attrs *attrs,
+ int mod_op)
+{
+ errno_t ret;
+ struct ldb_dn *dn;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ dn = sysdb_ssh_host_dn(tmp_ctx, domain, name);
+ if (!dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_set_entry_attr(domain->sysdb, dn, attrs, mod_op);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_update_ssh_known_host_expire(struct sss_domain_info *domain,
+ const char *name,
+ time_t now,
+ int known_hosts_timeout)
+{
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ struct sysdb_attrs *attrs;
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Updating known_hosts expire time of host %s\n", name);
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ attrs = sysdb_new_attrs(tmp_ctx);
+ if (!attrs) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_SSH_KNOWN_HOSTS_EXPIRE,
+ now + known_hosts_timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not set known_hosts expire time [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_update_ssh_host(domain, name, attrs);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+sysdb_delete_ssh_host(struct sss_domain_info *domain,
+ const char *name)
+{
+ DEBUG(SSSDBG_TRACE_FUNC, "Deleting host %s\n", name);
+ return sysdb_delete_custom(domain, name, SSH_HOSTS_SUBDIR);
+}
+
+errno_t
+sysdb_search_ssh_hosts(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *filter,
+ const char **attrs,
+ size_t *num_hosts,
+ struct ldb_message ***hosts)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message **results;
+ size_t num_results;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_search_custom(tmp_ctx, domain, filter,
+ SSH_HOSTS_SUBDIR, attrs,
+ &num_results, &results);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Error looking up host [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ } if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No such host\n");
+ *hosts = NULL;
+ *num_hosts = 0;
+ goto done;
+ }
+
+ *hosts = talloc_steal(mem_ctx, results);
+ *num_hosts = num_results;
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+sysdb_get_ssh_host(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char **attrs,
+ struct ldb_message **host)
+{
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ const char *filter;
+ struct ldb_message **hosts;
+ size_t num_hosts;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, "(%s=%s)", SYSDB_NAME, name);
+ if (!filter) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_ssh_hosts(tmp_ctx, domain, filter, attrs,
+ &num_hosts, &hosts);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (num_hosts > 1) {
+ ret = EINVAL;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Found more than one host with name %s\n", name);
+ goto done;
+ }
+
+ *host = talloc_steal(mem_ctx, hosts[0]);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+sysdb_get_ssh_known_hosts(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ time_t now,
+ const char **attrs,
+ struct ldb_message ***hosts,
+ size_t *num_hosts)
+{
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ const char *filter;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ filter = talloc_asprintf(tmp_ctx,
+ "(&(|(!(%s=*))(%s=0)(%s>=%lld))(%s>=%lld))",
+ SYSDB_CACHE_EXPIRE,
+ SYSDB_CACHE_EXPIRE,
+ SYSDB_CACHE_EXPIRE, (long long)now + 1,
+ SYSDB_SSH_KNOWN_HOSTS_EXPIRE, (long long)now + 1);
+ if (!filter) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_ssh_hosts(mem_ctx, domain, filter, attrs,
+ num_hosts, hosts);
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
diff --git a/src/db/sysdb_ssh.h b/src/db/sysdb_ssh.h
new file mode 100644
index 0000000..960a13f
--- /dev/null
+++ b/src/db/sysdb_ssh.h
@@ -0,0 +1,79 @@
+/*
+ Authors:
+ Jan Cholasta <jcholast@redhat.com>
+
+ Copyright (C) 2012 Red Hat
+
+ 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 _SYSDB_SSH_H_
+#define _SYSDB_SSH_H_
+
+#include "db/sysdb.h"
+
+#define SSH_HOSTS_SUBDIR "ssh_hosts"
+
+#define SYSDB_SSH_HOST_OC "sshHost"
+
+#define SYSDB_SSH_KNOWN_HOSTS_EXPIRE "sshKnownHostsExpire"
+
+errno_t
+sysdb_store_ssh_host(struct sss_domain_info *domain,
+ const char *name,
+ const char *alias,
+ int cache_timeout,
+ time_t now,
+ struct sysdb_attrs *attrs);
+
+errno_t
+sysdb_update_ssh_known_host_expire(struct sss_domain_info *domain,
+ const char *name,
+ time_t now,
+ int known_hosts_timeout);
+
+int
+sysdb_set_ssh_host_attr(struct sss_domain_info *domain,
+ const char *name,
+ struct sysdb_attrs *attrs,
+ int mod_op);
+
+errno_t
+sysdb_delete_ssh_host(struct sss_domain_info *domain,
+ const char *name);
+
+errno_t
+sysdb_search_ssh_hosts(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *filter,
+ const char **attrs,
+ size_t *num_hosts,
+ struct ldb_message ***hosts);
+
+errno_t
+sysdb_get_ssh_host(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char **attrs,
+ struct ldb_message **host);
+
+errno_t
+sysdb_get_ssh_known_hosts(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ time_t now,
+ const char **attrs,
+ struct ldb_message ***hosts,
+ size_t *num_hosts);
+
+#endif /* _SYSDB_SSH_H_ */
diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
new file mode 100644
index 0000000..61cf48c
--- /dev/null
+++ b/src/db/sysdb_subdomains.c
@@ -0,0 +1,1801 @@
+/*
+ SSSD
+
+ System Database - Sub-domain related calls
+
+ Copyright (C) 2012 Jan Zeleny <jzeleny@redhat.com>
+ Copyright (C) 2012 Sumit Bose <sbose@redhat.com>
+
+ 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 "db/sysdb_domain_resolution_order.h"
+
+static errno_t
+check_subdom_config_file(struct confdb_ctx *confdb,
+ struct sss_domain_info *subdomain);
+
+struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *parent,
+ const char *name,
+ const char *realm,
+ const char *flat_name,
+ const char *dns_name,
+ const char *id,
+ enum sss_domain_mpg_mode mpg_mode,
+ bool enumerate,
+ const char *forest,
+ const char **upn_suffixes,
+ uint32_t trust_direction,
+ struct confdb_ctx *confdb,
+ bool enabled)
+{
+ struct sss_domain_info *dom;
+ bool inherit_option;
+ errno_t ret;
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Creating [%s] as subdomain of [%s]!\n", name, parent->name);
+
+ dom = talloc_zero(mem_ctx, struct sss_domain_info);
+ if (dom == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ return NULL;
+ }
+
+ dom->parent = parent;
+
+ /* Sub-domains always have the same view as the parent */
+ dom->has_views = parent->has_views;
+ if (parent->view_name != NULL) {
+ dom->view_name = talloc_strdup(dom, parent->view_name);
+ if (dom->view_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to copy parent's view name.\n");
+ goto fail;
+ }
+ }
+
+ dom->name = talloc_strdup(dom, name);
+ if (dom->name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to copy domain name.\n");
+ goto fail;
+ }
+
+ dom->provider = talloc_strdup(dom, parent->provider);
+ if (dom->provider == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to copy provider name.\n");
+ goto fail;
+ }
+
+ dom->conn_name = talloc_strdup(dom, parent->conn_name);
+ if (dom->conn_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to copy connection name.\n");
+ goto fail;
+ }
+
+ if (realm != NULL) {
+ dom->realm = talloc_strdup(dom, realm);
+ if (dom->realm == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to copy realm name.\n");
+ goto fail;
+ }
+ }
+
+ if (flat_name != NULL) {
+ dom->flat_name = talloc_strdup(dom, flat_name);
+ if (dom->flat_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to copy flat name.\n");
+ goto fail;
+ }
+ }
+
+ if (dns_name != NULL) {
+ dom->dns_name = talloc_strdup(dom, dns_name);
+ if (dom->dns_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to copy dns name.\n");
+ goto fail;
+ }
+ }
+
+ if (id != NULL) {
+ dom->domain_id = talloc_strdup(dom, id);
+ if (dom->domain_id == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to copy id.\n");
+ goto fail;
+ }
+ }
+
+ if (forest != NULL) {
+ dom->forest = talloc_strdup(dom, forest);
+ if (dom->forest == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to copy forest.\n");
+ goto fail;
+ }
+ }
+
+ if (upn_suffixes != NULL) {
+ dom->upn_suffixes = dup_string_list(dom, upn_suffixes);
+ if (dom->upn_suffixes == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to copy UPN upn_suffixes.\n");
+ goto fail;
+ }
+ }
+
+ dom->hostname = talloc_strdup(dom, parent->hostname);
+ if (dom->hostname == NULL && parent->hostname != NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to copy hostname.\n");
+ goto fail;
+ }
+
+ dom->krb5_keytab = talloc_strdup(dom, parent->krb5_keytab);
+ if (dom->krb5_keytab == NULL && parent->krb5_keytab != NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to copy krb5_keytab.\n");
+ goto fail;
+ }
+
+ dom->enumerate = enumerate;
+ dom->fqnames = true;
+ dom->mpg_mode = mpg_mode;
+ dom->state = enabled ? DOM_ACTIVE : DOM_DISABLED;
+
+ /* use fully qualified names as output in order to avoid causing
+ * conflicts with users who have the same name and either the
+ * shortname user resolution is enabled or the trusted domain has
+ * been explicitly set to use non-fully qualified names as input.
+ */
+ dom->output_fqnames = true;
+
+ /* If the parent domain filters out group members, the subdomain should
+ * as well if configured */
+ inherit_option = string_in_list(CONFDB_DOMAIN_IGNORE_GROUP_MEMBERS,
+ parent->sd_inherit, false);
+ if (inherit_option) {
+ dom->ignore_group_members = parent->ignore_group_members;
+ }
+
+ /* Inherit case_sensitive. All subdomains are always case insensitive,
+ * but we want to inherit case preserving which is set with
+ * case_sensitive=Preserving. */
+ inherit_option = string_in_list(CONFDB_DOMAIN_CASE_SENSITIVE,
+ parent->sd_inherit, false);
+ dom->case_sensitive = false;
+ dom->case_preserve = inherit_option ? parent->case_preserve : false;
+
+ dom->trust_direction = trust_direction;
+ /* If the parent domain explicitly limits ID ranges, the subdomain
+ * should honour the limits as well.
+ */
+ dom->id_min = parent->id_min ? parent->id_min : 0;
+ dom->id_max = parent->id_max ? parent->id_max : 0xffffffff;
+ dom->pwd_expiration_warning = parent->pwd_expiration_warning;
+ dom->cache_credentials = parent->cache_credentials;
+ dom->cache_credentials_min_ff_length =
+ parent->cache_credentials_min_ff_length;
+ dom->cached_auth_timeout = parent->cached_auth_timeout;
+ dom->user_timeout = parent->user_timeout;
+ dom->group_timeout = parent->group_timeout;
+ dom->netgroup_timeout = parent->netgroup_timeout;
+ dom->service_timeout = parent->service_timeout;
+ dom->resolver_timeout = parent->resolver_timeout;
+ dom->names = parent->names;
+ dom->override_homedir = parent->override_homedir;
+ dom->fallback_homedir = parent->fallback_homedir;
+ dom->subdomain_homedir = parent->subdomain_homedir;
+ dom->override_shell = parent->override_shell;
+ dom->default_shell = parent->default_shell;
+ dom->homedir_substr = parent->homedir_substr;
+ dom->override_gid = parent->override_gid;
+
+ dom->gssapi_services = parent->gssapi_services;
+ dom->gssapi_indicators_map = parent->gssapi_indicators_map;
+
+ dom->not_found_counter = 0;
+
+ if (parent->sysdb == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Missing sysdb context in parent domain.\n");
+ goto fail;
+ }
+ dom->sysdb = parent->sysdb;
+
+ if (confdb != NULL) {
+ /* If confdb was provided, also check for sssd.conf */
+ ret = check_subdom_config_file(confdb, dom);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to read subdomain configuration [%d]: %s",
+ ret, sss_strerror(ret));
+ goto fail;
+ }
+ }
+
+ return dom;
+
+fail:
+ talloc_free(dom);
+ return NULL;
+}
+
+static errno_t
+check_subdom_config_file(struct confdb_ctx *confdb,
+ struct sss_domain_info *subdomain)
+{
+ char *sd_conf_path;
+ char *case_sensitive_opt;
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ sd_conf_path = subdomain_create_conf_path(tmp_ctx, subdomain);
+ if (sd_conf_path == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* use_fully_qualified_names */
+ ret = confdb_get_bool(confdb, sd_conf_path, CONFDB_DOMAIN_FQ,
+ true, &subdomain->fqnames);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to get %s option for the subdomain: %s\n",
+ CONFDB_DOMAIN_FQ, subdomain->name);
+ goto done;
+ }
+
+ DEBUG(SSSDBG_CONF_SETTINGS, "%s/%s has value %s\n",
+ sd_conf_path, CONFDB_DOMAIN_FQ,
+ subdomain->fqnames ? "TRUE" : "FALSE");
+
+ /* allow to set pam_gssapi_services */
+ ret = confdb_get_string_as_list(confdb, subdomain, sd_conf_path,
+ CONFDB_PAM_GSSAPI_SERVICES,
+ &subdomain->gssapi_services);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to get %s option for the subdomain: %s\n",
+ CONFDB_PAM_GSSAPI_SERVICES, subdomain->name);
+ goto done;
+ }
+
+ /* allow to set pam_gssapi_check_upn */
+ ret = confdb_get_string(confdb, subdomain, sd_conf_path,
+ CONFDB_PAM_GSSAPI_CHECK_UPN,
+ subdomain->parent->gssapi_check_upn,
+ &subdomain->gssapi_check_upn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to get %s option for the subdomain: %s\n",
+ CONFDB_PAM_GSSAPI_CHECK_UPN, subdomain->name);
+ goto done;
+ }
+
+ /* allow to set pam_gssapi_indicators_map */
+ ret = confdb_get_string_as_list(confdb, subdomain, sd_conf_path,
+ CONFDB_PAM_GSSAPI_INDICATORS_MAP,
+ &subdomain->gssapi_indicators_map);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to get %s option for the subdomain: %s\n",
+ CONFDB_PAM_GSSAPI_INDICATORS_MAP, subdomain->name);
+ goto done;
+ }
+
+ /* case_sensitive=Preserving */
+ ret = confdb_get_string(confdb, tmp_ctx, sd_conf_path,
+ CONFDB_DOMAIN_CASE_SENSITIVE, NULL,
+ &case_sensitive_opt);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to get %s option for the subdomain: %s\n",
+ CONFDB_DOMAIN_CASE_SENSITIVE, subdomain->name);
+ goto done;
+ }
+
+ if (case_sensitive_opt != NULL) {
+ DEBUG(SSSDBG_CONF_SETTINGS, "%s/%s has value %s\n", sd_conf_path,
+ CONFDB_DOMAIN_CASE_SENSITIVE, case_sensitive_opt);
+ if (strcasecmp(case_sensitive_opt, "true") == 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Warning: subdomain can not be set as case-sensitive.\n");
+ subdomain->case_sensitive = false;
+ subdomain->case_preserve = false;
+ } else if (strcasecmp(case_sensitive_opt, "false") == 0) {
+ subdomain->case_sensitive = false;
+ subdomain->case_preserve = false;
+ } else if (strcasecmp(case_sensitive_opt, "preserving") == 0) {
+ subdomain->case_sensitive = false;
+ subdomain->case_preserve = true;
+ } else {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Invalid value for %s\n", CONFDB_DOMAIN_CASE_SENSITIVE);
+ goto done;
+ }
+ }
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static bool is_forest_root(struct sss_domain_info *d)
+{
+ if (d->forest == NULL) {
+ /* IPA subdomain provider saves/saved trusted forest root domains
+ * without the forest attribute. Those are automatically forest
+ * roots
+ */
+ return true;
+ }
+
+ if (d->realm && (strcasecmp(d->forest, d->realm) == 0)) {
+ return true;
+ }
+
+ return false;
+}
+
+static bool is_same_forest(struct sss_domain_info *root,
+ struct sss_domain_info *member)
+{
+ if (member->forest != NULL
+ && root->realm != NULL
+ && strcasecmp(member->forest, root->realm) == 0) {
+ return true;
+ }
+
+ return false;
+}
+
+static void link_forest_roots(struct sss_domain_info *domain)
+{
+ struct sss_domain_info *d;
+ struct sss_domain_info *dd;
+ uint32_t gnd_flags = SSS_GND_ALL_DOMAINS;
+
+ for (d = domain; d; d = get_next_domain(d, gnd_flags)) {
+ d->forest_root = NULL;
+ }
+
+ for (d = domain; d; d = get_next_domain(d, gnd_flags)) {
+ if (d->forest_root != NULL) {
+ continue;
+ }
+
+ if (is_forest_root(d) == true) {
+ d->forest_root = d;
+ DEBUG(SSSDBG_TRACE_INTERNAL, "[%s] is a forest root\n", d->name);
+
+ for (dd = domain; dd; dd = get_next_domain(dd, gnd_flags)) {
+ if (dd->forest_root != NULL) {
+ continue;
+ }
+
+ if (is_same_forest(d, dd) == true) {
+ dd->forest_root = d;
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "[%s] is a forest root of [%s]\n",
+ d->forest_root->name,
+ dd->name);
+ }
+ }
+ }
+ }
+}
+
+errno_t sysdb_update_subdomains(struct sss_domain_info *domain,
+ struct confdb_ctx *confdb)
+{
+ int i;
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ const char *attrs[] = {"cn",
+ SYSDB_SUBDOMAIN_REALM,
+ SYSDB_SUBDOMAIN_FLAT,
+ SYSDB_SUBDOMAIN_DNS,
+ SYSDB_SUBDOMAIN_ID,
+ SYSDB_SUBDOMAIN_MPG,
+ SYSDB_SUBDOMAIN_ENUM,
+ SYSDB_SUBDOMAIN_FOREST,
+ SYSDB_SUBDOMAIN_TRUST_DIRECTION,
+ SYSDB_UPN_SUFFIXES,
+ SYSDB_ENABLED,
+ NULL};
+ struct sss_domain_info *dom;
+ struct ldb_dn *basedn;
+ const char *name;
+ const char *realm;
+ const char *flat;
+ const char *dns;
+ const char *id;
+ const char *forest;
+ const char *str_mpg_mode;
+ bool enabled;
+ enum sss_domain_mpg_mode mpg_mode;
+ bool enumerate;
+ uint32_t trust_direction;
+ struct ldb_message_element *tmp_el;
+ const char **upn_suffixes;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ basedn = ldb_dn_new(tmp_ctx, domain->sysdb->ldb, SYSDB_BASE);
+ if (basedn == NULL) {
+ ret = EIO;
+ goto done;
+ }
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res,
+ basedn, LDB_SCOPE_SUBTREE,
+ attrs, "objectclass=%s", SYSDB_SUBDOMAIN_CLASS);
+ if (ret != LDB_SUCCESS) {
+ ret = EIO;
+ goto done;
+ }
+
+ /* disable all domains,
+ * let the search result refresh any that are still valid */
+ for (dom = domain->subdomains; dom; dom = get_next_domain(dom, false)) {
+ sss_domain_set_state(dom, DOM_DISABLED);
+ }
+
+ if (res->count == 0) {
+ ret = EOK;
+ goto done;
+ }
+
+ for (i = 0; i < res->count; i++) {
+
+ name = ldb_msg_find_attr_as_string(res->msgs[i], "cn", NULL);
+ if (name == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "The object [%s] doesn't have a name\n",
+ ldb_dn_get_linearized(res->msgs[i]->dn));
+ ret = EINVAL;
+ goto done;
+ }
+
+ realm = ldb_msg_find_attr_as_string(res->msgs[i],
+ SYSDB_SUBDOMAIN_REALM, NULL);
+
+ flat = ldb_msg_find_attr_as_string(res->msgs[i],
+ SYSDB_SUBDOMAIN_FLAT, NULL);
+
+ dns = ldb_msg_find_attr_as_string(res->msgs[i],
+ SYSDB_SUBDOMAIN_DNS, NULL);
+
+ id = ldb_msg_find_attr_as_string(res->msgs[i],
+ SYSDB_SUBDOMAIN_ID, NULL);
+
+ str_mpg_mode = ldb_msg_find_attr_as_string(res->msgs[i],
+ SYSDB_SUBDOMAIN_MPG, NULL);
+ if (str_mpg_mode == NULL || *str_mpg_mode == '\0') {
+ str_mpg_mode = "false";
+ }
+ mpg_mode = str_to_domain_mpg_mode(str_mpg_mode);
+
+ enumerate = ldb_msg_find_attr_as_bool(res->msgs[i],
+ SYSDB_SUBDOMAIN_ENUM, false);
+
+ forest = ldb_msg_find_attr_as_string(res->msgs[i],
+ SYSDB_SUBDOMAIN_FOREST, NULL);
+
+ upn_suffixes = NULL;
+ tmp_el = ldb_msg_find_element(res->msgs[i], SYSDB_UPN_SUFFIXES);
+ if (tmp_el != NULL) {
+ upn_suffixes = sss_ldb_el_to_string_list(tmp_ctx, tmp_el);
+ if (upn_suffixes == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_ldb_el_to_string_list failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ trust_direction = ldb_msg_find_attr_as_int(res->msgs[i],
+ SYSDB_SUBDOMAIN_TRUST_DIRECTION,
+ 0);
+
+ enabled = ldb_msg_find_attr_as_bool(res->msgs[i], SYSDB_ENABLED, true);
+
+ for (dom = domain->subdomains; dom;
+ dom = get_next_domain(dom, SSS_GND_INCLUDE_DISABLED)) {
+ if (strcasecmp(dom->name, name) == 0) {
+ if (enabled) {
+ sss_domain_set_state(dom, DOM_ACTIVE);
+ }
+
+ /* in theory these may change, but it should never happen */
+ if ((dom->realm == NULL && realm != NULL)
+ || (dom->realm != NULL && realm != NULL
+ && strcasecmp(dom->realm, realm) != 0)) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Realm name changed from [%s] to [%s]!\n",
+ dom->realm, realm);
+ talloc_zfree(dom->realm);
+ dom->realm = talloc_strdup(dom, realm);
+ if (dom->realm == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ if ((dom->flat_name == NULL && flat != NULL)
+ || (dom->flat_name != NULL && flat != NULL
+ && strcasecmp(dom->flat_name, flat) != 0)) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Flat name changed from [%s] to [%s]!\n",
+ dom->flat_name, flat);
+ talloc_zfree(dom->flat_name);
+ dom->flat_name = talloc_strdup(dom, flat);
+ if (dom->flat_name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ if ((dom->dns_name == NULL && dns != NULL)
+ || (dom->dns_name != NULL && dns != NULL
+ && strcasecmp(dom->dns_name, dns) != 0)) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "DNS name changed from [%s] to [%s]!\n",
+ dom->dns_name, dns);
+ talloc_zfree(dom->dns_name);
+ dom->dns_name = talloc_strdup(dom, dns);
+ if (dom->dns_name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ if ((dom->domain_id == NULL && id != NULL)
+ || (dom->domain_id != NULL && id != NULL
+ && strcasecmp(dom->domain_id, id) != 0)) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Domain ID changed from [%s] to [%s]!\n",
+ dom->domain_id, id);
+ talloc_zfree(dom->domain_id);
+ dom->domain_id = talloc_strdup(dom, id);
+ if (dom->domain_id == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (dom->mpg_mode != mpg_mode) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "MPG state change from [%s] to [%s]!\n",
+ dom->mpg_mode == MPG_ENABLED ? "true" : "false",
+ mpg_mode == MPG_ENABLED ? "true" : "false");
+ dom->mpg_mode = mpg_mode;
+ }
+
+ if (dom->enumerate != enumerate) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "enumerate state change from [%s] to [%s]!\n",
+ dom->enumerate ? "true" : "false",
+ enumerate ? "true" : "false");
+ dom->enumerate = enumerate;
+ }
+
+ if ((dom->forest == NULL && forest != NULL)
+ || (dom->forest != NULL && forest != NULL
+ && strcasecmp(dom->forest, forest) != 0)) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Forest changed from [%s] to [%s]!\n",
+ dom->forest, forest);
+ talloc_zfree(dom->forest);
+ dom->forest = talloc_strdup(dom, forest);
+ if (dom->forest == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ talloc_zfree(dom->upn_suffixes);
+ dom->upn_suffixes = talloc_steal(dom, upn_suffixes);
+
+ if (!dom->has_views && dom->view_name == NULL) {
+ /* maybe views are not initialized, copy from parent */
+ dom->has_views = dom->parent->has_views;
+ if (dom->parent->view_name != NULL) {
+ dom->view_name = talloc_strdup(dom,
+ dom->parent->view_name);
+ if (dom->view_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to copy parent's view name.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ } else {
+ if (dom->has_views != dom->parent->has_views
+ || strcmp(dom->view_name,
+ dom->parent->view_name) != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Sub-domain [%s][%s] and parent [%s][%s] " \
+ "views are different.\n",
+ dom->has_views ? "has view" : "has no view",
+ dom->view_name,
+ dom->parent->has_views ? "has view" : "has no view",
+ dom->parent->view_name);
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ if (dom->trust_direction != trust_direction) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Trust direction change from [%d] to [%d]!\n",
+ dom->trust_direction, trust_direction);
+ dom->trust_direction = trust_direction;
+ }
+
+ break;
+ }
+ }
+ /* If not found in loop it is a new subdomain */
+ if (dom == NULL) {
+ dom = new_subdomain(domain, domain, name, realm,
+ flat, dns, id, mpg_mode, enumerate, forest,
+ upn_suffixes, trust_direction, confdb,
+ enabled);
+ if (dom == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ DLIST_ADD_END(domain->subdomains, dom, struct sss_domain_info *);
+ }
+ }
+
+ link_forest_roots(domain);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_master_domain_update(struct sss_domain_info *domain)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ const char *tmp_str;
+ struct ldb_message_element *tmp_el;
+ struct ldb_dn *basedn;
+ struct ldb_result *res;
+ enum sss_domain_state state;
+ bool enabled;
+ const char *attrs[] = {"cn",
+ SYSDB_SUBDOMAIN_REALM,
+ SYSDB_SUBDOMAIN_FLAT,
+ SYSDB_SUBDOMAIN_DNS,
+ SYSDB_SUBDOMAIN_ID,
+ SYSDB_SUBDOMAIN_FOREST,
+ SYSDB_UPN_SUFFIXES,
+ SYSDB_ENABLED,
+ NULL};
+ char *view_name = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ basedn = sysdb_domain_dn(tmp_ctx, domain);
+ if (basedn == NULL) {
+ ret = EIO;
+ goto done;
+ }
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res,
+ basedn, LDB_SCOPE_BASE, attrs, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = EIO;
+ goto done;
+ }
+
+ if (res->count == 0) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ if (res->count > 1) {
+ DEBUG(SSSDBG_OP_FAILURE, "Base search returned [%d] results, "
+ "expected 1.\n", res->count);
+ ret = EINVAL;
+ goto done;
+ }
+
+ tmp_str = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_SUBDOMAIN_REALM,
+ NULL);
+ if (tmp_str != NULL &&
+ (domain->realm == NULL || strcasecmp(tmp_str, domain->realm) != 0)) {
+ talloc_free(domain->realm);
+ domain->realm = talloc_strdup(domain, tmp_str);
+ if (domain->realm == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ tmp_str = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_SUBDOMAIN_FLAT,
+ NULL);
+ if (tmp_str != NULL &&
+ (domain->flat_name == NULL ||
+ strcasecmp(tmp_str, domain->flat_name) != 0)) {
+ talloc_free(domain->flat_name);
+ domain->flat_name = talloc_strdup(domain, tmp_str);
+ if (domain->flat_name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ tmp_str = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_SUBDOMAIN_DNS,
+ NULL);
+ if (tmp_str != NULL &&
+ (domain->dns_name == NULL ||
+ strcasecmp(tmp_str, domain->dns_name) != 0)) {
+ talloc_free(domain->dns_name);
+ domain->dns_name = talloc_strdup(domain, tmp_str);
+ if (domain->dns_name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ tmp_str = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_SUBDOMAIN_ID,
+ NULL);
+ if (tmp_str != NULL &&
+ (domain->domain_id == NULL ||
+ strcasecmp(tmp_str, domain->domain_id) != 0)) {
+ talloc_free(domain->domain_id);
+ domain->domain_id = talloc_strdup(domain, tmp_str);
+ if (domain->domain_id == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ tmp_str = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_SUBDOMAIN_FOREST,
+ NULL);
+ if (tmp_str != NULL &&
+ (domain->forest == NULL ||
+ strcasecmp(tmp_str, domain->forest) != 0)) {
+ talloc_free(domain->forest);
+ domain->forest = talloc_strdup(domain, tmp_str);
+ if (domain->forest == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ tmp_el = ldb_msg_find_element(res->msgs[0], SYSDB_UPN_SUFFIXES);
+ if (tmp_el != NULL) {
+ talloc_free(domain->upn_suffixes);
+ domain->upn_suffixes = sss_ldb_el_to_string_list(domain, tmp_el);
+ if (domain->upn_suffixes == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_ldb_el_to_string_list failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ } else {
+ talloc_zfree(domain->upn_suffixes);
+ }
+
+ state = sss_domain_get_state(domain);
+ enabled = ldb_msg_find_attr_as_bool(res->msgs[0], SYSDB_ENABLED, true);
+ if (!enabled) {
+ sss_domain_set_state(domain, DOM_DISABLED);
+ } else if (state == DOM_DISABLED) {
+ /* We do not want to enable INACTIVE or INCONSISTENT domain. This
+ * is managed by data provider. */
+ sss_domain_set_state(domain, DOM_ACTIVE);
+ }
+
+ ret = sysdb_get_view_name(tmp_ctx, domain->sysdb, &view_name);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_view_name failed.\n");
+ goto done;
+ }
+
+ /* If no view is defined the default view will be used. In this case
+ * domain->has_views is FALSE and
+ * domain->view_name is set to SYSDB_DEFAULT_VIEW_NAME
+ *
+ * If there is a view defined
+ * domain->has_views is TRUE and
+ * domain->view_name is set to the given view name
+ *
+ * Currently changing the view is not supported hence we have to check for
+ * changes and error out accordingly.
+ */
+ if (ret == ENOENT || is_default_view(view_name)) {
+ /* handle default view */
+ if (domain->has_views) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "View name change is currently not supported. " \
+ "New view is the default view while current view is [%s]. " \
+ "View name is not changed!\n", domain->view_name);
+ } else {
+ if (domain->view_name == NULL) {
+ domain->view_name = talloc_strdup(domain,
+ SYSDB_DEFAULT_VIEW_NAME);
+ if (domain->view_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ } else {
+ if (strcmp(domain->view_name, SYSDB_DEFAULT_VIEW_NAME) != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Domain [%s] has no view but view name [%s] " \
+ "is not the default view name [%s].\n",
+ domain->name, domain->view_name,
+ SYSDB_DEFAULT_VIEW_NAME);
+ ret = EINVAL;
+ goto done;
+ }
+ }
+ }
+ } else {
+ /* handle view other than default */
+ if (domain->has_views) {
+ if (strcmp(domain->view_name, view_name) != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "View name change is currently not supported. " \
+ "New view is [%s] while current view is [%s]. " \
+ "View name is not changed!\n",
+ view_name, domain->view_name);
+ }
+ } else {
+ if (domain->view_name == NULL) {
+ domain->has_views = true;
+ domain->view_name = talloc_steal(domain, view_name);
+ if (domain->view_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_steal failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ } else {
+ if (strcmp(domain->view_name, SYSDB_DEFAULT_VIEW_NAME) == 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "View name change is currently not supported. " \
+ "New view is [%s] while current is the default view. " \
+ "View name is not changed!\n", view_name);
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Domain currently has no views, " \
+ "but current view name is set to [%s] " \
+ "and new view name is [%s].\n",
+ domain->view_name, view_name);
+ ret = EINVAL;
+ goto done;
+ }
+ }
+ }
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_master_domain_add_info(struct sss_domain_info *domain,
+ const char *realm,
+ const char *flat,
+ const char *dns,
+ const char *id,
+ const char *forest,
+ struct ldb_message_element *upn_suffixes)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ int ret;
+ bool do_update = false;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = sysdb_domain_dn(tmp_ctx, domain);
+ if (msg->dn == NULL) {
+ ret = EIO;
+ goto done;
+ }
+
+ if (flat != NULL && (domain->flat_name == NULL ||
+ strcmp(domain->flat_name, flat) != 0)) {
+ ret = ldb_msg_add_empty(msg, SYSDB_SUBDOMAIN_FLAT,
+ LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, SYSDB_SUBDOMAIN_FLAT, flat);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ do_update = true;
+ }
+
+ if (dns != NULL && (domain->dns_name == NULL ||
+ strcmp(domain->dns_name, dns) != 0)) {
+ ret = ldb_msg_add_empty(msg, SYSDB_SUBDOMAIN_DNS,
+ LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, SYSDB_SUBDOMAIN_DNS, dns);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ do_update = true;
+ }
+
+ if (id != NULL && (domain->domain_id == NULL ||
+ strcmp(domain->domain_id, id) != 0)) {
+ ret = ldb_msg_add_empty(msg, SYSDB_SUBDOMAIN_ID,
+ LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, SYSDB_SUBDOMAIN_ID, id);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ do_update = true;
+ }
+
+ if (forest != NULL && (domain->forest == NULL ||
+ strcmp(domain->forest, forest) != 0)) {
+ ret = ldb_msg_add_empty(msg, SYSDB_SUBDOMAIN_FOREST,
+ LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, SYSDB_SUBDOMAIN_FOREST, forest);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ do_update = true;
+ }
+
+ if (realm != NULL && (domain->realm == NULL ||
+ strcmp(domain->realm, realm) != 0)) {
+ ret = ldb_msg_add_empty(msg, SYSDB_SUBDOMAIN_REALM,
+ LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ ret = ldb_msg_add_string(msg, SYSDB_SUBDOMAIN_REALM, realm);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ do_update = true;
+ }
+
+ if (upn_suffixes != NULL) {
+ talloc_free(discard_const(upn_suffixes->name));
+ upn_suffixes->name = talloc_strdup(upn_suffixes, SYSDB_UPN_SUFFIXES);
+ if (upn_suffixes->name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_msg_add(msg, upn_suffixes, LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ do_update = true;
+ } else {
+ /* Remove alternative_domain_suffixes from the cache */
+ if (domain->upn_suffixes != NULL) {
+ ret = ldb_msg_add_empty(msg, SYSDB_UPN_SUFFIXES,
+ LDB_FLAG_MOD_DELETE, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ do_update = true;
+ }
+ }
+
+ if (do_update == false) {
+ ret = EOK;
+ goto done;
+ }
+
+ ret = ldb_modify(domain->sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to add subdomain attributes to "
+ "[%s]: [%d][%s]!\n", domain->name, ret,
+ ldb_errstring(domain->sysdb->ldb));
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = sysdb_master_domain_update(domain);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
+ const char *name, const char *realm,
+ const char *flat_name, const char *dns_name,
+ const char *domain_id,
+ enum sss_domain_mpg_mode mpg_mode,
+ bool enumerate, const char *forest,
+ uint32_t trust_direction,
+ struct ldb_message_element *upn_suffixes)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ struct ldb_dn *dn;
+ struct ldb_result *res;
+ const char *attrs[] = {"cn",
+ SYSDB_SUBDOMAIN_REALM,
+ SYSDB_SUBDOMAIN_FLAT,
+ SYSDB_SUBDOMAIN_DNS,
+ SYSDB_SUBDOMAIN_ID,
+ SYSDB_SUBDOMAIN_MPG,
+ SYSDB_SUBDOMAIN_ENUM,
+ SYSDB_SUBDOMAIN_FOREST,
+ SYSDB_SUBDOMAIN_TRUST_DIRECTION,
+ SYSDB_UPN_SUFFIXES,
+ NULL};
+ const char *tmp_str;
+ struct ldb_message_element *tmp_el;
+ bool tmp_bool;
+ bool store = false;
+ int realm_flags = 0;
+ int flat_flags = 0;
+ int dns_flags = 0;
+ int id_flags = 0;
+ int mpg_flags = 0;
+ int enum_flags = 0;
+ int forest_flags = 0;
+ int td_flags = 0;
+ int upn_flags = 0;
+ uint32_t tmp_td;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_DOM_BASE, name);
+ if (dn == NULL) {
+ ret = EIO;
+ goto done;
+ }
+ ret = ldb_search(sysdb->ldb, tmp_ctx, &res,
+ dn, LDB_SCOPE_BASE, attrs, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = EIO;
+ goto done;
+ }
+
+ if (res->count == 0) {
+ ret = sysdb_domain_create(sysdb, name);
+ if (ret) {
+ goto done;
+ }
+ store = true;
+ if (realm) realm_flags = LDB_FLAG_MOD_ADD;
+ if (flat_name) flat_flags = LDB_FLAG_MOD_ADD;
+ if (dns_name) dns_flags = LDB_FLAG_MOD_ADD;
+ if (domain_id) id_flags = LDB_FLAG_MOD_ADD;
+ mpg_flags = LDB_FLAG_MOD_ADD;
+ enum_flags = LDB_FLAG_MOD_ADD;
+ if (forest) forest_flags = LDB_FLAG_MOD_ADD;
+ if (trust_direction) td_flags = LDB_FLAG_MOD_ADD;
+ if (upn_suffixes) upn_flags = LDB_FLAG_MOD_ADD;
+ } else if (res->count != 1) {
+ ret = EINVAL;
+ goto done;
+ } else { /* 1 found */
+ if (realm) {
+ tmp_str = ldb_msg_find_attr_as_string(res->msgs[0],
+ SYSDB_SUBDOMAIN_REALM, NULL);
+ if (!tmp_str || strcasecmp(tmp_str, realm) != 0) {
+ realm_flags = LDB_FLAG_MOD_REPLACE;
+ }
+ }
+ if (flat_name) {
+ tmp_str = ldb_msg_find_attr_as_string(res->msgs[0],
+ SYSDB_SUBDOMAIN_FLAT, NULL);
+ if (!tmp_str || strcasecmp(tmp_str, flat_name) != 0) {
+ flat_flags = LDB_FLAG_MOD_REPLACE;
+ }
+ }
+ if (dns_name) {
+ tmp_str = ldb_msg_find_attr_as_string(res->msgs[0],
+ SYSDB_SUBDOMAIN_DNS, NULL);
+ if (!tmp_str || strcasecmp(tmp_str, dns_name) != 0) {
+ dns_flags = LDB_FLAG_MOD_REPLACE;
+ }
+ }
+ if (domain_id) {
+ tmp_str = ldb_msg_find_attr_as_string(res->msgs[0],
+ SYSDB_SUBDOMAIN_ID, NULL);
+ if (!tmp_str || strcasecmp(tmp_str, domain_id) != 0) {
+ id_flags = LDB_FLAG_MOD_REPLACE;
+ }
+ }
+
+ tmp_str = ldb_msg_find_attr_as_string(res->msgs[0],
+ SYSDB_SUBDOMAIN_MPG,
+ "false");
+ /* If mpg_mode changed we need to replace the old value in sysdb */
+ switch (mpg_mode) {
+ case MPG_ENABLED:
+ if (strcasecmp(tmp_str, "true") != 0) {
+ mpg_flags = LDB_FLAG_MOD_REPLACE;
+ }
+ break;
+ case MPG_DISABLED:
+ if (strcasecmp(tmp_str, "false") != 0) {
+ mpg_flags = LDB_FLAG_MOD_REPLACE;
+ }
+ break;
+ case MPG_HYBRID:
+ if (strcasecmp(tmp_str, "hybrid") != 0) {
+ mpg_flags = LDB_FLAG_MOD_REPLACE;
+ }
+ break;
+ case MPG_DEFAULT:
+ if (strcasecmp(tmp_str, "default") != 0) {
+ mpg_flags = LDB_FLAG_MOD_REPLACE;
+ }
+ break;
+ }
+
+ tmp_bool = ldb_msg_find_attr_as_bool(res->msgs[0], SYSDB_SUBDOMAIN_ENUM,
+ !enumerate);
+ if (tmp_bool != enumerate) {
+ enum_flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ if (forest) {
+ tmp_str = ldb_msg_find_attr_as_string(res->msgs[0],
+ SYSDB_SUBDOMAIN_FOREST, NULL);
+ if (!tmp_str || strcasecmp(tmp_str, forest) != 0) {
+ forest_flags = LDB_FLAG_MOD_REPLACE;
+ }
+ }
+
+ tmp_td = ldb_msg_find_attr_as_uint(res->msgs[0],
+ SYSDB_SUBDOMAIN_TRUST_DIRECTION,
+ 0);
+ if (tmp_td != trust_direction) {
+ td_flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ if (upn_suffixes) {
+ tmp_el = ldb_msg_find_element(res->msgs[0], SYSDB_UPN_SUFFIXES);
+ /* Luckily ldb_msg_element_compare() only compares the values and
+ * not the name. */
+ if (tmp_el == NULL
+ || ldb_msg_element_compare(upn_suffixes, tmp_el) != 0) {
+ upn_flags = LDB_FLAG_MOD_REPLACE;
+ }
+ }
+ }
+
+ if (!store && realm_flags == 0 && flat_flags == 0
+ && dns_flags == 0 && id_flags == 0
+ && mpg_flags == 0 && enum_flags == 0 && forest_flags == 0
+ && td_flags == 0 && upn_flags == 0) {
+ ret = EOK;
+ goto done;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ msg->dn = dn;
+
+ if (store) {
+ ret = ldb_msg_add_empty(msg, SYSDB_OBJECTCLASS, LDB_FLAG_MOD_ADD, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, SYSDB_OBJECTCLASS, SYSDB_SUBDOMAIN_CLASS);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+
+ if (realm_flags) {
+ ret = ldb_msg_add_empty(msg, SYSDB_SUBDOMAIN_REALM, realm_flags, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, SYSDB_SUBDOMAIN_REALM, realm);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+
+ if (flat_flags) {
+ ret = ldb_msg_add_empty(msg, SYSDB_SUBDOMAIN_FLAT, flat_flags, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, SYSDB_SUBDOMAIN_FLAT, flat_name);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+
+ if (dns_flags) {
+ ret = ldb_msg_add_empty(msg, SYSDB_SUBDOMAIN_DNS, dns_flags, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, SYSDB_SUBDOMAIN_DNS, dns_name);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+
+ if (id_flags) {
+ ret = ldb_msg_add_empty(msg, SYSDB_SUBDOMAIN_ID, id_flags, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, SYSDB_SUBDOMAIN_ID, domain_id);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+
+ if (mpg_flags) {
+ ret = ldb_msg_add_empty(msg, SYSDB_SUBDOMAIN_MPG, mpg_flags, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ tmp_str = str_domain_mpg_mode(mpg_mode);
+ if (tmp_str == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Couldn't convert mpg_mode to string\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, SYSDB_SUBDOMAIN_MPG, tmp_str);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+
+ if (enum_flags) {
+ ret = ldb_msg_add_empty(msg, SYSDB_SUBDOMAIN_ENUM, enum_flags, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, SYSDB_SUBDOMAIN_ENUM,
+ enumerate ? "TRUE" : "FALSE");
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+
+ if (forest_flags) {
+ ret = ldb_msg_add_empty(msg, SYSDB_SUBDOMAIN_FOREST, forest_flags,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, SYSDB_SUBDOMAIN_FOREST, forest);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+
+ if (td_flags) {
+ ret = ldb_msg_add_empty(msg, SYSDB_SUBDOMAIN_TRUST_DIRECTION,
+ td_flags, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_msg_add_fmt(msg, SYSDB_SUBDOMAIN_TRUST_DIRECTION,
+ "%u", trust_direction);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+
+ if (upn_flags) {
+ tmp_el = talloc_zero(tmp_ctx, struct ldb_message_element);
+ if (tmp_el == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tmp_el->name = SYSDB_UPN_SUFFIXES;
+ tmp_el->num_values = upn_suffixes->num_values;
+ tmp_el->values = upn_suffixes->values;
+ ret = ldb_msg_add(msg, tmp_el, upn_flags);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+
+ ret = ldb_msg_add_empty(msg, SYSDB_ENABLED, LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, SYSDB_ENABLED, "TRUE");
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_modify(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to add subdomain attributes to "
+ "[%s]: [%d][%s]!\n", name, ret,
+ ldb_errstring(sysdb->ldb));
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t sysdb_subdomain_delete_with_filter(struct sysdb_ctx *sysdb,
+ const char *name,
+ const char *filter)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct ldb_dn *dn;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Removing sub-domain [%s] from db.\n", name);
+ dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_DOM_BASE, name);
+ if (dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_delete_recursive_with_filter(sysdb, dn, true, filter);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_delete_recursive failed.\n");
+ goto done;
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_subdomain_delete(struct sysdb_ctx *sysdb, const char *name)
+{
+ return sysdb_subdomain_delete_with_filter(sysdb, name,
+ "(distinguishedName=*)");
+}
+
+errno_t sysdb_subdomain_content_delete(struct sysdb_ctx *sysdb,
+ const char *name)
+{
+ const char *filter = "(|("SYSDB_UC")("SYSDB_GC"))";
+
+ return sysdb_subdomain_delete_with_filter(sysdb, name, filter);
+}
+
+errno_t
+sysdb_subdomain_get_id_by_name(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ const char *name,
+ const char **_id)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *base_dn;
+ const char *attrs[] = {SYSDB_DOMAIN_ID, NULL};
+ struct ldb_message **msgs;
+ const char *id;
+ char *filter;
+ size_t count;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ base_dn = sysdb_base_dn(sysdb, tmp_ctx);
+ if (base_dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ filter = talloc_asprintf(tmp_ctx,
+ "(&(" SYSDB_OBJECTCLASS "=" SYSDB_SUBDOMAIN_CLASS ")(cn=%s))", name);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_entry(tmp_ctx, sysdb, base_dn, LDB_SCOPE_ONELEVEL,
+ filter, attrs, &count, &msgs);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (count != 1) {
+ ret = ERR_MULTIPLE_ENTRIES;
+ goto done;
+ }
+
+ id = ldb_msg_find_attr_as_string(msgs[0], SYSDB_DOMAIN_ID, NULL);
+ if (id == NULL) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ *_id = talloc_steal(mem_ctx, id);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+
+errno_t
+sysdb_domain_get_domain_resolution_order(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ const char *domain_name,
+ const char **_domain_resolution_order)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *dn;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_DOM_BASE, domain_name);
+ if (dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_get_domain_resolution_order(mem_ctx, sysdb, dn,
+ _domain_resolution_order);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_domain_update_domain_resolution_order(struct sysdb_ctx *sysdb,
+ const char *domain_name,
+ const char *domain_resolution_order)
+{
+
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *dn;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_DOM_BASE, domain_name);
+ if (dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_update_domain_resolution_order(sysdb, dn,
+ domain_resolution_order);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_update_domain_resolution_order() failed [%d]: [%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+
+errno_t
+sysdb_get_site(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ const char **_site)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ struct ldb_dn *dn;
+ const char *attrs[] = { SYSDB_SITE, NULL };
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ dn = sysdb_domain_dn(tmp_ctx, dom);
+ if (dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_search(dom->sysdb->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
+ attrs, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ if (res->count == 0) {
+ *_site = NULL;
+ ret = EOK;
+ goto done;
+ } else if (res->count != 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Got more than one reply for base search!\n");
+ ret = EIO;
+ goto done;
+ }
+
+ *_site = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_SITE, NULL);
+ talloc_steal(mem_ctx, *_site);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_set_site(struct sss_domain_info *dom,
+ const char *site)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ struct ldb_dn *dn;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ dn = sysdb_domain_dn(tmp_ctx, dom);
+ if (dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = dn;
+
+ ret = ldb_msg_add_empty(msg, SYSDB_SITE, LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ if (site != NULL) {
+ ret = ldb_msg_add_string(msg, SYSDB_SITE, site);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+
+ ret = ldb_modify(dom->sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "ldb_modify()_failed: [%s][%d][%s]\n",
+ ldb_strerror(ret), ret, ldb_errstring(dom->sysdb->ldb));
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_domain_set_enabled(struct sysdb_ctx *sysdb,
+ const char *name,
+ bool enabled)
+{
+ struct ldb_dn *dn;
+ errno_t ret;
+
+ dn = ldb_dn_new_fmt(NULL, sysdb->ldb, SYSDB_DOM_BASE, name);
+ if (dn == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_set_bool(sysdb, dn, NULL, SYSDB_ENABLED, enabled);
+ talloc_free(dn);
+
+ return ret;
+}
+
+errno_t
+sysdb_list_subdomains(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ const char ***_names)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *base_dn;
+ const char *attrs[] = {"cn", NULL};
+ struct ldb_message **msgs;
+ const char *name;
+ size_t count;
+ const char **names;
+ errno_t ret;
+ size_t i;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ base_dn = sysdb_base_dn(sysdb, tmp_ctx);
+ if (base_dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+
+ ret = sysdb_search_entry(tmp_ctx, sysdb, base_dn, LDB_SCOPE_ONELEVEL,
+ "("SYSDB_OBJECTCLASS"="SYSDB_SUBDOMAIN_CLASS")",
+ attrs, &count, &msgs);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+
+ names = talloc_zero_array(tmp_ctx, const char *, count + 1);
+ if (names == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < count; i++) {
+ name = ldb_msg_find_attr_as_string(msgs[i], "cn", NULL);
+ if (name == NULL) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ names[i] = talloc_steal(names, name);
+ }
+
+ *_names = talloc_steal(mem_ctx, names);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
diff --git a/src/db/sysdb_subid.c b/src/db/sysdb_subid.c
new file mode 100644
index 0000000..519b083
--- /dev/null
+++ b/src/db/sysdb_subid.c
@@ -0,0 +1,163 @@
+/*
+ Copyright (C) 2021 Red Hat
+
+ 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 <talloc.h>
+
+#include "db/sysdb_private.h"
+#include "db/sysdb_subid.h"
+
+#define SUBID_SUBDIR "subid_ranges"
+
+
+errno_t sysdb_store_subid_range(struct sss_domain_info *domain,
+ const char *name,
+ int expiration_period,
+ struct sysdb_attrs *attrs)
+{
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret, sret;
+ bool in_transaction = false;
+ time_t now = time(NULL);
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Storing subid ranges for %s, expiration period = %d\n",
+ name, expiration_period);
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_transaction_start(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+ goto done;
+ }
+
+ in_transaction = true;
+
+ ret = sysdb_attrs_add_string(attrs, SYSDB_OBJECTCLASS, SYSDB_SUBID_RANGE_OC);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not set object class [%d]: %s\n", ret, strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_string(attrs, SYSDB_NAME, name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not set name attribute [%d]: %s\n", ret, strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_UPDATE, now);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not set sysdb lastUpdate [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE,
+ expiration_period ? (now + expiration_period) : 0);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set sysdb cache expire [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_store_custom(domain, name, SUBID_SUBDIR, attrs);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_transaction_commit(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
+ goto done;
+ }
+
+ in_transaction = false;
+
+ ret = EOK;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(domain->sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
+ }
+ }
+
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t sysdb_get_subid_ranges(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char **attrs,
+ struct ldb_message **_range)
+{
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ const char *filter;
+ struct ldb_message **ranges;
+ size_t num_ranges;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, "(&(%s=%s)(%s=%s))",
+ SYSDB_OBJECTCLASS, SYSDB_SUBID_RANGE_OC,
+ SYSDB_NAME, name);
+ if (!filter) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_custom(tmp_ctx, domain, filter,
+ SUBID_SUBDIR, attrs,
+ &num_ranges, &ranges);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (num_ranges > 1) {
+ ret = EINVAL;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Found more than one range with name %s\n", name);
+ goto done;
+ }
+
+ *_range = talloc_steal(mem_ctx, ranges[0]);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t sysdb_delete_subid_range(struct sss_domain_info *domain,
+ const char *name)
+{
+ DEBUG(SSSDBG_TRACE_FUNC, "Deleting subid ranges for %s\n", name);
+ return sysdb_delete_custom(domain, name, SUBID_SUBDIR);
+}
diff --git a/src/db/sysdb_subid.h b/src/db/sysdb_subid.h
new file mode 100644
index 0000000..4b4a863
--- /dev/null
+++ b/src/db/sysdb_subid.h
@@ -0,0 +1,39 @@
+/*
+ Copyright (C) 2021 Red Hat
+
+ 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 _SYSDB_SUBID_H_
+#define _SYSDB_SUBID_H_
+
+#include "db/sysdb.h"
+
+#define SYSDB_SUBID_RANGE_OC "subordinateid"
+
+errno_t sysdb_store_subid_range(struct sss_domain_info *domain,
+ const char *name,
+ int expiration_period,
+ struct sysdb_attrs *attrs);
+
+errno_t sysdb_get_subid_ranges(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char **attrs,
+ struct ldb_message **range);
+
+errno_t sysdb_delete_subid_range(struct sss_domain_info *domain,
+ const char *name);
+
+#endif /* _SYSDB_SSH_H_ */
diff --git a/src/db/sysdb_sudo.c b/src/db/sysdb_sudo.c
new file mode 100644
index 0000000..bb7a654
--- /dev/null
+++ b/src/db/sysdb_sudo.c
@@ -0,0 +1,1175 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) 2011 Red Hat
+
+ 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 "config.h"
+
+#include <talloc.h>
+#include <time.h>
+
+#include "db/sysdb.h"
+#include "db/sysdb_private.h"
+#include "db/sysdb_sudo.h"
+
+#define SUDO_ALL_FILTER "(" SYSDB_OBJECTCLASS "=" SYSDB_SUDO_CACHE_OC ")"
+
+#define NULL_CHECK(val, rval, label) do { \
+ if (!val) { \
+ rval = ENOMEM; \
+ goto label; \
+ } \
+} while(0)
+
+/* ==================== Utility functions ==================== */
+
+static errno_t sysdb_sudo_convert_time(const char *str, time_t *unix_time)
+{
+ struct tm tm;
+ char *tret = NULL;
+
+ /* SUDO requires times to be in generalized time format:
+ * YYYYMMDDHHMMSS[.|,fraction][(+|-HHMM)|Z]
+ *
+ * We need to use more format strings to parse this with strptime().
+ */
+ const char **format = NULL;
+ const char *formats[] = {"%Y%m%d%H%M%SZ", /* 201212121300Z */
+ "%Y%m%d%H%M%S%z", /* 201212121300+-0200 */
+ "%Y%m%d%H%M%S.0Z",
+ "%Y%m%d%H%M%S.0%z",
+ "%Y%m%d%H%M%S,0Z",
+ "%Y%m%d%H%M%S,0%z",
+ /* LDAP specification says that minutes and seconds
+ might be omitted and in that case these are meant
+ to be treated as zeros [1].
+ */
+ "%Y%m%d%H%MZ", /* Discard seconds */
+ "%Y%m%d%H%M%z",
+ "%Y%m%d%H%M.0Z",
+ "%Y%m%d%H%M.0%z",
+ "%Y%m%d%H%M,0Z",
+ "%Y%m%d%H%M,0%z",
+ "%Y%m%d%HZ", /* Discard minutes and seconds*/
+ "%Y%m%d%H%z",
+ "%Y%m%d%H.0Z",
+ "%Y%m%d%H.0%z",
+ "%Y%m%d%H,0Z",
+ "%Y%m%d%H,0%z",
+ NULL};
+
+ for (format = formats; *format != NULL; format++) {
+ /* strptime() may leave some fields uninitialized */
+ memset(&tm, 0, sizeof(struct tm));
+ /* Let underlying implementation figure out DST */
+ tm.tm_isdst = -1;
+ tret = strptime(str, *format, &tm);
+ if (tret != NULL && *tret == '\0') {
+ /* Convert broken-down time to local time */
+ if (tm.tm_gmtoff == 0) {
+ *unix_time = timegm(&tm);
+ } else {
+ long offset = tm.tm_gmtoff;
+ tm.tm_gmtoff = 0;
+ *unix_time = timegm(&tm) - offset;
+ }
+ return EOK;
+ }
+ }
+
+ return EINVAL;
+}
+
+static errno_t sysdb_sudo_check_time(struct sysdb_attrs *rule,
+ time_t now,
+ bool *result)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ const char **values = NULL;
+ const char *name = NULL;
+ time_t notBefore = 0;
+ time_t notAfter = 0;
+ time_t converted;
+ errno_t ret;
+ int i;
+
+ if (!result) return EINVAL;
+ *result = false;
+
+ tmp_ctx = talloc_new(NULL);
+ NULL_CHECK(tmp_ctx, ret, done);
+
+ ret = sysdb_attrs_get_string(rule, SYSDB_SUDO_CACHE_AT_CN, &name);
+ if (ret == ENOENT) {
+ name = "<missing>";
+ } else if(ret != EOK) {
+ goto done;
+ }
+
+ /*
+ * From man sudoers.ldap:
+ *
+ * If multiple sudoNotBefore entries are present, the *earliest* is used.
+ * If multiple sudoNotAfter entries are present, the *last one* is used.
+ *
+ * From sudo sources, ldap.c:
+ * If either the sudoNotAfter or sudoNotBefore attributes are missing,
+ * no time restriction shall be imposed.
+ */
+
+ /* check for sudoNotBefore */
+ ret = sysdb_attrs_get_string_array(rule, SYSDB_SUDO_CACHE_AT_NOTBEFORE,
+ tmp_ctx, &values);
+ if (ret == EOK) {
+ for (i=0; values[i] ; i++) {
+ ret = sysdb_sudo_convert_time(values[i], &converted);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Invalid time format in rule [%s]!\n",
+ name);
+ goto done;
+ }
+
+ /* Grab the earliest */
+ if (!notBefore) {
+ notBefore = converted;
+ } else if (notBefore > converted) {
+ notBefore = converted;
+ }
+ }
+ } else if (ret != ENOENT) {
+ goto done;
+ }
+
+ /* check for sudoNotAfter */
+ ret = sysdb_attrs_get_string_array(rule, SYSDB_SUDO_CACHE_AT_NOTAFTER,
+ tmp_ctx, &values);
+ if (ret == EOK) {
+ for (i=0; values[i] ; i++) {
+ ret = sysdb_sudo_convert_time(values[i], &converted);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Invalid time format in rule [%s]!\n",
+ name);
+ goto done;
+ }
+
+ /* Grab the latest */
+ if (!notAfter) {
+ notAfter = converted;
+ } else if (notAfter < converted) {
+ notAfter = converted;
+ }
+ }
+ } else if (ret != ENOENT) {
+ goto done;
+ }
+
+ if ((notBefore == 0 || now >= notBefore)
+ && (notAfter == 0 || now <= notAfter)) {
+ *result = true;
+ }
+
+ if (*result) {
+ DEBUG(SSSDBG_TRACE_ALL, "Rule [%s] matches time restrictions\n",
+ name);
+ } else {
+ DEBUG(SSSDBG_TRACE_ALL, "Rule [%s] does not match time "
+ "restrictions\n", name);
+ }
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_sudo_filter_rules_by_time(TALLOC_CTX *mem_ctx,
+ uint32_t in_num_rules,
+ struct sysdb_attrs **in_rules,
+ time_t now,
+ uint32_t *_num_rules,
+ struct sysdb_attrs ***_rules)
+{
+ uint32_t num_rules = 0;
+ struct sysdb_attrs **rules = NULL;
+ TALLOC_CTX *tmp_ctx = NULL;
+ bool allowed = false;
+ errno_t ret;
+ int i;
+
+ tmp_ctx = talloc_new(NULL);
+ NULL_CHECK(tmp_ctx, ret, done);
+
+ if (now == 0) {
+ now = time(NULL);
+ }
+
+ for (i = 0; i < in_num_rules; i++) {
+ ret = sysdb_sudo_check_time(in_rules[i], now, &allowed);
+ if (ret == EOK && allowed) {
+ num_rules++;
+ rules = talloc_realloc(tmp_ctx, rules, struct sysdb_attrs *,
+ num_rules);
+ NULL_CHECK(rules, ret, done);
+
+ rules[num_rules - 1] = in_rules[i];
+ }
+ }
+
+ *_num_rules = num_rules;
+ *_rules = talloc_steal(mem_ctx, rules);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static char *
+sysdb_sudo_filter_userinfo(TALLOC_CTX *mem_ctx,
+ const char *username,
+ char **groupnames,
+ uid_t uid)
+{
+ const char *attr = SYSDB_SUDO_CACHE_AT_USER;
+ TALLOC_CTX *tmp_ctx;
+ char *sanitized_name;
+ char *filter;
+ errno_t ret;
+ int i;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return NULL;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, "(%s=ALL)", attr);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_filter_sanitize(tmp_ctx, username, &sanitized_name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ filter = talloc_asprintf_append(filter, "(%s=%s)", attr, sanitized_name);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (uid != 0) {
+ filter = talloc_asprintf_append(filter, "(%s=#%"SPRIuid")", attr, uid);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (groupnames != NULL) {
+ for (i=0; groupnames[i] != NULL; i++) {
+ ret = sss_filter_sanitize(tmp_ctx, groupnames[i], &sanitized_name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ filter = talloc_asprintf_append(filter, "(%s=%%%s)", attr,
+ sanitized_name);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ }
+
+ talloc_steal(mem_ctx, filter);
+
+done:
+ talloc_free(tmp_ctx);
+
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ return filter;
+}
+
+char *
+sysdb_sudo_filter_expired(TALLOC_CTX *mem_ctx,
+ const char *username,
+ char **groupnames,
+ uid_t uid)
+{
+ char *userfilter;
+ char *filter;
+ time_t now;
+
+ userfilter = sysdb_sudo_filter_userinfo(mem_ctx, username, groupnames, uid);
+ if (userfilter == NULL) {
+ return NULL;
+ }
+
+ now = time(NULL);
+ filter = talloc_asprintf(mem_ctx,
+ "(&(%s=%s)(%s<=%lld)(|(%s=defaults)%s(%s=+*)))",
+ SYSDB_OBJECTCLASS, SYSDB_SUDO_CACHE_OC,
+ SYSDB_CACHE_EXPIRE, (long long)now,
+ SYSDB_NAME,
+ userfilter,
+ SYSDB_SUDO_CACHE_AT_USER);
+ talloc_free(userfilter);
+
+ return filter;
+}
+
+char *
+sysdb_sudo_filter_defaults(TALLOC_CTX *mem_ctx)
+{
+ return talloc_asprintf(mem_ctx, "(&(%s=%s)(%s=defaults))",
+ SYSDB_OBJECTCLASS, SYSDB_SUDO_CACHE_OC,
+ SYSDB_NAME);
+}
+
+char *
+sysdb_sudo_filter_user(TALLOC_CTX *mem_ctx,
+ const char *username,
+ char **groupnames,
+ uid_t uid)
+{
+ char *userfilter;
+ char *filter;
+
+ userfilter = sysdb_sudo_filter_userinfo(mem_ctx, username, groupnames, uid);
+ if (userfilter == NULL) {
+ return NULL;
+ }
+
+ filter = talloc_asprintf(mem_ctx, "(&(%s=%s)(|%s))",
+ SYSDB_OBJECTCLASS, SYSDB_SUDO_CACHE_OC,
+ userfilter);
+ talloc_free(userfilter);
+
+ return filter;
+}
+
+char *
+sysdb_sudo_filter_netgroups(TALLOC_CTX *mem_ctx,
+ const char *username,
+ char **groupnames,
+ uid_t uid)
+{
+ char *userfilter;
+ char *filter;
+
+ userfilter = sysdb_sudo_filter_userinfo(mem_ctx, username, groupnames, uid);
+ if (userfilter == NULL) {
+ return NULL;
+ }
+
+ filter = talloc_asprintf(mem_ctx, "(&(%s=%s)(%s=+*)(!(|%s)))",
+ SYSDB_OBJECTCLASS, SYSDB_SUDO_CACHE_OC,
+ SYSDB_SUDO_CACHE_AT_USER,
+ userfilter);
+ talloc_free(userfilter);
+
+ return filter;
+}
+
+errno_t
+sysdb_get_sudo_user_info(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *username,
+ const char **_orig_name,
+ uid_t *_uid,
+ char ***_groupnames)
+{
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ struct ldb_message *group_msg = NULL;
+ struct ldb_result *res;
+ char **sysdb_groupnames = NULL;
+ const char *primary_group = NULL;
+ uid_t uid = 0;
+ gid_t gid = 0;
+ size_t num_groups = 0;
+ const char *groupname;
+ const char *group_attrs[] = { SYSDB_NAME,
+ NULL };
+ const char *orig_name;
+
+ tmp_ctx = talloc_new(NULL);
+ NULL_CHECK(tmp_ctx, ret, done);
+
+ /*
+ * Even though we lookup initgroups with views, we don't want to use
+ * overridden group names/gids since the rules contains the original
+ * values.
+ */
+ ret = sysdb_initgroups_with_views(tmp_ctx, domain, username, &res);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Error looking up user %s\n", username);
+ goto done;
+ }
+
+ if (res->count == 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "No such user %s\n", username);
+ ret = ENOENT;
+ goto done;
+ }
+
+ /* Even though the database might be queried with the overridden name,
+ * the original name must be used in the filter later on
+ */
+ orig_name = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_NAME, NULL);
+ if (orig_name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "No original name?\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Original name: %s\n", orig_name);
+
+ orig_name = sss_get_cased_name(tmp_ctx, orig_name, domain->case_sensitive);
+ if (orig_name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Cased name: %s\n", orig_name);
+
+ if (_uid != NULL) {
+ uid = ldb_msg_find_attr_as_uint64(res->msgs[0], SYSDB_UIDNUM, 0);
+ if (!uid) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "A user with no UID?\n");
+ ret = EIO;
+ goto done;
+ }
+ }
+
+ /* get secondary group names */
+ if (_groupnames != NULL) {
+ if (res->count < 2) {
+ /* No groups for this user in sysdb currently */
+ sysdb_groupnames = NULL;
+ num_groups = 0;
+ } else {
+ sysdb_groupnames = talloc_zero_array(tmp_ctx, char *, res->count);
+ NULL_CHECK(sysdb_groupnames, ret, done);
+
+ /* Start counting from 1 to exclude the user entry */
+ num_groups = 0;
+ for (size_t i = 1; i < res->count; i++) {
+ groupname = ldb_msg_find_attr_as_string(res->msgs[i],
+ SYSDB_NAME,
+ NULL);
+ if (groupname == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "A group with no name?");
+ continue;
+ }
+
+ sysdb_groupnames[num_groups] = \
+ sss_get_cased_name(sysdb_groupnames, groupname,
+ domain->case_sensitive);
+ if (sysdb_groupnames[num_groups] == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sss_get_cased_name() failed for '%s'\n", groupname);
+ continue;
+ }
+ num_groups++;
+ }
+ }
+ }
+
+ /* resolve primary group */
+ gid = ldb_msg_find_attr_as_uint64(res->msgs[0], SYSDB_GIDNUM, 0);
+ if (gid != 0) {
+ ret = sysdb_search_group_by_gid(tmp_ctx, domain, gid, group_attrs,
+ &group_msg);
+ if (ret == EOK) {
+ primary_group = ldb_msg_find_attr_as_string(group_msg, SYSDB_NAME,
+ NULL);
+ if (primary_group == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ num_groups++;
+ sysdb_groupnames = talloc_realloc(tmp_ctx, sysdb_groupnames,
+ char *, num_groups + 1);
+ NULL_CHECK(sysdb_groupnames, ret, done);
+
+ sysdb_groupnames[num_groups - 1] = talloc_strdup(sysdb_groupnames,
+ primary_group);
+ NULL_CHECK(sysdb_groupnames[num_groups - 1], ret, done);
+
+ sysdb_groupnames[num_groups] = NULL;
+ } else if (ret != ENOENT) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Error looking up group [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+ if (orig_name != NULL) {
+ *_orig_name = talloc_steal(mem_ctx, orig_name);
+ }
+
+ if (_uid != NULL) {
+ *_uid = uid;
+ }
+
+ if (_groupnames != NULL) {
+ *_groupnames = talloc_steal(mem_ctx, sysdb_groupnames);
+ }
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t sysdb_sudo_set_refresh_time(struct sss_domain_info *domain,
+ const char *attr_name,
+ time_t value)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *dn;
+ struct ldb_message *msg = NULL;
+ struct ldb_result *res = NULL;
+ errno_t ret;
+ int lret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ dn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb,
+ SYSDB_TMPL_CUSTOM_SUBTREE,
+ SUDORULE_SUBDIR, domain->name);
+ if (!dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ lret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
+ NULL, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ msg->dn = dn;
+
+ if (res->count == 0) {
+ lret = ldb_msg_add_string(msg, "cn", SUDORULE_SUBDIR);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ } else if (res->count != 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Got more than one reply for base search!\n");
+ ret = EIO;
+ goto done;
+ } else {
+ lret = ldb_msg_add_empty(msg, attr_name, LDB_FLAG_MOD_REPLACE, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ }
+
+ lret = ldb_msg_add_fmt(msg, attr_name, "%lld", (long long)value);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ if (res->count) {
+ lret = ldb_modify(domain->sysdb->ldb, msg);
+ } else {
+ lret = ldb_add(domain->sysdb->ldb, msg);
+ }
+
+ if (lret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "ldb operation failed: [%s](%d)[%s]\n",
+ ldb_strerror(lret), lret, ldb_errstring(domain->sysdb->ldb));
+ }
+ ret = sysdb_error_to_errno(lret);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t sysdb_sudo_get_refresh_time(struct sss_domain_info *domain,
+ const char *attr_name,
+ time_t *value)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *dn;
+ struct ldb_result *res;
+ errno_t ret;
+ int lret;
+ const char *attrs[2] = {attr_name, NULL};
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ dn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb, SYSDB_TMPL_CUSTOM_SUBTREE,
+ SUDORULE_SUBDIR, domain->name);
+ if (!dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ lret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
+ attrs, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ if (res->count == 0) {
+ /* This entry has not been populated in LDB
+ * This is a common case, as unlike LDAP,
+ * LDB does not need to have all of its parent
+ * objects actually exist.
+ */
+ *value = 0;
+ ret = EOK;
+ goto done;
+ } else if (res->count != 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Got more than one reply for base search!\n");
+ ret = EIO;
+ goto done;
+ }
+
+ *value = ldb_msg_find_attr_as_int(res->msgs[0], attr_name, 0);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_sudo_set_last_full_refresh(struct sss_domain_info *domain,
+ time_t value)
+{
+ return sysdb_sudo_set_refresh_time(domain,
+ SYSDB_SUDO_AT_LAST_FULL_REFRESH, value);
+}
+
+errno_t sysdb_sudo_get_last_full_refresh(struct sss_domain_info *domain,
+ time_t *value)
+{
+ return sysdb_sudo_get_refresh_time(domain,
+ SYSDB_SUDO_AT_LAST_FULL_REFRESH, value);
+}
+
+/* ==================== Purge functions ==================== */
+
+static const char *
+sysdb_sudo_get_rule_name(struct sysdb_attrs *rule)
+{
+ const char *name;
+ errno_t ret;
+
+ ret = sysdb_attrs_get_string(rule, SYSDB_SUDO_CACHE_AT_CN, &name);
+ if (ret == ERANGE) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Warning: found rule that contains none "
+ "or multiple CN values. It will be skipped.\n");
+ return NULL;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to obtain rule name [%d]: %s\n",
+ ret, strerror(ret));
+ return NULL;
+ }
+
+ return name;
+}
+
+static errno_t sysdb_sudo_purge_all(struct sss_domain_info *domain)
+{
+ struct ldb_dn *base_dn = NULL;
+ TALLOC_CTX *tmp_ctx = NULL;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ NULL_CHECK(tmp_ctx, ret, done);
+
+ base_dn = sysdb_custom_subtree_dn(tmp_ctx, domain, SUDORULE_SUBDIR);
+ NULL_CHECK(base_dn, ret, done);
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Deleting all cached sudo rules\n");
+
+ ret = sysdb_delete_recursive(domain->sysdb, base_dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_delete_recursive failed.\n");
+ goto done;
+ }
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t
+sysdb_sudo_purge_byname(struct sss_domain_info *domain,
+ const char *name)
+{
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Deleting sudo rule %s\n", name);
+ return sysdb_delete_custom(domain, name, SUDORULE_SUBDIR);
+}
+
+static errno_t
+sysdb_sudo_purge_byrules(struct sss_domain_info *dom,
+ struct sysdb_attrs **rules,
+ size_t num_rules)
+{
+ const char *name;
+ errno_t ret;
+ size_t i;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "About to remove rules from sudo cache\n");
+
+ if (num_rules == 0 || rules == NULL) {
+ return EOK;
+ }
+
+ for (i = 0; i < num_rules; i++) {
+ name = sysdb_sudo_get_rule_name(rules[i]);
+ if (name == NULL) {
+ continue;
+ }
+
+ ret = sysdb_sudo_purge_byname(dom, name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Failed to delete rule "
+ "%s [%d]: %s\n", name, ret, sss_strerror(ret));
+ continue;
+ }
+ }
+
+ return EOK;
+}
+
+static errno_t
+sysdb_sudo_purge_byfilter(struct sss_domain_info *domain,
+ const char *filter)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sysdb_attrs **rules;
+ struct ldb_message **msgs;
+ size_t count;
+ errno_t ret;
+ const char *attrs[] = { SYSDB_OBJECTCLASS,
+ SYSDB_NAME,
+ SYSDB_SUDO_CACHE_AT_CN,
+ NULL };
+
+ if (filter == NULL || strcmp(filter, SUDO_ALL_FILTER) == 0) {
+ return sysdb_sudo_purge_all(domain);
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_custom(tmp_ctx, domain, filter,
+ SUDORULE_SUBDIR, attrs,
+ &count, &msgs);
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No rules matched\n");
+ ret = EOK;
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Error looking up SUDO rules\n");
+ goto done;
+ }
+
+ ret = sysdb_msg2attrs(tmp_ctx, count, msgs, &rules);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to convert ldb message to "
+ "sysdb attrs [%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_sudo_purge_byrules(domain, rules, count);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_sudo_purge(struct sss_domain_info *domain,
+ const char *delete_filter,
+ struct sysdb_attrs **rules,
+ size_t num_rules)
+{
+ bool in_transaction = false;
+ errno_t sret;
+ errno_t ret;
+
+ ret = sysdb_transaction_start(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+ return ret;
+ }
+ in_transaction = true;
+
+ if (delete_filter) {
+ ret = sysdb_sudo_purge_byfilter(domain, delete_filter);
+ } else {
+ ret = sysdb_sudo_purge_byrules(domain, rules, num_rules);
+ }
+
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_transaction_commit(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
+ goto done;
+ }
+ in_transaction = false;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(domain->sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not cancel transaction\n");
+ }
+ }
+
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to purge sudo cache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ }
+
+ return ret;
+}
+
+static errno_t
+sysdb_sudo_add_sss_attrs(struct sysdb_attrs *rule,
+ const char *name,
+ int cache_timeout,
+ time_t now)
+{
+ time_t expire;
+ errno_t ret;
+
+ ret = sysdb_attrs_add_string(rule, SYSDB_OBJECTCLASS, SYSDB_SUDO_CACHE_OC);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to add %s attribute [%d]: %s\n",
+ SYSDB_OBJECTCLASS, ret, strerror(ret));
+ return ret;
+ }
+
+ ret = sysdb_attrs_add_string(rule, SYSDB_NAME, name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to add %s attribute [%d]: %s\n",
+ SYSDB_OBJECTCLASS, ret, strerror(ret));
+ return ret;
+ }
+
+ expire = cache_timeout > 0 ? now + cache_timeout : 0;
+ ret = sysdb_attrs_add_time_t(rule, SYSDB_CACHE_EXPIRE, expire);
+ if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to add %s attribute [%d]: %s\n",
+ SYSDB_CACHE_EXPIRE, ret, strerror(ret));
+ return ret;
+ }
+
+ return EOK;
+}
+
+static errno_t sysdb_sudo_add_lowered_users(struct sss_domain_info *domain,
+ struct sysdb_attrs *rule,
+ const char *name)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char **users = NULL;
+ errno_t ret;
+
+ if (domain->case_sensitive == true || rule == NULL) {
+ return EOK;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_attrs_get_string_array(rule, SYSDB_SUDO_CACHE_AT_USER, tmp_ctx,
+ &users);
+ if (ret != EOK) {
+ /* Allow "defaults" sudoRole without sudoUser attribute */
+ if (name != NULL && !sss_string_equal(false, "defaults", name)) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to get %s attribute [%d]: %s\n",
+ SYSDB_SUDO_CACHE_AT_USER, ret, strerror(ret));
+ ret = ERR_MALFORMED_ENTRY;
+ goto done;
+ }
+ }
+
+ if (users == NULL) {
+ ret = EOK;
+ goto done;
+ }
+
+ for (int i = 0; users[i] != NULL; i++) {
+ ret = sysdb_attrs_add_lower_case_string(rule, true,
+ SYSDB_SUDO_CACHE_AT_USER,
+ users[i]);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Unable to add %s attribute [%d]: %s\n",
+ SYSDB_SUDO_CACHE_AT_USER, ret, strerror(ret));
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static errno_t
+sysdb_sudo_store_rule(struct sss_domain_info *domain,
+ struct sysdb_attrs *rule,
+ int cache_timeout,
+ time_t now)
+{
+ const char *name;
+ errno_t ret;
+
+ name = sysdb_sudo_get_rule_name(rule);
+ if (name == NULL) {
+ return EINVAL;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Adding sudo rule %s\n", name);
+
+ ret = sysdb_sudo_add_lowered_users(domain, rule, name);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sysdb_sudo_add_sss_attrs(rule, name, cache_timeout, now);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ /* Always delete the old rule and add a new one */
+ ret = sysdb_delete_custom(domain, name, SUDORULE_SUBDIR);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to delete the old rule %s [%d]: %s\n",
+ name, ret, strerror(ret));
+ return ret;
+ }
+
+ ret = sysdb_store_custom(domain, name, SUDORULE_SUBDIR, rule);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to store rule %s [%d]: %s\n",
+ name, ret, strerror(ret));
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t
+sysdb_sudo_store(struct sss_domain_info *domain,
+ struct sysdb_attrs **rules,
+ size_t num_rules)
+{
+ bool in_transaction = false;
+ errno_t sret;
+ errno_t ret;
+ time_t now;
+ size_t i;
+
+ if (num_rules == 0 || rules == NULL) {
+ return EOK;
+ }
+
+ ret = sysdb_transaction_start(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+ return ret;
+ }
+ in_transaction = true;
+
+ now = time(NULL);
+ for (i = 0; i < num_rules; i++) {
+ ret = sysdb_sudo_store_rule(domain, rules[i],
+ domain->sudo_timeout, now);
+ if (ret == EINVAL) {
+ /* Multiple CNs are error on server side, we can just ignore this
+ * rule and save the others. Loud debug message is in logs. */
+ continue;
+ } else if (ret == ERR_MALFORMED_ENTRY) {
+ /* Attribute SYSDB_SUDO_CACHE_AT_USER is missing but we can
+ * continue with next sudoRule. */
+ continue;
+ } else if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ ret = sysdb_transaction_commit(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
+ goto done;
+ }
+ in_transaction = false;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(domain->sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not cancel transaction\n");
+ }
+ }
+
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to store sudo rules [%d]: %s\n",
+ ret, sss_strerror(ret));
+ }
+
+ return ret;
+}
+
+errno_t sysdb_search_sudo_rules(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter,
+ const char **attrs,
+ size_t *_msgs_count,
+ struct ldb_message ***_msgs)
+{
+ TALLOC_CTX *tmp_ctx;
+ size_t msgs_count;
+ struct ldb_message **msgs;
+ struct ldb_dn *dn;
+ char *filter;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ NULL_CHECK(tmp_ctx, ret, done);
+
+ dn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb, SYSDB_TMPL_CUSTOM_SUBTREE,
+ SUDORULE_SUBDIR, domain->name);
+ if (dn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to build base dn\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (sub_filter == NULL) {
+ filter = talloc_asprintf(tmp_ctx, "(%s)", SUDO_ALL_FILTER);
+ } else {
+ filter = talloc_asprintf(tmp_ctx, "(&%s%s)",
+ SUDO_ALL_FILTER, sub_filter);
+ }
+ if (filter == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to build filter\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Search sudo rules with filter: %s\n", filter);
+
+ ret = sysdb_search_entry(tmp_ctx, domain->sysdb, dn,
+ LDB_SCOPE_SUBTREE, filter, attrs,
+ &msgs_count, &msgs);
+
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "No such entry\n");
+ *_msgs = NULL;
+ *_msgs_count = 0;
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Error: %d (%s)\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ *_msgs_count = msgs_count;
+ *_msgs = talloc_steal(mem_ctx, msgs);
+
+ ret = EOK;
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static struct ldb_dn *
+sysdb_sudo_rule_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name)
+{
+ return sysdb_custom_dn(mem_ctx, domain, name, SUDORULE_SUBDIR);
+}
+
+errno_t
+sysdb_set_sudo_rule_attr(struct sss_domain_info *domain,
+ const char *name,
+ struct sysdb_attrs *attrs,
+ int mod_op)
+{
+ errno_t ret;
+ struct ldb_dn *dn;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ dn = sysdb_sudo_rule_dn(tmp_ctx, domain, name);
+ NULL_CHECK(dn, ret, done);
+
+ ret = sysdb_set_entry_attr(domain->sysdb, dn, attrs, mod_op);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
diff --git a/src/db/sysdb_sudo.h b/src/db/sysdb_sudo.h
new file mode 100644
index 0000000..4770c88
--- /dev/null
+++ b/src/db/sysdb_sudo.h
@@ -0,0 +1,157 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) 2011 Red Hat
+
+ 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 _SYSDB_SUDO_H_
+#define _SYSDB_SUDO_H_
+
+#include "db/sysdb.h"
+
+/* subdirs in cn=custom in sysdb. We don't store sudo stuff in sysdb directly
+ * b/c it's not name-service-switch data */
+#define SUDORULE_SUBDIR "sudorules"
+
+/* attribute of SUDORULE_SUBDIR
+ * should be true if we have downloaded all rules atleast once */
+#define SYSDB_SUDO_AT_REFRESHED "refreshed"
+#define SYSDB_SUDO_AT_LAST_FULL_REFRESH "sudoLastFullRefreshTime"
+
+/* sysdb attributes */
+#define SYSDB_SUDO_CACHE_OC "sudoRule"
+#define SYSDB_SUDO_CACHE_AT_CN "cn"
+#define SYSDB_SUDO_CACHE_AT_USER "sudoUser"
+#define SYSDB_SUDO_CACHE_AT_HOST "sudoHost"
+#define SYSDB_SUDO_CACHE_AT_COMMAND "sudoCommand"
+#define SYSDB_SUDO_CACHE_AT_OPTION "sudoOption"
+#define SYSDB_SUDO_CACHE_AT_RUNAS "sudoRunAs"
+#define SYSDB_SUDO_CACHE_AT_RUNASUSER "sudoRunAsUser"
+#define SYSDB_SUDO_CACHE_AT_RUNASGROUP "sudoRunAsGroup"
+#define SYSDB_SUDO_CACHE_AT_NOTBEFORE "sudoNotBefore"
+#define SYSDB_SUDO_CACHE_AT_NOTAFTER "sudoNotAfter"
+#define SYSDB_SUDO_CACHE_AT_ORDER "sudoOrder"
+
+/* sysdb ipa attributes */
+#define SYSDB_IPA_SUDORULE_OC "ipasudorule"
+#define SYSDB_IPA_SUDORULE_ENABLED "ipaEnabledFlag"
+#define SYSDB_IPA_SUDORULE_OPTION "ipaSudoOpt"
+#define SYSDB_IPA_SUDORULE_RUNASUSER "ipaSudoRunAs"
+#define SYSDB_IPA_SUDORULE_RUNASGROUP "ipaSudoRunAsGroup"
+#define SYSDB_IPA_SUDORULE_ORIGCMD "originalMemberCommand"
+#define SYSDB_IPA_SUDORULE_ALLOWCMD "memberAllowCmd"
+#define SYSDB_IPA_SUDORULE_DENYCMD "memberDenyCmd"
+#define SYSDB_IPA_SUDORULE_HOST "memberHost"
+#define SYSDB_IPA_SUDORULE_USER "memberUser"
+#define SYSDB_IPA_SUDORULE_NOTAFTER "sudoNotAfter"
+#define SYSDB_IPA_SUDORULE_NOTBEFORE "sudoNotBefore"
+#define SYSDB_IPA_SUDORULE_SUDOORDER "sudoOrder"
+#define SYSDB_IPA_SUDORULE_CMDCATEGORY "cmdCategory"
+#define SYSDB_IPA_SUDORULE_HOSTCATEGORY "hostCategory"
+#define SYSDB_IPA_SUDORULE_USERCATEGORY "userCategory"
+#define SYSDB_IPA_SUDORULE_RUNASUSERCATEGORY "ipaSudoRunAsUserCategory"
+#define SYSDB_IPA_SUDORULE_RUNASGROUPCATEGORY "ipaSudoRunAsGroupCategory"
+#define SYSDB_IPA_SUDORULE_RUNASEXTUSER "ipaSudoRunAsExtUser"
+#define SYSDB_IPA_SUDORULE_RUNASEXTGROUP "ipaSudoRunAsExtGroup"
+#define SYSDB_IPA_SUDORULE_RUNASEXTUSERGROUP "ipaSudoRunAsExtUserGroup"
+#define SYSDB_IPA_SUDORULE_EXTUSER "externalUser"
+
+#define SYSDB_IPA_SUDOCMDGROUP_OC "ipasudocmdgrp"
+
+#define SYSDB_IPA_SUDOCMD_OC "ipasudocmd"
+#define SYSDB_IPA_SUDOCMD_SUDOCMD "sudoCmd"
+
+/* When constructing a sysdb filter, OR these values to include.. */
+#define SYSDB_SUDO_FILTER_NONE 0x00 /* no additional filter */
+#define SYSDB_SUDO_FILTER_USERNAME 0x01 /* username */
+#define SYSDB_SUDO_FILTER_UID 0x02 /* uid */
+#define SYSDB_SUDO_FILTER_GROUPS 0x04 /* groups */
+#define SYSDB_SUDO_FILTER_NGRS 0x08 /* netgroups */
+#define SYSDB_SUDO_FILTER_ONLY_EXPIRED 0x10 /* only expired */
+#define SYSDB_SUDO_FILTER_INCLUDE_ALL 0x20 /* ALL */
+#define SYSDB_SUDO_FILTER_INCLUDE_DFL 0x40 /* include cn=default */
+#define SYSDB_SUDO_FILTER_USERINFO SYSDB_SUDO_FILTER_USERNAME \
+ | SYSDB_SUDO_FILTER_UID \
+ | SYSDB_SUDO_FILTER_GROUPS \
+ | SYSDB_SUDO_FILTER_NGRS
+
+errno_t sysdb_sudo_filter_rules_by_time(TALLOC_CTX *mem_ctx,
+ uint32_t in_num_rules,
+ struct sysdb_attrs **in_rules,
+ time_t now,
+ uint32_t *_num_rules,
+ struct sysdb_attrs ***_rules);
+
+char *
+sysdb_sudo_filter_expired(TALLOC_CTX *mem_ctx,
+ const char *username,
+ char **groupnames,
+ uid_t uid);
+
+char *
+sysdb_sudo_filter_defaults(TALLOC_CTX *mem_ctx);
+
+char *
+sysdb_sudo_filter_user(TALLOC_CTX *mem_ctx,
+ const char *username,
+ char **groupnames,
+ uid_t uid);
+
+char *
+sysdb_sudo_filter_netgroups(TALLOC_CTX *mem_ctx,
+ const char *username,
+ char **groupnames,
+ uid_t uid);
+
+errno_t
+sysdb_get_sudo_user_info(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *username,
+ const char **_orig_name,
+ uid_t *_uid,
+ char ***_groupnames);
+
+errno_t sysdb_sudo_set_last_full_refresh(struct sss_domain_info *domain,
+ time_t value);
+errno_t sysdb_sudo_get_last_full_refresh(struct sss_domain_info *domain,
+ time_t *value);
+
+errno_t sysdb_sudo_purge(struct sss_domain_info *domain,
+ const char *delete_filter,
+ struct sysdb_attrs **rules,
+ size_t num_rules);
+
+errno_t
+sysdb_sudo_store(struct sss_domain_info *domain,
+ struct sysdb_attrs **rules,
+ size_t num_rules);
+
+errno_t
+sysdb_search_sudo_rules(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter,
+ const char **attrs,
+ size_t *_msgs_count,
+ struct ldb_message ***_msgs);
+
+errno_t
+sysdb_set_sudo_rule_attr(struct sss_domain_info *domain,
+ const char *name,
+ struct sysdb_attrs *attrs,
+ int mod_op);
+
+#endif /* _SYSDB_SUDO_H_ */
diff --git a/src/db/sysdb_upgrade.c b/src/db/sysdb_upgrade.c
new file mode 100644
index 0000000..346a1cb
--- /dev/null
+++ b/src/db/sysdb_upgrade.c
@@ -0,0 +1,2791 @@
+/*
+ SSSD
+
+ Authors:
+ Simo Sorce <ssorce@redhat.com>
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2008-2011 Simo Sorce <ssorce@redhat.com>
+ Copyright (C) 2008-2011 Stephen Gallagher
+
+ 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 "db/sysdb_autofs.h"
+#include "db/sysdb_iphosts.h"
+#include "db/sysdb_ipnetworks.h"
+
+struct upgrade_ctx {
+ struct ldb_context *ldb;
+ const char *new_version;
+};
+
+static errno_t commence_upgrade(TALLOC_CTX *mem_ctx, struct ldb_context *ldb,
+ const char *new_ver, struct upgrade_ctx **_ctx)
+{
+ struct upgrade_ctx *ctx;
+ int ret;
+
+ DEBUG(SSSDBG_IMPORTANT_INFO, "UPGRADING DB TO VERSION %s\n", new_ver);
+
+ ctx = talloc(mem_ctx, struct upgrade_ctx);
+ if (!ctx) {
+ return ENOMEM;
+ }
+
+ ctx->ldb = ldb;
+ ctx->new_version = new_ver;
+
+ ret = ldb_transaction_start(ldb);
+ if (ret != LDB_SUCCESS) {
+ ret = EIO;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(ctx);
+ } else {
+ *_ctx = ctx;
+ }
+ return ret;
+}
+
+static errno_t update_version(struct upgrade_ctx *ctx)
+{
+ struct ldb_message *msg = NULL;
+ errno_t ret;
+
+ msg = ldb_msg_new(ctx);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+ msg->dn = ldb_dn_new(msg, ctx->ldb, SYSDB_BASE);
+ 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", ctx->new_version);
+ 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_free(msg);
+ return ret;
+}
+
+static int finish_upgrade(int ret, struct upgrade_ctx **ctx, const char **ver)
+{
+ int lret;
+
+ if (ret == EOK) {
+ lret = ldb_transaction_commit((*ctx)->ldb);
+ ret = sysdb_error_to_errno(lret);
+ if (ret == EOK) {
+ *ver = (*ctx)->new_version;
+ }
+ }
+
+ if (ret != EOK) {
+ lret = ldb_transaction_cancel((*ctx)->ldb);
+ if (lret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not cancel transaction! [%s]\n",
+ ldb_strerror(lret));
+ /* Do not overwrite ret here, we want to return
+ * the original failure, not the failure of the
+ * transaction cancellation.
+ */
+ }
+ }
+
+ talloc_zfree(*ctx);
+ return ret;
+}
+
+/* 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
+ */
+int sysdb_upgrade_01(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;
+ /* No change needed because this version has objectclass group */
+ const char *filter = "(&(memberUid=*)(objectclass=group))";
+ const char *attrs[] = { "memberUid", NULL };
+ const char *mdn;
+ char *domain;
+ int ret, i, j;
+ TALLOC_CTX *tmp_ctx;
+ struct upgrade_ctx *ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = commence_upgrade(tmp_ctx, ldb, SYSDB_VERSION_0_2, &ctx);
+ if (ret) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ basedn = ldb_dn_new(tmp_ctx, ldb, SYSDB_BASE);
+ if (!basedn) {
+ ret = EIO;
+ goto done;
+ }
+
+ ret = ldb_search(ldb, tmp_ctx, &res,
+ basedn, LDB_SCOPE_SUBTREE,
+ attrs, "%s", filter);
+ 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(SSSDBG_CRIT_FAILURE,
+ "memberUid is missing from message [%s], skipping\n",
+ ldb_dn_get_linearized(res->msgs[i]->dn));
+ continue;
+ }
+
+ /* create modification message */
+ msg = ldb_msg_new(tmp_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(tmp_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(tmp_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, update version number */
+ ret = update_version(ctx);
+
+done:
+ ret = finish_upgrade(ret, &ctx, ver);
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_check_upgrade_02(struct sss_domain_info *domains,
+ const char *db_path)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct ldb_context *ldb;
+ char *ldb_file;
+ struct sysdb_ctx *sysdb;
+ 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(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ldb_file = talloc_asprintf(tmp_ctx, "%s/"LOCAL_SYSDB_FILE,
+ db_path);
+ if (ldb_file == NULL) {
+ ret = ENOMEM;
+ goto exit;
+ }
+
+ ret = sysdb_ldb_connect(tmp_ctx, ldb_file, 0, &ldb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_ldb_connect failed.\n");
+ return ret;
+ }
+
+ verdn = ldb_dn_new(tmp_ctx, ldb, SYSDB_BASE);
+ 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(SSSDBG_CONF_SETTINGS,
+ "Upgrading DB from version: %s\n", version);
+
+ if (strcmp(version, SYSDB_VERSION_0_1) == 0) {
+ /* convert database */
+ ret = sysdb_upgrade_01(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(SSSDBG_IMPORTANT_INFO,
+ "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, SSSDBG_FATAL_FAILURE);
+ if (ret != EOK) {
+ goto exit;
+ }
+
+ /* reopen */
+ ret = sysdb_ldb_connect(tmp_ctx, ldb_file, 0, &ldb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_ldb_connect failed.\n");
+ return ret;
+ }
+
+ /* open a transaction */
+ ret = ldb_transaction_start(ldb);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "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;
+
+ /* create new dom db */
+ ret = sysdb_domain_init_internal(tmp_ctx, dom,
+ db_path, false, &sysdb);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = ldb_transaction_start(sysdb->ldb);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "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, sysdb->ldb,
+ SYSDB_DOM_BASE, dom->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;
+ }
+
+ /*
+ * dom->sysdb->ldb is not initialized,
+ * so ldb_dn_new_fmt() shouldn't be changed to sysdb_*_base_dn()
+ */
+ users_dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb,
+ SYSDB_TMPL_USER_BASE, dom->name);
+ if (!users_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /*
+ * dom->sysdb->ldb is not initialized,
+ * so ldb_dn_new_fmt() shouldn't be changed to sysdb_*_base_dn()
+ */
+ groups_dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb,
+ SYSDB_TMPL_GROUP_BASE, dom->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, sysdb->ldb,
+ ldb_dn_get_linearized(orig_dn));
+ if (!msg->dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_add(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "WARNING: Could not add entry %s,"
+ " to new ldb file! (%d [%s])\n",
+ ldb_dn_get_linearized(msg->dn),
+ ret, ldb_errstring(sysdb->ldb));
+ }
+
+ ret = ldb_delete(ldb, orig_dn);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "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(SSSDBG_TRACE_ALL, "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(SSSDBG_TRACE_ALL, "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(SSSDBG_TRACE_ALL, "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(sysdb->ldb);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "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, SYSDB_BASE);
+ 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(SSSDBG_CRIT_FAILURE,
+ "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(sysdb->ldb);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to cancel ldb transaction! (%d)\n", ret);
+ }
+ }
+ ret = ldb_transaction_cancel(ldb);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to cancel ldb transaction! (%d)\n", ret);
+ }
+ }
+
+exit:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_upgrade_03(struct sysdb_ctx *sysdb, const char **ver)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ struct ldb_message *msg;
+ struct upgrade_ctx *ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_4, &ctx);
+ if (ret) {
+ return ret;
+ }
+
+ /* Make this database case-sensitive */
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+ msg->dn = ldb_dn_new(tmp_ctx, sysdb->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(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ /* conversion done, update version number */
+ ret = update_version(ctx);
+
+done:
+ ret = finish_upgrade(ret, &ctx, ver);
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_upgrade_04(struct sysdb_ctx *sysdb, const char **ver)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ struct ldb_message *msg;
+ struct upgrade_ctx *ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_5, &ctx);
+ if (ret) {
+ return ret;
+ }
+
+ /* Add new index */
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+ msg->dn = ldb_dn_new(tmp_ctx, sysdb->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(sysdb->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, sysdb->ldb, "@MEMBEROF-REBUILD");
+ if (!msg->dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_add(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ /* conversion done, update version number */
+ ret = update_version(ctx);
+
+done:
+ ret = finish_upgrade(ret, &ctx, ver);
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_upgrade_05(struct sysdb_ctx *sysdb, const char **ver)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ struct ldb_message *msg;
+ struct upgrade_ctx *ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_6, &ctx);
+ if (ret) {
+ return ret;
+ }
+
+ /* Add new indexes */
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+ msg->dn = ldb_dn_new(tmp_ctx, sysdb->ldb, "@INDEXLIST");
+ if (!msg->dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Add Index for dataExpireTimestamp */
+ 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", "dataExpireTimestamp");
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Add index to speed up ONELEVEL searches */
+ ret = ldb_msg_add_empty(msg, "@IDXONE", LDB_FLAG_MOD_ADD, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = ldb_msg_add_string(msg, "@IDXONE", "1");
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_modify(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ /* conversion done, update version number */
+ ret = update_version(ctx);
+
+done:
+ ret = finish_upgrade(ret, &ctx, ver);
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_upgrade_06(struct sysdb_ctx *sysdb, const char **ver)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ struct ldb_message *msg;
+ struct upgrade_ctx *ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_7, &ctx);
+ if (ret) {
+ return ret;
+ }
+
+ /* Add new indexes */
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+ msg->dn = ldb_dn_new(tmp_ctx, sysdb->ldb, "@ATTRIBUTES");
+ if (!msg->dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Case insensitive search for originalDN */
+ ret = ldb_msg_add_empty(msg, SYSDB_ORIG_DN, LDB_FLAG_MOD_ADD, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = ldb_msg_add_string(msg, SYSDB_ORIG_DN, "CASE_INSENSITIVE");
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_modify(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ /* conversion done, update version number */
+ ret = update_version(ctx);
+
+done:
+ ret = finish_upgrade(ret, &ctx, ver);
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_upgrade_07(struct sysdb_ctx *sysdb, const char **ver)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ struct ldb_message *msg;
+ struct upgrade_ctx *ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_8, &ctx);
+ if (ret) {
+ return ret;
+ }
+
+ /* Add new indexes */
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+ msg->dn = ldb_dn_new(tmp_ctx, sysdb->ldb, "@INDEXLIST");
+ if (!msg->dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Add Index for nameAlias */
+ 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", "nameAlias");
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_modify(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ /* conversion done, update version number */
+ ret = update_version(ctx);
+
+done:
+ ret = finish_upgrade(ret, &ctx, ver);
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_upgrade_08(struct sysdb_ctx *sysdb, const char **ver)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ struct ldb_message *msg;
+ struct upgrade_ctx *ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_9, &ctx);
+ if (ret) {
+ return ret;
+ }
+
+ /* Add new indexes */
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+ msg->dn = ldb_dn_new(tmp_ctx, sysdb->ldb, "@INDEXLIST");
+ if (!msg->dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Add Index for servicePort and serviceProtocol */
+ 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", "servicePort");
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, "@IDXATTR", "serviceProtocol");
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_modify(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ /* conversion done, update version number */
+ ret = update_version(ctx);
+
+done:
+ ret = finish_upgrade(ret, &ctx, ver);
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_upgrade_09(struct sysdb_ctx *sysdb, const char **ver)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ struct ldb_message *msg;
+ struct upgrade_ctx *ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_10, &ctx);
+ if (ret) {
+ return ret;
+ }
+
+ /* Add new indexes */
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+ msg->dn = ldb_dn_new(tmp_ctx, sysdb->ldb, "@INDEXLIST");
+ if (!msg->dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Add Index for ipHostNumber and ipNetworkNumber */
+ 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", "sudoUser");
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_modify(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ /* conversion done, update version number */
+ ret = update_version(ctx);
+
+done:
+ ret = finish_upgrade(ret, &ctx, ver);
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_upgrade_10(struct sysdb_ctx *sysdb, struct sss_domain_info *domain,
+ const char **ver)
+{
+
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ struct ldb_result *res;
+ struct ldb_message *msg;
+ struct ldb_message *user;
+ struct ldb_message_element *memberof_el;
+ const char *name;
+ struct ldb_dn *basedn;
+ /* No change needed because version 10 has objectclass user */
+ const char *filter = "(&(objectClass=user)(!(uidNumber=*))(memberOf=*))";
+ const char *attrs[] = { "name", "memberof", NULL };
+ struct upgrade_ctx *ctx;
+ int i, j;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_11, &ctx);
+ if (ret) {
+ return ret;
+ }
+
+ /*
+ * dom->sysdb->ldb is not initialized,
+ * so ldb_dn_new_fmt() shouldn't be changed to sysdb_*_base_dn()
+ */
+ basedn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb,
+ SYSDB_TMPL_USER_BASE, domain->name);
+ if (basedn == NULL) {
+ ret = EIO;
+ goto done;
+ }
+
+ ret = ldb_search(sysdb->ldb, tmp_ctx, &res, basedn, LDB_SCOPE_SUBTREE,
+ attrs, "%s", filter);
+ if (ret != LDB_SUCCESS) {
+ ret = EIO;
+ goto done;
+ }
+
+ for (i = 0; i < res->count; i++) {
+ user = res->msgs[i];
+ memberof_el = ldb_msg_find_element(user, "memberof");
+ if (memberof_el == NULL) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ name = ldb_msg_find_attr_as_string(user, "name", NULL);
+ if (name == NULL) {
+ ret = EIO;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, "User [%s] is a member of %d groups\n",
+ name, memberof_el->num_values);
+
+ for (j = 0; j < memberof_el->num_values; j++) {
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = ldb_dn_from_ldb_val(tmp_ctx, sysdb->ldb, &memberof_el->values[j]);
+ if (msg->dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (!ldb_dn_validate(msg->dn)) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "DN validation failed during "
+ "upgrade: [%s]\n",
+ memberof_el->values[j].data);
+ talloc_zfree(msg);
+ continue;
+ }
+
+ ret = ldb_msg_add_empty(msg, "ghost", LDB_FLAG_MOD_ADD, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = ldb_msg_add_string(msg, "ghost", name);
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Adding ghost [%s] to entry [%s]\n",
+ name, ldb_dn_get_linearized(msg->dn));
+
+ ret = sss_ldb_modify_permissive(sysdb->ldb, msg);
+ talloc_zfree(msg);
+ if (ret == LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS) {
+ /* If we failed adding the ghost user(s) because the values already
+ * exist, they were probably propagated from a parent that was
+ * upgraded before us. Mark the group as expired so that it is
+ * refreshed on next request.
+ */
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = ldb_dn_from_ldb_val(tmp_ctx, sysdb->ldb, &memberof_el->values[j]);
+ if (msg->dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_msg_add_empty(msg, SYSDB_CACHE_EXPIRE,
+ LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != LDB_SUCCESS) {
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, SYSDB_CACHE_EXPIRE, "1");
+ if (ret != LDB_SUCCESS) {
+ goto done;
+ }
+
+ ret = sss_ldb_modify_permissive(sysdb->ldb, msg);
+ talloc_zfree(msg);
+ if (ret != LDB_SUCCESS) {
+ goto done;
+ }
+ } else if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Removing fake user [%s]\n",
+ ldb_dn_get_linearized(user->dn));
+
+ ret = ldb_delete(sysdb->ldb, user->dn);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+
+ /* conversion done, update version number */
+ ret = update_version(ctx);
+
+done:
+ ret = finish_upgrade(ret, &ctx, ver);
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_upgrade_11(struct sysdb_ctx *sysdb, struct sss_domain_info *domain,
+ const char **ver)
+{
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ struct ldb_result *res;
+ struct ldb_message *entry;
+ const char *key;
+ const char *value;
+ struct ldb_message_element *memberof_el;
+ struct ldb_dn *memberof_dn;
+ struct ldb_dn *basedn;
+ const struct ldb_val *val;
+ const char *attrs[] = { SYSDB_AUTOFS_ENTRY_KEY,
+ SYSDB_AUTOFS_ENTRY_VALUE,
+ SYSDB_MEMBEROF,
+ NULL };
+ struct upgrade_ctx *ctx;
+ size_t i, j;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_12, &ctx);
+ if (ret) {
+ return ret;
+ }
+
+ basedn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_TMPL_CUSTOM_SUBTREE,
+ AUTOFS_ENTRY_SUBDIR, domain->name);
+ if (basedn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_search(sysdb->ldb, tmp_ctx, &res, basedn, LDB_SCOPE_SUBTREE,
+ attrs, "(objectClass=%s)", SYSDB_AUTOFS_ENTRY_OC);
+ if (ret != LDB_SUCCESS) {
+ ret = EIO;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Found %d autofs entries\n", res->count);
+
+ for (i = 0; i < res->count; i++) {
+ entry = res->msgs[i];
+ key = ldb_msg_find_attr_as_string(entry,
+ SYSDB_AUTOFS_ENTRY_KEY, NULL);
+ value = ldb_msg_find_attr_as_string(entry,
+ SYSDB_AUTOFS_ENTRY_VALUE, NULL);
+ memberof_el = ldb_msg_find_element(entry, SYSDB_MEMBEROF);
+
+ if (key && value && memberof_el) {
+ for (j = 0; j < memberof_el->num_values; j++) {
+ memberof_dn = ldb_dn_from_ldb_val(tmp_ctx, sysdb->ldb,
+ &(memberof_el->values[j]));
+ if (!memberof_dn) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot convert memberof into DN, skipping\n");
+ continue;
+ }
+
+ val = ldb_dn_get_rdn_val(memberof_dn);
+ if (!val) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot get map name from map DN\n");
+ continue;
+ }
+
+ ret = sysdb_save_autofsentry(domain,
+ (const char *) val->data,
+ key, value, NULL, 0, 0);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot save autofs entry [%s]-[%s] into map %s\n",
+ key, value, val->data);
+ continue;
+ }
+ }
+
+ }
+
+ /* Delete the old entry if it was either processed or incomplete */
+ DEBUG(SSSDBG_TRACE_LIBS, "Deleting [%s]\n",
+ ldb_dn_get_linearized(entry->dn));
+
+ ret = ldb_delete(sysdb->ldb, entry->dn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot delete old autofs entry %s\n",
+ ldb_dn_get_linearized(entry->dn));
+ continue;
+ }
+ }
+
+ /* conversion done, update version number */
+ ret = update_version(ctx);
+
+done:
+ ret = finish_upgrade(ret, &ctx, ver);
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_upgrade_12(struct sysdb_ctx *sysdb, const char **ver)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ struct ldb_message *msg;
+ struct upgrade_ctx *ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_13, &ctx);
+ if (ret) {
+ return ret;
+ }
+
+ /* add new indexes */
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+ msg->dn = ldb_dn_new(tmp_ctx, sysdb->ldb, "@INDEXLIST");
+ if (!msg->dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* add index for sshKnownHostsExpire */
+ 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", "sshKnownHostsExpire");
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_modify(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ /* conversion done, update version number */
+ ret = update_version(ctx);
+
+done:
+ ret = finish_upgrade(ret, &ctx, ver);
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_upgrade_13(struct sysdb_ctx *sysdb, const char **ver)
+{
+ struct upgrade_ctx *ctx;
+ struct ldb_result *dom_res;
+ struct ldb_result *res;
+ struct ldb_dn *basedn;
+ const char *attrs[] = { "cn", "name", NULL };
+ const char *tmp_str;
+ errno_t ret;
+ int i, j, l, n;
+
+ ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_14, &ctx);
+ if (ret) {
+ return ret;
+ }
+
+ basedn = ldb_dn_new(ctx, sysdb->ldb, SYSDB_BASE);
+ if (!basedn) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to build base dn\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = ldb_search(sysdb->ldb, ctx, &dom_res,
+ basedn, LDB_SCOPE_ONELEVEL,
+ attrs, "objectclass=%s", SYSDB_SUBDOMAIN_CLASS);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to search subdomains\n");
+ ret = EIO;
+ goto done;
+ }
+
+ for (i = 0; i < dom_res->count; i++) {
+
+ tmp_str = ldb_msg_find_attr_as_string(dom_res->msgs[i], "cn", NULL);
+ if (tmp_str == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "The object [%s] doesn't have a name\n",
+ ldb_dn_get_linearized(dom_res->msgs[i]->dn));
+ continue;
+ }
+
+ basedn = ldb_dn_new_fmt(ctx, sysdb->ldb, SYSDB_DOM_BASE, tmp_str);
+ if (!basedn) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to build base dn for subdomain %s\n", tmp_str);
+ continue;
+ }
+
+ ret = ldb_search(sysdb->ldb, ctx, &res,
+ basedn, LDB_SCOPE_SUBTREE, attrs, NULL);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to search subdomain %s\n", tmp_str);
+ talloc_free(basedn);
+ continue;
+ }
+
+ l = ldb_dn_get_comp_num(basedn);
+ for (j = 0; j < res->count; j++) {
+ n = ldb_dn_get_comp_num(res->msgs[j]->dn);
+ if (n <= l + 1) {
+ /* Do not remove subdomain containers, only their contents */
+ continue;
+ }
+ ret = ldb_delete(sysdb->ldb, res->msgs[j]->dn);
+ if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to delete %s\n",
+ ldb_dn_get_linearized(res->msgs[j]->dn));
+ continue;
+ }
+ }
+
+ talloc_free(basedn);
+ talloc_free(res);
+ }
+
+ talloc_free(dom_res);
+
+ /* conversion done, update version number */
+ ret = update_version(ctx);
+
+done:
+ ret = finish_upgrade(ret, &ctx, ver);
+ return ret;
+}
+
+int sysdb_upgrade_14(struct sysdb_ctx *sysdb, const char **ver)
+{
+ struct upgrade_ctx *ctx;
+ struct ldb_message *msg;
+ struct ldb_result *res;
+ struct ldb_dn *basedn;
+ struct ldb_dn *newdn;
+ const char *attrs[] = { SYSDB_NAME, NULL };
+ const char *tmp_str;
+ errno_t ret;
+ int i;
+
+ ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_15, &ctx);
+ if (ret) {
+ return ret;
+ }
+
+ basedn = ldb_dn_new(ctx, sysdb->ldb, SYSDB_BASE);
+ if (!basedn) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to build base dn\n");
+ ret = EIO;
+ goto done;
+ }
+
+ /* create base ranges container */
+ msg = ldb_msg_new(ctx);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+ msg->dn = ldb_dn_new(msg, sysdb->ldb, SYSDB_TMPL_RANGE_BASE);
+ if (!msg->dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = ldb_msg_add_string(msg, "cn", "ranges");
+ if (ret != LDB_SUCCESS) {
+ ret = EIO;
+ goto done;
+ }
+ /* do a synchronous add */
+ ret = ldb_add(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to upgrade DB (%d, [%s])!\n",
+ ret, ldb_errstring(sysdb->ldb));
+ ret = EIO;
+ goto done;
+ }
+ talloc_zfree(msg);
+
+ ret = ldb_search(sysdb->ldb, ctx, &res,
+ basedn, LDB_SCOPE_SUBTREE, attrs,
+ "objectclass=%s", SYSDB_ID_RANGE_CLASS);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to search range objects\n");
+ ret = EIO;
+ goto done;
+ }
+
+ /* Failure to convert any range is not fatal. As long as there are no
+ * left-over objects we can fail to move them around, as they will be
+ * recreated on the next online access */
+ for (i = 0; i < res->count; i++) {
+ tmp_str = ldb_msg_find_attr_as_string(res->msgs[i], SYSDB_NAME, NULL);
+ if (tmp_str == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "The object [%s] doesn't have a name\n",
+ ldb_dn_get_linearized(res->msgs[i]->dn));
+ ret = ldb_delete(sysdb->ldb, res->msgs[i]->dn);
+ if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to delete %s\n",
+ ldb_dn_get_linearized(res->msgs[i]->dn));
+ ret = EIO;
+ goto done;
+ }
+ continue;
+ }
+
+ newdn = ldb_dn_new_fmt(ctx, sysdb->ldb, SYSDB_TMPL_RANGE, tmp_str);
+ if (!newdn) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to create new DN to move [%s]\n",
+ ldb_dn_get_linearized(res->msgs[i]->dn));
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = ldb_rename(sysdb->ldb, res->msgs[i]->dn, newdn);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to move [%s] to [%s]\n",
+ ldb_dn_get_linearized(res->msgs[i]->dn),
+ ldb_dn_get_linearized(newdn));
+ ret = ldb_delete(sysdb->ldb, res->msgs[i]->dn);
+ if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to delete %s\n",
+ ldb_dn_get_linearized(res->msgs[i]->dn));
+ ret = EIO;
+ goto done;
+ }
+ }
+ talloc_zfree(newdn);
+ }
+
+ /* conversion done, update version number */
+ ret = update_version(ctx);
+
+done:
+ ret = finish_upgrade(ret, &ctx, ver);
+ return ret;
+}
+
+int sysdb_upgrade_15(struct sysdb_ctx *sysdb, const char **ver)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ struct ldb_message *msg;
+ struct upgrade_ctx *ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_16, &ctx);
+ if (ret) {
+ return ret;
+ }
+
+ /* Add new indexes */
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+ msg->dn = ldb_dn_new(tmp_ctx, sysdb->ldb, "@ATTRIBUTES");
+ if (!msg->dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Case insensitive search for canonicalUserPrincipalName */
+ ret = ldb_msg_add_empty(msg, SYSDB_CANONICAL_UPN, LDB_FLAG_MOD_ADD, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = ldb_msg_add_string(msg, SYSDB_CANONICAL_UPN, "CASE_INSENSITIVE");
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_modify(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ /* conversion done, update version number */
+ ret = update_version(ctx);
+
+done:
+ ret = finish_upgrade(ret, &ctx, ver);
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_upgrade_16(struct sysdb_ctx *sysdb, const char **ver)
+{
+ struct ldb_message *msg;
+ struct upgrade_ctx *ctx;
+ errno_t ret;
+
+ ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_17, &ctx);
+ if (ret) {
+ return ret;
+ }
+
+ msg = ldb_msg_new(ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = ldb_dn_new(msg, sysdb->ldb, "@INDEXLIST");
+ if (msg->dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* add index for objectSIDString */
+ 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", "objectSIDString");
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_modify(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ /* conversion done, update version number */
+ ret = update_version(ctx);
+
+done:
+ ret = finish_upgrade(ret, &ctx, ver);
+ return ret;
+}
+
+static char *object_domain_from_dn(TALLOC_CTX *mem_ctx,
+ struct ldb_dn *dn,
+ unsigned domain_index)
+{
+ const struct ldb_val *val;
+
+ val = ldb_dn_get_component_val(dn, domain_index);
+ if (val == NULL) {
+ return NULL;
+ }
+ return talloc_strdup(mem_ctx, (const char *) val->data);
+}
+
+static char *object_domain(TALLOC_CTX *mem_ctx,
+ struct ldb_context *ldb,
+ struct ldb_message *msg,
+ const char *domain_attr,
+ unsigned domain_index)
+{
+ struct ldb_dn *dom_dn;
+
+ if (domain_attr != NULL) {
+ dom_dn = ldb_msg_find_attr_as_dn(ldb, mem_ctx, msg, domain_attr);
+ } else {
+ /* If no specific attribute to take the domain from is specified,
+ * use the DN */
+ dom_dn = msg->dn;
+ }
+
+ if (dom_dn == NULL) {
+ return NULL;
+ }
+
+ return object_domain_from_dn(mem_ctx, dom_dn, domain_index);
+}
+
+/* Used for attributes like sudoUser which contain group or user name or
+ * ID, depending on the value prefix */
+typedef bool (*should_qualify_val_fn)(const char *val);
+
+/* Qualifies a string attribute using domain_name. Optionally, if qfn is
+ * given, only qualifies the name if qfn returns true */
+static errno_t qualify_attr(struct ldb_message *msg,
+ struct ldb_message *mod_msg,
+ struct sss_names_ctx *names,
+ const char *domain_name,
+ const char *attrname,
+ should_qualify_val_fn qfn)
+{
+ struct ldb_message_element *el;
+ struct ldb_message_element *mod_el;
+ char *fqval;
+ char *shortname;
+ const char *rawname;
+ int ret;
+ struct ldb_val val;
+ bool exists = false;
+
+ el = ldb_msg_find_element(msg, attrname);
+ if (el == NULL) {
+ /* This entry does not have this element, fine */
+ return EOK;
+ }
+
+ for (size_t c = 0; c < el->num_values; c++) {
+ rawname = (const char *) el->values[c].data;
+
+ if (qfn != NULL && qfn(rawname) == false) {
+ continue;
+ }
+
+ ret = sss_parse_name(mod_msg, names, rawname, NULL, &shortname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot parse raw attribute %s\n", rawname);
+ continue;
+ }
+
+ fqval = sss_create_internal_fqname(el->values, shortname, domain_name);
+ talloc_free(shortname);
+ if (fqval == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot qualify %s@%s\n",
+ shortname, domain_name);
+ continue;
+ }
+
+
+ mod_el = ldb_msg_find_element(mod_msg, attrname);
+ if (mod_el != NULL) {
+ val.data = (uint8_t *) fqval;
+ val.length = strlen(fqval);
+
+ if (ldb_msg_find_val(mod_el, &val) != NULL) {
+ return true;
+ }
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Qualified %s:%s into %s\n",
+ attrname, rawname, fqval);
+
+ if (!exists) {
+ ret = ldb_msg_add_empty(mod_msg, attrname, LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != LDB_SUCCESS) {
+ continue;
+ }
+
+ exists = true;
+ }
+
+ ret = ldb_msg_add_steal_string(mod_msg, attrname, fqval);
+ if (ret != LDB_SUCCESS) {
+ continue;
+ }
+ }
+
+ return EOK;
+}
+
+/* Returns a copy of old_dn_val with RDN qualified. The domain name
+ * is read from the DN itself
+ */
+static struct ldb_dn *qualify_rdn(TALLOC_CTX *mem_ctx,
+ struct ldb_context *ldb,
+ struct sss_names_ctx *names,
+ struct ldb_dn *old_dn_val)
+{
+ struct ldb_dn *parent_dn = NULL;
+ const struct ldb_val *val = NULL;
+ const char *rdn_name = NULL;
+ struct ldb_dn *new_dn = NULL;
+ char *fqrdn = NULL;
+ char *shortname = NULL;
+ char *dn_domain = NULL;
+ TALLOC_CTX *tmp_ctx = NULL;
+ int ret;
+
+ rdn_name = ldb_dn_get_rdn_name(old_dn_val);
+ if (rdn_name == NULL) {
+ return NULL;
+ }
+
+ if (strcmp(rdn_name, SYSDB_NAME) != 0) {
+ /* Only qualify DNs with name= rdn. This applies to overrideDNs mostly,
+ * because those can contain either names or UUIDs
+ */
+ return ldb_dn_copy(mem_ctx, old_dn_val);
+ }
+
+ val = ldb_dn_get_rdn_val(old_dn_val);
+ if (val == NULL) {
+ return NULL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return NULL;
+ }
+
+ dn_domain = object_domain_from_dn(tmp_ctx, old_dn_val, 2);
+ if (dn_domain == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot determine domain of %s\n",
+ ldb_dn_get_linearized(old_dn_val));
+ goto done;
+ }
+
+ ret = sss_parse_name(tmp_ctx, names, (const char *) val->data,
+ NULL, &shortname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot parse raw RDN %s\n", (const char *) val->data);
+ goto done;
+ }
+
+ fqrdn = sss_create_internal_fqname(tmp_ctx, shortname, dn_domain);
+ if (fqrdn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot qualify %s@%s\n",
+ shortname, dn_domain);
+ goto done;
+ }
+
+ parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn_val);
+ if (parent_dn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get parent of %s\n",
+ ldb_dn_get_linearized(old_dn_val));
+ goto done;
+ }
+
+ new_dn = ldb_dn_new_fmt(mem_ctx, ldb, "%s=%s,%s",
+ rdn_name, fqrdn,
+ ldb_dn_get_linearized(parent_dn));
+done:
+ talloc_free(tmp_ctx);
+ return new_dn;
+}
+
+static errno_t qualify_dn_attr(struct ldb_context *ldb,
+ struct ldb_message *msg,
+ struct ldb_message *mod_msg,
+ struct sss_names_ctx *names,
+ const char *attrname)
+{
+ struct ldb_message_element *el;
+ struct ldb_message_element *mod_el;
+ struct ldb_dn *attr_dn;
+ struct ldb_dn *fqdn;
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ el = ldb_msg_find_element(msg, attrname);
+ if (el == NULL || el->num_values == 0) {
+ return EOK;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ for (size_t c = 0; c < el->num_values; c++) {
+ attr_dn = ldb_dn_new(tmp_ctx, ldb, (const char *) el->values[c].data);
+ if (attr_dn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot create DN from %s\n",
+ (const char *) el->values[c].data);
+ continue;
+ }
+
+ if (!ldb_dn_validate(attr_dn)) {
+ DEBUG(SSSDBG_OP_FAILURE, "DN %s does not validate\n",
+ (const char *) el->values[c].data);
+ continue;
+ }
+
+ fqdn = qualify_rdn(tmp_ctx, ldb, names, attr_dn);
+ if (fqdn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot qualify %s\n",
+ (const char *) el->values[c].data);
+ continue;
+ }
+
+ ret = ldb_msg_add_linearized_dn(mod_msg, attrname, fqdn);
+ if (ret != LDB_SUCCESS) {
+ continue;
+ }
+
+ talloc_free(attr_dn);
+ talloc_free(fqdn);
+ }
+
+ mod_el = ldb_msg_find_element(mod_msg, attrname);
+ if (mod_el != NULL) {
+ mod_el->flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ talloc_free(tmp_ctx);
+ return EOK;
+}
+
+static errno_t expire_object(struct ldb_message *object,
+ struct ldb_message *mod_msg)
+{
+ errno_t ret;
+ struct ldb_message_element *el;
+ const char *attrs[] = { SYSDB_CACHE_EXPIRE,
+ SYSDB_LAST_UPDATE,
+ SYSDB_INITGR_EXPIRE,
+ NULL
+ };
+
+ for (size_t c = 0; attrs[c] != NULL; c++) {
+ el = ldb_msg_find_element(object, attrs[c]);
+ if (el == NULL) {
+ continue;
+ }
+
+ ret = ldb_msg_add_empty(mod_msg, attrs[c], LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_msg_add_fmt(mod_msg, attrs[c], "%d", 1);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+static errno_t qualify_object(TALLOC_CTX *mem_ctx,
+ struct ldb_context *ldb,
+ struct sss_names_ctx *names,
+ struct ldb_message *object,
+ bool qualify_dn,
+ const char *domain_attr,
+ unsigned domain_index,
+ const char *name_attrs[],
+ const char *dn_attrs[],
+ should_qualify_val_fn qfn)
+{
+ int ret;
+ struct ldb_message *mod_msg = NULL;
+ struct ldb_dn *new_object_dn = NULL;
+ const char *dom_name;
+
+ mod_msg = ldb_msg_new(mem_ctx);
+ if (mod_msg == NULL) {
+ return ENOMEM;
+ }
+ mod_msg->dn = object->dn;
+
+ dom_name = object_domain(mod_msg, ldb, object, domain_attr, domain_index);
+ if (dom_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot determine domain of %s\n",
+ ldb_dn_get_linearized(mod_msg->dn));
+ return EINVAL;
+ }
+
+ if (name_attrs != NULL) {
+ for (size_t c = 0; name_attrs[c]; c++) {
+ ret = qualify_attr(object, mod_msg, names,
+ dom_name, name_attrs[c], qfn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot qualify %s of %s\n",
+ name_attrs[c], ldb_dn_get_linearized(object->dn));
+ continue;
+ }
+ }
+ }
+
+ if (dn_attrs != NULL) {
+ for (size_t c = 0; dn_attrs[c]; c++) {
+ ret = qualify_dn_attr(ldb, object, mod_msg,
+ names, dn_attrs[c]);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot qualify %s of %s\n",
+ dn_attrs[c], ldb_dn_get_linearized(object->dn));
+ }
+ }
+ }
+
+ ret = expire_object(object, mod_msg);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot expire %s\n", ldb_dn_get_linearized(object->dn));
+ }
+
+ /* Override objects can contain both qualified and non-qualified names.
+ * Need to use permissive modification here, otherwise we might attempt
+ * to store duplicate qualified names
+ */
+ ret = sss_ldb_modify_permissive(ldb, mod_msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot modify %s\n", ldb_dn_get_linearized(object->dn));
+ goto done;
+ }
+
+ if (qualify_dn) {
+ new_object_dn = qualify_rdn(mod_msg, ldb, names, mod_msg->dn);
+ if (new_object_dn == NULL) {
+ ret = EIO;
+ goto done;
+ }
+
+ ret = ldb_rename(ldb, object->dn, new_object_dn);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot rename %s to %s\n",
+ ldb_dn_get_linearized(object->dn),
+ ldb_dn_get_linearized(new_object_dn));
+ goto done;
+ }
+ }
+
+ ret = EOK;
+done:
+ talloc_free(mod_msg);
+ return ret;
+}
+
+static void qualify_objects(struct upgrade_ctx *ctx,
+ struct ldb_context *ldb,
+ struct sss_names_ctx *names,
+ struct ldb_dn *base_dn,
+ bool qualify_dn,
+ const char *domain_attr,
+ unsigned domain_index,
+ const char *filter,
+ const char *name_attrs[],
+ const char *dn_attrs[],
+ should_qualify_val_fn qfn)
+{
+ errno_t ret;
+ struct ldb_result *objects = NULL;
+ const char *attrs[] = { "*", NULL };
+
+ ret = ldb_search(ldb, ctx, &objects, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, "%s", filter);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to search objects: %d\n", ret);
+ return;
+ }
+
+ if (objects == NULL || objects->count == 0) {
+ DEBUG(SSSDBG_TRACE_LIBS, "No match for: %s\n", filter);
+ return;
+ }
+
+ for (size_t c = 0; c < objects->count; c++) {
+ ret = qualify_object(ctx, ldb, names, objects->msgs[c],
+ qualify_dn, domain_attr, domain_index,
+ name_attrs, dn_attrs, qfn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not qualify object %s: %d\n",
+ ldb_dn_get_linearized(objects->msgs[c]->dn), ret);
+ continue;
+ }
+ }
+ talloc_free(objects);
+}
+
+static void qualify_users(struct upgrade_ctx *ctx,
+ struct ldb_context *ldb,
+ struct sss_names_ctx *names,
+ struct ldb_dn *base_dn)
+{
+ /* No change needed because this version has objectclass user */
+ const char *user_filter = "objectclass=user";
+ const char *user_name_attrs[] = { SYSDB_NAME,
+ SYSDB_NAME_ALIAS,
+ SYSDB_DEFAULT_OVERRIDE_NAME,
+ ORIGINALAD_PREFIX SYSDB_NAME,
+ NULL
+ };
+ const char *user_dn_attrs[] = { SYSDB_MEMBEROF,
+ SYSDB_OVERRIDE_DN,
+ NULL
+ };
+
+ return qualify_objects(ctx, ldb, names, base_dn,
+ true, /* qualify dn */
+ NULL, /* no special domain attr, use DN */
+ 2, /* DN's domain is third RDN from top */
+ user_filter,
+ user_name_attrs, user_dn_attrs, NULL);
+}
+
+static void qualify_groups(struct upgrade_ctx *ctx,
+ struct ldb_context *ldb,
+ struct sss_names_ctx *names,
+ struct ldb_dn *base_dn)
+{
+ /* No change needed because this version has objectclass group */
+ const char *group_filter = "objectclass=group";
+ const char *group_name_attrs[] = { SYSDB_NAME,
+ SYSDB_NAME_ALIAS,
+ SYSDB_DEFAULT_OVERRIDE_NAME,
+ ORIGINALAD_PREFIX SYSDB_NAME,
+ SYSDB_MEMBERUID,
+ SYSDB_GHOST,
+ NULL
+ };
+ const char *group_dn_attrs[] = { SYSDB_MEMBER,
+ SYSDB_MEMBEROF,
+ SYSDB_OVERRIDE_DN,
+ NULL
+ };
+
+ return qualify_objects(ctx, ldb, names, base_dn, true,
+ NULL, 2, group_filter,
+ group_name_attrs, group_dn_attrs, NULL);
+}
+
+static void qualify_user_overrides(struct upgrade_ctx *ctx,
+ struct ldb_context *ldb,
+ struct sss_names_ctx *names,
+ struct ldb_dn *base_dn)
+{
+ const char *user_override_filter = "objectclass=userOverride";
+ const char *user_ovr_name_attrs[] = { SYSDB_NAME,
+ SYSDB_NAME_ALIAS,
+ NULL
+ };
+ const char *user_ovr_dn_attrs[] = { SYSDB_OVERRIDE_OBJECT_DN,
+ NULL
+ };
+
+ return qualify_objects(ctx, ldb, names, base_dn,
+ /* Don't qualify RDN of override DN */
+ false,
+ /* Read domain from override DN */
+ SYSDB_OVERRIDE_OBJECT_DN,
+ 2, /* Third RDN from top is domain */
+ user_override_filter, user_ovr_name_attrs,
+ user_ovr_dn_attrs, NULL);
+}
+
+static void qualify_group_overrides(struct upgrade_ctx *ctx,
+ struct ldb_context *ldb,
+ struct sss_names_ctx *names,
+ struct ldb_dn *base_dn)
+{
+ const char *group_override_filter = "objectclass=groupOverride";
+ const char *group_ovr_name_attrs[] = { SYSDB_NAME,
+ SYSDB_NAME_ALIAS,
+ NULL
+ };
+ const char *group_ovr_dn_attrs[] = { SYSDB_OVERRIDE_OBJECT_DN,
+ NULL
+ };
+
+ return qualify_objects(ctx, ldb, names, base_dn,
+ false, SYSDB_OVERRIDE_OBJECT_DN, 2,
+ group_override_filter, group_ovr_name_attrs,
+ group_ovr_dn_attrs, NULL);
+}
+
+static void qualify_sudo_rules(struct upgrade_ctx *ctx,
+ struct ldb_context *ldb,
+ struct sss_names_ctx *names,
+ struct ldb_dn *base_dn)
+{
+ const char *group_override_filter = "objectclass=sudoRule";
+ const char *sudo_rule_name_attrs[] = { "sudoUser",
+ NULL
+ };
+
+ return qualify_objects(ctx, ldb, names, base_dn,
+ false, NULL, 3,
+ group_override_filter, sudo_rule_name_attrs,
+ NULL, is_user_or_group_name);
+}
+
+
+int sysdb_upgrade_17(struct sysdb_ctx *sysdb,
+ struct sysdb_dom_upgrade_ctx *upgrade_ctx,
+ const char **ver)
+{
+ struct upgrade_ctx *ctx;
+ errno_t ret, envret;
+ struct ldb_dn *base_dn;
+ struct sss_names_ctx *names = upgrade_ctx->names;
+
+ if (names == NULL) {
+ return EINVAL;
+ }
+
+ ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_18, &ctx);
+ if (ret) {
+ return ret;
+ }
+
+ /* Disable memberof plugin during this update */
+ ret = setenv("SSSD_UPGRADE_DB", "1", 1);
+ if (ret != 0) {
+ goto done;
+ }
+
+ base_dn = ldb_dn_new_fmt(ctx, sysdb->ldb, SYSDB_BASE);
+ if (base_dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ qualify_users(ctx, sysdb->ldb, names, base_dn);
+ qualify_groups(ctx, sysdb->ldb, names, base_dn);
+ qualify_user_overrides(ctx, sysdb->ldb, names, base_dn);
+ qualify_group_overrides(ctx, sysdb->ldb, names, base_dn);
+ qualify_sudo_rules(ctx, sysdb->ldb, names, base_dn);
+
+ /* conversion done, update version number */
+ ret = update_version(ctx);
+
+done:
+ ret = finish_upgrade(ret, &ctx, ver);
+ envret = unsetenv("SSSD_UPGRADE_DB");
+ if (envret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot unset SSSD_UPGRADE_DB, SSSD might not work correctly\n");
+ }
+ return ret;
+}
+
+int sysdb_upgrade_18(struct sysdb_ctx *sysdb, const char **ver)
+{
+ struct upgrade_ctx *ctx;
+ errno_t ret;
+ struct ldb_message *msg = NULL;
+
+ ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_19, &ctx);
+ if (ret) {
+ return ret;
+ }
+
+ /* Add missing indices */
+ msg = ldb_msg_new(ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = ldb_dn_new(msg, sysdb->ldb, "@INDEXLIST");
+ if (msg->dn == NULL) {
+ 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", SYSDB_GHOST);
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, "@IDXATTR", SYSDB_UPN);
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, "@IDXATTR", SYSDB_CANONICAL_UPN);
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, "@IDXATTR", SYSDB_UUID);
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, "@IDXATTR", SYSDB_USER_EMAIL);
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_modify(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ talloc_free(msg);
+
+ /* conversion done, update version number */
+ ret = update_version(ctx);
+
+done:
+ ret = finish_upgrade(ret, &ctx, ver);
+ return ret;
+}
+
+static errno_t add_object_category(struct ldb_context *ldb,
+ struct upgrade_ctx *ctx)
+{
+ errno_t ret;
+ struct ldb_result *objects = NULL;
+ const char *attrs[] = { SYSDB_OBJECTCLASS, NULL };
+ struct ldb_dn *base_dn;
+ size_t c;
+ const char *class_name;
+ struct ldb_message *msg = NULL;
+ struct ldb_message *del_msg = NULL;
+
+ base_dn = ldb_dn_new(ctx, ldb, SYSDB_BASE);
+ if (base_dn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed create base dn.\n");
+ return ENOMEM;
+ }
+
+ ret = ldb_search(ldb, ctx, &objects, base_dn,
+ LDB_SCOPE_SUBTREE, attrs,
+ "(|("SYSDB_OBJECTCLASS"="SYSDB_USER_CLASS")"
+ "("SYSDB_OBJECTCLASS"="SYSDB_GROUP_CLASS"))");
+ talloc_free(base_dn);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to search objects: %d\n", ret);
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ if (objects == NULL || objects->count == 0) {
+ DEBUG(SSSDBG_TRACE_LIBS, "No objects found, nothing to do.");
+ ret = EOK;
+ goto done;
+ }
+
+ del_msg = ldb_msg_new(ctx);
+ if (del_msg == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = ldb_msg_add_empty(del_msg, SYSDB_OBJECTCLASS, LDB_FLAG_MOD_DELETE,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_empty failed.\n");
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "Found [%d] objects.\n", objects->count);
+ for (c = 0; c < objects->count; c++) {
+ DEBUG(SSSDBG_TRACE_ALL, "Updating [%s].\n",
+ ldb_dn_get_linearized(objects->msgs[c]->dn));
+
+ class_name = ldb_msg_find_attr_as_string(objects->msgs[c],
+ SYSDB_OBJECTCLASS, NULL);
+ if (class_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Searched objects by objectClass, "
+ "but result does not have one.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ talloc_free(msg);
+ msg = ldb_msg_new(ctx);
+ if (msg == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = objects->msgs[c]->dn;
+ del_msg->dn = objects->msgs[c]->dn;
+
+ ret = ldb_msg_add_empty(msg, SYSDB_OBJECTCATEGORY, LDB_FLAG_MOD_ADD,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_empty failed.\n");
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, SYSDB_OBJECTCATEGORY, class_name);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_string failed.\n");
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "Adding [%s] to [%s].\n", class_name,
+ ldb_dn_get_linearized(objects->msgs[c]->dn));
+ ret = ldb_modify(ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to add objectCategory to %s: %d.\n",
+ ldb_dn_get_linearized(objects->msgs[c]->dn),
+ sysdb_error_to_errno(ret));
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_modify(ldb, del_msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to remove objectClass from %s: %d.\n",
+ ldb_dn_get_linearized(objects->msgs[c]->dn),
+ sysdb_error_to_errno(ret));
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(msg);
+ talloc_free(del_msg);
+ talloc_free(objects);
+
+ return ret;
+}
+
+int sysdb_upgrade_19(struct sysdb_ctx *sysdb, const char **ver)
+{
+ struct upgrade_ctx *ctx;
+ errno_t ret;
+ struct ldb_message *msg = NULL;
+
+ ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_20, &ctx);
+ if (ret) {
+ return ret;
+ }
+
+ ret = add_object_category(sysdb->ldb, ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "add_object_category failed: %d\n", ret);
+ goto done;
+ }
+
+ /* Remove @IDXONE from index */
+ msg = ldb_msg_new(ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = ldb_dn_new(msg, sysdb->ldb, "@INDEXLIST");
+ if (msg->dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_msg_add_empty(msg, "@IDXONE", LDB_FLAG_MOD_DELETE, NULL);
+ if (ret != LDB_SUCCESS) {
+ 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", SYSDB_USER_MAPPED_CERT);
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_modify(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ /* conversion done, update version number */
+ ret = update_version(ctx);
+
+done:
+ ret = finish_upgrade(ret, &ctx, ver);
+ return ret;
+}
+
+int sysdb_upgrade_20(struct sysdb_ctx *sysdb, const char **ver)
+{
+ struct upgrade_ctx *ctx;
+ errno_t ret;
+ struct ldb_message *msg = NULL;
+
+ ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_21, &ctx);
+ if (ret) {
+ return ret;
+ }
+
+ /* Add missing indices */
+ msg = ldb_msg_new(ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = ldb_dn_new(msg, sysdb->ldb, "@INDEXLIST");
+ if (msg->dn == NULL) {
+ 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", SYSDB_CCACHE_FILE);
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_modify(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ talloc_free(msg);
+
+ /* conversion done, update version number */
+ ret = update_version(ctx);
+
+done:
+ ret = finish_upgrade(ret, &ctx, ver);
+ return ret;
+}
+
+int sysdb_upgrade_21(struct sysdb_ctx *sysdb, const char **ver)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ struct ldb_message *msg;
+ struct upgrade_ctx *ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_22, &ctx);
+ if (ret) {
+ return ret;
+ }
+
+ /* Case insensitive search for ipHostNumber and ipNetworkNumber */
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = ldb_dn_new(tmp_ctx, sysdb->ldb, "@ATTRIBUTES");
+ if (msg->dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_msg_add_empty(msg, SYSDB_IP_HOST_ATTR_ADDRESS, LDB_FLAG_MOD_ADD, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, SYSDB_IP_HOST_ATTR_ADDRESS, "CASE_INSENSITIVE");
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_msg_add_empty(msg, SYSDB_IP_NETWORK_ATTR_NUMBER,
+ LDB_FLAG_MOD_ADD, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, SYSDB_IP_NETWORK_ATTR_NUMBER,
+ "CASE_INSENSITIVE");
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_modify(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ talloc_zfree(msg);
+
+ /* Add new indexes */
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = ldb_dn_new(tmp_ctx, sysdb->ldb, "@INDEXLIST");
+ if (msg->dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Add Index for ipHostNumber */
+ 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", SYSDB_IP_HOST_ATTR_ADDRESS);
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, "@IDXATTR", SYSDB_IP_NETWORK_ATTR_NUMBER);
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_modify(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ /* conversion done, update version number */
+ ret = update_version(ctx);
+
+done:
+ ret = finish_upgrade(ret, &ctx, ver);
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_upgrade_22(struct sysdb_ctx *sysdb, const char **ver)
+{
+ struct upgrade_ctx *ctx;
+ errno_t ret;
+ struct ldb_message *msg = NULL;
+
+ ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_23, &ctx);
+ if (ret) {
+ return ret;
+ }
+
+ /* Add missing indices */
+ msg = ldb_msg_new(ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = ldb_dn_new(msg, sysdb->ldb, "@INDEXLIST");
+ if (msg->dn == NULL) {
+ 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", SYSDB_ORIG_AD_GID_NUMBER);
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_modify(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ talloc_free(msg);
+
+ /* conversion done, update version number */
+ ret = update_version(ctx);
+
+done:
+ ret = finish_upgrade(ret, &ctx, ver);
+ return ret;
+}
+
+int sysdb_ts_upgrade_01(struct sysdb_ctx *sysdb, const char **ver)
+{
+ struct upgrade_ctx *ctx;
+ errno_t ret;
+ struct ldb_message *msg = NULL;
+
+ ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_TS_VERSION_0_2, &ctx);
+ if (ret) {
+ return ret;
+ }
+
+ /* Remove @IDXONE from index */
+ talloc_free(msg);
+ msg = ldb_msg_new(ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = ldb_dn_new(msg, sysdb->ldb, "@INDEXLIST");
+ if (msg->dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_msg_add_empty(msg, "@IDXONE", LDB_FLAG_MOD_DELETE, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_modify(sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ /* conversion done, update version number */
+ ret = update_version(ctx);
+
+done:
+ ret = finish_upgrade(ret, &ctx, ver);
+ return ret;
+}
+
+/*
+ * Example template for future upgrades.
+ * Copy and change version numbers as appropriate.
+ */
+#if 0
+
+int sysdb_upgrade_13(struct sysdb_ctx *sysdb, const char **ver)
+{
+ struct upgrade_ctx *ctx;
+ errno_t ret;
+
+ ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_14, &ctx);
+ if (ret) {
+ return ret;
+ }
+
+ /* DO STUFF HERE (use ctx, as the local temporary memory context) */
+
+ /* conversion done, update version number */
+ ret = update_version(ctx);
+
+done:
+ ret = finish_upgrade(ret, &ctx, ver);
+ return ret;
+}
+#endif
diff --git a/src/db/sysdb_views.c b/src/db/sysdb_views.c
new file mode 100644
index 0000000..19c1097
--- /dev/null
+++ b/src/db/sysdb_views.c
@@ -0,0 +1,1845 @@
+/*
+ SSSD
+
+ System Database - View and Override related calls
+
+ Copyright (C) 2014 Sumit Bose <sbose@redhat.com>
+
+ 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 "util/cert.h"
+#include "db/sysdb_private.h"
+#include "db/sysdb_domain_resolution_order.h"
+
+#define SYSDB_VIEWS_BASE "cn=views,cn=sysdb"
+
+/* In general is should not be possible that there is a view container without
+ * a view name set. But to be on the safe side we return both information
+ * separately. */
+static errno_t sysdb_get_view_name_ex(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ char **_view_name,
+ bool *view_container_exists)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ const char *tmp_str;
+ struct ldb_dn *view_base_dn;
+ struct ldb_result *res;
+ const char *attrs[] = {SYSDB_VIEW_NAME,
+ NULL};
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ view_base_dn = ldb_dn_new(tmp_ctx, sysdb->ldb, SYSDB_TMPL_VIEW_BASE);
+ if (view_base_dn == NULL) {
+ ret = EIO;
+ goto done;
+ }
+ ret = ldb_search(sysdb->ldb, tmp_ctx, &res, view_base_dn, LDB_SCOPE_BASE,
+ attrs, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = EIO;
+ goto done;
+ }
+
+ if (res->count > 1) {
+ DEBUG(SSSDBG_OP_FAILURE, "Base search returned [%d] results, "
+ "expected 1.\n", res->count);
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (res->count == 0) {
+ *view_container_exists = false;
+ ret = ENOENT;
+ goto done;
+ } else {
+ *view_container_exists = true;
+ tmp_str = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_VIEW_NAME,
+ NULL);
+ if (tmp_str == NULL) {
+ ret = ENOENT;
+ goto done;
+ }
+ }
+
+ *_view_name = talloc_steal(mem_ctx, discard_const(tmp_str));
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_get_view_name(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
+ char **view_name)
+{
+ bool view_container_exists;
+
+ return sysdb_get_view_name_ex(mem_ctx, sysdb, view_name,
+ &view_container_exists);
+}
+
+errno_t sysdb_update_view_name(struct sysdb_ctx *sysdb,
+ const char *view_name)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ char *tmp_str;
+ bool view_container_exists = false;
+ bool add_view_name = false;
+ struct ldb_message *msg;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_get_view_name_ex(tmp_ctx, sysdb, &tmp_str,
+ &view_container_exists);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_view_name_ex failed.\n");
+ goto done;
+ }
+
+ if (ret == EOK) {
+ if (strcmp(tmp_str, view_name) == 0) {
+ /* view name already known, nothing to do */
+ DEBUG(SSSDBG_TRACE_ALL, "View name already in place.\n");
+ ret = EOK;
+ goto done;
+ } else {
+ /* view name changed */
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "View name changed from [%s] to [%s].\n", tmp_str, view_name);
+ }
+ } else {
+ add_view_name = true;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = ldb_dn_new(tmp_ctx, sysdb->ldb, SYSDB_TMPL_VIEW_BASE);
+ if (msg->dn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = ldb_msg_add_empty(msg, SYSDB_VIEW_NAME,
+ add_view_name ? LDB_FLAG_MOD_ADD
+ : LDB_FLAG_MOD_REPLACE,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, SYSDB_VIEW_NAME, view_name);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ if (view_container_exists) {
+ ret = ldb_modify(sysdb->ldb, msg);
+ } else {
+ ret = ldb_add(sysdb->ldb, msg);
+ }
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to %s view container [%s](%d)[%s]\n",
+ view_container_exists ? "modify" : "add",
+ ldb_strerror(ret), ret, ldb_errstring(sysdb->ldb));
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_get_view_domain_resolution_order(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ const char **_domain_resolution_order)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *dn;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ dn = ldb_dn_new(tmp_ctx, sysdb->ldb, SYSDB_VIEWS_BASE);
+ if (dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_get_domain_resolution_order(mem_ctx, sysdb, dn,
+ _domain_resolution_order);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sysdb_update_view_domain_resolution_order(struct sysdb_ctx *sysdb,
+ const char *domain_resolution_order)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *dn;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ dn = ldb_dn_new(tmp_ctx, sysdb->ldb, SYSDB_VIEWS_BASE);
+ if (dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_update_domain_resolution_order(sysdb, dn,
+ domain_resolution_order);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_update_domain_resolution_order() failed [%d]: [%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_delete_view_tree(struct sysdb_ctx *sysdb, const char *view_name)
+{
+ struct ldb_dn *dn;
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_TMPL_VIEW_SEARCH_BASE,
+ view_name);
+ if (dn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new_fmt failed.\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = sysdb_delete_recursive(sysdb, dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_delete_recursive failed.\n");
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t invalidate_entry_override(struct sysdb_ctx *sysdb,
+ struct ldb_dn *dn,
+ struct ldb_message *msg_del,
+ struct ldb_message *msg_repl)
+{
+ int ret;
+
+ msg_del->dn = dn;
+ msg_repl->dn = dn;
+
+ ret = ldb_modify(sysdb->ldb, msg_del);
+ if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_ATTRIBUTE) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "ldb_modify failed: [%s](%d)[%s]\n",
+ ldb_strerror(ret), ret, ldb_errstring(sysdb->ldb));
+ return sysdb_error_to_errno(ret);
+ }
+
+ ret = ldb_modify(sysdb->ldb, msg_repl);
+ if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_ATTRIBUTE) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "ldb_modify failed: [%s](%d)[%s]\n",
+ ldb_strerror(ret), ret, ldb_errstring(sysdb->ldb));
+ return sysdb_error_to_errno(ret);
+ }
+
+ if (sysdb->ldb_ts != NULL) {
+ ret = ldb_modify(sysdb->ldb_ts, msg_repl);
+ if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_ATTRIBUTE) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "ldb_modify failed: [%s](%d)[%s]\n",
+ ldb_strerror(ret), ret, ldb_errstring(sysdb->ldb_ts));
+ return sysdb_error_to_errno(ret);
+ }
+ }
+
+ return EOK;
+}
+
+errno_t sysdb_invalidate_overrides(struct sysdb_ctx *sysdb)
+{
+ int ret;
+ int sret;
+ TALLOC_CTX *tmp_ctx;
+ bool in_transaction = false;
+ struct ldb_result *res;
+ size_t c;
+ struct ldb_message *msg_del;
+ struct ldb_message *msg_repl;
+ struct ldb_dn *base_dn;
+
+ if (sysdb->ldb_ts == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Timestamp cache context not available, cache might not be "
+ "invalidated completely. Please call 'sss_cache -E' or remove "
+ "the cache file if there are issues after a view name change.\n");
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ base_dn = ldb_dn_new(tmp_ctx, sysdb->ldb, SYSDB_BASE);
+ if (base_dn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg_del = ldb_msg_new(tmp_ctx);
+ if (msg_del == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = ldb_msg_add_empty(msg_del, SYSDB_OVERRIDE_DN, LDB_FLAG_MOD_DELETE,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_empty failed.\n");
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ msg_repl = ldb_msg_new(tmp_ctx);
+ if (msg_repl == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = ldb_msg_add_empty(msg_repl, SYSDB_CACHE_EXPIRE,
+ LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_empty failed.\n");
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ ret = ldb_msg_add_string(msg_repl, SYSDB_CACHE_EXPIRE, "1");
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_string failed.\n");
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = sysdb_transaction_start(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_transaction_start failed.\n");
+ goto done;
+ }
+ in_transaction = true;
+
+ ret = ldb_search(sysdb->ldb, tmp_ctx, &res, base_dn, LDB_SCOPE_SUBTREE,
+ NULL, "%s", SYSDB_UC);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_entry failed.\n");
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ for (c = 0; c < res->count; c++) {
+ ret = invalidate_entry_override(sysdb, res->msgs[c]->dn, msg_del,
+ msg_repl);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "invalidate_entry_override failed [%d][%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+ }
+
+ talloc_free(res);
+
+ ret = ldb_search(sysdb->ldb, tmp_ctx, &res, base_dn, LDB_SCOPE_SUBTREE,
+ NULL, "%s", SYSDB_GC);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_entry failed.\n");
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ for (c = 0; c < res->count; c++) {
+ ret = invalidate_entry_override(sysdb, res->msgs[c]->dn, msg_del,
+ msg_repl);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "invalidate_entry_override failed [%d][%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ if (in_transaction) {
+ if (ret == EOK) {
+ sret = sysdb_transaction_commit(sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_transaction_commit failed, " \
+ "nothing we can do about.\n");
+ ret = sret;
+ }
+ } else {
+ sret = sysdb_transaction_cancel(sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_transaction_cancel failed, " \
+ "nothing we can do about.\n");
+ }
+ }
+ }
+
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+add_name_and_aliases_for_name_override(struct sss_domain_info *domain,
+ struct sysdb_attrs *attrs,
+ bool add_name,
+ const char *name_override)
+{
+ int ret;
+
+ if (add_name) {
+ ret = sysdb_attrs_add_string(attrs, SYSDB_DEFAULT_OVERRIDE_NAME,
+ name_override);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_lc_name_alias failed.\n");
+ return ret;
+ }
+ }
+
+ if (!domain->case_sensitive) {
+ ret = sysdb_attrs_add_lc_name_alias(attrs, name_override);
+ } else {
+ ret = sysdb_attrs_add_string(attrs, SYSDB_NAME_ALIAS, name_override);
+ }
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_lc_name_alias failed.\n");
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t sysdb_store_override(struct sss_domain_info *domain,
+ const char *view_name,
+ enum sysdb_member_type type,
+ struct sysdb_attrs *attrs, struct ldb_dn *obj_dn)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *anchor;
+ int ret;
+ struct ldb_dn *override_dn;
+ const char *override_dn_str;
+ const char *obj_dn_str;
+ const char *obj_attrs[] = { SYSDB_OBJECTCLASS,
+ SYSDB_OVERRIDE_DN,
+ NULL};
+ size_t count = 0;
+ struct ldb_message **msgs;
+ struct ldb_message *msg = NULL;
+ const char *obj_override_dn;
+ bool add_ref = true;
+ size_t c;
+ bool in_transaction = false;
+ bool has_override = true;
+ const char *name_override;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (attrs != NULL) {
+ has_override = true;
+ ret = sysdb_attrs_get_string(attrs, SYSDB_OVERRIDE_ANCHOR_UUID,
+ &anchor);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Missing anchor in override attributes.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ override_dn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb,
+ SYSDB_TMPL_OVERRIDE, anchor, view_name);
+ if (override_dn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new_fmt failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ } else {
+ /* if there is no override for the given object, just store the DN of
+ * the object iself in the SYSDB_OVERRIDE_DN attribute to indicate
+ * that it was checked if an override exists and none was found. */
+ has_override = false;
+ override_dn = obj_dn;
+ }
+
+ override_dn_str = ldb_dn_get_linearized(override_dn);
+ obj_dn_str = ldb_dn_get_linearized(obj_dn);
+ if (override_dn_str == NULL || obj_dn_str == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_get_linearized failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_entry(tmp_ctx, domain->sysdb, obj_dn, LDB_SCOPE_BASE,
+ NULL, obj_attrs, &count, &msgs);
+ if (ret != EOK) {
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Object to override does not exists.\n");
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_search_entry failed.\n");
+ }
+ goto done;
+ }
+ if (count != 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Base search returned more than one object.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ obj_override_dn = ldb_msg_find_attr_as_string(msgs[0], SYSDB_OVERRIDE_DN,
+ NULL);
+ if (obj_override_dn != NULL) {
+ /* obj_override_dn can either point to the object itself, i.e there is
+ * no override, or to a override object. This means it can change from
+ * the object DN to a override DN and back but not from one override
+ * DN to a different override DN. If the new and the old DN are the
+ * same we do not need to update the original object. */
+ if (strcmp(obj_override_dn, override_dn_str) != 0) {
+ if (strcmp(obj_override_dn, obj_dn_str) != 0
+ && strcmp(override_dn_str, obj_dn_str) != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Existing [%s] and new [%s] override DN do not match.\n",
+ obj_override_dn, override_dn_str);
+ ret = EINVAL;
+ goto done;
+ }
+ } else {
+ add_ref = false;
+ }
+ }
+
+ ret = ldb_transaction_start(domain->sysdb->ldb);
+ if (ret != EOK) {
+ return sysdb_error_to_errno(ret);
+ }
+ in_transaction = true;
+
+ if (has_override) {
+ ret = ldb_delete(domain->sysdb->ldb, override_dn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_ALL,
+ "ldb_delete failed, maybe object did not exist. Ignoring.\n");
+ }
+
+ ret = sysdb_attrs_get_string(attrs, SYSDB_NAME, &name_override);
+ if (ret == EOK) {
+ ret = add_name_and_aliases_for_name_override(domain, attrs, false,
+ name_override);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "add_name_and_aliases_for_name_override failed.\n");
+ goto done;
+ }
+ } else if (ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
+ goto done;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = override_dn;
+
+ msg->elements = talloc_array(msg, struct ldb_message_element,
+ attrs->num);
+ if (msg->elements == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (c = 0; c < attrs->num; c++) {
+ /* Set num_values to 1 because by default user and group overrides
+ * use the same attribute name for the GID and this cause SSSD
+ * machinery to add the same value twice */
+ if (attrs->a[c].num_values > 1
+ && strcmp(attrs->a[c].name, SYSDB_GIDNUM) == 0) {
+ attrs->a[c].num_values = 1;
+ }
+ msg->elements[c] = attrs->a[c];
+ msg->elements[c].flags = LDB_FLAG_MOD_ADD;
+ }
+ msg->num_elements = attrs->num;
+
+ ret = ldb_msg_add_empty(msg, SYSDB_OBJECTCLASS, LDB_FLAG_MOD_ADD, NULL);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_empty failed.\n");
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ switch(type) {
+ case SYSDB_MEMBER_USER:
+ ret = ldb_msg_add_string(msg, SYSDB_OBJECTCLASS,
+ SYSDB_OVERRIDE_USER_CLASS);
+ break;
+ case SYSDB_MEMBER_GROUP:
+ ret = ldb_msg_add_string(msg, SYSDB_OBJECTCLASS,
+ SYSDB_OVERRIDE_GROUP_CLASS);
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected object type %d.\n", type);
+ ret = EINVAL;
+ goto done;
+ }
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_msg_add_empty(msg, SYSDB_OVERRIDE_OBJECT_DN, LDB_FLAG_MOD_ADD,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_empty failed.\n");
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, SYSDB_OVERRIDE_OBJECT_DN, obj_dn_str);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_add(domain->sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to store override entry: %s(%d)[%s]\n",
+ ldb_strerror(ret), ret, ldb_errstring(domain->sysdb->ldb));
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+
+ if (add_ref) {
+ talloc_free(msg);
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = obj_dn;
+
+ ret = ldb_msg_add_empty(msg, SYSDB_OVERRIDE_DN,
+ obj_override_dn == NULL ? LDB_FLAG_MOD_ADD
+ : LDB_FLAG_MOD_REPLACE,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_empty failed.\n");
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(msg, SYSDB_OVERRIDE_DN, override_dn_str);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_modify(domain->sysdb->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to store override DN: %s(%d)[%s]\n",
+ ldb_strerror(ret), ret, ldb_errstring(domain->sysdb->ldb));
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ if (in_transaction) {
+ if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, strerror(ret));
+ ldb_transaction_cancel(domain->sysdb->ldb);
+ } else {
+ ret = ldb_transaction_commit(domain->sysdb->ldb);
+ ret = sysdb_error_to_errno(ret);
+ }
+ }
+
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static errno_t safe_original_attributes(struct sss_domain_info *domain,
+ struct sysdb_attrs *attrs,
+ struct ldb_dn *obj_dn,
+ const char **allowed_attrs)
+{
+ int ret;
+ size_t c;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *orig_obj;
+ char *orig_attr_name;
+ struct ldb_message_element *el = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &orig_obj, obj_dn,
+ LDB_SCOPE_BASE, NULL, NULL);
+ if (ret != EOK || orig_obj->count != 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Original object not found.\n");
+ goto done;
+ }
+
+ /* Safe original values in attributes prefixed by OriginalAD. */
+ for (c = 0; allowed_attrs[c] != NULL; c++) {
+ el = ldb_msg_find_element(orig_obj->msgs[0], allowed_attrs[c]);
+ if (el != NULL) {
+ orig_attr_name = talloc_asprintf(tmp_ctx, "%s%s",
+ ORIGINALAD_PREFIX,
+ allowed_attrs[c]);
+ if (orig_attr_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_val(attrs, orig_attr_name,
+ &el->values[0]);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_attrs_add_val failed.\n");
+ goto done;
+ }
+ } else {
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Original object does not have [%s] set.\n",
+ allowed_attrs[c]);
+ }
+ }
+
+ /* Add existing aliases to new ones */
+ el = ldb_msg_find_element(orig_obj->msgs[0], SYSDB_NAME_ALIAS);
+ if (el != NULL) {
+ for (c = 0; c < el->num_values; c++) {
+ /* To avoid issue with ldb_modify if e.g. the original and the
+ * override name are the same, we use the *_safe version here. */
+ ret = sysdb_attrs_add_val_safe(attrs, SYSDB_NAME_ALIAS,
+ &el->values[c]);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_val failed.\n");
+ goto done;
+ }
+ }
+ }
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_apply_default_override(struct sss_domain_info *domain,
+ struct sysdb_attrs *override_attrs,
+ struct ldb_dn *obj_dn)
+{
+ int ret;
+ TALLOC_CTX *tmp_ctx;
+ struct sysdb_attrs *attrs;
+ struct sysdb_attrs *mapped_attrs = NULL;
+ size_t c;
+ size_t d;
+ size_t num_values;
+ struct ldb_message_element *el = NULL;
+ const char *allowed_attrs[] = { SYSDB_UIDNUM,
+ SYSDB_GIDNUM,
+ SYSDB_GECOS,
+ SYSDB_HOMEDIR,
+ SYSDB_SHELL,
+ SYSDB_NAME,
+ SYSDB_SSH_PUBKEY,
+ SYSDB_USER_CERT,
+ NULL };
+ bool override_attrs_found = false;
+ bool is_cert = false;
+ struct ldb_message_element el_del = { 0, SYSDB_SSH_PUBKEY, 0, NULL };
+ struct sysdb_attrs del_attrs = { 1, &el_del };
+
+ if (override_attrs == NULL) {
+ /* nothing to do */
+ return EOK;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ attrs = sysdb_new_attrs(tmp_ctx);
+ if (attrs == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (c = 0; allowed_attrs[c] != NULL; c++) {
+ ret = sysdb_attrs_get_el_ext(override_attrs, allowed_attrs[c], false,
+ &el);
+ if (ret == EOK) {
+ override_attrs_found = true;
+
+ if (strcmp(allowed_attrs[c], SYSDB_NAME) == 0) {
+ if (el->values[0].data[el->values[0].length] != '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "String attribute does not end with \\0.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = add_name_and_aliases_for_name_override(domain, attrs,
+ true,
+ (char *) el->values[0].data);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "add_name_and_aliases_for_name_override failed.\n");
+ goto done;
+ }
+ } else {
+ num_values = el->num_values;
+ /* Only SYSDB_SSH_PUBKEY and SYSDB_USER_CERT are allowed to
+ * have multiple values. */
+ if (strcmp(allowed_attrs[c], SYSDB_SSH_PUBKEY) != 0
+ && strcmp(allowed_attrs[c], SYSDB_USER_CERT) != 0
+ && num_values != 1) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Override attribute for [%s] has more [%zd] " \
+ "than one value, using only the first.\n",
+ allowed_attrs[c], num_values);
+ num_values = 1;
+ }
+
+ is_cert = false;
+ if (strcmp(allowed_attrs[c], SYSDB_USER_CERT) == 0) {
+ /* Certificates in overrides are explicitly used to map
+ * users to certificates, so we add them to
+ * SYSDB_USER_MAPPED_CERT as well. */
+ is_cert = true;
+
+ if (mapped_attrs == NULL) {
+ mapped_attrs = sysdb_new_attrs(tmp_ctx);
+ if (mapped_attrs == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_new_attrs failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ }
+
+ for (d = 0; d < num_values; d++) {
+ ret = sysdb_attrs_add_val(attrs, allowed_attrs[c],
+ &el->values[d]);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_attrs_add_val failed.\n");
+ goto done;
+ }
+
+ if (is_cert) {
+ ret = sysdb_attrs_add_val(mapped_attrs,
+ SYSDB_USER_MAPPED_CERT,
+ &el->values[d]);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_attrs_add_val failed.\n");
+ goto done;
+ }
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Override [%s] with [%.*s] for [%s].\n",
+ allowed_attrs[c], (int) el->values[d].length,
+ el->values[d].data, ldb_dn_get_linearized(obj_dn));
+ }
+ }
+ } else if (ret == ENOENT) {
+ if (strcmp(allowed_attrs[c], SYSDB_SSH_PUBKEY) == 0) {
+ ret = sysdb_set_entry_attr(domain->sysdb, obj_dn, &del_attrs,
+ SYSDB_MOD_DEL);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_set_entry_attr failed.\n");
+ goto done;
+ }
+ }
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_el_ext failed.\n");
+ goto done;
+ }
+ }
+
+ if (override_attrs_found) {
+ ret = safe_original_attributes(domain, attrs, obj_dn, allowed_attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "safe_original_attributes failed.\n");
+ goto done;
+ }
+
+ ret = sysdb_set_entry_attr(domain->sysdb, obj_dn, attrs, SYSDB_MOD_REP);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_set_entry_attr failed.\n");
+ goto done;
+ }
+
+ if (mapped_attrs != NULL) {
+ ret = sysdb_set_entry_attr(domain->sysdb, obj_dn, mapped_attrs,
+ SYSDB_MOD_ADD);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_set_entry_attr failed, ignored.\n");
+ }
+ }
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+
+#define SYSDB_USER_NAME_OVERRIDE_FILTER "(&(objectClass="SYSDB_OVERRIDE_USER_CLASS")(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
+#define SYSDB_USER_UID_OVERRIDE_FILTER "(&(objectClass="SYSDB_OVERRIDE_USER_CLASS")("SYSDB_UIDNUM"=%lu))"
+#define SYSDB_USER_CERT_OVERRIDE_FILTER "(&(objectClass="SYSDB_OVERRIDE_USER_CLASS")%s)"
+#define SYSDB_GROUP_NAME_OVERRIDE_FILTER "(&(objectClass="SYSDB_OVERRIDE_GROUP_CLASS")(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
+#define SYSDB_GROUP_GID_OVERRIDE_FILTER "(&(objectClass="SYSDB_OVERRIDE_GROUP_CLASS")("SYSDB_GIDNUM"=%lu))"
+
+enum override_object_type {
+ OO_TYPE_UNDEF = 0,
+ OO_TYPE_USER,
+ OO_TYPE_GROUP
+};
+
+errno_t sysdb_search_override_by_cert(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *cert,
+ const char **attrs,
+ struct ldb_result **override_obj,
+ struct ldb_result **orig_obj)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *base_dn;
+ struct ldb_result *override_res;
+ struct ldb_result *orig_res;
+ char *cert_filter;
+ int ret;
+ const char *orig_obj_dn;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ base_dn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb,
+ SYSDB_TMPL_VIEW_SEARCH_BASE, domain->view_name);
+ if (base_dn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new_fmt failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_cert_derb64_to_ldap_filter(tmp_ctx, cert, SYSDB_USER_CERT,
+ &cert_filter);
+
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_cert_derb64_to_ldap_filter failed.\n");
+ goto done;
+ }
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &override_res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, SYSDB_USER_CERT_OVERRIDE_FILTER,
+ cert_filter);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ if (override_res->count == 0) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No user override found for cert [%s].\n",
+ cert);
+ ret = ENOENT;
+ goto done;
+ } else if (override_res->count > 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Found more than one override for cert [%s].\n", cert);
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (orig_obj != NULL) {
+ orig_obj_dn = ldb_msg_find_attr_as_string(override_res->msgs[0],
+ SYSDB_OVERRIDE_OBJECT_DN,
+ NULL);
+ if (orig_obj_dn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Missing link to original object in override [%s].\n",
+ ldb_dn_get_linearized(override_res->msgs[0]->dn));
+ ret = EINVAL;
+ goto done;
+ }
+
+ base_dn = ldb_dn_new(tmp_ctx, domain->sysdb->ldb, orig_obj_dn);
+ if (base_dn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &orig_res, base_dn,
+ LDB_SCOPE_BASE, attrs, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ *orig_obj = talloc_steal(mem_ctx, orig_res);
+ }
+
+ *override_obj = talloc_steal(mem_ctx, override_res);
+
+ ret = EOK;
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static errno_t sysdb_search_override_by_name(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char *filter,
+ const char **attrs,
+ struct ldb_result **override_obj,
+ struct ldb_result **orig_obj)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *base_dn;
+ struct ldb_result *override_res;
+ struct ldb_result *orig_res;
+ char *sanitized_name;
+ char *lc_sanitized_name;
+ int ret;
+ const char *orig_obj_dn;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ base_dn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb,
+ SYSDB_TMPL_VIEW_SEARCH_BASE, domain->view_name);
+ if (base_dn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new_fmt failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_filter_sanitize_for_dom(tmp_ctx, name, domain,
+ &sanitized_name, &lc_sanitized_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_filter_sanitize_for_dom failed.\n");
+ goto done;
+ }
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &override_res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, filter,
+ lc_sanitized_name,
+ sanitized_name, sanitized_name);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ if (override_res->count == 0) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No user override found for name [%s].\n",
+ name);
+ ret = ENOENT;
+ goto done;
+ } else if (override_res->count > 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Found more than one override for name [%s].\n", name);
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (orig_obj != NULL) {
+ orig_obj_dn = ldb_msg_find_attr_as_string(override_res->msgs[0],
+ SYSDB_OVERRIDE_OBJECT_DN,
+ NULL);
+ if (orig_obj_dn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Missing link to original object in override [%s].\n",
+ ldb_dn_get_linearized(override_res->msgs[0]->dn));
+ ret = EINVAL;
+ goto done;
+ }
+
+ base_dn = ldb_dn_new(tmp_ctx, domain->sysdb->ldb, orig_obj_dn);
+ if (base_dn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &orig_res, base_dn,
+ LDB_SCOPE_BASE, attrs, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ *orig_obj = talloc_steal(mem_ctx, orig_res);
+ }
+
+
+ *override_obj = talloc_steal(mem_ctx, override_res);
+
+ ret = EOK;
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_search_user_override_attrs_by_name(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char **attrs,
+ struct ldb_result **override_obj,
+ struct ldb_result **orig_obj)
+{
+
+ return sysdb_search_override_by_name(mem_ctx, domain, name,
+ SYSDB_USER_NAME_OVERRIDE_FILTER,
+ attrs, override_obj, orig_obj);
+}
+
+errno_t sysdb_search_group_override_attrs_by_name(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char **attrs,
+ struct ldb_result **override_obj,
+ struct ldb_result **orig_obj)
+{
+ return sysdb_search_override_by_name(mem_ctx, domain, name,
+ SYSDB_GROUP_NAME_OVERRIDE_FILTER,
+ attrs, override_obj, orig_obj);
+}
+
+errno_t sysdb_search_user_override_by_name(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **override_obj,
+ struct ldb_result **orig_obj)
+{
+ const char *attrs[] = SYSDB_PW_ATTRS;
+
+ return sysdb_search_override_by_name(mem_ctx, domain, name,
+ SYSDB_USER_NAME_OVERRIDE_FILTER,
+ attrs, override_obj, orig_obj);
+}
+
+errno_t sysdb_search_group_override_by_name(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **override_obj,
+ struct ldb_result **orig_obj)
+{
+ const char *attrs[] = SYSDB_GRSRC_ATTRS;
+
+ return sysdb_search_override_by_name(mem_ctx, domain, name,
+ SYSDB_GROUP_NAME_OVERRIDE_FILTER,
+ attrs, override_obj, orig_obj);
+}
+
+static errno_t sysdb_search_override_by_id(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ unsigned long int id,
+ enum override_object_type type,
+ struct ldb_result **override_obj,
+ struct ldb_result **orig_obj)
+{
+ TALLOC_CTX *tmp_ctx;
+ static const char *user_attrs[] = SYSDB_PW_ATTRS;
+ static const char *group_attrs[] = SYSDB_GRSRC_ATTRS;
+ const char **attrs;
+ struct ldb_dn *base_dn;
+ struct ldb_result *override_res;
+ struct ldb_result *orig_res;
+ int ret;
+ const char *orig_obj_dn;
+ const char *filter;
+ const struct ldb_val *orig_domain;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ base_dn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb,
+ SYSDB_TMPL_VIEW_SEARCH_BASE, domain->view_name);
+ if (base_dn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new_fmt failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ switch(type) {
+ case OO_TYPE_USER:
+ filter = SYSDB_USER_UID_OVERRIDE_FILTER;
+ attrs = user_attrs;
+ break;
+ case OO_TYPE_GROUP:
+ filter = SYSDB_GROUP_GID_OVERRIDE_FILTER;
+ attrs = group_attrs;
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected override object type [%d].\n",
+ type);
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &override_res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, filter, id);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ if (override_res->count == 0) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "No user override found for %s with id [%lu].\n",
+ (type == OO_TYPE_USER ? "user" : "group"), id);
+ ret = ENOENT;
+ goto done;
+ } else if (override_res->count > 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Found more than one override for id [%lu].\n", id);
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (orig_obj != NULL) {
+ orig_obj_dn = ldb_msg_find_attr_as_string(override_res->msgs[0],
+ SYSDB_OVERRIDE_OBJECT_DN,
+ NULL);
+ if (orig_obj_dn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Missing link to original object in override [%s].\n",
+ ldb_dn_get_linearized(override_res->msgs[0]->dn));
+ ret = EINVAL;
+ goto done;
+ }
+
+ base_dn = ldb_dn_new(tmp_ctx, domain->sysdb->ldb, orig_obj_dn);
+ if (base_dn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Check if the found override object belongs to an object in this
+ * domain. The base dn is in the form:
+ * name=user@domain,cn=users,cn=domain,cn=sysdb
+ * = 0 = 1 = 2 = 3
+ */
+ orig_domain = ldb_dn_get_component_val(base_dn, 2);
+ if (orig_domain == NULL || !orig_domain->length) {
+ DEBUG(SSSDBG_OP_FAILURE, "Invalid original object DN\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (strcmp((const char*)orig_domain->data, domain->name) != 0) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &orig_res, base_dn,
+ LDB_SCOPE_BASE, attrs, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ *orig_obj = talloc_steal(mem_ctx, orig_res);
+ }
+
+
+ *override_obj = talloc_steal(mem_ctx, override_res);
+
+ ret = EOK;
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_search_user_override_by_uid(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ uid_t uid,
+ struct ldb_result **override_obj,
+ struct ldb_result **orig_obj)
+{
+ return sysdb_search_override_by_id(mem_ctx, domain, uid, OO_TYPE_USER,
+ override_obj, orig_obj);
+}
+
+errno_t sysdb_search_group_override_by_gid(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ gid_t gid,
+ struct ldb_result **override_obj,
+ struct ldb_result **orig_obj)
+{
+ return sysdb_search_override_by_id(mem_ctx, domain, gid, OO_TYPE_GROUP,
+ override_obj, orig_obj);
+}
+
+/**
+ * @brief Add override data to the original object
+ *
+ * @param[in] domain Domain struct, needed to access the cache
+ * @oaram[in] obj The original object
+ * @param[in] override_obj The object with the override data, may be NULL
+ * @param[in] req_attrs List of attributes to be requested, if not set a
+ * default list depending on the object type will be used
+ *
+ * @return EOK - Override data was added successfully
+ * @return ENOMEM - There was insufficient memory to complete the operation
+ * @return ENOENT - The original object did not have the SYSDB_OVERRIDE_DN
+ * attribute or the value of the attribute points an object
+ * which does not exists. Both conditions indicate that the
+ * cache must be refreshed.
+ */
+errno_t sysdb_add_overrides_to_object(struct sss_domain_info *domain,
+ struct ldb_message *obj,
+ struct ldb_message *override_obj,
+ const char **req_attrs)
+{
+ int ret;
+ const char *override_dn_str;
+ struct ldb_dn *override_dn;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ struct ldb_message *override;
+ uint64_t uid;
+ static const char *user_attrs[] = SYSDB_PW_ATTRS;
+ static const char *group_attrs[] = SYSDB_GRSRC_ATTRS;
+ const char **attrs;
+ struct attr_map {
+ const char *attr;
+ const char *new_attr;
+ } attr_map[] = {
+ {SYSDB_UIDNUM, OVERRIDE_PREFIX SYSDB_UIDNUM},
+ {SYSDB_GIDNUM, OVERRIDE_PREFIX SYSDB_GIDNUM},
+ {SYSDB_GECOS, OVERRIDE_PREFIX SYSDB_GECOS},
+ {SYSDB_HOMEDIR, OVERRIDE_PREFIX SYSDB_HOMEDIR},
+ {SYSDB_SHELL, OVERRIDE_PREFIX SYSDB_SHELL},
+ {SYSDB_NAME, OVERRIDE_PREFIX SYSDB_NAME},
+ {SYSDB_SSH_PUBKEY, OVERRIDE_PREFIX SYSDB_SSH_PUBKEY},
+ {SYSDB_USER_CERT, OVERRIDE_PREFIX SYSDB_USER_CERT},
+ {NULL, NULL}
+ };
+ size_t c;
+ size_t d;
+ struct ldb_message_element *tmp_el;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ if (override_obj == NULL) {
+ override_dn_str = ldb_msg_find_attr_as_string(obj,
+ SYSDB_OVERRIDE_DN, NULL);
+ if (override_dn_str == NULL) {
+ if (is_local_view(domain->view_name)) {
+ /* LOCAL view doesn't have to have overrideDN specified. */
+ ret = EOK;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Missing override DN for object [%s].\n",
+ ldb_dn_get_linearized(obj->dn));
+
+ ret = ENOENT;
+ goto done;
+ }
+
+ override_dn = ldb_dn_new(tmp_ctx, domain->sysdb->ldb, override_dn_str);
+ if (override_dn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (ldb_dn_compare(obj->dn, override_dn) == 0) {
+ DEBUG(SSSDBG_TRACE_ALL, "Object [%s] has no overrides.\n",
+ ldb_dn_get_linearized(obj->dn));
+ ret = EOK;
+ goto done;
+ }
+
+ attrs = req_attrs;
+ if (attrs == NULL) {
+ uid = ldb_msg_find_attr_as_uint64(obj, SYSDB_UIDNUM, 0);
+ if (uid == 0) {
+ /* No UID hence group object */
+ attrs = group_attrs;
+ } else {
+ attrs = user_attrs;
+ }
+ }
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, override_dn,
+ LDB_SCOPE_BASE, attrs, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ if (res->count == 1) {
+ override = res->msgs[0];
+ } else if (res->count == 0) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Override object [%s] does not exists.\n",
+ override_dn_str);
+ ret = ENOENT;
+ goto done;
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Base search for override object returned [%d] results.\n",
+ res->count);
+ ret = EINVAL;
+ goto done;
+ }
+ } else {
+ override = override_obj;
+ }
+
+ for (c = 0; attr_map[c].attr != NULL; c++) {
+ tmp_el = ldb_msg_find_element(override, attr_map[c].attr);
+ if (tmp_el != NULL) {
+ for (d = 0; d < tmp_el->num_values; d++) {
+ ret = ldb_msg_add_steal_value(obj, attr_map[c].new_attr,
+ &tmp_el->values[d]);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_value failed.\n");
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+ }
+ }
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t sysdb_add_group_member_overrides(struct sss_domain_info *domain,
+ struct ldb_message *obj,
+ bool expect_override_dn)
+{
+ int ret;
+ size_t c;
+ struct ldb_result *res_members;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *override_obj;
+ static const char *member_attrs[] = SYSDB_PW_ATTRS;
+ const char *override_dn_str;
+ struct ldb_dn *override_dn;
+ const char *memberuid;
+ const char *orig_name;
+ char *orig_domain;
+ char *val;
+ struct sss_domain_info *orig_dom;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_get_user_members_recursively(tmp_ctx, domain, obj->dn,
+ &res_members);
+ if (ret == ENOENT) {
+ ret = EOK;
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_get_user_members_recursively failed.\n");
+ goto done;
+ }
+
+ for (c = 0; c < res_members->count; c++) {
+
+ if (ldb_msg_find_attr_as_uint64(res_members->msgs[c],
+ SYSDB_UIDNUM, 0) == 0) {
+ /* Skip non-POSIX-user members i.e. groups and non-POSIX users */
+ continue;
+ }
+
+ if (expect_override_dn) {
+ override_dn_str = ldb_msg_find_attr_as_string(res_members->msgs[c],
+ SYSDB_OVERRIDE_DN,
+ NULL);
+ } else {
+ override_dn_str = ldb_dn_get_linearized(res_members->msgs[c]->dn);
+ }
+
+ if (override_dn_str == NULL) {
+ if (is_local_view(domain->view_name)) {
+ /* LOCAL view doesn't have to have overrideDN specified. */
+ ret = EOK;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Missing override DN for object [%s].\n",
+ ldb_dn_get_linearized(res_members->msgs[c]->dn));
+ ret = ENOENT;
+ goto done;
+ }
+
+ override_dn = ldb_dn_new(res_members, domain->sysdb->ldb,
+ override_dn_str);
+ if (override_dn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ orig_name = ldb_msg_find_attr_as_string(res_members->msgs[c],
+ SYSDB_NAME,
+ NULL);
+ if (orig_name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Object [%s] has no name.\n",
+ ldb_dn_get_linearized(res_members->msgs[c]->dn));
+ ret = EINVAL;
+ goto done;
+ }
+
+ /* start with default view name, if it exists or use NULL */
+ memberuid = ldb_msg_find_attr_as_string(res_members->msgs[c],
+ SYSDB_DEFAULT_OVERRIDE_NAME,
+ NULL);
+
+ /* If there is an override object, check if the name is overridden */
+ if (ldb_dn_compare(res_members->msgs[c]->dn, override_dn) != 0) {
+ DEBUG(SSSDBG_TRACE_ALL, "Checking override for object [%s].\n",
+ ldb_dn_get_linearized(res_members->msgs[c]->dn));
+
+ ret = ldb_search(domain->sysdb->ldb, res_members, &override_obj,
+ override_dn, LDB_SCOPE_BASE, member_attrs, NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ if (override_obj->count != 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Base search for override object returned [%d] results.\n",
+ override_obj->count);
+ ret = EINVAL;
+ goto done;
+ }
+
+ memberuid = ldb_msg_find_attr_as_string(override_obj->msgs[0],
+ SYSDB_NAME,
+ memberuid);
+ }
+
+ /* add domain name if memberuid is a short name */
+ if (memberuid != NULL && strchr(memberuid, '@') == NULL) {
+ ret = sss_parse_internal_fqname(tmp_ctx, orig_name,
+ NULL, &orig_domain);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_parse_internal_fqname failed to split [%s].\n",
+ orig_name);
+ goto done;
+ }
+
+ if (orig_domain != NULL) {
+ orig_dom = find_domain_by_name(get_domains_head(domain),
+ orig_domain, true);
+ if (orig_dom == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot find domain with name [%s].\n",
+ orig_domain);
+ ret = ERR_DOMAIN_NOT_FOUND;
+ goto done;
+ }
+ memberuid = sss_create_internal_fqname(tmp_ctx, memberuid,
+ orig_dom->name);
+ if (memberuid == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_create_internal_fqname failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ }
+
+ if (memberuid == NULL) {
+ DEBUG(SSSDBG_TRACE_ALL, "No override name available.\n");
+
+ memberuid = orig_name;
+ }
+
+ val = talloc_strdup(obj, memberuid);
+ if (val == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(obj, OVERRIDE_PREFIX SYSDB_MEMBERUID, val);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_string failed.\n");
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_ALL, "Added [%s] to [%s].\n", memberuid,
+ OVERRIDE_PREFIX SYSDB_MEMBERUID);
+
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+struct ldb_message_element *
+sss_view_ldb_msg_find_element(struct sss_domain_info *dom,
+ const struct ldb_message *msg,
+ const char *attr_name)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct ldb_message_element *val;
+ char *override_attr_name;
+
+ if (DOM_HAS_VIEWS(dom)) {
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ val = NULL;
+ goto done;
+ }
+
+ override_attr_name = talloc_asprintf(tmp_ctx, "%s%s", OVERRIDE_PREFIX,
+ attr_name);
+ if (override_attr_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+ val = NULL;
+ goto done;
+ }
+
+ val = ldb_msg_find_element(msg, override_attr_name);
+ if (val != NULL) {
+ goto done;
+ }
+ }
+
+ val = ldb_msg_find_element(msg, attr_name);
+
+done:
+ talloc_free(tmp_ctx);
+ return val;
+}
+
+uint64_t sss_view_ldb_msg_find_attr_as_uint64(struct sss_domain_info *dom,
+ const struct ldb_message *msg,
+ const char *attr_name,
+ uint64_t default_value)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ uint64_t val;
+ char *override_attr_name;
+
+ if (DOM_HAS_VIEWS(dom)) {
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ val = default_value;
+ goto done;
+ }
+
+ override_attr_name = talloc_asprintf(tmp_ctx, "%s%s", OVERRIDE_PREFIX,
+ attr_name);
+ if (override_attr_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+ val = default_value;
+ goto done;
+ }
+
+ if (ldb_msg_find_element(msg, override_attr_name) != NULL) {
+ val = ldb_msg_find_attr_as_uint64(msg, override_attr_name,
+ default_value);
+ goto done;
+ }
+ }
+
+ val = ldb_msg_find_attr_as_uint64(msg, attr_name, default_value);
+
+done:
+ talloc_free(tmp_ctx);
+ return val;
+}
+
+const char *sss_view_ldb_msg_find_attr_as_string_ex(struct sss_domain_info *dom,
+ const struct ldb_message *msg,
+ const char *attr_name,
+ const char *default_value,
+ bool *is_override)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ const char *val;
+ char *override_attr_name;
+
+ if (is_override != NULL) {
+ *is_override = false;
+ }
+
+ if (DOM_HAS_VIEWS(dom)) {
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ val = default_value;
+ goto done;
+ }
+
+ override_attr_name = talloc_asprintf(tmp_ctx, "%s%s", OVERRIDE_PREFIX,
+ attr_name);
+ if (override_attr_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+ val = default_value;
+ goto done;
+ }
+
+ if (ldb_msg_find_element(msg, override_attr_name) != NULL) {
+ val = ldb_msg_find_attr_as_string(msg, override_attr_name,
+ default_value);
+ if (is_override != NULL && val != default_value) {
+ *is_override = true;
+ }
+ goto done;
+ }
+ }
+
+ val = ldb_msg_find_attr_as_string(msg, attr_name, default_value);
+
+done:
+ talloc_free(tmp_ctx);
+ return val;
+}
+
+const char *sss_view_ldb_msg_find_attr_as_string(struct sss_domain_info *dom,
+ const struct ldb_message *msg,
+ const char *attr_name,
+ const char *default_value)
+{
+ return sss_view_ldb_msg_find_attr_as_string_ex(dom, msg, attr_name,
+ default_value, NULL);
+}