summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am38
-rw-r--r--configure.ac4
-rw-r--r--contrib/sssd.spec.in36
-rw-r--r--src/config/SSSDConfig.py2
-rw-r--r--src/config/etc/sssd.api.d/sssd-ipa.conf2
-rw-r--r--src/external/libunistring.m49
-rw-r--r--src/man/sssd-ipa.5.xml42
-rw-r--r--src/providers/ipa/hbac_evaluator.c356
-rw-r--r--src/providers/ipa/ipa_access.c2069
-rw-r--r--src/providers/ipa/ipa_access.h31
-rw-r--r--src/providers/ipa/ipa_common.c4
-rw-r--r--src/providers/ipa/ipa_common.h2
-rw-r--r--src/providers/ipa/ipa_hbac.h177
-rw-r--r--src/providers/ipa/ipa_hbac.pc.in11
-rw-r--r--src/providers/ipa/ipa_hbac_common.c883
-rw-r--r--src/providers/ipa/ipa_hbac_hosts.c524
-rw-r--r--src/providers/ipa/ipa_hbac_private.h194
-rw-r--r--src/providers/ipa/ipa_hbac_rules.c231
-rw-r--r--src/providers/ipa/ipa_hbac_services.c451
-rw-r--r--src/providers/ipa/ipa_hbac_users.c345
-rw-r--r--src/providers/ldap/ldap_common.c29
-rw-r--r--src/providers/ldap/ldap_common.h4
-rw-r--r--src/tests/ipa_hbac-tests.c850
23 files changed, 4592 insertions, 1702 deletions
diff --git a/Makefile.am b/Makefile.am
index 49520f3be..d75300bdb 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -32,6 +32,7 @@ pipepath = @pipepath@
initdir = @initdir@
logpath = @logpath@
pubconfpath = @pubconfpath@
+pkgconfigdir = $(libdir)/pkgconfig
AM_CFLAGS =
if WANT_AUX_INFO
@@ -43,6 +44,8 @@ if HAVE_GCC
-Werror-implicit-function-declaration
endif
+dist_pkgconfig_DATA =
+
ACLOCAL_AMFLAGS = -I m4 -I .
sbin_PROGRAMS = \
@@ -78,7 +81,8 @@ if HAVE_CHECK
ipa_ldap_opt-tests \
simple_access-tests \
crypto-tests \
- util-tests
+ util-tests \
+ ipa_hbac-tests
endif
check_PROGRAMS = \
@@ -358,6 +362,17 @@ if HAVE_NSS
endif
+lib_LTLIBRARIES = libipa_hbac.la
+dist_pkgconfig_DATA += src/providers/ipa/ipa_hbac.pc
+libipa_hbac_la_SOURCES = \
+ src/providers/ipa/hbac_evaluator.c
+libipa_hbac_la_LDFLAGS = \
+ -version 1:0:1 \
+ -lunistring
+
+include_HEADERS = \
+ src/providers/ipa/ipa_hbac.h
+
####################
# Program Binaries #
####################
@@ -690,6 +705,18 @@ crypto_tests_LDADD = \
$(CHECK_LIBS) \
libsss_test_common.la
+ipa_hbac_tests_SOURCES = \
+ src/tests/ipa_hbac-tests.c \
+ $(SSSD_UTIL_OBJ)
+ipa_hbac_tests_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(CHECK_CFLAGS)
+ipa_hbac_tests_LDADD = \
+ $(SSSD_LIBS) \
+ $(CHECK_LIBS) \
+ libsss_test_common.la \
+ libipa_hbac.la
+
endif
stress_tests_SOURCES = \
@@ -833,6 +860,12 @@ libsss_ipa_la_SOURCES = \
src/providers/ipa/ipa_auth.c \
src/providers/ipa/ipa_access.c \
src/providers/ipa/ipa_dyndns.c \
+ src/providers/ipa/ipa_hbac_hosts.c \
+ src/providers/ipa/ipa_hbac_private.h \
+ src/providers/ipa/ipa_hbac_rules.c \
+ src/providers/ipa/ipa_hbac_services.c \
+ src/providers/ipa/ipa_hbac_users.c \
+ src/providers/ipa/ipa_hbac_common.c \
src/providers/ldap/ldap_id.c \
src/providers/ldap/ldap_id_enum.c \
src/providers/ldap/ldap_id_cleanup.c \
@@ -870,7 +903,8 @@ libsss_ipa_la_LIBADD = \
$(DHASH_LIBS) \
$(KEYUTILS_LIBS) \
$(KRB5_LIBS) \
- libsss_crypt.la
+ libsss_crypt.la \
+ libipa_hbac.la
libsss_ipa_la_LDFLAGS = \
-version-info 1:0:0 \
-module
diff --git a/configure.ac b/configure.ac
index 84b83eb1f..f11dfb87e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -121,6 +121,7 @@ m4_include([src/external/nsupdate.m4])
m4_include([src/external/libkeyutils.m4])
m4_include([src/external/libnl.m4])
m4_include([src/util/signal.m4])
+m4_include([src/external/libunistring.m4])
PKG_CHECK_MODULES([DBUS],[dbus-1])
dnl if test -n "`$PKG_CONFIG --modversion dbus-1 | grep '^0\.'`" ; then
@@ -187,6 +188,7 @@ AC_DEFINE_UNQUOTED([ABS_BUILD_DIR], ["$abs_build_dir"], [Absolute path to the bu
AC_SUBST([abs_builddir], $abs_build_dir)
AC_CONFIG_FILES([Makefile contrib/sssd.spec src/examples/rwtab src/doxy.config
- src/sysv/systemd/sssd.service po/Makefile.in src/man/Makefile])
+ src/sysv/systemd/sssd.service po/Makefile.in src/man/Makefile
+ src/providers/ipa/ipa_hbac.pc])
AC_OUTPUT
diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
index c08633a0e..db0ecb3f4 100644
--- a/contrib/sssd.spec.in
+++ b/contrib/sssd.spec.in
@@ -19,7 +19,8 @@ BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
Requires: libldb >= 0.9.3
Requires: libtdb >= 1.1.3
-Requires: sssd-client = %{version}-%{release}
+Requires: sssd-client%{?_isa} = %{version}-%{release}
+Requires: libipa_hbac = %{version}-%{release}
Requires: cyrus-sasl-gssapi
Requires: keyutils-libs
Requires(post): initscripts chkconfig /sbin/ldconfig
@@ -73,7 +74,9 @@ BuildRequires: bind-utils
BuildRequires: keyutils-libs-devel
BuildRequires: libnl-devel
BuildRequires: nscd
-BuildRequires: gettext
+BuildRequires: gettext-devel
+BuildRequires: pkgconfig
+BuildRequires: libunistring-devel
%description
Provides a set of daemons to manage access to remote directories and
@@ -104,6 +107,23 @@ SSSD when using id_provider = local in /etc/sssd/sssd.conf.
Also provides a userspace tool for generating an obfuscated LDAP password for
use with ldap_default_authtok_type = obfuscated_password.
+%package -n libipa_hbac
+Summary: FreeIPA HBAC Evaluator library
+Group: Development/Libraries
+License: LGPLv3+
+
+%description -n libipa_hbac
+Utility library to validate FreeIPA HBAC rules for authorization requests
+
+%package -n libipa_hbac-devel
+Summary: FreeIPA HBAC Evaluator library
+Group: Development/Libraries
+License: LGPLv3+
+Requires: libipa_hbac = %{version}-%{release}
+
+%description -n libipa_hbac-devel
+Utility library to validate FreeIPA HBAC rules for authorization requests
+
%prep
%setup -q
@@ -158,6 +178,7 @@ rm -f \
$RPM_BUILD_ROOT/%{_libdir}/sssd/libsss_ipa.la \
$RPM_BUILD_ROOT/%{_libdir}/sssd/libsss_simple.la \
$RPM_BUILD_ROOT/%{_libdir}/krb5/plugins/libkrb5/sssd_krb5_locator_plugin.la \
+ $RPM_BUILD_ROOT/%{_libdir}/libipa_hbac.la \
$RPM_BUILD_ROOT/%{python_sitearch}/pysss.la
# Older versions of rpmbuild can only handle one -f option
@@ -232,6 +253,17 @@ rm -rf $RPM_BUILD_ROOT
%{_mandir}/man8/sss_usermod.8*
%{_mandir}/man8/sss_obfuscate.8*
+%files -n libipa_hbac
+%defattr(-,root,root,-)
+%doc src/sss_client/COPYING src/sss_client/COPYING.LESSER
+%{_libdir}/libipa_hbac.so.*
+
+%files -n libipa_hbac-devel
+%defattr(-,root,root,-)
+%{_includedir}/ipa_hbac.h
+%{_libdir}/libipa_hbac.so
+%{_libdir}/pkgconfig/ipa_hbac.pc
+
%post
/sbin/ldconfig
/sbin/chkconfig --add %{servicename}
diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py
index fed19f079..920a8a056 100644
--- a/src/config/SSSDConfig.py
+++ b/src/config/SSSDConfig.py
@@ -97,6 +97,8 @@ option_strings = {
'ipa_dyndns_update' : _("Whether to automatically update the client's DNS entry in FreeIPA"),
'ipa_dyndns_iface' : _("The interface whose IP should be used for dynamic DNS updates"),
'ipa_hbac_search_base' : _("Search base for HBAC related objects"),
+ 'ipa_hbac_refresh' : _("The amount of time between lookups of the HBAC rules against the IPA server"),
+ 'ipa_hbac_treat_deny_as' : _("If DENY rules are present, either DENY_ALL or IGNORE"),
# [provider/krb5]
'krb5_kdcip' : _('Kerberos server address'),
diff --git a/src/config/etc/sssd.api.d/sssd-ipa.conf b/src/config/etc/sssd.api.d/sssd-ipa.conf
index 31b7dc9be..d7992b608 100644
--- a/src/config/etc/sssd.api.d/sssd-ipa.conf
+++ b/src/config/etc/sssd.api.d/sssd-ipa.conf
@@ -100,6 +100,8 @@ krb5_renew_interval = int, None, false
krb5_use_fast = str, None, false
[provider/ipa/access]
+ipa_hbac_refresh = int, None, false
+ipa_hbac_treat_deny_as = str, None, false
[provider/ipa/chpass]
diff --git a/src/external/libunistring.m4 b/src/external/libunistring.m4
new file mode 100644
index 000000000..69c54fe3f
--- /dev/null
+++ b/src/external/libunistring.m4
@@ -0,0 +1,9 @@
+AC_CHECK_HEADERS(unistr.h,
+ [AC_CHECK_LIB([unistring], [u8_strlen], [ UNISTRING_LIBS="-lunistring" ], [AC_MSG_ERROR([No usable libunistring library found])])],
+ [AC_MSG_ERROR([libunistring header files are not installed])]
+)
+
+AC_CHECK_HEADERS(unicase.h,
+ [AC_CHECK_LIB([unistring], [u8_casecmp], [ UNISTRING_LIBS="-lunistring" ], [AC_MSG_ERROR([No usable libunistring library found])])],
+ [AC_MSG_ERROR([libunistring header files are not installed])]
+) \ No newline at end of file
diff --git a/src/man/sssd-ipa.5.xml b/src/man/sssd-ipa.5.xml
index 4604c55e2..8d2ba6818 100644
--- a/src/man/sssd-ipa.5.xml
+++ b/src/man/sssd-ipa.5.xml
@@ -175,6 +175,48 @@
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>ipa_hbac_refresh (integer)</term>
+ <listitem>
+ <para>
+ The amount of time between lookups of the HBAC
+ rules against the IPA server. This will reduce the
+ latency and load on the IPA server if there are
+ many access-control requests made in a short
+ period.
+ </para>
+ <para>
+ Default: 5 (seconds)
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ipa_hbac_treat_deny_as (string)</term>
+ <listitem>
+ <para>
+ This option specifies how to treat the deprecated
+ DENY-type HBAC rules. As of FreeIPA v2.1, DENY
+ rules are no longer supported on the server. All
+ users of FreeIPA will need to migrate their rules
+ to use only the ALLOW rules. The client will
+ support two modes of operation during this
+ transition period:
+ </para>
+ <para>
+ <emphasis>DENY_ALL</emphasis>: If any HBAC DENY
+ rules are detected, all users will be denied
+ access.
+ </para>
+ <para>
+ <emphasis>IGNORE</emphasis>: SSSD will ignore any
+ DENY rules. Be very careful with this option, as
+ it may result in opening unintended access.
+ </para>
+ <para>
+ Default: DENY_ALL
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
diff --git a/src/providers/ipa/hbac_evaluator.c b/src/providers/ipa/hbac_evaluator.c
new file mode 100644
index 000000000..476ad6482
--- /dev/null
+++ b/src/providers/ipa/hbac_evaluator.c
@@ -0,0 +1,356 @@
+/*
+ SSSD
+
+ IPA Backend Module -- Access control
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2011 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistr.h>
+#include <unicase.h>
+#include <errno.h>
+#include "providers/ipa/ipa_hbac.h"
+
+#ifndef HAVE_ERRNO_T
+#define HAVE_ERRNO_T
+typedef int errno_t;
+#endif
+
+#ifndef EOK
+#define EOK 0
+#endif
+
+/* Placeholder structure for future HBAC time-based
+ * evaluation rules
+ */
+struct hbac_time_rules {
+ int not_yet_implemented;
+};
+
+enum hbac_eval_result_int {
+ HBAC_EVAL_MATCH_ERROR = -1,
+ HBAC_EVAL_MATCHED,
+ HBAC_EVAL_UNMATCHED
+};
+
+static bool hbac_rule_element_is_complete(struct hbac_rule_element *el)
+{
+ if (el == NULL) return false;
+ if (el->category == HBAC_CATEGORY_ALL) return true;
+
+ if (el->names == NULL && el->groups == NULL) return false;
+
+ if ((el->names && el->names[0] != NULL)
+ || (el->groups && el->groups[0] != NULL))
+ return true;
+
+ /* If other categories are added, handle them here */
+
+ return false;
+}
+
+bool hbac_rule_is_complete(struct hbac_rule *rule, uint32_t *missing_attrs)
+{
+ bool complete = true;
+
+ *missing_attrs = 0;
+
+ if (rule == NULL) {
+ /* No rule passed in? */
+ return false;
+ }
+
+ /* Make sure we have all elements */
+ if (!hbac_rule_element_is_complete(rule->users)) {
+ complete = false;
+ *missing_attrs |= HBAC_RULE_ELEMENT_USERS;
+ }
+
+ if (!hbac_rule_element_is_complete(rule->services)) {
+ complete = false;
+ *missing_attrs |= HBAC_RULE_ELEMENT_SERVICES;
+ }
+
+ if (!hbac_rule_element_is_complete(rule->targethosts)) {
+ complete = false;
+ *missing_attrs |= HBAC_RULE_ELEMENT_TARGETHOSTS;
+ }
+
+ if (!hbac_rule_element_is_complete(rule->srchosts)) {
+ complete = false;
+ *missing_attrs |= HBAC_RULE_ELEMENT_SOURCEHOSTS;
+ }
+
+ return complete;
+}
+
+enum hbac_eval_result_int hbac_evaluate_rule(struct hbac_rule *rule,
+ struct hbac_eval_req *hbac_req,
+ enum hbac_error_code *error);
+
+enum hbac_eval_result hbac_evaluate(struct hbac_rule **rules,
+ struct hbac_eval_req *hbac_req,
+ struct hbac_info **info)
+{
+ enum hbac_error_code ret;
+ enum hbac_eval_result result = HBAC_EVAL_DENY;
+ enum hbac_eval_result_int intermediate_result;
+
+ if (info) {
+ *info = malloc(sizeof(struct hbac_info));
+ if (!*info) {
+ return HBAC_EVAL_OOM;
+ }
+ (*info)->code = HBAC_ERROR_UNKNOWN;
+ (*info)->rule_name = NULL;
+ }
+ uint32_t i;
+
+ for (i = 0; rules[i]; i++) {
+ intermediate_result = hbac_evaluate_rule(rules[i], hbac_req, &ret);
+ if (intermediate_result == HBAC_EVAL_UNMATCHED) {
+ /* This rule did not match at all. Skip it */
+ continue;
+ } else if (intermediate_result == HBAC_EVAL_MATCHED) {
+ /* This request matched an ALLOW rule
+ * Set the result to ALLOW but continue checking
+ * the other rules in case a DENY rule trumps it.
+ */
+ result = HBAC_EVAL_ALLOW;
+ if (info) {
+ (*info)->code = HBAC_SUCCESS;
+ (*info)->rule_name = strdup(rules[i]->name);
+ if (!(*info)->rule_name) {
+ result = HBAC_EVAL_ERROR;
+ (*info)->code = HBAC_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ break;
+ } else {
+ /* An error occurred processing this rule */
+ result = HBAC_EVAL_ERROR;
+ (*info)->code = ret;
+ (*info)->rule_name = strdup(rules[i]->name);
+ /* Explicitly not checking the result of strdup(), since if
+ * it's NULL, we can't do anything anyway.
+ */
+ goto done;
+ }
+ }
+
+ /* If we've reached the end of the loop, we have either set the
+ * result to ALLOW explicitly or we'll stick with the default DENY.
+ */
+done:
+
+ return result;
+}
+
+static errno_t hbac_evaluate_element(struct hbac_rule_element *rule_el,
+ struct hbac_request_element *req_el,
+ bool *matched);
+
+enum hbac_eval_result_int hbac_evaluate_rule(struct hbac_rule *rule,
+ struct hbac_eval_req *hbac_req,
+ enum hbac_error_code *error)
+{
+ errno_t ret;
+ bool matched;
+
+ if (!rule->enabled) return HBAC_EVAL_UNMATCHED;
+
+ /* Make sure we have all elements */
+ if (!rule->users
+ || !rule->services
+ || !rule->targethosts
+ || !rule->srchosts) {
+ *error = HBAC_ERROR_UNPARSEABLE_RULE;
+ return HBAC_EVAL_MATCH_ERROR;
+ }
+
+ /* Check users */
+ ret = hbac_evaluate_element(rule->users,
+ hbac_req->user,
+ &matched);
+ if (ret != EOK) {
+ *error = HBAC_ERROR_UNPARSEABLE_RULE;
+ return HBAC_EVAL_MATCH_ERROR;
+ } else if (!matched) {
+ return HBAC_EVAL_UNMATCHED;
+ }
+
+ /* Check services */
+ ret = hbac_evaluate_element(rule->services,
+ hbac_req->service,
+ &matched);
+ if (ret != EOK) {
+ *error = HBAC_ERROR_UNPARSEABLE_RULE;
+ return HBAC_EVAL_MATCH_ERROR;
+ } else if (!matched) {
+ return HBAC_EVAL_UNMATCHED;
+ }
+
+ /* Check target hosts */
+ ret = hbac_evaluate_element(rule->targethosts,
+ hbac_req->targethost,
+ &matched);
+ if (ret != EOK) {
+ *error = HBAC_ERROR_UNPARSEABLE_RULE;
+ return HBAC_EVAL_MATCH_ERROR;
+ } else if (!matched) {
+ return HBAC_EVAL_UNMATCHED;
+ }
+
+ /* Check source hosts */
+ ret = hbac_evaluate_element(rule->srchosts,
+ hbac_req->srchost,
+ &matched);
+ if (ret != EOK) {
+ *error = HBAC_ERROR_UNPARSEABLE_RULE;
+ return HBAC_EVAL_MATCH_ERROR;
+ } else if (!matched) {
+ return HBAC_EVAL_UNMATCHED;
+ }
+ return HBAC_EVAL_MATCHED;
+}
+
+static errno_t hbac_evaluate_element(struct hbac_rule_element *rule_el,
+ struct hbac_request_element *req_el,
+ bool *matched)
+{
+ size_t i, j;
+ const uint8_t *rule_name;
+ const uint8_t *req_name;
+ int result;
+ int ret;
+
+ if (rule_el->category & HBAC_CATEGORY_ALL) {
+ *matched = true;
+ return EOK;
+ }
+
+ /* First check the name list */
+ if (rule_el->names) {
+ for (i = 0; rule_el->names[i]; i++) {
+ if (req_el->name != NULL) {
+ rule_name = (const uint8_t *) rule_el->names[i];
+ req_name = (const uint8_t *) req_el->name;
+
+ /* Do a case-insensitive comparison.
+ * The input must be encoded in UTF8.
+ * We have no way of knowing the language,
+ * so we'll pass NULL for the language and
+ * hope for the best.
+ */
+ errno = 0;
+ ret = u8_casecmp(rule_name, u8_strlen(rule_name),
+ req_name, u8_strlen(req_name),
+ NULL, NULL, &result);
+ if (ret < 0) {
+ return errno;
+ }
+
+ if (result == 0) {
+ *matched = true;
+ return EOK;
+ }
+ }
+ }
+ }
+
+ if (rule_el->groups) {
+ /* Not found in the name list
+ * Check for group membership
+ */
+ for (i = 0; rule_el->groups[i]; i++) {
+ rule_name = (const uint8_t *) rule_el->groups[i];
+
+ for (j = 0; req_el->groups[j]; j++) {
+ req_name = (const uint8_t *) req_el->groups[j];
+
+ /* Do a case-insensitive comparison.
+ * The input must be encoded in UTF8.
+ * We have no way of knowing the language,
+ * so we'll pass NULL for the language and
+ * hope for the best.
+ */
+ errno = 0;
+ ret = u8_casecmp(rule_name, u8_strlen(rule_name),
+ req_name, u8_strlen(req_name),
+ NULL, NULL, &result);
+ if (ret < 0) {
+ return errno;
+ }
+
+ if (result == 0) {
+ *matched = true;
+ return EOK;
+ }
+ }
+ }
+ }
+
+ /* Not found in groups either */
+ *matched = false;
+ return EOK;
+}
+
+const char *hbac_result_string(enum hbac_eval_result result)
+{
+ switch(result) {
+ case HBAC_EVAL_ALLOW:
+ return "HBAC_EVAL_ALLOW";
+ case HBAC_EVAL_DENY:
+ return "HBAC_EVAL_DENY";
+ case HBAC_EVAL_ERROR:
+ return "HBAC_EVAL_ERROR";
+ case HBAC_EVAL_OOM:
+ return "Could not allocate memory for hbac_info object";
+ }
+ return "HBAC_EVAL_ERROR";
+}
+
+void hbac_free_info(struct hbac_info *info)
+{
+ if (info == NULL) return;
+
+ free(info->rule_name);
+ free(info);
+ info = NULL;
+}
+
+const char *hbac_error_string(enum hbac_error_code code)
+{
+ switch(code) {
+ case HBAC_SUCCESS:
+ return "Success";
+ case HBAC_ERROR_NOT_IMPLEMENTED:
+ return "Function is not yet implemented";
+ case HBAC_ERROR_OUT_OF_MEMORY:
+ return "Out of memory";
+ case HBAC_ERROR_UNPARSEABLE_RULE:
+ return "Rule could not be evaluated";
+ case HBAC_ERROR_UNKNOWN:
+ default:
+ return "Unknown error code";
+ }
+}
diff --git a/src/providers/ipa/ipa_access.c b/src/providers/ipa/ipa_access.c
index f07eb7b5b..3bd377320 100644
--- a/src/providers/ipa/ipa_access.c
+++ b/src/providers/ipa/ipa_access.c
@@ -29,36 +29,8 @@
#include "providers/ldap/sdap_async.h"
#include "providers/ipa/ipa_common.h"
#include "providers/ipa/ipa_access.h"
-
-#define OBJECTCLASS "objectclass"
-#define IPA_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_UNIQUE_ID "ipauniqueid"
-#define IPA_ENABLED_FLAG "ipaenabledflag"
-#define IPA_MEMBER_HOST "memberHost"
-#define IPA_HOST_CATEGORY "hostCategory"
-#define IPA_CN "cn"
-#define IPA_MEMBER_SERVICE "memberService"
-#define IPA_SERVICE_CATEGORY "serviceCategory"
-#define IPA_TRUE_VALUE "TRUE"
-
-#define IPA_HOST_BASE_TMPL "cn=computers,cn=accounts,%s"
-#define IPA_HBAC_BASE_TMPL "cn=hbac,%s"
-#define IPA_SERVICES_BASE_TMPL "cn=hbacservices,cn=accounts,%s"
-
-#define SYSDB_HBAC_BASE_TMPL "cn=hbac,"SYSDB_TMPL_CUSTOM_BASE
-
-#define HBAC_RULES_SUBDIR "hbac_rules"
-#define HBAC_HOSTS_SUBDIR "hbac_hosts"
-#define HBAC_SERVICES_SUBDIR "hbac_services"
+#include "providers/ipa/ipa_hbac.h"
+#include "providers/ipa/ipa_hbac_private.h"
static char *get_hbac_search_base(TALLOC_CTX *mem_ctx,
struct dp_option *ipa_options)
@@ -84,247 +56,6 @@ static char *get_hbac_search_base(TALLOC_CTX *mem_ctx,
return base;
}
-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 errno_t replace_attribute_name(const char *old_name,
- const char *new_name, const size_t count,
- struct sysdb_attrs **list)
-{
- int ret;
- int i;
-
- for (i = 0; i < count; i++) {
- ret = sysdb_attrs_replace_name(list[i], old_name, new_name);
- if (ret != EOK) {
- DEBUG(1, ("sysdb_attrs_replace_name failed.\n"));
- return ret;
- }
- }
-
- return EOK;
-}
-
-static errno_t hbac_sdap_data_recv(struct tevent_req *subreq,
- TALLOC_CTX *mem_ctx, size_t *count,
- struct sysdb_attrs ***attrs)
-{
- int ret;
-
- ret = sdap_get_generic_recv(subreq, mem_ctx, count, attrs);
- if (ret != EOK) {
- DEBUG(1, ("sdap_get_generic_recv failed.\n"));
- return ret;
- }
-
- ret = replace_attribute_name(IPA_MEMBEROF, SYSDB_ORIG_MEMBEROF,
- *count, *attrs);
- if (ret != EOK) {
- DEBUG(1, ("replace_attribute_name failed.\n"));
- return ret;
- }
-
- return EOK;
-}
-
-static errno_t hbac_sysdb_data_recv(TALLOC_CTX *mem_ctx,
- struct sysdb_ctx *sysdb,
- struct sss_domain_info *domain,
- const char *filter,
- const char *subtree_name,
- const char **search_attrs,
- size_t *count,
- struct sysdb_attrs ***reply_attrs)
-{
- int ret;
- struct ldb_message **msgs;
-
- ret = sysdb_search_custom(mem_ctx, sysdb, domain, filter, subtree_name,
- search_attrs, count, &msgs);
- if (ret != EOK) {
- if (ret == ENOENT) {
- *count = 0;
- *reply_attrs = NULL;
- return EOK;
- }
- DEBUG(1, ("sysdb_search_custom failed.\n"));
- return ret;
- }
-
- ret = msgs2attrs_array(mem_ctx, *count, msgs, reply_attrs);
- talloc_zfree(msgs);
- if (ret != EOK) {
- DEBUG(1, ("msgs2attrs_array failed.\n"));
- return ret;
- }
-
- return EOK;
-}
-
-static errno_t set_local_and_remote_host_info(TALLOC_CTX *mem_ctx,
- size_t host_count,
- struct sysdb_attrs **host_list,
- const char *local_hostname,
- const char *remote_hostname,
- struct hbac_host_info **local_hhi,
- struct hbac_host_info **remote_hhi)
-
-{
- size_t c;
- int ret;
- struct hbac_host_info *hhi;
- struct ldb_message_element *el;
- TALLOC_CTX *tmp_ctx = NULL;
-
- if (local_hostname == NULL || *local_hostname == '\0') {
- DEBUG(1, ("Missing local hostname.\n"));
- ret = EINVAL;
- goto done;
- }
-
- if (host_count == 0) {
- DEBUG(1, ("No host data available.\n"));
- ret = EINVAL;
- goto done;
- }
-
- tmp_ctx = talloc_new(mem_ctx);
- if (tmp_ctx == NULL) {
- ret = ENOMEM;
- goto done;
- }
-
- for (c = 0; c < host_count; c++) {
- hhi = talloc_zero(tmp_ctx, struct hbac_host_info);
- if (hhi == NULL) {
- DEBUG(1, ("talloc_zero failed.\n"));
- ret = ENOMEM;
- goto done;
- }
-
- ret = sysdb_attrs_get_el(host_list[c], SYSDB_ORIG_DN, &el);
- if (ret != EOK) {
- DEBUG(1, ("sysdb_attrs_get_el failed.\n"));
- goto done;
- }
- if (el->num_values == 0) {
- DEBUG(1, ("Missing OriginalDN.\n"));
- ret = EINVAL;
- goto done;
- }
- DEBUG(9, ("OriginalDN: [%.*s].\n", el->values[0].length,
- (char *)el->values[0].data));
- hhi->dn = talloc_strndup(hhi, (char *)el->values[0].data,
- el->values[0].length);
- if (hhi->dn == NULL) {
- DEBUG(1, ("talloc_strndup failed.\n"));
- ret = ENOMEM;
- goto done;
- }
-
- ret = sysdb_attrs_get_el(host_list[c], IPA_HOST_SERVERHOSTNAME, &el);
- if (ret != EOK) {
- DEBUG(1, ("sysdb_attrs_get_el failed.\n"));
- goto done;
- }
- if (el->num_values == 0) {
- DEBUG(1, ("Missing ServerHostName.\n"));
- ret = EINVAL;
- goto done;
- }
- DEBUG(9, ("ServerHostName: [%.*s].\n", el->values[0].length,
- (char *)el->values[0].data));
- hhi->serverhostname = talloc_strndup(hhi, (char *)el->values[0].data,
- el->values[0].length);
- if (hhi->serverhostname == NULL) {
- DEBUG(1, ("talloc_strndup failed.\n"));
- ret = ENOMEM;
- goto done;
- }
-
- ret = sysdb_attrs_get_el(host_list[c], IPA_HOST_FQDN, &el);
- if (ret != EOK) {
- DEBUG(1, ("sysdb_attrs_get_el failed.\n"));
- goto done;
- }
- if (el->num_values == 0) {
- DEBUG(1, ("Missing FQDN.\n"));
- ret = EINVAL;
- goto done;
- }
- DEBUG(9, ("FQDN: [%.*s].\n", el->values[0].length,
- (char *)el->values[0].data));
- hhi->fqdn = talloc_strndup(hhi, (char *)el->values[0].data,
- el->values[0].length);
- if (hhi->fqdn == NULL) {
- DEBUG(1, ("talloc_strndup failed.\n"));
- ret = ENOMEM;
- goto done;
- }
-
- ret = sysdb_attrs_get_string_array(host_list[c], SYSDB_ORIG_MEMBEROF,
- hhi, &hhi->memberof);
- if (ret != EOK) {
- if (ret != ENOENT) {
- DEBUG(1, ("sysdb_attrs_get_string_array failed.\n"));
- goto done;
- }
-
- hhi->memberof = talloc_array(hhi, const char *, 1);
- if (hhi->memberof == NULL) {
- DEBUG(1, ("talloc_array failed.\n"));
- ret = ENOMEM;
- goto done;
- }
- hhi->memberof[0] = NULL;
- }
-
- if (strcmp(hhi->fqdn, local_hostname) == 0 ||
- strcmp(hhi->serverhostname, local_hostname) == 0) {
- *local_hhi = talloc_steal(mem_ctx, hhi);
- }
-
- if (remote_hostname != NULL && *remote_hostname != '\0') {
- if (strcmp(hhi->fqdn, remote_hostname) == 0 ||
- strcmp(hhi->serverhostname, remote_hostname) == 0) {
- *remote_hhi = talloc_steal(mem_ctx, hhi);
- }
- }
- }
-
- ret = EOK;
-
-done:
- talloc_free(tmp_ctx);
- return ret;
-}
-
static void ipa_access_reply(struct hbac_ctx *hbac_ctx, int pam_status)
{
struct be_req *be_req = hbac_ctx->be_req;
@@ -342,873 +73,6 @@ static void ipa_access_reply(struct hbac_ctx *hbac_ctx, int pam_status)
}
}
-static errno_t hbac_save_list(struct sysdb_ctx *sysdb, bool delete_subdir,
- const char *subdir, struct sss_domain_info *domain,
- const char *naming_attribute, size_t count,
- struct sysdb_attrs **list)
-{
- int ret;
- size_t c;
- struct ldb_dn *base_dn;
- const char *object_name;
- struct ldb_message_element *el;
- TALLOC_CTX *tmp_ctx;
-
- tmp_ctx = talloc_new(NULL);
- if (tmp_ctx == NULL) {
- DEBUG(1, ("talloc_new failed.\n"));
- return ENOMEM;
- }
-
- if (delete_subdir) {
- base_dn = sysdb_custom_subtree_dn(sysdb, tmp_ctx, domain->name, subdir);
- if (base_dn == NULL) {
- ret = ENOMEM;
- goto done;
- }
-
- ret = sysdb_delete_recursive(tmp_ctx, sysdb, base_dn, true);
- if (ret != EOK) {
- DEBUG(1, ("sysdb_delete_recursive failed.\n"));
- goto done;
- }
- }
-
- for (c = 0; c < count; c++) {
- ret = sysdb_attrs_get_el(list[c], naming_attribute, &el);
- if (ret != EOK) {
- DEBUG(1, ("sysdb_attrs_get_el failed.\n"));
- goto done;
- }
- if (el->num_values == 0) {
- DEBUG(1, ("IPA_UNIQUE_ID not found.\n"));
- ret = EINVAL;
- goto done;
- }
- object_name = talloc_strndup(tmp_ctx, (const char *)el->values[0].data,
- el->values[0].length);
- if (object_name == NULL) {
- DEBUG(1, ("talloc_strndup failed.\n"));
- ret = ENOMEM;
- goto done;
- }
- DEBUG(9, ("Object name: [%s].\n", object_name));
-
- ret = sysdb_store_custom(tmp_ctx, sysdb, domain, object_name, subdir,
- list[c]);
- if (ret != EOK) {
- DEBUG(1, ("sysdb_store_custom failed.\n"));
- goto done;
- }
- }
-
- ret = EOK;
-
-done:
- talloc_free(tmp_ctx);
- return ret;
-}
-
-static errno_t hbac_save_data_to_sysdb(struct hbac_ctx *hbac_ctx)
-{
- int ret;
- bool in_transaction = false;
- struct sysdb_ctx *sysdb = hbac_ctx_sysdb(hbac_ctx);
- struct sss_domain_info *domain = hbac_ctx_be(hbac_ctx)->domain;
-
- ret = sysdb_transaction_start(sysdb);
- if (ret != EOK) {
- DEBUG(1, ("sysdb_transaction_start failed.\n"));
- return ret;
- }
- in_transaction = true;
-
- ret = hbac_save_list(sysdb, true, HBAC_SERVICES_SUBDIR, domain,
- IPA_UNIQUE_ID, hbac_ctx->hbac_services_count,
- hbac_ctx->hbac_services_list);
- if (ret != EOK) {
- DEBUG(1, ("hbac_save_list failed.\n"));
- goto done;
- }
-
- ret = hbac_save_list(sysdb, true, HBAC_RULES_SUBDIR, domain,
- IPA_UNIQUE_ID, hbac_ctx->hbac_rule_count,
- hbac_ctx->hbac_rule_list);
- if (ret != EOK) {
- DEBUG(1, ("hbac_save_list failed.\n"));
- goto done;
- }
-
- ret = hbac_save_list(sysdb, false, HBAC_HOSTS_SUBDIR, domain,
- IPA_HOST_FQDN, hbac_ctx->hbac_hosts_count,
- hbac_ctx->hbac_hosts_list);
- if (ret != EOK) {
- DEBUG(1, ("hbac_save_list failed.\n"));
- goto done;
- }
-
- ret = sysdb_transaction_commit(sysdb);
- if (ret != EOK) {
- DEBUG(1, ("sysdb_transaction_commit failed.\n"));
- goto done;
- }
- in_transaction = false;
-
- ret = EOK;
-
-done:
- if (in_transaction) {
- sysdb_transaction_cancel(sysdb);
- }
- return ret;
-}
-
-struct hbac_get_service_data_state {
- struct hbac_ctx *hbac_ctx;
- bool offline;
-
- char *services_filter;
- const char **services_attrs;
- struct sysdb_attrs **services_reply_list;
- size_t services_reply_count;
-
- size_t current_item;
-};
-
-static void hbac_services_get_done(struct tevent_req *subreq);
-
-struct tevent_req *hbac_get_service_data_send(TALLOC_CTX *memctx,
- struct hbac_ctx *hbac_ctx)
-{
- struct tevent_req *req = NULL;
- struct tevent_req *subreq = NULL;
- struct hbac_get_service_data_state *state;
- struct sdap_handle *sdap_handle;
- int ret;
-
- req = tevent_req_create(memctx, &state, struct hbac_get_service_data_state);
- if (req == NULL) {
- DEBUG(1, ("tevent_req_create failed.\n"));
- return NULL;
- }
-
- state->hbac_ctx = hbac_ctx;
-
- state->services_reply_list = NULL;
- state->services_reply_count = 0;
-
- state->current_item = 0;
-
- state->services_attrs = talloc_array(state, const char *, 7);
- if (state->services_attrs == NULL) {
- DEBUG(1, ("Failed to allocate service attribute list.\n"));
- ret = ENOMEM;
- goto fail;
- }
- state->services_attrs[0] = IPA_CN;
- state->services_attrs[1] = SYSDB_ORIG_DN;
- state->services_attrs[2] = IPA_UNIQUE_ID;
- state->services_attrs[3] = IPA_MEMBEROF;
- state->services_attrs[4] = SYSDB_ORIG_MEMBEROF;
- state->services_attrs[5] = OBJECTCLASS;
- state->services_attrs[6] = NULL;
-
- state->services_filter = talloc_asprintf(state,
- "(objectclass=ipaHBACService)");
- if (state->services_filter == NULL) {
- ret = ENOMEM;
- goto fail;
- }
-
- DEBUG(9, ("Services filter: [%s].\n", state->services_filter));
-
- if (hbac_ctx_is_offline(state->hbac_ctx)) {
- ret = hbac_sysdb_data_recv(state,
- hbac_ctx_sysdb(state->hbac_ctx),
- hbac_ctx_be(state->hbac_ctx)->domain,
- state->services_filter, HBAC_SERVICES_SUBDIR,
- state->services_attrs,
- &state->services_reply_count,
- &state->services_reply_list);
- if (ret) {
- DEBUG(1, ("hbac_sysdb_data_recv failed.\n"));
- goto fail;
- }
-
- tevent_req_done(req);
- tevent_req_post(req, hbac_ctx_ev(state->hbac_ctx));
- return req;
- }
-
- sdap_handle = sdap_id_op_handle(hbac_ctx_sdap_id_op(state->hbac_ctx));
- if (sdap_handle == NULL) {
- DEBUG(1, ("Bug: sdap_id_op is disconnected.\n"));
- ret = EIO;
- goto fail;
- }
- subreq = sdap_get_generic_send(state,
- hbac_ctx_ev(state->hbac_ctx),
- hbac_ctx_sdap_id_ctx(state->hbac_ctx)->opts,
- sdap_handle,
- state->hbac_ctx->hbac_search_base,
- LDAP_SCOPE_SUB,
- state->services_filter,
- state->services_attrs,
- NULL, 0,
- dp_opt_get_int(
- hbac_ctx_sdap_id_ctx(state->hbac_ctx)->opts->basic,
- SDAP_ENUM_SEARCH_TIMEOUT));
-
- if (subreq == NULL) {
- DEBUG(1, ("sdap_get_generic_send failed.\n"));
- ret = ENOMEM;
- goto fail;
- }
-
- tevent_req_set_callback(subreq, hbac_services_get_done, req);
-
- return req;
-
-fail:
- tevent_req_error(req, ret);
- tevent_req_post(req, hbac_ctx_ev(state->hbac_ctx));
- return req;
-}
-
-static void hbac_services_get_done(struct tevent_req *subreq)
-{
- struct tevent_req *req = tevent_req_callback_data(subreq,
- struct tevent_req);
- struct hbac_get_service_data_state *state = tevent_req_data(req,
- struct hbac_get_service_data_state);
- int ret;
-
- ret = hbac_sdap_data_recv(subreq, state, &state->services_reply_count,
- &state->services_reply_list);
- talloc_zfree(subreq);
- if (ret != EOK) {
- tevent_req_error(req, ret);
- return;
- }
-
- tevent_req_done(req);
- return;
-}
-
-static int hbac_get_service_data_recv(struct tevent_req *req,
- TALLOC_CTX *memctx,
- size_t *hbac_services_count,
- struct sysdb_attrs ***hbac_services_list)
-{
- struct hbac_get_service_data_state *state = tevent_req_data(req,
- struct hbac_get_service_data_state);
- int i;
-
- TEVENT_REQ_RETURN_ON_ERROR(req);
-
- *hbac_services_count = state->services_reply_count;
- *hbac_services_list = talloc_steal(memctx, state->services_reply_list);
- for (i = 0; i < state->services_reply_count; i++) {
- talloc_steal(memctx, state->services_reply_list[i]);
- }
-
- return EOK;
-}
-
-static int hbac_get_user_info(TALLOC_CTX *memctx,
- struct be_ctx *be_ctx,
- const char *user,
- const char **user_dn,
- size_t *groups_count,
- const char ***_groups)
-{
- TALLOC_CTX *tmpctx;
- const char *attrs[] = { SYSDB_ORIG_DN, NULL };
- struct ldb_message *user_msg;
- const char *user_orig_dn;
- struct ldb_message **msgs;
- size_t count;
- const char **groups;
- int ret;
- int i;
-
- tmpctx = talloc_new(memctx);
- if (!tmpctx) {
- return ENOMEM;
- }
-
- ret = sysdb_search_user_by_name(tmpctx, be_ctx->sysdb,
- be_ctx->domain, user, attrs, &user_msg);
- if (ret != EOK) {
- goto fail;
- }
-
- DEBUG(9, ("Found user info for user [%s].\n", user));
- user_orig_dn = ldb_msg_find_attr_as_string(user_msg, SYSDB_ORIG_DN, NULL);
- if (user_orig_dn == NULL) {
- DEBUG(1, ("Original DN of user [%s] not available.\n", user));
- ret = EINVAL;
- goto fail;
- }
- DEBUG(9, ("Found original DN [%s] for user [%s].\n",
- user_orig_dn, user));
-
- ret = sysdb_asq_search(tmpctx, be_ctx->sysdb, be_ctx->domain,
- user_msg->dn, NULL, SYSDB_MEMBEROF, attrs,
- &count, &msgs);
- if (ret != EOK) {
- DEBUG(1, ("sysdb_asq_search on user %s failed.\n", user));
- goto fail;
- }
-
- if (count == 0) {
- *user_dn = talloc_strdup(memctx, user_orig_dn);
- if (*user_dn == NULL) {
- ret = ENOMEM;
- goto fail;
- }
- *groups_count = 0;
- *_groups = NULL;
- talloc_zfree(tmpctx);
- return EOK;
- }
-
- groups = talloc_array(tmpctx, const char *, count);
- if (groups == NULL) {
- DEBUG(1, ("talloc_groups failed.\n"));
- ret = ENOMEM;
- goto fail;
- }
-
- for(i = 0; i < count; i++) {
- if (msgs[i]->num_elements != 1) {
- DEBUG(1, ("Unexpected number of elements.\n"));
- ret = EINVAL;
- goto fail;
- }
-
- if (msgs[i]->elements[0].num_values != 1) {
- DEBUG(1, ("Unexpected number of values.\n"));
- ret = EINVAL;
- goto fail;
- }
-
- groups[i] = talloc_strndup(groups,
- (const char *)msgs[i]->elements[0].values[0].data,
- msgs[i]->elements[0].values[0].length);
- if (groups[i] == NULL) {
- DEBUG(1, ("talloc_strndup failed.\n"));
- ret = ENOMEM;
- goto fail;
- }
-
- DEBUG(9, ("Found group [%s].\n", groups[i]));
- }
-
- *user_dn = talloc_strdup(memctx, user_orig_dn);
- if (*user_dn == NULL) {
- ret = ENOMEM;
- goto fail;
- }
- *groups_count = count;
- *_groups = talloc_steal(memctx, groups);
-
- talloc_zfree(tmpctx);
- return EOK;
-
-fail:
- DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret)));
- talloc_zfree(tmpctx);
- return ret;
-}
-
-
-struct hbac_get_host_info_state {
- struct hbac_ctx *hbac_ctx;
-
- char *host_filter;
- 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_memberof(struct tevent_req *req, bool offline);
-static void hbac_get_host_memberof_done(struct tevent_req *subreq);
-
-static struct tevent_req *hbac_get_host_info_send(TALLOC_CTX *memctx,
- struct hbac_ctx *hbac_ctx,
- const char **hostnames)
-{
- struct tevent_req *req = NULL;
- struct tevent_req *subreq = NULL;
- struct hbac_get_host_info_state *state;
- struct sdap_handle *sdap_handle;
- char *host;
- int ret;
- int i;
-
- if (hostnames == NULL) {
- DEBUG(1, ("Missing hostnames.\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->hbac_ctx = hbac_ctx;
-
- 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, "(&(objectclass=ipaHost)(|");
- if (state->host_filter == NULL) {
- DEBUG(1, ("Failed to create filter.\n"));
- ret = ENOMEM;
- goto fail;
- }
- for (i = 0; hostnames[i] != NULL; i++) {
- ret = sss_filter_sanitize(state->host_filter, hostnames[i], &host);
- if (ret != EOK) {
- goto fail;
- }
-
- state->host_filter = talloc_asprintf_append(state->host_filter,
- "(%s=%s)(%s=%s)",
- IPA_HOST_FQDN, host,
- IPA_HOST_SERVERHOSTNAME, host);
-
- if (state->host_filter == NULL) {
- ret = ENOMEM;
- goto fail;
- }
- talloc_zfree(host);
- }
- state->host_filter = talloc_asprintf_append(state->host_filter, "))");
- if (state->host_filter == NULL) {
- ret = ENOMEM;
- goto fail;
- }
-
- state->host_attrs = talloc_array(state, const char *, 8);
- if (state->host_attrs == NULL) {
- DEBUG(1, ("Failed to allocate host attribute list.\n"));
- ret = ENOMEM;
- goto fail;
- }
- state->host_attrs[0] = IPA_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] = IPA_UNIQUE_ID;
- state->host_attrs[7] = NULL;
-
- if (hbac_ctx_is_offline(state->hbac_ctx)) {
- ret = hbac_sysdb_data_recv(state, hbac_ctx_sysdb(state->hbac_ctx),
- hbac_ctx_be(state->hbac_ctx)->domain,
- state->host_filter, HBAC_HOSTS_SUBDIR,
- state->host_attrs,
- &state->host_reply_count,
- &state->host_reply_list);
- if (ret != EOK) {
- DEBUG(1, ("hbac_sysdb_data_recv failed.\n"));
- goto fail;
- }
- hbac_get_host_memberof(req, true);
- tevent_req_post(req, hbac_ctx_ev(state->hbac_ctx));
- return req;
- }
-
- sdap_handle = sdap_id_op_handle(hbac_ctx_sdap_id_op(state->hbac_ctx));
- if (sdap_handle == NULL) {
- DEBUG(1, ("Bug: sdap_id_op is disconnected.\n"));
- ret = EIO;
- goto fail;
- }
- subreq = sdap_get_generic_send(state, hbac_ctx_ev(state->hbac_ctx),
- hbac_ctx_sdap_id_ctx(state->hbac_ctx)->opts,
- sdap_handle,
- state->hbac_ctx->hbac_search_base,
- LDAP_SCOPE_SUB,
- state->host_filter,
- state->host_attrs,
- NULL, 0,
- dp_opt_get_int(
- hbac_ctx_sdap_id_ctx(state->hbac_ctx)->opts->basic,
- SDAP_ENUM_SEARCH_TIMEOUT));
-
- 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, hbac_ctx_ev(state->hbac_ctx));
- return req;
-}
-
-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;
-
- ret = hbac_sdap_data_recv(subreq, state, &state->host_reply_count,
- &state->host_reply_list);
- talloc_zfree(subreq);
- if (ret != EOK) {
- tevent_req_error(req, ret);
- return;
- }
-
- hbac_get_host_memberof(req, false);
-}
-
-static bool hbac_is_known_host(size_t host_reply_count,
- struct sysdb_attrs **host_reply_list,
- const char *fqdn)
-{
- int i;
- const char *new_fqdn;
- int ret;
-
- if (!host_reply_list || !fqdn) {
- return false;
- }
-
- for (i = 0; i < host_reply_count; i++) {
- ret = sysdb_attrs_get_string(host_reply_list[i], IPA_HOST_FQDN,
- &new_fqdn);
- if (ret != 0) {
- DEBUG(1, ("missing FQDN in new HBAC host record\n"));
- continue;
- }
-
- if(strcmp(new_fqdn, fqdn) == 0) {
- return true;
- }
- }
-
- return false;
-}
-
-static void hbac_get_host_memberof(struct tevent_req *req, bool offline)
-{
- struct hbac_get_host_info_state *state =
- tevent_req_data(req, struct hbac_get_host_info_state);
- struct sysdb_ctx *sysdb;
- struct sss_domain_info *domain;
- bool in_transaction = false;
- int ret;
- int i;
- const char *fqdn_attrs[] = { IPA_HOST_FQDN, NULL };
- const char *fqdn;
-
- size_t cached_count;
- struct ldb_message **cached_entries = 0;
-
- if (offline) {
- tevent_req_done(req);
- return;
- }
-
- sysdb = hbac_ctx_sysdb(state->hbac_ctx);
- domain = hbac_ctx_be(state->hbac_ctx)->domain;
-
- ret = sysdb_transaction_start(sysdb);
- if (ret != EOK) {
- tevent_req_error(req, ret);
- return;
- }
- in_transaction = true;
-
- ret = sysdb_search_custom(state, sysdb, domain,
- state->host_filter, HBAC_HOSTS_SUBDIR,
- fqdn_attrs,
- &cached_count,
- &cached_entries);
-
- if (ret == ENOENT) {
- cached_count = 0;
- ret = EOK;
- }
-
- if (ret) {
- DEBUG(1, ("sysdb_search_custom failed: [%d](%s)\n", ret, strerror(ret)));
- goto fail;
- }
-
- for (i = 0; i < cached_count; i++) {
- fqdn = ldb_msg_find_attr_as_string(cached_entries[i], IPA_HOST_FQDN, NULL);
- if (!fqdn) {
- DEBUG(1, ("missing FQDN in cached HBAC host record\n"));
- } else if (hbac_is_known_host(state->host_reply_count,
- state->host_reply_list, fqdn)) {
- continue;
- } else {
- DEBUG(9, ("deleting obsolete HBAC host record for %s\n", fqdn));
- }
-
- ret = sysdb_delete_entry(sysdb, cached_entries[i]->dn, true);
- if (ret) {
- DEBUG(1, ("sysdb_delete_entry failed: [%d](%s)\n", ret, strerror(ret)));
- goto fail;
- }
- }
-
- talloc_zfree(cached_entries);
-
- ret = sysdb_transaction_commit(sysdb);
- if (ret) {
- DEBUG(1, ("sysdb_transaction_commit failed.\n"));
- goto fail;
- }
- in_transaction = false;
-
- tevent_req_done(req);
- return;
-
-fail:
- talloc_zfree(cached_entries);
-
- if (in_transaction) {
- sysdb_transaction_cancel(sysdb);
- }
- tevent_req_error(req, ret);
- return;
-}
-
-static int hbac_get_host_info_recv(struct tevent_req *req, TALLOC_CTX *memctx,
- size_t *hbac_hosts_count,
- struct sysdb_attrs ***hbac_hosts_list)
-{
- size_t c;
- struct hbac_get_host_info_state *state = tevent_req_data(req,
- struct hbac_get_host_info_state);
-
- TEVENT_REQ_RETURN_ON_ERROR(req);
-
- *hbac_hosts_count = state->host_reply_count;
- *hbac_hosts_list = talloc_steal(memctx, state->host_reply_list);
- for (c = 0; c < state->host_reply_count; c++) {
- talloc_steal(memctx, state->host_reply_list[c]);
- }
-
- return EOK;
-}
-
-
-struct hbac_get_rules_state {
- struct hbac_ctx *hbac_ctx;
-
- const char *host_dn;
- const char **memberof;
- char *hbac_filter;
- 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_rule_get_done(struct tevent_req *subreq);
-
-static struct tevent_req *hbac_get_rules_send(TALLOC_CTX *memctx,
- struct hbac_ctx *hbac_ctx,
- const char *host_dn,
- const char **memberof)
-{
- struct tevent_req *req = NULL;
- struct tevent_req *subreq = NULL;
- struct hbac_get_rules_state *state;
- struct sdap_handle *sdap_handle;
- char *host_dn_clean;
- int ret;
- int i;
-
- if (host_dn == NULL) {
- DEBUG(1, ("Missing host_dn.\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->hbac_ctx = hbac_ctx;
- 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_attrs = talloc_array(state, const char *, 17);
- 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_UNIQUE_ID;
- state->hbac_attrs[8] = IPA_ENABLED_FLAG;
- state->hbac_attrs[9] = IPA_CN;
- state->hbac_attrs[10] = OBJECTCLASS;
- state->hbac_attrs[11] = IPA_MEMBER_HOST;
- state->hbac_attrs[12] = IPA_HOST_CATEGORY;
- state->hbac_attrs[13] = IPA_MEMBER_SERVICE;
- state->hbac_attrs[14] = IPA_SERVICE_CATEGORY;
- state->hbac_attrs[15] = SYSDB_ORIG_DN;
- state->hbac_attrs[16] = NULL;
-
- ret = sss_filter_sanitize(state, host_dn, &host_dn_clean);
- if (ret != EOK) {
- goto fail;
- }
-
- state->hbac_filter = talloc_asprintf(state,
- "(&(objectclass=ipaHBACRule)"
- "(%s=%s)(|(%s=%s)(%s=%s)",
- IPA_ENABLED_FLAG, IPA_TRUE_VALUE,
- IPA_HOST_CATEGORY, "all",
- IPA_MEMBER_HOST, host_dn_clean);
- if (state->hbac_filter == NULL) {
- ret = ENOMEM;
- goto fail;
- }
- talloc_zfree(host_dn_clean);
-
- 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 (hbac_ctx_is_offline(state->hbac_ctx)) {
- ret = hbac_sysdb_data_recv(state, hbac_ctx_sysdb(state->hbac_ctx),
- hbac_ctx_be(state->hbac_ctx)->domain,
- state->hbac_filter, HBAC_RULES_SUBDIR,
- state->hbac_attrs,
- &state->hbac_reply_count,
- &state->hbac_reply_list);
- if (ret) {
- DEBUG(1, ("hbac_sysdb_data_recv failed.\n"));
- goto fail;
- }
- tevent_req_done(req);
- tevent_req_post(req, hbac_ctx_ev(state->hbac_ctx));
- return req;
- }
-
- sdap_handle = sdap_id_op_handle(hbac_ctx_sdap_id_op(state->hbac_ctx));
- if (sdap_handle == NULL) {
- DEBUG(1, ("Bug: sdap_id_op is disconnected.\n"));
- ret = EIO;
- goto fail;
- }
- subreq = sdap_get_generic_send(state, hbac_ctx_ev(state->hbac_ctx),
- hbac_ctx_sdap_id_ctx(state->hbac_ctx)->opts,
- sdap_handle,
- state->hbac_ctx->hbac_search_base,
- LDAP_SCOPE_SUB,
- state->hbac_filter,
- state->hbac_attrs,
- NULL, 0,
- dp_opt_get_int(
- hbac_ctx_sdap_id_ctx(state->hbac_ctx)->opts->basic,
- SDAP_ENUM_SEARCH_TIMEOUT));
-
- 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, hbac_ctx_ev(state->hbac_ctx));
- return req;
-}
-
-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;
-
- ret = hbac_sdap_data_recv(subreq, state, &state->hbac_reply_count,
- &state->hbac_reply_list);
- talloc_zfree(subreq);
- if (ret != EOK) {
- tevent_req_error(req, ret);
- return;
- }
-
- tevent_req_done(req);
- return;
-}
-
-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);
-
- TEVENT_REQ_RETURN_ON_ERROR(req);
-
- *hbac_rule_count = state->hbac_reply_count;
- *hbac_rule_list = talloc_steal(memctx, state->hbac_reply_list);
- /* we do not need to steal each hbac_reply_list[i]
- * as it belongs to hbac_reply_list memory block */
- return EOK;
-}
-
enum hbac_result {
HBAC_ALLOW = 1,
HBAC_DENY,
@@ -1221,442 +85,21 @@ enum check_result {
RULE_ERROR
};
-static errno_t get_service_data(const char *cn, size_t count,
- struct sysdb_attrs **list, const char **dn,
- struct ldb_message_element **mof)
-{
- int ret;
- int i;
- int j;
- struct ldb_message_element *el;
-
- for (i = 0; i < count; i++) {
- ret = sysdb_attrs_get_el(list[i], IPA_CN, &el);
- if (ret != EOK) {
- DEBUG(1, ("sysdb_attrs_get_el failed.\n"));
- return ENOENT;
- }
- if (el->num_values == 0) {
- DEBUG(9, ("No cn found.\n"));
- return ENOENT;
- } else {
- for (j = 0; j < el->num_values; j++) {
- if (strlen(cn) == el->values[j].length &&
- strncmp(cn, (const char *) el->values[j].data,
- el->values[j].length) == 0) {
-
- ret = sysdb_attrs_get_string(list[i], SYSDB_ORIG_DN, dn);
- if (ret != EOK) {
- DEBUG(1, ("sysdb_attrs_get_string failed.\n"));
- return ret;
- }
-
- ret = sysdb_attrs_get_el(list[i], SYSDB_ORIG_MEMBEROF, mof);
- if (ret != EOK) {
- DEBUG(1, ("sysdb_attrs_get_el failed.\n"));
- return ret;
- }
-
- return EOK;
- }
- }
- }
- }
-
- return ENOENT;
-}
-
-enum check_result check_service(struct hbac_ctx *hbac_ctx,
- struct sysdb_attrs *rule_attrs)
-{
- int ret;
- int i;
- int g;
- struct ldb_message_element *el;
- const char *service_dn;
- struct ldb_message_element *service_memberof;
-
- if (hbac_ctx->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_CATEGORY, &el);
- if (ret != EOK) {
- DEBUG(1, ("sysdb_attrs_get_el failed.\n"));
- return RULE_ERROR;
- }
- if (el->num_values == 0) {
- DEBUG(9, ("Service 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, ("Service category is set to 'all', rule applies.\n"));
- return RULE_APPLICABLE;
- }
- DEBUG(9, ("Unsupported service category [%.*s].\n",
- el->values[i].length,
- (char *) el->values[i].data));
- }
- }
-
- ret = get_service_data(hbac_ctx->pd->service, hbac_ctx->hbac_services_count,
- hbac_ctx->hbac_services_list, &service_dn,
- &service_memberof);
- if (ret != EOK) {
- DEBUG(1, ("Cannot find original DN for service [%s].\n",
- hbac_ctx->pd->service));
- return RULE_ERROR;
- }
- DEBUG(9, ("OriginalDN for service [%s]: [%s].\n", hbac_ctx->pd->service,
- service_dn));
-
- ret = sysdb_attrs_get_el(rule_attrs, IPA_MEMBER_SERVICE, &el);
- if (ret != EOK) {
- DEBUG(1, ("sysdb_attrs_get_el failed.\n"));
- return RULE_ERROR;
- }
- if (el->num_values == 0) {
- DEBUG(9, ("No service or service group specified, rule does not apply.\n"));
- return RULE_NOT_APPLICABLE;
- }
-
- for (i = 0; i < el->num_values; i++) {
- if (strncmp(service_dn, (const char *) el->values[i].data,
- el->values[i].length) == 0) {
- DEBUG(9, ("Service [%s] found in the list of allowed "
- "services.\n", hbac_ctx->pd->service));
- return RULE_APPLICABLE;
- }
-
- for (g = 0; g < service_memberof->num_values; g++) {
- if (service_memberof->values[g].length == el->values[i].length &&
- strncmp((const char *) service_memberof->values[g].data,
- (const char *) el->values[i].data,
- el->values[i].length) == 0) {
- DEBUG(9, ("Service [%s] is a member of a group in the list of "
- "allowed service groups.\n", hbac_ctx->pd->service));
- return RULE_APPLICABLE;
- }
- }
- }
-
- DEBUG(9, ("Service [%s] was not found in the list of allowed services and "
- "service groups.\n", hbac_ctx->pd->service));
- return RULE_NOT_APPLICABLE;
-}
-
-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_NOT_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(struct hbac_ctx *hbac_ctx,
- struct sysdb_attrs *rule_attrs,
- enum hbac_result *result) {
- 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));
-
- ret = sysdb_attrs_get_el(rule_attrs, IPA_ENABLED_FLAG, &el);
- if (ret != EOK) {
- DEBUG(1, ("Failed to find out if rule is enabled or not, "
- "assuming it is enabled.\n"));
- } else {
- if (el->num_values == 0) {
- DEBUG(1, ("Failed to find out if rule is enabled or not, "
- "assuming it is enabled.\n"));
- } else {
- if (strncasecmp("false", (const char*) el->values[0].data,
- el->values[0].length) == 0) {
- DEBUG(7, ("Rule is disabled.\n"));
- *result = HBAC_NOT_APPLICABLE;
- return EOK;
- }
- }
- }
-
- /* 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(hbac_ctx, 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->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(hbac_ctx, hbac_ctx->hbac_rule_list[i],
- &result);
- 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 int hbac_retry(struct hbac_ctx *hbac_ctx);
static void hbac_connect_done(struct tevent_req *subreq);
static bool hbac_check_step_result(struct hbac_ctx *hbac_ctx, int ret);
static int hbac_get_host_info_step(struct hbac_ctx *hbac_ctx);
-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_service_data_done(struct tevent_req *req);
+
+static void ipa_hbac_evaluate_rules(struct hbac_ctx *hbac_ctx);
void ipa_access_handler(struct be_req *be_req)
{
struct pam_data *pd;
struct hbac_ctx *hbac_ctx;
+ const char *deny_method;
int pam_status = PAM_SYSTEM_ERR;
struct ipa_access_ctx *ipa_access_ctx;
- bool offline;
int ret;
pd = talloc_get_type(be_req->req_data, struct pam_data);
@@ -1666,11 +109,13 @@ void ipa_access_handler(struct be_req *be_req)
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->access_ctx = 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;
@@ -1681,16 +126,12 @@ void ipa_access_handler(struct be_req *be_req)
goto fail;
}
- offline = be_is_offline(be_req->be_ctx);
- DEBUG(9, ("Connection status is [%s].\n", offline ? "offline" : "online"));
-
- if (!offline) {
- hbac_ctx->sdap_op = sdap_id_op_create(hbac_ctx,
- hbac_ctx_sdap_id_ctx(hbac_ctx)->conn_cache);
- if (!hbac_ctx->sdap_op) {
- DEBUG(1, ("sdap_id_op_create failed.\n"));
- goto fail;
- }
+ deny_method = dp_opt_get_string(hbac_ctx->ipa_options,
+ IPA_HBAC_DENY_METHOD);
+ if (strcasecmp(deny_method, "IGNORE") == 0) {
+ hbac_ctx->get_deny_rules = false;
+ } else {
+ hbac_ctx->get_deny_rules = true;
}
ret = hbac_retry(hbac_ctx);
@@ -1713,18 +154,48 @@ static int hbac_retry(struct hbac_ctx *hbac_ctx)
{
struct tevent_req *subreq;
int ret;
+ bool offline;
+ time_t now, refresh_interval;
+ struct ipa_access_ctx *access_ctx = hbac_ctx->access_ctx;
- if (hbac_ctx_is_offline(hbac_ctx)) {
- return hbac_get_host_info_step(hbac_ctx);
- }
+ offline = be_is_offline(hbac_ctx->be_req->be_ctx);
+ DEBUG(9, ("Connection status is [%s].\n", offline ? "offline" : "online"));
+
+ refresh_interval = dp_opt_get_int(hbac_ctx->ipa_options,
+ IPA_HBAC_REFRESH);
- subreq = sdap_id_op_connect_send(hbac_ctx_sdap_id_op(hbac_ctx), hbac_ctx, &ret);
- if (!subreq) {
- DEBUG(1, ("sdap_id_op_connect_send failed: %d(%s).\n", ret, strerror(ret)));
- return ret;
+ now = time(NULL);
+ if (now < access_ctx->last_update + refresh_interval) {
+ /* Simulate offline mode and just go to the cache */
+ DEBUG(6, ("Performing cached HBAC evaluation\n"));
+ offline = true;
}
- tevent_req_set_callback(subreq, hbac_connect_done, hbac_ctx);
+ if (!offline) {
+ if (hbac_ctx->sdap_op == NULL) {
+ hbac_ctx->sdap_op = sdap_id_op_create(hbac_ctx,
+ hbac_ctx_sdap_id_ctx(hbac_ctx)->conn_cache);
+ if (hbac_ctx->sdap_op == NULL) {
+ DEBUG(1, ("sdap_id_op_create failed.\n"));
+ return EIO;
+ }
+ }
+
+ subreq = sdap_id_op_connect_send(hbac_ctx_sdap_id_op(hbac_ctx), hbac_ctx, &ret);
+ if (!subreq) {
+ DEBUG(1, ("sdap_id_op_connect_send failed: %d(%s).\n", ret, strerror(ret)));
+ talloc_zfree(hbac_ctx->sdap_op);
+ return ret;
+ }
+
+ tevent_req_set_callback(subreq, hbac_connect_done, hbac_ctx);
+ } else {
+ /* Evaluate the rules based on what we have in the
+ * sysdb
+ */
+ ipa_hbac_evaluate_rules(hbac_ctx);
+ return EOK;
+ }
return EOK;
}
@@ -1739,6 +210,9 @@ static void hbac_connect_done(struct tevent_req *subreq)
if (dp_error == DP_ERR_OFFLINE) {
/* switching to offline mode */
talloc_zfree(hbac_ctx->sdap_op);
+
+ ipa_hbac_evaluate_rules(hbac_ctx);
+ return;
} else if (ret != EOK) {
goto fail;
}
@@ -1754,6 +228,24 @@ fail:
ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
}
+static void hbac_clear_rule_data(struct hbac_ctx *hbac_ctx)
+{
+ hbac_ctx->host_count = 0;
+ talloc_zfree(hbac_ctx->hosts);
+
+ hbac_ctx->hostgroup_count = 0;
+ talloc_zfree(hbac_ctx->hostgroups);
+
+ hbac_ctx->service_count = 0;
+ talloc_zfree(hbac_ctx->services);
+
+ hbac_ctx->servicegroup_count = 0;
+ talloc_zfree(hbac_ctx->servicegroups);
+
+ hbac_ctx->rule_count = 0;
+ talloc_zfree(hbac_ctx->rules);
+}
+
/* Check the step result code and continue, retry, get offline result or abort accordingly */
static bool hbac_check_step_result(struct hbac_ctx *hbac_ctx, int ret)
{
@@ -1774,6 +266,10 @@ static bool hbac_check_step_result(struct hbac_ctx *hbac_ctx, int ret)
if (dp_error == DP_ERR_OFFLINE) {
/* switching to offline mode */
talloc_zfree(hbac_ctx->sdap_op);
+
+ /* Free any of the results we've gotten */
+ hbac_clear_rule_data(hbac_ctx);
+
dp_error = DP_ERR_OK;
}
@@ -1790,173 +286,374 @@ static bool hbac_check_step_result(struct hbac_ctx *hbac_ctx, int ret)
return false;
}
+static void hbac_get_service_info_step(struct tevent_req *req);
+static void hbac_get_rule_info_step(struct tevent_req *req);
+static void hbac_sysdb_save (struct tevent_req *req);
+
static int hbac_get_host_info_step(struct hbac_ctx *hbac_ctx)
{
- struct pam_data *pd = hbac_ctx->pd;
- const char *hostlist[3];
- struct tevent_req *subreq;
-
- hostlist[0] = dp_opt_get_cstring(hbac_ctx->ipa_options, IPA_HOSTNAME);
- if (hostlist[0] == NULL) {
- DEBUG(1, ("ipa_hostname not available.\n"));
- return EINVAL;
- }
- if (pd->rhost != NULL && *pd->rhost != '\0') {
- hostlist[1] = pd->rhost;
- hostlist[2] = NULL;
- } else {
- hostlist[1] = NULL;
- pd->rhost = discard_const_p(char, hostlist[0]);
- }
-
- subreq = hbac_get_host_info_send(hbac_ctx, hbac_ctx, hostlist);
- if (!subreq) {
- DEBUG(1, ("hbac_get_host_info_send failed.\n"));
+ struct tevent_req *req =
+ ipa_hbac_host_info_send(hbac_ctx,
+ hbac_ctx_ev(hbac_ctx),
+ hbac_ctx_sysdb(hbac_ctx),
+ hbac_ctx_be(hbac_ctx)->domain,
+ sdap_id_op_handle(hbac_ctx->sdap_op),
+ hbac_ctx_sdap_id_ctx(hbac_ctx)->opts,
+ hbac_ctx->hbac_search_base);
+ if (req == NULL) {
+ DEBUG(1, ("Could not get host info\n"));
return ENOMEM;
}
+ tevent_req_set_callback(req, hbac_get_service_info_step, hbac_ctx);
- tevent_req_set_callback(subreq, hbac_get_host_info_done, hbac_ctx);
return EOK;
}
-static void hbac_get_host_info_done(struct tevent_req *req)
+static void hbac_get_service_info_step(struct tevent_req *req)
{
- struct hbac_ctx *hbac_ctx = tevent_req_callback_data(req, struct hbac_ctx);
- int ret;
- int pam_status = PAM_SYSTEM_ERR;
- const char *ipa_hostname;
- struct hbac_host_info *local_hhi = NULL;
+ errno_t ret;
+ struct hbac_ctx *hbac_ctx =
+ tevent_req_callback_data(req, struct hbac_ctx);
- ret = hbac_get_host_info_recv(req, hbac_ctx, &hbac_ctx->hbac_hosts_count,
- &hbac_ctx->hbac_hosts_list);
+ ret = ipa_hbac_host_info_recv(req, hbac_ctx,
+ &hbac_ctx->host_count,
+ &hbac_ctx->hosts,
+ &hbac_ctx->hostgroup_count,
+ &hbac_ctx->hostgroups);
talloc_zfree(req);
+ if (!hbac_check_step_result(hbac_ctx, ret)) {
+ return;
+ }
+
+ /* Get services and service groups */
+ req = ipa_hbac_service_info_send(hbac_ctx,
+ hbac_ctx_ev(hbac_ctx),
+ hbac_ctx_sysdb(hbac_ctx),
+ hbac_ctx_be(hbac_ctx)->domain,
+ sdap_id_op_handle(hbac_ctx->sdap_op),
+ hbac_ctx_sdap_id_ctx(hbac_ctx)->opts,
+ hbac_ctx->hbac_search_base);
+ if (req == NULL) {
+ DEBUG(1,("Could not get service info\n"));
+ goto fail;
+ }
+ tevent_req_set_callback(req, hbac_get_rule_info_step, hbac_ctx);
+ return;
+
+fail:
+ ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
+}
+static void hbac_get_rule_info_step(struct tevent_req *req)
+{
+ errno_t ret;
+ size_t i;
+ const char *ipa_hostname;
+ const char *hostname;
+ struct hbac_ctx *hbac_ctx =
+ tevent_req_callback_data(req, struct hbac_ctx);
+
+ ret = ipa_hbac_service_info_recv(req, hbac_ctx,
+ &hbac_ctx->service_count,
+ &hbac_ctx->services,
+ &hbac_ctx->servicegroup_count,
+ &hbac_ctx->servicegroups);
+ talloc_zfree(req);
if (!hbac_check_step_result(hbac_ctx, ret)) {
return;
}
+ /* Get the ipa_host attrs */
+ hbac_ctx->ipa_host = NULL;
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"));
goto fail;
}
- ret = set_local_and_remote_host_info(hbac_ctx, hbac_ctx->hbac_hosts_count,
- hbac_ctx->hbac_hosts_list, ipa_hostname,
- hbac_ctx->pd->rhost, &local_hhi,
- &hbac_ctx->remote_hhi);
- if (ret != EOK) {
- DEBUG(1, ("set_local_and_remote_host_info failed.\n"));
- goto fail;
- }
+ for (i = 0; i < hbac_ctx->host_count; i++) {
+ ret = sysdb_attrs_get_string(hbac_ctx->hosts[i],
+ IPA_HOST_FQDN,
+ &hostname);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not locate IPA host\n"));
+ goto fail;
+ }
- if (local_hhi == NULL) {
- DEBUG(1, ("Missing host info for [%s].\n", ipa_hostname));
- pam_status = PAM_PERM_DENIED;
+ if (strcmp(hostname, ipa_hostname) == 0) {
+ hbac_ctx->ipa_host = hbac_ctx->hosts[i];
+ break;
+ }
+ }
+ if (hbac_ctx->ipa_host == NULL) {
+ DEBUG(1, ("Could not locate IPA host\n"));
goto fail;
}
- req = hbac_get_rules_send(hbac_ctx, hbac_ctx, local_hhi->dn,
- local_hhi->memberof);
+
+
+ /* Get the list of applicable rules */
+ req = ipa_hbac_rule_info_send(hbac_ctx,
+ hbac_ctx->get_deny_rules,
+ hbac_ctx_ev(hbac_ctx),
+ hbac_ctx_sysdb(hbac_ctx),
+ hbac_ctx_be(hbac_ctx)->domain,
+ sdap_id_op_handle(hbac_ctx->sdap_op),
+ hbac_ctx_sdap_id_ctx(hbac_ctx)->opts,
+ hbac_ctx->hbac_search_base,
+ hbac_ctx->ipa_host);
if (req == NULL) {
- DEBUG(1, ("hbac_get_rules_send failed.\n"));
+ DEBUG(1, ("Could not get rules\n"));
goto fail;
}
- tevent_req_set_callback(req, hbac_get_rules_done, hbac_ctx);
+ tevent_req_set_callback(req, hbac_sysdb_save, hbac_ctx);
return;
fail:
- ipa_access_reply(hbac_ctx, pam_status);
+ ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
}
-static void hbac_get_rules_done(struct tevent_req *req)
+static void hbac_sysdb_save(struct tevent_req *req)
{
- struct hbac_ctx *hbac_ctx = tevent_req_callback_data(req, struct hbac_ctx);
- int ret;
- int pam_status = PAM_SYSTEM_ERR;
-
- hbac_ctx->hbac_rule_count = 0;
- talloc_zfree(hbac_ctx->hbac_rule_list);
+ errno_t ret;
+ bool in_transaction = false;
+ struct hbac_ctx *hbac_ctx =
+ tevent_req_callback_data(req, struct hbac_ctx);
+ struct sss_domain_info *domain = hbac_ctx_be(hbac_ctx)->domain;
+ struct sysdb_ctx *sysdb = hbac_ctx_sysdb(hbac_ctx);
+ struct ldb_dn *base_dn;
+ struct be_ctx *be_ctx = hbac_ctx_be(hbac_ctx);
+ struct ipa_access_ctx *access_ctx =
+ talloc_get_type(be_ctx->bet_info[BET_ACCESS].pvt_bet_data,
+ struct ipa_access_ctx);
+ TALLOC_CTX *tmp_ctx;
- ret = hbac_get_rules_recv(req, hbac_ctx, &hbac_ctx->hbac_rule_count,
- &hbac_ctx->hbac_rule_list);
+ ret = ipa_hbac_rule_info_recv(req, hbac_ctx,
+ &hbac_ctx->rule_count,
+ &hbac_ctx->rules);
talloc_zfree(req);
+ if (ret == ENOENT) {
+ /* No rules were found that apply to this
+ * host.
+ */
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
+ return;
+ }
+ /* Delete any rules in the sysdb so offline logins
+ * are also denied.
+ */
+ base_dn = sysdb_custom_subtree_dn(sysdb, tmp_ctx,
+ domain->name,
+ HBAC_RULES_SUBDIR);
+ if (base_dn == NULL) {
+ talloc_free(tmp_ctx);
+ ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
+ return;
+ }
+
+ ret = sysdb_delete_recursive(tmp_ctx, sysdb, base_dn, true);
+ talloc_free(tmp_ctx);
+ if (ret != EOK) {
+ DEBUG(1, ("sysdb_delete_recursive failed.\n"));
+ ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
+ return;
+ }
+
+ /* If no rules are found, we default to DENY */
+ ipa_access_reply(hbac_ctx, PAM_PERM_DENIED);
+ return;
+ }
if (!hbac_check_step_result(hbac_ctx, ret)) {
return;
}
- req = hbac_get_service_data_send(hbac_ctx, hbac_ctx);
- if (req == NULL) {
- DEBUG(1, ("hbac_get_service_data_send failed.\n"));
- goto failed;
+ ret = sysdb_transaction_start(sysdb);
+ if (ret != EOK) {
+ DEBUG(0, ("Could not start transaction\n"));
+ goto fail;
}
+ in_transaction = true;
- tevent_req_set_callback(req, hbac_get_service_data_done, hbac_ctx);
- return;
+ /* Save the hosts */
+ ret = ipa_hbac_sysdb_save(sysdb, domain,
+ HBAC_HOSTS_SUBDIR, IPA_HOST_FQDN,
+ hbac_ctx->host_count, hbac_ctx->hosts,
+ HBAC_HOSTGROUPS_SUBDIR, IPA_CN,
+ hbac_ctx->hostgroup_count,
+ hbac_ctx->hostgroups);
+ if (ret != EOK) {
+ DEBUG(1, ("Error saving hosts: [%d][%s]\n",
+ ret, strerror(ret)));
+ goto fail;
+ }
-failed:
- ipa_access_reply(hbac_ctx, pam_status);
-}
+ /* Save the services */
+ ret = ipa_hbac_sysdb_save(sysdb, domain,
+ HBAC_SERVICES_SUBDIR, IPA_CN,
+ hbac_ctx->service_count, hbac_ctx->services,
+ HBAC_SERVICEGROUPS_SUBDIR, IPA_CN,
+ hbac_ctx->servicegroup_count,
+ hbac_ctx->servicegroups);
+ if (ret != EOK) {
+ DEBUG(1, ("Error saving services: [%d][%s]\n",
+ ret, strerror(ret)));
+ goto fail;
+ }
+ /* Save the rules */
+ ret = ipa_hbac_sysdb_save(sysdb, domain,
+ HBAC_RULES_SUBDIR, IPA_UNIQUE_ID,
+ hbac_ctx->rule_count,
+ hbac_ctx->rules,
+ NULL, NULL, 0, NULL);
+ if (ret != EOK) {
+ DEBUG(1, ("Error saving rules: [%d][%s]\n",
+ ret, strerror(ret)));
+ goto fail;
+ }
-static void hbac_get_service_data_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;
- int ret;
- int pam_status = PAM_SYSTEM_ERR;
- bool access_allowed = false;
+ ret = sysdb_transaction_commit(sysdb);
+ if (ret != EOK) {
+ DEBUG(0, ("Failed to commit transaction\n"));
+ goto fail;
+ }
- hbac_ctx->hbac_services_count = 0;
- talloc_zfree(hbac_ctx->hbac_services_list);
+ /* We don't need the rule data any longer,
+ * the rest of the processing relies on
+ * sysdb lookups.
+ */
+ hbac_clear_rule_data(hbac_ctx);
- ret = hbac_get_service_data_recv(req, hbac_ctx,
- &hbac_ctx->hbac_services_count,
- &hbac_ctx->hbac_services_list);
- talloc_zfree(req);
- if (!hbac_check_step_result(hbac_ctx, ret)) {
- return;
- }
+ access_ctx->last_update = time(NULL);
- if (hbac_ctx->user_dn) {
- talloc_free(discard_const_p(TALLOC_CTX, hbac_ctx->user_dn));
- hbac_ctx->user_dn = 0;
- }
+ /* Now evaluate the request against the rules */
+ ipa_hbac_evaluate_rules(hbac_ctx);
+
+ return;
- if (!hbac_ctx_is_offline(hbac_ctx)) {
- ret = hbac_save_data_to_sysdb(hbac_ctx);
+fail:
+ if (in_transaction) {
+ ret = sysdb_transaction_cancel(sysdb);
if (ret != EOK) {
- DEBUG(1, ("Failed to save data, "
- "offline authentication might not work.\n"));
- /* This is not a fatal error. */
+ DEBUG(0, ("Could not cancel transaction\n"));
}
}
+ ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
+}
+
+static errno_t hbac_get_cached_rules(TALLOC_CTX *mem_ctx,
+ struct hbac_ctx *hbac_ctx);
- hbac_ctx->groups_count = 0;
- talloc_zfree(hbac_ctx->groups);
+void ipa_hbac_evaluate_rules(struct hbac_ctx *hbac_ctx)
+{
+ errno_t ret;
+ struct hbac_rule **hbac_rules;
+ struct hbac_eval_req *eval_req;
+ enum hbac_eval_result result;
+ struct hbac_info *info;
- ret = hbac_get_user_info(hbac_ctx, hbac_ctx_be(hbac_ctx),
- pd->user, &hbac_ctx->user_dn,
- &hbac_ctx->groups_count, &hbac_ctx->groups);
+ /* Get HBAC rules from the sysdb */
+ ret = hbac_get_cached_rules(hbac_ctx, hbac_ctx);
if (ret != EOK) {
- goto failed;
+ DEBUG(1, ("Could not retrieve rules from the cache\n"));
+ ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
}
- ret = evaluate_ipa_hbac_rules(hbac_ctx, &access_allowed);
- if (ret != EOK) {
- DEBUG(1, ("evaluate_ipa_hbac_rules failed.\n"));
- goto failed;
+ ret = hbac_ctx_to_rules(hbac_ctx, hbac_ctx,
+ &hbac_rules, &eval_req);
+ if (ret == EPERM) {
+ DEBUG(1, ("DENY rules detected. Denying access to all users\n"));
+ ipa_access_reply(hbac_ctx, PAM_PERM_DENIED);
+ return;
+ } else if (ret != EOK) {
+ DEBUG(1, ("Could not construct HBAC rules\n"));
+ ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
+ return;
}
- if (access_allowed) {
- pam_status = PAM_SUCCESS;
- DEBUG(5, ("Access allowed.\n"));
- } else {
- pam_status = PAM_PERM_DENIED;
- DEBUG(3, ("Access denied.\n"));
+ result = hbac_evaluate(hbac_rules, eval_req, &info);
+ if (result == HBAC_EVAL_ALLOW) {
+ DEBUG(3, ("Access granted by HBAC rule [%s]\n",
+ info->rule_name));
+ hbac_free_info(info);
+ ipa_access_reply(hbac_ctx, PAM_SUCCESS);
+ return;
+ } else if (result == HBAC_EVAL_ERROR) {
+ DEBUG(1, ("Error [%s] occurred in rule [%s]\n",
+ hbac_error_string(info->code),
+ info->rule_name));
+ hbac_free_info(info);
+ ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
+ return;
+ } else if (result == HBAC_EVAL_OOM) {
+ DEBUG(1, ("Insufficient memory\n"));
+ ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR);
+ return;
+ }
+
+ DEBUG(3, ("Access denied by HBAC rules\n"));
+ hbac_free_info(info);
+ ipa_access_reply(hbac_ctx, PAM_PERM_DENIED);
+}
+
+static errno_t hbac_get_cached_rules(TALLOC_CTX *mem_ctx,
+ struct hbac_ctx *hbac_ctx)
+{
+ errno_t ret;
+ struct sysdb_ctx *sysdb = hbac_ctx_sysdb(hbac_ctx);
+ struct sss_domain_info *domain = hbac_ctx_be(hbac_ctx)->domain;
+ size_t count;
+ struct ldb_message **msgs;
+ TALLOC_CTX *tmp_ctx;
+ char *filter;
+ const char *attrs[] = { OBJECTCLASS,
+ IPA_CN,
+ IPA_UNIQUE_ID,
+ IPA_ENABLED_FLAG,
+ IPA_ACCESS_RULE_TYPE,
+ IPA_MEMBER_USER,
+ IPA_USER_CATEGORY,
+ IPA_MEMBER_SERVICE,
+ IPA_SERVICE_CATEGORY,
+ IPA_SOURCE_HOST,
+ IPA_SOURCE_HOST_CATEGORY,
+ IPA_EXTERNAL_HOST,
+ IPA_MEMBER_HOST,
+ IPA_HOST_CATEGORY,
+ NULL };
+
+ tmp_ctx = talloc_new(hbac_ctx);
+ if (tmp_ctx == NULL) return ENOMEM;
+
+ filter = talloc_asprintf(tmp_ctx, "(objectClass=%s)", IPA_HBAC_RULE);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_custom(mem_ctx, sysdb, domain, filter,
+ HBAC_RULES_SUBDIR, attrs,
+ &count, &msgs);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(1, ("Error looking up HBAC rules"));
+ goto done;
+ } if (ret == ENOENT) {
+ count = 0;
+ }
+
+ ret = msgs2attrs_array(mem_ctx, count, msgs, &hbac_ctx->rules);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not convert ldb message to sysdb_attrs\n"));
+ goto done;
}
+ hbac_ctx->rule_count = count;
-failed:
- ipa_access_reply(hbac_ctx, pam_status);
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
}
diff --git a/src/providers/ipa/ipa_access.h b/src/providers/ipa/ipa_access.h
index 52b2f0707..2a6bdad50 100644
--- a/src/providers/ipa/ipa_access.h
+++ b/src/providers/ipa/ipa_access.h
@@ -43,26 +43,37 @@ struct ipa_access_ctx {
struct sdap_id_ctx *sdap_ctx;
struct dp_option *ipa_options;
struct time_rules_ctx *tr_ctx;
+ time_t last_update;
};
struct hbac_ctx {
struct sdap_id_ctx *sdap_ctx;
+ struct ipa_access_ctx *access_ctx;
struct sdap_id_op *sdap_op;
struct dp_option *ipa_options;
struct time_rules_ctx *tr_ctx;
struct be_req *be_req;
struct pam_data *pd;
- struct sysdb_attrs **hbac_hosts_list;
- size_t hbac_hosts_count;
- struct hbac_host_info *remote_hhi;
- struct sysdb_attrs **hbac_rule_list;
- size_t hbac_rule_count;
- const char *user_dn;
- size_t groups_count;
- const char **groups;
+
char *hbac_search_base;
- struct sysdb_attrs **hbac_services_list;
- size_t hbac_services_count;
+
+ /* Hosts */
+ size_t host_count;
+ struct sysdb_attrs **hosts;
+ size_t hostgroup_count;
+ struct sysdb_attrs **hostgroups;
+ struct sysdb_attrs *ipa_host;
+
+ /* Rules */
+ bool get_deny_rules;
+ size_t rule_count;
+ struct sysdb_attrs **rules;
+
+ /* Services */
+ size_t service_count;
+ struct sysdb_attrs **services;
+ size_t servicegroup_count;
+ struct sysdb_attrs **servicegroups;
};
/* Get BE context associated with HBAC context */
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index c1581305d..115172250 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -36,7 +36,9 @@ struct dp_option ipa_basic_opts[] = {
{ "ipa_dyndns_update", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
{ "ipa_dyndns_iface", DP_OPT_STRING, NULL_STRING, NULL_STRING},
{ "ipa_hbac_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING},
- { "krb5_realm", DP_OPT_STRING, NULL_STRING, NULL_STRING}
+ { "krb5_realm", DP_OPT_STRING, NULL_STRING, NULL_STRING},
+ { "ipa_hbac_refresh", DP_OPT_NUMBER, { .number = 5 }, NULL_NUMBER },
+ { "ipa_hbac_treat_deny_as", DP_OPT_STRING, { "DENY_ALL" }, NULL_STRING }
};
struct dp_option ipa_def_ldap_opts[] = {
diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h
index 922806234..f6e1d4e5b 100644
--- a/src/providers/ipa/ipa_common.h
+++ b/src/providers/ipa/ipa_common.h
@@ -50,6 +50,8 @@ enum ipa_basic_opt {
IPA_DYNDNS_IFACE,
IPA_HBAC_SEARCH_BASE,
IPA_KRB5_REALM,
+ IPA_HBAC_REFRESH,
+ IPA_HBAC_DENY_METHOD,
IPA_OPTS_BASIC /* opts counter */
};
diff --git a/src/providers/ipa/ipa_hbac.h b/src/providers/ipa/ipa_hbac.h
new file mode 100644
index 000000000..7de49d1ff
--- /dev/null
+++ b/src/providers/ipa/ipa_hbac.h
@@ -0,0 +1,177 @@
+/*
+ SSSD
+
+ IPA Backend Module -- Access control
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+ Stephen Gallagher <sgallagh@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_HBAC_H_
+#define IPA_HBAC_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+
+enum hbac_eval_result {
+ HBAC_EVAL_ERROR = -1,
+ HBAC_EVAL_ALLOW,
+ HBAC_EVAL_DENY,
+ HBAC_EVAL_OOM
+};
+
+#define HBAC_CATEGORY_NULL 0x0000 /* No service category specified */
+#define HBAC_CATEGORY_ALL 0x0001 /* Rule should apply to all */
+
+/* Opaque type contained in hbac_evaluator.c */
+struct hbac_time_rules;
+
+struct hbac_rule_element {
+ uint32_t category;
+ const char **names;
+ const char **groups;
+};
+
+struct hbac_rule {
+ const char *name;
+ bool enabled;
+
+ /* Services and service groups
+ * for which this rule applies
+ */
+ struct hbac_rule_element *services;
+
+ /* Users and groups for which this
+ * rule applies
+ */
+ struct hbac_rule_element *users;
+
+ /* Target hosts for which this rule apples */
+ struct hbac_rule_element *targethosts;
+
+ /* Source hosts for which this rule applies */
+ struct hbac_rule_element *srchosts;
+
+ /* For future use */
+ struct hbac_time_rules *timerules;
+};
+
+struct hbac_request_element {
+ const char *name;
+ const char **groups;
+};
+
+struct hbac_eval_req {
+ /* This is a list of service DNs to check,
+ * it must consist of the actual service
+ * requested, as well as all parent groups
+ * containing that service.
+ */
+ struct hbac_request_element *service;
+
+ /* This is a list of user DNs to check,
+ * it must consist of the actual user
+ * requested, as well as all parent groups
+ * containing that user.
+ */
+ struct hbac_request_element *user;
+
+ /* This is a list of target hosts to check,
+ * it must consist of the actual target host
+ * requested, as well as all parent groups
+ * containing that target host.
+ */
+ struct hbac_request_element *targethost;
+
+ /* This is a list of source hosts to check,
+ * it must consist of the actual source host
+ * requested, as well as all parent groups
+ * containing that source host.
+ */
+ struct hbac_request_element *srchost;
+
+ /* For future use */
+ time_t request_time;
+};
+
+enum hbac_error_code {
+ HBAC_ERROR_UNKNOWN = -1,
+ HBAC_SUCCESS,
+ HBAC_ERROR_NOT_IMPLEMENTED,
+ HBAC_ERROR_OUT_OF_MEMORY,
+ HBAC_ERROR_UNPARSEABLE_RULE
+};
+
+/* Extended information */
+struct hbac_info {
+ /* If the hbac_eval_result was HBAC_EVAL_ERROR,
+ * this will be an error code.
+ * Otherwise it will be HBAC_SUCCESS
+ */
+ enum hbac_error_code code;
+
+ /* Specify the name of the rule that matched or
+ * threw an error
+ */
+ char *rule_name;
+};
+
+
+/**
+ * @brief Evaluate an authorization request against a set of HBAC rules
+ *
+ * @param[in] rules A NULL-terminated list of rules to evaluate against
+ * @param[in] hbac_req A user authorization request
+ * @param[out] info Extended information (including the name of the
+ * rule that allowed access (or caused a parse error)
+ * @return
+ */
+enum hbac_eval_result hbac_evaluate(struct hbac_rule **rules,
+ struct hbac_eval_req *hbac_req,
+ struct hbac_info **info);
+
+const char *hbac_result_string(enum hbac_eval_result result);
+const char *hbac_error_string(enum hbac_error_code code);
+
+void hbac_free_info(struct hbac_info *info);
+
+
+#define HBAC_RULE_ELEMENT_USERS 0x01
+#define HBAC_RULE_ELEMENT_SERVICES 0x02
+#define HBAC_RULE_ELEMENT_TARGETHOSTS 0x04
+#define HBAC_RULE_ELEMENT_SOURCEHOSTS 0x08
+
+/**
+ * @brief Evaluate whether an HBAC rule contains all necessary elements
+ *
+ * @param[in] rule An HBAC rule to evaluate
+ * @param[out] missing_attrs A list of attributes missing from the rule
+ * This is a bitmask that may contain one or more
+ * of HBAC_RULE_ELEMENT_USERS,
+ * HBAC_RULE_ELEMENT_SERVICES,
+ * HBAC_RULE_ELEMENT_TARGETHOSTS and
+ * HBAC_RULE_ELEMENT_SOURCEHOSTS
+ *
+ * @return True if the rule contains all mandatory attributes
+ *
+ * @note This function does not care if the rule is enabled or disabled
+ */
+bool hbac_rule_is_complete(struct hbac_rule *rule, uint32_t *missing_attrs);
+
+#endif /* IPA_HBAC_H_ */
diff --git a/src/providers/ipa/ipa_hbac.pc.in b/src/providers/ipa/ipa_hbac.pc.in
new file mode 100644
index 000000000..9d799e850
--- /dev/null
+++ b/src/providers/ipa/ipa_hbac.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: ipa_hbac
+Description: FreeIPA HBAC Evaluator library
+Version: @VERSION@
+Libs: -L$(libdir) -lipa_hbac
+Cflags:
+URL: http://fedorahosted.org/sssd/
diff --git a/src/providers/ipa/ipa_hbac_common.c b/src/providers/ipa/ipa_hbac_common.c
new file mode 100644
index 000000000..4633f2f9e
--- /dev/null
+++ b/src/providers/ipa/ipa_hbac_common.c
@@ -0,0 +1,883 @@
+/*
+ SSSD
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2011 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "providers/ipa/ipa_hbac_private.h"
+#include "providers/ipa/ipa_hbac.h"
+#include "providers/ipa/ipa_common.h"
+
+errno_t
+ipa_hbac_save_list(struct sysdb_ctx *sysdb, bool delete_subdir,
+ const char *subdir, struct sss_domain_info *domain,
+ const char *naming_attribute, size_t count,
+ struct sysdb_attrs **list)
+{
+ int ret;
+ size_t c;
+ struct ldb_dn *base_dn;
+ const char *object_name;
+ struct ldb_message_element *el;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(1, ("talloc_new failed.\n"));
+ return ENOMEM;
+ }
+
+ if (delete_subdir) {
+ base_dn = sysdb_custom_subtree_dn(sysdb, tmp_ctx, domain->name, subdir);
+ if (base_dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_delete_recursive(tmp_ctx, sysdb, base_dn, true);
+ if (ret != EOK) {
+ DEBUG(1, ("sysdb_delete_recursive failed.\n"));
+ goto done;
+ }
+ }
+
+ for (c = 0; c < count; c++) {
+ ret = sysdb_attrs_get_el(list[c], naming_attribute, &el);
+ if (ret != EOK) {
+ DEBUG(1, ("sysdb_attrs_get_el failed.\n"));
+ goto done;
+ }
+ if (el->num_values == 0) {
+ DEBUG(1, ("[%s] not found.\n", naming_attribute));
+ ret = EINVAL;
+ goto done;
+ }
+ object_name = talloc_strndup(tmp_ctx, (const char *)el->values[0].data,
+ el->values[0].length);
+ if (object_name == NULL) {
+ DEBUG(1, ("talloc_strndup failed.\n"));
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(9, ("Object name: [%s].\n", object_name));
+
+ ret = sysdb_store_custom(tmp_ctx, sysdb, domain, object_name, subdir,
+ list[c]);
+ if (ret != EOK) {
+ DEBUG(1, ("sysdb_store_custom failed.\n"));
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+ipa_hbac_sysdb_save(struct sysdb_ctx *sysdb, struct sss_domain_info *domain,
+ const char *primary_subdir, const char *attr_name,
+ size_t primary_count, struct sysdb_attrs **primary,
+ const char *group_subdir, const char *groupattr_name,
+ size_t group_count, struct sysdb_attrs **groups)
+{
+ int lret;
+ errno_t ret, sret;
+ bool in_transaction = false;
+ const char **orig_member_dns;
+ size_t i, j, member_count;
+ struct ldb_message **members;
+ TALLOC_CTX *tmp_ctx = NULL;
+ const char *member_dn;
+ const char *group_id;
+ struct ldb_message *msg;
+ char *member_filter;
+
+ if ((primary_count == 0 || primary == NULL)
+ || (group_count > 0 && groups == NULL)) {
+ /* There always has to be at least one
+ * primary entry.
+ */
+ return EINVAL;
+ }
+
+ /* Save the entries and groups to the cache */
+ ret = sysdb_transaction_start(sysdb);
+ if (ret != EOK) return ret;
+ in_transaction = true;
+
+ /* First, save the specific entries */
+ ret = ipa_hbac_save_list(sysdb, true,
+ primary_subdir,
+ domain,
+ attr_name,
+ primary_count,
+ primary);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not save %s. [%d][%s]\n",
+ primary_subdir, ret, strerror(ret)));
+ goto done;
+ }
+
+ /* Second, save the groups */
+ if (group_count > 0) {
+ ret = ipa_hbac_save_list(sysdb, true,
+ group_subdir,
+ domain,
+ groupattr_name,
+ group_count,
+ groups);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not save %s. [%d][%s]\n",
+ group_subdir, ret, strerror(ret)));
+ goto done;
+ }
+
+ /* Third, save the memberships */
+ for (i = 0; i < group_count; i++) {
+ if (!groups[i]) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ talloc_free(tmp_ctx);
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_attrs_get_string(groups[i],
+ groupattr_name,
+ &group_id);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not determine group attribute name\n"));
+ goto done;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = sysdb_custom_dn(sysdb, msg, domain->name,
+ group_id, group_subdir);
+ if (msg->dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ lret = ldb_msg_add_empty(msg, SYSDB_MEMBER, LDB_FLAG_MOD_ADD, NULL);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ ret = sysdb_attrs_get_string_array(groups[i],
+ SYSDB_ORIG_MEMBER,
+ tmp_ctx,
+ &orig_member_dns);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not determine original members\n"));
+ goto done;
+ }
+
+ for (j = 0; orig_member_dns[j]; j++) {
+ member_filter = talloc_asprintf(tmp_ctx, "%s=%s",
+ SYSDB_ORIG_DN,
+ orig_member_dns[j]);
+ if (member_filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_custom(tmp_ctx, sysdb, domain,
+ member_filter, primary_subdir,
+ NULL, &member_count, &members);
+ talloc_zfree(member_filter);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ } else if (ret == ENOENT || member_count == 0) {
+ /* No member exists with this orig_dn. Skip it */
+ DEBUG(6, ("[%s] does not exist\n", orig_member_dns[j]));
+ continue;
+ } else if (member_count > 1) {
+ /* This probably means corruption in the cache, but
+ * we'll try to proceed anyway.
+ */
+ DEBUG(1, ("More than one result for DN [%s], skipping\n"));
+ continue;
+ }
+
+ member_dn = ldb_dn_get_linearized(members[0]->dn);
+ if (!member_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+ lret = ldb_msg_add_fmt(msg, SYSDB_MEMBER, "%s", member_dn);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ }
+
+ lret = ldb_modify(sysdb_ctx_get_ldb(sysdb), msg);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+ }
+ talloc_zfree(tmp_ctx);
+ }
+
+ ret = sysdb_transaction_commit(sysdb);
+ if (ret != EOK) goto done;
+ in_transaction = false;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(sysdb);
+ if (sret != EOK) {
+ DEBUG(0, ("Could not cancel sysdb transaction\n"));
+ }
+ }
+
+ if (ret != EOK) {
+ DEBUG(3, ("Error [%d][%s]\n", ret, strerror(ret)));
+ }
+ return ret;
+}
+
+errno_t
+replace_attribute_name(const char *old_name,
+ const char *new_name, const size_t count,
+ struct sysdb_attrs **list)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ ret = sysdb_attrs_replace_name(list[i], old_name, new_name);
+ if (ret != EOK) {
+ DEBUG(1, ("sysdb_attrs_replace_name failed.\n"));
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+
+/********************************************
+ * Functions for handling conversion to the *
+ * HBAC evaluator format *
+ ********************************************/
+
+static errno_t
+hbac_attrs_to_rule(TALLOC_CTX *mem_ctx,
+ struct hbac_ctx *hbac_ctx,
+ size_t index,
+ struct hbac_rule **rule);
+
+static errno_t
+hbac_ctx_to_eval_request(TALLOC_CTX *mem_ctx,
+ struct hbac_ctx *hbac_ctx,
+ struct hbac_eval_req **request);
+
+errno_t
+hbac_ctx_to_rules(TALLOC_CTX *mem_ctx,
+ struct hbac_ctx *hbac_ctx,
+ struct hbac_rule ***rules,
+ struct hbac_eval_req **request)
+{
+ errno_t ret;
+ struct hbac_rule **new_rules;
+ struct hbac_eval_req *new_request;
+ size_t i;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ if (!rules || !request) return EINVAL;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) return ENOMEM;
+
+ /* First create an array of rules */
+ new_rules = talloc_array(tmp_ctx, struct hbac_rule *,
+ hbac_ctx->rule_count + 1);
+ if (new_rules == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Create each rule one at a time */
+ for (i = 0; i < hbac_ctx->rule_count ; i++) {
+ ret = hbac_attrs_to_rule(new_rules, hbac_ctx, i, &(new_rules[i]));
+ if (ret == EPERM) {
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(1, ("Could not construct rules\n"))
+ goto done;
+ }
+ }
+ new_rules[i] = NULL;
+
+ /* Create the eval request */
+ ret = hbac_ctx_to_eval_request(tmp_ctx, hbac_ctx, &new_request);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not construct eval request\n"));
+ goto done;
+ }
+
+ *rules = talloc_steal(mem_ctx, new_rules);
+ *request = talloc_steal(mem_ctx, new_request);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t
+hbac_attrs_to_rule(TALLOC_CTX *mem_ctx,
+ struct hbac_ctx *hbac_ctx,
+ size_t idx,
+ struct hbac_rule **rule)
+{
+ errno_t ret;
+ struct hbac_rule *new_rule;
+ struct ldb_message_element *el;
+ const char *rule_type;
+
+ new_rule = talloc_zero(mem_ctx, struct hbac_rule);
+ if (new_rule == NULL) return ENOMEM;
+
+ ret = sysdb_attrs_get_el(hbac_ctx->rules[idx],
+ IPA_CN, &el);
+ if (ret != EOK || el->num_values == 0) {
+ DEBUG(4, ("rule has no name, assuming '(none)'.\n"));
+ new_rule->name = talloc_strdup(new_rule, "(none)");
+ } else {
+ new_rule->name = talloc_strndup(new_rule,
+ (const char*) el->values[0].data,
+ el->values[0].length);
+ }
+
+ DEBUG(7, ("Processing rule [%s]\n", new_rule->name));
+
+ ret = sysdb_attrs_get_bool(hbac_ctx->rules[idx], IPA_ENABLED_FLAG,
+ &new_rule->enabled);
+ if (ret != EOK) goto done;
+
+ if (!new_rule->enabled) {
+ ret = EOK;
+ goto done;
+ }
+
+ ret = sysdb_attrs_get_string(hbac_ctx->rules[idx],
+ IPA_ACCESS_RULE_TYPE,
+ &rule_type);
+ if (ret != EOK) goto done;
+
+ if (strcasecmp(rule_type, IPA_HBAC_ALLOW) != 0) {
+ DEBUG(7, ("Rule [%s] is not an ALLOW rule\n", new_rule->name));
+ ret = EPERM;
+ goto done;
+ }
+
+ /* Get the users */
+ ret = hbac_user_attrs_to_rule(new_rule,
+ hbac_ctx_sysdb(hbac_ctx),
+ hbac_ctx_be(hbac_ctx)->domain,
+ new_rule->name,
+ hbac_ctx->rules[idx],
+ &new_rule->users);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not parse users for rule [%s]\n",
+ new_rule->name));
+ goto done;
+ }
+
+ /* Get the services */
+ ret = hbac_service_attrs_to_rule(new_rule,
+ hbac_ctx_sysdb(hbac_ctx),
+ hbac_ctx_be(hbac_ctx)->domain,
+ new_rule->name,
+ hbac_ctx->rules[idx],
+ &new_rule->services);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not parse services for rule [%s]\n",
+ new_rule->name));
+ goto done;
+ }
+
+ /* Get the target hosts */
+ ret = hbac_thost_attrs_to_rule(new_rule,
+ hbac_ctx_sysdb(hbac_ctx),
+ hbac_ctx_be(hbac_ctx)->domain,
+ new_rule->name,
+ hbac_ctx->rules[idx],
+ &new_rule->targethosts);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not parse target hosts for rule [%s]\n",
+ new_rule->name));
+ goto done;
+ }
+
+ /* Get the source hosts */
+ ret = hbac_shost_attrs_to_rule(new_rule,
+ hbac_ctx_sysdb(hbac_ctx),
+ hbac_ctx_be(hbac_ctx)->domain,
+ new_rule->name,
+ hbac_ctx->rules[idx],
+ &new_rule->srchosts);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not parse source hosts for rule [%s]\n",
+ new_rule->name));
+ goto done;
+ }
+
+ *rule = new_rule;
+ ret = EOK;
+
+done:
+ if (ret != EOK) talloc_free(new_rule);
+ return ret;
+}
+
+errno_t
+hbac_get_category(struct sysdb_attrs *attrs,
+ const char *category_attr,
+ uint32_t *_categories)
+{
+ errno_t ret;
+ size_t i;
+ uint32_t cats = HBAC_CATEGORY_NULL;
+ const char **categories;
+
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) return ENOMEM;
+
+ ret = sysdb_attrs_get_string_array(attrs, category_attr,
+ tmp_ctx, &categories);
+ if (ret != EOK && ret != ENOENT) goto done;
+
+ if (ret != ENOENT) {
+ for (i = 0; categories[i]; i++) {
+ if (strcasecmp("all", categories[i]) == 0) {
+ DEBUG(5, ("Category is set to 'all'.\n"));
+ cats |= HBAC_CATEGORY_ALL;
+ continue;
+ }
+ DEBUG(9, ("Unsupported user category [%s].\n",
+ categories[i]));
+ }
+ }
+
+ *_categories = cats;
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t
+hbac_eval_user_element(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ const char *username,
+ struct hbac_request_element **user_element);
+
+static errno_t
+hbac_eval_service_element(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ const char *hostname,
+ struct hbac_request_element **svc_element);
+
+static errno_t
+hbac_eval_host_element(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ const char *hostname,
+ struct hbac_request_element **host_element);
+
+static errno_t
+hbac_ctx_to_eval_request(TALLOC_CTX *mem_ctx,
+ struct hbac_ctx *hbac_ctx,
+ struct hbac_eval_req **request)
+{
+ errno_t ret;
+ struct pam_data *pd = hbac_ctx->pd;
+ TALLOC_CTX *tmp_ctx;
+ struct hbac_eval_req *eval_req;
+ struct sysdb_ctx *sysdb = hbac_ctx_sysdb(hbac_ctx);
+ struct sss_domain_info *domain = hbac_ctx_be(hbac_ctx)->domain;
+ const char *rhost;
+ const char *thost;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) return ENOMEM;
+
+ eval_req = talloc_zero(tmp_ctx, struct hbac_eval_req);
+ if (eval_req == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ eval_req->request_time = time(NULL);
+
+ /* Get user the user name and groups */
+ ret = hbac_eval_user_element(eval_req, sysdb, domain,
+ pd->user, &eval_req->user);
+ if (ret != EOK) goto done;
+
+ /* Get the PAM service and service groups */
+ ret = hbac_eval_service_element(eval_req, sysdb, domain,
+ pd->service, &eval_req->service);
+ if (ret != EOK) goto done;
+
+ /* Get the source host */
+ if (pd->rhost == NULL || pd->rhost[0] == '\0') {
+ /* If we haven't been passed an rhost,
+ * the rhost is unknown. This will fail
+ * to match any rule requiring the
+ * source host.
+ */
+ rhost = NULL;
+ } else {
+ rhost = pd->rhost;
+ }
+
+ ret = hbac_eval_host_element(eval_req, sysdb, domain,
+ rhost, &eval_req->srchost);
+ if (ret != EOK) goto done;
+
+ /* The target host is always the current machine */
+ thost = dp_opt_get_cstring(hbac_ctx->ipa_options, IPA_HOSTNAME);
+ if (thost == NULL) {
+ DEBUG(1, ("Missing ipa_hostname, this should never happen.\n"));
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = hbac_eval_host_element(eval_req, sysdb, domain,
+ thost, &eval_req->targethost);
+ if (ret != EOK) goto done;
+
+ *request = talloc_steal(mem_ctx, eval_req);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t
+hbac_eval_user_element(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ const char *username,
+ struct hbac_request_element **user_element)
+{
+ errno_t ret;
+ unsigned int i;
+ unsigned int num_groups = 0;
+ TALLOC_CTX *tmp_ctx;
+ const char *member_dn;
+ struct hbac_request_element *users;
+ struct ldb_message *msg;
+ struct ldb_message_element *el;
+ const char *attrs[] = { SYSDB_ORIG_MEMBEROF, NULL };
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) return ENOMEM;
+
+ users = talloc_zero(tmp_ctx, struct hbac_request_element);
+ if (users == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ users->name = username;
+
+ /* Read the originalMemberOf attribute
+ * This will give us the list of both POSIX and
+ * non-POSIX groups that this user belongs to.
+ */
+ ret = sysdb_search_user_by_name(tmp_ctx, sysdb, domain,
+ users->name, attrs, &msg);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not determine user memberships for [%s]\n",
+ users->name));
+ goto done;
+ }
+
+ el = ldb_msg_find_element(msg, SYSDB_ORIG_MEMBEROF);
+ if (el == NULL || el->num_values == 0) {
+ DEBUG(7, ("No groups for [%s]\n", users->name));
+ users->groups = talloc_array(users, const char *, 1);
+ if (users->groups == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ users->groups[0] = NULL;
+ goto done;
+ }
+ DEBUG(7, ("[%d] groups for [%s]\n", el->num_values, users->name));
+
+ users->groups = talloc_array(users, const char *, el->num_values + 1);
+ if (users->groups == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < el->num_values; i++) {
+ member_dn = (const char *)el->values[i].data;
+
+ ret = get_ipa_groupname(users->groups, sysdb, member_dn,
+ &users->groups[num_groups]);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(3, ("Parse error on [%s]\n", member_dn));
+ goto done;
+ } else if (ret == EOK) {
+ DEBUG(7, ("Added group [%s] for user [%s]\n",
+ users->groups[num_groups], users->name));
+ num_groups++;
+ continue;
+ }
+ /* Skip entries that are not groups */
+ DEBUG(8, ("Skipping non-group memberOf [%s]\n", member_dn));
+ }
+ users->groups[num_groups] = NULL;
+
+ if (num_groups < el->num_values) {
+ /* Shrink the array memory */
+ users->groups = talloc_realloc(users, users->groups, const char *,
+ num_groups+1);
+ if (users->groups == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = EOK;
+done:
+ if (ret == EOK) {
+ *user_element = talloc_steal(mem_ctx, users);
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t
+hbac_eval_service_element(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ const char *hostname,
+ struct hbac_request_element **svc_element)
+{
+ errno_t ret;
+ size_t i, count;
+ TALLOC_CTX *tmp_ctx;
+ struct hbac_request_element *svc;
+ struct ldb_message **msgs;
+ const char *group_name;
+ struct ldb_dn *svc_dn;
+ const char *attrs[] = { IPA_CN, NULL };
+ const char *service_filter;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) return ENOMEM;
+
+ svc = talloc_zero(tmp_ctx, struct hbac_request_element);
+ if (svc == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ svc->name = hostname;
+
+ service_filter = talloc_asprintf(tmp_ctx,
+ "(objectClass=%s)",
+ IPA_HBAC_SERVICE_GROUP);
+ if (service_filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ svc_dn = sysdb_custom_dn(sysdb, tmp_ctx, domain->name,
+ svc->name, HBAC_SERVICES_SUBDIR);
+ if (svc_dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Find the service groups */
+ ret = sysdb_asq_search(tmp_ctx, sysdb, domain, svc_dn,
+ service_filter, SYSDB_MEMBEROF,
+ attrs, &count, &msgs);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(1, ("Could not look up servicegroups\n"));
+ goto done;
+ } else if (ret == ENOENT) {
+ count = 0;
+ }
+
+ svc->groups = talloc_array(svc, const char *, count + 1);
+ if (svc->groups == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < count; i++) {
+ group_name = ldb_msg_find_attr_as_string(msgs[i], IPA_CN, NULL);
+ if (group_name == NULL) {
+ DEBUG(1, ("Group with no name?\n"));
+ ret = EINVAL;
+ goto done;
+ }
+ svc->groups[i] = talloc_strdup(svc->groups,
+ group_name);
+ if (svc->groups[i] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(6, ("Added service group [%s] to the eval request\n",
+ svc->groups[i]));
+ }
+ svc->groups[i] = NULL;
+
+ *svc_element = talloc_steal(mem_ctx, svc);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t
+hbac_eval_host_element(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ const char *hostname,
+ struct hbac_request_element **host_element)
+{
+ errno_t ret;
+ size_t i, count;
+ TALLOC_CTX *tmp_ctx;
+ struct hbac_request_element *host;
+ struct ldb_message **msgs;
+ const char *group_name;
+ struct ldb_dn *host_dn;
+ const char *attrs[] = { IPA_HOST_FQDN, NULL };
+ const char *host_filter;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) return ENOMEM;
+
+ host = talloc_zero(tmp_ctx, struct hbac_request_element);
+ if (host == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ host->name = hostname;
+
+ if (host->name == NULL) {
+ /* We don't know the host (probably an rhost)
+ * So we can't determine it's groups either.
+ */
+ host->groups = talloc_array(host, const char *, 1);
+ if (host->groups == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ host->groups[0] = NULL;
+ ret = EOK;
+ goto done;
+ }
+
+ host_filter = talloc_asprintf(tmp_ctx,
+ "(objectClass=%s)",
+ IPA_HOSTGROUP);
+ if (host_filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ host_dn = sysdb_custom_dn(sysdb, tmp_ctx, domain->name,
+ host->name, HBAC_SERVICES_SUBDIR);
+ if (host_dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Find the host groups */
+ ret = sysdb_asq_search(tmp_ctx, sysdb, domain, host_dn,
+ host_filter, SYSDB_MEMBEROF,
+ attrs, &count, &msgs);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(1, ("Could not look up host groups\n"));
+ goto done;
+ } else if (ret == ENOENT) {
+ count = 0;
+ }
+
+ host->groups = talloc_array(host, const char *, count + 1);
+ if (host->groups == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < count; i++) {
+ group_name = ldb_msg_find_attr_as_string(msgs[i],
+ IPA_HOST_FQDN,
+ NULL);
+ if (group_name == NULL) {
+ DEBUG(1, ("Group with no name?\n"));
+ ret = EINVAL;
+ goto done;
+ }
+ host->groups[i] = talloc_strdup(host->groups,
+ group_name);
+ if (host->groups[i] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(6, ("Added host group [%s] to the eval request\n",
+ host->groups[i]));
+ }
+ host->groups[i] = NULL;
+
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ *host_element = talloc_steal(mem_ctx, host);
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
diff --git a/src/providers/ipa/ipa_hbac_hosts.c b/src/providers/ipa/ipa_hbac_hosts.c
new file mode 100644
index 000000000..4e753f374
--- /dev/null
+++ b/src/providers/ipa/ipa_hbac_hosts.c
@@ -0,0 +1,524 @@
+/*
+ SSSD
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2011 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "util/util.h"
+#include "db/sysdb.h"
+#include "providers/ipa/ipa_hbac_private.h"
+#include "providers/ldap/sdap_async.h"
+
+struct ipa_hbac_host_state {
+ struct tevent_context *ev;
+ struct sysdb_ctx *sysdb;
+ struct sss_domain_info *dom;
+ struct sdap_handle *sh;
+ struct sdap_options *opts;
+ const char *search_base;
+ const char **attrs;
+
+ /* Return values */
+ size_t host_count;
+ struct sysdb_attrs **hosts;
+
+ size_t hostgroup_count;
+ struct sysdb_attrs **hostgroups;
+};
+
+static void
+ipa_hbac_host_info_done(struct tevent_req *subreq);
+
+static void
+ipa_hbac_hostgroup_info_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ipa_hbac_host_info_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *dom,
+ struct sdap_handle *sh,
+ struct sdap_options *opts,
+ const char *search_base)
+{
+ errno_t ret;
+ struct ipa_hbac_host_state *state;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ char *host_filter;
+
+ req = tevent_req_create(mem_ctx, &state, struct ipa_hbac_host_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->sysdb = sysdb;
+ state->dom = dom;
+ state->sh = sh;
+ state->opts = opts;
+ state->search_base = search_base;
+
+ host_filter = talloc_asprintf(state, "(objectClass=%s)", IPA_HOST);
+ if (host_filter == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ state->attrs = talloc_array(state, const char *, 8);
+ if (state->attrs == NULL) {
+ DEBUG(1, ("Failed to allocate host attribute list.\n"));
+ ret = ENOMEM;
+ goto immediate;
+ }
+ state->attrs[0] = "objectClass";
+ state->attrs[1] = IPA_HOST_SERVERHOSTNAME;
+ state->attrs[2] = IPA_HOST_FQDN;
+ state->attrs[3] = IPA_UNIQUE_ID;
+ state->attrs[4] = IPA_MEMBER;
+ state->attrs[5] = IPA_MEMBEROF;
+ state->attrs[6] = IPA_CN;
+ state->attrs[7] = NULL;
+
+ subreq = sdap_get_generic_send(state, ev, opts, sh, search_base,
+ LDAP_SCOPE_SUB, host_filter,
+ state->attrs, NULL, 0,
+ dp_opt_get_int(opts->basic,
+ SDAP_ENUM_SEARCH_TIMEOUT));
+ if (subreq == NULL) {
+ DEBUG(1, ("Error requesting host info\n"));
+ ret = EIO;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, ipa_hbac_host_info_done, req);
+
+ return req;
+
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void
+ipa_hbac_host_info_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct ipa_hbac_host_state *state =
+ tevent_req_data(req, struct ipa_hbac_host_state);
+ char *hostgroup_filter;
+
+ ret = sdap_get_generic_recv(subreq, state,
+ &state->host_count,
+ &state->hosts);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (state->host_count == 0) {
+ tevent_req_error(req, ENOENT);
+ return;
+ }
+
+ ret = replace_attribute_name(IPA_MEMBEROF, SYSDB_ORIG_MEMBEROF,
+ state->host_count,
+ state->hosts);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not replace attribute names\n"));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ hostgroup_filter = talloc_asprintf(state, "(objectClass=%s)",
+ IPA_HOSTGROUP);
+ if (hostgroup_filter == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ /* Look up host groups */
+ subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh,
+ state->search_base, LDAP_SCOPE_SUB,
+ hostgroup_filter, state->attrs, NULL, 0,
+ dp_opt_get_int(state->opts->basic,
+ SDAP_ENUM_SEARCH_TIMEOUT));
+ if (subreq == NULL) {
+ DEBUG(1, ("Error requesting host info\n"));
+ tevent_req_error(req, EIO);
+ return;
+ }
+ tevent_req_set_callback(subreq, ipa_hbac_hostgroup_info_done, req);
+}
+
+static void
+ipa_hbac_hostgroup_info_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct ipa_hbac_host_state *state =
+ tevent_req_data(req, struct ipa_hbac_host_state);
+
+ ret = sdap_get_generic_recv(subreq, state,
+ &state->hostgroup_count,
+ &state->hostgroups);
+ talloc_zfree(subreq);
+ if (ret != EOK) goto done;
+
+ ret = replace_attribute_name(IPA_MEMBER, SYSDB_ORIG_MEMBER,
+ state->hostgroup_count,
+ state->hostgroups);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not replace attribute names\n"));
+ goto done;
+ }
+
+ ret = replace_attribute_name(IPA_MEMBEROF, SYSDB_ORIG_MEMBEROF,
+ state->hostgroup_count,
+ state->hostgroups);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not replace attribute names\n"));
+ goto done;
+ }
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ DEBUG(3, ("Error [%d][%s]\n", ret, strerror(ret)));
+ tevent_req_error(req, ret);
+ }
+}
+
+errno_t
+ipa_hbac_host_info_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ size_t *host_count,
+ struct sysdb_attrs ***hosts,
+ size_t *hostgroup_count,
+ struct sysdb_attrs ***hostgroups)
+{
+ size_t c;
+ struct ipa_hbac_host_state *state =
+ tevent_req_data(req, struct ipa_hbac_host_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *host_count = state->host_count;
+ *hosts = talloc_steal(mem_ctx, state->hosts);
+ for (c = 0; c < state->host_count; c++) {
+ /* Guarantee the memory heirarchy of the list */
+ talloc_steal(state->hosts, state->hosts[c]);
+ }
+
+ *hostgroup_count = state->hostgroup_count;
+ *hostgroups = talloc_steal(mem_ctx, state->hostgroups);
+
+ return EOK;
+}
+
+/*
+ * Functions to convert sysdb_attrs to the hbac_rule format
+ */
+static errno_t hbac_host_attrs_to_rule(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ const char *rule_name,
+ struct sysdb_attrs *rule_attrs,
+ const char *category_attr,
+ const char *member_attr,
+ size_t *host_count,
+ struct hbac_rule_element **hosts)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ struct hbac_rule_element *new_hosts;
+ const char *attrs[] = { IPA_HOST_FQDN, NULL };
+ struct ldb_message_element *el;
+ size_t num_hosts = 0;
+ size_t num_hostgroups = 0;
+ size_t i;
+ char *member_dn;
+ char *filter;
+ size_t count;
+ struct ldb_message **msgs;
+ const char *name;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) return ENOMEM;
+
+ new_hosts = talloc_zero(tmp_ctx, struct hbac_rule_element);
+ if (new_hosts == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* First check for host category */
+ ret = hbac_get_category(rule_attrs, category_attr, &new_hosts->category);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not identify host categories\n"));
+ goto done;
+ }
+ if (new_hosts->category & HBAC_CATEGORY_ALL) {
+ /* Short-cut to the exit */
+ ret = EOK;
+ goto done;
+ }
+
+ /* Get the list of DNs from the member_attr */
+ ret = sysdb_attrs_get_el(rule_attrs, member_attr, &el);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(1, ("sysdb_attrs_get_el failed.\n"));
+ goto done;
+ }
+ if (ret == ENOENT || el->num_values == 0) {
+ el->num_values = 0;
+ DEBUG(4, ("No host specified, rule will never apply.\n"));
+ }
+
+ /* Assume maximum size; We'll trim it later */
+ new_hosts->names = talloc_array(new_hosts,
+ const char *,
+ el->num_values +1);
+ if (new_hosts->names == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ new_hosts->groups = talloc_array(new_hosts,
+ const char *,
+ el->num_values + 1);
+ if (new_hosts->groups == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < el->num_values; i++) {
+ ret = sss_filter_sanitize(tmp_ctx,
+ (const char *)el->values[i].data,
+ &member_dn);
+ if (ret != EOK) goto done;
+
+ filter = talloc_asprintf(member_dn, "(%s=%s)",
+ SYSDB_ORIG_DN, member_dn);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* First check if this is a specific host */
+ ret = sysdb_search_custom(tmp_ctx, sysdb, domain, filter,
+ HBAC_HOSTS_SUBDIR, attrs,
+ &count, &msgs);
+ if (ret != EOK && ret != ENOENT) goto done;
+ if (ret == EOK && count == 0) {
+ ret = ENOENT;
+ }
+
+ if (ret == EOK) {
+ if (count > 1) {
+ DEBUG(1, ("Original DN matched multiple hosts. Skipping \n"));
+ talloc_zfree(member_dn);
+ continue;
+ }
+
+ /* Original DN matched a single host. Get the hostname */
+ name = ldb_msg_find_attr_as_string(msgs[0],
+ IPA_HOST_FQDN,
+ NULL);
+ if (name == NULL) {
+ DEBUG(1, ("Attribute is missing!\n"));
+ ret = EFAULT;
+ goto done;
+ }
+
+ new_hosts->names[num_hosts] = talloc_strdup(new_hosts->names,
+ name);
+ if (new_hosts->names[num_hosts] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(8, ("Added host [%s] to rule [%s]\n",
+ name, rule_name));
+ num_hosts++;
+ } else { /* ret == ENOENT */
+ /* Check if this is a hostgroup */
+ ret = sysdb_search_custom(tmp_ctx, sysdb, domain, filter,
+ HBAC_HOSTGROUPS_SUBDIR, attrs,
+ &count, &msgs);
+ if (ret != EOK && ret != ENOENT) goto done;
+ if (ret == EOK && count == 0) {
+ ret = ENOENT;
+ }
+
+ if (ret == EOK) {
+ if (count > 1) {
+ DEBUG(1, ("Original DN matched multiple hostgroups. "
+ "Skipping\n"));
+ talloc_zfree(member_dn);
+ continue;
+ }
+
+ /* Original DN matched a single group. Get the groupname */
+ name = ldb_msg_find_attr_as_string(msgs[0], SYSDB_NAME, NULL);
+ if (name == NULL) {
+ DEBUG(1, ("Attribute is missing!\n"));
+ ret = EFAULT;
+ goto done;
+ }
+
+ new_hosts->groups[num_hostgroups] =
+ talloc_strdup(new_hosts->groups, name);
+ if (new_hosts->groups[num_hostgroups] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(8, ("Added hostgroup [%s] to rule [%s]\n",
+ name, rule_name));
+ num_hostgroups++;
+ } else { /* ret == ENOENT */
+ /* Neither a host nor a hostgroup? Skip it */
+ DEBUG(1, ("[%s] does not map to either a host or hostgroup. "
+ "Skipping\n", member_dn));
+ }
+ }
+ talloc_zfree(member_dn);
+ }
+ new_hosts->names[num_hosts] = NULL;
+ new_hosts->groups[num_hostgroups] = NULL;
+
+ /* Shrink the arrays down to their real sizes */
+ new_hosts->names = talloc_realloc(new_hosts, new_hosts->names,
+ const char *, num_hosts + 1);
+ if (new_hosts->names == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ new_hosts->groups = talloc_realloc(new_hosts, new_hosts->groups,
+ const char *, num_hostgroups + 1);
+ if (new_hosts->groups == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ *hosts = talloc_steal(mem_ctx, new_hosts);
+ if (host_count) *host_count = num_hosts;
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+hbac_thost_attrs_to_rule(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ const char *rule_name,
+ struct sysdb_attrs *rule_attrs,
+ struct hbac_rule_element **thosts)
+{
+ DEBUG(7, ("Processing target hosts for rule [%s]\n", rule_name));
+
+ return hbac_host_attrs_to_rule(mem_ctx, sysdb, domain,
+ rule_name, rule_attrs,
+ IPA_HOST_CATEGORY, IPA_MEMBER_HOST,
+ NULL, thosts);
+}
+
+errno_t
+hbac_shost_attrs_to_rule(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ const char *rule_name,
+ struct sysdb_attrs *rule_attrs,
+ struct hbac_rule_element **source_hosts)
+{
+ errno_t ret;
+ size_t host_count;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ size_t idx;
+ struct ldb_message_element *el;
+ struct hbac_rule_element *shosts;
+
+ DEBUG(7, ("Processing source hosts for rule [%s]\n", rule_name));
+
+ ret = hbac_host_attrs_to_rule(tmp_ctx, sysdb, domain,
+ rule_name, rule_attrs,
+ IPA_SOURCE_HOST_CATEGORY, IPA_SOURCE_HOST,
+ &host_count, &shosts);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (shosts->category & HBAC_CATEGORY_ALL) {
+ /* All hosts (including external) are
+ * allowed.
+ */
+ goto done;
+ }
+
+ /* Include external (non-IPA-managed) source hosts */
+ ret = sysdb_attrs_get_el(rule_attrs, IPA_EXTERNAL_HOST, &el);
+ if (ret != EOK && ret != ENOENT) goto done;
+ if (ret == EOK && el->num_values == 0) ret = ENOENT;
+
+ if (ret != ENOENT) {
+ shosts->names = talloc_realloc(shosts, shosts->names, const char *,
+ host_count + el->num_values + 1);
+ if (shosts->names == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (idx = host_count; idx <= host_count + el->num_values; idx++) {
+ shosts->names[idx] =
+ talloc_strdup(shosts->names,
+ (const char *)el->values[idx].data);
+ if (shosts->names[idx] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(8, ("Added external source host [%s] to rule [%s]\n",
+ shosts->names[idx], rule_name));
+ }
+ shosts->names[idx] = NULL;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ *source_hosts = talloc_steal(mem_ctx, shosts);
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
diff --git a/src/providers/ipa/ipa_hbac_private.h b/src/providers/ipa/ipa_hbac_private.h
new file mode 100644
index 000000000..7289a0422
--- /dev/null
+++ b/src/providers/ipa/ipa_hbac_private.h
@@ -0,0 +1,194 @@
+/*
+ SSSD
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2011 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef IPA_HBAC_PRIVATE_H_
+#define IPA_HBAC_PRIVATE_H_
+
+#include "providers/ipa/ipa_access.h"
+#include "providers/ipa/ipa_hbac.h"
+
+#define IPA_HBAC_RULE "ipaHBACRule"
+
+#define IPA_HOST "ipaHost"
+#define IPA_HOSTGROUP "ipaHostGroup"
+
+#define IPA_HBAC_SERVICE "ipaHBACService"
+#define IPA_HBAC_SERVICE_GROUP "ipaHBACServiceGroup"
+
+#define IPA_HOST_SERVERHOSTNAME "serverHostName"
+#define IPA_HOST_FQDN "fqdn"
+#define IPA_UNIQUE_ID "ipauniqueid"
+
+#define IPA_MEMBER "member"
+#define SYSDB_ORIG_MEMBER "orig_member"
+#define HBAC_HOSTS_SUBDIR "hbac_hosts"
+#define HBAC_HOSTGROUPS_SUBDIR "hbac_hostgroups"
+
+#define OBJECTCLASS "objectclass"
+#define IPA_MEMBEROF "memberOf"
+#define IPA_ACCESS_RULE_TYPE "accessRuleType"
+#define IPA_HBAC_ALLOW "allow"
+#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_ENABLED_FLAG "ipaenabledflag"
+#define IPA_MEMBER_HOST "memberHost"
+#define IPA_HOST_CATEGORY "hostCategory"
+#define IPA_CN "cn"
+#define IPA_MEMBER_SERVICE "memberService"
+#define IPA_SERVICE_CATEGORY "serviceCategory"
+#define IPA_TRUE_VALUE "TRUE"
+
+#define IPA_HOST_BASE_TMPL "cn=computers,cn=accounts,%s"
+#define IPA_HBAC_BASE_TMPL "cn=hbac,%s"
+#define IPA_SERVICES_BASE_TMPL "cn=hbacservices,cn=accounts,%s"
+
+#define SYSDB_HBAC_BASE_TMPL "cn=hbac,"SYSDB_TMPL_CUSTOM_BASE
+
+#define HBAC_RULES_SUBDIR "hbac_rules"
+#define HBAC_SERVICES_SUBDIR "hbac_services"
+#define HBAC_SERVICEGROUPS_SUBDIR "hbac_servicegroups"
+
+/* From ipa_hbac_common.c */
+errno_t ipa_hbac_save_list(struct sysdb_ctx *sysdb, bool delete_subdir,
+ const char *subdir, struct sss_domain_info *domain,
+ const char *naming_attribute, size_t count,
+ struct sysdb_attrs **list);
+errno_t
+ipa_hbac_sysdb_save(struct sysdb_ctx *sysdb, struct sss_domain_info *domain,
+ const char *primary_subdir, const char *attr_name,
+ size_t primary_count, struct sysdb_attrs **primary,
+ const char *group_subdir, const char *groupattr_name,
+ size_t group_count, struct sysdb_attrs **groups);
+
+errno_t
+replace_attribute_name(const char *old_name,
+ const char *new_name, const size_t count,
+ struct sysdb_attrs **list);
+
+errno_t hbac_ctx_to_rules(TALLOC_CTX *mem_ctx,
+ struct hbac_ctx *hbac_ctx,
+ struct hbac_rule ***rules,
+ struct hbac_eval_req **request);
+
+errno_t
+hbac_get_category(struct sysdb_attrs *attrs,
+ const char *category_attr,
+ uint32_t *_categories);
+
+/* From ipa_hbac_hosts.c */
+struct tevent_req *
+ipa_hbac_host_info_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *dom,
+ struct sdap_handle *sh,
+ struct sdap_options *opts,
+ const char *search_base);
+
+errno_t
+ipa_hbac_host_info_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ size_t *host_count,
+ struct sysdb_attrs ***hosts,
+ size_t *hostgroup_count,
+ struct sysdb_attrs ***hostgroups);
+
+errno_t
+hbac_thost_attrs_to_rule(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ const char *rule_name,
+ struct sysdb_attrs *rule_attrs,
+ struct hbac_rule_element **thosts);
+
+errno_t
+hbac_shost_attrs_to_rule(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ const char *rule_name,
+ struct sysdb_attrs *rule_attrs,
+ struct hbac_rule_element **source_hosts);
+
+/* From ipa_hbac_services.c */
+struct tevent_req *
+ipa_hbac_service_info_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *dom,
+ struct sdap_handle *sh,
+ struct sdap_options *opts,
+ const char *search_base);
+
+errno_t
+ipa_hbac_service_info_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ size_t *service_count,
+ struct sysdb_attrs ***services,
+ size_t *servicegroup_count,
+ struct sysdb_attrs ***servicegroups);
+
+errno_t
+hbac_service_attrs_to_rule(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ const char *rule_name,
+ struct sysdb_attrs *rule_attrs,
+ struct hbac_rule_element **services);
+
+/* From ipa_hbac_rules.c */
+struct tevent_req *
+ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx,
+ bool get_deny_rules,
+ struct tevent_context *ev,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *dom,
+ struct sdap_handle *sh,
+ struct sdap_options *opts,
+ const char *search_base,
+ struct sysdb_attrs *ipa_host);
+
+errno_t
+ipa_hbac_rule_info_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ size_t *rule_count,
+ struct sysdb_attrs ***rules);
+
+/* From ipa_hbac_users.c */
+errno_t
+hbac_user_attrs_to_rule(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ const char *rule_name,
+ struct sysdb_attrs *rule_attrs,
+ struct hbac_rule_element **users);
+
+errno_t
+get_ipa_groupname(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ const char *group_dn,
+ const char **groupname);
+
+#endif /* IPA_HBAC_PRIVATE_H_ */
diff --git a/src/providers/ipa/ipa_hbac_rules.c b/src/providers/ipa/ipa_hbac_rules.c
new file mode 100644
index 000000000..43e1e4263
--- /dev/null
+++ b/src/providers/ipa/ipa_hbac_rules.c
@@ -0,0 +1,231 @@
+/*
+ SSSD
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2011 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "util/util.h"
+#include "providers/ipa/ipa_hbac_private.h"
+#include "providers/ldap/sdap_async.h"
+
+struct ipa_hbac_rule_state {
+ struct sdap_options *opts;
+
+ size_t rule_count;
+ struct sysdb_attrs **rules;
+};
+
+static void
+ipa_hbac_rule_info_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx,
+ bool get_deny_rules,
+ struct tevent_context *ev,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *dom,
+ struct sdap_handle *sh,
+ struct sdap_options *opts,
+ const char *search_base,
+ struct sysdb_attrs *ipa_host)
+{
+ errno_t ret;
+ size_t i;
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq;
+ struct ipa_hbac_rule_state *state;
+ TALLOC_CTX *tmp_ctx;
+ const char *host_dn;
+ char *host_dn_clean;
+ char *host_group_clean;
+ char *rule_filter;
+ const char **memberof_list;
+ const char *rule_attrs[] = { OBJECTCLASS,
+ IPA_CN,
+ IPA_UNIQUE_ID,
+ IPA_ENABLED_FLAG,
+ IPA_ACCESS_RULE_TYPE,
+ IPA_MEMBER_USER,
+ IPA_USER_CATEGORY,
+ IPA_MEMBER_SERVICE,
+ IPA_SERVICE_CATEGORY,
+ IPA_SOURCE_HOST,
+ IPA_SOURCE_HOST_CATEGORY,
+ IPA_EXTERNAL_HOST,
+ IPA_MEMBER_HOST,
+ IPA_HOST_CATEGORY,
+ NULL };
+
+ if (ipa_host == NULL) {
+ DEBUG(1, ("Missing host\n"));
+ return NULL;
+ }
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) return NULL;
+
+ ret = sysdb_attrs_get_string(ipa_host, SYSDB_ORIG_DN, &host_dn);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not identify IPA hostname\n"));
+ goto error;
+ }
+
+ ret = sss_filter_sanitize(tmp_ctx, host_dn, &host_dn_clean);
+ if (ret != EOK) goto error;
+
+ req = tevent_req_create(mem_ctx, &state, struct ipa_hbac_rule_state);
+ if (req == NULL) {
+ DEBUG(1, ("tevent_req_create failed.\n"));
+ return NULL;
+ }
+
+ state->opts = opts;
+
+ if (get_deny_rules) {
+ rule_filter = talloc_asprintf(tmp_ctx,
+ "(&(objectclass=%s)"
+ "(%s=%s)(|(%s=%s)(%s=%s)",
+ IPA_HBAC_RULE,
+ IPA_ENABLED_FLAG, IPA_TRUE_VALUE,
+ IPA_HOST_CATEGORY, "all",
+ IPA_MEMBER_HOST, host_dn_clean);
+ } else {
+ rule_filter = talloc_asprintf(tmp_ctx,
+ "(&(objectclass=%s)"
+ "(%s=%s)(%s=%s)"
+ "(|(%s=%s)(%s=%s)",
+ IPA_HBAC_RULE,
+ IPA_ENABLED_FLAG, IPA_TRUE_VALUE,
+ IPA_ACCESS_RULE_TYPE, IPA_HBAC_ALLOW,
+ IPA_HOST_CATEGORY, "all",
+ IPA_MEMBER_HOST, host_dn_clean);
+ }
+ if (rule_filter == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ /* Add all parent groups of ipa_hostname to the filter */
+ ret = sysdb_attrs_get_string_array(ipa_host, SYSDB_ORIG_MEMBEROF,
+ tmp_ctx, &memberof_list);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(1, ("Could not identify "))
+ } if (ret == ENOENT) {
+ /* This host is not a member of any hostgroups */
+ memberof_list = talloc_array(tmp_ctx, const char *, 1);
+ if (memberof_list == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ memberof_list[0] = NULL;
+ }
+
+ for (i = 0; memberof_list[i]; i++) {
+ ret = sss_filter_sanitize(tmp_ctx,
+ memberof_list[i],
+ &host_group_clean);
+ if (ret != EOK) goto immediate;
+
+ rule_filter = talloc_asprintf_append(rule_filter, "(%s=%s)",
+ IPA_MEMBER_HOST,
+ host_group_clean);
+ if (rule_filter == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ }
+
+ rule_filter = talloc_asprintf_append(rule_filter, "))");
+ if (rule_filter == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ talloc_steal(state, rule_filter);
+
+ subreq = sdap_get_generic_send(state, ev, opts, sh, search_base,
+ LDAP_SCOPE_SUB, rule_filter, rule_attrs,
+ NULL, 0,
+ dp_opt_get_int(state->opts->basic,
+ SDAP_ENUM_SEARCH_TIMEOUT));
+ if (subreq == NULL) {
+ DEBUG(1, ("sdap_get_generic_send failed.\n"));
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, ipa_hbac_rule_info_done, req);
+
+ talloc_free(tmp_ctx);
+ return req;
+
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ talloc_free(tmp_ctx);
+ return req;
+
+error:
+ talloc_free(tmp_ctx);
+ return NULL;
+}
+
+static void
+ipa_hbac_rule_info_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct ipa_hbac_rule_state *state =
+ tevent_req_data(req, struct ipa_hbac_rule_state);
+
+ ret = sdap_get_generic_recv(subreq, state,
+ &state->rule_count,
+ &state->rules);
+ if (ret != EOK) {
+ DEBUG(3, ("Could not retrieve HBAC rules\n"));
+ tevent_req_error(req, ret);
+ return;
+ } else if (state->rule_count == 0) {
+ DEBUG(3, ("No rules apply to this host\n"));
+ tevent_req_error(req, ENOENT);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t
+ipa_hbac_rule_info_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ size_t *rule_count,
+ struct sysdb_attrs ***rules)
+{
+ struct ipa_hbac_rule_state *state =
+ tevent_req_data(req, struct ipa_hbac_rule_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *rule_count = state->rule_count;
+ *rules = talloc_steal(mem_ctx, state->rules);
+
+ return EOK;
+}
diff --git a/src/providers/ipa/ipa_hbac_services.c b/src/providers/ipa/ipa_hbac_services.c
new file mode 100644
index 000000000..df276b86c
--- /dev/null
+++ b/src/providers/ipa/ipa_hbac_services.c
@@ -0,0 +1,451 @@
+/*
+ SSSD
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2011 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "util/util.h"
+#include "providers/ipa/ipa_hbac_private.h"
+#include "providers/ldap/sdap_async.h"
+
+struct ipa_hbac_service_state {
+ struct tevent_context *ev;
+ struct sysdb_ctx *sysdb;
+ struct sss_domain_info *dom;
+ struct sdap_handle *sh;
+ struct sdap_options *opts;
+ const char *search_base;
+ const char **attrs;
+
+ /* Return values */
+ size_t service_count;
+ struct sysdb_attrs **services;
+
+ size_t servicegroup_count;
+ struct sysdb_attrs **servicegroups;
+};
+
+static void
+ipa_hbac_service_info_done(struct tevent_req *subreq);
+static void
+ipa_hbac_servicegroup_info_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ipa_hbac_service_info_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *dom,
+ struct sdap_handle *sh,
+ struct sdap_options *opts,
+ const char *search_base)
+{
+ errno_t ret;
+ struct ipa_hbac_service_state *state;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ char *service_filter;
+
+ req = tevent_req_create(mem_ctx, &state, struct ipa_hbac_service_state);
+ if (req == NULL) {
+ DEBUG(1, ("tevent_req_create failed.\n"));
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->sysdb = sysdb;
+ state->dom = dom;
+ state->sh = sh;
+ state->opts = opts;
+ state->search_base = search_base;
+
+ service_filter = talloc_asprintf(state, "(objectClass=%s)",
+ IPA_HBAC_SERVICE);
+ if (service_filter == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ state->attrs = talloc_array(state, const char *, 6);
+ if (state->attrs == NULL) {
+ DEBUG(1, ("Failed to allocate service attribute list.\n"));
+ ret = ENOMEM;
+ goto immediate;
+ }
+ state->attrs[0] = OBJECTCLASS;
+ state->attrs[1] = IPA_CN;
+ state->attrs[2] = IPA_UNIQUE_ID;
+ state->attrs[3] = IPA_MEMBER;
+ state->attrs[4] = IPA_MEMBEROF;
+ state->attrs[5] = NULL;
+
+ subreq = sdap_get_generic_send(state, ev, opts, sh, search_base,
+ LDAP_SCOPE_SUB, service_filter,
+ state->attrs, NULL, 0,
+ dp_opt_get_int(opts->basic,
+ SDAP_ENUM_SEARCH_TIMEOUT));
+ if (subreq == NULL) {
+ DEBUG(1, ("Error requesting service info\n"));
+ ret = EIO;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, ipa_hbac_service_info_done, req);
+
+ return req;
+
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void
+ipa_hbac_service_info_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct ipa_hbac_service_state *state =
+ tevent_req_data(req, struct ipa_hbac_service_state);
+ char *servicegroup_filter;
+
+ ret = sdap_get_generic_recv(subreq, state,
+ &state->service_count,
+ &state->services);
+ talloc_zfree(subreq);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+
+ if (ret == ENOENT || state->service_count == 0) {
+ /* If there are no services, we'll shortcut out
+ * This is still valid, as rules can apply to
+ * all services
+ *
+ * There's no reason to try to process groups
+ */
+
+ state->service_count = 0;
+ state->services = NULL;
+ ret = EOK;
+ goto done;
+ }
+
+ ret = replace_attribute_name(IPA_MEMBEROF, SYSDB_ORIG_MEMBEROF,
+ state->service_count,
+ state->services);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not replace attribute names\n"));
+ goto done;
+ }
+
+ servicegroup_filter = talloc_asprintf(state, "(objectClass=%s)",
+ IPA_HBAC_SERVICE_GROUP);
+ if (servicegroup_filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Look up service groups */
+ subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh,
+ state->search_base, LDAP_SCOPE_SUB,
+ servicegroup_filter, state->attrs, NULL, 0,
+ dp_opt_get_int(state->opts->basic,
+ SDAP_ENUM_SEARCH_TIMEOUT));
+ if (subreq == NULL) {
+ DEBUG(1, ("Error requesting host info\n"));
+ ret = EIO;
+ goto done;
+ }
+ tevent_req_set_callback(subreq, ipa_hbac_servicegroup_info_done, req);
+
+ return;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+}
+
+static void
+ipa_hbac_servicegroup_info_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct ipa_hbac_service_state *state =
+ tevent_req_data(req, struct ipa_hbac_service_state);
+
+ ret = sdap_get_generic_recv(subreq, state,
+ &state->servicegroup_count,
+ &state->servicegroups);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = replace_attribute_name(IPA_MEMBER, SYSDB_ORIG_MEMBER,
+ state->servicegroup_count,
+ state->servicegroups);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not replace attribute names\n"));
+ goto done;
+ }
+
+ ret = replace_attribute_name(IPA_MEMBEROF, SYSDB_ORIG_MEMBEROF,
+ state->servicegroup_count,
+ state->servicegroups);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not replace attribute names\n"));
+ goto done;
+ }
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ DEBUG(3, ("Error [%d][%s]\n", ret, strerror(ret)));
+ tevent_req_error(req, ret);
+ }
+}
+
+errno_t
+ipa_hbac_service_info_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ size_t *service_count,
+ struct sysdb_attrs ***services,
+ size_t *servicegroup_count,
+ struct sysdb_attrs ***servicegroups)
+{
+ size_t c;
+ struct ipa_hbac_service_state *state =
+ tevent_req_data(req, struct ipa_hbac_service_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *service_count = state->service_count;
+ *services = talloc_steal(mem_ctx, state->services);
+ for (c = 0; c < state->service_count; c++) {
+ /* Guarantee the memory heirarchy of the list */
+ talloc_steal(state->services, state->services[c]);
+ }
+
+ *servicegroup_count = state->servicegroup_count;
+ *servicegroups = talloc_steal(mem_ctx, state->servicegroups);
+
+ return EOK;
+}
+
+errno_t
+hbac_service_attrs_to_rule(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ const char *rule_name,
+ struct sysdb_attrs *rule_attrs,
+ struct hbac_rule_element **services)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ struct hbac_rule_element *new_services;
+ const char *attrs[] = { IPA_CN, NULL };
+ struct ldb_message_element *el;
+ size_t num_services = 0;
+ size_t num_servicegroups = 0;
+ size_t i;
+ char *member_dn;
+ char *filter;
+ size_t count;
+ struct ldb_message **msgs;
+ const char *name;
+
+ DEBUG(7, ("Processing PAM services for rule [%s]\n", rule_name));
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) return ENOMEM;
+
+ new_services = talloc_zero(tmp_ctx, struct hbac_rule_element);
+ if (new_services == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* First check for service category */
+ ret = hbac_get_category(rule_attrs, IPA_SERVICE_CATEGORY,
+ &new_services->category);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not identify service categories\n"));
+ goto done;
+ }
+ if (new_services->category & HBAC_CATEGORY_ALL) {
+ /* Short-cut to the exit */
+ ret = EOK;
+ goto done;
+ }
+
+ /* Get the list of DNs from the member attr */
+ ret = sysdb_attrs_get_el(rule_attrs, IPA_MEMBER_SERVICE, &el);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(1, ("sysdb_attrs_get_el failed.\n"));
+ goto done;
+ }
+ if (ret == ENOENT || el->num_values == 0) {
+ el->num_values = 0;
+ DEBUG(4, ("No services specified, rule will never apply.\n"));
+ }
+
+ /* Assume maximum size; We'll trim it later */
+ new_services->names = talloc_array(new_services,
+ const char *,
+ el->num_values +1);
+ if (new_services->names == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ new_services->groups = talloc_array(new_services,
+ const char *,
+ el->num_values + 1);
+ if (new_services->groups == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < el->num_values; i++) {
+ ret = sss_filter_sanitize(tmp_ctx,
+ (const char *)el->values[i].data,
+ &member_dn);
+ if (ret != EOK) goto done;
+
+ filter = talloc_asprintf(member_dn, "(%s=%s)",
+ SYSDB_ORIG_DN, member_dn);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* First check if this is a specific service */
+ ret = sysdb_search_custom(tmp_ctx, sysdb, domain, filter,
+ HBAC_SERVICES_SUBDIR, attrs,
+ &count, &msgs);
+ if (ret != EOK && ret != ENOENT) goto done;
+ if (ret == EOK && count == 0) {
+ ret = ENOENT;
+ }
+
+ if (ret == EOK) {
+ if (count > 1) {
+ DEBUG(1, ("Original DN matched multiple services. "
+ "Skipping \n"));
+ talloc_zfree(member_dn);
+ continue;
+ }
+
+ /* Original DN matched a single service. Get the service name */
+ name = ldb_msg_find_attr_as_string(msgs[0], IPA_CN, NULL);
+ if (name == NULL) {
+ DEBUG(1, ("Attribute is missing!\n"));
+ ret = EFAULT;
+ goto done;
+ }
+
+ new_services->names[num_services] =
+ talloc_strdup(new_services->names, name);
+ if (new_services->names[num_services] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(8, ("Added service [%s] to rule [%s]\n",
+ name, rule_name));
+ num_services++;
+ } else { /* ret == ENOENT */
+ /* Check if this is a service group */
+ ret = sysdb_search_custom(tmp_ctx, sysdb, domain, filter,
+ HBAC_SERVICEGROUPS_SUBDIR, attrs,
+ &count, &msgs);
+ if (ret != EOK && ret != ENOENT) goto done;
+ if (ret == EOK && count == 0) {
+ ret = ENOENT;
+ }
+
+ if (ret == EOK) {
+ if (count > 1) {
+ DEBUG(1, ("Original DN matched multiple service groups. "
+ "Skipping\n"));
+ talloc_zfree(member_dn);
+ continue;
+ }
+
+ /* Original DN matched a single group. Get the groupname */
+ name = ldb_msg_find_attr_as_string(msgs[0], IPA_CN, NULL);
+ if (name == NULL) {
+ DEBUG(1, ("Attribute is missing!\n"));
+ ret = EFAULT;
+ goto done;
+ }
+
+ new_services->groups[num_servicegroups] =
+ talloc_strdup(new_services->groups, name);
+ if (new_services->groups[num_servicegroups] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(8, ("Added service group [%s] to rule [%s]\n",
+ name, rule_name));
+ num_servicegroups++;
+ } else { /* ret == ENOENT */
+ /* Neither a service nor a service group? Skip it */
+ DEBUG(1, ("[%s] does not map to either a service or "
+ "service group. Skipping\n", member_dn));
+ }
+ }
+ talloc_zfree(member_dn);
+ }
+ new_services->names[num_services] = NULL;
+ new_services->groups[num_servicegroups] = NULL;
+
+ /* Shrink the arrays down to their real sizes */
+ new_services->names = talloc_realloc(new_services, new_services->names,
+ const char *, num_services + 1);
+ if (new_services->names == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ new_services->groups = talloc_realloc(new_services, new_services->groups,
+ const char *, num_servicegroups + 1);
+ if (new_services->groups == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ *services = talloc_steal(mem_ctx, new_services);
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
diff --git a/src/providers/ipa/ipa_hbac_users.c b/src/providers/ipa/ipa_hbac_users.c
new file mode 100644
index 000000000..9b7cadb2e
--- /dev/null
+++ b/src/providers/ipa/ipa_hbac_users.c
@@ -0,0 +1,345 @@
+/*
+ SSSD
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2011 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "util/util.h"
+#include "providers/ipa/ipa_hbac_private.h"
+#include "providers/ldap/sdap_async.h"
+
+struct hbac_update_groups_state {
+ struct hbac_ctx *hbac_ctx;
+ struct sysdb_ctx *sysdb;
+ struct sss_domain_info *domain;
+};
+
+
+/* Returns EOK and populates groupname if
+ * the group_dn is actually a group.
+ * Returns ENOENT if group_dn does not point
+ * at a a group.
+ * Returns EINVAL if there is a parsing error.
+ * Returns ENOMEM as appropriate
+ */
+errno_t
+get_ipa_groupname(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ const char *group_dn,
+ const char **groupname)
+{
+ errno_t ret;
+ struct ldb_dn *dn;
+ const char *rdn_name;
+ const char *group_comp_name;
+ const char *account_comp_name;
+ const struct ldb_val *rdn_val;
+ const struct ldb_val *group_comp_val;
+ const struct ldb_val *account_comp_val;
+
+ /* This is an IPA-specific hack. It may not
+ * work for non-IPA servers and will need to
+ * be changed if SSSD ever supports HBAC on
+ * a non-IPA server.
+ */
+ *groupname = NULL;
+
+ dn = ldb_dn_new(mem_ctx, sysdb_ctx_get_ldb(sysdb), group_dn);
+ if (dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (!ldb_dn_validate(dn)) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (ldb_dn_get_comp_num(dn) < 4) {
+ /* RDN, groups, accounts, and at least one DC= */
+ ret = EINVAL;
+ goto done;
+ }
+
+ /* If the RDN name is 'cn' */
+ rdn_name = ldb_dn_get_rdn_name(dn);
+ if (rdn_name == NULL) {
+ /* Shouldn't happen if ldb_dn_validate()
+ * passed, but we'll be careful.
+ */
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (strcasecmp("cn", rdn_name) != 0) {
+ /* RDN has the wrong attribute name.
+ * It's not a group.
+ */
+ ret = ENOENT;
+ goto done;
+ }
+
+ /* and the second component is "cn=groups" */
+ group_comp_name = ldb_dn_get_component_name(dn, 1);
+ if (strcasecmp("cn", group_comp_name) != 0) {
+ /* The second component name is not "cn" */
+ ret = ENOENT;
+ goto done;
+ }
+
+ group_comp_val = ldb_dn_get_component_val(dn, 1);
+ if (strncasecmp("groups",
+ (const char *) group_comp_val->data,
+ group_comp_val->length) != 0) {
+ /* The second component value is not "groups" */
+ ret = ENOENT;
+ goto done;
+ }
+
+ /* and the third component is "accounts" */
+ account_comp_name = ldb_dn_get_component_name(dn, 2);
+ if (strcasecmp("cn", account_comp_name) != 0) {
+ /* The third component name is not "cn" */
+ ret = ENOENT;
+ goto done;
+ }
+
+ account_comp_val = ldb_dn_get_component_val(dn, 2);
+ if (strncasecmp("accounts",
+ (const char *) account_comp_val->data,
+ account_comp_val->length) != 0) {
+ /* The third component value is not "accounts" */
+ ret = ENOENT;
+ goto done;
+ }
+
+ /* Then the value of the RDN is the group name */
+ rdn_val = ldb_dn_get_rdn_val(dn);
+ *groupname = talloc_strndup(mem_ctx,
+ (const char *)rdn_val->data,
+ rdn_val->length);
+ if (*groupname == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(dn);
+ return ret;
+}
+
+errno_t
+hbac_user_attrs_to_rule(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct sss_domain_info *domain,
+ const char *rule_name,
+ struct sysdb_attrs *rule_attrs,
+ struct hbac_rule_element **users)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct hbac_rule_element *new_users = NULL;
+ struct ldb_message_element *el = NULL;
+ struct ldb_message **msgs = NULL;
+ char *filter;
+ char *member_dn;
+ const char *member_user;
+ const char *attrs[] = { SYSDB_NAME, NULL };
+ size_t num_users = 0;
+ size_t num_groups = 0;
+ const char *name;
+
+ size_t count;
+ size_t i;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) return ENOMEM;
+
+ new_users = talloc_zero(tmp_ctx, struct hbac_rule_element);
+ if (new_users == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(7, ("Processing users for rule [%s]\n", rule_name));
+
+ ret = hbac_get_category(rule_attrs, IPA_USER_CATEGORY,
+ &new_users->category);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not identify user categories\n"));
+ goto done;
+ }
+ if (new_users->category & HBAC_CATEGORY_ALL) {
+ /* Short-cut to the exit */
+ ret = EOK;
+ goto done;
+ }
+
+ ret = sysdb_attrs_get_el(rule_attrs, IPA_MEMBER_USER, &el);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(1, ("sysdb_attrs_get_el failed.\n"));
+ goto done;
+ }
+ if (ret == ENOENT || el->num_values == 0) {
+ el->num_values = 0;
+ DEBUG(4, ("No user specified, rule will never apply.\n"));
+ }
+
+ new_users->names = talloc_array(new_users,
+ const char *,
+ el->num_values + 1);
+ if (new_users->names == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ new_users->groups = talloc_array(new_users,
+ const char *,
+ el->num_values + 1);
+ if (new_users->groups == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < el->num_values; i++) {
+ member_user = (const char *)el->values[i].data;
+ ret = sss_filter_sanitize(tmp_ctx, member_user, &member_dn);
+ if (ret != EOK) goto done;
+
+ filter = talloc_asprintf(member_dn, "(%s=%s)",
+ SYSDB_ORIG_DN, member_dn);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* First check if this is a user */
+ ret = sysdb_search_users(tmp_ctx, sysdb, domain,
+ filter, attrs, &count, &msgs);
+ if (ret != EOK && ret != ENOENT) goto done;
+ if (ret == EOK && count == 0) {
+ ret = ENOENT;
+ }
+
+ if (ret == EOK) {
+ if (count > 1) {
+ DEBUG(1, ("Original DN matched multiple users. Skipping \n"));
+ talloc_zfree(member_dn);
+ continue;
+ }
+
+ /* Original DN matched a single user. Get the username */
+ name = ldb_msg_find_attr_as_string(msgs[0], SYSDB_NAME, NULL);
+ if (name == NULL) {
+ DEBUG(1, ("Attribute is missing!\n"));
+ ret = EFAULT;
+ goto done;
+ }
+
+ new_users->names[num_users] = talloc_strdup(new_users->names,
+ name);
+ if (new_users->names[num_users] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(8, ("Added user [%s] to rule [%s]\n",
+ name, rule_name));
+ num_users++;
+ } else {
+ /* Check if it is a group instead */
+ ret = sysdb_search_groups(tmp_ctx, sysdb, domain,
+ filter, attrs, &count, &msgs);
+ if (ret != EOK && ret != ENOENT) goto done;
+ if (ret == EOK && count == 0) {
+ ret = ENOENT;
+ }
+
+ if (ret == EOK) {
+ if (count > 1) {
+ DEBUG(1, ("Original DN matched multiple groups. "
+ "Skipping\n"));
+ talloc_zfree(member_dn);
+ continue;
+ }
+
+ /* Original DN matched a single group. Get the groupname */
+ name = ldb_msg_find_attr_as_string(msgs[0], SYSDB_NAME, NULL);
+ if (name == NULL) {
+ DEBUG(1, ("Attribute is missing!\n"));
+ ret = EFAULT;
+ goto done;
+ }
+
+ new_users->groups[num_groups] =
+ talloc_strdup(new_users->groups, name);
+ if (new_users->groups[num_groups] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(8, ("Added POSIX group [%s] to rule [%s]\n",
+ name, rule_name));
+ num_groups++;
+ } else {
+ /* If the group still matches the group pattern,
+ * we can assume it is a non-POSIX group.
+ */
+ ret = get_ipa_groupname(new_users->groups, sysdb, member_user,
+ &new_users->groups[num_groups]);
+ if (ret == EOK) {
+ DEBUG(8, ("Added non-POSIX group [%s] to rule [%s]\n",
+ new_users->groups[num_groups], rule_name));
+ num_groups++;
+ } else {
+ /* Not a group, so we don't care about it */
+ DEBUG(1, ("[%s] does not map to either a user or group. "
+ "Skipping\n", member_dn));
+ }
+ }
+ }
+ talloc_zfree(member_dn);
+ }
+ new_users->names[num_users] = NULL;
+ new_users->groups[num_groups] = NULL;
+
+ /* Shrink the arrays down to their real sizes */
+ new_users->names = talloc_realloc(new_users, new_users->names,
+ const char *, num_users + 1);
+ if (new_users->names == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ new_users->groups = talloc_realloc(new_users, new_users->groups,
+ const char *, num_groups + 1);
+ if (new_users->groups == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = EOK;
+done:
+ if (ret == EOK) {
+ *users = talloc_steal(mem_ctx, new_users);
+ }
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
index d2477bcf2..31b8139ae 100644
--- a/src/providers/ldap/ldap_common.c
+++ b/src/providers/ldap/ldap_common.c
@@ -1045,3 +1045,32 @@ bool sdap_is_secure_uri(const char *uri)
}
return false;
}
+
+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;
+}
diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
index 9146da5a9..70ffd1485 100644
--- a/src/providers/ldap/ldap_common.h
+++ b/src/providers/ldap/ldap_common.h
@@ -162,4 +162,8 @@ errno_t list_missing_attrs(TALLOC_CTX *mem_ctx,
bool sdap_is_secure_uri(const char *uri);
+errno_t msgs2attrs_array(TALLOC_CTX *mem_ctx, size_t count,
+ struct ldb_message **msgs,
+ struct sysdb_attrs ***attrs);
+
#endif /* _LDAP_COMMON_H_ */
diff --git a/src/tests/ipa_hbac-tests.c b/src/tests/ipa_hbac-tests.c
new file mode 100644
index 000000000..330e49e7c
--- /dev/null
+++ b/src/tests/ipa_hbac-tests.c
@@ -0,0 +1,850 @@
+/*
+ SSSD
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2011 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#include <stdlib.h>
+#include <check.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <talloc.h>
+
+#include "tests/common.h"
+#include "providers/ipa/ipa_hbac.h"
+
+#define HBAC_TEST_USER "testuser"
+#define HBAC_TEST_INVALID_USER "nosuchuser"
+
+#define HBAC_TEST_GROUP1 "testgroup1"
+#define HBAC_TEST_GROUP2 "testgroup2"
+#define HBAC_TEST_INVALID_GROUP "nosuchgroup"
+
+#define HBAC_TEST_SERVICE "testservice"
+#define HBAC_TEST_INVALID_SERVICE "nosuchservice"
+
+#define HBAC_TEST_SERVICEGROUP1 "login_services"
+#define HBAC_TEST_SERVICEGROUP2 "all_services"
+#define HBAC_TEST_INVALID_SERVICEGROUP "nosuchservicegroup"
+
+#define HBAC_TEST_SRCHOST "client.example.com"
+#define HBAC_TEST_INVALID_SRCHOST "nosuchsrchost"
+
+#define HBAC_TEST_SRCHOSTGROUP1 "site_hosts"
+#define HBAC_TEST_SRCHOSTGROUP2 "corp_hosts"
+#define HBAC_TEST_INVALID_SRCHOSTGROUP "nosuchsrchostgroup"
+
+
+/* These don't make sense for a user/group/service but they do the job and
+ * every one is from a different codepage */
+/* Latin Extended A - "Czech" */
+const uint8_t user_utf8_lowcase[] = { 0xC4, 0x8D, 'e', 'c', 'h', 0x0 };
+const uint8_t user_utf8_upcase[] = { 0xC4, 0x8C, 'e', 'c', 'h', 0x0 };
+const uint8_t user_utf8_lowcase_neg[] = { 0xC4, 0x8E, 'e', 'c', 'h', 0x0 };
+/* Latin 1 Supplement - "Munchen" */
+const uint8_t service_utf8_lowcase[] = { 'm', 0xC3, 0xBC, 'n', 'c', 'h', 'e', 'n', 0x0 };
+const uint8_t service_utf8_upcase[] = { 'M', 0xC3, 0x9C, 'N', 'C', 'H', 'E', 'N', 0x0 };
+/* Greek - "AlphaBetaGamma" */
+const uint8_t srchost_utf8_lowcase[] = { 0xCE, 0xB1, 0xCE, 0xB2, 0xCE, 0xB3, 0x0 };
+const uint8_t srchost_utf8_upcase[] = { 0xCE, 0x91, 0xCE, 0x92, 0xCE, 0x93, 0x0 };
+/* Turkish "capital I" and "dotless i" */
+const uint8_t user_lowcase_tr[] = { 0xC4, 0xB1, 0x0 };
+const uint8_t user_upcase_tr[] = { 0x49, 0x0 };
+
+static void get_allow_all_rule(TALLOC_CTX *mem_ctx,
+ struct hbac_rule **allow_rule)
+{
+ struct hbac_rule *rule;
+ /* Create a rule that ALLOWs all services, users and
+ * remote hosts.
+ */
+ rule = talloc_zero(mem_ctx, struct hbac_rule);
+ fail_if (rule == NULL);
+
+ rule->enabled = true;
+
+ rule->services = talloc_zero(rule, struct hbac_rule_element);
+ fail_if (rule->services == NULL);
+ rule->services->category = HBAC_CATEGORY_ALL;
+ rule->services->names = NULL;
+ rule->services->groups = NULL;
+
+ rule->users = talloc_zero(rule, struct hbac_rule_element);
+ fail_if (rule->users == NULL);
+ rule->users->category = HBAC_CATEGORY_ALL;
+ rule->users->names = NULL;
+ rule->users->groups = NULL;
+
+ rule->targethosts = talloc_zero(rule, struct hbac_rule_element);
+ fail_if (rule->targethosts == NULL);
+ rule->targethosts->category = HBAC_CATEGORY_ALL;
+ rule->targethosts->names = NULL;
+ rule->targethosts->groups = NULL;
+
+ rule->srchosts = talloc_zero(rule, struct hbac_rule_element);
+ fail_if (rule->srchosts == NULL);
+ rule->srchosts->category = HBAC_CATEGORY_ALL;
+ rule->srchosts->names = NULL;
+ rule->srchosts->groups = NULL;
+
+ *allow_rule = rule;
+}
+
+static void get_test_user(TALLOC_CTX *mem_ctx,
+ struct hbac_request_element **user)
+{
+ struct hbac_request_element *new_user;
+
+ new_user = talloc_zero(mem_ctx, struct hbac_request_element);
+ fail_if (new_user == NULL);
+
+ new_user->name = talloc_strdup(new_user, HBAC_TEST_USER);
+ fail_if(new_user->name == NULL);
+
+ new_user->groups = talloc_array(new_user, const char *, 3);
+ fail_if(new_user->groups == NULL);
+
+ new_user->groups[0] = talloc_strdup(new_user->groups, HBAC_TEST_GROUP1);
+ fail_if(new_user->groups[0] == NULL);
+
+ new_user->groups[1] = talloc_strdup(new_user->groups, HBAC_TEST_GROUP2);
+ fail_if(new_user->groups[1] == NULL);
+
+ new_user->groups[2] = NULL;
+
+ *user = new_user;
+}
+
+static void get_test_service(TALLOC_CTX *mem_ctx,
+ struct hbac_request_element **service)
+{
+ struct hbac_request_element *new_service;
+
+ new_service = talloc_zero(mem_ctx, struct hbac_request_element);
+ fail_if (new_service == NULL);
+
+ new_service->name = talloc_strdup(new_service, HBAC_TEST_SERVICE);
+ fail_if(new_service->name == NULL);
+
+ new_service->groups = talloc_array(new_service, const char *, 3);
+ fail_if(new_service->groups == NULL);
+
+ new_service->groups[0] = talloc_strdup(new_service->groups, HBAC_TEST_SERVICEGROUP1);
+ fail_if(new_service->groups[0] == NULL);
+
+ new_service->groups[1] = talloc_strdup(new_service->groups, HBAC_TEST_SERVICEGROUP2);
+ fail_if(new_service->groups[1] == NULL);
+
+ new_service->groups[2] = NULL;
+
+ *service = new_service;
+}
+
+static void get_test_srchost(TALLOC_CTX *mem_ctx,
+ struct hbac_request_element **srchost)
+{
+ struct hbac_request_element *new_srchost;
+
+ new_srchost = talloc_zero(mem_ctx, struct hbac_request_element);
+ fail_if (new_srchost == NULL);
+
+ new_srchost->name = talloc_strdup(new_srchost, "client.example.com");
+ fail_if(new_srchost->name == NULL);
+
+ new_srchost->groups = talloc_array(new_srchost, const char *, 3);
+ fail_if(new_srchost->groups == NULL);
+
+ new_srchost->groups[0] = talloc_strdup(new_srchost->groups, "site_hosts");
+ fail_if(new_srchost->groups[0] == NULL);
+
+ new_srchost->groups[1] = talloc_strdup(new_srchost->groups, "corp_hosts");
+ fail_if(new_srchost->groups[1] == NULL);
+
+ new_srchost->groups[2] = NULL;
+
+ *srchost = new_srchost;
+}
+
+START_TEST(ipa_hbac_test_allow_all)
+{
+ enum hbac_eval_result result;
+ TALLOC_CTX *test_ctx;
+ struct hbac_rule **rules;
+ struct hbac_eval_req *eval_req;
+ struct hbac_info *info;
+ bool is_valid;
+ uint32_t missing_attrs;
+
+ test_ctx = talloc_new(global_talloc_context);
+
+ /* Create a request */
+ eval_req = talloc_zero(test_ctx, struct hbac_eval_req);
+ fail_if (eval_req == NULL);
+
+ get_test_user(eval_req, &eval_req->user);
+ get_test_service(eval_req, &eval_req->service);
+ get_test_srchost(eval_req, &eval_req->srchost);
+
+ /* Create the rules to evaluate against */
+ rules = talloc_array(test_ctx, struct hbac_rule *, 2);
+ fail_if (rules == NULL);
+
+ get_allow_all_rule(rules, &rules[0]);
+ rules[0]->name = talloc_strdup(rules[0], "Allow All");
+ fail_if(rules[0]->name == NULL);
+ rules[1] = NULL;
+
+ /* Validate this rule */
+ is_valid = hbac_rule_is_complete(rules[0], &missing_attrs);
+ fail_unless(is_valid);
+ fail_unless(missing_attrs == 0);
+
+ /* Evaluate the rules */
+ result = hbac_evaluate(rules, eval_req, &info);
+ fail_unless(result == HBAC_EVAL_ALLOW,
+ "Expected [%s], got [%s]; "
+ "Error: [%s]",
+ hbac_result_string(HBAC_EVAL_ALLOW),
+ hbac_result_string(result),
+ info ? hbac_error_string(info->code):"Unknown");
+
+ talloc_free(test_ctx);
+}
+END_TEST
+
+START_TEST(ipa_hbac_test_allow_user)
+{
+ enum hbac_eval_result result;
+ TALLOC_CTX *test_ctx;
+ struct hbac_rule **rules;
+ struct hbac_eval_req *eval_req;
+ struct hbac_info *info;
+ bool is_valid;
+ uint32_t missing_attrs;
+
+ test_ctx = talloc_new(global_talloc_context);
+
+ /* Create a request */
+ eval_req = talloc_zero(test_ctx, struct hbac_eval_req);
+ fail_if (eval_req == NULL);
+
+ get_test_user(eval_req, &eval_req->user);
+ get_test_service(eval_req, &eval_req->service);
+ get_test_srchost(eval_req, &eval_req->srchost);
+
+ /* Create the rules to evaluate against */
+ rules = talloc_array(test_ctx, struct hbac_rule *, 2);
+ fail_if (rules == NULL);
+
+ get_allow_all_rule(rules, &rules[0]);
+
+ /* Modify the rule to allow only a specific user */
+ rules[0]->name = talloc_strdup(rules[0], "Allow user");
+ fail_if(rules[0]->name == NULL);
+ rules[0]->users->category = HBAC_CATEGORY_NULL;
+
+ rules[0]->users->names = talloc_array(rules[0], const char *, 2);
+ fail_if(rules[0]->users->names == NULL);
+
+ rules[0]->users->names[0] = HBAC_TEST_USER;
+ rules[0]->users->names[1] = NULL;
+
+ rules[1] = NULL;
+
+ /* Validate this rule */
+ is_valid = hbac_rule_is_complete(rules[0], &missing_attrs);
+ fail_unless(is_valid);
+ fail_unless(missing_attrs == 0);
+
+ /* Evaluate the rules */
+ result = hbac_evaluate(rules, eval_req, &info);
+ fail_unless(result == HBAC_EVAL_ALLOW,
+ "Expected [%s], got [%s]; "
+ "Error: [%s]",
+ hbac_result_string(HBAC_EVAL_ALLOW),
+ hbac_result_string(result),
+ info ? hbac_error_string(info->code):"Unknown");
+
+ /* Negative test */
+ rules[0]->users->names[0] = HBAC_TEST_INVALID_USER;
+
+ /* Validate this rule */
+ is_valid = hbac_rule_is_complete(rules[0], &missing_attrs);
+ fail_unless(is_valid);
+ fail_unless(missing_attrs == 0);
+
+ /* Evaluate the rules */
+ result = hbac_evaluate(rules, eval_req, &info);
+ fail_unless(result == HBAC_EVAL_DENY,
+ "Expected [%s], got [%s]; "
+ "Error: [%s]",
+ hbac_result_string(HBAC_EVAL_DENY),
+ hbac_result_string(result),
+ info ? hbac_error_string(info->code):"Unknown");
+
+ talloc_free(test_ctx);
+}
+END_TEST
+
+START_TEST(ipa_hbac_test_allow_utf8)
+{
+ enum hbac_eval_result result;
+ TALLOC_CTX *test_ctx;
+ struct hbac_rule **rules;
+ struct hbac_eval_req *eval_req;
+ struct hbac_info *info;
+ bool is_valid;
+ uint32_t missing_attrs;
+
+ test_ctx = talloc_new(global_talloc_context);
+
+ /* Create a request */
+ eval_req = talloc_zero(test_ctx, struct hbac_eval_req);
+ fail_if (eval_req == NULL);
+
+ get_test_user(eval_req, &eval_req->user);
+ get_test_service(eval_req, &eval_req->service);
+ get_test_srchost(eval_req, &eval_req->srchost);
+
+ /* Override the with UTF8 values */
+ eval_req->user->name = (const char *) &user_utf8_lowcase;
+ eval_req->srchost->name = (const char *) &srchost_utf8_lowcase;
+ eval_req->service->name = (const char *) &service_utf8_lowcase;
+
+ /* Create the rules to evaluate against */
+ rules = talloc_array(test_ctx, struct hbac_rule *, 2);
+ fail_if (rules == NULL);
+
+ get_allow_all_rule(rules, &rules[0]);
+
+ rules[0]->name = talloc_strdup(rules[0], "Allow user");
+ fail_if(rules[0]->name == NULL);
+ rules[0]->users->category = HBAC_CATEGORY_NULL;
+
+ /* Modify the rule to allow only a specific user */
+ rules[0]->users->names = talloc_array(rules[0], const char *, 2);
+ fail_if(rules[0]->users->names == NULL);
+
+ rules[0]->users->names[0] = (const char *) &user_utf8_upcase;
+ rules[0]->users->names[1] = NULL;
+
+ /* Modify the rule to allow only a specific service */
+ rules[0]->services->category = HBAC_CATEGORY_NULL;
+
+ rules[0]->services->names = talloc_array(rules[0], const char *, 2);
+ fail_if(rules[0]->services->names == NULL);
+
+ rules[0]->services->names[0] = (const char *) &service_utf8_upcase;
+ rules[0]->services->names[1] = NULL;
+
+ /* Modify the rule to allow only a specific service */
+ rules[0]->srchosts->category = HBAC_CATEGORY_NULL;
+
+ rules[0]->srchosts->names = talloc_array(rules[0], const char *, 2);
+ fail_if(rules[0]->services->names == NULL);
+
+ rules[0]->srchosts->names[0] = (const char *) &srchost_utf8_upcase;
+ rules[0]->services->names[1] = NULL;
+
+ rules[1] = NULL;
+
+ /* Validate this rule */
+ is_valid = hbac_rule_is_complete(rules[0], &missing_attrs);
+ fail_unless(is_valid);
+ fail_unless(missing_attrs == 0);
+
+ /* Evaluate the rules */
+ result = hbac_evaluate(rules, eval_req, &info);
+ fail_unless(result == HBAC_EVAL_ALLOW,
+ "Expected [%s], got [%s]; "
+ "Error: [%s]",
+ hbac_result_string(HBAC_EVAL_ALLOW),
+ hbac_result_string(result),
+ info ? hbac_error_string(info->code):"Unknown");
+
+ /* Negative test - a different letter */
+ rules[0]->users->names[0] = (const char *) &user_utf8_lowcase_neg;
+
+ /* Evaluate the rules */
+ result = hbac_evaluate(rules, eval_req, &info);
+ fail_unless(result == HBAC_EVAL_DENY,
+ "Expected [%s], got [%s]; "
+ "Error: [%s]",
+ hbac_result_string(HBAC_EVAL_DENY),
+ hbac_result_string(result),
+ info ? hbac_error_string(info->code):"Unknown");
+
+ /* Negative test - Turkish dotless i. We cannot know that capital I
+ * casefolds into dotless i unless we know the language is Turkish */
+ eval_req->user->name = (const char *) &user_lowcase_tr;
+ rules[0]->users->names[0] = (const char *) &user_upcase_tr;
+
+ /* Validate this rule */
+ is_valid = hbac_rule_is_complete(rules[0], &missing_attrs);
+ fail_unless(is_valid);
+ fail_unless(missing_attrs == 0);
+
+ /* Evaluate the rules */
+ result = hbac_evaluate(rules, eval_req, &info);
+ fail_unless(result == HBAC_EVAL_DENY,
+ "Expected [%s], got [%s]; "
+ "Error: [%s]",
+ hbac_result_string(HBAC_EVAL_DENY),
+ hbac_result_string(result),
+ info ? hbac_error_string(info->code):"Unknown");
+
+ talloc_free(test_ctx);
+}
+END_TEST
+
+START_TEST(ipa_hbac_test_allow_group)
+{
+ enum hbac_eval_result result;
+ TALLOC_CTX *test_ctx;
+ struct hbac_rule **rules;
+ struct hbac_eval_req *eval_req;
+ struct hbac_info *info;
+ bool is_valid;
+ uint32_t missing_attrs;
+
+ test_ctx = talloc_new(global_talloc_context);
+
+ /* Create a request */
+ eval_req = talloc_zero(test_ctx, struct hbac_eval_req);
+ fail_if (eval_req == NULL);
+
+ get_test_user(eval_req, &eval_req->user);
+ get_test_service(eval_req, &eval_req->service);
+ get_test_srchost(eval_req, &eval_req->srchost);
+
+ /* Create the rules to evaluate against */
+ rules = talloc_array(test_ctx, struct hbac_rule *, 2);
+ fail_if (rules == NULL);
+
+ get_allow_all_rule(rules, &rules[0]);
+
+ /* Modify the rule to allow only a group of users */
+ rules[0]->name = talloc_strdup(rules[0], "Allow group");
+ fail_if(rules[0]->name == NULL);
+ rules[0]->users->category = HBAC_CATEGORY_NULL;
+
+ rules[0]->users->names = NULL;
+ rules[0]->users->groups = talloc_array(rules[0], const char *, 2);
+ fail_if(rules[0]->users->groups == NULL);
+
+ rules[0]->users->groups[0] = HBAC_TEST_GROUP1;
+ rules[0]->users->groups[1] = NULL;
+
+ rules[1] = NULL;
+
+ /* Validate this rule */
+ is_valid = hbac_rule_is_complete(rules[0], &missing_attrs);
+ fail_unless(is_valid);
+ fail_unless(missing_attrs == 0);
+
+ /* Evaluate the rules */
+ result = hbac_evaluate(rules, eval_req, &info);
+ fail_unless(result == HBAC_EVAL_ALLOW,
+ "Expected [%s], got [%s]; "
+ "Error: [%s]",
+ hbac_result_string(HBAC_EVAL_ALLOW),
+ hbac_result_string(result),
+ info ? hbac_error_string(info->code):"Unknown");
+
+ /* Negative test */
+ rules[0]->users->groups[0] = HBAC_TEST_INVALID_GROUP;
+
+ /* Validate this rule */
+ is_valid = hbac_rule_is_complete(rules[0], &missing_attrs);
+ fail_unless(is_valid);
+ fail_unless(missing_attrs == 0);
+
+ /* Evaluate the rules */
+ result = hbac_evaluate(rules, eval_req, &info);
+ fail_unless(result == HBAC_EVAL_DENY,
+ "Expected [%s], got [%s]; "
+ "Error: [%s]",
+ hbac_result_string(HBAC_EVAL_DENY),
+ hbac_result_string(result),
+ info ? hbac_error_string(info->code):"Unknown");
+
+ talloc_free(test_ctx);
+}
+END_TEST
+
+START_TEST(ipa_hbac_test_allow_svc)
+{
+ enum hbac_eval_result result;
+ TALLOC_CTX *test_ctx;
+ struct hbac_rule **rules;
+ struct hbac_eval_req *eval_req;
+ struct hbac_info *info;
+ bool is_valid;
+ uint32_t missing_attrs;
+
+ test_ctx = talloc_new(global_talloc_context);
+
+ /* Create a request */
+ eval_req = talloc_zero(test_ctx, struct hbac_eval_req);
+ fail_if (eval_req == NULL);
+
+ get_test_user(eval_req, &eval_req->user);
+ get_test_service(eval_req, &eval_req->service);
+ get_test_srchost(eval_req, &eval_req->srchost);
+
+ /* Create the rules to evaluate against */
+ rules = talloc_array(test_ctx, struct hbac_rule *, 2);
+ fail_if (rules == NULL);
+
+ get_allow_all_rule(rules, &rules[0]);
+
+ /* Modify the rule to allow only a specific service */
+ rules[0]->name = talloc_strdup(rules[0], "Allow service");
+ fail_if(rules[0]->name == NULL);
+ rules[0]->services->category = HBAC_CATEGORY_NULL;
+
+ rules[0]->services->names = talloc_array(rules[0], const char *, 2);
+ fail_if(rules[0]->services->names == NULL);
+
+ rules[0]->services->names[0] = HBAC_TEST_SERVICE;
+ rules[0]->services->names[1] = NULL;
+
+ rules[1] = NULL;
+
+ /* Validate this rule */
+ is_valid = hbac_rule_is_complete(rules[0], &missing_attrs);
+ fail_unless(is_valid);
+ fail_unless(missing_attrs == 0);
+
+ /* Evaluate the rules */
+ result = hbac_evaluate(rules, eval_req, &info);
+ fail_unless(result == HBAC_EVAL_ALLOW,
+ "Expected [%s], got [%s]; "
+ "Error: [%s]",
+ hbac_result_string(HBAC_EVAL_ALLOW),
+ hbac_result_string(result),
+ info ? hbac_error_string(info->code):"Unknown");
+
+ /* Negative test */
+ rules[0]->services->names[0] = HBAC_TEST_INVALID_SERVICE;
+
+ /* Validate this rule */
+ is_valid = hbac_rule_is_complete(rules[0], &missing_attrs);
+ fail_unless(is_valid);
+ fail_unless(missing_attrs == 0);
+
+ /* Evaluate the rules */
+ result = hbac_evaluate(rules, eval_req, &info);
+ fail_unless(result == HBAC_EVAL_DENY,
+ "Expected [%s], got [%s]; "
+ "Error: [%s]",
+ hbac_result_string(HBAC_EVAL_DENY),
+ hbac_result_string(result),
+ info ? hbac_error_string(info->code):"Unknown");
+
+ talloc_free(test_ctx);
+}
+END_TEST
+
+START_TEST(ipa_hbac_test_allow_svcgroup)
+{
+ enum hbac_eval_result result;
+ TALLOC_CTX *test_ctx;
+ struct hbac_rule **rules;
+ struct hbac_eval_req *eval_req;
+ struct hbac_info *info;
+ bool is_valid;
+ uint32_t missing_attrs;
+
+ test_ctx = talloc_new(global_talloc_context);
+
+ /* Create a request */
+ eval_req = talloc_zero(test_ctx, struct hbac_eval_req);
+ fail_if (eval_req == NULL);
+
+ get_test_user(eval_req, &eval_req->user);
+ get_test_service(eval_req, &eval_req->service);
+ get_test_srchost(eval_req, &eval_req->srchost);
+
+ /* Create the rules to evaluate against */
+ rules = talloc_array(test_ctx, struct hbac_rule *, 2);
+ fail_if (rules == NULL);
+
+ get_allow_all_rule(rules, &rules[0]);
+
+ /* Modify the rule to allow only a group of users */
+ rules[0]->name = talloc_strdup(rules[0], "Allow servicegroup");
+ fail_if(rules[0]->name == NULL);
+ rules[0]->services->category = HBAC_CATEGORY_NULL;
+
+ rules[0]->services->names = NULL;
+ rules[0]->services->groups = talloc_array(rules[0], const char *, 2);
+ fail_if(rules[0]->services->groups == NULL);
+
+ rules[0]->services->groups[0] = HBAC_TEST_SERVICEGROUP1;
+ rules[0]->services->groups[1] = NULL;
+
+ rules[1] = NULL;
+
+ /* Validate this rule */
+ is_valid = hbac_rule_is_complete(rules[0], &missing_attrs);
+ fail_unless(is_valid);
+ fail_unless(missing_attrs == 0);
+
+ /* Evaluate the rules */
+ result = hbac_evaluate(rules, eval_req, &info);
+ fail_unless(result == HBAC_EVAL_ALLOW,
+ "Expected [%s], got [%s]; "
+ "Error: [%s]",
+ hbac_result_string(HBAC_EVAL_ALLOW),
+ hbac_result_string(result),
+ info ? hbac_error_string(info->code):"Unknown");
+
+ /* Negative test */
+ rules[0]->services->groups[0] = HBAC_TEST_INVALID_SERVICEGROUP;
+
+ /* Validate this rule */
+ is_valid = hbac_rule_is_complete(rules[0], &missing_attrs);
+ fail_unless(is_valid);
+ fail_unless(missing_attrs == 0);
+
+ /* Evaluate the rules */
+ result = hbac_evaluate(rules, eval_req, &info);
+ fail_unless(result == HBAC_EVAL_DENY,
+ "Expected [%s], got [%s]; "
+ "Error: [%s]",
+ hbac_result_string(HBAC_EVAL_DENY),
+ hbac_result_string(result),
+ info ? hbac_error_string(info->code):"Unknown");
+
+ talloc_free(test_ctx);
+}
+END_TEST
+
+START_TEST(ipa_hbac_test_allow_srchost)
+{
+ enum hbac_eval_result result;
+ TALLOC_CTX *test_ctx;
+ struct hbac_rule **rules;
+ struct hbac_eval_req *eval_req;
+ struct hbac_info *info;
+ bool is_valid;
+ uint32_t missing_attrs;
+
+ test_ctx = talloc_new(global_talloc_context);
+
+ /* Create a request */
+ eval_req = talloc_zero(test_ctx, struct hbac_eval_req);
+ fail_if (eval_req == NULL);
+
+ get_test_user(eval_req, &eval_req->user);
+ get_test_service(eval_req, &eval_req->service);
+ get_test_srchost(eval_req, &eval_req->srchost);
+
+ /* Create the rules to evaluate against */
+ rules = talloc_array(test_ctx, struct hbac_rule *, 2);
+ fail_if (rules == NULL);
+
+ get_allow_all_rule(rules, &rules[0]);
+
+ /* Modify the rule to allow only a specific service */
+ rules[0]->name = talloc_strdup(rules[0], "Allow srchost");
+ fail_if(rules[0]->name == NULL);
+ rules[0]->srchosts->category = HBAC_CATEGORY_NULL;
+
+ rules[0]->srchosts->names = talloc_array(rules[0], const char *, 2);
+ fail_if(rules[0]->srchosts->names == NULL);
+
+ rules[0]->srchosts->names[0] = HBAC_TEST_SRCHOST;
+ rules[0]->srchosts->names[1] = NULL;
+
+ rules[1] = NULL;
+
+ /* Validate this rule */
+ is_valid = hbac_rule_is_complete(rules[0], &missing_attrs);
+ fail_unless(is_valid);
+ fail_unless(missing_attrs == 0);
+
+ /* Evaluate the rules */
+ result = hbac_evaluate(rules, eval_req, &info);
+ fail_unless(result == HBAC_EVAL_ALLOW,
+ "Expected [%s], got [%s]; "
+ "Error: [%s]",
+ hbac_result_string(HBAC_EVAL_ALLOW),
+ hbac_result_string(result),
+ info ? hbac_error_string(info->code):"Unknown");
+
+ /* Negative test */
+ rules[0]->srchosts->names[0] = HBAC_TEST_INVALID_SRCHOST;
+
+ /* Validate this rule */
+ is_valid = hbac_rule_is_complete(rules[0], &missing_attrs);
+ fail_unless(is_valid);
+ fail_unless(missing_attrs == 0);
+
+ /* Evaluate the rules */
+ result = hbac_evaluate(rules, eval_req, &info);
+ fail_unless(result == HBAC_EVAL_DENY,
+ "Expected [%s], got [%s]; "
+ "Error: [%s](%s)",
+ hbac_result_string(HBAC_EVAL_DENY),
+ hbac_result_string(result),
+ info ? hbac_error_string(info->code):"Unknown");
+
+ talloc_free(test_ctx);
+}
+END_TEST
+
+START_TEST(ipa_hbac_test_allow_srchostgroup)
+{
+ enum hbac_eval_result result;
+ TALLOC_CTX *test_ctx;
+ struct hbac_rule **rules;
+ struct hbac_eval_req *eval_req;
+ struct hbac_info *info;
+ bool is_valid;
+ uint32_t missing_attrs;
+
+ test_ctx = talloc_new(global_talloc_context);
+
+ /* Create a request */
+ eval_req = talloc_zero(test_ctx, struct hbac_eval_req);
+ fail_if (eval_req == NULL);
+
+ get_test_user(eval_req, &eval_req->user);
+ get_test_service(eval_req, &eval_req->service);
+ get_test_srchost(eval_req, &eval_req->srchost);
+
+ /* Create the rules to evaluate against */
+ rules = talloc_array(test_ctx, struct hbac_rule *, 2);
+ fail_if (rules == NULL);
+
+ get_allow_all_rule(rules, &rules[0]);
+
+ /* Modify the rule to allow only a group of users */
+ rules[0]->name = talloc_strdup(rules[0], "Allow srchostgroup");
+ fail_if(rules[0]->name == NULL);
+ rules[0]->srchosts->category = HBAC_CATEGORY_NULL;
+
+ rules[0]->srchosts->names = NULL;
+ rules[0]->srchosts->groups = talloc_array(rules[0], const char *, 2);
+ fail_if(rules[0]->srchosts->groups == NULL);
+
+ rules[0]->srchosts->groups[0] = HBAC_TEST_SRCHOSTGROUP1;
+ rules[0]->srchosts->groups[1] = NULL;
+
+ rules[1] = NULL;
+
+ /* Validate this rule */
+ is_valid = hbac_rule_is_complete(rules[0], &missing_attrs);
+ fail_unless(is_valid);
+ fail_unless(missing_attrs == 0);
+
+ /* Evaluate the rules */
+ result = hbac_evaluate(rules, eval_req, &info);
+ fail_unless(result == HBAC_EVAL_ALLOW,
+ "Expected [%s], got [%s]; "
+ "Error: [%s]",
+ hbac_result_string(HBAC_EVAL_ALLOW),
+ hbac_result_string(result),
+ info ? hbac_error_string(info->code):"Unknown");
+
+ /* Negative test */
+ rules[0]->srchosts->groups[0] = HBAC_TEST_INVALID_SRCHOSTGROUP;
+
+ /* Validate this rule */
+ is_valid = hbac_rule_is_complete(rules[0], &missing_attrs);
+ fail_unless(is_valid);
+ fail_unless(missing_attrs == 0);
+
+ /* Evaluate the rules */
+ result = hbac_evaluate(rules, eval_req, &info);
+ fail_unless(result == HBAC_EVAL_DENY,
+ "Expected [%s], got [%s]; "
+ "Error: [%s]",
+ hbac_result_string(HBAC_EVAL_DENY),
+ hbac_result_string(result),
+ info ? hbac_error_string(info->code):"Unknown");
+
+ talloc_free(test_ctx);
+}
+END_TEST
+
+START_TEST(ipa_hbac_test_incomplete)
+{
+ TALLOC_CTX *test_ctx;
+ struct hbac_rule *rule;
+ bool is_valid;
+ uint32_t missing_attrs;
+
+ test_ctx = talloc_new(global_talloc_context);
+
+ rule = talloc_zero(test_ctx, struct hbac_rule);
+
+ /* Validate this rule */
+ is_valid = hbac_rule_is_complete(rule, &missing_attrs);
+ fail_if(is_valid);
+ fail_unless(missing_attrs | HBAC_RULE_ELEMENT_USERS);
+ fail_unless(missing_attrs | HBAC_RULE_ELEMENT_SERVICES);
+ fail_unless(missing_attrs | HBAC_RULE_ELEMENT_TARGETHOSTS);
+ fail_unless(missing_attrs | HBAC_RULE_ELEMENT_SOURCEHOSTS);
+
+ talloc_free(test_ctx);
+}
+END_TEST
+
+Suite *hbac_test_suite (void)
+{
+ Suite *s = suite_create ("HBAC");
+
+ TCase *tc_hbac = tcase_create("HBAC_rules");
+ tcase_add_checked_fixture(tc_hbac,
+ leak_check_setup,
+ leak_check_teardown);
+
+ tcase_add_test(tc_hbac, ipa_hbac_test_allow_all);
+ tcase_add_test(tc_hbac, ipa_hbac_test_allow_user);
+ tcase_add_test(tc_hbac, ipa_hbac_test_allow_group);
+ tcase_add_test(tc_hbac, ipa_hbac_test_allow_svc);
+ tcase_add_test(tc_hbac, ipa_hbac_test_allow_svcgroup);
+ tcase_add_test(tc_hbac, ipa_hbac_test_allow_srchost);
+ tcase_add_test(tc_hbac, ipa_hbac_test_allow_srchostgroup);
+ tcase_add_test(tc_hbac, ipa_hbac_test_allow_utf8);
+ tcase_add_test(tc_hbac, ipa_hbac_test_incomplete);
+
+ suite_add_tcase(s, tc_hbac);
+ return s;
+}
+
+int main(int argc, const char *argv[])
+{
+ int number_failed;
+
+ tests_set_cwd();
+
+ Suite *s = hbac_test_suite();
+ SRunner *sr = srunner_create(s);
+
+ /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */
+ srunner_run_all(sr, CK_ENV);
+ number_failed = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}