diff options
Diffstat (limited to 'src/providers/ipa/ipa_common.c')
-rw-r--r-- | src/providers/ipa/ipa_common.c | 1370 |
1 files changed, 1370 insertions, 0 deletions
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c new file mode 100644 index 0000000..01c835c --- /dev/null +++ b/src/providers/ipa/ipa_common.c @@ -0,0 +1,1370 @@ +/* + SSSD + + IPA Provider Common Functions + + Authors: + Simo Sorce <ssorce@redhat.com> + + Copyright (C) 2009 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <netdb.h> +#include <ctype.h> +#include <arpa/inet.h> +#include <ldb.h> + +#include "db/sysdb_selinux.h" +#include "providers/ipa/ipa_common.h" +#include "providers/ipa/ipa_dyndns.h" +#include "providers/ldap/sdap_async_private.h" +#include "providers/be_dyndns.h" +#include "util/sss_krb5.h" +#include "db/sysdb_services.h" +#include "db/sysdb_autofs.h" + +#include "providers/ipa/ipa_opts.h" +#include "providers/data_provider/dp_private.h" + +int ipa_get_options(TALLOC_CTX *memctx, + struct confdb_ctx *cdb, + const char *conf_path, + struct sss_domain_info *dom, + struct ipa_options **_opts) +{ + struct ipa_options *opts; + char *domain; + char *server; + char *realm; + char *ipa_hostname; + int ret; + char hostname[HOST_NAME_MAX + 1]; + + opts = talloc_zero(memctx, struct ipa_options); + if (!opts) return ENOMEM; + + ret = dp_get_options(opts, cdb, conf_path, + ipa_basic_opts, + IPA_OPTS_BASIC, + &opts->basic); + if (ret != EOK) { + goto done; + } + + domain = dp_opt_get_string(opts->basic, IPA_DOMAIN); + if (!domain) { + ret = dp_opt_set_string(opts->basic, IPA_DOMAIN, dom->name); + if (ret != EOK) { + goto done; + } + domain = dom->name; + } + + server = dp_opt_get_string(opts->basic, IPA_SERVER); + if (!server) { + DEBUG(SSSDBG_CRIT_FAILURE, + "No ipa server set, will use service discovery!\n"); + } + + ipa_hostname = dp_opt_get_string(opts->basic, IPA_HOSTNAME); + if (ipa_hostname == NULL) { + ret = gethostname(hostname, sizeof(hostname)); + if (ret != EOK) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, "gethostname failed [%d][%s].\n", ret, + strerror(ret)); + goto done; + } + hostname[HOST_NAME_MAX] = '\0'; + DEBUG(SSSDBG_TRACE_ALL, "Setting ipa_hostname to [%s].\n", hostname); + ret = dp_opt_set_string(opts->basic, IPA_HOSTNAME, hostname); + if (ret != EOK) { + goto done; + } + } + + /* First check whether the realm has been manually specified */ + realm = dp_opt_get_string(opts->basic, IPA_KRB5_REALM); + if (!realm) { + /* No explicit krb5_realm, use the IPA domain, transform to upper-case */ + realm = get_uppercase_realm(opts, domain); + if (!realm) { + ret = ENOMEM; + goto done; + } + + ret = dp_opt_set_string(opts->basic, IPA_KRB5_REALM, + realm); + if (ret != EOK) { + goto done; + } + } + + ret = EOK; + *_opts = opts; + +done: + if (ret != EOK) { + talloc_zfree(opts); + } + return ret; +} + +static errno_t ipa_parse_search_base(TALLOC_CTX *mem_ctx, + struct ldb_context *ldb, + struct dp_option *opts, int class, + struct sdap_search_base ***_search_bases) +{ + const char *class_name; + char *unparsed_base; + + *_search_bases = NULL; + + switch (class) { + case IPA_HBAC_SEARCH_BASE: + class_name = "IPA_HBAC"; + break; + case IPA_SELINUX_SEARCH_BASE: + class_name = "IPA_SELINUX"; + break; + case IPA_SUBDOMAINS_SEARCH_BASE: + class_name = "IPA_SUBDOMAINS"; + break; + case IPA_MASTER_DOMAIN_SEARCH_BASE: + class_name = "IPA_MASTER_DOMAIN"; + break; + case IPA_RANGES_SEARCH_BASE: + class_name = "IPA_RANGES"; + break; + case IPA_VIEWS_SEARCH_BASE: + class_name = "IPA_VIEWS"; + break; + case IPA_DESKPROFILE_SEARCH_BASE: + class_name = "IPA_DESKPROFILE"; + break; + case IPA_SUBID_RANGES_SEARCH_BASE: + class_name = "IPA_SUBID_RANGES"; + break; + default: + DEBUG(SSSDBG_CONF_SETTINGS, + "Unknown search base type: [%d]\n", class); + class_name = "UNKNOWN"; + /* Non-fatal */ + break; + } + + unparsed_base = dp_opt_get_string(opts, class); + if (!unparsed_base || unparsed_base[0] == '\0') return ENOENT; + + return common_parse_search_base(mem_ctx, unparsed_base, ldb, + class_name, NULL, + _search_bases); +} + +int ipa_get_id_options(struct ipa_options *ipa_opts, + struct confdb_ctx *cdb, + const char *conf_path, + struct data_provider *dp, + struct sdap_options **_opts) +{ + TALLOC_CTX *tmpctx; + char *basedn; + char *realm; + char *value; + int ret; + int i; + bool server_mode; + struct ldb_context *ldb; + + ldb = sysdb_ctx_get_ldb(dp->be_ctx->domain->sysdb); + + tmpctx = talloc_new(ipa_opts); + if (!tmpctx) { + return ENOMEM; + } + + ipa_opts->id = talloc_zero(ipa_opts, struct sdap_options); + if (!ipa_opts->id) { + ret = ENOMEM; + goto done; + } + ipa_opts->id->dp = dp; + + ret = sdap_domain_add(ipa_opts->id, + ipa_opts->id_ctx->sdap_id_ctx->be->domain, + NULL); + if (ret != EOK) { + goto done; + } + + /* get sdap options */ + ret = dp_get_options(ipa_opts->id, cdb, conf_path, + ipa_def_ldap_opts, + SDAP_OPTS_BASIC, + &ipa_opts->id->basic); + if (ret != EOK) { + goto done; + } + + /* sssd-ipa can't use simple bind, ignore option that potentially can be set + * for sssd-ldap in the same domain + */ + ret = dp_opt_set_string(ipa_opts->id->basic, + SDAP_DEFAULT_AUTHTOK_TYPE, NULL); + if (ret != EOK) { + goto done; + } + + ret = domain_to_basedn(tmpctx, + dp_opt_get_string(ipa_opts->basic, IPA_KRB5_REALM), + &basedn); + if (ret != EOK) { + goto done; + } + + if (NULL == dp_opt_get_string(ipa_opts->id->basic, SDAP_SEARCH_BASE)) { + /* FIXME: get values by querying IPA */ + /* set search base */ + value = talloc_asprintf(tmpctx, "cn=accounts,%s", basedn); + if (!value) { + ret = ENOMEM; + goto done; + } + ret = dp_opt_set_string(ipa_opts->id->basic, + SDAP_SEARCH_BASE, value); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Option %s set to %s\n", + ipa_opts->id->basic[SDAP_SEARCH_BASE].opt_name, + dp_opt_get_string(ipa_opts->id->basic, SDAP_SEARCH_BASE)); + } + ret = sdap_parse_search_base(ipa_opts->id, ldb, ipa_opts->id->basic, + SDAP_SEARCH_BASE, + &ipa_opts->id->sdom->search_bases); + if (ret != EOK) goto done; + + /* set krb realm */ + if (NULL == dp_opt_get_string(ipa_opts->id->basic, SDAP_KRB5_REALM)) { + realm = dp_opt_get_string(ipa_opts->basic, IPA_KRB5_REALM); + value = talloc_strdup(tmpctx, realm); + if (value == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n"); + ret = ENOMEM; + goto done; + } + ret = dp_opt_set_string(ipa_opts->id->basic, + SDAP_KRB5_REALM, value); + if (ret != EOK) { + goto done; + } + DEBUG(SSSDBG_TRACE_FUNC, "Option %s set to %s\n", + ipa_opts->id->basic[SDAP_KRB5_REALM].opt_name, + dp_opt_get_string(ipa_opts->id->basic, SDAP_KRB5_REALM)); + } + + ret = sdap_set_sasl_options(ipa_opts->id, + dp_opt_get_string(ipa_opts->basic, + IPA_HOSTNAME), + dp_opt_get_string(ipa_opts->id->basic, + SDAP_KRB5_REALM), + dp_opt_get_string(ipa_opts->id->basic, + SDAP_KRB5_KEYTAB)); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot set the SASL-related options\n"); + goto done; + } + + /* fix schema to IPAv1 for now */ + ipa_opts->id->schema_type = SDAP_SCHEMA_IPA_V1; + + /* set user/group search bases if they are not specified */ + if (NULL == dp_opt_get_string(ipa_opts->id->basic, + SDAP_USER_SEARCH_BASE)) { + ret = dp_opt_set_string(ipa_opts->id->basic, SDAP_USER_SEARCH_BASE, + dp_opt_get_string(ipa_opts->id->basic, + SDAP_SEARCH_BASE)); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Option %s set to %s\n", + ipa_opts->id->basic[SDAP_USER_SEARCH_BASE].opt_name, + dp_opt_get_string(ipa_opts->id->basic, + SDAP_USER_SEARCH_BASE)); + } + ret = sdap_parse_search_base(ipa_opts->id, ldb, ipa_opts->id->basic, + SDAP_USER_SEARCH_BASE, + &ipa_opts->id->sdom->user_search_bases); + if (ret != EOK) goto done; + + /* In server mode we need to search both cn=accounts,$SUFFIX and + * cn=trusts,$SUFFIX to allow trusted domain object accounts to be found. + * If cn=trusts,$SUFFIX is missing in the user search bases, add one + */ + server_mode = dp_opt_get_bool(ipa_opts->basic, IPA_SERVER_MODE); + if (server_mode != false) { + /* bases is not NULL at this point already */ + struct sdap_search_base **bases = ipa_opts->id->sdom->user_search_bases; + struct sdap_search_base *new_base = NULL; + + for (i = 0; bases[i] != NULL; i++) { + if (strcasestr(bases[i]->basedn, "cn=trusts,") != NULL) { + break; + } + } + if (NULL == bases[i]) { + /* no cn=trusts in the base, add a new one */ + char *new_dn = talloc_asprintf(bases, + "cn=trusts,%s", + basedn); + if (NULL == new_dn) { + ret = ENOMEM; + goto done; + } + + ret = sdap_create_search_base(bases, ldb, new_dn, + LDAP_SCOPE_SUBTREE, + "(objectClass=ipaIDObject)", + &new_base); + if (ret != EOK) { + goto done; + } + + bases = talloc_realloc(ipa_opts->id, + ipa_opts->id->sdom->user_search_bases, + struct sdap_search_base*, + i + 2); + + if (NULL == bases) { + ret = ENOMEM; + goto done; + } + + bases[i] = new_base; + bases[i+1] = NULL; + ipa_opts->id->sdom->user_search_bases = bases; + + DEBUG(SSSDBG_TRACE_FUNC, + "Option %s expanded to cover cn=trusts base\n", + ipa_opts->id->basic[SDAP_USER_SEARCH_BASE].opt_name); + } + } + + if (NULL == dp_opt_get_string(ipa_opts->id->basic, + SDAP_GROUP_SEARCH_BASE)) { + ret = dp_opt_set_string(ipa_opts->id->basic, SDAP_GROUP_SEARCH_BASE, + dp_opt_get_string(ipa_opts->id->basic, + SDAP_SEARCH_BASE)); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Option %s set to %s\n", + ipa_opts->id->basic[SDAP_GROUP_SEARCH_BASE].opt_name, + dp_opt_get_string(ipa_opts->id->basic, + SDAP_GROUP_SEARCH_BASE)); + } + ret = sdap_parse_search_base(ipa_opts->id, ldb, ipa_opts->id->basic, + SDAP_GROUP_SEARCH_BASE, + &ipa_opts->id->sdom->group_search_bases); + if (ret != EOK) goto done; + + if (NULL == dp_opt_get_string(ipa_opts->id->basic, + SDAP_NETGROUP_SEARCH_BASE)) { + value = talloc_asprintf(tmpctx, "cn=ng,cn=alt,%s", basedn); + if (!value) { + ret = ENOMEM; + goto done; + } + ret = dp_opt_set_string(ipa_opts->id->basic, SDAP_NETGROUP_SEARCH_BASE, + value); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Option %s set to %s\n", + ipa_opts->id->basic[SDAP_NETGROUP_SEARCH_BASE].opt_name, + dp_opt_get_string(ipa_opts->id->basic, + SDAP_NETGROUP_SEARCH_BASE)); + } + ret = sdap_parse_search_base(ipa_opts->id, ldb, ipa_opts->id->basic, + SDAP_NETGROUP_SEARCH_BASE, + &ipa_opts->id->sdom->netgroup_search_bases); + if (ret != EOK) goto done; + + if (NULL == dp_opt_get_string(ipa_opts->id->basic, + SDAP_HOST_SEARCH_BASE)) { + + value = dp_opt_get_string(ipa_opts->basic, IPA_HOST_SEARCH_BASE); + if (!value) { + value = dp_opt_get_string(ipa_opts->id->basic, SDAP_SEARCH_BASE); + } + + ret = dp_opt_set_string(ipa_opts->id->basic, SDAP_HOST_SEARCH_BASE, + value); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_CONF_SETTINGS, "Option %s set to %s\n", + ipa_opts->id->basic[SDAP_HOST_SEARCH_BASE].opt_name, + value); + } + ret = sdap_parse_search_base(ipa_opts->id->basic, ldb, ipa_opts->id->basic, + SDAP_HOST_SEARCH_BASE, + &ipa_opts->id->sdom->host_search_bases); + if (ret != EOK) goto done; + + if (NULL == dp_opt_get_string(ipa_opts->basic, + IPA_HBAC_SEARCH_BASE)) { + value = talloc_asprintf(tmpctx, "cn=hbac,%s", basedn); + if (!value) { + ret = ENOMEM; + goto done; + } + + ret = dp_opt_set_string(ipa_opts->basic, IPA_HBAC_SEARCH_BASE, value); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Option %s set to %s\n", + ipa_opts->basic[IPA_HBAC_SEARCH_BASE].opt_name, + dp_opt_get_string(ipa_opts->basic, + IPA_HBAC_SEARCH_BASE)); + } + ret = ipa_parse_search_base(ipa_opts->basic, ldb, ipa_opts->basic, + IPA_HBAC_SEARCH_BASE, + &ipa_opts->hbac_search_bases); + if (ret != EOK) goto done; + + if (NULL == dp_opt_get_string(ipa_opts->basic, + IPA_SELINUX_SEARCH_BASE)) { + value = talloc_asprintf(tmpctx, "cn=selinux,%s", basedn); + if (!value) { + ret = ENOMEM; + goto done; + } + + ret = dp_opt_set_string(ipa_opts->basic, IPA_SELINUX_SEARCH_BASE, value); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_CONF_SETTINGS, "Option %s set to %s\n", + ipa_opts->basic[IPA_SELINUX_SEARCH_BASE].opt_name, + dp_opt_get_string(ipa_opts->basic, + IPA_SELINUX_SEARCH_BASE)); + } + ret = ipa_parse_search_base(ipa_opts->basic, ldb, ipa_opts->basic, + IPA_SELINUX_SEARCH_BASE, + &ipa_opts->selinux_search_bases); + if (ret != EOK) goto done; + + if (NULL == dp_opt_get_string(ipa_opts->basic, + IPA_DESKPROFILE_SEARCH_BASE)) { + value = talloc_asprintf(tmpctx, "cn=desktop-profile,%s", basedn); + if (!value) { + ret = ENOMEM; + goto done; + } + + ret = dp_opt_set_string(ipa_opts->basic, IPA_DESKPROFILE_SEARCH_BASE, value); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Option %s set to %s\n", + ipa_opts->basic[IPA_DESKPROFILE_SEARCH_BASE].opt_name, + dp_opt_get_string(ipa_opts->basic, + IPA_DESKPROFILE_SEARCH_BASE)); + } + ret = ipa_parse_search_base(ipa_opts->basic, ldb, ipa_opts->basic, + IPA_DESKPROFILE_SEARCH_BASE, + &ipa_opts->deskprofile_search_bases); + if (ret != EOK) goto done; + +#ifdef BUILD_SUBID + if (NULL == dp_opt_get_string(ipa_opts->basic, + IPA_SUBID_RANGES_SEARCH_BASE)) { + value = talloc_asprintf(tmpctx, "cn=subids,%s", + ipa_opts->id->sdom->search_bases[0]->basedn); + if (!value) { + ret = ENOMEM; + goto done; + } + + ret = dp_opt_set_string(ipa_opts->basic, IPA_SUBID_RANGES_SEARCH_BASE, value); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Option %s set to %s\n", + ipa_opts->basic[IPA_SUBID_RANGES_SEARCH_BASE].opt_name, + dp_opt_get_string(ipa_opts->basic, + IPA_SUBID_RANGES_SEARCH_BASE)); + } + ret = ipa_parse_search_base(ipa_opts->basic, ldb, ipa_opts->basic, + IPA_SUBID_RANGES_SEARCH_BASE, + &ipa_opts->id->sdom->subid_ranges_search_bases); + if (ret != EOK) goto done; + + ret = sdap_get_map(ipa_opts->id, + cdb, conf_path, + ipa_subid_map, + SDAP_OPTS_SUBID_RANGE, + &ipa_opts->id->subid_map); + if (ret != EOK) { + goto done; + } +#endif + + value = dp_opt_get_string(ipa_opts->id->basic, SDAP_DEREF); + if (value != NULL) { + ret = deref_string_to_val(value, &i); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to verify ldap_deref option.\n"); + goto done; + } + } + + if (NULL == dp_opt_get_string(ipa_opts->id->basic, + SDAP_SERVICE_SEARCH_BASE)) { + value = talloc_asprintf(tmpctx, "cn=ipservices,%s", + dp_opt_get_string(ipa_opts->id->basic, + SDAP_SEARCH_BASE)); + if (!value) { + ret = ENOMEM; + goto done; + } + ret = dp_opt_set_string(ipa_opts->id->basic, + SDAP_SERVICE_SEARCH_BASE, value); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Option %s set to %s\n", + ipa_opts->id->basic[SDAP_SERVICE_SEARCH_BASE].opt_name, + dp_opt_get_string(ipa_opts->id->basic, + SDAP_SERVICE_SEARCH_BASE)); + } + ret = sdap_parse_search_base(ipa_opts->id, ldb, ipa_opts->id->basic, + SDAP_SERVICE_SEARCH_BASE, + &ipa_opts->id->sdom->service_search_bases); + if (ret != EOK) goto done; + + if (NULL == dp_opt_get_string(ipa_opts->basic, + IPA_SUBDOMAINS_SEARCH_BASE)) { + value = talloc_asprintf(tmpctx, "cn=trusts,%s", basedn); + if (value == NULL) { + ret = ENOMEM; + goto done; + } + + ret = dp_opt_set_string(ipa_opts->basic, IPA_SUBDOMAINS_SEARCH_BASE, value); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_CONF_SETTINGS, "Option %s set to %s\n", + ipa_opts->basic[IPA_SUBDOMAINS_SEARCH_BASE].opt_name, + dp_opt_get_string(ipa_opts->basic, + IPA_SUBDOMAINS_SEARCH_BASE)); + } + ret = ipa_parse_search_base(ipa_opts, ldb, ipa_opts->basic, + IPA_SUBDOMAINS_SEARCH_BASE, + &ipa_opts->subdomains_search_bases); + if (ret != EOK) goto done; + + if (NULL == dp_opt_get_string(ipa_opts->basic, + IPA_MASTER_DOMAIN_SEARCH_BASE)) { + value = talloc_asprintf(tmpctx, "cn=ad,cn=etc,%s", basedn); + if (value == NULL) { + ret = ENOMEM; + goto done; + } + + ret = dp_opt_set_string(ipa_opts->basic, IPA_MASTER_DOMAIN_SEARCH_BASE, value); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_CONF_SETTINGS, "Option %s set to %s\n", + ipa_opts->basic[IPA_MASTER_DOMAIN_SEARCH_BASE].opt_name, + dp_opt_get_string(ipa_opts->basic, + IPA_MASTER_DOMAIN_SEARCH_BASE)); + } + ret = ipa_parse_search_base(ipa_opts, ldb, ipa_opts->basic, + IPA_MASTER_DOMAIN_SEARCH_BASE, + &ipa_opts->master_domain_search_bases); + if (ret != EOK) goto done; + + if (NULL == dp_opt_get_string(ipa_opts->basic, + IPA_RANGES_SEARCH_BASE)) { + value = talloc_asprintf(tmpctx, "cn=ranges,cn=etc,%s", basedn); + if (value == NULL) { + ret = ENOMEM; + goto done; + } + + ret = dp_opt_set_string(ipa_opts->basic, IPA_RANGES_SEARCH_BASE, value); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_CONF_SETTINGS, "Option %s set to %s\n", + ipa_opts->basic[IPA_RANGES_SEARCH_BASE].opt_name, + dp_opt_get_string(ipa_opts->basic, + IPA_RANGES_SEARCH_BASE)); + } + ret = ipa_parse_search_base(ipa_opts, ldb, ipa_opts->basic, + IPA_RANGES_SEARCH_BASE, + &ipa_opts->ranges_search_bases); + if (ret != EOK) goto done; + + if (NULL == dp_opt_get_string(ipa_opts->basic, + IPA_VIEWS_SEARCH_BASE)) { + value = talloc_asprintf(tmpctx, "cn=views,cn=accounts,%s", basedn); + if (value == NULL) { + ret = ENOMEM; + goto done; + } + + ret = dp_opt_set_string(ipa_opts->basic, IPA_VIEWS_SEARCH_BASE, value); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_CONF_SETTINGS, "Option %s set to %s\n", + ipa_opts->basic[IPA_VIEWS_SEARCH_BASE].opt_name, + dp_opt_get_string(ipa_opts->basic, + IPA_VIEWS_SEARCH_BASE)); + } + ret = ipa_parse_search_base(ipa_opts, ldb, ipa_opts->basic, + IPA_VIEWS_SEARCH_BASE, + &ipa_opts->views_search_bases); + if (ret != EOK) goto done; + + ret = sdap_get_map(ipa_opts->id, cdb, conf_path, + ipa_attr_map, + SDAP_AT_GENERAL, + &ipa_opts->id->gen_map); + if (ret != EOK) { + goto done; + } + + ret = sdap_get_map(ipa_opts->id, + cdb, conf_path, + ipa_user_map, + SDAP_OPTS_USER, + &ipa_opts->id->user_map); + if (ret != EOK) { + goto done; + } + + ret = sdap_extend_map_with_list(ipa_opts->id, ipa_opts->id, + SDAP_USER_EXTRA_ATTRS, + ipa_opts->id->user_map, + SDAP_OPTS_USER, + &ipa_opts->id->user_map, + &ipa_opts->id->user_map_cnt); + if (ret != EOK) { + goto done; + } + + ret = sdap_get_map(ipa_opts->id, + cdb, conf_path, + ipa_group_map, + SDAP_OPTS_GROUP, + &ipa_opts->id->group_map); + if (ret != EOK) { + goto done; + } + + ret = sdap_get_map(ipa_opts->id, + cdb, conf_path, + ipa_netgroup_map, + IPA_OPTS_NETGROUP, + &ipa_opts->id->netgroup_map); + if (ret != EOK) { + goto done; + } + + ret = sdap_get_map(ipa_opts->id, + cdb, conf_path, + ipa_host_map, + SDAP_OPTS_HOST, + &ipa_opts->id->host_map); + if (ret != EOK) { + goto done; + } + + ret = sdap_get_map(ipa_opts->id, + cdb, conf_path, + ipa_hostgroup_map, + IPA_OPTS_HOSTGROUP, + &ipa_opts->hostgroup_map); + if (ret != EOK) { + goto done; + } + + ret = sdap_get_map(ipa_opts->id, + cdb, conf_path, + ipa_service_map, + SDAP_OPTS_SERVICES, + &ipa_opts->id->service_map); + if (ret != EOK) { + goto done; + } + + ret = sdap_get_map(ipa_opts->id, + cdb, conf_path, + ipa_selinux_user_map, + IPA_OPTS_SELINUX_USERMAP, + &ipa_opts->selinuxuser_map); + if (ret != EOK) { + goto done; + } + + ret = sdap_get_map(ipa_opts->id, + cdb, conf_path, + ipa_view_map, + IPA_OPTS_VIEW, + &ipa_opts->view_map); + if (ret != EOK) { + goto done; + } + + ret = sdap_get_map(ipa_opts->id, + cdb, conf_path, + ipa_override_map, + IPA_OPTS_OVERRIDE, + &ipa_opts->override_map); + if (ret != EOK) { + goto done; + } + + ret = EOK; + *_opts = ipa_opts->id; + +done: + talloc_zfree(tmpctx); + if (ret != EOK) { + talloc_zfree(ipa_opts->id); + } + return ret; +} + +int ipa_get_auth_options(struct ipa_options *ipa_opts, + struct confdb_ctx *cdb, + const char *conf_path, + struct dp_option **_opts) +{ + char *value; + char *copy = NULL; + int ret; + + ipa_opts->auth = talloc_zero(ipa_opts, struct dp_option); + if (ipa_opts->auth == NULL) { + ret = ENOMEM; + goto done; + } + + /* get krb5 options */ + ret = dp_get_options(ipa_opts, cdb, conf_path, + ipa_def_krb5_opts, + KRB5_OPTS, &ipa_opts->auth); + if (ret != EOK) { + goto done; + } + + /* If there is no KDC, try the deprecated krb5_kdcip option, too */ + /* FIXME - this can be removed in a future version */ + ret = krb5_try_kdcip(cdb, conf_path, ipa_opts->auth, KRB5_KDC); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "sss_krb5_try_kdcip failed.\n"); + goto done; + } + + /* set krb realm */ + if (NULL == dp_opt_get_string(ipa_opts->auth, KRB5_REALM)) { + value = dp_opt_get_string(ipa_opts->basic, IPA_KRB5_REALM); + if (!value) { + ret = ENOMEM; + goto done; + } + copy = talloc_strdup(ipa_opts->auth, value); + if (copy == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n"); + ret = ENOMEM; + goto done; + } + ret = dp_opt_set_string(ipa_opts->auth, KRB5_REALM, copy); + if (ret != EOK) { + goto done; + } + DEBUG(SSSDBG_TRACE_FUNC, "Option %s set to %s\n", + ipa_opts->auth[KRB5_REALM].opt_name, + dp_opt_get_string(ipa_opts->auth, KRB5_REALM)); + } + + /* If krb5_fast_principal was not set explicitly, default to + * host/$client_hostname@REALM + */ + value = dp_opt_get_string(ipa_opts->auth, KRB5_FAST_PRINCIPAL); + if (value == NULL) { + value = talloc_asprintf(ipa_opts->auth, "host/%s@%s", + dp_opt_get_string(ipa_opts->basic, + IPA_HOSTNAME), + dp_opt_get_string(ipa_opts->auth, + KRB5_REALM)); + if (value == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n"); + ret = ENOMEM; + goto done; + } + + ret = dp_opt_set_string(ipa_opts->auth, KRB5_FAST_PRINCIPAL, + value); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot set %s!\n", + ipa_opts->auth[KRB5_FAST_PRINCIPAL].opt_name); + goto done; + } + + DEBUG(SSSDBG_CONF_SETTINGS, "Option %s set to %s\n", + ipa_opts->auth[KRB5_FAST_PRINCIPAL].opt_name, value); + } + + /* Set flag that controls whether we want to write the + * kdcinfo files at all + */ + ipa_opts->service->krb5_service->write_kdcinfo = \ + dp_opt_get_bool(ipa_opts->auth, KRB5_USE_KDCINFO); + DEBUG(SSSDBG_CONF_SETTINGS, "Option %s set to %s\n", + ipa_opts->auth[KRB5_USE_KDCINFO].opt_name, + ipa_opts->service->krb5_service->write_kdcinfo ? "true" : "false"); + if (ipa_opts->service->krb5_service->write_kdcinfo) { + sss_krb5_parse_lookahead( + dp_opt_get_string(ipa_opts->auth, KRB5_KDCINFO_LOOKAHEAD), + &ipa_opts->service->krb5_service->lookahead_primary, + &ipa_opts->service->krb5_service->lookahead_backup); + } + + *_opts = ipa_opts->auth; + ret = EOK; + +done: + talloc_free(copy); + if (ret != EOK) { + talloc_zfree(ipa_opts->auth); + } + return ret; +} + +static void ipa_resolve_callback(void *private_data, struct fo_server *server) +{ + TALLOC_CTX *tmp_ctx = NULL; + struct ipa_service *service; + struct resolv_hostent *srvaddr; + struct sockaddr *sockaddr; + char *new_uri; + const char *srv_name; + socklen_t sockaddr_len; + int ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed\n"); + return; + } + + service = talloc_get_type(private_data, struct ipa_service); + if (!service) { + DEBUG(SSSDBG_CRIT_FAILURE, "FATAL: Bad private_data\n"); + talloc_free(tmp_ctx); + return; + } + + srvaddr = fo_get_server_hostent(server); + if (!srvaddr) { + DEBUG(SSSDBG_CRIT_FAILURE, + "No hostent available for server (%s)\n", + fo_get_server_str_name(server)); + talloc_free(tmp_ctx); + return; + } + + sockaddr = resolv_get_sockaddr_address(tmp_ctx, srvaddr, LDAP_PORT, &sockaddr_len); + if (sockaddr == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "resolv_get_sockaddr_address failed.\n"); + talloc_free(tmp_ctx); + return; + } + + srv_name = fo_get_server_name(server); + if (srv_name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Could not get server host name\n"); + talloc_free(tmp_ctx); + return; + } + + new_uri = talloc_asprintf(service, "ldap://%s", srv_name); + if (!new_uri) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to copy URI ...\n"); + talloc_free(tmp_ctx); + return; + } + DEBUG(SSSDBG_TRACE_FUNC, "Constructed uri '%s'\n", new_uri); + + /* free old one and replace with new one */ + talloc_zfree(service->sdap->uri); + service->sdap->uri = new_uri; + talloc_zfree(service->sdap->sockaddr); + service->sdap->sockaddr = talloc_steal(service, sockaddr); + service->sdap->sockaddr_len = sockaddr_len; + + if (service->krb5_service->write_kdcinfo) { + ret = write_krb5info_file_from_fo_server(service->krb5_service, + server, + true, + SSS_KRB5KDC_FO_SRV, + NULL); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "write to %s/kdcinfo.%s failed, authentication might fail.\n", + PUBCONF_PATH, service->krb5_service->realm); + } + } + + talloc_free(tmp_ctx); +} + +static errno_t _ipa_servers_init(struct be_ctx *ctx, + struct ipa_service *service, + struct ipa_options *options, + const char *servers, + bool primary) +{ + TALLOC_CTX *tmp_ctx; + char **list = NULL; + char *ipa_domain; + int ret = 0; + int i; + int j; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + return ENOMEM; + } + + /* split server parm into a list */ + ret = split_on_separator(tmp_ctx, servers, ',', true, true, &list, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to parse server list!\n"); + goto done; + } + + for (j = 0; list[j]; j++) { + if (resolv_is_address(list[j])) { + DEBUG(SSSDBG_IMPORTANT_INFO, + "ipa_server [%s] is detected as IP address, " + "this can cause GSSAPI problems\n", list[j]); + } + } + + /* now for each one add a new server to the failover service */ + for (i = 0; list[i]; i++) { + + talloc_steal(service, list[i]); + + if (be_fo_is_srv_identifier(list[i])) { + if (!primary) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failed to add server [%s] to failover service: " + "SRV resolution only allowed for primary servers!\n", + list[i]); + continue; + } + + ipa_domain = dp_opt_get_string(options->basic, IPA_DOMAIN); + ret = be_fo_add_srv_server(ctx, "IPA", "ldap", ipa_domain, + BE_FO_PROTO_TCP, false, NULL); + if (ret) { + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to add server\n"); + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Added service lookup for service IPA\n"); + continue; + } + + /* It could be ipv6 address in square brackets. Remove + * the brackets if needed. */ + ret = remove_ipv6_brackets(list[i]); + if (ret != EOK) { + goto done; + } + + ret = be_fo_add_server(ctx, "IPA", list[i], 0, NULL, primary); + if (ret && ret != EEXIST) { + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to add server\n"); + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Added Server %s\n", list[i]); + } + +done: + talloc_free(tmp_ctx); + return ret; +} + +static inline errno_t +ipa_primary_servers_init(struct be_ctx *ctx, struct ipa_service *service, + struct ipa_options *options, const char *servers) +{ + return _ipa_servers_init(ctx, service, options, servers, true); +} + +static inline errno_t +ipa_backup_servers_init(struct be_ctx *ctx, struct ipa_service *service, + struct ipa_options *options, const char *servers) +{ + return _ipa_servers_init(ctx, service, options, servers, false); +} + +static int ipa_user_data_cmp(void *ud1, void *ud2) +{ + return strcasecmp((char*) ud1, (char*) ud2); +} + +int ipa_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, + const char *primary_servers, + const char *backup_servers, + struct ipa_options *options, + struct ipa_service **_service) +{ + TALLOC_CTX *tmp_ctx; + struct ipa_service *service; + char *realm; + int ret; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + return ENOMEM; + } + + realm = dp_opt_get_string(options->basic, IPA_KRB5_REALM); + if (!realm) { + DEBUG(SSSDBG_CRIT_FAILURE, "No Kerberos realm set\n"); + ret = EINVAL; + goto done; + } + + service = talloc_zero(tmp_ctx, struct ipa_service); + if (!service) { + ret = ENOMEM; + goto done; + } + service->sdap = talloc_zero(service, struct sdap_service); + if (!service->sdap) { + ret = ENOMEM; + goto done; + } + + service->krb5_service = krb5_service_new(service, ctx, + "IPA", realm, + true, /* The configured value */ + 0, /* will be set later when */ + 0); /* the auth provider is set up */ + + if (!service->krb5_service) { + ret = ENOMEM; + goto done; + } + + ret = be_fo_add_service(ctx, "IPA", ipa_user_data_cmp); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create failover service!\n"); + goto done; + } + + service->sdap->name = talloc_strdup(service, "IPA"); + if (!service->sdap->name) { + ret = ENOMEM; + goto done; + } + + service->sdap->kinit_service_name = service->krb5_service->name; + + if (!primary_servers) { + DEBUG(SSSDBG_CONF_SETTINGS, + "No primary servers defined, using service discovery\n"); + primary_servers = BE_SRV_IDENTIFIER; + } + + ret = ipa_primary_servers_init(ctx, service, options, primary_servers); + if (ret != EOK) { + goto done; + } + + if (backup_servers) { + ret = ipa_backup_servers_init(ctx, service, options, backup_servers); + if (ret != EOK) { + goto done; + } + } + + ret = be_fo_service_add_callback(memctx, ctx, "IPA", + ipa_resolve_callback, service); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to add failover callback!\n"); + goto done; + } + + ret = EOK; + +done: + if (ret == EOK) { + *_service = talloc_steal(memctx, service); + } + talloc_zfree(tmp_ctx); + return ret; +} + +int ipa_get_autofs_options(struct ipa_options *ipa_opts, + struct ldb_context *ldb, + struct confdb_ctx *cdb, + const char *conf_path, + struct sdap_options **_opts) +{ + TALLOC_CTX *tmp_ctx; + char *basedn; + char *autofs_base; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + return ENOMEM; + } + + ret = domain_to_basedn(tmp_ctx, + dp_opt_get_string(ipa_opts->basic, IPA_KRB5_REALM), + &basedn); + if (ret != EOK) { + goto done; + } + + if (NULL == dp_opt_get_string(ipa_opts->id->basic, + SDAP_AUTOFS_SEARCH_BASE)) { + + autofs_base = talloc_asprintf(tmp_ctx, "cn=%s,cn=automount,%s", + dp_opt_get_string(ipa_opts->basic, + IPA_AUTOMOUNT_LOCATION), + basedn); + if (!autofs_base) { + ret = ENOMEM; + goto done; + } + + ret = dp_opt_set_string(ipa_opts->id->basic, + SDAP_AUTOFS_SEARCH_BASE, + autofs_base); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_TRACE_LIBS, "Option %s set to %s\n", + ipa_opts->id->basic[SDAP_AUTOFS_SEARCH_BASE].opt_name, + dp_opt_get_string(ipa_opts->id->basic, + SDAP_AUTOFS_SEARCH_BASE)); + } + + ret = sdap_parse_search_base(ipa_opts->id, ldb, ipa_opts->id->basic, + SDAP_AUTOFS_SEARCH_BASE, + &ipa_opts->id->sdom->autofs_search_bases); + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_OP_FAILURE, "Could not parse autofs search base\n"); + goto done; + } + + ret = sdap_get_map(ipa_opts->id, cdb, conf_path, + ipa_autofs_mobject_map, + SDAP_OPTS_AUTOFS_MAP, + &ipa_opts->id->autofs_mobject_map); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Could not get autofs map object attribute map\n"); + goto done; + } + + ret = sdap_get_map(ipa_opts->id, cdb, conf_path, + ipa_autofs_entry_map, + SDAP_OPTS_AUTOFS_ENTRY, + &ipa_opts->id->autofs_entry_map); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Could not get autofs entry object attribute map\n"); + goto done; + } + + *_opts = ipa_opts->id; + ret = EOK; +done: + talloc_free(tmp_ctx); + return ret; +} + +errno_t ipa_get_dyndns_options(struct be_ctx *be_ctx, + struct ipa_options *ctx) +{ + errno_t ret; + char *val; + bool update; + int ttl; + + ret = be_nsupdate_init(ctx, be_ctx, ipa_dyndns_opts, &ctx->dyndns_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot initialize IPA dyndns opts [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; + } + + if (ctx->basic == NULL) { + DEBUG(SSSDBG_MINOR_FAILURE, "IPA basic options not (yet) " + "initialized, cannot copy legacy options\n"); + return EOK; + } + + /* Reuse legacy option values */ + ret = confdb_get_string(be_ctx->cdb, ctx, be_ctx->conf_path, + "ipa_dyndns_update", NULL, &val); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot get the value of %s\n", + "ipa_dyndns_update"); + /* Not fatal */ + } else if (ret == EOK && val) { + if (strcasecmp(val, "FALSE") == 0) { + update = false; + } else if (strcasecmp(val, "TRUE") == 0) { + update = true; + } else { + DEBUG(SSSDBG_MINOR_FAILURE, + "ipa_dyndns_update value is not a boolean!\n"); + talloc_free(val); + return EINVAL; + } + + DEBUG(SSSDBG_MINOR_FAILURE, "Deprecation warning: The option %s is " + "deprecated and should not be used in favor of %s\n", + "ipa_dyndns_update", "dyndns_update"); + + ret = dp_opt_set_bool(ctx->dyndns_ctx->opts, + DP_OPT_DYNDNS_UPDATE, update); + talloc_free(val); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot set option value\n"); + return ret; + } + } + + ret = confdb_get_int(be_ctx->cdb, be_ctx->conf_path, + "ipa_dyndns_ttl", -1, &ttl); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot get the value of %s\n", + "ipa_dyndns_ttl"); + /* Not fatal */ + } else if (ret == EOK && ttl != -1) { + DEBUG(SSSDBG_MINOR_FAILURE, "Deprecation warning: The option %s is " + "deprecated and should not be used in favor of %s\n", + "ipa_dyndns_ttl", "dyndns_ttl"); + + ret = dp_opt_set_int(ctx->dyndns_ctx->opts, DP_OPT_DYNDNS_TTL, ttl); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot set option value\n"); + return ret; + } + } + + /* Reuse legacy option values */ + ret = confdb_get_string(be_ctx->cdb, ctx, be_ctx->conf_path, + "ipa_dyndns_iface", NULL, &val); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot get the value of %s\n", + "ipa_dyndns_iface"); + /* Not fatal */ + } else if (ret == EOK && val) { + DEBUG(SSSDBG_MINOR_FAILURE, "Deprecation warning: The option %s is " + "deprecated and should not be used in favor of %s\n", + "ipa_dyndns_iface", "dyndns_iface"); + + ret = dp_opt_set_string(ctx->dyndns_ctx->opts, + DP_OPT_DYNDNS_IFACE, val); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot set option value\n"); + return ret; + } + } + + return EOK; +} + +errno_t ipa_get_host_attrs(struct dp_option *ipa_options, + size_t host_count, + struct sysdb_attrs **hosts, + struct sysdb_attrs **_ipa_host) +{ + const char *ipa_hostname; + const char *hostname; + errno_t ret; + + *_ipa_host = NULL; + ipa_hostname = dp_opt_get_cstring(ipa_options, IPA_HOSTNAME); + if (ipa_hostname == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Missing ipa_hostname, this should never happen.\n"); + ret = EINVAL; + goto done; + } + + for (size_t i = 0; i < host_count; i++) { + ret = sysdb_attrs_get_string(hosts[i], SYSDB_FQDN, &hostname); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Could not locate IPA host\n"); + goto done; + } + + if (strcasecmp(hostname, ipa_hostname) == 0) { + *_ipa_host = hosts[i]; + break; + } + } + + if (*_ipa_host == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Could not locate IPA host\n"); + ret = EINVAL; + goto done; + } + + ret = EOK; + +done: + return ret; +} |