summaryrefslogtreecommitdiffstats
path: root/src/providers/ipa/ipa_access.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/providers/ipa/ipa_access.c')
-rw-r--r--src/providers/ipa/ipa_access.c1823
1 files changed, 1823 insertions, 0 deletions
diff --git a/src/providers/ipa/ipa_access.c b/src/providers/ipa/ipa_access.c
new file mode 100644
index 000000000..7dfe1fd90
--- /dev/null
+++ b/src/providers/ipa/ipa_access.c
@@ -0,0 +1,1823 @@
+/*
+ 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"
+#include "providers/ipa/ipa_timerules.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_HOST_CATEGORY "hostCategory"
+#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"
+
+static errno_t msgs2attrs_array(TALLOC_CTX *mem_ctx, size_t count,
+ struct ldb_message **msgs,
+ struct sysdb_attrs ***attrs)
+{
+ int i;
+ struct sysdb_attrs **a;
+
+ a = talloc_array(mem_ctx, struct sysdb_attrs *, count);
+ if (a == NULL) {
+ DEBUG(1, ("talloc_array failed.\n"));
+ return ENOMEM;
+ }
+
+ for (i = 0; i < count; i++) {
+ a[i] = talloc(a, struct sysdb_attrs);
+ if (a[i] == NULL) {
+ DEBUG(1, ("talloc_array failed.\n"));
+ talloc_free(a);
+ return ENOMEM;
+ }
+ a[i]->num = msgs[i]->num_elements;
+ a[i]->a = talloc_steal(a[i], msgs[i]->elements);
+ }
+
+ *attrs = a;
+
+ return EOK;
+}
+
+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);
+ int i;
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *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;
+ bool offline;
+
+ 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 hbac_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,
+ bool offline,
+ struct sdap_id_ctx *sdap_ctx,
+ struct sysdb_ctx *sysdb,
+ const char *ipa_domain,
+ const char **hostnames)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct hbac_get_host_info_state *state;
+ int ret;
+ int i;
+
+ if (hostnames == NULL || ipa_domain == NULL) {
+ DEBUG(1, ("Missing hostnames 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->offline = offline;
+
+ state->host_reply_list = NULL;
+ state->host_reply_count = 0;
+ state->current_item = 0;
+ state->hbac_host_info = NULL;
+
+ state->host_filter = talloc_asprintf(state, "(|");
+ if (state->host_filter == NULL) {
+ DEBUG(1, ("Failed to create filter.\n"));
+ ret = ENOMEM;
+ goto fail;
+ }
+ for (i = 0; hostnames[i] != NULL; i++) {
+ state->host_filter = talloc_asprintf_append(state->host_filter,
+ "(&(objectclass=ipaHost)"
+ "(|(fqdn=%s)(serverhostname=%s)))",
+ hostnames[i], hostnames[i]);
+ if (state->host_filter == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ }
+ state->host_filter = talloc_asprintf_append(state->host_filter, ")");
+ if (state->host_filter == NULL) {
+ 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 *, 7);
+ 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] = SYSDB_ORIG_DN;
+ state->host_attrs[5] = SYSDB_ORIG_MEMBEROF;
+ state->host_attrs[6] = NULL;
+
+ if (offline) {
+ subreq = sysdb_search_custom_send(state, state->ev, state->sysdb, NULL,
+ state->sdap_ctx->be->domain,
+ state->host_filter, HBAC_HOSTS_SUBDIR,
+ state->host_attrs);
+ if (subreq == NULL) {
+ DEBUG(1, ("sysdb_search_custom_send.\n"));
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ tevent_req_set_callback(subreq, hbac_get_host_memberof_done, req);
+
+ return req;
+ }
+
+ 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,
+ sdap_ctx->be, sdap_ctx->service, 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,
+ NULL, 0);
+
+ if (subreq == NULL) {
+ DEBUG(1, ("sdap_get_generic_send failed.\n"));
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ tevent_req_set_callback(subreq, hbac_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,
+ NULL, 0);
+
+ if (subreq == NULL) {
+ DEBUG(1, ("sdap_get_generic_send failed.\n"));
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ tevent_req_set_callback(subreq, hbac_get_host_memberof_done, req);
+
+ return;
+
+fail:
+ tevent_req_error(req, ret);
+ return;
+}
+
+static void hbac_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;
+ struct ldb_message **msgs;
+
+ if (state->offline) {
+ ret = sysdb_search_custom_recv(subreq, state, &state->host_reply_count,
+ &msgs);
+ } else {
+ 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_count == 0) {
+ DEBUG(1, ("No hosts not found in IPA server.\n"));
+ ret = ENOENT;
+ goto fail;
+ }
+
+ if (state->offline) {
+ ret = msgs2attrs_array(state, state->host_reply_count, msgs,
+ &state->host_reply_list);
+ talloc_zfree(msgs);
+ if (ret != EOK) {
+ DEBUG(1, ("msgs2attrs_array failed.\n"));
+ goto fail;
+ }
+ }
+
+ hhi = talloc_array(state, struct hbac_host_info *, state->host_reply_count + 1);
+ if (hhi == NULL) {
+ DEBUG(1, ("talloc_array 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(hhi, 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],
+ state->offline ? SYSDB_ORIG_MEMBEROF :
+ 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;
+
+ if (state->offline) {
+ 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_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);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *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;
+ bool offline;
+
+ 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,
+ bool offline,
+ 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->offline = offline;
+ 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 *, 16);
+ 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] = IPA_MEMBER_HOST;
+ state->hbac_attrs[13] = IPA_HOST_CATEGORY;
+ state->hbac_attrs[14] = SYSDB_ORIG_DN;
+ state->hbac_attrs[15] = NULL;
+
+ state->hbac_filter = talloc_asprintf(state,
+ "(&(objectclass=ipaHBACRule)"
+ "(|(%s=%s)(%s=%s)",
+ IPA_HOST_CATEGORY, "all",
+ 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 (offline) {
+ subreq = sysdb_search_custom_send(state, state->ev, state->sysdb, NULL,
+ state->sdap_ctx->be->domain,
+ state->hbac_filter, HBAC_RULES_SUBDIR,
+ state->hbac_attrs);
+ if (subreq == NULL) {
+ DEBUG(1, ("sysdb_search_custom_send failed.\n"));
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ tevent_req_set_callback(subreq, hbac_rule_get_done, req);
+
+ return req;
+ }
+
+ 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,
+ sdap_ctx->be, sdap_ctx->service, 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,
+ NULL, 0);
+
+ 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,
+ NULL, 0);
+
+ 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;
+ struct ldb_message **msgs;
+
+ if (state->offline) {
+ ret = sysdb_search_custom_recv(subreq, state, &state->hbac_reply_count,
+ &msgs);
+ } else {
+ 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;
+ }
+
+ if (state->offline) {
+ ret = msgs2attrs_array(state, state->hbac_reply_count, msgs,
+ &state->hbac_reply_list);
+ talloc_zfree(msgs);
+ if (ret != EOK) {
+ DEBUG(1, ("msgs2attrs_array failed.\n"));
+ goto fail;
+ }
+ }
+
+ 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) {
+ DEBUG(1, ("Missing original DN.\n"));
+ ret = EINVAL;
+ goto fail;
+ }
+ DEBUG(9, ("OriginalDN: [%s].\n", (const char *)el->values[0].data));
+ }
+
+ if (state->hbac_reply_count == 0 || state->offline) {
+ 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);
+ int i;
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *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_access_time(struct time_rules_ctx *tr_ctx,
+ struct sysdb_attrs *rule_attrs)
+{
+ int ret;
+ int i;
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct ldb_message_element *el;
+ char *rule;
+ time_t now;
+ bool result;
+
+ now = time(NULL);
+ if (now == (time_t) -1) {
+ DEBUG(1, ("time failed [%d][%s].\n", errno, strerror(errno)));
+ return RULE_ERROR;
+ }
+
+ ret = sysdb_attrs_get_el(rule_attrs, IPA_ACCESS_TIME, &el);
+ if (ret != EOK) {
+ DEBUG(1, ("sysdb_attrs_get_el failed.\n"));
+ return RULE_ERROR;
+ }
+ if (el->num_values == 0) {
+ DEBUG(9, ("No access time specified, assuming rule applies.\n"));
+ return RULE_APPLICABLE;
+ } else {
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(1, ("talloc_new failed.\n"));
+ return RULE_ERROR;
+ }
+
+ for (i = 0; i < el->num_values; i++) {
+ rule = talloc_strndup(tmp_ctx, (const char *) el->values[i].data,
+ el->values[i].length);
+ ret = check_time_rule(tmp_ctx, tr_ctx, rule, now, &result);
+ if (ret != EOK) {
+ DEBUG(1, ("check_time_rule failed.\n"));
+ ret = RULE_ERROR;
+ goto done;
+ }
+
+ if (result) {
+ DEBUG(9, ("Current time [%d] matches rule [%s].\n", now, rule));
+ ret = RULE_APPLICABLE;
+ goto done;
+ }
+ }
+ }
+
+ ret = RULE_NOT_APPLICABLE;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+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;
+ }
+
+ ret = sysdb_attrs_get_el(rule_attrs, IPA_USER_CATEGORY, &el);
+ if (ret != EOK) {
+ DEBUG(1, ("sysdb_attrs_get_el failed.\n"));
+ return RULE_ERROR;
+ }
+ if (el->num_values == 0) {
+ DEBUG(9, ("USer category is not set.\n"));
+ } else {
+ for (i = 0; i < el->num_values; i++) {
+ if (strncasecmp("all", (const char *) el->values[i].data,
+ el->values[i].length) == 0) {
+ DEBUG(9, ("User category is set to 'all', rule applies.\n"));
+ return RULE_APPLICABLE;
+ }
+ DEBUG(9, ("Unsupported user category [%.*s].\n",
+ el->values[i].length,
+ (char *) el->values[i].data));
+ }
+ }
+
+ 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, rule does not apply.\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(const char *rhost,
+ struct hbac_host_info *hhi,
+ struct sysdb_attrs *rule_attrs)
+{
+ int ret;
+ int i;
+ int m;
+ struct ldb_message_element *cat_el;
+ struct ldb_message_element *src_el;
+ struct ldb_message_element *ext_el;
+
+ if (hhi == NULL && (rhost == NULL || *rhost == '\0')) {
+ DEBUG(1, ("No remote host information specified, assuming error.\n"));
+ return RULE_ERROR;
+ }
+
+ ret = sysdb_attrs_get_el(rule_attrs, IPA_SOURCE_HOST_CATEGORY, &cat_el);
+ if (ret != EOK) {
+ DEBUG(1, ("sysdb_attrs_get_el failed.\n"));
+ return RULE_ERROR;
+ }
+ if (cat_el->num_values == 0) {
+ DEBUG(9, ("Source host category not set.\n"));
+ } else {
+ for(i = 0; i < cat_el->num_values; i++) {
+ if (strncasecmp("all", (const char *) cat_el->values[i].data,
+ cat_el->values[i].length) == 0) {
+ DEBUG(9, ("Source host category is set to 'all', "
+ "rule applies.\n"));
+ return RULE_APPLICABLE;
+ }
+ DEBUG(9, ("Unsupported source hosts category [%.*s].\n",
+ cat_el->values[i].length,
+ (char *) cat_el->values[i].data));
+ }
+ }
+
+ 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 in rule, rule does not apply.\n"));
+ return RULE_NOT_APPLICABLE;
+ } else {
+ if (hhi != NULL) {
+ for (i = 0; i < src_el->num_values; i++) {
+ if (strncasecmp(hhi->dn, (const char *) src_el->values[i].data,
+ src_el->values[i].length) == 0) {
+ DEBUG(9, ("Source host [%s] found, rule applies.\n",
+ hhi->dn));
+ return RULE_APPLICABLE;
+ }
+ for (m = 0; hhi->memberof[m] != NULL; m++) {
+ if (strncasecmp(hhi->memberof[m],
+ (const char *) src_el->values[i].data,
+ src_el->values[i].length) == 0) {
+ DEBUG(9, ("Source host group [%s] found, rule applies.\n",
+ hhi->memberof[m]));
+ return RULE_APPLICABLE;
+ }
+ }
+ }
+ }
+
+ if (rhost != NULL && *rhost != '\0') {
+ for (i = 0; i < ext_el->num_values; i++) {
+ if (strncasecmp(rhost, (const char *) ext_el->values[i].data,
+ ext_el->values[i].length) == 0) {
+ DEBUG(9, ("External host [%s] found, rule applies.\n",
+ rhost));
+ 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_access_time(hbac_ctx->tr_ctx, rule_attrs);
+ if (ret != RULE_APPLICABLE) {
+ goto not_applicable;
+ }
+
+ ret = check_remote_hosts(pd->rhost, hbac_ctx->remote_hhi, 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;
+ const char *hostlist[3];
+
+ 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;
+ hbac_ctx->tr_ctx = ipa_access_ctx->tr_ctx;
+ hbac_ctx->offline = be_is_offline(be_req->be_ctx);
+
+ DEBUG(9, ("Connection status is [%s].\n", hbac_ctx->offline ? "offline" :
+ "online"));
+
+
+ hostlist[0] = dp_opt_get_cstring(hbac_ctx->ipa_options, IPA_HOSTNAME);
+ if (hostlist[0] == NULL) {
+ DEBUG(1, ("ipa_hostname not available.\n"));
+ goto fail;
+ }
+ if (pd->rhost != NULL && *pd->rhost != '\0') {
+ hostlist[1] = pd->rhost;
+ } else {
+ hostlist[1] = NULL;
+ pd->rhost = dp_opt_get_string(hbac_ctx->ipa_options, IPA_HOSTNAME);
+ if (pd->rhost == NULL) {
+ DEBUG(1, ("ipa_hostname not available.\n"));
+ goto fail;
+ }
+ }
+ hostlist[2] = NULL;
+
+ req = hbac_get_host_info_send(hbac_ctx, be_req->be_ctx->ev,
+ hbac_ctx->offline,
+ hbac_ctx->sdap_ctx, be_req->be_ctx->sysdb,
+ dp_opt_get_string(hbac_ctx->ipa_options,
+ IPA_DOMAIN),
+ hostlist);
+ 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;
+ struct hbac_host_info *local_hhi = NULL;
+ 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) {
+ local_hhi = hbac_ctx->hbac_host_info[i];
+ }
+ if (hbac_ctx->pd->rhost != NULL && *hbac_ctx->pd->rhost != '\0') {
+ if (strcmp(hbac_ctx->hbac_host_info[i]->fqdn,
+ hbac_ctx->pd->rhost) == 0 ||
+ strcmp(hbac_ctx->hbac_host_info[i]->serverhostname,
+ hbac_ctx->pd->rhost) == 0) {
+ hbac_ctx->remote_hhi = hbac_ctx->hbac_host_info[i];
+ }
+ }
+ }
+ if (local_hhi == 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->offline,
+ hbac_ctx->sdap_ctx, be_req->be_ctx->sysdb,
+ dp_opt_get_string(hbac_ctx->ipa_options,
+ IPA_DOMAIN),
+ local_hhi->dn, local_hhi->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);
+}