1dedfbb334
Resolves: upstream#3588 - sssd_nss consumes more memory until restarted or machine swaps Resolves: failure in glibc tests https://sourceware.org/bugzilla/show_bug.cgi?id=22530 Resolves: upstream#3451 - When sssd is configured with id_provider proxy and auth_provider ldap, login fails if the LDAP server is not allowing anonymous binds Resolves: upstream#3285 - SSSD needs restart after incorrect clock is corrected with AD Resolves: upstream#3586 - Give a more detailed debug and system-log message if krb5_init_context() failed Resolves: rhbz#1431153 - SSSD ships a drop-in configuration snippet in /etc/systemd/system Backport few upstream features from 1.16.1
504 lines
16 KiB
Diff
504 lines
16 KiB
Diff
From f4d860aa678d899fdcbcd8c991c99b3b85c5c8e0 Mon Sep 17 00:00:00 2001
|
|
From: Jakub Hrozek <jhrozek@redhat.com>
|
|
Date: Mon, 23 Oct 2017 18:08:12 +0200
|
|
Subject: [PATCH 56/79] TOOLS: Add a new sssctl command access-report
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
Resolves:
|
|
https://pagure.io/SSSD/sssd/issue/2840
|
|
|
|
Reviewed-by: Pavel Březina <pbrezina@redhat.com>
|
|
Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
|
|
---
|
|
Makefile.am | 1 +
|
|
src/tools/sssctl/sssctl.c | 1 +
|
|
src/tools/sssctl/sssctl.h | 5 +
|
|
src/tools/sssctl/sssctl_access_report.c | 435 ++++++++++++++++++++++++++++++++
|
|
4 files changed, 442 insertions(+)
|
|
create mode 100644 src/tools/sssctl/sssctl_access_report.c
|
|
|
|
diff --git a/Makefile.am b/Makefile.am
|
|
index 04e7d59e9fb0fdbdd420573e7d737a9a465b66ea..4c8fe64c3e689c0d411277d58995a4c0fb008dc1 100644
|
|
--- a/Makefile.am
|
|
+++ b/Makefile.am
|
|
@@ -1752,6 +1752,7 @@ sssctl_SOURCES = \
|
|
src/tools/sssctl/sssctl_sifp.c \
|
|
src/tools/sssctl/sssctl_config.c \
|
|
src/tools/sssctl/sssctl_user_checks.c \
|
|
+ src/tools/sssctl/sssctl_access_report.c \
|
|
$(SSSD_TOOLS_OBJ) \
|
|
$(NULL)
|
|
sssctl_LDADD = \
|
|
diff --git a/src/tools/sssctl/sssctl.c b/src/tools/sssctl/sssctl.c
|
|
index d9bc897c1a32954bbdd2d4ae2b0a9fb6d2c34752..afaa84bc0b44a0fc91fe8b62c0c1fd36ac4e1e0b 100644
|
|
--- a/src/tools/sssctl/sssctl.c
|
|
+++ b/src/tools/sssctl/sssctl.c
|
|
@@ -264,6 +264,7 @@ int main(int argc, const char **argv)
|
|
SSS_TOOL_COMMAND("domain-list", "List available domains", 0, sssctl_domain_list),
|
|
SSS_TOOL_COMMAND("domain-status", "Print information about domain", 0, sssctl_domain_status),
|
|
SSS_TOOL_COMMAND("user-checks", "Print information about a user and check authentication", 0, sssctl_user_checks),
|
|
+ SSS_TOOL_COMMAND("access-report", "Generate access report for a domain", 0, sssctl_access_report),
|
|
SSS_TOOL_DELIMITER("Information about cached content:"),
|
|
SSS_TOOL_COMMAND("user-show", "Information about cached user", 0, sssctl_user_show),
|
|
SSS_TOOL_COMMAND("group-show", "Information about cached group", 0, sssctl_group_show),
|
|
diff --git a/src/tools/sssctl/sssctl.h b/src/tools/sssctl/sssctl.h
|
|
index 22ca5d41e2c084e64b58bc5aa066414b002e7e8b..70fc19eff07317c264978a1ecb9159ae3acdfced 100644
|
|
--- a/src/tools/sssctl/sssctl.h
|
|
+++ b/src/tools/sssctl/sssctl.h
|
|
@@ -133,4 +133,9 @@ errno_t sssctl_config_check(struct sss_cmdline *cmdline,
|
|
errno_t sssctl_user_checks(struct sss_cmdline *cmdline,
|
|
struct sss_tool_ctx *tool_ctx,
|
|
void *pvt);
|
|
+
|
|
+errno_t sssctl_access_report(struct sss_cmdline *cmdline,
|
|
+ struct sss_tool_ctx *tool_ctx,
|
|
+ void *pvt);
|
|
+
|
|
#endif /* _SSSCTL_H_ */
|
|
diff --git a/src/tools/sssctl/sssctl_access_report.c b/src/tools/sssctl/sssctl_access_report.c
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..11172329817b4dedaca480ab8a4537149853c330
|
|
--- /dev/null
|
|
+++ b/src/tools/sssctl/sssctl_access_report.c
|
|
@@ -0,0 +1,435 @@
|
|
+/*
|
|
+ Copyright (C) 2017 Red Hat
|
|
+
|
|
+ This program is free software; you can redistribute it and/or modify
|
|
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
|
|
+
|
|
+ You should have received a copy of the GNU Lesser General Public License
|
|
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
+*/
|
|
+
|
|
+#include <security/pam_appl.h>
|
|
+
|
|
+#include "util/util.h"
|
|
+#include "tools/common/sss_tools.h"
|
|
+#include "tools/sssctl/sssctl.h"
|
|
+
|
|
+/*
|
|
+ * We're searching the cache directly..
|
|
+ */
|
|
+#include "providers/ipa/ipa_hbac_private.h"
|
|
+#include "providers/ipa/ipa_rules_common.h"
|
|
+
|
|
+#ifdef HAVE_SECURITY_PAM_MISC_H
|
|
+# include <security/pam_misc.h>
|
|
+#elif defined(HAVE_SECURITY_OPENPAM_H)
|
|
+# include <security/openpam.h>
|
|
+#endif
|
|
+
|
|
+#ifdef HAVE_SECURITY_PAM_MISC_H
|
|
+static struct pam_conv conv = {
|
|
+ misc_conv,
|
|
+ NULL
|
|
+};
|
|
+#elif defined(HAVE_SECURITY_OPENPAM_H)
|
|
+static struct pam_conv conv = {
|
|
+ openpam_ttyconv,
|
|
+ NULL
|
|
+};
|
|
+#else
|
|
+# error "Missing text based pam conversation function"
|
|
+#endif
|
|
+
|
|
+#ifndef DEFAULT_SERVICE
|
|
+#define DEFAULT_SERVICE "system-auth"
|
|
+#endif /* DEFAULT_SERVICE */
|
|
+
|
|
+#ifndef DEFAULT_USER
|
|
+#define DEFAULT_USER "admin"
|
|
+#endif /* DEFAULT_USER */
|
|
+
|
|
+typedef errno_t (*sssctl_dom_access_reporter_fn)(struct sss_tool_ctx *tool_ctx,
|
|
+ const char *user,
|
|
+ const char *service,
|
|
+ struct sss_domain_info *domain);
|
|
+
|
|
+static errno_t run_pam_acct(struct sss_tool_ctx *tool_ctx,
|
|
+ const char *user,
|
|
+ const char *service,
|
|
+ struct sss_domain_info *domain)
|
|
+{
|
|
+ errno_t ret;
|
|
+ pam_handle_t *pamh;
|
|
+
|
|
+ ret = pam_start(service, user, &conv, &pamh);
|
|
+ if (ret != PAM_SUCCESS) {
|
|
+ ERROR("pam_start failed: %s\n", pam_strerror(pamh, ret));
|
|
+ return EIO;
|
|
+ }
|
|
+
|
|
+ ret = pam_acct_mgmt(pamh, 0);
|
|
+ pam_end(pamh, ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static errno_t get_rdn_value(TALLOC_CTX *mem_ctx,
|
|
+ struct sss_domain_info *dom,
|
|
+ const char *dn_attr,
|
|
+ const char **_rdn_value)
|
|
+{
|
|
+ errno_t ret;
|
|
+ TALLOC_CTX *tmp_ctx;
|
|
+ struct ldb_dn *dn = NULL;
|
|
+ const struct ldb_val *rdn_val;
|
|
+ const char *rdn_str;
|
|
+
|
|
+ tmp_ctx = talloc_new(NULL);
|
|
+ if (tmp_ctx == NULL) {
|
|
+ return ENOMEM;
|
|
+ }
|
|
+
|
|
+ dn = ldb_dn_new(tmp_ctx, sysdb_ctx_get_ldb(dom->sysdb), dn_attr);
|
|
+ if (dn == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ rdn_val = ldb_dn_get_rdn_val(dn);
|
|
+ if (rdn_val == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "No RDN value?\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ rdn_str = talloc_strndup(tmp_ctx,
|
|
+ (const char *)rdn_val->data,
|
|
+ rdn_val->length);
|
|
+ if (rdn_str == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = EOK;
|
|
+ *_rdn_value = talloc_steal(mem_ctx, rdn_str);
|
|
+done:
|
|
+ talloc_zfree(tmp_ctx);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static errno_t is_member_group(struct sss_domain_info *dom,
|
|
+ const char *dn_attr,
|
|
+ const char *group_rdn,
|
|
+ bool *_is_group)
|
|
+{
|
|
+ const char *comp_name;
|
|
+ const struct ldb_val *comp_val;
|
|
+ TALLOC_CTX *tmp_ctx;
|
|
+ bool is_group = false;
|
|
+ errno_t ret;
|
|
+ struct ldb_dn *dn = NULL;
|
|
+
|
|
+ tmp_ctx = talloc_new(NULL);
|
|
+ if (tmp_ctx == NULL) {
|
|
+ return ENOMEM;
|
|
+ }
|
|
+
|
|
+ dn = ldb_dn_new(tmp_ctx, sysdb_ctx_get_ldb(dom->sysdb), dn_attr);
|
|
+ if (dn == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ comp_name = ldb_dn_get_component_name(dn, 1);
|
|
+ comp_val = ldb_dn_get_component_val(dn, 1);
|
|
+ if (strcasecmp("cn", comp_name) == 0
|
|
+ && strncasecmp(group_rdn,
|
|
+ (const char *) comp_val->data,
|
|
+ comp_val->length) == 0) {
|
|
+ is_group = true;
|
|
+ }
|
|
+
|
|
+ ret = EOK;
|
|
+done:
|
|
+ *_is_group = is_group;
|
|
+ talloc_zfree(tmp_ctx);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void print_category(struct sss_domain_info *domain,
|
|
+ struct ldb_message *rule_msg,
|
|
+ const char *category_attr_name,
|
|
+ const char *category_label)
|
|
+{
|
|
+ struct ldb_message_element *category_attr;
|
|
+
|
|
+ category_attr = ldb_msg_find_element(rule_msg, category_attr_name);
|
|
+ if (category_attr == NULL) {
|
|
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot find %s\n", category_attr_name);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (category_attr->num_values > 0) {
|
|
+ PRINT("\t%s: ", category_label);
|
|
+ for (unsigned i = 0; i < category_attr->num_values; i++) {
|
|
+ PRINT("%s%s",
|
|
+ i > 0 ? ", " : "",
|
|
+ (const char *) category_attr->values[i].data);
|
|
+ }
|
|
+ PRINT("\n");
|
|
+ }
|
|
+}
|
|
+
|
|
+static void print_member_attr(struct sss_domain_info *domain,
|
|
+ struct ldb_message *rule_msg,
|
|
+ const char *member_attr_name,
|
|
+ const char *group_rdn,
|
|
+ const char *object_label,
|
|
+ const char *group_label)
|
|
+{
|
|
+ errno_t ret;
|
|
+ TALLOC_CTX *tmp_ctx = NULL;
|
|
+ const char **member_names = NULL;
|
|
+ size_t name_count = 0;
|
|
+ const char **member_group_names = NULL;
|
|
+ size_t group_count = 0;
|
|
+ struct ldb_message_element *member_attr = NULL;
|
|
+
|
|
+ tmp_ctx = talloc_new(NULL);
|
|
+ if (tmp_ctx == NULL) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ member_attr = ldb_msg_find_element(rule_msg, member_attr_name);
|
|
+ if (member_attr == NULL) {
|
|
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot find %s\n", member_attr_name);
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ member_names = talloc_zero_array(tmp_ctx,
|
|
+ const char *,
|
|
+ member_attr->num_values + 1);
|
|
+ member_group_names = talloc_zero_array(tmp_ctx,
|
|
+ const char *,
|
|
+ member_attr->num_values + 1);
|
|
+ if (member_names == NULL || member_group_names == NULL) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "OOM?\n");
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ for (size_t i = 0; i < member_attr->num_values; i++) {
|
|
+ bool is_group;
|
|
+ const char *rdn_string;
|
|
+ const char *dn_attr;
|
|
+
|
|
+ dn_attr = (const char *) member_attr->values[i].data;
|
|
+
|
|
+ ret = is_member_group(domain, dn_attr, group_rdn, &is_group);
|
|
+ if (ret != EOK) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ ret = get_rdn_value(tmp_ctx, domain, dn_attr, &rdn_string);
|
|
+ if (ret != EOK) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (is_group == false) {
|
|
+ member_names[name_count] = talloc_steal(member_names,
|
|
+ rdn_string);
|
|
+ if (member_names[name_count] == NULL) {
|
|
+ goto done;
|
|
+ }
|
|
+ name_count++;
|
|
+ } else {
|
|
+ member_group_names[group_count] = talloc_strdup(member_group_names,
|
|
+ rdn_string);
|
|
+ if (member_group_names[group_count] == NULL) {
|
|
+ goto done;
|
|
+ }
|
|
+ group_count++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (member_names[0] != NULL) {
|
|
+ PRINT("\t%s: ", object_label);
|
|
+ for (int i = 0; member_names[i]; i++) {
|
|
+ PRINT("%s%s", i > 0 ? ", " : "", member_names[i]);
|
|
+ }
|
|
+ PRINT("\n");
|
|
+ }
|
|
+
|
|
+ if (member_group_names[0] != NULL) {
|
|
+ PRINT("\t%s: ", group_label);
|
|
+ for (int i = 0; member_group_names[i]; i++) {
|
|
+ PRINT("%s%s", i > 0 ? ", " : "", member_group_names[i]);
|
|
+ }
|
|
+ PRINT("\n");
|
|
+ }
|
|
+
|
|
+done:
|
|
+ talloc_free(tmp_ctx);
|
|
+}
|
|
+
|
|
+static void print_ipa_hbac_rule(struct sss_domain_info *domain,
|
|
+ struct ldb_message *rule_msg)
|
|
+{
|
|
+ struct ldb_message_element *el;
|
|
+
|
|
+ el = ldb_msg_find_element(rule_msg, IPA_CN);
|
|
+ if (el == NULL || el->num_values < 1) {
|
|
+ DEBUG(SSSDBG_MINOR_FAILURE, "A rule with no name\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ PRINT("Rule name: %1$s\n", el->values[0].data);
|
|
+
|
|
+ print_member_attr(domain,
|
|
+ rule_msg,
|
|
+ IPA_MEMBER_USER,
|
|
+ "groups",
|
|
+ _("Member users"),
|
|
+ _("Member groups"));
|
|
+ print_category(domain,
|
|
+ rule_msg,
|
|
+ IPA_USER_CATEGORY,
|
|
+ _("User category"));
|
|
+
|
|
+ print_member_attr(domain,
|
|
+ rule_msg,
|
|
+ IPA_MEMBER_SERVICE,
|
|
+ "hbacservicegroups",
|
|
+ _("Member services"),
|
|
+ _("Member service groups"));
|
|
+ print_category(domain,
|
|
+ rule_msg,
|
|
+ IPA_SERVICE_CATEGORY,
|
|
+ _("Service category"));
|
|
+
|
|
+ PRINT("\n");
|
|
+}
|
|
+
|
|
+static errno_t sssctl_ipa_access_report(struct sss_tool_ctx *tool_ctx,
|
|
+ const char *user,
|
|
+ const char *service,
|
|
+ struct sss_domain_info *domain)
|
|
+{
|
|
+ TALLOC_CTX *tmp_ctx = NULL;
|
|
+ const char *filter = NULL;
|
|
+ errno_t ret;
|
|
+ const char *attrs[] = {
|
|
+ OBJECTCLASS,
|
|
+ IPA_CN,
|
|
+ IPA_MEMBER_USER,
|
|
+ IPA_USER_CATEGORY,
|
|
+ IPA_MEMBER_SERVICE,
|
|
+ IPA_SERVICE_CATEGORY,
|
|
+ IPA_MEMBER_HOST,
|
|
+ IPA_HOST_CATEGORY,
|
|
+ NULL,
|
|
+ };
|
|
+ size_t rule_count;
|
|
+ struct ldb_message **msgs = NULL;
|
|
+
|
|
+ /* Run the pam account phase to make sure the rules are fetched by SSSD */
|
|
+ ret = run_pam_acct(tool_ctx, user, service, domain);
|
|
+ if (ret != PAM_SUCCESS && ret != PAM_PERM_DENIED) {
|
|
+ ERROR("Cannot run the PAM account phase, reporting stale rules\n");
|
|
+ /* Non-fatal */
|
|
+ }
|
|
+
|
|
+ tmp_ctx = talloc_new(tool_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(tmp_ctx, domain, filter,
|
|
+ HBAC_RULES_SUBDIR, attrs,
|
|
+ &rule_count, &msgs);
|
|
+ if (ret != EOK && ret != ENOENT) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "Error looking up HBAC rules\n");
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (ret == ENOENT) {
|
|
+ PRINT("No cached rules. All users will be denied access\n");
|
|
+ ret = EOK;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ PRINT("%1$zu rules cached\n\n", rule_count);
|
|
+
|
|
+ for (size_t i = 0; i < rule_count; i++) {
|
|
+ print_ipa_hbac_rule(domain, msgs[i]);
|
|
+ }
|
|
+
|
|
+ ret = EOK;
|
|
+done:
|
|
+ talloc_zfree(tmp_ctx);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+sssctl_dom_access_reporter_fn get_report_fn(const char *provider)
|
|
+{
|
|
+ if (strcmp(provider, "ipa") == 0) {
|
|
+ return sssctl_ipa_access_report;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+errno_t sssctl_access_report(struct sss_cmdline *cmdline,
|
|
+ struct sss_tool_ctx *tool_ctx,
|
|
+ void *pvt)
|
|
+{
|
|
+ errno_t ret;
|
|
+ const char *domname = NULL;
|
|
+ sssctl_dom_access_reporter_fn reporter;
|
|
+ struct sss_domain_info *dom;
|
|
+ const char *user = DEFAULT_USER;
|
|
+ const char *service = DEFAULT_SERVICE;
|
|
+
|
|
+ /* Parse command line. */
|
|
+ struct poptOption options[] = {
|
|
+ { "user", 'u', POPT_ARG_STRING, &user, 0,
|
|
+ _("PAM user, default: " DEFAULT_USER), NULL },
|
|
+ { "service", 's', POPT_ARG_STRING, &service, 0,
|
|
+ _("PAM service, default: " DEFAULT_SERVICE), NULL },
|
|
+ POPT_TABLEEND
|
|
+ };
|
|
+
|
|
+ ret = sss_tool_popt_ex(cmdline, options, SSS_TOOL_OPT_OPTIONAL,
|
|
+ NULL, NULL, "DOMAIN", _("Specify domain name."),
|
|
+ &domname, NULL);
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ dom = find_domain_by_name(tool_ctx->domains, domname, true);
|
|
+ if (dom == NULL) {
|
|
+ ERROR("Cannot find domain %1$s\n", domname);
|
|
+ return ERR_DOMAIN_NOT_FOUND;
|
|
+ }
|
|
+
|
|
+ reporter = get_report_fn(dom->provider);
|
|
+ if (reporter == NULL) {
|
|
+ ERROR("Access report not implemented for domains of type %1$s\n",
|
|
+ dom->provider);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return reporter(tool_ctx, user, service, dom);
|
|
+}
|
|
--
|
|
2.15.1
|
|
|