diff options
author | Jan Zeleny <jzeleny@redhat.com> | 2012-02-05 15:59:20 -0500 |
---|---|---|
committer | Stephen Gallagher <sgallagh@redhat.com> | 2012-02-06 08:25:22 -0500 |
commit | 1a853121ca2ba8ede6df429ee76942131ffb0f65 (patch) | |
tree | b724db01bf92a61809c25a62c634f0e8da5dc257 | |
parent | 2d0550acbe07024d034fb616c1ec5b81929c4844 (diff) | |
download | sssd-1a853121ca2ba8ede6df429ee76942131ffb0f65.tar.gz sssd-1a853121ca2ba8ede6df429ee76942131ffb0f65.tar.xz sssd-1a853121ca2ba8ede6df429ee76942131ffb0f65.zip |
Session target in IPA provider
-rw-r--r-- | Makefile.am | 6 | ||||
-rw-r--r-- | src/providers/ipa/ipa_common.c | 47 | ||||
-rw-r--r-- | src/providers/ipa/ipa_common.h | 17 | ||||
-rw-r--r-- | src/providers/ipa/ipa_init.c | 42 | ||||
-rw-r--r-- | src/providers/ipa/ipa_selinux_common.c | 103 | ||||
-rw-r--r-- | src/providers/ipa/ipa_selinux_common.h | 39 | ||||
-rw-r--r-- | src/providers/ipa/ipa_selinux_maps.c | 217 | ||||
-rw-r--r-- | src/providers/ipa/ipa_selinux_maps.h | 44 | ||||
-rw-r--r-- | src/providers/ipa/ipa_session.c | 616 | ||||
-rw-r--r-- | src/providers/ipa/ipa_session.h | 40 | ||||
-rw-r--r-- | src/providers/ldap/sdap.h | 1 |
11 files changed, 1172 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am index 388edb7c0..c0b4c7000 100644 --- a/Makefile.am +++ b/Makefile.am @@ -361,7 +361,10 @@ dist_noinst_HEADERS = \ src/providers/ipa/ipa_common.h \ src/providers/ipa/ipa_config.h \ src/providers/ipa/ipa_access.h \ + src/providers/ipa/ipa_session.h \ src/providers/ipa/ipa_hosts.h \ + src/providers/ipa/ipa_selinux_common.h \ + src/providers/ipa/ipa_selinux_maps.h \ src/providers/ipa/ipa_auth.h \ src/providers/ipa/ipa_dyndns.h \ src/providers/ipa/ipa_id.h \ @@ -1080,6 +1083,9 @@ libsss_ipa_la_SOURCES = \ src/providers/ipa/ipa_hbac_services.c \ src/providers/ipa/ipa_hbac_users.c \ src/providers/ipa/ipa_hbac_common.c \ + src/providers/ipa/ipa_session.c \ + src/providers/ipa/ipa_selinux_maps.c \ + src/providers/ipa/ipa_selinux_common.c \ src/providers/ldap/ldap_id.c \ src/providers/ldap/ldap_id_enum.c \ src/providers/ldap/ldap_id_cleanup.c \ diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c index 58db9e78d..02485b801 100644 --- a/src/providers/ipa/ipa_common.c +++ b/src/providers/ipa/ipa_common.c @@ -26,6 +26,7 @@ #include <ctype.h> #include <arpa/inet.h> +#include "db/sysdb_selinux.h" #include "providers/ipa/ipa_common.h" #include "providers/ldap/sdap_async_private.h" #include "util/sss_krb5.h" @@ -39,6 +40,7 @@ struct dp_option ipa_basic_opts[] = { { "ipa_dyndns_iface", DP_OPT_STRING, NULL_STRING, NULL_STRING}, { "ipa_hbac_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING}, { "ipa_host_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING }, + { "ipa_selinux_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "krb5_realm", DP_OPT_STRING, NULL_STRING, NULL_STRING}, { "ipa_hbac_refresh", DP_OPT_NUMBER, { .number = 5 }, NULL_NUMBER }, { "ipa_hbac_treat_deny_as", DP_OPT_STRING, { "DENY_ALL" }, NULL_STRING }, @@ -181,6 +183,19 @@ struct sdap_attr_map ipa_host_map[] = { { "ipa_host_member_of", "memberOf", SYSDB_MEMBEROF, NULL }, }; +static struct sdap_attr_map ipa_selinux_user_map[] = { + {"ipa_selinux_usermap_object_class", "ipaselinuxusermap", SYSDB_SELINUX_USERMAP_CLASS, NULL}, + {"ipa_selinux_usermap_name", "cn", SYSDB_NAME, NULL}, + {"ipa_selinux_usermap_member_user", "memberUser", SYSDB_ORIG_MEMBER_USER, NULL}, + {"ipa_selinux_usermap_member_host", "memberHost", SYSDB_ORIG_MEMBER_HOST, NULL}, + {"ipa_selinux_usermap_see_also", "seeAlso", SYSDB_SELINUX_SEEALSO, NULL}, + {"ipa_selinux_usermap_selinux_user", "ipaSELinuxUser", SYSDB_SELINUX_USER, NULL}, + {"ipa_selinux_usermap_enabled", "ipaEnabledFlag", SYSDB_SELINUX_ENABLED, NULL}, + {"ipa_selinux_usermap_user_category", "userCategory", SYSDB_USER_CATEGORY, NULL}, + {"ipa_selinux_usermap_host_category", "hostCategory", SYSDB_HOST_CATEGORY, NULL}, + {"ipa_selinux_usermap_uuid", "ipaUniqueID", SYSDB_UUID, NULL} +}; + struct dp_option ipa_def_krb5_opts[] = { { "krb5_server", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "krb5_realm", DP_OPT_STRING, NULL_STRING, NULL_STRING }, @@ -605,6 +620,29 @@ int ipa_get_id_options(struct ipa_options *ipa_opts, &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 = sdap_parse_search_base(ipa_opts->basic, ipa_opts->basic, + IPA_SELINUX_SEARCH_BASE, + &ipa_opts->selinux_search_bases); + if (ret != EOK) goto done; + value = dp_opt_get_string(ipa_opts->id->basic, SDAP_DEREF); if (value != NULL) { ret = deref_string_to_val(value, &i); @@ -686,6 +724,15 @@ int ipa_get_id_options(struct ipa_options *ipa_opts, goto done; } + ret = sdap_get_map(ipa_opts->id, + cdb, conf_path, + ipa_selinux_user_map, + IPA_OPTS_SELINUX_USERMAP, + &ipa_opts->id->selinuxuser_map); + if (ret != EOK) { + goto done; + } + ret = EOK; *_opts = ipa_opts->id; diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h index 49afe74db..6be972966 100644 --- a/src/providers/ipa/ipa_common.h +++ b/src/providers/ipa/ipa_common.h @@ -52,6 +52,7 @@ enum ipa_basic_opt { IPA_DYNDNS_IFACE, IPA_HBAC_SEARCH_BASE, IPA_HOST_SEARCH_BASE, + IPA_SELINUX_SEARCH_BASE, IPA_KRB5_REALM, IPA_HBAC_REFRESH, IPA_HBAC_DENY_METHOD, @@ -82,6 +83,21 @@ enum ipa_host_attrs { IPA_OPTS_HOST /* attrs counter */ }; +enum ipa_selinux_usermap_attrs { + IPA_OC_SELINUX_USERMAP = 0, + IPA_AT_SELINUX_USERMAP_NAME, + IPA_AT_SELINUX_USERMAP_MEMBER_USER, + IPA_AT_SELINUX_USERMAP_MEMBER_HOST, + IPA_AT_SELINUX_USERMAP_SEE_ALSO, + IPA_AT_SELINUX_USERMAP_SELINUX_USER, + IPA_AT_SELINUX_USERMAP_ENABLED, + IPA_AT_SELINUX_USERMAP_USERCAT, + IPA_AT_SELINUX_USERMAP_HOSTCAT, + IPA_AT_SELINUX_USERMAP_UUID, + + IPA_OPTS_SELINUX_USERMAP /* attrs counter */ +}; + struct ipa_auth_ctx { struct krb5_ctx *krb5_auth_ctx; struct sdap_id_ctx *sdap_id_ctx; @@ -99,6 +115,7 @@ struct ipa_options { struct sdap_search_base **host_search_bases; struct sdap_search_base **hbac_search_bases; + struct sdap_search_base **selinux_search_bases; struct ipa_service *service; /* id provider */ diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c index b3544f749..0484200cd 100644 --- a/src/providers/ipa/ipa_init.c +++ b/src/providers/ipa/ipa_init.c @@ -34,6 +34,7 @@ #include "providers/ipa/ipa_auth.h" #include "providers/ipa/ipa_access.h" #include "providers/ipa/ipa_dyndns.h" +#include "providers/ipa/ipa_session.h" struct ipa_options *ipa_options = NULL; @@ -59,6 +60,11 @@ struct bet_ops ipa_access_ops = { .finalize = NULL }; +struct bet_ops ipa_session_ops = { + .handler = ipa_session_handler, + .finalize = NULL +}; + int common_ipa_init(struct be_ctx *bectx) { const char *ipa_servers; @@ -393,3 +399,39 @@ done: } return ret; } + +int sssm_ipa_session_init(struct be_ctx *bectx, + struct bet_ops **ops, + void **pvt_data) +{ + int ret; + struct ipa_session_ctx *session_ctx; + struct ipa_options *opts; + + session_ctx = talloc_zero(bectx, struct ipa_session_ctx); + if (session_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_zero failed.\n")); + return ENOMEM; + } + + ret = sssm_ipa_id_init(bectx, ops, (void **) &session_ctx->id_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("sssm_ipa_id_init failed.\n")); + goto done; + } + + opts = session_ctx->id_ctx->ipa_options; + + session_ctx->hbac_search_bases = opts->hbac_search_bases; + session_ctx->host_search_bases = opts->host_search_bases; + session_ctx->selinux_search_bases = opts->selinux_search_bases; + + *ops = &ipa_session_ops; + *pvt_data = session_ctx; + +done: + if (ret != EOK) { + talloc_free(session_ctx); + } + return ret; +} diff --git a/src/providers/ipa/ipa_selinux_common.c b/src/providers/ipa/ipa_selinux_common.c new file mode 100644 index 000000000..15f0b4588 --- /dev/null +++ b/src/providers/ipa/ipa_selinux_common.c @@ -0,0 +1,103 @@ +/* + SSSD + + IPA Backend Module -- SELinux common routines + + Authors: + Jan Zeleny <jzeleny@redhat.com> + + Copyright (C) 2012 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "db/sysdb_selinux.h" +#include "providers/ldap/sdap_async.h" +#include "providers/ipa/ipa_selinux_common.h" + + +errno_t ipa_selinux_map_merge(struct sysdb_attrs *map, + struct sysdb_attrs *rule, + const char *attr) +{ + struct ldb_message_element *map_el; + struct ldb_message_element *rule_el; + size_t total_cnt; + errno_t ret; + int i = 0; + + ret = sysdb_attrs_get_el(map, attr, &map_el); + if (ret != EOK) { + goto done; + } + + ret = sysdb_attrs_get_el(rule, attr, &rule_el); + if (ret != EOK) { + goto done; + } + + total_cnt = map_el->num_values + rule_el->num_values; + map_el->values = talloc_realloc(map->a, map_el->values, + struct ldb_val, total_cnt); + if (map_el->values == NULL) { + ret = ENOMEM; + goto done; + } + + while (map_el->num_values < total_cnt) + { + map_el->values[map_el->num_values] = ldb_val_dup(map_el->values, + &rule_el->values[i]); + map_el->num_values++; + i++; + } + + ret = EOK; +done: + return ret; +} + +errno_t ipa_save_user_maps(struct sysdb_ctx *sysdb, + size_t map_count, + struct sysdb_attrs **maps) +{ + errno_t ret; + int i; + + ret = sysdb_transaction_start(sysdb); + if (ret) { + goto done; + } + + for (i = 0; i < map_count; i++) { + ret = sysdb_store_selinux_usermap(sysdb, maps[i]); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Failed to store user map %d. " + "Ignoring.\n", i)); + } else { + DEBUG(SSSDBG_TRACE_FUNC, ("User map %d processed.\n", i)); + } + } + + ret = sysdb_transaction_commit(sysdb); + if (ret) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to commit transaction!\n")); + goto done; + } + + ret = EOK; + +done: + return ret; +} diff --git a/src/providers/ipa/ipa_selinux_common.h b/src/providers/ipa/ipa_selinux_common.h new file mode 100644 index 000000000..17ccb2312 --- /dev/null +++ b/src/providers/ipa/ipa_selinux_common.h @@ -0,0 +1,39 @@ +/* + SSSD + + IPA Backend Module -- SELinux common routines + + Authors: + Jan Zeleny <jzeleny@redhat.com> + + Copyright (C) 2012 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef IPA_SELINUX_COMMON_H_ +#define IPA_SELINUX_COMMON_H_ + +errno_t ipa_selinux_map_merge(struct sysdb_attrs *map, + struct sysdb_attrs *rule, + const char *attr); + +errno_t ipa_save_host(struct sysdb_ctx *sysdb, + struct sysdb_attrs *host); + +errno_t ipa_save_user_maps(struct sysdb_ctx *sysdb, + size_t map_count, + struct sysdb_attrs **maps); + +#endif /* IPA_SELINUX_COMMON_H_ */ diff --git a/src/providers/ipa/ipa_selinux_maps.c b/src/providers/ipa/ipa_selinux_maps.c new file mode 100644 index 000000000..87650f6ce --- /dev/null +++ b/src/providers/ipa/ipa_selinux_maps.c @@ -0,0 +1,217 @@ +/* + SSSD + + IPA Backend Module -- SELinux user maps (maps retrieval) + + Authors: + Jan Zeleny <jzeleny@redhat.com> + + Copyright (C) 2012 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "providers/ipa/ipa_common.h" +#include "providers/ipa/ipa_selinux_maps.h" + +struct ipa_selinux_get_maps_state { + struct tevent_context *ev; + struct sysdb_ctx *sysdb; + struct sdap_handle *sh; + struct sdap_options *opts; + const char **attrs; + + struct sdap_search_base **search_bases; + int search_base_iter; + + char *cur_filter; + char *maps_filter; + + size_t map_count; + struct sysdb_attrs **maps; +}; + +static errno_t +ipa_selinux_get_maps_next(struct tevent_req *req, + struct ipa_selinux_get_maps_state *state); +static void +ipa_selinux_get_maps_done(struct tevent_req *subreq); + +struct tevent_req *ipa_selinux_get_maps_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sdap_handle *sh, + struct sdap_options *opts, + struct sdap_search_base **search_bases) +{ + struct tevent_req *req; + struct ipa_selinux_get_maps_state *state; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct ipa_selinux_get_maps_state); + if (req == NULL) { + return NULL; + } + + state->ev = ev; + state->sysdb = sysdb; + state->sh = sh; + state->opts = opts; + state->search_bases = search_bases; + state->search_base_iter = 0; + state->map_count = 0; + state->maps = NULL; + + ret = build_attrs_from_map(state, opts->selinuxuser_map, + IPA_OPTS_SELINUX_USERMAP, &state->attrs); + if (ret != EOK) goto fail; + + state->cur_filter = NULL; + state->maps_filter = talloc_asprintf(state, + "(&(objectclass=%s)(%s=TRUE))", + opts->selinuxuser_map[IPA_OC_SELINUX_USERMAP].name, + opts->selinuxuser_map[IPA_AT_SELINUX_USERMAP_ENABLED].name); + if (state->maps_filter == NULL) { + ret = ENOMEM; + goto fail; + } + + ret = ipa_selinux_get_maps_next(req, state); + if (ret == EOK) { + ret = EINVAL; + } + + if (ret != EAGAIN) { + goto fail; + } + + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static errno_t +ipa_selinux_get_maps_next(struct tevent_req *req, + struct ipa_selinux_get_maps_state *state) +{ + struct sdap_search_base *base; + struct tevent_req *subreq; + + base = state->search_bases[state->search_base_iter]; + if (base == NULL) { + return EOK; + } + + talloc_zfree(state->cur_filter); + state->cur_filter = sdap_get_id_specific_filter(state, state->maps_filter, + base->filter); + if (state->cur_filter == NULL) { + return ENOMEM; + } + + DEBUG(SSSDBG_TRACE_FUNC, ("Trying to fetch SELinux maps with following " + "parameters: [%d][%s][%s]\n", base->scope, + base->filter, base->basedn)); + subreq = sdap_get_generic_send(state, state->ev, state->opts, + state->sh, base->basedn, + base->scope, state->cur_filter, + state->attrs, + state->opts->selinuxuser_map, + IPA_OPTS_SELINUX_USERMAP, + dp_opt_get_int(state->opts->basic, + SDAP_ENUM_SEARCH_TIMEOUT)); + if (subreq == NULL) { + return ENOMEM; + } + + tevent_req_set_callback(subreq, ipa_selinux_get_maps_done, req); + return EAGAIN; +} + +static void ipa_selinux_get_maps_done(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct ipa_selinux_get_maps_state *state = tevent_req_data(req, + struct ipa_selinux_get_maps_state); + struct sysdb_attrs **results; + size_t total_count; + size_t count; + int i; + + ret = sdap_get_generic_recv(subreq, state, &count, &results); + if (ret != EOK) { + goto done; + } + + if (count > 0) { + DEBUG(SSSDBG_TRACE_FUNC, ("Found %d user maps in current search base\n", + count)); + + total_count = count + state->map_count; + state->maps = talloc_realloc(state, state->maps, struct sysdb_attrs *, total_count); + if (state->maps == NULL) { + ret = ENOMEM; + goto done; + } + + i = 0; + while (state->map_count < total_count) { + state->maps[state->map_count] = talloc_steal(state->maps, results[i]); + state->map_count++; + i++; + } + } + + state->search_base_iter++; + ret = ipa_selinux_get_maps_next(req, state); + if (ret == EAGAIN) { + return; + } else if (ret != EOK) { + goto done; + } + + if (state->map_count == 0) { + DEBUG(SSSDBG_TRACE_FUNC, ("No SELinux user maps found!\n")); + ret = ENOENT; + } + +done: + if (ret != EOK) { + tevent_req_error(req, ret); + } else { + tevent_req_done(req); + } +} + +errno_t +ipa_selinux_get_maps_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *count, + struct sysdb_attrs ***maps) +{ + struct ipa_selinux_get_maps_state *state = + tevent_req_data(req, struct ipa_selinux_get_maps_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *count = state->map_count; + *maps = talloc_steal(mem_ctx, state->maps); + + return EOK; +} diff --git a/src/providers/ipa/ipa_selinux_maps.h b/src/providers/ipa/ipa_selinux_maps.h new file mode 100644 index 000000000..efd10bf4e --- /dev/null +++ b/src/providers/ipa/ipa_selinux_maps.h @@ -0,0 +1,44 @@ +/* + SSSD + + IPA Backend Module -- SELinux user maps (maps retrieval) + + Authors: + Jan Zeleny <jzeleny@redhat.com> + + Copyright (C) 2012 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef IPA_SELINUX_MAPS_H_ +#define IPA_SELINUX_MAPS_H_ + +#include "providers/ldap/sdap_async.h" + +struct tevent_req * +ipa_selinux_get_maps_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sdap_handle *sh, + struct sdap_options *opts, + struct sdap_search_base **search_bases); + +errno_t +ipa_selinux_get_maps_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *count, + struct sysdb_attrs ***maps); + +#endif /* IPA_SELINUX_MAPS_H_ */ diff --git a/src/providers/ipa/ipa_session.c b/src/providers/ipa/ipa_session.c new file mode 100644 index 000000000..40e8c186b --- /dev/null +++ b/src/providers/ipa/ipa_session.c @@ -0,0 +1,616 @@ +/* + SSSD + + IPA Backend Module -- session loading + + Authors: + Jan Zeleny <jzeleny@redhat.com> + + Copyright (C) 2012 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <security/pam_modules.h> + +#include "db/sysdb_selinux.h" +#include "util/sss_selinux.h" +#include "providers/ldap/sdap_async.h" +#include "providers/ipa/ipa_common.h" +#include "providers/ipa/ipa_config.h" +#include "providers/ipa/ipa_session.h" +#include "providers/ipa/ipa_hosts.h" +#include "providers/ipa/ipa_hbac_rules.h" +#include "providers/ipa/ipa_selinux_common.h" +#include "providers/ipa/ipa_selinux_maps.h" + +/* FIXME: this is temporary until host map is implemented in ipa_common.c */ +#include "providers/ipa/ipa_hbac_private.h" + +struct ipa_get_selinux_state { + struct be_req *be_req; + struct pam_data *pd; + struct ipa_session_ctx *session_ctx; + struct sdap_id_op *op; + + /* Just tmp stuff so we can free it after query */ + const char **attrs; + + const char *hostname; + struct sysdb_attrs *host; + struct sysdb_attrs *user; + + struct sysdb_attrs *defaults; + + struct sysdb_attrs **possible_match; + struct sysdb_attrs **confirmed_match; +}; + +static struct +tevent_req *ipa_get_selinux_send(struct be_req *breq, + struct pam_data *pd, + struct ipa_session_ctx *session_ctx); +static void ipa_session_handler_done(struct tevent_req *subreq); +static errno_t ipa_get_selinux_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *count, + struct sysdb_attrs ***maps, + char **default_user, + char **map_order); + +static void ipa_get_selinux_connect_done(struct tevent_req *subreq); +static void ipa_get_selinux_hosts_done(struct tevent_req *subreq); +static void ipa_get_selinux_maps_done(struct tevent_req *subreq); +static void ipa_get_selinux_hbac_done(struct tevent_req *subreq); +static void ipa_get_selinux_config_done(struct tevent_req *subreq); + +void ipa_session_handler(struct be_req *be_req) +{ + struct ipa_session_ctx *session_ctx; + struct tevent_req *req; + struct pam_data *pd; + + pd = talloc_get_type(be_req->req_data, struct pam_data); + + session_ctx = talloc_get_type( + be_req->be_ctx->bet_info[BET_SESSION].pvt_bet_data, + struct ipa_session_ctx); + + + req = ipa_get_selinux_send(be_req, pd, session_ctx); + if (req == NULL) { + goto fail; + } + + tevent_req_set_callback(req, ipa_session_handler_done, be_req); + + return; + +fail: + be_req->fn(be_req, DP_ERR_FATAL, PAM_SYSTEM_ERR, NULL); +} + +static void ipa_session_handler_done(struct tevent_req *req) +{ + struct be_req *breq = tevent_req_callback_data(req, struct be_req); + struct sysdb_ctx *sysdb = breq->be_ctx->sysdb; + errno_t ret, sret; + size_t map_count; + struct sysdb_attrs **maps; + bool in_transaction = false; + char *default_user; + char *map_order; + + ret = ipa_get_selinux_recv(req, breq, &map_count, &maps, + &default_user, &map_order); + if (ret != EOK) { + goto fail; + } + + ret = sysdb_transaction_start(sysdb); + if (ret != EOK) goto fail; + in_transaction = true; + + if (default_user != NULL && map_order != NULL) { + ret = sysdb_store_selinux_config(sysdb, default_user, map_order); + if (ret != EOK) { + goto fail; + } + } + + if (map_count > 0 && maps != NULL) { + ret = ipa_save_user_maps(sysdb, map_count, maps); + if (ret != EOK) { + goto fail; + } + } + + ret = sysdb_transaction_commit(sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Could not commit transaction\n")); + goto fail; + } + + /* Just in case more code will follow after this place in the future */ + in_transaction = false; + + + breq->fn(breq, DP_ERR_OK, EOK, "Success"); + return; + +fail: + if (in_transaction) { + sret = sysdb_transaction_cancel(sysdb); + if (sret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Could not cancel transaction\n")); + } + } + if (ret == EAGAIN) { + breq->fn(breq, DP_ERR_OFFLINE, EAGAIN, "Offline"); + } else { + breq->fn(breq, DP_ERR_FATAL, ret, NULL); + } +} + +static struct tevent_req *ipa_get_selinux_send(struct be_req *breq, + struct pam_data *pd, + struct ipa_session_ctx *session_ctx) +{ + struct tevent_req *req; + struct tevent_req *subreq; + struct be_ctx *bctx = breq->be_ctx; + struct ipa_get_selinux_state *state; + bool offline; + int ret = EOK; + + DEBUG(SSSDBG_TRACE_FUNC, ("Retrieving SELinux user mapping\n")); + req = tevent_req_create(breq, &state, struct ipa_get_selinux_state); + if (req == NULL) { + return NULL; + } + + state->be_req = breq; + state->pd = pd; + state->session_ctx = session_ctx; + + offline = be_is_offline(bctx); + DEBUG(SSSDBG_TRACE_INTERNAL, ("Connection status is [%s].\n", + offline ? "offline" : "online")); + + if (!offline) { + state->op = sdap_id_op_create(state, session_ctx->id_ctx->sdap_id_ctx->conn_cache); + if (!state->op) { + DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_create failed\n")); + ret = ENOMEM; + goto immediate; + } + + subreq = sdap_id_op_connect_send(state->op, state, &ret); + if (!subreq) { + DEBUG(SSSDBG_CRIT_FAILURE, ("sdap_id_op_connect_send failed: " + "%d(%s).\n", ret, strerror(ret))); + talloc_zfree(state->op); + goto immediate; + } + + tevent_req_set_callback(subreq, ipa_get_selinux_connect_done, req); + } else { + ret = EAGAIN; + goto immediate; + } + + return req; + +immediate: + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + tevent_req_post(req, bctx->ev); + return req; +} + +static void ipa_get_selinux_connect_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct ipa_get_selinux_state *state = tevent_req_data(req, + struct ipa_get_selinux_state); + int dp_error = DP_ERR_FATAL; + int ret; + struct ipa_id_ctx *id_ctx = state->session_ctx->id_ctx; + struct be_ctx *bctx = state->be_req->be_ctx; + + ret = sdap_id_op_connect_recv(subreq, &dp_error); + talloc_zfree(subreq); + + if (dp_error == DP_ERR_OFFLINE) { + talloc_zfree(state->op); + ret = EAGAIN; + } + + if (ret != EOK) { + goto fail; + } + + state->hostname = dp_opt_get_string(state->session_ctx->id_ctx->ipa_options->basic, + IPA_HOSTNAME); + + /* FIXME: detect if HBAC is configured + * - if yes, we can skip host retrieval and get it directly from sysdb + */ + state->attrs = talloc_array(state, const char *, 3); + if (state->attrs == NULL) { + ret = ENOMEM; + goto fail; + } + state->attrs[0] = "objectClass"; + state->attrs[1] = IPA_MEMBEROF; + state->attrs[2] = NULL; + + subreq = ipa_host_info_send(state, bctx->ev, bctx->sysdb, + sdap_id_op_handle(state->op), + id_ctx->sdap_id_ctx->opts, + state->hostname, + state->attrs, false, state->session_ctx->host_search_bases); + if (subreq == NULL) { + ret = ENOMEM; + goto fail; + } + + tevent_req_set_callback(subreq, ipa_get_selinux_hosts_done, req); + + return; + +fail: + tevent_req_error(req, ret); +} + +static void ipa_get_selinux_hosts_done(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct ipa_get_selinux_state *state = tevent_req_data(req, + struct ipa_get_selinux_state); + struct be_ctx *bctx = state->be_req->be_ctx; + struct sdap_id_ctx *id_ctx = state->session_ctx->id_ctx->sdap_id_ctx; + size_t host_count, hostgroup_count; + struct sysdb_attrs **hostgroups; + struct sysdb_attrs **host; + + ret = ipa_host_info_recv(subreq, state, &host_count, &host, + &hostgroup_count, &hostgroups); + talloc_free(subreq); + if (ret != EOK) { + goto done; + } + state->host = host[0]; + + ret = sysdb_attrs_add_string(state->host, SYSDB_NAME, state->hostname); + if (ret != EOK) { + goto done; + } + + ret = sss_selinux_extract_user(state, bctx->sysdb, + state->pd->user, &state->user); + if (ret != EOK) { + goto done; + } + + subreq = ipa_selinux_get_maps_send(state, bctx->ev, bctx->sysdb, + sdap_id_op_handle(state->op), + id_ctx->opts, + state->session_ctx->selinux_search_bases); + if (subreq == NULL) { + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, ipa_get_selinux_maps_done, req); + +done: + if (ret != EOK) { + tevent_req_error(req, ret); + } +} + +static void ipa_get_selinux_maps_done(struct tevent_req *subreq) +{ + struct tevent_req *req; + struct ipa_get_selinux_state *state; + + struct be_ctx *bctx; + struct ipa_id_ctx *id_ctx; + + struct sysdb_attrs **results; + size_t count; + const char *domain; + const char *tmp_str; + size_t conf_cnt = 0; + size_t pos_cnt = 0; + errno_t ret; + int i; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_get_selinux_state); + bctx = state->be_req->be_ctx; + id_ctx = state->session_ctx->id_ctx; + + ret = ipa_selinux_get_maps_recv(subreq, state, &count, &results); + talloc_free(subreq); + if (ret != EOK) { + if (ret == ENOENT) { + /* This is returned if no SELinux mapping + * rules were found. In that case no error + * occurred, but we don't want any more processing.*/ + ret = EOK; + } + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, ("Found %d SELinux user maps in total, " + "filtering ...\n", count)); + state->confirmed_match = talloc_zero_array(state, struct sysdb_attrs *, + count + 1); + state->possible_match = talloc_zero_array(state, struct sysdb_attrs *, + count + 1); + if (state->confirmed_match == NULL || state->possible_match == NULL) { + ret = ENOMEM; + goto done; + } + + for (i = 0; i < count; i++) { + if (sss_selinux_match(results[i], state->user, state->host)) { + state->confirmed_match[conf_cnt] = talloc_steal(state->confirmed_match, + results[i]); + conf_cnt++; + continue; + } + + ret = sysdb_attrs_get_string(results[i], SYSDB_SELINUX_SEEALSO, &tmp_str); + if (ret == ENOENT) { + continue; + } + + state->possible_match[pos_cnt] = talloc_steal(state->possible_match, + results[i]); + pos_cnt++; + } + DEBUG(SSSDBG_TRACE_FUNC, ("Filtering done. Results: %d confirmed and %d " + "possible maps remained.\n", conf_cnt, pos_cnt)); + + /* Don't shrink the confirmed list, it could be later filled by HBAC rules */ + state->possible_match = talloc_realloc(state, state->possible_match, + struct sysdb_attrs *, pos_cnt + 1); + if (state->possible_match == NULL) { + ret = ENOMEM; + goto done; + } + + /* This will free all rules that are neither confirmed nor possible */ + talloc_free(results); + + if (pos_cnt) { + /* FIXME: detect if HBAC is configured + * - if yes, we can skip HBAC retrieval and get it directly from sysdb + */ + subreq = ipa_hbac_rule_info_send(state, false, bctx->ev, + sdap_id_op_handle(state->op), + id_ctx->sdap_id_ctx->opts, + state->session_ctx->hbac_search_bases, + state->host); + if (subreq == NULL) { + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, ipa_get_selinux_hbac_done, req); + return; + } + + domain = dp_opt_get_string(state->session_ctx->id_ctx->ipa_options->basic, + IPA_KRB5_REALM); + subreq = ipa_get_config_send(state, bctx->ev, + sdap_id_op_handle(state->op), + id_ctx->sdap_id_ctx->opts, + domain, NULL); + if (subreq == NULL) { + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, ipa_get_selinux_config_done, req); + + return; + +done: + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } +} + +static void ipa_get_selinux_hbac_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct ipa_get_selinux_state *state = tevent_req_data(req, + struct ipa_get_selinux_state); + struct be_ctx *bctx = state->be_req->be_ctx; + struct ipa_id_ctx *id_ctx = state->session_ctx->id_ctx; + struct sysdb_attrs **rules; + struct sysdb_attrs *usermap; + const char *hbac_dn; + const char *seealso_dn; + const char *domain; + size_t rule_count; + size_t conf_cnt; + size_t pos_cnt; + errno_t ret; + int i, j; + + ret = ipa_hbac_rule_info_recv(subreq, state, &rule_count, + &rules); + talloc_free(subreq); + if (ret != EOK) { + goto done; + } + for (conf_cnt = 0 ; state->confirmed_match[conf_cnt]; conf_cnt++) ; + for (pos_cnt = 0 ; state->possible_match[pos_cnt]; pos_cnt++) ; + + for (i = 0; i < rule_count; i++) { + if (!sss_selinux_match(rules[i], state->user, state->host)) { + continue; + } + + ret = sysdb_attrs_get_string(rules[i], SYSDB_ORIG_DN, &hbac_dn); + if (ret != EOK) { + goto done; + } + + /* HBAC rule matched, find if it is in the "possible" list */ + for (j = 0; state->possible_match[j]; j++) { + usermap = state->possible_match[j]; + if (usermap == NULL) { + continue; + } + + ret = sysdb_attrs_get_string(usermap, SYSDB_SELINUX_SEEALSO, &seealso_dn); + if (ret != EOK) { + goto done; + } + + if (strcasecmp(hbac_dn, seealso_dn) == 0) { + state->confirmed_match[conf_cnt++] = talloc_steal( + state->confirmed_match, + usermap); + /* Just to boost the following lookup */ + state->possible_match[j] = NULL; + + ret = ipa_selinux_map_merge(usermap, rules[i], SYSDB_ORIG_MEMBER_USER); + if (ret != EOK) { + goto done; + } + + ret = ipa_selinux_map_merge(usermap, rules[i], SYSDB_ORIG_MEMBER_HOST); + if (ret != EOK) { + goto done; + } + } + } + } + + /* Now we can dispose all possible rules, since they aren't possible any more */ + talloc_zfree(state->possible_match); + + domain = dp_opt_get_string(state->session_ctx->id_ctx->ipa_options->basic, + IPA_KRB5_REALM); + subreq = ipa_get_config_send(state, bctx->ev, + sdap_id_op_handle(state->op), + id_ctx->sdap_id_ctx->opts, + domain, NULL); + if (subreq == NULL) { + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, ipa_get_selinux_config_done, req); + +done: + if (ret != EOK) { + tevent_req_error(req, ret); + } +} + +static void ipa_get_selinux_config_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct ipa_get_selinux_state *state = tevent_req_data(req, + struct ipa_get_selinux_state); + errno_t ret; + + ret = ipa_get_config_recv(subreq, state, &state->defaults); + talloc_free(subreq); + if (ret != EOK) { + goto done; + } + +done: + if (ret != EOK) { + tevent_req_error(req, ret); + } else { + tevent_req_done(req); + } +} + +static errno_t +ipa_get_selinux_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *count, + struct sysdb_attrs ***maps, + char **default_user, + char **map_order) +{ + struct ipa_get_selinux_state *state = + tevent_req_data(req, struct ipa_get_selinux_state); + const char *tmp_str; + errno_t ret; + int i; + + TEVENT_REQ_RETURN_ON_ERROR(req); + + if (state->defaults != NULL) { + ret = sysdb_attrs_get_string(state->defaults, IPA_CONFIG_SELINUX_DEFAULT_MAP, + &tmp_str); + if (ret != EOK) { + return ret; + } + + *default_user = talloc_strdup(mem_ctx, tmp_str); + if (*default_user == NULL) { + return ENOMEM; + } + + ret = sysdb_attrs_get_string(state->defaults, IPA_CONFIG_SELINUX_MAP_ORDER, + &tmp_str); + if (ret != EOK) { + return ret; + } + + *map_order = talloc_strdup(mem_ctx, tmp_str); + if (*map_order == NULL) { + talloc_zfree(*default_user); + return ENOMEM; + } + } else { + *map_order = NULL; + *default_user = NULL; + } + + if (state->confirmed_match != NULL) { + for (i = 0; state->confirmed_match[i]; i++) ; + + *count = i; + *maps = talloc_steal(mem_ctx, state->confirmed_match); + } else { + *count = 0; + *maps = NULL; + } + + return EOK; +} diff --git a/src/providers/ipa/ipa_session.h b/src/providers/ipa/ipa_session.h new file mode 100644 index 000000000..e185799f7 --- /dev/null +++ b/src/providers/ipa/ipa_session.h @@ -0,0 +1,40 @@ +/* + SSSD + + IPA Backend Module -- session loading + + Authors: + Jan Zeleny <jzeleny@redhat.com> + + Copyright (C) 2011 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _IPA_SESSION_H_ +#define _IPA_SESSION_H_ + +#include "providers/ldap/ldap_common.h" + +struct ipa_session_ctx { + struct ipa_id_ctx *id_ctx; + + struct sdap_search_base **selinux_search_bases; + struct sdap_search_base **host_search_bases; + struct sdap_search_base **hbac_search_bases; +}; + +void ipa_session_handler(struct be_req *be_req); + +#endif diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h index 0a0f996d1..19d9ed9ad 100644 --- a/src/providers/ldap/sdap.h +++ b/src/providers/ldap/sdap.h @@ -353,6 +353,7 @@ struct sdap_options { struct sdap_attr_map *sudorule_map; struct sdap_attr_map *autofs_mobject_map; struct sdap_attr_map *autofs_entry_map; + struct sdap_attr_map *selinuxuser_map; /* supported schema types */ enum schema_type { |