ipa_selinux.c revision ccc2af010bbbe6d8a7496fb717216135bc4c1993
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz/*
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz SSSD
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz IPA Backend Module -- selinux loading
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz Authors:
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd Jan Zeleny <jzeleny@redhat.com>
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd Copyright (C) 2012 Red Hat
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd This program is free software; you can redistribute it and/or modify
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd it under the terms of the GNU General Public License as published by
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd the Free Software Foundation; either version 3 of the License, or
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd (at your option) any later version.
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd
52fff662005b1866a3ff09bb6c902800c5cc6dedjerenkrantz This program is distributed in the hope that it will be useful,
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd but WITHOUT ANY WARRANTY; without even the implied warranty of
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd GNU General Public License for more details.
4b5981e276e93df97c34e4da05ca5cf8bbd937dand
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd You should have received a copy of the GNU General Public License
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd along with this program. If not, see <http://www.gnu.org/licenses/>.
3b3b7fc78d1f5bfc2769903375050048ff41ff26nd*/
0066eddda7203f6345b56f77d146a759298dc635gryzor
0066eddda7203f6345b56f77d146a759298dc635gryzor#include <security/pam_modules.h>
3b3b7fc78d1f5bfc2769903375050048ff41ff26nd
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd#include "db/sysdb_selinux.h"
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd#include "util/sss_selinux.h"
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd#include "providers/ldap/sdap_async.h"
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd#include "providers/ipa/ipa_common.h"
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd#include "providers/ipa/ipa_config.h"
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin#include "providers/ipa/ipa_selinux.h"
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd#include "providers/ipa/ipa_hosts.h"
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd#include "providers/ipa/ipa_hbac_rules.h"
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin#include "providers/ipa/ipa_hbac_private.h"
3267af3f6fbf9743e64a9f019c745317f18cd9f7poirier#include "providers/ipa/ipa_access.h"
3267af3f6fbf9743e64a9f019c745317f18cd9f7poirier#include "providers/ipa/ipa_selinux_common.h"
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin#include "providers/ipa/ipa_selinux_maps.h"
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirinstatic struct tevent_req *
cb3a1082aec4b3b4f4ed238c93c3cc54933a7f0endipa_get_selinux_send(TALLOC_CTX *mem_ctx,
cb3a1082aec4b3b4f4ed238c93c3cc54933a7f0end struct be_ctx *be_ctx,
f8b7daeb0e3f0ac4544fcc665de10c6b69a1ce0dsf struct sysdb_attrs *user,
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz struct sysdb_attrs *host,
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz struct ipa_selinux_ctx *selinux_ctx);
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantzstatic errno_t ipa_get_selinux_recv(struct tevent_req *req,
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz TALLOC_CTX *mem_ctx,
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz size_t *count,
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz struct sysdb_attrs ***maps,
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz size_t *hbac_count,
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd struct sysdb_attrs ***hbac_rules,
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd char **default_user,
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin char **map_order);
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirinstatic struct ipa_selinux_op_ctx *
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirinipa_selinux_create_op_ctx(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
35ff2d06df95b9593ee312dfff883c76f3b97798noodl struct sss_domain_info *domain,
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin struct be_req *be_req, const char *username,
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd const char *hostname);
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirinstatic void ipa_selinux_handler_done(struct tevent_req *subreq);
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirinstatic void ipa_get_selinux_connect_done(struct tevent_req *subreq);
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4ndstatic void ipa_get_selinux_hosts_done(struct tevent_req *subreq);
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4ndstatic void ipa_get_config_step(struct tevent_req *req);
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirinstatic void ipa_get_selinux_config_done(struct tevent_req *subreq);
35ff2d06df95b9593ee312dfff883c76f3b97798noodlstatic void ipa_get_selinux_maps_done(struct tevent_req *subreq);
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirinstatic void ipa_get_selinux_hbac_done(struct tevent_req *subreq);
3267af3f6fbf9743e64a9f019c745317f18cd9f7poirierstatic errno_t ipa_selinux_process_maps(struct sysdb_attrs *user,
3267af3f6fbf9743e64a9f019c745317f18cd9f7poirier struct sysdb_attrs *host,
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin struct sysdb_attrs **selinux_maps,
f8b7daeb0e3f0ac4544fcc665de10c6b69a1ce0dsf size_t selinux_map_count,
f8b7daeb0e3f0ac4544fcc665de10c6b69a1ce0dsf struct sysdb_attrs **hbac_rules,
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin size_t hbac_rule_count);
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirinstruct ipa_selinux_op_ctx {
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz struct be_req *be_req;
f8b7daeb0e3f0ac4544fcc665de10c6b69a1ce0dsf struct sss_domain_info *domain;
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz
a7a43799fed7fcdeaa70584dbd3ecd130b25deb3noodl struct sysdb_attrs *user;
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz struct sysdb_attrs *host;
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin};
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirinvoid ipa_selinux_handler(struct be_req *be_req)
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin{
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz struct ipa_selinux_ctx *selinux_ctx;
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin struct ipa_selinux_op_ctx *op_ctx;
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin struct tevent_req *req;
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin struct pam_data *pd;
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin const char *hostname;
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin pd = talloc_get_type(be_req->req_data, struct pam_data);
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin selinux_ctx = talloc_get_type(
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin be_req->be_ctx->bet_info[BET_SELINUX].pvt_bet_data,
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin struct ipa_selinux_ctx);
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin hostname = dp_opt_get_string(selinux_ctx->id_ctx->ipa_options->basic,
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin IPA_HOSTNAME);
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin if (!hostname) {
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin DEBUG(SSSDBG_OP_FAILURE, ("Cannot determine this machine's host name\n"));
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin goto fail;
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin }
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin op_ctx = ipa_selinux_create_op_ctx(be_req, be_req->be_ctx->domain->sysdb,
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin be_req->be_ctx->domain,
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin be_req, pd->user, hostname);
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin if (op_ctx == NULL) {
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin DEBUG(SSSDBG_OP_FAILURE, ("Cannot create op context\n"));
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin goto fail;
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin }
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin req = ipa_get_selinux_send(be_req, be_req->be_ctx,
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin op_ctx->user, op_ctx->host, selinux_ctx);
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin if (req == NULL) {
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin DEBUG(SSSDBG_OP_FAILURE, ("Cannot initiate the search\n"));
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin goto fail;
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin }
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin tevent_req_set_callback(req, ipa_selinux_handler_done, op_ctx);
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin return;
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirinfail:
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin be_req->fn(be_req, DP_ERR_FATAL, PAM_SYSTEM_ERR, NULL);
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin}
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirinstatic struct ipa_selinux_op_ctx *
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirinipa_selinux_create_op_ctx(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin struct sss_domain_info *domain,
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin struct be_req *be_req, const char *username,
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz const char *hostname)
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz{
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz struct ipa_selinux_op_ctx *op_ctx;
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz struct ldb_dn *host_dn;
a7a43799fed7fcdeaa70584dbd3ecd130b25deb3noodl const char *attrs[] = { SYSDB_ORIG_DN,
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz SYSDB_ORIG_MEMBEROF,
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin NULL };
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin size_t count;
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin struct ldb_message **msgs;
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin struct sysdb_attrs **hosts;
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz errno_t ret;
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin op_ctx = talloc_zero(mem_ctx, struct ipa_selinux_op_ctx);
aa0b2780958e9b1467c9d0153a05738e399811a5nd if (op_ctx == NULL) {
9a367ec3d570bcbaf8923dad66cb3b1532963964trawick return NULL;
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin }
aa0b2780958e9b1467c9d0153a05738e399811a5nd op_ctx->be_req = be_req;
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin op_ctx->domain = domain;
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin ret = sss_selinux_extract_user(op_ctx, sysdb, domain, username, &op_ctx->user);
9a367ec3d570bcbaf8923dad66cb3b1532963964trawick if (ret != EOK) {
9a367ec3d570bcbaf8923dad66cb3b1532963964trawick goto fail;
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin }
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin host_dn = sysdb_custom_dn(sysdb, op_ctx, domain, hostname, HBAC_HOSTS_SUBDIR);
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin if (host_dn == NULL) {
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin goto fail;
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin }
8951c7d73bfa2ae5a2c8fe5bd27f3e677be02564noirin
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz /* Look up the host to get its originalMemberOf entries */
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz ret = sysdb_search_entry(op_ctx, sysdb, host_dn,
1f53e295ebd19aed1767d12da7abfab9936c148cjerenkrantz LDB_SCOPE_BASE, NULL,
1b390add6886fb1c0acdea82be0ef0920f1158casf attrs, &count, &msgs);
1b390add6886fb1c0acdea82be0ef0920f1158casf if (ret == ENOENT || count == 0) {
1b390add6886fb1c0acdea82be0ef0920f1158casf op_ctx->host = NULL;
1b390add6886fb1c0acdea82be0ef0920f1158casf return op_ctx;
1b390add6886fb1c0acdea82be0ef0920f1158casf } else if (ret != EOK) {
1b390add6886fb1c0acdea82be0ef0920f1158casf goto fail;
1b390add6886fb1c0acdea82be0ef0920f1158casf } else if (count > 1) {
1b390add6886fb1c0acdea82be0ef0920f1158casf DEBUG(SSSDBG_OP_FAILURE, ("More than one result for a BASE search!\n"));
1b390add6886fb1c0acdea82be0ef0920f1158casf goto fail;
1b390add6886fb1c0acdea82be0ef0920f1158casf }
1b390add6886fb1c0acdea82be0ef0920f1158casf
1b390add6886fb1c0acdea82be0ef0920f1158casf ret = sysdb_msg2attrs(op_ctx, count, msgs, &hosts);
1b390add6886fb1c0acdea82be0ef0920f1158casf talloc_free(msgs);
1b390add6886fb1c0acdea82be0ef0920f1158casf if (ret != EOK) {
1b390add6886fb1c0acdea82be0ef0920f1158casf goto fail;
1b390add6886fb1c0acdea82be0ef0920f1158casf }
1b390add6886fb1c0acdea82be0ef0920f1158casf
1b390add6886fb1c0acdea82be0ef0920f1158casf op_ctx->host = hosts[0];
1b390add6886fb1c0acdea82be0ef0920f1158casf return op_ctx;
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4ndfail:
3b3b7fc78d1f5bfc2769903375050048ff41ff26nd talloc_free(op_ctx);
0066eddda7203f6345b56f77d146a759298dc635gryzor return NULL;
0066eddda7203f6345b56f77d146a759298dc635gryzor}
3b3b7fc78d1f5bfc2769903375050048ff41ff26nd
50039065d571fe01fd458a3f031c995a1fd53c22rbowenstatic void ipa_selinux_handler_done(struct tevent_req *req)
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd{
bdd978e5ecd8daa2542d4d4e1988c78a622cd7f4nd struct ipa_selinux_op_ctx *op_ctx = tevent_req_callback_data(req, struct ipa_selinux_op_ctx);
struct be_req *breq = op_ctx->be_req;
struct sysdb_ctx *sysdb = op_ctx->domain->sysdb;
errno_t ret, sret;
size_t map_count = 0;
struct sysdb_attrs **maps = NULL;
bool in_transaction = false;
char *default_user = NULL;
struct pam_data *pd = talloc_get_type(breq->req_data, struct pam_data);
char *map_order = NULL;
size_t hbac_count = 0;
struct sysdb_attrs **hbac_rules = 0;
ret = ipa_get_selinux_recv(req, breq, &map_count, &maps,
&hbac_count, &hbac_rules,
&default_user, &map_order);
if (ret != EOK) {
goto fail;
}
ret = ipa_selinux_process_maps(op_ctx->user, op_ctx->host,
maps, map_count,
hbac_rules, hbac_count);
if (ret != EOK) {
goto fail;
}
ret = sysdb_transaction_start(sysdb);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to start transaction\n"));
goto fail;
}
in_transaction = true;
ret = sysdb_delete_usermaps(op_ctx->domain->sysdb, op_ctx->domain);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,
("Cannot delete existing maps from sysdb\n"));
goto fail;
}
ret = sysdb_store_selinux_config(sysdb, op_ctx->domain,
default_user, map_order);
if (ret != EOK) {
goto fail;
}
if (map_count > 0 && maps != NULL) {
ret = ipa_save_user_maps(sysdb, op_ctx->domain, 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;
}
in_transaction = false;
pd->pam_status = PAM_SUCCESS;
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 errno_t
ipa_selinux_process_seealso_maps(struct sysdb_attrs *user,
struct sysdb_attrs *host,
struct sysdb_attrs **seealso_rules,
size_t seealso_rules_count,
struct sysdb_attrs **hbac_rules,
size_t hbac_rule_count);
static errno_t
ipa_selinux_process_maps(struct sysdb_attrs *user,
struct sysdb_attrs *host,
struct sysdb_attrs **selinux_maps,
size_t selinux_map_count,
struct sysdb_attrs **hbac_rules,
size_t hbac_rule_count)
{
TALLOC_CTX *tmp_ctx;
int i;
errno_t ret;
uint32_t priority = 0;
struct sysdb_attrs **seealso_rules;
size_t num_seealso_rules;
const char *seealso_str;
tmp_ctx = talloc_new(NULL);
if (!tmp_ctx) {
return ENOMEM;
}
seealso_rules = talloc_zero_array(tmp_ctx, struct sysdb_attrs *,
selinux_map_count + 1);
if (seealso_rules == NULL) {
ret = ENOMEM;
goto done;
}
num_seealso_rules = 0;
for (i = 0; i < selinux_map_count; i++) {
if (sss_selinux_match(selinux_maps[i], user,
host, &priority)) {
priority &= ~(SELINUX_PRIORITY_USER_NAME |
SELINUX_PRIORITY_USER_GROUP |
SELINUX_PRIORITY_USER_CAT);
ret = sysdb_attrs_add_uint32(selinux_maps[i],
SYSDB_SELINUX_HOST_PRIORITY,
priority);
if (ret != EOK) {
goto done;
}
continue;
}
ret = sysdb_attrs_get_string(selinux_maps[i],
SYSDB_SELINUX_SEEALSO, &seealso_str);
if (ret == ENOENT) {
continue;
} else if (ret != EOK) {
goto done;
}
seealso_rules[num_seealso_rules] = selinux_maps[i];
num_seealso_rules++;
}
ret = ipa_selinux_process_seealso_maps(user, host,
seealso_rules, num_seealso_rules,
hbac_rules, hbac_rule_count);
if (ret != EOK) {
goto done;
}
ret = EOK;
done:
talloc_free(tmp_ctx);
return ret;
}
static errno_t
ipa_selinux_process_seealso_maps(struct sysdb_attrs *user,
struct sysdb_attrs *host,
struct sysdb_attrs **seealso_rules,
size_t seealso_rules_count,
struct sysdb_attrs **hbac_rules,
size_t hbac_rule_count)
{
int i, j;
errno_t ret;
struct ldb_message_element *el;
uint32_t priority = 0;
struct sysdb_attrs *usermap;
const char *seealso_dn;
const char *hbac_dn;
for (i = 0; i < hbac_rule_count; i++) {
ret = sysdb_attrs_get_string(hbac_rules[i], SYSDB_ORIG_DN, &hbac_dn);
if (ret != EOK) {
return ret;
}
/* We need to do this translation for further processing. We have to
* do it manually because no map was used to retrieve HBAC rules.
*/
ret = sysdb_attrs_get_el(hbac_rules[i], IPA_MEMBER_HOST, &el);
if (ret != EOK) return ret;
el->name = SYSDB_ORIG_MEMBER_HOST;
ret = sysdb_attrs_get_el(hbac_rules[i], IPA_MEMBER_USER, &el);
if (ret != EOK) return ret;
el->name = SYSDB_ORIG_MEMBER_USER;
DEBUG(SSSDBG_TRACE_ALL,
("Matching HBAC rule %s with SELinux mappings\n", hbac_dn));
if (!sss_selinux_match(hbac_rules[i], user, host, &priority)) {
DEBUG(SSSDBG_TRACE_ALL, ("Rule did not match\n"));
continue;
}
/* HBAC rule matched, find if it is in the "possible" list */
for (j = 0; j < seealso_rules_count; j++) {
usermap = seealso_rules[j];
if (usermap == NULL) {
continue;
}
ret = sysdb_attrs_get_string(usermap, SYSDB_SELINUX_SEEALSO, &seealso_dn);
if (ret != EOK) {
return ret;
}
if (strcasecmp(hbac_dn, seealso_dn) == 0) {
DEBUG(SSSDBG_TRACE_FUNC, ("HBAC rule [%s] matched, copying its"
"attributes to SELinux user map [%s]\n",
hbac_dn, seealso_dn));
priority &= ~(SELINUX_PRIORITY_USER_NAME |
SELINUX_PRIORITY_USER_GROUP |
SELINUX_PRIORITY_USER_CAT);
ret = sysdb_attrs_add_uint32(usermap,
SYSDB_SELINUX_HOST_PRIORITY,
priority);
if (ret != EOK) {
return ret;
}
ret = sysdb_attrs_copy_values(hbac_rules[i], usermap, SYSDB_ORIG_MEMBER_USER);
if (ret != EOK) {
return ret;
}
ret = sysdb_attrs_copy_values(hbac_rules[i], usermap, SYSDB_USER_CATEGORY);
if (ret != EOK) {
return ret;
}
/* Speed up the next iteration */
seealso_rules[j] = NULL;
}
}
}
return EOK;
}
/* A more generic request to gather all SELinux and HBAC rules. Updates
* cache if necessary
*/
struct ipa_get_selinux_state {
struct be_ctx *be_ctx;
struct ipa_selinux_ctx *selinux_ctx;
struct sdap_id_op *op;
struct sysdb_attrs *host;
struct sysdb_attrs *user;
struct sysdb_attrs *defaults;
struct sysdb_attrs **selinuxmaps;
size_t nmaps;
struct sysdb_attrs **hbac_rules;
size_t hbac_rule_count;
};
static errno_t
ipa_get_selinux_maps_offline(struct tevent_req *req);
static struct tevent_req *
ipa_get_selinux_send(TALLOC_CTX *mem_ctx,
struct be_ctx *be_ctx,
struct sysdb_attrs *user,
struct sysdb_attrs *host,
struct ipa_selinux_ctx *selinux_ctx)
{
struct tevent_req *req;
struct tevent_req *subreq;
struct ipa_get_selinux_state *state;
bool offline;
int ret = EOK;
DEBUG(SSSDBG_TRACE_FUNC, ("Retrieving SELinux user mapping\n"));
req = tevent_req_create(mem_ctx, &state, struct ipa_get_selinux_state);
if (req == NULL) {
return NULL;
}
state->be_ctx = be_ctx;
state->selinux_ctx = selinux_ctx;
state->user = user;
state->host = host;
offline = be_is_offline(be_ctx);
DEBUG(SSSDBG_TRACE_INTERNAL, ("Connection status is [%s].\n",
offline ? "offline" : "online"));
if (!offline) {
state->op = sdap_id_op_create(state, selinux_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 = ipa_get_selinux_maps_offline(req);
goto immediate;
}
return req;
immediate:
if (ret == EOK) {
tevent_req_done(req);
} else {
tevent_req_error(req, ret);
}
tevent_req_post(req, be_ctx->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->selinux_ctx->id_ctx;
const char *access_name;
const char *selinux_name;
const char *hostname;
ret = sdap_id_op_connect_recv(subreq, &dp_error);
talloc_zfree(subreq);
if (dp_error == DP_ERR_OFFLINE) {
talloc_zfree(state->op);
ret = ipa_get_selinux_maps_offline(req);
if (ret == EOK) {
tevent_req_done(req);
return;
}
goto fail;
}
if (ret != EOK) {
goto fail;
}
access_name = state->be_ctx->bet_info[BET_ACCESS].mod_name;
selinux_name = state->be_ctx->bet_info[BET_SELINUX].mod_name;
if (strcasecmp(access_name, selinux_name) == 0 && state->host != NULL) {
/* If the access control module is the same as the selinux module
* and the access control had already discovered the host
*/
return ipa_get_config_step(req);
}
hostname = dp_opt_get_string(state->selinux_ctx->id_ctx->ipa_options->basic,
IPA_HOSTNAME);
if (hostname == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot determine the host name\n"));
goto fail;
}
subreq = ipa_host_info_send(state, state->be_ctx->ev,
sdap_id_op_handle(state->op),
id_ctx->sdap_id_ctx->opts,
hostname,
id_ctx->ipa_options->host_map,
NULL,
state->selinux_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 errno_t
ipa_get_selinux_maps_offline(struct tevent_req *req)
{
errno_t ret;
size_t nmaps;
struct ldb_message **maps;
struct ldb_message *defaults;
const char *attrs[] = { SYSDB_NAME,
SYSDB_USER_CATEGORY,
SYSDB_HOST_CATEGORY,
SYSDB_ORIG_MEMBER_USER,
SYSDB_ORIG_MEMBER_HOST,
SYSDB_SELINUX_SEEALSO,
SYSDB_SELINUX_USER,
NULL };
const char *default_user;
const char *order;
struct ipa_get_selinux_state *state = tevent_req_data(req,
struct ipa_get_selinux_state);
/* read the config entry */
ret = sysdb_search_selinux_config(state, state->be_ctx->domain->sysdb,
state->be_ctx->domain, NULL, &defaults);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, ("sysdb_search_selinux_config failed [%d]: %s\n",
ret, strerror(ret)));
return ret;
}
default_user = ldb_msg_find_attr_as_string(defaults,
SYSDB_SELINUX_DEFAULT_USER,
NULL);
order = ldb_msg_find_attr_as_string(defaults, SYSDB_SELINUX_DEFAULT_ORDER,
NULL);
state->defaults = sysdb_new_attrs(state);
if (state->defaults == NULL) {
return ENOMEM;
}
ret = sysdb_attrs_add_string(state->defaults,
IPA_CONFIG_SELINUX_DEFAULT_USER_CTX,
default_user);
if (ret != EOK) {
return ret;
}
ret = sysdb_attrs_add_string(state->defaults,
IPA_CONFIG_SELINUX_MAP_ORDER, order);
if (ret != EOK) {
return ret;
}
/* read all the SELinux rules */
ret = sysdb_get_selinux_usermaps(state, state->be_ctx->domain->sysdb,
state->be_ctx->domain,
attrs, &nmaps, &maps);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, ("sysdb_get_selinux_usermaps failed [%d]: %s\n",
ret, strerror(ret)));
return ret;
}
ret = sysdb_msg2attrs(state, nmaps, maps, &state->selinuxmaps);
if (ret != EOK) {
return ret;
}
state->nmaps = nmaps;
/* read all the HBAC rules */
ret = hbac_get_cached_rules(state, state->be_ctx->domain,
&state->hbac_rule_count, &state->hbac_rules);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, ("hbac_get_cached_rules failed [%d]: %s\n",
ret, strerror(ret)));
return ret;
}
return EOK;
}
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);
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];
return ipa_get_config_step(req);
done:
if (ret != EOK) {
tevent_req_error(req, ret);
}
}
static void ipa_get_config_step(struct tevent_req *req)
{
const char *domain;
struct tevent_req *subreq;
struct ipa_get_selinux_state *state = tevent_req_data(req,
struct ipa_get_selinux_state);
struct ipa_id_ctx *id_ctx = state->selinux_ctx->id_ctx;
domain = dp_opt_get_string(state->selinux_ctx->id_ctx->ipa_options->basic,
IPA_KRB5_REALM);
subreq = ipa_get_config_send(state, state->be_ctx->ev,
sdap_id_op_handle(state->op),
id_ctx->sdap_id_ctx->opts,
domain, NULL);
if (subreq == NULL) {
tevent_req_error(req, ENOMEM);
}
tevent_req_set_callback(subreq, ipa_get_selinux_config_done, req);
}
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);
struct sdap_id_ctx *id_ctx = state->selinux_ctx->id_ctx->sdap_id_ctx;
errno_t ret;
ret = ipa_get_config_recv(subreq, state, &state->defaults);
talloc_free(subreq);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, ("Could not get IPA config\n"));
goto done;
}
subreq = ipa_selinux_get_maps_send(state, state->be_ctx->ev,
state->be_ctx->domain->sysdb,
sdap_id_op_handle(state->op),
id_ctx->opts,
state->selinux_ctx->id_ctx->ipa_options,
state->selinux_ctx->selinux_search_bases);
if (!subreq) {
ret = ENOMEM;
goto done;
}
tevent_req_set_callback(subreq, ipa_get_selinux_maps_done, req);
return;
done:
if (ret != EOK) {
tevent_req_error(req, ret);
} else {
tevent_req_done(req);
}
}
static void ipa_get_selinux_maps_done(struct tevent_req *subreq)
{
struct tevent_req *req;
struct ipa_get_selinux_state *state;
struct ipa_id_ctx *id_ctx;
char *selinux_name;
char *access_name;
const char *tmp_str;
bool check_hbac;
errno_t ret;
int i;
req = tevent_req_callback_data(subreq, struct tevent_req);
state = tevent_req_data(req, struct ipa_get_selinux_state);
id_ctx = state->selinux_ctx->id_ctx;
ret = ipa_selinux_get_maps_recv(subreq, state,
&state->nmaps, &state->selinuxmaps);
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\n", state->nmaps));
check_hbac = false;
for (i = 0; i < state->nmaps; i++) {
ret = sysdb_attrs_get_string(state->selinuxmaps[i],
SYSDB_SELINUX_SEEALSO, &tmp_str);
if (ret == EOK) {
check_hbac = true;
break;
}
}
if (check_hbac) {
access_name = state->be_ctx->bet_info[BET_ACCESS].mod_name;
selinux_name = state->be_ctx->bet_info[BET_SELINUX].mod_name;
if (strcasecmp(access_name, selinux_name) == 0) {
ret = hbac_get_cached_rules(state, state->be_ctx->domain,
&state->hbac_rule_count,
&state->hbac_rules);
/* Terminates the request */
goto done;
}
DEBUG(SSSDBG_TRACE_FUNC, ("SELinux maps referenced an HBAC rule. "
"Need to refresh HBAC rules\n"));
subreq = ipa_hbac_rule_info_send(state, false, state->be_ctx->ev,
sdap_id_op_handle(state->op),
id_ctx->sdap_id_ctx->opts,
state->selinux_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;
}
ret = EOK;
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);
errno_t ret;
ret = ipa_hbac_rule_info_recv(subreq, state, &state->hbac_rule_count,
&state->hbac_rules);
DEBUG(SSSDBG_TRACE_INTERNAL,
("Received %d HBAC rules\n", state->hbac_rule_count));
talloc_free(subreq);
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,
size_t *hbac_count,
struct sysdb_attrs ***hbac_rules,
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;
TEVENT_REQ_RETURN_ON_ERROR(req);
if (state->defaults != NULL) {
ret = sysdb_attrs_get_string(state->defaults,
IPA_CONFIG_SELINUX_DEFAULT_USER_CTX,
&tmp_str);
if (ret != EOK && ret != ENOENT) {
return ret;
}
if (ret == EOK) {
*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->selinuxmaps != NULL) {
*count = state->nmaps;
*maps = talloc_steal(mem_ctx, state->selinuxmaps);
} else {
*count = 0;
*maps = NULL;
}
if (state->hbac_rules != NULL) {
*hbac_count = state->hbac_rule_count;
*hbac_rules = talloc_steal(mem_ctx, state->hbac_rules);
} else {
*hbac_count = 0;
*hbac_rules = NULL;
}
return EOK;
}