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/confdb | |
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/confdb')
-rw-r--r-- | src/confdb/confdb.c | 2826 | ||||
-rw-r--r-- | src/confdb/confdb.h | 785 | ||||
-rw-r--r-- | src/confdb/confdb_private.h | 35 | ||||
-rw-r--r-- | src/confdb/confdb_setup.c | 444 | ||||
-rw-r--r-- | src/confdb/confdb_setup.h | 55 |
5 files changed, 4145 insertions, 0 deletions
diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c new file mode 100644 index 0000000..a7344e1 --- /dev/null +++ b/src/confdb/confdb.c @@ -0,0 +1,2826 @@ +/* + SSSD + + SSSD Configuration DB + + 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 "config.h" + +#include <ctype.h> +#include "util/util.h" +#include "confdb/confdb.h" +#include "confdb/confdb_private.h" +#include "util/strtonum.h" +#include "db/sysdb.h" + +#define CONFDB_ZERO_CHECK_OR_JUMP(var, ret, err, label) do { \ + if (!var) { \ + ret = err; \ + goto label; \ + } \ +} while(0) + +/* Warning messages */ +#define SAME_DOMAINS_ERROR_MSG "Domain '%s' is the same as or differs only "\ + "in case from domain '%s'.\n" + +#define RETRIEVE_DOMAIN_ERROR_MSG "Error (%d [%s]) retrieving domain [%s], "\ + "skipping!\n" + +#ifdef BUILD_FILES_PROVIDER +/* SSSD domain name that is used for the auto-configured files domain */ +#define IMPLICIT_FILES_DOMAIN_NAME "implicit_files" +#endif + +static int confdb_get_domain_enabled(struct confdb_ctx *cdb, + const char *domain, bool *_enabled); + + +static char *prepend_cn(char *str, int *slen, const char *comp, int clen) +{ + char *ret; + + ret = talloc_realloc(NULL, str, char, *slen + 4 + clen + 1); + if (!ret) + return NULL; + + /* move current string to the end */ + memmove(&ret[clen +4], ret, *slen+1); /* includes termination */ + memcpy(ret, "cn=", 3); + memcpy(&ret[3], comp, clen); + ret[clen+3] = ','; + + *slen = *slen + 4 + clen; + + return ret; +} + +int parse_section(TALLOC_CTX *mem_ctx, const char *section, + char **sec_dn, const char **rdn_name) +{ + TALLOC_CTX *tmp_ctx; + char *dn = NULL; + char *p; + const char *s; + int l, ret; + + /* section must be a non null string and must not start with '/' */ + if (!section || !*section || *section == '/') return EINVAL; + + tmp_ctx = talloc_new(mem_ctx); + if (!tmp_ctx) return ENOMEM; + + s = section; + l = 0; + while ((p = strchrnul(s, '/'))) { + if (l == 0) { + dn = talloc_asprintf(tmp_ctx, "cn=%s", s); + l = 3 + (p-s); + dn[l] = '\0'; + } else { + dn = prepend_cn(dn, &l, s, p-s); + } + if (!dn) { + ret = ENOMEM; + goto done; + } + if (*p == '\0') { + if (rdn_name) *rdn_name = s; + break; /* reached end */ + } + s = p+1; + if (*s == '\0') { /* a section cannot end in '.' */ + ret = EINVAL; + goto done; + } + } + + *sec_dn = talloc_steal(mem_ctx, dn); + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +int confdb_add_param(struct confdb_ctx *cdb, + bool replace, + const char *section, + const char *attribute, + const char **values) +{ + TALLOC_CTX *tmp_ctx = NULL; + struct ldb_message *msg; + struct ldb_result *res; + struct ldb_dn *dn; + char *secdn; + const char *rdn_name; + int ret, i; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + ret = ENOMEM; + goto done; + } + + ret = parse_section(tmp_ctx, section, &secdn, &rdn_name); + if (ret != EOK) { + goto done; + } + + dn = ldb_dn_new(tmp_ctx, cdb->ldb, secdn); + CONFDB_ZERO_CHECK_OR_JUMP(dn, ret, EIO, done); + + ret = ldb_search(cdb->ldb, tmp_ctx, &res, + dn, LDB_SCOPE_BASE, NULL, NULL); + if (ret != LDB_SUCCESS) { + ret = EIO; + goto done; + } + + msg = ldb_msg_new(tmp_ctx); + CONFDB_ZERO_CHECK_OR_JUMP(msg, ret, ENOMEM, done); + + msg->dn = talloc_steal(msg, dn); + CONFDB_ZERO_CHECK_OR_JUMP(msg->dn, ret, ENOMEM, done); + + if (res->count == 0) { /* add a new message */ + errno = 0; + + /* cn first */ + ret = ldb_msg_add_string(msg, "cn", rdn_name); + if (ret != LDB_SUCCESS) { + if (errno) ret = errno; + else ret = EIO; + goto done; + } + + /* now the requested attribute */ + for (i = 0; values[i]; i++) { + ret = ldb_msg_add_string(msg, attribute, values[i]); + if (ret != LDB_SUCCESS) { + if (errno) ret = errno; + else ret = EIO; + goto done; + } + } + + ret = ldb_add(cdb->ldb, msg); + if (ret != LDB_SUCCESS) { + ret = EIO; + goto done; + } + + } else { + int optype; + errno = 0; + + /* mark this as a replacement */ + if (replace) optype = LDB_FLAG_MOD_REPLACE; + else optype = LDB_FLAG_MOD_ADD; + ret = ldb_msg_add_empty(msg, attribute, optype, NULL); + if (ret != LDB_SUCCESS) { + if (errno) ret = errno; + else ret = EIO; + goto done; + } + + /* now the requested attribute */ + for (i = 0; values[i]; i++) { + ret = ldb_msg_add_string(msg, attribute, values[i]); + if (ret != LDB_SUCCESS) { + if (errno) ret = errno; + else ret = EIO; + goto done; + } + } + + ret = ldb_modify(cdb->ldb, msg); + if (ret != LDB_SUCCESS) { + DEBUG(SSSDBG_MINOR_FAILURE, + "ldb_modify failed: [%s](%d)[%s]\n", + ldb_strerror(ret), ret, ldb_errstring(cdb->ldb)); + ret = EIO; + goto done; + } + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to add [%s] to [%s], error [%d] (%s)\n", + attribute, section, ret, strerror(ret)); + } + return ret; +} + +int confdb_get_param(struct confdb_ctx *cdb, + TALLOC_CTX *mem_ctx, + const char *section, + const char *attribute, + char ***values) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_result *res; + struct ldb_dn *dn; + char *secdn = NULL; + const char *attrs[] = { attribute, NULL }; + char **vals; + struct ldb_message_element *el; + int ret, i; + + tmp_ctx = talloc_new(mem_ctx); + if (!tmp_ctx) + return ENOMEM; + + ret = parse_section(tmp_ctx, section, &secdn, NULL); + if (ret != EOK) { + goto done; + } + + dn = ldb_dn_new(tmp_ctx, cdb->ldb, secdn); + if (!dn) { + ret = EIO; + goto done; + } + + ret = ldb_search(cdb->ldb, tmp_ctx, &res, + dn, LDB_SCOPE_BASE, attrs, NULL); + if (ret != LDB_SUCCESS) { + ret = EIO; + goto done; + } + if (res->count > 1) { + ret = EIO; + goto done; + } + + vals = talloc_zero(mem_ctx, char *); + ret = EOK; + + if (res->count > 0) { + el = ldb_msg_find_element(res->msgs[0], attribute); + if (el && el->num_values > 0) { + vals = talloc_realloc(mem_ctx, vals, char *, el->num_values +1); + if (!vals) { + ret = ENOMEM; + goto done; + } + /* should always be strings so this should be safe */ + for (i = 0; i < el->num_values; i++) { + struct ldb_val v = el->values[i]; + vals[i] = talloc_strndup(vals, (char *)v.data, v.length); + if (!vals[i]) { + ret = ENOMEM; + goto done; + } + } + vals[i] = NULL; + } + } + + *values = vals; + +done: + talloc_free(tmp_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to get [%s] from [%s], error [%d] (%s)\n", + attribute, section, ret, strerror(ret)); + } + return ret; +} + +int confdb_set_string(struct confdb_ctx *cdb, + const char *section, + const char *attribute, + const char *val) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_dn *dn; + char *secdn; + struct ldb_message *msg; + int ret, lret; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + return ENOMEM; + } + + ret = parse_section(tmp_ctx, section, &secdn, NULL); + if (ret != EOK) { + goto done; + } + + dn = ldb_dn_new(tmp_ctx, cdb->ldb, secdn); + if (!dn) { + ret = EIO; + goto done; + } + + msg = ldb_msg_new(tmp_ctx); + if (!msg) { + ret = ENOMEM; + goto done; + } + + msg->dn = dn; + + lret = ldb_msg_add_empty(msg, attribute, LDB_FLAG_MOD_REPLACE, NULL); + if (lret != LDB_SUCCESS) { + DEBUG(SSSDBG_MINOR_FAILURE, + "ldb_msg_add_empty failed: [%s]\n", ldb_strerror(lret)); + ret = EIO; + goto done; + } + + lret = ldb_msg_add_string(msg, attribute, val); + if (lret != LDB_SUCCESS) { + DEBUG(SSSDBG_MINOR_FAILURE, + "ldb_msg_add_string failed: [%s]\n", ldb_strerror(lret)); + ret = EIO; + goto done; + } + + lret = ldb_modify(cdb->ldb, msg); + if (lret != LDB_SUCCESS) { + DEBUG(SSSDBG_MINOR_FAILURE, + "ldb_modify failed: [%s](%d)[%s]\n", + ldb_strerror(lret), lret, ldb_errstring(cdb->ldb)); + ret = EIO; + goto done; + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to set [%s] from [%s], error [%d] (%s)\n", + attribute, section, ret, strerror(ret)); + } + return ret; +} + +int confdb_get_string(struct confdb_ctx *cdb, TALLOC_CTX *ctx, + const char *section, const char *attribute, + const char *defstr, char **result) +{ + char **values = NULL; + char *restr; + int ret; + + ret = confdb_get_param(cdb, ctx, section, attribute, &values); + if (ret != EOK) { + goto failed; + } + + if (values[0]) { + if (values[1] != NULL) { + /* too many values */ + ret = EINVAL; + goto failed; + } + restr = talloc_steal(ctx, values[0]); + } else { + /* Did not return a value, so use the default */ + + if (defstr == NULL) { /* No default given */ + *result = NULL; + talloc_free(values); + return EOK; + } + + /* Copy the default string */ + restr = talloc_strdup(ctx, defstr); + } + if (!restr) { + ret = ENOMEM; + goto failed; + } + + talloc_free(values); + + *result = restr; + return EOK; + +failed: + talloc_free(values); + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to get [%s] from [%s], error [%d] (%s)\n", + attribute, section, ret, strerror(ret)); + return ret; +} + +int confdb_get_int(struct confdb_ctx *cdb, + const char *section, const char *attribute, + int defval, int *result) +{ + char **values = NULL; + long val; + int ret; + char *endptr; + TALLOC_CTX *tmp_ctx; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + ret = ENOMEM; + goto failed; + } + + ret = confdb_get_param(cdb, tmp_ctx, section, attribute, &values); + if (ret != EOK) { + goto failed; + } + + if (values[0]) { + if (values[1] != NULL) { + /* too many values */ + ret = EINVAL; + goto failed; + } + + errno = 0; + val = strtol(values[0], &endptr, 0); + ret = errno; + if (ret != 0) { + goto failed; + } + if (*endptr || (values[0] == endptr)) { + ret = EINVAL; + goto failed; + } + if (val < INT_MIN || val > INT_MAX) { + ret = ERANGE; + goto failed; + } + + } else { + val = defval; + } + + talloc_free(tmp_ctx); + + *result = (int)val; + return EOK; + +failed: + talloc_free(tmp_ctx); + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to read [%s] from [%s], error [%d] (%s)\n", + attribute, section, ret, strerror(ret)); + return ret; +} + +long confdb_get_long(struct confdb_ctx *cdb, + const char *section, const char *attribute, + long defval, long *result) +{ + char **values = NULL; + long val; + int ret; + char *endptr; + TALLOC_CTX *tmp_ctx; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + ret = ENOMEM; + goto failed; + } + + ret = confdb_get_param(cdb, tmp_ctx, section, attribute, &values); + if (ret != EOK) { + goto failed; + } + + if (values[0]) { + if (values[1] != NULL) { + /* too many values */ + ret = EINVAL; + goto failed; + } + + errno = 0; + val = strtol(values[0], &endptr, 0); + ret = errno; + if (ret != 0) { + goto failed; + } + if (*endptr || (values[0] == endptr)) { + ret = EINVAL; + goto failed; + } + } else { + val = defval; + } + + talloc_free(tmp_ctx); + + *result = val; + return EOK; + +failed: + talloc_free(tmp_ctx); + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to read [%s] from [%s], error [%d] (%s)\n", + attribute, section, ret, strerror(ret)); + return ret; +} + +int confdb_get_bool(struct confdb_ctx *cdb, + const char *section, const char *attribute, + bool defval, bool *result) +{ + char **values = NULL; + bool val; + int ret; + TALLOC_CTX *tmp_ctx; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + ret = ENOMEM; + goto failed; + } + + ret = confdb_get_param(cdb, tmp_ctx, section, attribute, &values); + if (ret != EOK) { + goto failed; + } + + if (values[0]) { + if (values[1] != NULL) { + /* too many values */ + ret = EINVAL; + goto failed; + } + + if (strcasecmp(values[0], "FALSE") == 0) { + val = false; + + } else if (strcasecmp(values[0], "TRUE") == 0) { + val = true; + + } else { + + DEBUG(SSSDBG_OP_FAILURE, "Value is not a boolean!\n"); + ret = EINVAL; + goto failed; + } + + } else { + val = defval; + } + + talloc_free(tmp_ctx); + + *result = val; + return EOK; + +failed: + talloc_free(tmp_ctx); + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to read [%s] from [%s], error [%d] (%s)\n", + attribute, section, ret, strerror(ret)); + return ret; +} + +/* WARNING: Unlike other similar functions, this one does NOT take a default, + * and returns ENOENT if the attribute was not found ! */ +int confdb_get_string_as_list(struct confdb_ctx *cdb, TALLOC_CTX *ctx, + const char *section, const char *attribute, + char ***result) +{ + char **values = NULL; + int ret; + + ret = confdb_get_param(cdb, ctx, section, attribute, &values); + if (ret != EOK) { + goto done; + } + + if (values && values[0]) { + if (values[1] != NULL) { + /* too many values */ + ret = EINVAL; + goto done; + } + } else { + /* Did not return a value */ + ret = ENOENT; + goto done; + } + + ret = split_on_separator(ctx, values[0], ',', true, true, result, NULL); + +done: + talloc_free(values); + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to get [%s] from [%s], error [%d] (%s)\n", + attribute, section, ret, strerror(ret)); + } + return ret; +} + +int confdb_init(TALLOC_CTX *mem_ctx, + struct confdb_ctx **cdb_ctx, + const char *confdb_location) +{ + struct confdb_ctx *cdb; + int ret = EOK; + mode_t old_umask; + uid_t sssd_uid; + gid_t sssd_gid; + + cdb = talloc_zero(mem_ctx, struct confdb_ctx); + if (!cdb) + return ENOMEM; + + /* Because confdb calls use sync ldb calls, we create a separate event + * context here. This will prevent the ldb sync calls to start nested + * events. + * NOTE: this means that we *cannot* do async calls and return in confdb + * unless we convert all calls and hook back to the main event context. + */ + + cdb->pev = tevent_context_init(cdb); + if (!cdb->pev) { + talloc_free(cdb); + return EIO; + } + + cdb->ldb = ldb_init(cdb, cdb->pev); + if (!cdb->ldb) { + talloc_free(cdb); + return EIO; + } + + ret = ldb_set_debug(cdb->ldb, ldb_debug_messages, NULL); + if (ret != LDB_SUCCESS) { + DEBUG(SSSDBG_FATAL_FAILURE,"Could not set up debug fn.\n"); + talloc_free(cdb); + return EIO; + } + + old_umask = umask(SSS_DFL_UMASK); + /* file may exists and could be owned by root from previous version */ + sss_sssd_user_uid_and_gid(&sssd_uid, &sssd_gid); + ret = chown(confdb_location, sssd_uid, sssd_gid); + if (ret != EOK && errno != ENOENT) { + DEBUG(SSSDBG_MINOR_FAILURE, "Unable to chown config database [%s]: %s\n", + confdb_location, sss_strerror(errno)); + } + sss_set_sssd_user_eid(); + + ret = ldb_connect(cdb->ldb, confdb_location, 0, NULL); + + sss_restore_sssd_user_eid(); + umask(old_umask); + if (ret != LDB_SUCCESS) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to open config database [%s]\n", + confdb_location); + talloc_free(cdb); + return EIO; + } + + *cdb_ctx = cdb; + + return EOK; +} + +static errno_t get_entry_as_uint32(struct ldb_message *msg, + uint32_t *return_value, + const char *entry, + uint32_t default_value) +{ + const char *tmp = NULL; + char *endptr; + uint32_t u32ret = 0; + + *return_value = 0; + + if (!msg || !entry) { + return EFAULT; + } + + tmp = ldb_msg_find_attr_as_string(msg, entry, NULL); + if (tmp == NULL) { + *return_value = default_value; + return EOK; + } + + if ((*tmp == '-') || (*tmp == '\0')) { + return EINVAL; + } + + u32ret = strtouint32 (tmp, &endptr, 10); + if (errno) { + return errno; + } + + if (*endptr != '\0') { + /* Not all of the string was a valid number */ + return EINVAL; + } + + *return_value = u32ret; + return EOK; +} + +static errno_t get_entry_as_bool(struct ldb_message *msg, + bool *return_value, + const char *entry, + bool default_value) +{ + const char *tmp = NULL; + + *return_value = 0; + + if (!msg || !entry) { + return EFAULT; + } + + tmp = ldb_msg_find_attr_as_string(msg, entry, NULL); + if (tmp == NULL || *tmp == '\0') { + *return_value = default_value; + return EOK; + } + + if (strcasecmp(tmp, "FALSE") == 0) { + *return_value = 0; + } + else if (strcasecmp(tmp, "TRUE") == 0) { + *return_value = 1; + } + else { + return EINVAL; + } + + return EOK; +} + + +/* The default UID/GID for domains is 1. */ +static uint32_t confdb_get_min_id(struct sss_domain_info *domain) +{ + return SSSD_MIN_ID; +} + +static errno_t init_cached_auth_timeout(struct confdb_ctx *cdb, + struct ldb_message *msg, + uint32_t *_cached_auth_timeout) +{ + int cred_expiration; + int id_timeout; + errno_t ret; + uint32_t cached_auth_timeout; + + ret = get_entry_as_uint32(msg, &cached_auth_timeout, + CONFDB_DOMAIN_CACHED_AUTH_TIMEOUT, 0); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value for [%s]\n", CONFDB_DOMAIN_CACHED_AUTH_TIMEOUT); + goto done; + } + + 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 [%s] of offline credentials.\n", + CONFDB_PAM_CRED_TIMEOUT); + goto done; + } + + /* convert from days to seconds */ + cred_expiration *= 3600 * 24; + if (cred_expiration != 0 && + cred_expiration < cached_auth_timeout) { + cached_auth_timeout = cred_expiration; + } + + /* Set up the PAM identity timeout */ + ret = confdb_get_int(cdb, CONFDB_PAM_CONF_ENTRY, + CONFDB_PAM_ID_TIMEOUT, 5, + &id_timeout); + if (ret != EOK) goto done; + + if (cached_auth_timeout > id_timeout) { + DEBUG(SSSDBG_MINOR_FAILURE, + "cached_auth_timeout is greater than pam_id_timeout so be aware " + "that back end could be called to handle initgroups.\n"); + } + + ret = EOK; + +done: + if (ret == EOK) { + *_cached_auth_timeout = cached_auth_timeout; + } + return ret; +} + +static int confdb_get_domain_section(TALLOC_CTX *mem_ctx, + struct confdb_ctx *cdb, + const char *section, + const char *name, + struct ldb_result **_res) +{ + TALLOC_CTX *tmp_ctx; + int ret; + struct ldb_result *res; + struct ldb_dn *dn; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + dn = ldb_dn_new_fmt(tmp_ctx, cdb->ldb, "cn=%s,%s", name, section); + if (dn == NULL) { + ret = ENOMEM; + goto done; + } + + ret = ldb_search(cdb->ldb, tmp_ctx, &res, dn, + LDB_SCOPE_BASE, NULL, NULL); + if (ret != LDB_SUCCESS) { + ret = sss_ldb_error_to_errno(ret); + goto done; + } + + if (res->count == 0) { + ret = ENOENT; + goto done; + } else if (res->count > 1) { + ret = E2BIG; + goto done; + } + + *_res = talloc_steal(mem_ctx, res); + ret = EOK; +done: + talloc_free(tmp_ctx); + return ret; +} + +static char *confdb_get_domain_hostname(TALLOC_CTX *mem_ctx, + struct ldb_result *res, + const char *provider) +{ + char sys[HOST_NAME_MAX + 1] = {'\0'}; + const char *opt = NULL; + int ret; + + if (strcasecmp(provider, "ad") == 0) { + opt = ldb_msg_find_attr_as_string(res->msgs[0], "ad_hostname", NULL); + } else if (strcasecmp(provider, "ipa") == 0) { + opt = ldb_msg_find_attr_as_string(res->msgs[0], "ipa_hostname", NULL); + } + + if (opt != NULL) { + return talloc_strdup(mem_ctx, opt); + } + + ret = gethostname(sys, sizeof(sys)); + if (ret != 0) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get hostname [%d]: %s\n", ret, + sss_strerror(ret)); + return NULL; + } + + return talloc_strdup(mem_ctx, sys); +} + +static errno_t confdb_init_domain(struct sss_domain_info *domain, + struct ldb_result *res) +{ + errno_t ret; + const char *tmp; + + tmp = ldb_msg_find_attr_as_string(res->msgs[0], "cn", NULL); + if (!tmp) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid configuration entry, fatal error!\n"); + ret = EINVAL; + goto done; + } + domain->name = talloc_strdup(domain, tmp); + if (!domain->name) { + ret = ENOMEM; + goto done; + } + domain->conn_name = domain->name; + + tmp = ldb_msg_find_attr_as_string(res->msgs[0], + CONFDB_DOMAIN_ID_PROVIDER, + NULL); + if (tmp) { + domain->provider = talloc_strdup(domain, tmp); + if (!domain->provider) { + ret = ENOMEM; + goto done; + } + } + else { + DEBUG(SSSDBG_FATAL_FAILURE, + "Domain [%s] does not specify an ID provider, disabling!\n", + domain->name); + ret = EINVAL; + goto done; + } + + tmp = ldb_msg_find_attr_as_string(res->msgs[0], CONFDB_DOMAIN_AUTO_UPG, NULL); + if (tmp == NULL || *tmp == '\0') { + tmp = "false"; + } + + domain->mpg_mode = str_to_domain_mpg_mode(tmp); + + domain->timeout = ldb_msg_find_attr_as_int(res->msgs[0], + CONFDB_DOMAIN_TIMEOUT, 0); + + ret = get_entry_as_bool(res->msgs[0], &domain->ignore_group_members, + CONFDB_DOMAIN_IGNORE_GROUP_MEMBERS, 0); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value for %s\n", + CONFDB_DOMAIN_IGNORE_GROUP_MEMBERS); + goto done; + } + + ret = get_entry_as_uint32(res->msgs[0], &domain->id_min, + CONFDB_DOMAIN_MINID, + confdb_get_min_id(domain)); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Invalid value for minId\n"); + ret = EINVAL; + goto done; + } + + ret = get_entry_as_uint32(res->msgs[0], &domain->id_max, + CONFDB_DOMAIN_MAXID, 0); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Invalid value for maxId\n"); + ret = EINVAL; + goto done; + } + + if (domain->id_max && (domain->id_max < domain->id_min)) { + DEBUG(SSSDBG_FATAL_FAILURE, "Invalid domain range\n"); + ret = EINVAL; + goto done; + } + + /* Do we allow to cache credentials */ + ret = get_entry_as_bool(res->msgs[0], &domain->cache_credentials, + CONFDB_DOMAIN_CACHE_CREDS, 0); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value for %s\n", CONFDB_DOMAIN_CACHE_CREDS); + goto done; + } + + ret = get_entry_as_uint32(res->msgs[0], + &domain->cache_credentials_min_ff_length, + CONFDB_DOMAIN_CACHE_CREDS_MIN_FF_LENGTH, + CONFDB_DEFAULT_CACHE_CREDS_MIN_FF_LENGTH); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value for %s\n", + CONFDB_DOMAIN_CACHE_CREDS_MIN_FF_LENGTH); + goto done; + } + + ret = get_entry_as_uint32(res->msgs[0], &domain->override_gid, + CONFDB_DOMAIN_OVERRIDE_GID, 0); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value for [%s]\n", CONFDB_DOMAIN_OVERRIDE_GID); + goto done; + } + + domain->hostname = confdb_get_domain_hostname(domain, res, domain->provider); + if (domain->hostname == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get domain hostname\n"); + goto done; + } + + domain->krb5_keytab = NULL; + tmp = ldb_msg_find_attr_as_string(res->msgs[0], "krb5_keytab", NULL); + if (tmp != NULL) { + domain->krb5_keytab = talloc_strdup(domain, tmp); + if (domain->krb5_keytab == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get domain keytab!\n"); + goto done; + } + } + + domain->has_views = false; + domain->view_name = NULL; + + domain->state = DOM_ACTIVE; + +#ifdef BUILD_FILES_PROVIDER + domain->fallback_to_nss = false; + if (is_files_provider(domain)) { + ret = get_entry_as_bool(res->msgs[0], &domain->fallback_to_nss, + CONFDB_DOMAIN_FALLBACK_TO_NSS, true); + if(ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value for %s\n", CONFDB_DOMAIN_FALLBACK_TO_NSS); + goto done; + } + } +#endif + + domain->not_found_counter = 0; + ret = EOK; + +done: + return ret; +} + +static errno_t confdb_init_domain_provider_and_enum(struct sss_domain_info *domain, + struct ldb_result *res) +{ + int val; + errno_t ret; + const char *tmp, *tmp_pam_target, *tmp_auth; + + /* TEMP: test if the old bitfield conf value is used and warn it has been + * superseded. */ + val = ldb_msg_find_attr_as_int(res->msgs[0], CONFDB_DOMAIN_ENUMERATE, 0); + if (val > 0) { /* ok there was a number in here */ + DEBUG(SSSDBG_FATAL_FAILURE, + "Warning: enumeration parameter in %s still uses integers! " + "Enumeration is now a boolean and takes true/false values. " + "Interpreting as true\n", domain->name); + domain->enumerate = true; + } else { /* assume the new format */ + ret = get_entry_as_bool(res->msgs[0], &domain->enumerate, + CONFDB_DOMAIN_ENUMERATE, 0); + if(ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value for %s\n", CONFDB_DOMAIN_ENUMERATE); + goto done; + } + } + + if (is_files_provider(domain)) { + /* The password field must be reported as 'x', else pam_unix won't + * authenticate this entry. See man pwconv(8) for more details. + */ + domain->pwfield = "x"; + } + + if (domain->provider != NULL && strcasecmp(domain->provider, "proxy") == 0) { + /* The password field must be reported as 'x' for proxy provider + * using files library, else pam_unix won't authenticate this entry. + * We set this only for sssd-shadowutils target which can be used + * to authenticate with pam_unix only. Otherwise we let administrator + * to overwrite default * value with pwfield option to avoid regression + * on more common use case where remote authentication is required. */ + tmp = ldb_msg_find_attr_as_string(res->msgs[0], + CONFDB_PROXY_LIBNAME, + NULL); + + tmp_auth = ldb_msg_find_attr_as_string(res->msgs[0], + CONFDB_DOMAIN_AUTH_PROVIDER, + NULL); + + tmp_pam_target = ldb_msg_find_attr_as_string(res->msgs[0], + CONFDB_PROXY_PAM_TARGET, + NULL); + + if (tmp != NULL && tmp_pam_target != NULL + && strcasecmp(tmp, "files") == 0 + && (tmp_auth == NULL || strcasecmp(tmp_auth, "proxy") == 0) + && strcmp(tmp_pam_target, "sssd-shadowutils") == 0) { + domain->pwfield = "x"; + } + } + + if (!domain->enumerate) { + DEBUG(SSSDBG_TRACE_FUNC, "No enumeration for [%s]!\n", domain->name); + DEBUG(SSSDBG_TRACE_FUNC, + "Please note that when enumeration is disabled `getent " + "passwd` does not return all users by design. See " + "sssd.conf man page for more detailed information\n"); + } + + ret = EOK; + +done: + return ret; +} + +static errno_t confdb_init_domain_fqn(struct confdb_ctx *cdb, + TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + struct ldb_result *res) +{ + errno_t ret; + char *default_domain; + bool fqnames_default = false; + + ret = confdb_get_string(cdb, mem_ctx, CONFDB_MONITOR_CONF_ENTRY, + CONFDB_MONITOR_DEFAULT_DOMAIN, NULL, + &default_domain); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot get the default domain [%d]: %s\n", + ret, strerror(ret)); + goto done; + } + + /* Determine if user/group names will be Fully Qualified + * in NSS interfaces */ + if (default_domain != NULL + && is_files_provider(domain) == false + ) { + DEBUG(SSSDBG_CONF_SETTINGS, + "Default domain suffix set. Changing default for " + "use_fully_qualified_names to True.\n"); + fqnames_default = true; + } + + ret = get_entry_as_bool(res->msgs[0], &domain->fqnames, + CONFDB_DOMAIN_FQ, fqnames_default); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Invalid value for %s\n", + CONFDB_DOMAIN_FQ); + goto done; + } + + if (default_domain != NULL + && domain->fqnames == false + && is_files_provider(domain) == false + ) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid configuration detected (default_domain_suffix is used " + "while use_fully_qualified_names was set to false).\n"); + ret = ERR_INVALID_CONFIG; + goto done; + } + + ret = EOK; + +done: + return ret; +} + +static errno_t confdb_init_domain_timeouts(struct confdb_ctx *cdb, + struct sss_domain_info *domain, + struct ldb_result *res) +{ + errno_t ret; + uint32_t entry_cache_timeout; + int memcache_timeout; + + ret = confdb_get_int(cdb, + CONFDB_NSS_CONF_ENTRY, + CONFDB_MEMCACHE_TIMEOUT, + 300, &memcache_timeout); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Unable to get memory cache entry timeout [%s].\n", + CONFDB_MEMCACHE_TIMEOUT); + goto done; + } + + /* Get the global entry cache timeout setting */ + ret = get_entry_as_uint32(res->msgs[0], &entry_cache_timeout, + CONFDB_DOMAIN_ENTRY_CACHE_TIMEOUT, 5400); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value for [%s]\n", + CONFDB_DOMAIN_ENTRY_CACHE_TIMEOUT); + goto done; + } + + /* Override the user cache timeout, if specified */ + ret = get_entry_as_uint32(res->msgs[0], &domain->user_timeout, + CONFDB_DOMAIN_USER_CACHE_TIMEOUT, + entry_cache_timeout); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value for [%s]\n", + CONFDB_DOMAIN_USER_CACHE_TIMEOUT); + goto done; + } + + if (domain->user_timeout < memcache_timeout) { + DEBUG(SSSDBG_CONF_SETTINGS, + "%s is less than %s. User records will not be updated before " + "memory cache entry expires.\n", + CONFDB_DOMAIN_USER_CACHE_TIMEOUT, CONFDB_MEMCACHE_TIMEOUT); + } + + /* Override the group cache timeout, if specified */ + ret = get_entry_as_uint32(res->msgs[0], &domain->group_timeout, + CONFDB_DOMAIN_GROUP_CACHE_TIMEOUT, + entry_cache_timeout); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value for [%s]\n", + CONFDB_DOMAIN_GROUP_CACHE_TIMEOUT); + goto done; + } + + if (domain->group_timeout < memcache_timeout) { + DEBUG(SSSDBG_CONF_SETTINGS, + "%s is less than %s. Group records will not be updated before " + "memory cache entry expires.\n", + CONFDB_DOMAIN_GROUP_CACHE_TIMEOUT, CONFDB_MEMCACHE_TIMEOUT); + } + + /* Override the netgroup cache timeout, if specified */ + ret = get_entry_as_uint32(res->msgs[0], &domain->netgroup_timeout, + CONFDB_DOMAIN_NETGROUP_CACHE_TIMEOUT, + entry_cache_timeout); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value for [%s]\n", + CONFDB_DOMAIN_NETGROUP_CACHE_TIMEOUT); + goto done; + } + + /* Override the service cache timeout, if specified */ + ret = get_entry_as_uint32(res->msgs[0], &domain->service_timeout, + CONFDB_DOMAIN_SERVICE_CACHE_TIMEOUT, + entry_cache_timeout); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value for [%s]\n", + CONFDB_DOMAIN_SERVICE_CACHE_TIMEOUT); + goto done; + } + + /* Override the autofs cache timeout, if specified */ + ret = get_entry_as_uint32(res->msgs[0], &domain->autofsmap_timeout, + CONFDB_DOMAIN_AUTOFS_CACHE_TIMEOUT, + entry_cache_timeout); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value for [%s]\n", + CONFDB_DOMAIN_AUTOFS_CACHE_TIMEOUT); + goto done; + } + + /* Override the sudo cache timeout, if specified */ + ret = get_entry_as_uint32(res->msgs[0], &domain->sudo_timeout, + CONFDB_DOMAIN_SUDO_CACHE_TIMEOUT, + entry_cache_timeout); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value for [%s]\n", + CONFDB_DOMAIN_SUDO_CACHE_TIMEOUT); + goto done; + } + + /* Override the ssh known hosts timeout, if specified */ + ret = get_entry_as_uint32(res->msgs[0], &domain->ssh_host_timeout, + CONFDB_DOMAIN_SSH_HOST_CACHE_TIMEOUT, + entry_cache_timeout); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value for [%s]\n", + CONFDB_DOMAIN_SSH_HOST_CACHE_TIMEOUT); + goto done; + } + + /* Override the computer timeout, if specified */ + ret = get_entry_as_uint32(res->msgs[0], &domain->computer_timeout, + CONFDB_DOMAIN_COMPUTER_CACHE_TIMEOUT, + entry_cache_timeout); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value for [%s]\n", + CONFDB_DOMAIN_COMPUTER_CACHE_TIMEOUT); + goto done; + } + + /* Override the resolver timeout, if specified */ + ret = get_entry_as_uint32(res->msgs[0], &domain->resolver_timeout, + CONFDB_DOMAIN_RESOLVER_CACHE_TIMEOUT, + entry_cache_timeout); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value for [%s]\n", + CONFDB_DOMAIN_RESOLVER_CACHE_TIMEOUT); + goto done; + } + + /* Set refresh_expired_interval, if specified */ + ret = get_entry_as_uint32(res->msgs[0], &domain->refresh_expired_interval, + CONFDB_DOMAIN_REFRESH_EXPIRED_INTERVAL, + 0); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value for [%s]\n", + CONFDB_DOMAIN_REFRESH_EXPIRED_INTERVAL); + goto done; + } + + /* Set refresh_expired_interval_offset, if specified */ + ret = get_entry_as_uint32(res->msgs[0], + &domain->refresh_expired_interval_offset, + CONFDB_DOMAIN_REFRESH_EXPIRED_INTERVAL_OFFSET, + 0); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value for [%s]\n", + CONFDB_DOMAIN_REFRESH_EXPIRED_INTERVAL_OFFSET); + goto done; + } + + /* detect and fix misconfiguration */ + if (domain->refresh_expired_interval + domain->refresh_expired_interval_offset > entry_cache_timeout) { + DEBUG(SSSDBG_CONF_SETTINGS, + "refresh_expired_interval (%d) + " + "refresh_expired_interval_offset (%d) cannot be greater than " + "entry_cache_timeout (%u)\n", + domain->refresh_expired_interval, + domain->refresh_expired_interval_offset, entry_cache_timeout); + + domain->refresh_expired_interval = 0.70 * entry_cache_timeout; + domain->refresh_expired_interval_offset = + 0.10 * domain->refresh_expired_interval; + + DEBUG(SSSDBG_CONF_SETTINGS, + "refresh_expired_interval is being set to recommended value " + "entry_cache_timeout * 0.70 (%u).\n", + domain->refresh_expired_interval); + DEBUG(SSSDBG_CONF_SETTINGS, + "refresh_expired_interval_offset is being set to recommended value " + "refresh_expired_interval * 0.10 (%u).\n", + domain->refresh_expired_interval_offset); + } + + ret = init_cached_auth_timeout(cdb, res->msgs[0], + &domain->cached_auth_timeout); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "init_cached_auth_timeout failed: %s:[%d].\n", + sss_strerror(ret), ret); + goto done; + } + + ret = EOK; + +done: + return ret; +} + +static errno_t confdb_init_domain_user_info(struct sss_domain_info *domain, + struct ldb_result *res) +{ + errno_t ret; + const char *tmp; + + tmp = ldb_msg_find_attr_as_string(res->msgs[0], + CONFDB_NSS_OVERRIDE_HOMEDIR, NULL); + /* Here we skip the files provider as it should always return *only* + * what's in the files and nothing else. */ + if (tmp != NULL + && !is_files_provider(domain) + ) { + domain->override_homedir = talloc_strdup(domain, tmp); + if (!domain->override_homedir) { + ret = ENOMEM; + goto done; + } + } + + tmp = ldb_msg_find_attr_as_string(res->msgs[0], + CONFDB_NSS_FALLBACK_HOMEDIR, NULL); + if (tmp != NULL) { + domain->fallback_homedir = talloc_strdup(domain, tmp); + if (!domain->fallback_homedir) { + ret = ENOMEM; + goto done; + } + } else { + if (strcasecmp(domain->provider, "ad") == 0) { + /* ad provider default */ + domain->fallback_homedir = talloc_strdup(domain, "/home/%d/%u"); + if (!domain->fallback_homedir) { + ret = ENOMEM; + goto done; + } + } + } + + tmp = ldb_msg_find_attr_as_string(res->msgs[0], + CONFDB_DOMAIN_SUBDOMAIN_HOMEDIR, + CONFDB_DOMAIN_DEFAULT_SUBDOMAIN_HOMEDIR); + if (tmp != NULL) { + domain->subdomain_homedir = talloc_strdup(domain, tmp); + if (!domain->subdomain_homedir) { + ret = ENOMEM; + goto done; + } + } + + tmp = ldb_msg_find_attr_as_string(res->msgs[0], + CONFDB_NSS_HOMEDIR_SUBSTRING, NULL); + if (tmp != NULL) { + domain->homedir_substr = talloc_strdup(domain, tmp); + if (domain->homedir_substr == NULL) { + ret = ENOMEM; + goto done; + } + } + + tmp = ldb_msg_find_attr_as_string(res->msgs[0], CONFDB_NSS_OVERRIDE_SHELL, NULL); + /* Here we skip the files provider as it should always return *only* + * what's in the files and nothing else. */ + if (tmp != NULL + && !is_files_provider(domain) + ) { + domain->override_shell = talloc_strdup(domain, tmp); + if (!domain->override_shell) { + ret = ENOMEM; + goto done; + } + } + + tmp = ldb_msg_find_attr_as_string(res->msgs[0], CONFDB_NSS_DEFAULT_SHELL, NULL); + if (tmp != NULL) { + domain->default_shell = talloc_strdup(domain, tmp); + if (!domain->default_shell) { + ret = ENOMEM; + goto done; + } + } + + tmp = ldb_msg_find_attr_as_string(res->msgs[0], CONFDB_NSS_PWFIELD, NULL); + if (tmp != NULL) { + domain->pwfield = talloc_strdup(domain, tmp); + if (!domain->pwfield) { + ret = ENOMEM; + goto done; + } + } + + ret = EOK; + +done: + return ret; +} + +static errno_t confdb_init_domain_case(struct sss_domain_info *domain, + struct ldb_result *res) +{ + errno_t ret; + const char *tmp; + + tmp = ldb_msg_find_attr_as_string(res->msgs[0], + CONFDB_DOMAIN_CASE_SENSITIVE, NULL); + if (tmp != NULL) { + if (strcasecmp(tmp, "true") == 0) { + domain->case_sensitive = true; + domain->case_preserve = true; + } else if (strcasecmp(tmp, "false") == 0) { + domain->case_sensitive = false; + domain->case_preserve = false; + } else if (strcasecmp(tmp, "preserving") == 0) { + domain->case_sensitive = false; + domain->case_preserve = true; + } else { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value for %s\n", CONFDB_DOMAIN_CASE_SENSITIVE); + ret = EINVAL; + goto done; + } + } else { + /* default */ + if (strcasecmp(domain->provider, "ad") == 0) { + domain->case_sensitive = false; + domain->case_preserve = false; + } else { + domain->case_sensitive = true; + domain->case_preserve = true; + } + } + + ret = EOK; + +done: + return ret; +} + +static errno_t confdb_init_domain_subdomains(struct sss_domain_info *domain, + struct ldb_result *res) +{ + errno_t ret; + const char *tmp; + + tmp = ldb_msg_find_attr_as_string(res->msgs[0], + CONFDB_SUBDOMAIN_ENUMERATE, + CONFDB_DEFAULT_SUBDOMAIN_ENUMERATE); + if (tmp != NULL) { + ret = split_on_separator(domain, tmp, ',', true, true, + &domain->sd_enumerate, NULL); + if (ret != 0) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Cannot parse %s\n", CONFDB_SUBDOMAIN_ENUMERATE); + goto done; + } + } + + tmp = ldb_msg_find_attr_as_string(res->msgs[0], + CONFDB_DOMAIN_SUBDOMAIN_INHERIT, + NULL); + if (tmp != NULL) { + ret = split_on_separator(domain, tmp, ',', true, true, + &domain->sd_inherit, NULL); + if (ret != 0) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Cannot parse %s\n", CONFDB_SUBDOMAIN_ENUMERATE); + goto done; + } + } + + domain->type = DOM_TYPE_POSIX; + tmp = ldb_msg_find_attr_as_string(res->msgs[0], + CONFDB_DOMAIN_TYPE, + CONFDB_DOMAIN_TYPE_POSIX); + if (tmp != NULL) { + if (strcasecmp(tmp, CONFDB_DOMAIN_TYPE_POSIX) == 0) { + domain->type = DOM_TYPE_POSIX; + } else if (strcasecmp(tmp, CONFDB_DOMAIN_TYPE_APP) == 0) { + domain->type = DOM_TYPE_APPLICATION; + } else { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value %s for [%s]\n", tmp, CONFDB_DOMAIN_TYPE); + ret = EINVAL; + goto done; + } + } + + ret = get_entry_as_uint32(res->msgs[0], &domain->subdomain_refresh_interval, + CONFDB_DOMAIN_SUBDOMAIN_REFRESH, + CONFDB_DOMAIN_SUBDOMAIN_REFRESH_DEFAULT_VALUE); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value for [%s]\n", CONFDB_DOMAIN_SUBDOMAIN_REFRESH); + goto done; + } else if (domain->subdomain_refresh_interval == 0) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Invalid value for [%s]. Setting up the default value: %d\n", + CONFDB_DOMAIN_SUBDOMAIN_REFRESH, + CONFDB_DOMAIN_SUBDOMAIN_REFRESH_DEFAULT_VALUE); + + domain->subdomain_refresh_interval = + CONFDB_DOMAIN_SUBDOMAIN_REFRESH_DEFAULT_VALUE; + } + + ret = get_entry_as_uint32(res->msgs[0], + &domain->subdomain_refresh_interval_offset, + CONFDB_DOMAIN_SUBDOMAIN_REFRESH_OFFSET, + CONFDB_DOMAIN_SUBDOMAIN_REFRESH_OFFSET_DEFAULT_VALUE); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value for [%s]\n", CONFDB_DOMAIN_SUBDOMAIN_REFRESH_OFFSET); + goto done; + } + + ret = EOK; + +done: + return ret; +} + +static errno_t confdb_init_domain_gssapi(struct sss_domain_info *domain, + struct ldb_result *res) +{ + errno_t ret; + const char *tmp; + + tmp = ldb_msg_find_attr_as_string(res->msgs[0], CONFDB_PAM_GSSAPI_SERVICES, + NULL); + if (tmp != NULL) { + ret = split_on_separator(domain, tmp, ',', true, true, + &domain->gssapi_services, NULL); + if (ret != 0) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Cannot parse %s\n", CONFDB_PAM_GSSAPI_SERVICES); + goto done; + } + } + + tmp = ldb_msg_find_attr_as_string(res->msgs[0], CONFDB_PAM_GSSAPI_CHECK_UPN, + NULL); + if (tmp != NULL) { + domain->gssapi_check_upn = talloc_strdup(domain, tmp); + if (domain->gssapi_check_upn == NULL) { + ret = ENOMEM; + goto done; + } + } + + tmp = ldb_msg_find_attr_as_string(res->msgs[0], + CONFDB_PAM_GSSAPI_INDICATORS_MAP, + NULL); + if (tmp != NULL && tmp[0] != '\0') { + ret = split_on_separator(domain, tmp, ',', true, true, + &domain->gssapi_indicators_map, NULL); + if (ret != 0) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Cannot parse %s\n", CONFDB_PAM_GSSAPI_INDICATORS_MAP); + goto done; + } + } + + ret = EOK; + +done: + return ret; +} + +static errno_t confdb_init_domain_pwd_expire(struct confdb_ctx *cdb, + struct sss_domain_info *domain, + struct ldb_result *res) +{ + int val; + errno_t ret; + + /* Set the PAM warning time, if specified. If not specified, pass on + * the "not set" value of "-1" which means "use provider default". The + * value 0 means "always display the warning if server sends one" */ + domain->pwd_expiration_warning = -1; + + val = ldb_msg_find_attr_as_int(res->msgs[0], + CONFDB_DOMAIN_PWD_EXPIRATION_WARNING, + -1); + if (val == -1) { + ret = confdb_get_int(cdb, CONFDB_PAM_CONF_ENTRY, + CONFDB_PAM_PWD_EXPIRATION_WARNING, + -1, &val); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to read PAM expiration warning, not fatal.\n"); + val = -1; + } + } + + DEBUG(SSSDBG_TRACE_LIBS, "pwd_expiration_warning is %d\n", val); + if (val >= 0) { + DEBUG(SSSDBG_CONF_SETTINGS, + "Setting domain password expiration warning to %d days\n", val); + /* The value is in days, transform it to seconds */ + domain->pwd_expiration_warning = val * 24 * 3600; + } + + return EOK; +} + +static int confdb_get_domain_internal(struct confdb_ctx *cdb, + TALLOC_CTX *mem_ctx, + const char *name, + struct sss_domain_info **_domain) +{ + struct sss_domain_info *domain; + struct ldb_result *res; + TALLOC_CTX *tmp_ctx; + errno_t ret; + + tmp_ctx = talloc_new(mem_ctx); + if (!tmp_ctx) return ENOMEM; + + ret = confdb_get_domain_section(tmp_ctx, cdb, CONFDB_DOMAIN_BASEDN, + name, &res); + if (ret == ENOENT) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unknown domain [%s]\n", name); + goto done; + } else if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Error %d: %s while retrieving %s\n", + ret, sss_strerror(ret), name); + goto done; + } + + domain = talloc_zero(mem_ctx, struct sss_domain_info); + if (!domain) { + ret = ENOMEM; + goto done; + } + + ret = confdb_init_domain(domain, res); + if (ret != EOK) { + goto done; + } + + /* Determine if this domain can be enumerated */ + ret = confdb_init_domain_provider_and_enum(domain, res); + if (ret != EOK) { + goto done; + } + + ret = confdb_init_domain_fqn(cdb, mem_ctx, domain, res); + if (ret != EOK) { + goto done; + } + + /* Get all the timeouts */ + ret = confdb_init_domain_timeouts(cdb, domain, res); + if (ret != EOK) { + goto done; + } + + /* Get password expiration information */ + ret = confdb_init_domain_pwd_expire(cdb, domain, res); + if (ret != EOK) { + goto done; + } + + /* Get the behavior about the homedir */ + ret = confdb_init_domain_user_info(domain, res); + if (ret != EOK) { + goto done; + } + + /* Get the case sensitivity */ + ret = confdb_init_domain_case(domain, res); + if (ret != EOK) { + goto done; + } + + /* Get the subdomains information */ + ret = confdb_init_domain_subdomains(domain, res); + if (ret != EOK) { + goto done; + } + + /* Get the GSSAPI information */ + ret = confdb_init_domain_gssapi(domain, res); + if (ret != EOK) { + goto done; + } + + + *_domain = domain; + ret = EOK; +done: + talloc_free(tmp_ctx); + return ret; +} + +int confdb_get_domains(struct confdb_ctx *cdb, + struct sss_domain_info **domains) +{ + TALLOC_CTX *tmp_ctx; + struct sss_domain_info *domain = NULL; + char **domlist; + int ret, i; + + if (cdb->doms) { + *domains = cdb->doms; + return EOK; + } + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) return ENOMEM; + + ret = confdb_get_enabled_domain_list(cdb, tmp_ctx, &domlist); + if (ret == ENOENT) { + DEBUG(SSSDBG_CRIT_FAILURE, "No domains configured, fatal error!\n"); + ret = ERR_NO_DOMAIN_ENABLED; + goto done; + } else if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Fatal error retrieving domains list!\n"); + goto done; + } + + for (i = 0; domlist[i]; i++) { + /* check if domain name is really unique */ + DLIST_FOR_EACH(domain, cdb->doms) { + if (strcasecmp(domain->name, domlist[i]) == 0) { + DEBUG(SSSDBG_FATAL_FAILURE, + SAME_DOMAINS_ERROR_MSG, domlist[i], domain->name); + sss_log(SSS_LOG_CRIT, + SAME_DOMAINS_ERROR_MSG, domlist[i], domain->name); + + ret = EINVAL; + goto done; + } + } + + domain = NULL; + ret = confdb_get_domain_internal(cdb, cdb, domlist[i], &domain); + if (ret) { + DEBUG(SSSDBG_FATAL_FAILURE, + RETRIEVE_DOMAIN_ERROR_MSG, + ret, sss_strerror(ret), domlist[i]); + sss_log(SSS_LOG_CRIT, + RETRIEVE_DOMAIN_ERROR_MSG, + ret, sss_strerror(ret), domlist[i]); + + continue; + } + + DLIST_ADD_END(cdb->doms, domain, struct sss_domain_info *); + } + + if (cdb->doms == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, + "No properly configured domains, fatal error!\n"); + ret = ENOENT; + goto done; + } + + *domains = cdb->doms; + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +int confdb_get_domain(struct confdb_ctx *cdb, + const char *name, + struct sss_domain_info **_domain) +{ + struct sss_domain_info *dom, *doms; + int ret; + + ret = confdb_get_domains(cdb, &doms); + if (ret != EOK) { + return ret; + } + + for (dom = doms; dom; dom = get_next_domain(dom, 0)) { + if (strcasecmp(dom->name, name) == 0) { + *_domain = dom; + return EOK; + } + } + + return ENOENT; +} + +int confdb_list_all_domain_names(TALLOC_CTX *mem_ctx, + struct confdb_ctx *cdb, + char ***_names) +{ + TALLOC_CTX *tmp_ctx = NULL; + struct ldb_dn *dn = NULL; + struct ldb_result *res = NULL; + static const char *attrs[] = {CONFDB_DOMAIN_ATTR, NULL}; + const char *name = NULL; + char **names = NULL; + int i; + int ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + dn = ldb_dn_new(tmp_ctx, cdb->ldb, CONFDB_DOMAIN_BASEDN); + if (dn == NULL) { + ret = ENOMEM; + goto done; + } + + ret = ldb_search(cdb->ldb, tmp_ctx, &res, dn, LDB_SCOPE_ONELEVEL, + attrs, NULL); + if (ret != LDB_SUCCESS) { + ret = EIO; + goto done; + } + + names = talloc_zero_array(tmp_ctx, char*, res->count + 1); + if (names == NULL) { + ret = ENOMEM; + goto done; + } + + for (i = 0; i < res->count; i++) { + name = ldb_msg_find_attr_as_string(res->msgs[i], CONFDB_DOMAIN_ATTR, + 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; + } + + names[i] = talloc_strdup(names, name); + if (names[i] == NULL) { + ret = ENOMEM; + goto done; + } + } + + *_names = talloc_steal(mem_ctx, names); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +int confdb_get_sub_sections(TALLOC_CTX *mem_ctx, + struct confdb_ctx *cdb, + const char *section, + char ***sections, + int *num_sections) +{ + TALLOC_CTX *tmp_ctx = NULL; + char *secdn; + struct ldb_dn *base = NULL; + struct ldb_result *res = NULL; + static const char *attrs[] = {"cn", NULL}; + char **names; + int base_comp_num; + int num; + int i; + int ret; + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ret = parse_section(tmp_ctx, section, &secdn, NULL); + if (ret != EOK) { + goto done; + } + + base = ldb_dn_new(tmp_ctx, cdb->ldb, secdn); + if (base == NULL) { + ret = ENOMEM; + goto done; + } + + base_comp_num = ldb_dn_get_comp_num(base); + + ret = ldb_search(cdb->ldb, tmp_ctx, &res, base, LDB_SCOPE_SUBTREE, + attrs, NULL); + if (ret != LDB_SUCCESS) { + ret = EIO; + goto done; + } + + names = talloc_zero_array(tmp_ctx, char *, res->count + 1); + if (names == NULL) { + ret = ENOMEM; + goto done; + } + + for (num = 0, i = 0; i < res->count; i++) { + const struct ldb_val *val; + char *name; + int n; + int j; + + n = ldb_dn_get_comp_num(res->msgs[i]->dn); + if (n == base_comp_num) continue; + + name = NULL; + for (j = n - base_comp_num - 1; j >= 0; j--) { + val = ldb_dn_get_component_val(res->msgs[i]->dn, j); + if (name == NULL) { + name = talloc_strndup(names, + (const char *)val->data, val->length); + } else { + name = talloc_asprintf(names, "%s/%.*s", name, + (int)val->length, + (const char *)val->data); + } + if (name == NULL) { + ret = ENOMEM; + goto done; + } + } + + names[num] = name; + if (names[num] == NULL) { + ret = ENOMEM; + goto done; + } + + num++; + } + + *sections = talloc_steal(mem_ctx, names); + *num_sections = num; + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +#ifdef BUILD_FILES_PROVIDER +static bool need_implicit_files_domain(TALLOC_CTX *tmp_ctx, + struct confdb_ctx *cdb, + struct ldb_result *doms) +{ + const char *id_provider = NULL; + unsigned int i; + errno_t ret; + char **domlist; + const char *val; + + ret = confdb_get_string_as_list(cdb, tmp_ctx, + CONFDB_MONITOR_CONF_ENTRY, + CONFDB_MONITOR_ACTIVE_DOMAINS, + &domlist); + if (ret == ENOENT) { + return true; + } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Cannot get active domains %d[%s]\n", + ret, sss_strerror(ret)); + return false; + } + + for (i = 0; i < doms->count; i++) { + val = ldb_msg_find_attr_as_string(doms->msgs[i], CONFDB_DOMAIN_ATTR, + NULL); + if (val == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "The object [%s] doesn't have a name\n", + ldb_dn_get_linearized(doms->msgs[i]->dn)); + continue; + } + + /* skip disabled domain */ + if (!string_in_list(val, domlist, false)) { + continue; + } + + id_provider = ldb_msg_find_attr_as_string(doms->msgs[i], + CONFDB_DOMAIN_ID_PROVIDER, + NULL); + if (id_provider == NULL) { + DEBUG(SSSDBG_OP_FAILURE, + "The object [%s] doesn't have an id_provider\n", + ldb_dn_get_linearized(doms->msgs[i]->dn)); + continue; + } + + if (strcasecmp(id_provider, "files") == 0) { + return false; + } + + if (strcasecmp(id_provider, "proxy") == 0) { + val = ldb_msg_find_attr_as_string(doms->msgs[i], + CONFDB_PROXY_LIBNAME, NULL); + if (val == NULL) { + DEBUG(SSSDBG_OP_FAILURE, + "The object [%s] doesn't have proxy_lib_name with " + "id_provider proxy\n", + ldb_dn_get_linearized(doms->msgs[i]->dn)); + continue; + } + + /* id_provider = proxy + proxy_lib_name = files are equivalent + * to id_provider = files + */ + if (strcmp(val, "files") == 0) { + return false; + } + } + } + + return true; +} + +static int confdb_has_files_domain(struct confdb_ctx *cdb) +{ + TALLOC_CTX *tmp_ctx = NULL; + struct ldb_dn *dn = NULL; + struct ldb_result *res = NULL; + static const char *attrs[] = { CONFDB_DOMAIN_ID_PROVIDER, + CONFDB_DOMAIN_ATTR, + CONFDB_PROXY_LIBNAME, NULL }; + int ret; + bool need_files_dom; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + dn = ldb_dn_new(tmp_ctx, cdb->ldb, CONFDB_DOMAIN_BASEDN); + if (dn == NULL) { + ret = ENOMEM; + goto done; + } + + ret = ldb_search(cdb->ldb, tmp_ctx, &res, dn, LDB_SCOPE_ONELEVEL, + attrs, NULL); + if (ret != LDB_SUCCESS) { + ret = EIO; + goto done; + } + + need_files_dom = need_implicit_files_domain(tmp_ctx, cdb, res); + + ret = need_files_dom ? ENOENT : EOK; +done: + talloc_free(tmp_ctx); + return ret; +} + +static int create_files_domain(struct confdb_ctx *cdb, + const char *name) +{ + TALLOC_CTX *tmp_ctx = NULL; + errno_t ret; + char *cdb_path = NULL; + const char *val[2] = { NULL, NULL }; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n"); + return ENOMEM; + } + + cdb_path = talloc_asprintf(tmp_ctx, CONFDB_DOMAIN_PATH_TMPL, name); + if (cdb_path == NULL) { + ret = ENOMEM; + goto done; + } + + val[0] = "files"; + ret = confdb_add_param(cdb, true, cdb_path, "id_provider", val); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add id_provider [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + ret = EOK; +done: + talloc_free(tmp_ctx); + return ret; +} + +static int activate_files_domain(struct confdb_ctx *cdb, + const char *name) +{ + errno_t ret; + TALLOC_CTX *tmp_ctx; + char *monitor_domlist; + const char *domlist[2] = { NULL, NULL }; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ret = confdb_get_string(cdb, tmp_ctx, + CONFDB_MONITOR_CONF_ENTRY, + CONFDB_MONITOR_ACTIVE_DOMAINS, + NULL, + &monitor_domlist); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Fatal error retrieving domains list!\n"); + goto done; + } + + if (monitor_domlist != NULL) { + domlist[0] = talloc_asprintf(tmp_ctx, "%s,%s", name, monitor_domlist); + if (domlist[0] == NULL) { + ret = ENOMEM; + goto done; + } + } else { + domlist[0] = name; + } + + ret = confdb_add_param(cdb, true, + CONFDB_MONITOR_CONF_ENTRY, + CONFDB_MONITOR_ACTIVE_DOMAINS, + domlist); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Cannot extend the domain list [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; + } + + ret = EOK; +done: + talloc_free(tmp_ctx); + return ret; +} + +static int confdb_ensure_files_domain(struct confdb_ctx *cdb, + const char *implicit_files_dom_name) +{ + errno_t ret; + bool enable_files; + + ret = confdb_get_bool(cdb, + CONFDB_MONITOR_CONF_ENTRY, + CONFDB_MONITOR_ENABLE_FILES_DOM, + false, &enable_files); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot get the value of %s\n", + CONFDB_MONITOR_ENABLE_FILES_DOM); + return ret; + } + + if (enable_files == false) { + DEBUG(SSSDBG_CONF_SETTINGS, "The implicit files domain is disabled\n"); + return EOK; + } + + ret = confdb_has_files_domain(cdb); + if (ret == EOK) { + DEBUG(SSSDBG_CONF_SETTINGS, "The files domain is already enabled\n"); + return EOK; + } else if (ret != ENOENT) { + DEBUG(SSSDBG_CRIT_FAILURE, "Error looking up the files domain\n"); + return ret; + } + + /* ENOENT, so let's add a files domain */ + ret = create_files_domain(cdb, implicit_files_dom_name); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot add an implicit files domain\n"); + return ret; + } + + return activate_files_domain(cdb, implicit_files_dom_name); +} +#endif + +static int confdb_get_parent_domain(TALLOC_CTX *mem_ctx, + const char *name, + struct confdb_ctx *cdb, + struct ldb_result *app_dom, + struct ldb_result **_parent_dom) +{ + const char *inherit_from; + + inherit_from = ldb_msg_find_attr_as_string(app_dom->msgs[0], + CONFDB_DOMAIN_INHERIT_FROM, NULL); + if (inherit_from == NULL) { + DEBUG(SSSDBG_CONF_SETTINGS, + "%s does not inherit from any POSIX domain\n", name); + *_parent_dom = NULL; + return EOK; + } + + return confdb_get_domain_section(mem_ctx, cdb, + CONFDB_DOMAIN_BASEDN, inherit_from, + _parent_dom); +} + +static int confdb_add_app_domain(TALLOC_CTX *mem_ctx, + struct confdb_ctx *cdb, + const char *name) +{ + char *cdb_path = NULL; + const char *val[2] = { NULL, NULL }; + int ret; + + cdb_path = talloc_asprintf(mem_ctx, CONFDB_DOMAIN_PATH_TMPL, name); + if (cdb_path == NULL) { + return ENOMEM; + } + + val[0] = CONFDB_DOMAIN_TYPE_APP; + ret = confdb_add_param(cdb, true, cdb_path, CONFDB_DOMAIN_TYPE, val); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add id_provider [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; + } + + return EOK; +} + +static int confdb_merge_parent_domain(const char *name, + struct confdb_ctx *cdb, + struct ldb_result *app_section) +{ + int ret; + int ldb_flag; + struct ldb_result *parent_domain = NULL; + struct ldb_message *replace_msg = NULL; + struct ldb_message *app_msg = NULL; + struct ldb_dn *domain_dn; + struct ldb_message_element *el = NULL; + TALLOC_CTX *tmp_ctx = NULL; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n"); + return ENOMEM; + } + + domain_dn = ldb_dn_new_fmt(tmp_ctx, + cdb->ldb, + "%s=%s,%s", + CONFDB_DOMAIN_ATTR, + name, + CONFDB_DOMAIN_BASEDN); + if (domain_dn == NULL) { + ret = ENOMEM; + goto done; + } + + /* Copy the parent domain parameters */ + ret = confdb_get_parent_domain(tmp_ctx, name, cdb, + app_section, &parent_domain); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot retrieve the parent domain [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + if (parent_domain != NULL) { + replace_msg = ldb_msg_copy(tmp_ctx, parent_domain->msgs[0]); + if (replace_msg == NULL) { + ret = ENOMEM; + goto done; + } + replace_msg->dn = domain_dn; + + for (unsigned i = 0; i < replace_msg->num_elements; i++) { + replace_msg->elements[i].flags = LDB_FLAG_MOD_ADD; + } + + el = ldb_msg_find_element(replace_msg, "cn"); + if (el != NULL) { + /* Don't add second cn */ + ldb_msg_remove_element(replace_msg, el); + } + + ret = ldb_modify(cdb->ldb, replace_msg); + if (ret != LDB_SUCCESS) { + ret = sss_ldb_error_to_errno(ret); + DEBUG(SSSDBG_OP_FAILURE, + "Inheriting options from parent domain failed [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + } + + /* Finally, add any app-domain specific overrides */ + app_msg = ldb_msg_new(tmp_ctx); + if (app_msg == NULL) { + ret = ENOMEM; + goto done; + } + app_msg->dn = domain_dn; + + for (unsigned i = 0; i < app_section->msgs[0]->num_elements; i++) { + struct ldb_message_element *app_el = &app_section->msgs[0]->elements[i]; + + /* These elements will be skipped when replacing attributes in + * a domain to avoid EEXIST errors + */ + if (strcasecmp(app_el->name, "cn") == 0) { + continue; + } + + if (replace_msg != NULL) { + el = ldb_msg_find_element(replace_msg, + app_section->msgs[0]->elements[i].name); + if (el == NULL) { + /* Adding an element */ + ldb_flag = LDB_FLAG_MOD_ADD; + } else { + /* Overriding an element */ + ldb_flag = LDB_FLAG_MOD_REPLACE; + } + } else { + /* If there was no domain to inherit from, just add all */ + ldb_flag = LDB_FLAG_MOD_ADD; + } + + ret = ldb_msg_add(app_msg, + &app_section->msgs[0]->elements[i], + ldb_flag); + if (ret != LDB_SUCCESS) { + continue; + } + } + + /* We use permissive modification here because adding cn or + * distinguishedName from the app_section to the application + * message would throw EEXIST + */ + ret = sss_ldb_modify_permissive(cdb->ldb, app_msg); + if (ret != LDB_SUCCESS) { + ret = sss_ldb_error_to_errno(ret); + DEBUG(SSSDBG_OP_FAILURE, + "Adding app-specific options failed [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + DEBUG(SSSDBG_TRACE_LIBS, "Added a domain section for %s\n", name); + ret = EOK; +done: + talloc_free(tmp_ctx); + return ret; +} + +int confdb_expand_app_domains(struct confdb_ctx *cdb) +{ + int ret; + char **domlist; + TALLOC_CTX *tmp_ctx; + struct ldb_result *app_domain = NULL; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + +#ifdef BUILD_FILES_PROVIDER + ret = confdb_ensure_files_domain(cdb, IMPLICIT_FILES_DOMAIN_NAME); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot add the implicit files domain [%d]: %s\n", + ret, strerror(ret)); + /* Not fatal */ + } +#endif + + ret = confdb_get_enabled_domain_list(cdb, tmp_ctx, &domlist); + if (ret == ENOENT) { + DEBUG(SSSDBG_FATAL_FAILURE, "No domains configured, fatal error!\n"); + ret = ERR_NO_DOMAIN_ENABLED; + goto done; + } else if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Fatal error retrieving domains list!\n"); + goto done; + } + + for (int i = 0; domlist[i]; i++) { + ret = confdb_get_domain_section(tmp_ctx, cdb, + CONFDB_APP_DOMAIN_BASEDN, domlist[i], + &app_domain); + if (ret == ENOENT) { + DEBUG(SSSDBG_TRACE_INTERNAL, + "%s is not an app domain\n", domlist[i]); + continue; + } else if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Error %d: %s while retrieving %s\n", + ret, sss_strerror(ret), domlist[i]); + goto done; + } + + ret = confdb_add_app_domain(tmp_ctx, cdb, domlist[i]); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot add the app domain section [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + ret = confdb_merge_parent_domain(domlist[i], cdb, app_domain); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot add options into the app domain section [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + } + + ret = EOK; +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t certmap_local_check(struct ldb_message *msg) +{ + const char *rule_name; + const char *tmp_str; + int ret; + + rule_name = ldb_msg_find_attr_as_string(msg, CONFDB_CERTMAP_NAME, NULL); + if (rule_name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Certificate mapping rule [%s] has no name.", + ldb_dn_get_linearized(msg->dn)); + return EINVAL; + } + + tmp_str = ldb_msg_find_attr_as_string(msg, CONFDB_CERTMAP_DOMAINS, NULL); + if (tmp_str != NULL) { + DEBUG(SSSDBG_CONF_SETTINGS, + "Option [%s] is ignored for local certmap rules.\n", + CONFDB_CERTMAP_DOMAINS); + } + + tmp_str = ldb_msg_find_attr_as_string(msg, CONFDB_CERTMAP_MAPRULE, NULL); + if (tmp_str != NULL) { + if (tmp_str[0] != '(' || tmp_str[strlen(tmp_str) - 1] != ')') { + DEBUG(SSSDBG_CONF_SETTINGS, + "Mapping rule must be in braces (...).\n"); + return EINVAL; + } + DEBUG(SSSDBG_TRACE_ALL, "Using [%s] mapping rule of [%s].\n", + tmp_str, ldb_dn_get_linearized(msg->dn)); + return EOK; + } + + tmp_str = talloc_asprintf(msg, "(%s)", rule_name); + if (tmp_str == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n"); + return ENOMEM; + } + ret = ldb_msg_add_string(msg, CONFDB_CERTMAP_MAPRULE, tmp_str); + if (ret != LDB_SUCCESS) { + talloc_free(discard_const(tmp_str)); + DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_string failed.\n"); + return EIO; + } + + DEBUG(SSSDBG_TRACE_ALL, "Using [%s] as mapping rule for [%s].\n", + tmp_str, ldb_dn_get_linearized(msg->dn)); + + return EOK; +} + +static errno_t confdb_get_all_certmaps(TALLOC_CTX *mem_ctx, + struct confdb_ctx *cdb, + struct sss_domain_info *dom, + bool certmaps_for_local_users, + struct certmap_info ***_certmap_list) +{ + TALLOC_CTX *tmp_ctx = NULL; + struct ldb_dn *dn = NULL; + struct ldb_result *res = NULL; + /* The attribute order is important, because it is used in + * sysdb_ldb_msg_attr_to_certmap_info and must match + * enum certmap_info_member. */ + static const char *attrs[] = { CONFDB_CERTMAP_NAME, + CONFDB_CERTMAP_MAPRULE, + CONFDB_CERTMAP_MATCHRULE, + CONFDB_CERTMAP_PRIORITY, + CONFDB_CERTMAP_DOMAINS, + NULL}; + struct certmap_info **certmap_list = NULL; + size_t c; + int ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + dn = ldb_dn_new_fmt(tmp_ctx, cdb->ldb, "cn=%s,%s", dom->name, + CONFDB_CERTMAP_BASEDN); + if (dn == NULL) { + ret = ENOMEM; + goto done; + } + + ret = ldb_search(cdb->ldb, tmp_ctx, &res, dn, LDB_SCOPE_ONELEVEL, + attrs, NULL); + if (ret != LDB_SUCCESS) { + ret = EIO; + goto done; + } + + certmap_list = talloc_zero_array(tmp_ctx, struct certmap_info *, + res->count + 1); + if (certmap_list == NULL) { + ret = ENOMEM; + goto done; + } + + for (c = 0; c < res->count; c++) { + if (certmaps_for_local_users) { + ret = certmap_local_check(res->msgs[c]); + if (ret != EOK) { + DEBUG(SSSDBG_CONF_SETTINGS, + "Invalid certificate mapping [%s] for local user, " + "ignored.\n", ldb_dn_get_linearized(res->msgs[c]->dn)); + continue; + } + } + ret = sysdb_ldb_msg_attr_to_certmap_info(certmap_list, res->msgs[c], + attrs, &certmap_list[c]); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "sysdb_ldb_msg_attr_to_certmap_info failed.\n"); + goto done; + } + } + + *_certmap_list = talloc_steal(mem_ctx, certmap_list); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +int confdb_certmap_to_sysdb(struct confdb_ctx *cdb, + struct sss_domain_info *dom, + bool certmaps_for_local_users) +{ + int ret; + TALLOC_CTX *tmp_ctx; + struct certmap_info **certmap_list; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n"); + return ENOMEM; + } + + ret = confdb_get_all_certmaps(tmp_ctx, cdb, dom, certmaps_for_local_users, + &certmap_list); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "confdb_get_all_certmaps failed.\n"); + goto done; + } + + ret = sysdb_update_certmap(dom->sysdb, certmap_list, false /* TODO */); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sysdb_update_certmap failed.\n"); + goto done; + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + + return ret; +} + +int confdb_get_enabled_domain_list(struct confdb_ctx *cdb, + TALLOC_CTX *ctx, char ***_result) +{ + int ret; + char **domlist = NULL; + char **all_domains = NULL; + bool enabled = false; + + TALLOC_CTX *tmp_ctx = NULL; + + tmp_ctx = talloc_new(NULL); + ret = confdb_get_string_as_list(cdb, tmp_ctx, + CONFDB_MONITOR_CONF_ENTRY, + CONFDB_MONITOR_ACTIVE_DOMAINS, + &domlist); + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to get [%s] from [%s], error [%d] (%s)\n", + CONFDB_MONITOR_ACTIVE_DOMAINS, "sssd", + ret, strerror(ret)); + goto done; + } + + ret = confdb_list_all_domain_names(tmp_ctx, cdb, &all_domains); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed retrieving all domain name " + "list, error [%d], description '%s'\n", + ret, strerror(ret)); + goto done; + } + + for (int idx = 0; all_domains[idx]; idx++) { + ret = confdb_get_domain_enabled(cdb, all_domains[idx], &enabled); + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed retrieving 'enabled' attribute from '%s' domain; " + "error [%d], description '%s'\n", + all_domains[idx], + ret, strerror(ret)); + goto done; + } + + if (ret == ENOENT) continue; + + if (enabled && !string_in_list(all_domains[idx], domlist, false)) { + ret = add_string_to_list(tmp_ctx, all_domains[idx], &domlist); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed adding '%s' domain to domain list; " + "error [%d], description '%s'\n", + all_domains[idx], + ret, strerror(ret)); + goto done; + } + } + + if (!enabled && string_in_list(all_domains[idx], domlist, false)) { + ret = del_string_from_list(all_domains[idx], &domlist, false); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed deleting '%s' domain from domain list; " + "error [%d], description '%s'\n", + all_domains[idx], + ret, strerror(ret)); + goto done; + } + } + } + + if (domlist == NULL || domlist[0] == NULL) { + ret = ENOENT; + goto done; + } + + ret = EOK; + talloc_steal(ctx, domlist); + *_result = domlist; + +done: + talloc_free(tmp_ctx); + return ret; +} + + +/** + * Retrieve the enabled attribute for a specific domain. + * @param cdb The database configuration context. + * @param domain The domain name. + * @param enabled The output variable; it can not be NULL; if the + * domain is explicitely enabled, *enabled is equal to 1; if the + * domain is explicitely disabled, *enabled is equal to 0. + * @param Return EOK if the operation happened properly and *enabled + * contain the value of the attribute; if no entry found for enabled + * attribute it returns ENOENT, else an error code. + */ +static int confdb_get_domain_enabled(struct confdb_ctx *cdb, + const char *domain, bool *_enabled) +{ + TALLOC_CTX *tmp_ctx = NULL; + char *section = NULL; + char **values = NULL; + int ret = EINVAL; + + section = talloc_asprintf(tmp_ctx, CONFDB_DOMAIN_PATH_TMPL, domain); + values = NULL; + ret = confdb_get_param(cdb, tmp_ctx, section, CONFDB_DOMAIN_ENABLED, &values); + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed retrieving '%s' attribute in '%s' section; " + "error [%d], description '%s'\n", + CONFDB_DOMAIN_ENABLED, section, + ret, strerror(ret)); + goto done; + } + + /* Check return and output value */ + if (ret == ENOENT || !values || !values[0]) { + ret = ENOENT; + goto done; + } + if (values[1]) { + /* More than one value it's an invalid configuration file */ + ret = EINVAL; + goto done; + } + if (0 == strcasecmp(values[0], "true")) { + ret = EOK; + *_enabled = true; + goto done; + } + if (0 == strcasecmp(values[0], "false")) { + ret = EOK; + *_enabled = false; + goto done; + } + /* Failed to parse value */ + ret = EINVAL; +done: + talloc_free(tmp_ctx); + return ret; +} diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h new file mode 100644 index 0000000..4487d78 --- /dev/null +++ b/src/confdb/confdb.h @@ -0,0 +1,785 @@ +/* + SSSD + + SSSD Configuration DB + + 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 _CONF_DB_H +#define _CONF_DB_H + +#include <stdbool.h> +#include <talloc.h> +#include <tevent.h> +#include <ldb.h> +#include <ldb_errors.h> + +#include "config.h" + +/** + * @defgroup sss_confdb The ConfDB API + * The ConfDB is an interface for data providers to + * access the configuration information provided in + * the sssd.conf + * @{ + */ + +#define CONFDB_DEFAULT_CFG_FILE_VER 2 +#define CONFDB_FILE "config.ldb" +#define SSSD_CONFIG_FILE_NAME "sssd.conf" +#define SSSD_CONFIG_FILE SSSD_CONF_DIR"/"SSSD_CONFIG_FILE_NAME +#define CONFDB_DEFAULT_CONFIG_DIR_NAME "conf.d" +#define CONFDB_DEFAULT_CONFIG_DIR SSSD_CONF_DIR"/"CONFDB_DEFAULT_CONFIG_DIR_NAME +#define SSSD_MIN_ID 1 +#define CONFDB_DEFAULT_SHELL_FALLBACK "/bin/sh" +#define CONFDB_FALLBACK_CONFIG \ + "[sssd]\n" \ + "services = nss\n" + + +/* Configuration options */ + +/* Services */ +#define CONFDB_SERVICE_PATH_TMPL "config/%s" +#define CONFDB_SERVICE_COMMAND "command" +#define CONFDB_SERVICE_DEBUG_LEVEL "debug_level" +#define CONFDB_SERVICE_DEBUG_LEVEL_ALIAS "debug" +#define CONFDB_SERVICE_DEBUG_TIMESTAMPS "debug_timestamps" +#define CONFDB_SERVICE_DEBUG_MICROSECONDS "debug_microseconds" +#define CONFDB_SERVICE_DEBUG_BACKTRACE_ENABLED "debug_backtrace_enabled" +#define CONFDB_SERVICE_RECON_RETRIES "reconnection_retries" +#define CONFDB_SERVICE_FD_LIMIT "fd_limit" +#define CONFDB_SERVICE_ALLOWED_UIDS "allowed_uids" + +/* Monitor */ +#define CONFDB_MONITOR_CONF_ENTRY "config/sssd" +#define CONFDB_MONITOR_ACTIVE_SERVICES "services" +#define CONFDB_MONITOR_ACTIVE_DOMAINS "domains" +#define CONFDB_MONITOR_RESOLV_CONF "monitor_resolv_conf" +#define CONFDB_MONITOR_TRY_INOTIFY "try_inotify" +#define CONFDB_MONITOR_KRB5_RCACHEDIR "krb5_rcache_dir" +#define CONFDB_MONITOR_DEFAULT_DOMAIN "default_domain_suffix" +#define CONFDB_MONITOR_OVERRIDE_SPACE "override_space" +#ifdef SSSD_NON_ROOT_USER +#define CONFDB_MONITOR_USER_RUNAS "user" +#endif +#define CONFDB_MONITOR_CERT_VERIFICATION "certificate_verification" +#define CONFDB_MONITOR_DISABLE_NETLINK "disable_netlink" +#define CONFDB_MONITOR_ENABLE_FILES_DOM "enable_files_domain" +#define CONFDB_MONITOR_DOMAIN_RESOLUTION_ORDER "domain_resolution_order" +#define CONFDB_MONITOR_IMPLICIT_PAC_RESPONDER "implicit_pac_responder" +#define CONFDB_MONITOR_DUMPABLE "core_dumpable" +#define CONFDB_MONITOR_PASSKEY_VERIFICATION "passkey_verification" + +/* Both monitor and domains */ +#define CONFDB_NAME_REGEX "re_expression" +#define CONFDB_FULL_NAME_FORMAT "full_name_format" +#define CONFDB_DEFAULT_FULL_NAME_FORMAT_INTERNAL "%1$s@%2$s%3$s" +#define CONFDB_DEFAULT_FULL_NAME_FORMAT "%1$s@%2$s" + +/* Responders */ +#define CONFDB_RESPONDER_GET_DOMAINS_TIMEOUT "get_domains_timeout" +#define CONFDB_RESPONDER_CLI_IDLE_TIMEOUT "client_idle_timeout" +#define CONFDB_RESPONDER_CLI_IDLE_DEFAULT_TIMEOUT 60 +#define CONFDB_RESPONDER_LOCAL_NEG_TIMEOUT "local_negative_timeout" +#define CONFDB_RESPONDER_LOCAL_NEG_TIMEOUT_DEFAULT 14400 +#define CONFDB_RESPONDER_IDLE_TIMEOUT "responder_idle_timeout" +#define CONFDB_RESPONDER_IDLE_DEFAULT_TIMEOUT 300 +#define CONFDB_RESPONDER_CACHE_FIRST "cache_first" +#ifdef BUILD_FILES_PROVIDER +/* There is a subtile issue with this option when 'files' + another domain is enabled */ +#define CONFDB_RESPONDER_CACHE_FIRST_DEFAILT false +#else +#define CONFDB_RESPONDER_CACHE_FIRST_DEFAILT true +#endif + +/* NSS */ +#define CONFDB_NSS_CONF_ENTRY "config/nss" +#define CONFDB_NSS_ENUM_CACHE_TIMEOUT "enum_cache_timeout" +#define CONFDB_NSS_ENTRY_CACHE_NOWAIT_PERCENTAGE "entry_cache_nowait_percentage" +#define CONFDB_NSS_ENTRY_NEG_TIMEOUT "entry_negative_timeout" +#define CONFDB_NSS_FILTER_USERS_IN_GROUPS "filter_users_in_groups" +#define CONFDB_NSS_FILTER_USERS "filter_users" +#define CONFDB_NSS_FILTER_GROUPS "filter_groups" +#define CONFDB_NSS_PWFIELD "pwfield" +#define CONFDB_NSS_OVERRIDE_HOMEDIR "override_homedir" +#define CONFDB_NSS_FALLBACK_HOMEDIR "fallback_homedir" +#define CONFDB_NSS_OVERRIDE_SHELL "override_shell" +#define CONFDB_NSS_VETOED_SHELL "vetoed_shells" +#define CONFDB_NSS_ALLOWED_SHELL "allowed_shells" +#define CONFDB_NSS_SHELL_FALLBACK "shell_fallback" +#define CONFDB_NSS_DEFAULT_SHELL "default_shell" +#define CONFDB_MEMCACHE_TIMEOUT "memcache_timeout" +#define CONFDB_NSS_MEMCACHE_SIZE_PASSWD "memcache_size_passwd" +#define CONFDB_NSS_MEMCACHE_SIZE_GROUP "memcache_size_group" +#define CONFDB_NSS_MEMCACHE_SIZE_INITGROUPS "memcache_size_initgroups" +#define CONFDB_NSS_MEMCACHE_SIZE_SID "memcache_size_sid" +#define CONFDB_NSS_HOMEDIR_SUBSTRING "homedir_substring" +#define CONFDB_DEFAULT_HOMEDIR_SUBSTRING "/home" + +/* PAM */ +#define CONFDB_PAM_CONF_ENTRY "config/pam" +#define CONFDB_PAM_CRED_TIMEOUT "offline_credentials_expiration" +#define CONFDB_PAM_FAILED_LOGIN_ATTEMPTS "offline_failed_login_attempts" +#define CONFDB_DEFAULT_PAM_FAILED_LOGIN_ATTEMPTS 0 +#define CONFDB_PAM_FAILED_LOGIN_DELAY "offline_failed_login_delay" +#define CONFDB_DEFAULT_PAM_FAILED_LOGIN_DELAY 5 +#define CONFDB_PAM_VERBOSITY "pam_verbosity" +#define CONFDB_PAM_RESPONSE_FILTER "pam_response_filter" +#define CONFDB_PAM_ID_TIMEOUT "pam_id_timeout" +#define CONFDB_PAM_PWD_EXPIRATION_WARNING "pam_pwd_expiration_warning" +#define CONFDB_PAM_TRUSTED_USERS "pam_trusted_users" +#define CONFDB_PAM_PUBLIC_DOMAINS "pam_public_domains" +#define CONFDB_PAM_ACCOUNT_EXPIRED_MESSAGE "pam_account_expired_message" +#define CONFDB_PAM_ACCOUNT_LOCKED_MESSAGE "pam_account_locked_message" +#define CONFDB_PAM_CERT_AUTH "pam_cert_auth" +#define CONFDB_PAM_CERT_DB_PATH "pam_cert_db_path" +#define CONFDB_PAM_CERT_VERIFICATION "pam_cert_verification" +#define CONFDB_PAM_P11_CHILD_TIMEOUT "p11_child_timeout" +#define CONFDB_PAM_WAIT_FOR_CARD_TIMEOUT "p11_wait_for_card_timeout" +#define CONFDB_PAM_APP_SERVICES "pam_app_services" +#define CONFDB_PAM_P11_ALLOWED_SERVICES "pam_p11_allowed_services" +#define CONFDB_PAM_P11_URI "p11_uri" +#define CONFDB_PAM_INITGROUPS_SCHEME "pam_initgroups_scheme" +#define CONFDB_PAM_GSSAPI_SERVICES "pam_gssapi_services" +#define CONFDB_PAM_GSSAPI_CHECK_UPN "pam_gssapi_check_upn" +#define CONFDB_PAM_GSSAPI_INDICATORS_MAP "pam_gssapi_indicators_map" +#define CONFDB_PAM_PASSKEY_AUTH "pam_passkey_auth" +#define CONFDB_PAM_PASSKEY_CHILD_TIMEOUT "passkey_child_timeout" +#define CONFDB_PAM_PASSKEY_DEBUG_LIBFIDO2 "passkey_debug_libfido2" + +/* SUDO */ +#define CONFDB_SUDO_CONF_ENTRY "config/sudo" +#define CONFDB_SUDO_CACHE_TIMEOUT "sudo_cache_timeout" +#define CONFDB_DEFAULT_SUDO_CACHE_TIMEOUT 180 +#define CONFDB_SUDO_TIMED "sudo_timed" +#define CONFDB_DEFAULT_SUDO_TIMED false +#define CONFDB_SUDO_INVERSE_ORDER "sudo_inverse_order" +#define CONFDB_DEFAULT_SUDO_INVERSE_ORDER false +#define CONFDB_SUDO_THRESHOLD "sudo_threshold" +#define CONFDB_DEFAULT_SUDO_THRESHOLD 50 + +/* autofs */ +#define CONFDB_AUTOFS_CONF_ENTRY "config/autofs" +#define CONFDB_AUTOFS_MAP_NEG_TIMEOUT "autofs_negative_timeout" + +/* SSH */ +#define CONFDB_SSH_CONF_ENTRY "config/ssh" +#define CONFDB_SSH_HASH_KNOWN_HOSTS "ssh_hash_known_hosts" +#define CONFDB_DEFAULT_SSH_HASH_KNOWN_HOSTS false +#define CONFDB_SSH_KNOWN_HOSTS_TIMEOUT "ssh_known_hosts_timeout" +#define CONFDB_DEFAULT_SSH_KNOWN_HOSTS_TIMEOUT 180 +#define CONFDB_SSH_CA_DB "ca_db" +#define CONFDB_DEFAULT_SSH_CA_DB SYSCONFDIR"/sssd/pki/sssd_auth_ca_db.pem" +#define CONFDB_SSH_USE_CERT_KEYS "ssh_use_certificate_keys" +#define CONFDB_DEFAULT_SSH_USE_CERT_KEYS true +#define CONFDB_SSH_USE_CERT_RULES "ssh_use_certificate_matching_rules" + +/* PAC */ +#define CONFDB_PAC_CONF_ENTRY "config/pac" +#define CONFDB_PAC_LIFETIME "pac_lifetime" +#define CONFDB_PAC_CHECK "pac_check" +#define CONFDB_PAC_CHECK_DEFAULT "no_check" +#define CONFDB_PAC_CHECK_IPA_AD_DEFAULT "check_upn, check_upn_allow_missing, check_upn_dns_info_ex" + +/* InfoPipe */ +#define CONFDB_IFP_CONF_ENTRY "config/ifp" +#define CONFDB_IFP_USER_ATTR_LIST "user_attributes" +#define CONFDB_IFP_WILDCARD_LIMIT "wildcard_limit" + +/* Session Recording */ +#define CONFDB_SESSION_RECORDING_CONF_ENTRY "config/session_recording" +#define CONFDB_SESSION_RECORDING_SCOPE "scope" +#define CONFDB_SESSION_RECORDING_USERS "users" +#define CONFDB_SESSION_RECORDING_GROUPS "groups" +#define CONFDB_SESSION_RECORDING_EXCLUDE_USERS "exclude_users" +#define CONFDB_SESSION_RECORDING_EXCLUDE_GROUPS "exclude_groups" + +/* Domains */ +#define CONFDB_DOMAIN_ENABLED "enabled" +#define CONFDB_DOMAIN_PATH_TMPL "config/domain/%s" +#define CONFDB_DOMAIN_BASEDN "cn=domain,cn=config" +#define CONFDB_APP_DOMAIN_BASEDN "cn=application,cn=config" +#define CONFDB_DOMAIN_ID_PROVIDER "id_provider" +#define CONFDB_DOMAIN_AUTH_PROVIDER "auth_provider" +#define CONFDB_DOMAIN_ACCESS_PROVIDER "access_provider" +#define CONFDB_DOMAIN_CHPASS_PROVIDER "chpass_provider" +#define CONFDB_DOMAIN_SUDO_PROVIDER "sudo_provider" +#define CONFDB_DOMAIN_AUTOFS_PROVIDER "autofs_provider" +#define CONFDB_DOMAIN_SELINUX_PROVIDER "selinux_provider" +#define CONFDB_DOMAIN_HOSTID_PROVIDER "hostid_provider" +#define CONFDB_DOMAIN_SUBDOMAINS_PROVIDER "subdomains_provider" +#define CONFDB_DOMAIN_SESSION_PROVIDER "session_provider" +#define CONFDB_DOMAIN_RESOLVER_PROVIDER "resolver_provider" +#define CONFDB_DOMAIN_COMMAND "command" +#define CONFDB_DOMAIN_TIMEOUT "timeout" +#define CONFDB_DOMAIN_ATTR "cn" +#define CONFDB_DOMAIN_ENUMERATE "enumerate" +#define CONFDB_SUBDOMAIN_ENUMERATE "subdomain_enumerate" +#define CONFDB_DEFAULT_SUBDOMAIN_ENUMERATE "none" +#define CONFDB_DOMAIN_MINID "min_id" +#define CONFDB_DOMAIN_MAXID "max_id" +#define CONFDB_DOMAIN_CACHE_CREDS "cache_credentials" +#define CONFDB_DOMAIN_CACHE_CREDS_MIN_FF_LENGTH \ + "cache_credentials_minimal_first_factor_length" +#define CONFDB_DEFAULT_CACHE_CREDS_MIN_FF_LENGTH 8 +#define CONFDB_DOMAIN_AUTO_UPG "auto_private_groups" +#define CONFDB_DOMAIN_FQ "use_fully_qualified_names" +#define CONFDB_DOMAIN_ENTRY_CACHE_TIMEOUT "entry_cache_timeout" +#define CONFDB_DOMAIN_ACCOUNT_CACHE_EXPIRATION "account_cache_expiration" +#define CONFDB_DOMAIN_OVERRIDE_GID "override_gid" +#define CONFDB_DOMAIN_CASE_SENSITIVE "case_sensitive" +#define CONFDB_DOMAIN_SUBDOMAIN_HOMEDIR "subdomain_homedir" +#define CONFDB_DOMAIN_DEFAULT_SUBDOMAIN_HOMEDIR "/home/%d/%u" +#define CONFDB_DOMAIN_IGNORE_GROUP_MEMBERS "ignore_group_members" +#define CONFDB_DOMAIN_SUBDOMAIN_REFRESH "subdomain_refresh_interval" +#define CONFDB_DOMAIN_SUBDOMAIN_REFRESH_DEFAULT_VALUE 14400 +#define CONFDB_DOMAIN_SUBDOMAIN_REFRESH_OFFSET "subdomain_refresh_interval_offset" +#define CONFDB_DOMAIN_SUBDOMAIN_REFRESH_OFFSET_DEFAULT_VALUE 300 + +#define CONFDB_DOMAIN_USER_CACHE_TIMEOUT "entry_cache_user_timeout" +#define CONFDB_DOMAIN_GROUP_CACHE_TIMEOUT "entry_cache_group_timeout" +#define CONFDB_DOMAIN_NETGROUP_CACHE_TIMEOUT "entry_cache_netgroup_timeout" +#define CONFDB_DOMAIN_SERVICE_CACHE_TIMEOUT "entry_cache_service_timeout" +#define CONFDB_DOMAIN_AUTOFS_CACHE_TIMEOUT "entry_cache_autofs_timeout" +#define CONFDB_DOMAIN_SUDO_CACHE_TIMEOUT "entry_cache_sudo_timeout" +#define CONFDB_DOMAIN_SSH_HOST_CACHE_TIMEOUT "entry_cache_ssh_host_timeout" +#define CONFDB_DOMAIN_COMPUTER_CACHE_TIMEOUT "entry_cache_computer_timeout" +#define CONFDB_DOMAIN_RESOLVER_CACHE_TIMEOUT "entry_cache_resolver_timeout" +#define CONFDB_DOMAIN_PWD_EXPIRATION_WARNING "pwd_expiration_warning" +#define CONFDB_DOMAIN_REFRESH_EXPIRED_INTERVAL "refresh_expired_interval" +#define CONFDB_DOMAIN_REFRESH_EXPIRED_INTERVAL_OFFSET "refresh_expired_interval_offset" +#define CONFDB_DOMAIN_OFFLINE_TIMEOUT "offline_timeout" +#define CONFDB_DOMAIN_OFFLINE_TIMEOUT_MAX "offline_timeout_max" +#define CONFDB_DOMAIN_OFFLINE_TIMEOUT_RANDOM_OFFSET "offline_timeout_random_offset" +#define CONFDB_DOMAIN_SUBDOMAIN_INHERIT "subdomain_inherit" +#define CONFDB_DOMAIN_CACHED_AUTH_TIMEOUT "cached_auth_timeout" +#define CONFDB_DOMAIN_TYPE "domain_type" +#define CONFDB_DOMAIN_TYPE_POSIX "posix" +#define CONFDB_DOMAIN_TYPE_APP "application" +#define CONFDB_DOMAIN_INHERIT_FROM "inherit_from" +#define CONFDB_DOMAIN_LOCAL_AUTH_POLICY "local_auth_policy" + +/* Proxy Provider */ +#define CONFDB_PROXY_LIBNAME "proxy_lib_name" +#define CONFDB_PROXY_RESOLVER_LIBNAME "proxy_resolver_lib_name" +#define CONFDB_PROXY_PAM_TARGET "proxy_pam_target" +#define CONFDB_PROXY_FAST_ALIAS "proxy_fast_alias" +#define CONFDB_PROXY_MAX_CHILDREN "proxy_max_children" + +#ifdef BUILD_FILES_PROVIDER +/* Files Provider */ +#define CONFDB_FILES_PASSWD "passwd_files" +#define CONFDB_FILES_GROUP "group_files" +#define CONFDB_DOMAIN_FALLBACK_TO_NSS "fallback_to_nss" +#endif + +/* KCM Service */ +#define CONFDB_KCM_CONF_ENTRY "config/kcm" +#define CONFDB_KCM_SOCKET "socket_path" +#define CONFDB_KCM_DB "ccache_storage" /* Undocumented on purpose */ +#define CONFDB_KCM_CONTAINERS_NEST_LEVEL "containers_nest_level" +#define CONFDB_KCM_MAX_CCACHES "max_ccaches" +#define CONFDB_KCM_MAX_UID_CCACHES "max_uid_ccaches" +#define CONFDB_KCM_MAX_CCACHE_SIZE "max_ccache_size" +#define CONFDB_KCM_TGT_RENEWAL "tgt_renewal" +#define CONFDB_KCM_TGT_RENEWAL_INHERIT "tgt_renewal_inherit" +#define CONFDB_KCM_KRB5_LIFETIME "krb5_lifetime" +#define CONFDB_KCM_KRB5_RENEWABLE_LIFETIME "krb5_renewable_lifetime" +#define CONFDB_KCM_KRB5_RENEW_INTERVAL "krb5_renew_interval" +#define CONFDB_KCM_KRB5_VALIDATE "krb5_validate" +#define CONFDB_KCM_KRB5_CANONICALIZE "krb5_canonicalize" +#define CONFDB_KCM_KRB5_AUTH_TIMEOUT "krb5_auth_timeout" + +/* Certificate mapping rules */ +#define CONFDB_CERTMAP_BASEDN "cn=certmap,cn=config" +#define CONFDB_CERTMAP_NAME "cn" +#define CONFDB_CERTMAP_MAPRULE "maprule" +#define CONFDB_CERTMAP_MATCHRULE "matchrule" +#define CONFDB_CERTMAP_DOMAINS "domains" +#define CONFDB_CERTMAP_PRIORITY "priority" + +/* Prompting */ +#define CONFDB_PC_CONF_ENTRY "config/prompting" +#define CONFDB_PC_TYPE_PASSWORD "password" +#define CONFDB_PC_PASSWORD_PROMPT "password_prompt" +#define CONFDB_PC_TYPE_2FA "2fa" +#define CONFDB_PC_2FA_SINGLE_PROMPT "single_prompt" +#define CONFDB_PC_2FA_1ST_PROMPT "first_prompt" +#define CONFDB_PC_2FA_2ND_PROMPT "second_prompt" +#define CONFDB_PC_TYPE_CERT_AUTH "cert_auth" +#define CONFDB_PC_TYPE_PASSKEY "passkey" +#define CONFDB_PC_PASSKEY_INTERACTIVE "interactive" +#define CONFDB_PC_PASSKEY_INTERACTIVE_PROMPT "interactive_prompt" +#define CONFDB_PC_PASSKEY_TOUCH "touch" +#define CONFDB_PC_PASSKEY_TOUCH_PROMPT "touch_prompt" + +struct confdb_ctx; + +/** sssd domain state */ +enum sss_domain_state { + /** Domain is usable by both responders and providers. This + * is the default state after creating a new domain + */ + DOM_ACTIVE, + /** Domain was removed, should not be used be neither responders + * not providers. + */ + DOM_DISABLED, + /** Domain cannot be contacted. Providers return an offline error code + * when receiving request for inactive domain, but responders should + * return cached data + */ + DOM_INACTIVE, + /** Domain is being updated. Responders should ignore cached data and + * always contact the DP + */ + DOM_INCONSISTENT, +}; + +/** Whether the domain only supports looking up POSIX entries */ +enum sss_domain_type { + /** This is the default domain type. It resolves only entries + * with the full POSIX set of attributes + */ + DOM_TYPE_POSIX, + /** In this mode, entries are typically resolved only by name */ + DOM_TYPE_APPLICATION, +}; + +enum sss_domain_mpg_mode { + MPG_DISABLED, + MPG_ENABLED, + MPG_HYBRID, + MPG_DEFAULT, /* Use default value for given id mapping. */ +}; + +/** + * Data structure storing all of the basic features + * of a domain. + */ +struct sss_domain_info { + enum sss_domain_type type; + + char *name; + char *conn_name; + char *provider; + int timeout; + bool enumerate; + char **sd_enumerate; + bool fqnames; + enum sss_domain_mpg_mode mpg_mode; + bool ignore_group_members; + uint32_t id_min; + uint32_t id_max; + const char *pwfield; + + bool cache_credentials; + uint32_t cache_credentials_min_ff_length; + bool case_sensitive; + bool case_preserve; + + gid_t override_gid; + const char *override_homedir; + const char *fallback_homedir; + const char *subdomain_homedir; + const char *homedir_substr; + const char *override_shell; + const char *default_shell; + + uint32_t user_timeout; + uint32_t group_timeout; + uint32_t netgroup_timeout; + uint32_t service_timeout; + uint32_t autofsmap_timeout; + uint32_t sudo_timeout; + uint32_t ssh_host_timeout; + uint32_t computer_timeout; + uint32_t resolver_timeout; + + uint32_t refresh_expired_interval; + uint32_t refresh_expired_interval_offset; + uint32_t subdomain_refresh_interval; + uint32_t subdomain_refresh_interval_offset; + uint32_t cached_auth_timeout; + + int pwd_expiration_warning; + + struct sysdb_ctx *sysdb; + struct sss_names_ctx *names; + + struct sss_domain_info *parent; + struct sss_domain_info *subdomains; + char *realm; + char *flat_name; + char *dns_name; + char *domain_id; + uint32_t trust_direction; + struct timeval subdomains_last_checked; + + bool has_views; + const char *view_name; + + struct sss_domain_info *prev; + struct sss_domain_info *next; + + enum sss_domain_state state; +#ifdef BUILD_FILES_PROVIDER + bool fallback_to_nss; +#endif + char **sd_inherit; + + /* Do not use the forest pointer directly in new code, but rather the + * forest_root pointer. sss_domain_info will be more opaque in the future + */ + char *forest; + struct sss_domain_info *forest_root; + const char **upn_suffixes; + + struct certmap_info **certmaps; + bool user_name_hint; + + /* Do not use the _output_fqnames property directly in new code, but rather + * use sss_domain_info_{get,set}_output_fqnames(). */ + bool output_fqnames; + + /* Hostname associated with this domain. */ + const char *hostname; + + /* Keytab used by this domain. */ + const char *krb5_keytab; + + /* List of PAM services that are allowed to authenticate with GSSAPI. */ + char **gssapi_services; + char *gssapi_check_upn; /* true | false | NULL */ + /* List of indicators associated with the specific PAM service */ + char **gssapi_indicators_map; + + /* Counts how often the domain was not found during a refresh of the + * domain list */ + size_t not_found_counter; +}; + +/** + * Initialize the connection to the ConfDB + * + * @param[in] mem_ctx The parent memory context for the confdb_ctx + * @param[out] cdb_ctx The newly-created connection object + * @param[in] confdb_location The absolute path to the ConfDB file on the + * filesystem + * + * @return 0 - Connection succeeded and cdb_ctx was populated + * @return ENOMEM - There was not enough memory to create the cdb_ctx + * @return EIO - There was an I/O error communicating with the ConfDB file + */ +int confdb_init(TALLOC_CTX *mem_ctx, + struct confdb_ctx **cdb_ctx, + const char *confdb_location); + +/** + * Get a domain object for the named domain + * + * @param[in] cdb The connection object to the confdb + * @param[in] name The name of the domain to retrieve + * @param[out] domain A pointer to a domain object for the domain given by + * name + * + * @return 0 - Lookup succeeded and domain was populated + * @return ENOMEM - There was insufficient memory to complete the operation + * @return ENOENT - The named domain does not exist or is not set active + */ +int confdb_get_domain(struct confdb_ctx *cdb, + const char *name, + struct sss_domain_info **domain); + +/** + * Get a null-terminated linked-list of active domain objects + * @param[in] cdb The connection object to the confdb + * @param[out] domains A pointer to the first entry of a linked-list of domain + * objects + * + * @return 0 - Lookup succeeded and all active domains are in the list + * @return ENOMEM - There was insufficient memory to complete the operation + * @return ENOENT - No active domains are configured + */ +int confdb_get_domains(struct confdb_ctx *cdb, + struct sss_domain_info **domains); + +/** + * Retrieve the list of enabled domains considering the explicit list + * and the 'enabled' attribute. + * @param cdb The database configuration context. + * @param ctx The memory context. + * @param result Output variable where the list of domains will be stored. + * @return 0 if the list was retrieved properly, ENOENT if no domain is enabled, another value on error. + */ +int confdb_get_enabled_domain_list(struct confdb_ctx *cdb, + TALLOC_CTX *ctx, char ***_result); + +int confdb_expand_app_domains(struct confdb_ctx *cdb); + +/** + * Get a null-terminated linked-list of all domain names + * @param[in] mem_ctx The parent memory context for the value list + * @param[in] cdb The connection object to the confdb + * @param[out] _names Output list + * + * @return 0 - Lookup succeeded and all domain names are in the list + * @return ENOMEM - There was insufficient memory to complete the operation + * @return ENOENT - No active domains are configured + * @return EIO - There was an I/O error communicating with the ConfDB file + * @return EINVAL - Corrupted confdb object + */ +int confdb_list_all_domain_names(TALLOC_CTX *mem_ctx, + struct confdb_ctx *cdb, + char ***_names); + + +/** + * @brief Add an arbitrary parameter to the confdb. + * + * This is mostly useful + * for testing, as they will not persist between SSSD restarts. For + * persistence, make changes to the sssd.conf file. + * + * @param[in] cdb The connection object to the confdb + * @param[in] replace If replace is set to true, pre-existing values will be + * overwritten. + * If it is false, the provided values will be added to the + * attribute. + * @param[in] section The ConfDB section to update. This is constructed from + * the format of the sssd.conf file. All sections start + * with 'config/'. Subsections are separated by slashes. + * e.g. [domain/LDAP] in sssd.conf would translate to + * config/domain/LDAP + * @param[in] attribute The name of the attribute to update + * @param[in] values A null-terminated array of values to add to the attribute + * + * @return 0 - Successfully added the provided value(s) + * @return ENOMEM - There was insufficient memory to complete the operation + * @return EINVAL - The section could not be parsed + * @return EIO - An I/O error occurred communicating with the ConfDB + */ +int confdb_add_param(struct confdb_ctx *cdb, + bool replace, + const char *section, + const char *attribute, + const char **values); + +/** + * @brief Retrieve all values for an attribute + * + * @param[in] cdb The connection object to the confdb + * @param[in] mem_ctx The parent memory context for the value list + * @param[in] section The ConfDB section to update. This is constructed from + * the format of the sssd.conf file. All sections start + * with 'config/'. Subsections are separated by slashes. + * e.g. [domain/LDAP] in sssd.conf would translate to + * config/domain/LDAP + * @param[in] attribute The name of the attribute to update + * @param[out] values A null-terminated array of cstrings containing all + * values for this attribute + * + * @return 0 - Successfully retrieved the value(s) + * @return ENOMEM - There was insufficient memory to complete the operation + * @return EINVAL - The section could not be parsed + * @return EIO - An I/O error occurred while communicating with the ConfDB + */ +int confdb_get_param(struct confdb_ctx *cdb, + TALLOC_CTX *mem_ctx, + const char *section, + const char *attribute, + char ***values); + +/** + * @brief Convenience function to retrieve a single-valued attribute as a + * string + * + * @param[in] cdb The connection object to the confdb + * @param[in] ctx The parent memory context for the returned string + * @param[in] section The ConfDB section to update. This is constructed from + * the format of the sssd.conf file. All sections start + * with 'config/'. Subsections are separated by slashes. + * e.g. [domain/LDAP] in sssd.conf would translate to + * config/domain/LDAP + * @param[in] attribute The name of the attribute to update + * @param[in] defstr If not NULL, the string to use if the attribute does not + * exist in the ConfDB + * @param[out] result A pointer to the retrieved (or default) string + * + * @return 0 - Successfully retrieved the entry (or used the default) + * @return ENOMEM - There was insufficient memory to complete the operation + * @return EINVAL - The section could not be parsed, or the attribute was not + * single-valued. + * @return EIO - An I/O error occurred while communicating with the ConfDB + */ +int confdb_get_string(struct confdb_ctx *cdb, TALLOC_CTX *ctx, + const char *section, const char *attribute, + const char *defstr, char **result); + +/** + * @brief Convenience function to retrieve a single-valued attribute as an + * integer + * + * @param[in] cdb The connection object to the confdb + * @param[in] section The ConfDB section to update. This is constructed from + * the format of the sssd.conf file. All sections start + * with 'config/'. Subsections are separated by slashes. + * e.g. [domain/LDAP] in sssd.conf would translate to + * config/domain/LDAP + * @param[in] attribute The name of the attribute to update + * @param[in] defval If not NULL, the integer to use if the attribute does not + * exist in the ConfDB + * @param[out] result A pointer to the retrieved (or default) integer + * + * @return 0 - Successfully retrieved the entry (or used the default) + * @return ENOMEM - There was insufficient memory to complete the operation + * @return EINVAL - The section could not be parsed, or the attribute was not + * single-valued. + * @return EIO - An I/O error occurred while communicating with the ConfDB + * @return ERANGE - The value stored in the ConfDB was outside the range + * [INT_MIN..INT_MAX] + */ +int confdb_get_int(struct confdb_ctx *cdb, + const char *section, const char *attribute, + int defval, int *result); + +/** + * @brief Convenience function to retrieve a single-valued attribute as a + * boolean + * + * This function will read (in a case-insensitive manner) a "true" or "false" + * value from the ConfDB and convert it to an integral bool value. + * + * @param[in] cdb The connection object to the confdb + * @param[in] section The ConfDB section to update. This is constructed from + * the format of the sssd.conf file. All sections start + * with 'config/'. Subsections are separated by slashes. + * e.g. [domain/LDAP] in sssd.conf would translate to + * config/domain/LDAP + * @param[in] attribute The name of the attribute to update + * @param[in] defval If not NULL, the boolean state to use if the attribute + * does not exist in the ConfDB + * @param[out] result A pointer to the retrieved (or default) bool + * + * @return 0 - Successfully retrieved the entry (or used the default) + * @return ENOMEM - There was insufficient memory to complete the operation + * @return EINVAL - The section could not be parsed, the attribute was not + * single-valued, or the value was not a boolean. + * @return EIO - An I/O error occurred while communicating with the ConfDB + */ +int confdb_get_bool(struct confdb_ctx *cdb, + const char *section, const char *attribute, + bool defval, bool *result); + +/** + * @brief Convenience function to set a single-valued attribute as a string + * + * @param[in] cdb The connection object to the confdb + * @param[in] section The ConfDB section to update. This is constructed from + * the format of the sssd.conf file. All sections start + * with 'config/'. Subsections are separated by slashes. + * e.g. [domain/LDAP] in sssd.conf would translate to + * config/domain/LDAP + * @param[in] attribute The name of the attribute to update + * @param[in] val New value of the attribute. + * + * @return 0 - Successfully retrieved the entry (or used the default) + * @return ENOMEM - There was insufficient memory to complete the operation + * @return EINVAL - The section could not be parsed + * @return EIO - An I/O error occurred while communicating with the ConfDB + */ +int confdb_set_string(struct confdb_ctx *cdb, + const char *section, + const char *attribute, + const char *val); + +/** + * @brief Convenience function to retrieve a single-valued attribute as a + * null-terminated array of strings + * + * This function will automatically split a comma-separated string in an + * attribute into a null-terminated array of strings. This is useful for + * storing and retrieving ordered lists, as ConfDB multivalued attributes do + * not guarantee retrieval order. + * + * @param[in] cdb The connection object to the confdb + * @param[in] ctx The parent memory context for the returned string + * @param[in] section The ConfDB section to update. This is constructed from + * the format of the sssd.conf file. All sections start + * with 'config/'. Subsections are separated by slashes. + * e.g. [domain/LDAP] in sssd.conf would translate to + * config/domain/LDAP + * @param[in] attribute The name of the attribute to update + * @param[out] result A pointer to the retrieved array of strings + * + * @return 0 - Successfully retrieved the entry (or used the default) + * @return ENOMEM - There was insufficient memory to complete the operation + * @return EINVAL - The section could not be parsed, or the attribute was not + * single-valued. + * @return ENOENT - The attribute was not found. + * @return EIO - An I/O error occurred while communicating with the ConfDB + */ +int confdb_get_string_as_list(struct confdb_ctx *cdb, TALLOC_CTX *ctx, + const char *section, const char *attribute, + char ***result); + +/** + * @brief Convenience function to retrieve a list of subsections given a + * configuration section name + * + * @param[in] mem_ctx The parent memory context for the returned list + * @param[in] cdb The connection object to the confdb + * @param[in] section The ConfDB section to look for. + * All sections should start with 'config/'. + * Subsections are separated by slashes. + * @param[out] sections Names of the subsections relative to the section + * requested. If "a/b" is requested then "c/d" is + * returned for the section named [a/b/c/d] + * @param[out] num_sections Number of section names returned + * + * @return 0 - Successfully retrieved the entry (or used the default) + * @return ENOMEM - There was insufficient memory to complete the operation + * @return EINVAL - The section could not be parsed. + * @return ENOENT - No section was found. + * @return EIO - An I/O error occurred while communicating with the ConfDB + */ +int confdb_get_sub_sections(TALLOC_CTX *mem_ctx, + struct confdb_ctx *cdb, + const char *section, + char ***sections, + int *num_sections); + +/** + * @brief Convenience function to write the certificate mapping and matching + * rules from the configuration database to the cache of a domain + * + * @param[in] cdb The connection object to the confdb + * @param[in] dom Target domain where to rules should be written to + * + * @return 0 - Successfully retrieved the entry (or used the default) + * @return ENOMEM - There was insufficient memory to complete the operation + * @return EINVAL - Typically internal processing error + */ +int confdb_certmap_to_sysdb(struct confdb_ctx *cdb, + struct sss_domain_info *dom, + bool certmaps_for_local_users); + +/** + * @} + */ +#endif diff --git a/src/confdb/confdb_private.h b/src/confdb/confdb_private.h new file mode 100644 index 0000000..1bab99c --- /dev/null +++ b/src/confdb/confdb_private.h @@ -0,0 +1,35 @@ +/* + SSSD + + Configuration Database + + Copyright (C) Stephen Gallagher <sgallagh@redhat.com> 2009 + + 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 CONFDB_PRIVATE_H_ +#define CONFDB_PRIVATE_H_ + +struct confdb_ctx { + struct tevent_context *pev; + struct ldb_context *ldb; + + struct sss_domain_info *doms; +}; + +int parse_section(TALLOC_CTX *mem_ctx, const char *section, + char **sec_dn, const char **rdn_name); + +#endif /* CONFDB_PRIVATE_H_ */ diff --git a/src/confdb/confdb_setup.c b/src/confdb/confdb_setup.c new file mode 100644 index 0000000..d33df1f --- /dev/null +++ b/src/confdb/confdb_setup.c @@ -0,0 +1,444 @@ +/* + SSSD + + Configuration Database + + Copyright (C) Stephen Gallagher <sgallagh@redhat.com> 2009 + + 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 <sys/stat.h> +#include "util/util.h" +#include "db/sysdb.h" +#include "confdb.h" +#include "confdb_private.h" +#include "confdb_setup.h" +#include "util/sss_ini.h" + +static int confdb_test(struct confdb_ctx *cdb) +{ + char **values; + int ret; + + ret = confdb_get_param(cdb, cdb, + "config", + "version", + &values); + if (ret != EOK) { + return ret; + } + + if (values[0] == NULL) { + /* empty database, will need to init */ + talloc_free(values); + return ENOENT; + } + + if (values[1] != NULL) { + /* more than 1 value?? */ + talloc_free(values); + return EIO; + } + + if (strcmp(values[0], CONFDB_VERSION) != 0) { + /* Existing version does not match executable version */ + DEBUG(SSSDBG_CRIT_FAILURE, "Upgrading confdb version from %s to %s\n", + values[0], CONFDB_VERSION); + + /* This is recoverable, since we purge the confdb file + * when we re-initialize it. + */ + talloc_free(values); + return ENOENT; + } + + talloc_free(values); + return EOK; +} + +static int confdb_purge(struct confdb_ctx *cdb) +{ + int ret; + unsigned int i; + TALLOC_CTX *tmp_ctx; + struct ldb_result *res; + struct ldb_dn *dn; + const char *attrs[] = { "dn", NULL }; + + tmp_ctx = talloc_new(NULL); + + dn = ldb_dn_new(tmp_ctx, cdb->ldb, "cn=config"); + + /* Get the list of all DNs */ + ret = ldb_search(cdb->ldb, tmp_ctx, &res, dn, + LDB_SCOPE_SUBTREE, attrs, NULL); + if (ret != LDB_SUCCESS) { + ret = sss_ldb_error_to_errno(ret); + goto done; + } + + for(i=0; i<res->count; i++) { + /* Delete this DN */ + ret = ldb_delete(cdb->ldb, res->msgs[i]->dn); + if (ret != LDB_SUCCESS) { + ret = sss_ldb_error_to_errno(ret); + goto done; + } + } + +done: + talloc_free(tmp_ctx); + return ret; +} + +static int confdb_create_base(struct confdb_ctx *cdb) +{ + int ret; + struct ldb_ldif *ldif; + + const char *base_ldif = CONFDB_BASE_LDIF; + + while ((ldif = ldb_ldif_read_string(cdb->ldb, &base_ldif))) { + ret = ldb_add(cdb->ldb, ldif->msg); + if (ret != LDB_SUCCESS) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Failed to initialize DB (%d,[%s]), aborting!\n", + ret, ldb_errstring(cdb->ldb)); + return EIO; + } + ldb_ldif_read_free(cdb->ldb, ldif); + } + + return EOK; +} + +static int confdb_ldif_from_ini_file(TALLOC_CTX *mem_ctx, + const char *config_file, + const char *config_dir, + const char *only_section, + struct sss_ini *init_data, + const char **_timestr, + const char **_ldif) +{ + errno_t ret; + char timestr[21] = "1"; + int version; + + ret = sss_ini_read_sssd_conf(init_data, + config_file, + config_dir); + if (ret != EOK) { + return ret; + } + + if (sss_ini_exists(init_data)) { + ret = sss_ini_get_stat(init_data); + if (ret != EOK) { + ret = errno; + DEBUG(SSSDBG_FATAL_FAILURE, + "Status check on config file failed.\n"); + return ret; + } + + errno = 0; + ret = sss_ini_get_mtime(init_data, sizeof(timestr), timestr); + if (ret <= 0 || ret >= (int)sizeof(timestr)) { + ret = errno ? errno : EFAULT; + DEBUG(SSSDBG_FATAL_FAILURE, + "Failed to convert time_t to string??\n"); + return ret; + } + } + + /* FIXME: Determine if the conf file or any snippet has changed + * since we last updated the confdb or if some snippet was + * added or removed. + */ + + ret = sss_ini_call_validators(init_data, + SSSDDATADIR"/cfg_rules.ini"); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to call validators\n"); + /* This is not fatal, continue */ + } + + /* Make sure that the config file version matches the confdb version */ + ret = sss_ini_get_cfgobj(init_data, "sssd", "config_file_version"); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Internal error determining config_file_version\n"); + return ret; + } + + ret = sss_ini_check_config_obj(init_data); + if (ret != EOK) { + /* No known version. Use default. */ + DEBUG(SSSDBG_CONF_SETTINGS, + "Value of config_file_version option not found. " + "Assumed to be version %d.\n", CONFDB_DEFAULT_CFG_FILE_VER); + } else { + version = sss_ini_get_int_config_value(init_data, + CONFDB_DEFAULT_CFG_FILE_VER, + -1, &ret); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Config file version could not be determined\n"); + return ret; + } else if (version < CONFDB_VERSION_INT) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Config file is an old version. " + "Please run configuration upgrade script.\n"); + return EINVAL; + } else if (version > CONFDB_VERSION_INT) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Config file version is newer than confdb\n"); + return EINVAL; + } + } + + ret = sss_confdb_create_ldif(mem_ctx, init_data, only_section, _ldif); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Could not create LDIF for confdb\n"); + return ret; + } + + *_timestr = talloc_strdup(mem_ctx, timestr); + if (*_timestr == NULL) { + return ENOMEM; + } + + return EOK; +} + +static int confdb_write_ldif(struct confdb_ctx *cdb, + const char *config_ldif, + bool replace_whole_db) +{ + int ret; + struct ldb_ldif *ldif; + + while ((ldif = ldb_ldif_read_string(cdb->ldb, &config_ldif))) { + if (ldif->changetype == LDB_CHANGETYPE_DELETE) { + /* We should remove this section */ + ret = ldb_delete(cdb->ldb, ldif->msg->dn); + if (ret == LDB_ERR_NO_SUCH_OBJECT) { + /* Removing a non-existing section is not an error */ + ret = LDB_SUCCESS; + } + } else { + ret = ldb_add(cdb->ldb, ldif->msg); + if (ret != LDB_SUCCESS && replace_whole_db == false) { + /* This section already existed, remove and re-add it. We + * really want to replace the whole thing instead of messing + * around with changetypes and flags on individual elements + */ + ret = ldb_delete(cdb->ldb, ldif->msg->dn); + if (ret == LDB_SUCCESS) { + ret = ldb_add(cdb->ldb, ldif->msg); + } + } + } + + if (ret != LDB_SUCCESS) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Failed to initialize DB (%d,[%s]), aborting!\n", + ret, ldb_errstring(cdb->ldb)); + return EIO; + } + ldb_ldif_read_free(cdb->ldb, ldif); + } + + return EOK; +} + +static int confdb_init_db(const char *config_file, + const char *config_dir, + const char *only_section, + struct confdb_ctx *cdb) +{ + TALLOC_CTX *tmp_ctx; + int ret; + int sret = EOK; + bool in_transaction = false; + const char *timestr = NULL; + const char *config_ldif; + const char *vals[2] = { NULL, NULL }; + struct sss_ini *init_data; + + tmp_ctx = talloc_new(cdb); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory.\n"); + return ENOMEM; + } + + init_data = sss_ini_new(tmp_ctx); + if (!init_data) { + DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory.\n"); + ret = ENOMEM; + goto done; + } + + ret = confdb_ldif_from_ini_file(tmp_ctx, + config_file, + config_dir, + only_section, + init_data, + ×tr, + &config_ldif); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Cannot convert INI to LDIF [%d]: [%s]\n", + ret, sss_strerror(ret)); + goto done; + } + + DEBUG(SSSDBG_CONF_SETTINGS, "LDIF file to import: \n%s\n", config_ldif); + + /* Set up a transaction to replace the configuration */ + ret = ldb_transaction_start(cdb->ldb); + if (ret != LDB_SUCCESS) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Failed to start a transaction for " + "updating the configuration\n"); + ret = sss_ldb_error_to_errno(ret); + goto done; + } + in_transaction = true; + + /* Purge existing database, if we are reinitializing the confdb completely */ + if (only_section == NULL) { + ret = confdb_purge(cdb); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Could not purge existing configuration\n"); + goto done; + } + } + + ret = confdb_write_ldif(cdb, + config_ldif, + only_section == NULL ? true : false); + if (ret != EOK) { + goto done; + } + + /* now store the lastUpdate time so that we do not re-init if nothing + * changed on restart */ + + vals[0] = timestr; + ret = confdb_add_param(cdb, true, "config", "lastUpdate", vals); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Failed to set last update time on db!\n"); + goto done; + } + + ret = ldb_transaction_commit(cdb->ldb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n"); + goto done; + } + in_transaction = false; + + ret = EOK; + +done: + if (in_transaction) { + sret = ldb_transaction_cancel(cdb->ldb); + if (sret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to cancel transaction\n"); + } + } + + talloc_zfree(tmp_ctx); + return ret; +} + +errno_t confdb_setup(TALLOC_CTX *mem_ctx, + const char *cdb_file, + const char *config_file, + const char *config_dir, + const char *only_section, + struct confdb_ctx **_cdb) +{ + TALLOC_CTX *tmp_ctx; + struct confdb_ctx *cdb; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n"); + return ENOMEM; + } + + ret = confdb_init(tmp_ctx, &cdb, cdb_file); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "The confdb initialization failed " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + /* Initialize the CDB from the configuration file */ + ret = confdb_test(cdb); + if (ret == ENOENT) { + /* First-time setup */ + + /* Purge any existing confdb in case an old + * misconfiguration gets in the way + */ + talloc_zfree(cdb); + ret = unlink(cdb_file); + if (ret != EOK && errno != ENOENT) { + ret = errno; + DEBUG(SSSDBG_MINOR_FAILURE, + "Purging existing confdb failed: %d [%s].\n", + ret, sss_strerror(ret)); + goto done; + } + + ret = confdb_init(tmp_ctx, &cdb, cdb_file); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "The confdb initialization failed " + "[%d]: %s\n", ret, sss_strerror(ret)); + } + + /* Load special entries */ + ret = confdb_create_base(cdb); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Unable to load special entries into confdb\n"); + goto done; + } + } else if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Fatal error initializing confdb\n"); + goto done; + } + + ret = confdb_init_db(config_file, config_dir, only_section, cdb); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "ConfDB initialization has failed " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + *_cdb = talloc_steal(mem_ctx, cdb); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} diff --git a/src/confdb/confdb_setup.h b/src/confdb/confdb_setup.h new file mode 100644 index 0000000..c7fe595 --- /dev/null +++ b/src/confdb/confdb_setup.h @@ -0,0 +1,55 @@ +/* + SSSD + + Configuration Database + + Copyright (C) Stephen Gallagher <sgallagh@redhat.com> 2009 + + 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 CONFDB_SETUP_H_ +#define CONFDB_SETUP_H_ + +#define CONFDB_VERSION "2" +#define CONFDB_VERSION_INT 2 + +#define CONFDB_BASE_LDIF \ + "dn: @ATTRIBUTES\n" \ + "cn: CASE_INSENSITIVE\n" \ + "dc: CASE_INSENSITIVE\n" \ + "dn: CASE_INSENSITIVE\n" \ + "name: CASE_INSENSITIVE\n" \ + "objectclass: CASE_INSENSITIVE\n" \ + "\n" \ + "dn: @INDEXLIST\n" \ + "@IDXATTR: cn\n" \ + "\n" \ + "dn: @MODULES\n" \ + "@LIST: server_sort\n" \ + "\n" + +#define CONFDB_INTERNAL_LDIF \ + "dn: cn=config\n" \ + "version: "CONFDB_VERSION"\n" \ + "\n" + +errno_t confdb_setup(TALLOC_CTX *mem_ctx, + const char *cdb_file, + const char *config_file, + const char *config_dir, + const char *only_section, + struct confdb_ctx **_cdb); + +#endif /* CONFDB_SETUP_H_ */ |