diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/Makefile.am | 6 | ||||
-rw-r--r-- | server/db/sysdb.c | 8 | ||||
-rw-r--r-- | server/db/sysdb.h | 4 | ||||
-rw-r--r-- | server/providers/ipa/ipa_access.c | 1572 | ||||
-rw-r--r-- | server/providers/ipa/ipa_access.h | 55 | ||||
-rw-r--r-- | server/providers/ipa/ipa_init.c | 56 |
6 files changed, 1697 insertions, 4 deletions
diff --git a/server/Makefile.am b/server/Makefile.am index 88053fc5..a64a3939 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -293,6 +293,7 @@ dist_noinst_HEADERS = \ providers/ldap/sdap_async.h \ providers/ldap/sdap_async_private.h \ providers/ipa/ipa_common.h \ + providers/ipa/ipa_access.h \ tools/tools_util.h \ tools/sss_sync_ops.h \ resolv/async_resolv.h \ @@ -550,8 +551,9 @@ libsss_krb5_la_LDFLAGS = \ -module libsss_ipa_la_SOURCES = \ - providers/ipa/ipa_init.c \ - providers/ipa/ipa_common.c \ + providers/ipa/ipa_init.c \ + providers/ipa/ipa_common.c \ + providers/ipa/ipa_access.c \ providers/ldap/ldap_id.c \ providers/ldap/ldap_id_enum.c \ providers/ldap/ldap_auth.c \ diff --git a/server/db/sysdb.c b/server/db/sysdb.c index 4e6e71e4..971add09 100644 --- a/server/db/sysdb.c +++ b/server/db/sysdb.c @@ -24,6 +24,14 @@ #include "confdb/confdb.h" #include <time.h> + +struct ldb_dn *sysdb_custom_subtree_dn(struct sysdb_ctx *ctx, void *memctx, + const char *domain, + const char *subtree_name) +{ + return ldb_dn_new_fmt(memctx, ctx->ldb, SYSDB_TMPL_CUSTOM_SUBTREE, + subtree_name, domain); +} struct ldb_dn *sysdb_custom_dn(struct sysdb_ctx *ctx, void *memctx, const char *domain, const char *object_name, const char *subtree_name) diff --git a/server/db/sysdb.h b/server/db/sysdb.h index 85aea4b7..68486de4 100644 --- a/server/db/sysdb.h +++ b/server/db/sysdb.h @@ -140,6 +140,7 @@ #define SYSDB_TMPL_USER SYSDB_NAME"=%s,"SYSDB_TMPL_USER_BASE #define SYSDB_TMPL_GROUP SYSDB_NAME"=%s,"SYSDB_TMPL_GROUP_BASE +#define SYSDB_TMPL_CUSTOM_SUBTREE "cn=%s,"SYSDB_TMPL_CUSTOM_BASE #define SYSDB_TMPL_CUSTOM SYSDB_NAME"=%s,cn=%s,"SYSDB_TMPL_CUSTOM_BASE #define SYSDB_MOD_ADD LDB_FLAG_MOD_ADD @@ -193,6 +194,9 @@ struct ldb_dn *sysdb_domain_dn(struct sysdb_ctx *ctx, void *memctx, struct ldb_dn *sysdb_custom_dn(struct sysdb_ctx *ctx, void *memctx, const char *domain, const char *object_name, const char *subtree_name); +struct ldb_dn *sysdb_custom_subtree_dn(struct sysdb_ctx *ctx, void *memctx, + const char *domain, + const char *subtree_name); diff --git a/server/providers/ipa/ipa_access.c b/server/providers/ipa/ipa_access.c new file mode 100644 index 00000000..8f6d70b8 --- /dev/null +++ b/server/providers/ipa/ipa_access.c @@ -0,0 +1,1572 @@ +/* + SSSD + + IPA Backend Module -- Access control + + Authors: + Sumit Bose <sbose@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 <sys/param.h> +#include <security/pam_modules.h> + +#include "util/util.h" +#include "providers/ldap/sdap_async.h" +#include "providers/ipa/ipa_common.h" +#include "providers/ipa/ipa_access.h" + +#define IPA_HOST_MEMBEROF "memberOf" +#define IPA_HOST_SERVERHOSTNAME "serverHostName" +#define IPA_HOST_FQDN "fqdn" +#define IPA_ACCESS_RULE_TYPE "accessRuleType" +#define IPA_MEMBER_USER "memberUser" +#define IPA_USER_CATEGORY "userCategory" +#define IPA_SERVICE_NAME "serviceName" +#define IPA_SOURCE_HOST "sourceHost" +#define IPA_SOURCE_HOST_CATEGORY "sourceHostCategory" +#define IPA_EXTERNAL_HOST "externalHost" +#define IPA_ACCESS_TIME "accessTime" +#define IPA_UNIQUE_ID "ipauniqueid" +#define IPA_ENABLED_FLAG "ipaenabledflag" +#define IPA_MEMBER_HOST "memberHost" +#define IPA_CN "cn" + +#define IPA_HOST_BASE_TMPL "cn=computers,cn=accounts,dc=%s" +#define IPA_HBAC_BASE_TMPL "cn=hbac,dc=%s" + +#define SYSDB_HBAC_BASE_TMPL "cn=hbac,"SYSDB_TMPL_CUSTOM_BASE + +#define HBAC_RULES_SUBDIR "hbac_rules" +#define HBAC_HOSTS_SUBDIR "hbac_hosts" + +struct hbac_host_info { + const char *fqdn; + const char *serverhostname; + const char *dn; + const char **memberof; +}; + + +static void ipa_access_reply(struct be_req *be_req, int pam_status) +{ + struct pam_data *pd; + pd = talloc_get_type(be_req->req_data, struct pam_data); + pd->pam_status = pam_status; + + if (pam_status == PAM_SUCCESS) { + be_req->fn(be_req, DP_ERR_OK, pam_status, NULL); + } else { + be_req->fn(be_req, DP_ERR_FATAL, pam_status, NULL); + } +} + +struct hbac_get_user_info_state { + struct tevent_context *ev; + struct be_ctx *be_ctx;; + struct sysdb_handle *handle; + + const char *user; + const char *user_orig_dn; + struct ldb_dn *user_dn; + size_t groups_count; + const char **groups; +}; + +static void search_user_done(struct tevent_req *subreq); +static void search_groups_done(struct tevent_req *subreq); + +struct tevent_req *hbac_get_user_info_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + const char *user) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct hbac_get_user_info_state *state; + int ret; + const char **attrs; + + req = tevent_req_create(memctx, &state, struct hbac_get_user_info_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ev = ev; + state->be_ctx = be_ctx; + state->handle = NULL; + state->user = user; + state->user_orig_dn = NULL; + state->user_dn = NULL; + state->groups_count = 0; + state->groups = NULL; + + attrs = talloc_array(state, const char *, 2); + if (attrs == NULL) { + ret = ENOMEM; + goto fail; + } + + attrs[0] = SYSDB_ORIG_DN; + attrs[1] = NULL; + + subreq = sysdb_search_user_by_name_send(state, ev, be_ctx->sysdb, NULL, + be_ctx->domain, user, attrs); + if (subreq == NULL) { + DEBUG(1, ("sysdb_search_user_by_name_send failed.\n")); + ret = ENOMEM; + goto fail; + } + + tevent_req_set_callback(subreq, search_user_done, req); + + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void search_user_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct hbac_get_user_info_state *state = tevent_req_data(req, + struct hbac_get_user_info_state); + int ret; + const char **attrs; + const char *dummy; + struct ldb_message *user_msg; + + + ret = sysdb_search_user_recv(subreq, state, &user_msg); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + DEBUG(9, ("Found user info for user [%s].\n", state->user)); + state->user_dn = talloc_steal(state, user_msg->dn); + dummy = ldb_msg_find_attr_as_string(user_msg, SYSDB_ORIG_DN, NULL); + if (dummy == NULL) { + DEBUG(1, ("Original DN of user [%s] not available.\n", state->user)); + ret = EINVAL; + goto failed; + } + state->user_orig_dn = talloc_strdup(state, dummy); + if (state->user_dn == NULL) { + DEBUG(1, ("talloc_strdup failed.\n")); + ret = ENOMEM; + goto failed; + } + DEBUG(9, ("Found original DN [%s] for user [%s].\n", state->user_orig_dn, + state->user)); + + attrs = talloc_array(state, const char *, 2); + if (attrs == NULL) { + DEBUG(1, ("talloc_array failed.\n")); + ret = ENOMEM; + goto failed; + } + attrs[0] = SYSDB_ORIG_DN; + attrs[1] = NULL; + + subreq = sysdb_asq_search_send(state, state->ev, state->be_ctx->sysdb, NULL, + state->be_ctx->domain, state->user_dn, NULL, + SYSDB_MEMBEROF, attrs); + if (subreq == NULL) { + DEBUG(1, ("sysdb_asq_search_send failed.\n")); + ret = ENOMEM; + goto failed; + } + + tevent_req_set_callback(subreq, search_groups_done, req); + return; + +failed: + tevent_req_error(req, ret); + return; +} + +static void search_groups_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct hbac_get_user_info_state *state = tevent_req_data(req, + struct hbac_get_user_info_state); + int ret; + int i; + struct ldb_message **msg; + + ret = sysdb_asq_search_recv(subreq, state, &state->groups_count, &msg); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + if (state->groups_count == 0) { + tevent_req_done(req); + return; + } + + state->groups = talloc_array(state, const char *, state->groups_count); + if (state->groups == NULL) { + DEBUG(1, ("talloc_groups failed.\n")); + ret = ENOMEM; + goto failed; + } + + for(i = 0; i < state->groups_count; i++) { + if (msg[i]->num_elements != 1) { + DEBUG(1, ("Unexpected number of elements.\n")); + ret = EINVAL; + goto failed; + } + + if (msg[i]->elements[0].num_values != 1) { + DEBUG(1, ("Unexpected number of values.\n")); + ret = EINVAL; + goto failed; + } + + state->groups[i] = talloc_strndup(state->groups, + (const char *) msg[i]->elements[0].values[0].data, + msg[i]->elements[0].values[0].length); + if (state->groups[i] == NULL) { + DEBUG(1, ("talloc_strndup failed.\n")); + ret = ENOMEM; + goto failed; + } + + DEBUG(9, ("Found group [%s].\n", state->groups[i])); + } + + tevent_req_done(req); + return; + +failed: + talloc_free(state->groups); + tevent_req_error(req, ret); + return; +} + +static int hbac_get_user_info_recv(struct tevent_req *req, TALLOC_CTX *memctx, + const char **user_dn, size_t *groups_count, + const char ***groups) +{ + struct hbac_get_user_info_state *state = tevent_req_data(req, + struct hbac_get_user_info_state); + enum tevent_req_state tstate; + uint64_t err; + int i; + + if (tevent_req_is_error(req, &tstate, &err)) { + if (err) return err; + return EIO; + } + + *user_dn = talloc_steal(memctx, state->user_orig_dn); + *groups_count = state->groups_count; + for (i = 0; i < state->groups_count; i++) { + talloc_steal(memctx, state->groups[i]); + } + *groups = talloc_steal(memctx, state->groups); + + return EOK; +} + + +struct hbac_get_host_info_state { + struct tevent_context *ev; + struct sdap_id_ctx *sdap_ctx; + struct sysdb_ctx *sysdb; + struct sysdb_handle *handle; + + const char *hostname; + char *host_filter; + char *host_search_base; + const char **host_attrs; + + struct sysdb_attrs **host_reply_list; + size_t host_reply_count; + size_t current_item; + struct hbac_host_info **hbac_host_info; +}; + +static void hbac_get_host_info_connect_done(struct tevent_req *subreq); +static void host_get_host_memberof_done(struct tevent_req *subreq); +static void hbac_get_host_info_sysdb_transaction_started(struct tevent_req *subreq); +static void hbac_get_host_info_store_prepare(struct tevent_req *req); +static void hbac_get_host_info_store_done(struct tevent_req *subreq); + +static struct tevent_req *hbac_get_host_info_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_id_ctx *sdap_ctx, + struct sysdb_ctx *sysdb, + const char *ipa_domain, + const char *hostname) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct hbac_get_host_info_state *state; + int ret; + + if (hostname == NULL || ipa_domain == NULL) { + DEBUG(1, ("Missing fqdn or domain.\n")); + return NULL; + } + + req = tevent_req_create(memctx, &state, struct hbac_get_host_info_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ev = ev; + state->sdap_ctx = sdap_ctx; + state->sysdb = sysdb; + state->handle = NULL; + state->hostname= hostname; + + state->host_reply_list = NULL; + state->host_reply_count = 0; + state->current_item = 0; + + state->host_filter = talloc_asprintf(state, + "(&(|(fqdn=%s)(serverhostname=%s))(objectclass=ipaHost))", + hostname, hostname); + if (state->host_filter == NULL) { + DEBUG(1, ("Failed to create filter.\n")); + ret = ENOMEM; + goto fail; + } + + state->host_search_base = talloc_asprintf(state, IPA_HOST_BASE_TMPL, + ipa_domain); + if (state->host_search_base == NULL) { + DEBUG(1, ("Failed to create host search base.\n")); + ret = ENOMEM; + goto fail; + } + + state->host_attrs = talloc_array(state, const char *, 5); + if (state->host_attrs == NULL) { + DEBUG(1, ("Failed to allocate host attribute list.\n")); + ret = ENOMEM; + goto fail; + } + state->host_attrs[0] = IPA_HOST_MEMBEROF; + state->host_attrs[1] = IPA_HOST_SERVERHOSTNAME; + state->host_attrs[2] = IPA_HOST_FQDN; + state->host_attrs[3] = "objectClass"; + state->host_attrs[4] = NULL; + + if (sdap_ctx->gsh == NULL || ! sdap_ctx->gsh->connected) { + if (sdap_ctx->gsh != NULL) { + talloc_zfree(sdap_ctx->gsh); + } + + subreq = sdap_cli_connect_send(state, ev, sdap_ctx->opts, NULL); + if (!subreq) { + DEBUG(1, ("sdap_cli_connect_send failed.\n")); + ret = ENOMEM; + goto fail; + } + + tevent_req_set_callback(subreq, hbac_get_host_info_connect_done, req); + + return req; + } + + subreq = sdap_get_generic_send(state, state->ev, + state->sdap_ctx->opts, + state->sdap_ctx->gsh, + state->host_search_base, + LDAP_SCOPE_SUB, + state->host_filter, + state->host_attrs); + + if (subreq == NULL) { + DEBUG(1, ("sdap_get_generic_send failed.\n")); + ret = ENOMEM; + goto fail; + } + + tevent_req_set_callback(subreq, host_get_host_memberof_done, req); + + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void hbac_get_host_info_connect_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct hbac_get_host_info_state *state = tevent_req_data(req, + struct hbac_get_host_info_state); + int ret; + + ret = sdap_cli_connect_recv(subreq, state->sdap_ctx, &state->sdap_ctx->gsh, + NULL); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + subreq = sdap_get_generic_send(state, state->ev, + state->sdap_ctx->opts, + state->sdap_ctx->gsh, + state->host_search_base, + LDAP_SCOPE_SUB, + state->host_filter, + state->host_attrs); + + if (subreq == NULL) { + DEBUG(1, ("sdap_get_generic_send failed.\n")); + ret = ENOMEM; + goto fail; + } + + tevent_req_set_callback(subreq, host_get_host_memberof_done, req); + return; + +fail: + tevent_req_error(req, ret); + return; +} + +static void host_get_host_memberof_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct hbac_get_host_info_state *state = tevent_req_data(req, + struct hbac_get_host_info_state); + int ret; + int i; + int v; + struct ldb_message_element *el; + struct hbac_host_info **hhi; + + ret = sdap_get_generic_recv(subreq, state, &state->host_reply_count, + &state->host_reply_list); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + if (state->host_reply_list == NULL) { + DEBUG(1, ("Host [%s] not found in IPA server.\n", state->hostname)); + ret = ENOENT; + goto fail; + } + + hhi = talloc_array(state, struct hbac_host_info *, state->host_reply_count + 1); + if (hhi == NULL) { + DEBUG(1, ("talloc failed.\n")); + ret = ENOMEM; + goto fail; + } + memset(hhi, 0, + sizeof(struct hbac_host_info *) * (state->host_reply_count + 1)); + + for (i = 0; i < state->host_reply_count; i++) { + hhi[i] = talloc_zero(state, struct hbac_host_info); + if (hhi[i] == NULL) { + ret = ENOMEM; + goto fail; + } + + ret = sysdb_attrs_get_el(state->host_reply_list[i], SYSDB_ORIG_DN, &el); + if (ret != EOK) { + DEBUG(1, ("sysdb_attrs_get_el failed.\n")); + goto fail; + } + if (el->num_values == 0) { + ret = EINVAL; + goto fail; + } + DEBUG(9, ("OriginalDN: [%.*s].\n", el->values[0].length, + (char *)el->values[0].data)); + hhi[i]->dn = talloc_strndup(hhi, (char *)el->values[0].data, + el->values[0].length); + if (hhi[i]->dn == NULL) { + ret = ENOMEM; + goto fail; + } + + ret = sysdb_attrs_get_el(state->host_reply_list[i], + IPA_HOST_SERVERHOSTNAME, &el); + if (ret != EOK) { + DEBUG(1, ("sysdb_attrs_get_el failed.\n")); + goto fail; + } + if (el->num_values == 0) { + ret = EINVAL; + goto fail; + } + DEBUG(9, ("ServerHostName: [%.*s].\n", el->values[0].length, + (char *)el->values[0].data)); + hhi[i]->serverhostname = talloc_strndup(hhi, (char *)el->values[0].data, + el->values[0].length); + if (hhi[i]->serverhostname == NULL) { + ret = ENOMEM; + goto fail; + } + + ret = sysdb_attrs_get_el(state->host_reply_list[i], + IPA_HOST_FQDN, &el); + if (ret != EOK) { + DEBUG(1, ("sysdb_attrs_get_el failed.\n")); + goto fail; + } + if (el->num_values == 0) { + ret = EINVAL; + goto fail; + } + DEBUG(9, ("FQDN: [%.*s].\n", el->values[0].length, + (char *)el->values[0].data)); + hhi[i]->fqdn = talloc_strndup(hhi, (char *)el->values[0].data, + el->values[0].length); + if (hhi[i]->fqdn == NULL) { + ret = ENOMEM; + goto fail; + } + + ret = sysdb_attrs_get_el(state->host_reply_list[i], + IPA_HOST_MEMBEROF, &el); + if (ret != EOK) { + DEBUG(1, ("sysdb_attrs_get_el failed.\n")); + goto fail; + } + + hhi[i]->memberof = talloc_array(hhi, const char *, el->num_values + 1); + if (hhi[i]->memberof == NULL) { + ret = ENOMEM; + goto fail; + } + memset(hhi[i]->memberof, 0, + sizeof(const char *) * (el->num_values + 1)); + + for(v = 0; v < el->num_values; v++) { + DEBUG(9, ("%s: [%.*s].\n", IPA_HOST_MEMBEROF, el->values[v].length, + (const char *)el->values[v].data)); + hhi[i]->memberof[v] = talloc_strndup(hhi, + (const char *)el->values[v].data, + el->values[v].length); + if (hhi[i]->memberof[v] == NULL) { + ret = ENOMEM; + goto fail; + } + } + } + + state->hbac_host_info = hhi; + + subreq = sysdb_transaction_send(state, state->ev, state->sysdb); + if (subreq == NULL) { + DEBUG(1, ("sysdb_transaction_send failed.\n")); + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, hbac_get_host_info_sysdb_transaction_started, req); + return; + +fail: + tevent_req_error(req, ret); + return; +} + +static void hbac_get_host_info_sysdb_transaction_started( + struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct hbac_get_host_info_state *state = tevent_req_data(req, + struct hbac_get_host_info_state); + int ret; + + ret = sysdb_transaction_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + state->current_item = 0; + hbac_get_host_info_store_prepare(req); + return; +} + +static void hbac_get_host_info_store_prepare(struct tevent_req *req) +{ + struct hbac_get_host_info_state *state = tevent_req_data(req, + struct hbac_get_host_info_state); + int ret; + char *object_name; + struct ldb_message_element *el; + struct tevent_req *subreq; + + if (state->current_item < state->host_reply_count) { + ret = sysdb_attrs_get_el(state->host_reply_list[state->current_item], + IPA_HOST_FQDN, &el); + if (ret != EOK) { + DEBUG(1, ("sysdb_attrs_get_el failed.\n")); + goto fail; + } + if (el->num_values == 0) { + ret = EINVAL; + goto fail; + } + object_name = talloc_strndup(state, (const char *)el->values[0].data, + el->values[0].length); + if (object_name == NULL) { + ret = ENOMEM; + goto fail; + } + DEBUG(9, ("Fqdn [%s].\n", object_name)); + + + ret = sysdb_attrs_replace_name( + state->host_reply_list[state->current_item], + IPA_HOST_MEMBEROF, SYSDB_ORIG_MEMBEROF); + if (ret != EOK) { + DEBUG(1, ("sysdb_attrs_replace_name failed.\n")); + goto fail; + } + + subreq = sysdb_store_custom_send(state, state->ev, + state->handle, + state->sdap_ctx->be->domain, + object_name, + HBAC_HOSTS_SUBDIR, + state->host_reply_list[state->current_item]); + + if (subreq == NULL) { + DEBUG(1, ("sysdb_store_custom_send failed.\n")); + ret = ENOMEM; + goto fail; + } + + tevent_req_set_callback(subreq, hbac_get_host_info_store_done, req); + return; + } + + subreq = sysdb_transaction_commit_send(state, state->ev, state->handle); + if (subreq == NULL) { + DEBUG(1, ("sysdb_transaction_commit_send failed.\n")); + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sysdb_transaction_complete, req); + + return; + +fail: + tevent_req_error(req, ret); + return; +} + +static void hbac_get_host_info_store_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct hbac_get_host_info_state *state = tevent_req_data(req, + struct hbac_get_host_info_state); + int ret; + + ret = sysdb_store_custom_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + state->current_item++; + hbac_get_host_info_store_prepare(req); +} + +static int hbac_get_host_info_recv(struct tevent_req *req, TALLOC_CTX *memctx, + struct hbac_host_info ***hhi) +{ + struct hbac_get_host_info_state *state = tevent_req_data(req, + struct hbac_get_host_info_state); + enum tevent_req_state tstate; + uint64_t err; + + if (tevent_req_is_error(req, &tstate, &err)) { + if (err) return err; + return EIO; + } + + *hhi = talloc_steal(memctx, state->hbac_host_info); + return EOK; +} + + +struct hbac_get_rules_state { + struct tevent_context *ev; + struct sdap_id_ctx *sdap_ctx; + struct sysdb_ctx *sysdb; + struct sysdb_handle *handle; + + const char *host_dn; + const char **memberof; + char *hbac_filter; + char *hbac_search_base; + const char **hbac_attrs; + + struct ldb_message *old_rules; + struct sysdb_attrs **hbac_reply_list; + size_t hbac_reply_count; + int current_item; +}; + +static void hbac_get_rules_connect_done(struct tevent_req *subreq); +static void hbac_rule_get_done(struct tevent_req *subreq); +static void hbac_rule_sysdb_transaction_started(struct tevent_req *subreq); +static void hbac_rule_sysdb_delete_done(struct tevent_req *subreq); +static void hbac_rule_store_prepare(struct tevent_req *req); +static void hbac_rule_store_done(struct tevent_req *subreq); + +static struct tevent_req *hbac_get_rules_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_id_ctx *sdap_ctx, + struct sysdb_ctx *sysdb, + const char *ipa_domain, + const char *host_dn, + const char **memberof) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct hbac_get_rules_state *state; + int ret; + int i; + + if (host_dn == NULL || ipa_domain == NULL) { + DEBUG(1, ("Missing host_dn or domain.\n")); + return NULL; + } + + req = tevent_req_create(memctx, &state, struct hbac_get_rules_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ev = ev; + state->sdap_ctx = sdap_ctx; + state->sysdb = sysdb; + state->handle = NULL; + state->host_dn = host_dn; + state->memberof = memberof; + + state->old_rules = NULL; + state->hbac_reply_list = NULL; + state->hbac_reply_count = 0; + state->current_item = 0; + + state->hbac_search_base = talloc_asprintf(state, IPA_HBAC_BASE_TMPL, + ipa_domain); + if (state->hbac_search_base == NULL) { + DEBUG(1, ("Failed to create HBAC search base.\n")); + ret = ENOMEM; + goto fail; + } + + state->hbac_attrs = talloc_array(state, const char *, 13); + if (state->hbac_attrs == NULL) { + DEBUG(1, ("Failed to allocate HBAC attribute list.\n")); + ret = ENOMEM; + goto fail; + } + state->hbac_attrs[0] = IPA_ACCESS_RULE_TYPE; + state->hbac_attrs[1] = IPA_MEMBER_USER; + state->hbac_attrs[2] = IPA_USER_CATEGORY; + state->hbac_attrs[3] = IPA_SERVICE_NAME; + state->hbac_attrs[4] = IPA_SOURCE_HOST; + state->hbac_attrs[5] = IPA_SOURCE_HOST_CATEGORY; + state->hbac_attrs[6] = IPA_EXTERNAL_HOST; + state->hbac_attrs[7] = IPA_ACCESS_TIME; + state->hbac_attrs[8] = IPA_UNIQUE_ID; + state->hbac_attrs[9] = IPA_ENABLED_FLAG; + state->hbac_attrs[10] = IPA_CN; + state->hbac_attrs[11] = "objectclass"; + state->hbac_attrs[12] = NULL; + + state->hbac_filter = talloc_asprintf(state, + "(&(objectclass=ipaHBACRule)(|(%s=%s)", + IPA_MEMBER_HOST, host_dn); + if (state->hbac_filter == NULL) { + ret = ENOMEM; + goto fail; + } + for (i = 0; memberof[i] != NULL; i++) { + state->hbac_filter = talloc_asprintf_append(state->hbac_filter, + "(%s=%s)", + IPA_MEMBER_HOST, + memberof[i]); + if (state->hbac_filter == NULL) { + ret = ENOMEM; + goto fail; + } + } + state->hbac_filter = talloc_asprintf_append(state->hbac_filter, "))"); + if (state->hbac_filter == NULL) { + ret = ENOMEM; + goto fail; + } + + DEBUG(9, ("HBAC rule filter: [%s].\n", state->hbac_filter)); + + if (sdap_ctx->gsh == NULL || ! sdap_ctx->gsh->connected) { + if (sdap_ctx->gsh != NULL) { + talloc_zfree(sdap_ctx->gsh); + } + + subreq = sdap_cli_connect_send(state, ev, sdap_ctx->opts, NULL); + if (!subreq) { + DEBUG(1, ("sdap_cli_connect_send failed.\n")); + ret = ENOMEM; + goto fail; + } + + tevent_req_set_callback(subreq, hbac_get_rules_connect_done, req); + + return req; + } + + subreq = sdap_get_generic_send(state, state->ev, + state->sdap_ctx->opts, + state->sdap_ctx->gsh, + state->hbac_search_base, + LDAP_SCOPE_SUB, + state->hbac_filter, + state->hbac_attrs); + + if (subreq == NULL) { + DEBUG(1, ("sdap_get_generic_send failed.\n")); + ret = ENOMEM; + goto fail; + } + + tevent_req_set_callback(subreq, hbac_rule_get_done, req); + + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void hbac_get_rules_connect_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct hbac_get_rules_state *state = tevent_req_data(req, + struct hbac_get_rules_state); + int ret; + + ret = sdap_cli_connect_recv(subreq, state->sdap_ctx, &state->sdap_ctx->gsh, + NULL); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + subreq = sdap_get_generic_send(state, state->ev, + state->sdap_ctx->opts, + state->sdap_ctx->gsh, + state->hbac_search_base, + LDAP_SCOPE_SUB, + state->hbac_filter, + state->hbac_attrs); + + if (subreq == NULL) { + DEBUG(1, ("sdap_get_generic_send failed.\n")); + ret = ENOMEM; + goto fail; + } + + tevent_req_set_callback(subreq, hbac_rule_get_done, req); + return; + +fail: + tevent_req_error(req, ret); + return; +} + +static void hbac_rule_get_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct hbac_get_rules_state *state = tevent_req_data(req, + struct hbac_get_rules_state); + int ret; + int i; + struct ldb_message_element *el; + + ret = sdap_get_generic_recv(subreq, state, &state->hbac_reply_count, + &state->hbac_reply_list); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + for (i = 0; i < state->hbac_reply_count; i++) { + ret = sysdb_attrs_get_el(state->hbac_reply_list[i], SYSDB_ORIG_DN, &el); + if (ret != EOK) { + DEBUG(1, ("sysdb_attrs_get_el failed.\n")); + goto fail; + } + if (el->num_values == 0) { + ret = EINVAL; + goto fail; + } + DEBUG(9, ("OriginalDN: [%s].\n", (const char *)el->values[0].data)); + } + + if (state->hbac_reply_count == 0) { + tevent_req_done(req); + return; + } + + subreq = sysdb_transaction_send(state, state->ev, state->sysdb); + if (subreq == NULL) { + DEBUG(1, ("sysdb_transaction_send failed.\n")); + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, hbac_rule_sysdb_transaction_started, req); + return; + +fail: + tevent_req_error(req, ret); + return; +} + +static void hbac_rule_sysdb_transaction_started(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct hbac_get_rules_state *state = tevent_req_data(req, + struct hbac_get_rules_state); + int ret; + struct ldb_dn *hbac_base_dn; + + ret = sysdb_transaction_recv(subreq, state, &state->handle); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + hbac_base_dn = sysdb_custom_subtree_dn(state->sysdb, state, + state->sdap_ctx->be->domain->name, + HBAC_RULES_SUBDIR); + if (hbac_base_dn == NULL) { + ret = ENOMEM; + goto fail; + } + subreq = sysdb_delete_recursive_send(state, state->ev, state->handle, + hbac_base_dn, true); + if (subreq == NULL) { + DEBUG(1, ("sysdb_delete_recursive_send failed.\n")); + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, hbac_rule_sysdb_delete_done, req); + return; + +fail: + tevent_req_error(req, ret); + return; +} + +static void hbac_rule_sysdb_delete_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct hbac_get_rules_state *state = tevent_req_data(req, + struct hbac_get_rules_state); + int ret; + + ret = sysdb_delete_recursive_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + state->current_item = 0; + hbac_rule_store_prepare(req); +} + +static void hbac_rule_store_prepare(struct tevent_req *req) +{ + struct hbac_get_rules_state *state = tevent_req_data(req, + struct hbac_get_rules_state); + int ret; + struct ldb_message_element *el; + struct tevent_req *subreq; + char *object_name; + + if (state->current_item < state->hbac_reply_count) { + + ret = sysdb_attrs_get_el(state->hbac_reply_list[state->current_item], + IPA_UNIQUE_ID, &el); + if (ret != EOK) { + DEBUG(1, ("sysdb_attrs_get_el failed.\n")); + goto fail; + } + if (el->num_values == 0) { + ret = EINVAL; + goto fail; + } + object_name = talloc_strndup(state, (const char *)el->values[0].data, + el->values[0].length); + if (object_name == NULL) { + ret = ENOMEM; + goto fail; + } + DEBUG(9, ("IPAUniqueId: [%s].\n", object_name)); + + subreq = sysdb_store_custom_send(state, state->ev, + state->handle, + state->sdap_ctx->be->domain, + object_name, + HBAC_RULES_SUBDIR, + state->hbac_reply_list[state->current_item]); + + if (subreq == NULL) { + DEBUG(1, ("sysdb_store_custom_send failed.\n")); + ret = ENOMEM; + goto fail; + } + + tevent_req_set_callback(subreq, hbac_rule_store_done, req); + return; + } + + subreq = sysdb_transaction_commit_send(state, state->ev, state->handle); + if (subreq == NULL) { + DEBUG(1, ("sysdb_transaction_commit_send failed.\n")); + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, sysdb_transaction_complete, req); + + return; + +fail: + tevent_req_error(req, ret); + return; +} + +static void hbac_rule_store_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct hbac_get_rules_state *state = tevent_req_data(req, + struct hbac_get_rules_state); + int ret; + + ret = sysdb_store_custom_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + state->current_item++; + hbac_rule_store_prepare(req); +} + +static int hbac_get_rules_recv(struct tevent_req *req, TALLOC_CTX *memctx, + size_t *hbac_rule_count, + struct sysdb_attrs ***hbac_rule_list) +{ + struct hbac_get_rules_state *state = tevent_req_data(req, + struct hbac_get_rules_state); + enum tevent_req_state tstate; + uint64_t err; + int i; + + if (tevent_req_is_error(req, &tstate, &err)) { + if (err) return err; + return EIO; + } + + *hbac_rule_count = state->hbac_reply_count; + *hbac_rule_list = talloc_steal(memctx, state->hbac_reply_list); + for (i = 0; i < state->hbac_reply_count; i++) { + talloc_steal(memctx, state->hbac_reply_list[i]); + } + return EOK; +} + +enum hbac_result { + HBAC_ALLOW = 1, + HBAC_DENY, + HBAC_NOT_APPLICABLE +}; + +enum check_result { + RULE_APPLICABLE = 0, + RULE_NOT_APPLICABLE, + RULE_ERROR +}; + +enum check_result check_service(struct pam_data *pd, + struct sysdb_attrs *rule_attrs) +{ + int ret; + int i; + struct ldb_message_element *el; + + if (pd->service == NULL) { + DEBUG(1, ("No service in pam data, assuming error.\n")); + return RULE_ERROR; + } + + ret = sysdb_attrs_get_el(rule_attrs, IPA_SERVICE_NAME, &el); + if (ret != EOK) { + DEBUG(1, ("sysdb_attrs_get_el failed.\n")); + return RULE_ERROR; + } + if (el->num_values == 0) { + DEBUG(9, ("No services in rule specified, assuming rule applies.\n")); + return RULE_APPLICABLE; + } else { + for (i = 0; i < el->num_values; i++) { + if (strncasecmp(pd->service, (const char *) el->values[i].data, + el->values[i].length) == 0) { + DEBUG(9, ("Service [%s] found, rule applies.\n", + pd->service)); + return RULE_APPLICABLE; + } + } + DEBUG(9, ("No matching service found, rule does not apply.\n")); + return RULE_NOT_APPLICABLE; + } + + return RULE_ERROR; +} + +enum check_result check_user(struct hbac_ctx *hbac_ctx, + struct sysdb_attrs *rule_attrs) +{ + int ret; + int i; + int g; + struct ldb_message_element *el; + + if (hbac_ctx->user_dn == NULL) { + DEBUG(1, ("No user DN available, this should never happen.\n")); + return RULE_ERROR; + } + +/* TODO: add group and category checks */ + ret = sysdb_attrs_get_el(rule_attrs, IPA_MEMBER_USER, &el); + if (ret != EOK) { + DEBUG(1, ("sysdb_attrs_get_el failed.\n")); + return RULE_ERROR; + } + if (el->num_values == 0) { + DEBUG(9, ("No user specified, assuming rule applies.\n")); + return RULE_APPLICABLE; + } else { + for (i = 0; i < el->num_values; i++) { + DEBUG(9, ("Searching matches for [%.*s].\n", el->values[i].length, + (const char *) el->values[i].data)); + DEBUG(9, ("Checking user [%s].\n", hbac_ctx->user_dn)); + if (strncmp(hbac_ctx->user_dn, (const char *) el->values[i].data, + el->values[i].length) == 0) { + DEBUG(9, ("User [%s] found, rule applies.\n", + hbac_ctx->user_dn)); + return RULE_APPLICABLE; + } + + for (g = 0; g < hbac_ctx->groups_count; g++) { + DEBUG(9, ("Checking group [%s].\n", hbac_ctx->groups[g])); + if (strncmp(hbac_ctx->groups[g], + (const char *) el->values[i].data, + el->values[i].length) == 0) { + DEBUG(9, ("Group [%s] found, rule applies.\n", + hbac_ctx->groups[g])); + return RULE_APPLICABLE; + } + } + } + DEBUG(9, ("No matching user found, rule does not apply.\n")); + return RULE_NOT_APPLICABLE; + } + + return RULE_ERROR; +} + +enum check_result check_remote_hosts(struct pam_data *pd, + struct sysdb_attrs *rule_attrs) +{ + int ret; + int i; + struct ldb_message_element *src_el; + struct ldb_message_element *ext_el; + const char *remote_hostname; + + if (pd->rhost == NULL && pd->tty == NULL) { + DEBUG(1, ("Neither remote host nor tty specified in pam data, " + "assuming error.\n")); + return RULE_ERROR; + } + + if (pd->rhost == NULL) { + DEBUG(5, ("No remote host specified, but tty is set [%s], " + "assuming 'localhost'.\n", pd->tty)); + remote_hostname = "localhost"; + } else { + remote_hostname = pd->rhost; + } + +/* TODO: add group and category checks */ + ret = sysdb_attrs_get_el(rule_attrs, IPA_SOURCE_HOST, &src_el); + if (ret != EOK) { + DEBUG(1, ("sysdb_attrs_get_el failed.\n")); + return RULE_ERROR; + } + ret = sysdb_attrs_get_el(rule_attrs, IPA_EXTERNAL_HOST, &ext_el); + if (ret != EOK) { + DEBUG(1, ("sysdb_attrs_get_el failed.\n")); + return RULE_ERROR; + } + + if (src_el->num_values == 0 && ext_el->num_values == 0) { + DEBUG(9, ("No remote host specified, assuming rule applies.\n")); + return RULE_APPLICABLE; + } else { + for (i = 0; i < src_el->num_values; i++) { + if (strncasecmp(remote_hostname, + (const char *) src_el->values[i].data, + src_el->values[i].length) == 0) { + DEBUG(9, ("Source host [%s] found, rule applies.\n", + remote_hostname)); + return RULE_APPLICABLE; + } + } + for (i = 0; i < ext_el->num_values; i++) { + if (strncasecmp(remote_hostname, + (const char *) ext_el->values[i].data, + ext_el->values[i].length) == 0) { + DEBUG(9, ("External host [%s] found, rule applies.\n", + remote_hostname)); + return RULE_APPLICABLE; + } + } + DEBUG(9, ("No matching remote host found.\n")); + return RULE_NOT_APPLICABLE; + } + + return RULE_ERROR; +} + +static errno_t check_if_rule_applies(enum hbac_result *result, + struct hbac_ctx *hbac_ctx, + struct sysdb_attrs *rule_attrs) { + int ret; + struct ldb_message_element *el; + enum hbac_result rule_type; + char *rule_name; + struct pam_data *pd = hbac_ctx->pd; + + ret = sysdb_attrs_get_el(rule_attrs, IPA_CN, &el); + if (ret != EOK) { + DEBUG(1, ("sysdb_attrs_get_el failed.\n")); + return ret; + } + if (el->num_values == 0) { + DEBUG(4, ("rule has no name, assuming '(none)'.\n")); + rule_name = talloc_strdup(rule_attrs, "(none)"); + } else { + rule_name = talloc_strndup(rule_attrs, (const char*) el->values[0].data, + el->values[0].length); + } + if (rule_name == NULL) { + DEBUG(1, ("talloc_strdup failed.\n")); + return ENOMEM; + } + DEBUG(9, ("Processsing rule [%s].\n", rule_name)); + + /* rule type */ + ret = sysdb_attrs_get_el(rule_attrs, IPA_ACCESS_RULE_TYPE, &el); + if (ret != EOK) { + DEBUG(1, ("sysdb_attrs_get_el failed.\n")); + return ret; + } + if (el->num_values == 0) { + DEBUG(4, ("rule has no type, assuming 'deny'.\n")); + rule_type = HBAC_DENY; + } else if (el->num_values == 1) { + if (strncasecmp((const char *) el->values[0].data, "allow", + el->values[0].length) == 0) { + rule_type = HBAC_ALLOW; + } else { + rule_type = HBAC_DENY; + } + } else { + DEBUG(1, ("rule has an unsupported number of values [%d].\n", + el->num_values)); + return EINVAL; + } + + ret = check_service(pd, rule_attrs); + if (ret != RULE_APPLICABLE) { + goto not_applicable; + } + + ret = check_user(hbac_ctx, rule_attrs); + if (ret != RULE_APPLICABLE) { + goto not_applicable; + } + + ret = check_remote_hosts(pd, rule_attrs); + if (ret != RULE_APPLICABLE) { + goto not_applicable; + } + + *result = rule_type; + + return EOK; + +not_applicable: + if (ret == RULE_NOT_APPLICABLE) { + *result = HBAC_NOT_APPLICABLE; + } else { + *result = HBAC_DENY; + } + return EOK; +} + +static int evaluate_ipa_hbac_rules(struct hbac_ctx *hbac_ctx, + bool *access_allowed) +{ + bool allow_matched = false; + enum hbac_result result; + int ret; + int i; + + *access_allowed = false; + + for (i = 0; i < hbac_ctx->hbac_rule_count ; i++) { + + ret = check_if_rule_applies(&result, hbac_ctx, + hbac_ctx->hbac_rule_list[i]); + if (ret != EOK) { + DEBUG(1, ("check_if_rule_applies failed.\n")); + return ret; + } + + switch (result) { + case HBAC_DENY: + DEBUG(3, ("Access denied by single rule.\n")); + return EOK; + break; + case HBAC_ALLOW: + allow_matched = true; + DEBUG(9, ("Current rule allows access.\n")); + break; + default: + DEBUG(9, ("Current rule does not apply.\n")); + } + + } + + *access_allowed = allow_matched; + + return EOK; +} + +static void hbac_get_host_info_done(struct tevent_req *req); +static void hbac_get_rules_done(struct tevent_req *req); +static void hbac_get_user_info_done(struct tevent_req *req); + +void ipa_access_handler(struct be_req *be_req) +{ + struct tevent_req *req; + struct pam_data *pd; + struct hbac_ctx *hbac_ctx; + int pam_status = PAM_SYSTEM_ERR; + struct ipa_access_ctx *ipa_access_ctx; + + pd = talloc_get_type(be_req->req_data, struct pam_data); + + hbac_ctx = talloc_zero(be_req, struct hbac_ctx); + if (hbac_ctx == NULL) { + DEBUG(1, ("talloc failed.\n")); + goto fail; + } + hbac_ctx->be_req = be_req; + hbac_ctx->pd = pd; + ipa_access_ctx = talloc_get_type( + be_req->be_ctx->bet_info[BET_ACCESS].pvt_bet_data, + struct ipa_access_ctx); + hbac_ctx->sdap_ctx = ipa_access_ctx->sdap_ctx; + hbac_ctx->ipa_options = ipa_access_ctx->ipa_options; + + req = hbac_get_host_info_send(hbac_ctx, be_req->be_ctx->ev, + hbac_ctx->sdap_ctx, be_req->be_ctx->sysdb, + dp_opt_get_string(hbac_ctx->ipa_options, + IPA_DOMAIN), + dp_opt_get_string(hbac_ctx->ipa_options, + IPA_HOSTNAME)); + if (req == NULL) { + DEBUG(1, ("hbac_get_host_info_send failed.\n")); + goto fail; + } + + tevent_req_set_callback(req, hbac_get_host_info_done, hbac_ctx); + return; + +fail: + ipa_access_reply(be_req, pam_status); +} + +static void hbac_get_host_info_done(struct tevent_req *req) +{ + struct hbac_ctx *hbac_ctx = tevent_req_callback_data(req, struct hbac_ctx); + struct be_req *be_req = hbac_ctx->be_req; + int ret; + int pam_status = PAM_SYSTEM_ERR; + const char *ipa_hostname; + int i; + + ret = hbac_get_host_info_recv(req, hbac_ctx, &hbac_ctx->hbac_host_info); + talloc_zfree(req); + if (ret != EOK) { + goto fail; + } + + ipa_hostname = dp_opt_get_cstring(hbac_ctx->ipa_options, IPA_HOSTNAME); + if (ipa_hostname == NULL) { + DEBUG(1, ("Missing ipa_hostname, this should never happen.\n")); + ret = EINVAL; + goto fail; + } + + for (i = 0; hbac_ctx->hbac_host_info[i] != NULL; i++) { + if (strcmp(hbac_ctx->hbac_host_info[i]->fqdn, ipa_hostname) == 0 || + strcmp(hbac_ctx->hbac_host_info[i]->serverhostname, + ipa_hostname) == 0) { + break; + } + } + if (hbac_ctx->hbac_host_info[i] == NULL) { + DEBUG(1, ("Missing host info for [%s].\n", ipa_hostname)); + ret = EINVAL; + goto fail; + } + req = hbac_get_rules_send(hbac_ctx, be_req->be_ctx->ev, hbac_ctx->sdap_ctx, + be_req->be_ctx->sysdb, + dp_opt_get_string(hbac_ctx->ipa_options, + IPA_DOMAIN), + hbac_ctx->hbac_host_info[i]->dn, + hbac_ctx->hbac_host_info[i]->memberof); + if (req == NULL) { + DEBUG(1, ("hbac_get_rules_send failed.\n")); + goto fail; + } + + tevent_req_set_callback(req, hbac_get_rules_done, hbac_ctx); + return; + +fail: + ipa_access_reply(be_req, pam_status); +} + +static void hbac_get_rules_done(struct tevent_req *req) +{ + struct hbac_ctx *hbac_ctx = tevent_req_callback_data(req, struct hbac_ctx); + struct pam_data *pd = hbac_ctx->pd; + struct be_req *be_req = hbac_ctx->be_req; + int ret; + int pam_status = PAM_SYSTEM_ERR; + + ret = hbac_get_rules_recv(req, hbac_ctx, &hbac_ctx->hbac_rule_count, + &hbac_ctx->hbac_rule_list); + talloc_zfree(req); + if (ret != EOK) { + goto fail; + } + + req = hbac_get_user_info_send(hbac_ctx, be_req->be_ctx->ev, be_req->be_ctx, + pd->user); + if (req == NULL) { + DEBUG(1, ("hbac_get_user_info_send failed.\n")); + goto fail; + } + + tevent_req_set_callback(req, hbac_get_user_info_done, hbac_ctx); + return; + +fail: + ipa_access_reply(be_req, pam_status); +} + +static void hbac_get_user_info_done(struct tevent_req *req) +{ + struct hbac_ctx *hbac_ctx = tevent_req_callback_data(req, struct hbac_ctx); + struct be_req *be_req = hbac_ctx->be_req; + int ret; + int pam_status = PAM_SYSTEM_ERR; + bool access_allowed = false; + + ret = hbac_get_user_info_recv(req, hbac_ctx, &hbac_ctx->user_dn, + &hbac_ctx->groups_count, + &hbac_ctx->groups); + talloc_zfree(req); + if (ret != EOK) { + goto failed; + } + + ret = evaluate_ipa_hbac_rules(hbac_ctx, &access_allowed); + if (ret != EOK) { + DEBUG(1, ("evaluate_ipa_hbac_rules failed.\n")); + goto failed; + } + + if (access_allowed) { + pam_status = PAM_SUCCESS; + DEBUG(5, ("Access allowed.\n")); + } else { + pam_status = PAM_PERM_DENIED; + DEBUG(3, ("Access denied.\n")); + } + +failed: + ipa_access_reply(be_req, pam_status); +} diff --git a/server/providers/ipa/ipa_access.h b/server/providers/ipa/ipa_access.h new file mode 100644 index 00000000..e4903cb7 --- /dev/null +++ b/server/providers/ipa/ipa_access.h @@ -0,0 +1,55 @@ +/* + SSSD + + IPA Backend Module -- Access control + + Authors: + Sumit Bose <sbose@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/>. +*/ + +#ifndef _IPA_ACCESS_H_ +#define _IPA_ACCESS_H_ + +#include "providers/ldap/ldap_common.h" + +enum ipa_access_mode { + IPA_ACCESS_DENY = 0, + IPA_ACCESS_ALLOW +}; + +struct ipa_access_ctx { + struct sdap_id_ctx *sdap_ctx; + struct dp_option *ipa_options; +}; + +struct hbac_ctx { + struct sdap_id_ctx *sdap_ctx; + struct dp_option *ipa_options; + struct be_req *be_req; + struct pam_data *pd; + struct hbac_host_info **hbac_host_info; + struct sysdb_attrs **hbac_rule_list; + size_t hbac_rule_count; + const char *user_dn; + size_t groups_count; + const char **groups; +}; + +void ipa_access_handler(struct be_req *be_req); + +#endif /* _IPA_ACCESS_H_ */ diff --git a/server/providers/ipa/ipa_init.c b/server/providers/ipa/ipa_init.c index d1439ded..7ef98e62 100644 --- a/server/providers/ipa/ipa_init.c +++ b/server/providers/ipa/ipa_init.c @@ -29,6 +29,7 @@ #include "providers/ipa/ipa_common.h" #include "providers/krb5/krb5_auth.h" +#include "providers/ipa/ipa_access.h" struct ipa_options *ipa_options = NULL; @@ -48,6 +49,11 @@ struct bet_ops ipa_chpass_ops = { .finalize = NULL, }; +struct bet_ops ipa_access_ops = { + .handler = ipa_access_handler, + .finalize = NULL +}; + int sssm_ipa_init(struct be_ctx *bectx, struct bet_ops **ops, void **pvt_data) @@ -64,6 +70,13 @@ int sssm_ipa_init(struct be_ctx *bectx, return ENOMEM; } + if (ipa_options->id_ctx) { + /* already initialized */ + *ops = &ipa_id_ops; + *pvt_data = ipa_options->id_ctx; + return EOK; + } + ctx = talloc_zero(ipa_options, struct sdap_id_ctx); if (!ctx) { return ENOMEM; @@ -186,7 +199,46 @@ done: int sssm_ipa_chpass_init(struct be_ctx *bectx, struct bet_ops **ops, - void **pvt_auth_data) + void **pvt_data) +{ + int ret; + ret = sssm_ipa_auth_init(bectx, ops, pvt_data); + *ops = &ipa_chpass_ops; + return ret; +} + +int sssm_ipa_access_init(struct be_ctx *bectx, + struct bet_ops **ops, + void **pvt_data) { - return sssm_ipa_auth_init(bectx, ops, pvt_auth_data); + int ret; + struct ipa_access_ctx *ipa_access_ctx; + + ipa_access_ctx = talloc_zero(bectx, struct ipa_access_ctx); + if (ipa_access_ctx == NULL) { + DEBUG(1, ("talloc_zero failed.\n")); + return ENOMEM; + } + + ret = sssm_ipa_init(bectx, ops, (void **) &ipa_access_ctx->sdap_ctx); + if (ret != EOK) { + DEBUG(1, ("sssm_ipa_init failed.\n")); + goto done; + } + + ret = dp_copy_options(ipa_access_ctx, ipa_options->basic, + IPA_OPTS_BASIC, &ipa_access_ctx->ipa_options); + if (ret != EOK) { + DEBUG(1, ("dp_copy_options failed.\n")); + goto done; + } + + *ops = &ipa_access_ops; + *pvt_data = ipa_access_ctx; + +done: + if (ret != EOK) { + talloc_free(ipa_access_ctx); + } + return ret; } |