ipa_access.c revision fdfe33975cd902bf7a334e49f2667f6346c4e6ae
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay IPA Backend Module -- Access control
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay Sumit Bose <sbose@redhat.com>
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay Copyright (C) 2009 Red Hat
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay This program is free software; you can redistribute it and/or modify
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay it under the terms of the GNU General Public License as published by
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay the Free Software Foundation; either version 3 of the License, or
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay (at your option) any later version.
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay This program is distributed in the hope that it will be useful,
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay but WITHOUT ANY WARRANTY; without even the implied warranty of
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay GNU General Public License for more details.
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay You should have received a copy of the GNU General Public License
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay along with this program. If not, see <http://www.gnu.org/licenses/>.
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guaystatic void ipa_access_reply(struct hbac_ctx *hbac_ctx, int pam_status)
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay pd = talloc_get_type(be_req_get_data(be_req), struct pam_data);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* destroy HBAC context now to release all used resources and LDAP connection */
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay if (pam_status == PAM_SUCCESS || pam_status == PAM_PERM_DENIED) {
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay be_req_terminate(be_req, DP_ERR_OK, pam_status, NULL);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay be_req_terminate(be_req, DP_ERR_FATAL, pam_status, NULL);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guaystatic void ipa_hbac_check(struct tevent_req *req);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guaystatic void hbac_connect_done(struct tevent_req *subreq);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guaystatic bool hbac_check_step_result(struct hbac_ctx *hbac_ctx, int ret);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guaystatic int hbac_get_host_info_step(struct hbac_ctx *hbac_ctx);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guaystatic void ipa_hbac_evaluate_rules(struct hbac_ctx *hbac_ctx);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay struct be_ctx *be_ctx = be_req_get_be_ctx(be_req);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay pd = talloc_get_type(be_req_get_data(be_req), struct pam_data);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay ipa_access_ctx = talloc_get_type(be_ctx->bet_info[BET_ACCESS].pvt_bet_data,
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay if (strcasecmp(pd->domain, be_ctx->domain->name) != 0) {
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* Subdomain request, verify subdomain */
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay dom = find_domain_by_name(be_ctx->domain, pd->domain, true);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* First, verify that this account isn't locked.
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay * We need to do this in case the auth phase was
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay * skipped (such as during GSSAPI single-sign-on
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay * or SSH public key exchange.
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay req = sdap_access_send(be_req, be_ctx->ev, be_ctx, dom,
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay be_req_terminate(be_req, DP_ERR_FATAL, PAM_SYSTEM_ERR, NULL);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay tevent_req_set_callback(req, ipa_hbac_check, be_req);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay be_req = tevent_req_callback_data(req, struct be_req);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay pd = talloc_get_type(be_req_get_data(be_req), struct pam_data);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* Account wasn't locked. Continue below
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay * to HBAC processing.
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* Account was locked. Return permission denied
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay be_req_terminate(be_req, DP_ERR_OK, pd->pam_status, NULL);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay be_req_terminate(be_req, DP_ERR_OK, pd->pam_status, NULL);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* We got an unexpected error. Return it as-is */
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay be_req_terminate(be_req, DP_ERR_FATAL, pd->pam_status,
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay ipa_access_ctx = talloc_get_type(be_ctx->bet_info[BET_ACCESS].pvt_bet_data,
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay hbac_ctx->ipa_options = ipa_access_ctx->ipa_options;
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay hbac_ctx->search_bases = ipa_access_ctx->hbac_search_bases;
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay DEBUG(SSSDBG_CRIT_FAILURE, "No HBAC search base found.\n");
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay deny_method = dp_opt_get_string(hbac_ctx->ipa_options,
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay "WARNING: Using deny rules is deprecated, the option "
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay "ipa_hbac_treat_deny_as will be removed in the next "
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay "upstream version\n");
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* Return an proper error */
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay be_req_terminate(be_req, DP_ERR_FATAL, PAM_SYSTEM_ERR, NULL);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay struct ipa_access_ctx *access_ctx = hbac_ctx->access_ctx;
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay struct be_ctx *be_ctx = be_req_get_be_ctx(hbac_ctx->be_req);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay "Connection status is [%s].\n", offline ? "offline" : "online");
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay refresh_interval = dp_opt_get_int(hbac_ctx->ipa_options,
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay if (now < access_ctx->last_update + refresh_interval) {
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* Simulate offline mode and just go to the cache */
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay DEBUG(SSSDBG_TRACE_FUNC, "Performing cached HBAC evaluation\n");
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay DEBUG(SSSDBG_CRIT_FAILURE, "sdap_id_op_create failed.\n");
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay subreq = sdap_id_op_connect_send(hbac_ctx->sdap_op, hbac_ctx, &ret);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay "sdap_id_op_connect_send failed: %d(%s).\n", ret, strerror(ret));
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay tevent_req_set_callback(subreq, hbac_connect_done, hbac_ctx);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* Evaluate the rules based on what we have in the
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guaystatic void hbac_connect_done(struct tevent_req *subreq)
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay struct hbac_ctx *hbac_ctx = tevent_req_callback_data(subreq, struct hbac_ctx);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* switching to offline mode */
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guaystatic void hbac_clear_rule_data(struct hbac_ctx *hbac_ctx)
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay/* Check whether the current HBAC request is processed in off-line mode */
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guaystatic inline bool hbac_ctx_is_offline(struct hbac_ctx *ctx)
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay/* Check the step result code and continue, retry, get offline result or abort accordingly */
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guaystatic bool hbac_check_step_result(struct hbac_ctx *hbac_ctx, int ret)
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay return true;
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* already offline => the error is fatal */
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay return false;
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay ret = sdap_id_op_done(hbac_ctx->sdap_op, ret, &dp_error);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* switching to offline mode */
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* Free any of the results we've gotten */
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay return false;
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay return false;
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guaystatic void hbac_get_service_info_step(struct tevent_req *req);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guaystatic void hbac_get_rule_info_step(struct tevent_req *req);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guaystatic void hbac_sysdb_save (struct tevent_req *req);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guaystatic int hbac_get_host_info_step(struct hbac_ctx *hbac_ctx)
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay struct be_ctx *be_ctx = be_req_get_be_ctx(hbac_ctx->be_req);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay if (dp_opt_get_bool(hbac_ctx->ipa_options, IPA_HBAC_SUPPORT_SRCHOST)) {
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* Support srchost
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay * -> we don't want any particular host,
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay * we want all hosts
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* THIS FEATURE IS DEPRECATED */
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay DEBUG(SSSDBG_MINOR_FAILURE, "WARNING: Using deprecated option "
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay "ipa_hbac_support_srchost.\n");
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay sss_log(SSS_LOG_NOTICE, "WARNING: Using deprecated option "
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay "ipa_hbac_support_srchost.\n");
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay hostname = dp_opt_get_string(hbac_ctx->ipa_options, IPA_HOSTNAME);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay DEBUG(SSSDBG_CRIT_FAILURE, "Could not get host info\n");
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay tevent_req_set_callback(req, hbac_get_service_info_step, hbac_ctx);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guaystatic void hbac_get_service_info_step(struct tevent_req *req)
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay struct be_ctx *be_ctx = be_req_get_be_ctx(hbac_ctx->be_req);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* Get services and service groups */
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay req = ipa_hbac_service_info_send(hbac_ctx, be_ctx->ev,
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay DEBUG(SSSDBG_CRIT_FAILURE,"Could not get service info\n");
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay tevent_req_set_callback(req, hbac_get_rule_info_step, hbac_ctx);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guaystatic void hbac_get_rule_info_step(struct tevent_req *req)
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay struct be_ctx *be_ctx = be_req_get_be_ctx(hbac_ctx->be_req);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* Get the ipa_host attrs */
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay ipa_hostname = dp_opt_get_cstring(hbac_ctx->ipa_options, IPA_HOSTNAME);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay "Missing ipa_hostname, this should never happen.\n");
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay DEBUG(SSSDBG_CRIT_FAILURE, "Could not locate IPA host\n");
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay DEBUG(SSSDBG_CRIT_FAILURE, "Could not locate IPA host\n");
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* Get the list of applicable rules */
a004873f367b026fd033d4aa0bfa4225555c346eMichal Zidek DEBUG(SSSDBG_CRIT_FAILURE, "Could not get rules\n");
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay tevent_req_set_callback(req, hbac_sysdb_save, hbac_ctx);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guaystatic void hbac_sysdb_save(struct tevent_req *req)
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay struct be_ctx *be_ctx = be_req_get_be_ctx(hbac_ctx->be_req);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay talloc_get_type(be_ctx->bet_info[BET_ACCESS].pvt_bet_data,
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* No rules were found that apply to this
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* Delete any rules in the sysdb so offline logins
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay * are also denied.
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay base_dn = sysdb_custom_subtree_dn(tmp_ctx, domain, HBAC_RULES_SUBDIR);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay ret = sysdb_delete_recursive(domain->sysdb, base_dn, true);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_delete_recursive failed.\n");
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* If no rules are found, we default to DENY */
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay DEBUG(SSSDBG_FATAL_FAILURE, "Could not start transaction\n");
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* Save the hosts */
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay DEBUG(SSSDBG_CRIT_FAILURE, "Error saving hosts: [%d][%s]\n",
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* Save the services */
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay DEBUG(SSSDBG_CRIT_FAILURE, "Error saving services: [%d][%s]\n",
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* Save the rules */
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay DEBUG(SSSDBG_CRIT_FAILURE, "Error saving rules: [%d][%s]\n",
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* We don't need the rule data any longer,
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay * the rest of the processing relies on
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay * sysdb lookups.
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* Now evaluate the request against the rules */
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay DEBUG(SSSDBG_FATAL_FAILURE, "Could not cancel transaction\n");
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guayvoid ipa_hbac_evaluate_rules(struct hbac_ctx *hbac_ctx)
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay struct be_ctx *be_ctx = be_req_get_be_ctx(hbac_ctx->be_req);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* Get HBAC rules from the sysdb */
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay ret = hbac_get_cached_rules(hbac_ctx, be_ctx->domain,
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay DEBUG(SSSDBG_CRIT_FAILURE, "Could not retrieve rules from the cache\n");
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay "DENY rules detected. Denying access to all users\n");
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay DEBUG(SSSDBG_CRIT_FAILURE, "Could not construct HBAC rules\n");
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay result = hbac_evaluate(hbac_rules, eval_req, &info);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay DEBUG(SSSDBG_MINOR_FAILURE, "Access granted by HBAC rule [%s]\n",
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay DEBUG(SSSDBG_CRIT_FAILURE, "Error [%s] occurred in rule [%s]\n",
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay DEBUG(SSSDBG_CRIT_FAILURE, "Insufficient memory\n");
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay DEBUG(SSSDBG_MINOR_FAILURE, "Access denied by HBAC rules\n");
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay filter = talloc_asprintf(tmp_ctx, "(objectClass=%s)", IPA_HBAC_RULE);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay ret = sysdb_search_custom(tmp_ctx, domain, filter,
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay DEBUG(SSSDBG_CRIT_FAILURE, "Error looking up HBAC rules\n");
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay ret = sysdb_msg2attrs(tmp_ctx, rule_count, msgs, &rules);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay "Could not convert ldb message to sysdb_attrs\n");