diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
commit | 74aa0bc6779af38018a03fd2cf4419fe85917904 (patch) | |
tree | 9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/db | |
parent | Initial commit. (diff) | |
download | sssd-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')
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, ¤t); + 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); +} |