Blame SOURCES/0048-TOOLS-Add-a-new-sssctl-command-access-report.patch

9f2ebf
From 73a04a5c53c0e7701aa7753fd459ffbea52e28b8 Mon Sep 17 00:00:00 2001
9f2ebf
From: Jakub Hrozek <jhrozek@redhat.com>
9f2ebf
Date: Mon, 23 Oct 2017 18:08:12 +0200
9f2ebf
Subject: [PATCH 48/57] TOOLS: Add a new sssctl command access-report
9f2ebf
MIME-Version: 1.0
9f2ebf
Content-Type: text/plain; charset=UTF-8
9f2ebf
Content-Transfer-Encoding: 8bit
9f2ebf
9f2ebf
Resolves:
9f2ebf
https://pagure.io/SSSD/sssd/issue/2840
9f2ebf
9f2ebf
Reviewed-by: Pavel Březina <pbrezina@redhat.com>
9f2ebf
Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
9f2ebf
(cherry picked from commit 3ee8659bc6a77a78bc6c61b9650a36bd18ea95c8)
9f2ebf
---
9f2ebf
 Makefile.am                             |   1 +
9f2ebf
 src/tools/sssctl/sssctl.c               |   1 +
9f2ebf
 src/tools/sssctl/sssctl.h               |   5 +
9f2ebf
 src/tools/sssctl/sssctl_access_report.c | 435 ++++++++++++++++++++++++++++++++
9f2ebf
 4 files changed, 442 insertions(+)
9f2ebf
 create mode 100644 src/tools/sssctl/sssctl_access_report.c
9f2ebf
9f2ebf
diff --git a/Makefile.am b/Makefile.am
9f2ebf
index 16bcb4efc028b05c1196249245f4f3091b9366af..5917bd904054055a259eb69217282e4fb914c700 100644
9f2ebf
--- a/Makefile.am
9f2ebf
+++ b/Makefile.am
9f2ebf
@@ -1754,6 +1754,7 @@ sssctl_SOURCES = \
9f2ebf
     src/tools/sssctl/sssctl_sifp.c \
9f2ebf
     src/tools/sssctl/sssctl_config.c \
9f2ebf
     src/tools/sssctl/sssctl_user_checks.c \
9f2ebf
+    src/tools/sssctl/sssctl_access_report.c \
9f2ebf
     $(SSSD_TOOLS_OBJ) \
9f2ebf
     $(NULL)
9f2ebf
 sssctl_LDADD = \
9f2ebf
diff --git a/src/tools/sssctl/sssctl.c b/src/tools/sssctl/sssctl.c
9f2ebf
index 1e061c00d2238bf34adff4183e560dc127dd62c7..eee2d613966a5dda81627d2e225bfdc9bade4041 100644
9f2ebf
--- a/src/tools/sssctl/sssctl.c
9f2ebf
+++ b/src/tools/sssctl/sssctl.c
9f2ebf
@@ -264,6 +264,7 @@ int main(int argc, const char **argv)
9f2ebf
         SSS_TOOL_COMMAND("domain-list", "List available domains", 0, sssctl_domain_list),
9f2ebf
         SSS_TOOL_COMMAND("domain-status", "Print information about domain", 0, sssctl_domain_status),
9f2ebf
         SSS_TOOL_COMMAND("user-checks", "Print information about a user and check authentication", 0, sssctl_user_checks),
9f2ebf
+        SSS_TOOL_COMMAND("access-report", "Generate access report for a domain", 0, sssctl_access_report),
9f2ebf
         SSS_TOOL_DELIMITER("Information about cached content:"),
9f2ebf
         SSS_TOOL_COMMAND("user-show", "Information about cached user", 0, sssctl_user_show),
9f2ebf
         SSS_TOOL_COMMAND("group-show", "Information about cached group", 0, sssctl_group_show),
9f2ebf
diff --git a/src/tools/sssctl/sssctl.h b/src/tools/sssctl/sssctl.h
9f2ebf
index 22ca5d41e2c084e64b58bc5aa066414b002e7e8b..70fc19eff07317c264978a1ecb9159ae3acdfced 100644
9f2ebf
--- a/src/tools/sssctl/sssctl.h
9f2ebf
+++ b/src/tools/sssctl/sssctl.h
9f2ebf
@@ -133,4 +133,9 @@ errno_t sssctl_config_check(struct sss_cmdline *cmdline,
9f2ebf
 errno_t sssctl_user_checks(struct sss_cmdline *cmdline,
9f2ebf
                            struct sss_tool_ctx *tool_ctx,
9f2ebf
                            void *pvt);
9f2ebf
+
9f2ebf
+errno_t sssctl_access_report(struct sss_cmdline *cmdline,
9f2ebf
+                             struct sss_tool_ctx *tool_ctx,
9f2ebf
+                             void *pvt);
9f2ebf
+
9f2ebf
 #endif /* _SSSCTL_H_ */
9f2ebf
diff --git a/src/tools/sssctl/sssctl_access_report.c b/src/tools/sssctl/sssctl_access_report.c
9f2ebf
new file mode 100644
9f2ebf
index 0000000000000000000000000000000000000000..11172329817b4dedaca480ab8a4537149853c330
9f2ebf
--- /dev/null
9f2ebf
+++ b/src/tools/sssctl/sssctl_access_report.c
9f2ebf
@@ -0,0 +1,435 @@
9f2ebf
+/*
9f2ebf
+    Copyright (C) 2017 Red Hat
9f2ebf
+
9f2ebf
+    This program is free software; you can redistribute it and/or modify
9f2ebf
+    it under the terms of the GNU Lesser General Public License as published by
9f2ebf
+    the Free Software Foundation; either version 3 of the License, or
9f2ebf
+    (at your option) any later version.
9f2ebf
+
9f2ebf
+    This program is distributed in the hope that it will be useful,
9f2ebf
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
9f2ebf
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9f2ebf
+    GNU Lesser General Public License for more details.
9f2ebf
+
9f2ebf
+    You should have received a copy of the GNU Lesser General Public License
9f2ebf
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
9f2ebf
+*/
9f2ebf
+
9f2ebf
+#include <security/pam_appl.h>
9f2ebf
+
9f2ebf
+#include "util/util.h"
9f2ebf
+#include "tools/common/sss_tools.h"
9f2ebf
+#include "tools/sssctl/sssctl.h"
9f2ebf
+
9f2ebf
+/*
9f2ebf
+ * We're searching the cache directly..
9f2ebf
+ */
9f2ebf
+#include "providers/ipa/ipa_hbac_private.h"
9f2ebf
+#include "providers/ipa/ipa_rules_common.h"
9f2ebf
+
9f2ebf
+#ifdef HAVE_SECURITY_PAM_MISC_H
9f2ebf
+# include <security/pam_misc.h>
9f2ebf
+#elif defined(HAVE_SECURITY_OPENPAM_H)
9f2ebf
+# include <security/openpam.h>
9f2ebf
+#endif
9f2ebf
+
9f2ebf
+#ifdef HAVE_SECURITY_PAM_MISC_H
9f2ebf
+static struct pam_conv conv = {
9f2ebf
+    misc_conv,
9f2ebf
+    NULL
9f2ebf
+};
9f2ebf
+#elif defined(HAVE_SECURITY_OPENPAM_H)
9f2ebf
+static struct pam_conv conv = {
9f2ebf
+    openpam_ttyconv,
9f2ebf
+    NULL
9f2ebf
+};
9f2ebf
+#else
9f2ebf
+# error "Missing text based pam conversation function"
9f2ebf
+#endif
9f2ebf
+
9f2ebf
+#ifndef DEFAULT_SERVICE
9f2ebf
+#define DEFAULT_SERVICE "system-auth"
9f2ebf
+#endif /* DEFAULT_SERVICE */
9f2ebf
+
9f2ebf
+#ifndef DEFAULT_USER
9f2ebf
+#define DEFAULT_USER "admin"
9f2ebf
+#endif /* DEFAULT_USER */
9f2ebf
+
9f2ebf
+typedef errno_t (*sssctl_dom_access_reporter_fn)(struct sss_tool_ctx *tool_ctx,
9f2ebf
+                                                 const char *user,
9f2ebf
+                                                 const char *service,
9f2ebf
+                                                 struct sss_domain_info *domain);
9f2ebf
+
9f2ebf
+static errno_t run_pam_acct(struct sss_tool_ctx *tool_ctx,
9f2ebf
+                            const char *user,
9f2ebf
+                            const char *service,
9f2ebf
+                            struct sss_domain_info *domain)
9f2ebf
+{
9f2ebf
+    errno_t ret;
9f2ebf
+    pam_handle_t *pamh;
9f2ebf
+
9f2ebf
+    ret = pam_start(service, user, &conv, &pamh);
9f2ebf
+    if (ret != PAM_SUCCESS) {
9f2ebf
+        ERROR("pam_start failed: %s\n", pam_strerror(pamh, ret));
9f2ebf
+        return EIO;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    ret = pam_acct_mgmt(pamh, 0);
9f2ebf
+    pam_end(pamh, ret);
9f2ebf
+    return ret;
9f2ebf
+}
9f2ebf
+
9f2ebf
+static errno_t get_rdn_value(TALLOC_CTX *mem_ctx,
9f2ebf
+                             struct sss_domain_info *dom,
9f2ebf
+                             const char *dn_attr,
9f2ebf
+                             const char **_rdn_value)
9f2ebf
+{
9f2ebf
+    errno_t ret;
9f2ebf
+    TALLOC_CTX *tmp_ctx;
9f2ebf
+    struct ldb_dn *dn = NULL;
9f2ebf
+    const struct ldb_val *rdn_val;
9f2ebf
+    const char *rdn_str;
9f2ebf
+
9f2ebf
+    tmp_ctx = talloc_new(NULL);
9f2ebf
+    if (tmp_ctx == NULL) {
9f2ebf
+        return ENOMEM;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    dn = ldb_dn_new(tmp_ctx, sysdb_ctx_get_ldb(dom->sysdb), dn_attr);
9f2ebf
+    if (dn == NULL) {
9f2ebf
+        ret = ENOMEM;
9f2ebf
+        goto done;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    rdn_val = ldb_dn_get_rdn_val(dn);
9f2ebf
+    if (rdn_val == NULL) {
9f2ebf
+        DEBUG(SSSDBG_OP_FAILURE, "No RDN value?\n");
9f2ebf
+        ret = ENOMEM;
9f2ebf
+        goto done;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    rdn_str = talloc_strndup(tmp_ctx,
9f2ebf
+                               (const char *)rdn_val->data,
9f2ebf
+                               rdn_val->length);
9f2ebf
+    if (rdn_str == NULL) {
9f2ebf
+        ret = ENOMEM;
9f2ebf
+        goto done;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    ret = EOK;
9f2ebf
+    *_rdn_value = talloc_steal(mem_ctx, rdn_str);
9f2ebf
+done:
9f2ebf
+    talloc_zfree(tmp_ctx);
9f2ebf
+    return ret;
9f2ebf
+}
9f2ebf
+
9f2ebf
+static errno_t is_member_group(struct sss_domain_info *dom,
9f2ebf
+                               const char *dn_attr,
9f2ebf
+                               const char *group_rdn,
9f2ebf
+                               bool *_is_group)
9f2ebf
+{
9f2ebf
+    const char *comp_name;
9f2ebf
+    const struct ldb_val *comp_val;
9f2ebf
+    TALLOC_CTX *tmp_ctx;
9f2ebf
+    bool is_group = false;
9f2ebf
+    errno_t ret;
9f2ebf
+    struct ldb_dn *dn = NULL;
9f2ebf
+
9f2ebf
+    tmp_ctx = talloc_new(NULL);
9f2ebf
+    if (tmp_ctx == NULL) {
9f2ebf
+        return ENOMEM;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    dn = ldb_dn_new(tmp_ctx, sysdb_ctx_get_ldb(dom->sysdb), dn_attr);
9f2ebf
+    if (dn == NULL) {
9f2ebf
+        ret = ENOMEM;
9f2ebf
+        goto done;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    comp_name = ldb_dn_get_component_name(dn, 1);
9f2ebf
+    comp_val = ldb_dn_get_component_val(dn, 1);
9f2ebf
+    if (strcasecmp("cn", comp_name) == 0
9f2ebf
+            && strncasecmp(group_rdn,
9f2ebf
+                           (const char *) comp_val->data,
9f2ebf
+                           comp_val->length) == 0) {
9f2ebf
+        is_group = true;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    ret = EOK;
9f2ebf
+done:
9f2ebf
+    *_is_group = is_group;
9f2ebf
+    talloc_zfree(tmp_ctx);
9f2ebf
+    return ret;
9f2ebf
+}
9f2ebf
+
9f2ebf
+static void print_category(struct sss_domain_info *domain,
9f2ebf
+                           struct ldb_message *rule_msg,
9f2ebf
+                           const char *category_attr_name,
9f2ebf
+                           const char *category_label)
9f2ebf
+{
9f2ebf
+    struct ldb_message_element *category_attr;
9f2ebf
+
9f2ebf
+    category_attr = ldb_msg_find_element(rule_msg, category_attr_name);
9f2ebf
+    if (category_attr == NULL) {
9f2ebf
+        DEBUG(SSSDBG_MINOR_FAILURE, "Cannot find %s\n", category_attr_name);
9f2ebf
+        return;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    if (category_attr->num_values > 0) {
9f2ebf
+        PRINT("\t%s: ", category_label);
9f2ebf
+        for (unsigned i = 0; i < category_attr->num_values; i++) {
9f2ebf
+            PRINT("%s%s",
9f2ebf
+                  i > 0 ? ", " : "",
9f2ebf
+                  (const char *) category_attr->values[i].data);
9f2ebf
+        }
9f2ebf
+        PRINT("\n");
9f2ebf
+    }
9f2ebf
+}
9f2ebf
+
9f2ebf
+static void print_member_attr(struct sss_domain_info *domain,
9f2ebf
+                              struct ldb_message *rule_msg,
9f2ebf
+                              const char *member_attr_name,
9f2ebf
+                              const char *group_rdn,
9f2ebf
+                              const char *object_label,
9f2ebf
+                              const char *group_label)
9f2ebf
+{
9f2ebf
+    errno_t ret;
9f2ebf
+    TALLOC_CTX *tmp_ctx = NULL;
9f2ebf
+    const char **member_names = NULL;
9f2ebf
+    size_t name_count = 0;
9f2ebf
+    const char **member_group_names = NULL;
9f2ebf
+    size_t group_count = 0;
9f2ebf
+    struct ldb_message_element *member_attr = NULL;
9f2ebf
+
9f2ebf
+    tmp_ctx = talloc_new(NULL);
9f2ebf
+    if (tmp_ctx == NULL) {
9f2ebf
+        return;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    member_attr = ldb_msg_find_element(rule_msg, member_attr_name);
9f2ebf
+    if (member_attr == NULL) {
9f2ebf
+        DEBUG(SSSDBG_MINOR_FAILURE, "Cannot find %s\n", member_attr_name);
9f2ebf
+        goto done;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    member_names = talloc_zero_array(tmp_ctx,
9f2ebf
+                                      const char *,
9f2ebf
+                                      member_attr->num_values + 1);
9f2ebf
+    member_group_names = talloc_zero_array(tmp_ctx,
9f2ebf
+                                           const char *,
9f2ebf
+                                           member_attr->num_values + 1);
9f2ebf
+    if (member_names == NULL || member_group_names == NULL) {
9f2ebf
+        DEBUG(SSSDBG_CRIT_FAILURE, "OOM?\n");
9f2ebf
+        goto done;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    for (size_t i = 0; i < member_attr->num_values; i++) {
9f2ebf
+        bool is_group;
9f2ebf
+        const char *rdn_string;
9f2ebf
+        const char *dn_attr;
9f2ebf
+
9f2ebf
+        dn_attr = (const char *) member_attr->values[i].data;
9f2ebf
+
9f2ebf
+        ret = is_member_group(domain, dn_attr, group_rdn, &is_group);
9f2ebf
+        if (ret != EOK) {
9f2ebf
+            continue;
9f2ebf
+        }
9f2ebf
+
9f2ebf
+        ret = get_rdn_value(tmp_ctx, domain, dn_attr, &rdn_string);
9f2ebf
+        if (ret != EOK) {
9f2ebf
+            continue;
9f2ebf
+        }
9f2ebf
+
9f2ebf
+        if (is_group == false) {
9f2ebf
+            member_names[name_count] = talloc_steal(member_names,
9f2ebf
+                                                    rdn_string);
9f2ebf
+            if (member_names[name_count] == NULL) {
9f2ebf
+                goto done;
9f2ebf
+            }
9f2ebf
+            name_count++;
9f2ebf
+        } else {
9f2ebf
+            member_group_names[group_count] = talloc_strdup(member_group_names,
9f2ebf
+                                                            rdn_string);
9f2ebf
+            if (member_group_names[group_count] == NULL) {
9f2ebf
+                goto done;
9f2ebf
+            }
9f2ebf
+            group_count++;
9f2ebf
+        }
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    if (member_names[0] != NULL) {
9f2ebf
+        PRINT("\t%s: ", object_label);
9f2ebf
+        for (int i = 0; member_names[i]; i++) {
9f2ebf
+            PRINT("%s%s", i > 0 ? ", " : "", member_names[i]);
9f2ebf
+        }
9f2ebf
+        PRINT("\n");
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    if (member_group_names[0] != NULL) {
9f2ebf
+        PRINT("\t%s: ", group_label);
9f2ebf
+        for (int i = 0; member_group_names[i]; i++) {
9f2ebf
+            PRINT("%s%s", i > 0 ? ", " : "", member_group_names[i]);
9f2ebf
+        }
9f2ebf
+        PRINT("\n");
9f2ebf
+    }
9f2ebf
+
9f2ebf
+done:
9f2ebf
+    talloc_free(tmp_ctx);
9f2ebf
+}
9f2ebf
+
9f2ebf
+static void print_ipa_hbac_rule(struct sss_domain_info *domain,
9f2ebf
+                                struct ldb_message *rule_msg)
9f2ebf
+{
9f2ebf
+    struct ldb_message_element *el;
9f2ebf
+
9f2ebf
+    el = ldb_msg_find_element(rule_msg, IPA_CN);
9f2ebf
+    if (el == NULL || el->num_values < 1) {
9f2ebf
+        DEBUG(SSSDBG_MINOR_FAILURE, "A rule with no name\n");
9f2ebf
+        return;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    PRINT("Rule name: %1$s\n", el->values[0].data);
9f2ebf
+
9f2ebf
+    print_member_attr(domain,
9f2ebf
+                      rule_msg,
9f2ebf
+                      IPA_MEMBER_USER,
9f2ebf
+                      "groups",
9f2ebf
+                      _("Member users"),
9f2ebf
+                      _("Member groups"));
9f2ebf
+    print_category(domain,
9f2ebf
+                   rule_msg,
9f2ebf
+                   IPA_USER_CATEGORY,
9f2ebf
+                   _("User category"));
9f2ebf
+
9f2ebf
+    print_member_attr(domain,
9f2ebf
+                      rule_msg,
9f2ebf
+                      IPA_MEMBER_SERVICE,
9f2ebf
+                      "hbacservicegroups",
9f2ebf
+                      _("Member services"),
9f2ebf
+                      _("Member service groups"));
9f2ebf
+    print_category(domain,
9f2ebf
+                   rule_msg,
9f2ebf
+                   IPA_SERVICE_CATEGORY,
9f2ebf
+                   _("Service category"));
9f2ebf
+
9f2ebf
+    PRINT("\n");
9f2ebf
+}
9f2ebf
+
9f2ebf
+static errno_t sssctl_ipa_access_report(struct sss_tool_ctx *tool_ctx,
9f2ebf
+                                        const char *user,
9f2ebf
+                                        const char *service,
9f2ebf
+                                        struct sss_domain_info *domain)
9f2ebf
+{
9f2ebf
+    TALLOC_CTX *tmp_ctx = NULL;
9f2ebf
+    const char *filter = NULL;
9f2ebf
+    errno_t ret;
9f2ebf
+    const char *attrs[] = {
9f2ebf
+        OBJECTCLASS,
9f2ebf
+        IPA_CN,
9f2ebf
+        IPA_MEMBER_USER,
9f2ebf
+        IPA_USER_CATEGORY,
9f2ebf
+        IPA_MEMBER_SERVICE,
9f2ebf
+        IPA_SERVICE_CATEGORY,
9f2ebf
+        IPA_MEMBER_HOST,
9f2ebf
+        IPA_HOST_CATEGORY,
9f2ebf
+        NULL,
9f2ebf
+    };
9f2ebf
+    size_t rule_count;
9f2ebf
+    struct ldb_message **msgs = NULL;
9f2ebf
+
9f2ebf
+    /* Run the pam account phase to make sure the rules are fetched by SSSD */
9f2ebf
+    ret = run_pam_acct(tool_ctx, user, service, domain);
9f2ebf
+    if (ret != PAM_SUCCESS && ret != PAM_PERM_DENIED) {
9f2ebf
+        ERROR("Cannot run the PAM account phase, reporting stale rules\n");
9f2ebf
+        /* Non-fatal */
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    tmp_ctx = talloc_new(tool_ctx);
9f2ebf
+    if (tmp_ctx == NULL) {
9f2ebf
+        return ENOMEM;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    filter = talloc_asprintf(tmp_ctx, "(objectClass=%s)", IPA_HBAC_RULE);
9f2ebf
+    if (filter == NULL) {
9f2ebf
+        ret = ENOMEM;
9f2ebf
+        goto done;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    ret = sysdb_search_custom(tmp_ctx, domain, filter,
9f2ebf
+                              HBAC_RULES_SUBDIR, attrs,
9f2ebf
+                              &rule_count, &msgs);
9f2ebf
+    if (ret != EOK && ret != ENOENT) {
9f2ebf
+        DEBUG(SSSDBG_CRIT_FAILURE, "Error looking up HBAC rules\n");
9f2ebf
+        goto done;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    if (ret == ENOENT) {
9f2ebf
+        PRINT("No cached rules. All users will be denied access\n");
9f2ebf
+        ret = EOK;
9f2ebf
+        goto done;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    PRINT("%1$zu rules cached\n\n", rule_count);
9f2ebf
+
9f2ebf
+    for (size_t i = 0; i < rule_count; i++) {
9f2ebf
+        print_ipa_hbac_rule(domain, msgs[i]);
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    ret = EOK;
9f2ebf
+done:
9f2ebf
+    talloc_zfree(tmp_ctx);
9f2ebf
+    return ret;
9f2ebf
+}
9f2ebf
+
9f2ebf
+sssctl_dom_access_reporter_fn get_report_fn(const char *provider)
9f2ebf
+{
9f2ebf
+    if (strcmp(provider, "ipa") == 0) {
9f2ebf
+        return sssctl_ipa_access_report;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    return NULL;
9f2ebf
+}
9f2ebf
+
9f2ebf
+errno_t sssctl_access_report(struct sss_cmdline *cmdline,
9f2ebf
+                             struct sss_tool_ctx *tool_ctx,
9f2ebf
+                             void *pvt)
9f2ebf
+{
9f2ebf
+    errno_t ret;
9f2ebf
+    const char *domname = NULL;
9f2ebf
+    sssctl_dom_access_reporter_fn reporter;
9f2ebf
+    struct sss_domain_info *dom;
9f2ebf
+    const char *user = DEFAULT_USER;
9f2ebf
+    const char *service = DEFAULT_SERVICE;
9f2ebf
+
9f2ebf
+    /* Parse command line. */
9f2ebf
+    struct poptOption options[] = {
9f2ebf
+        { "user", 'u', POPT_ARG_STRING, &user, 0,
9f2ebf
+          _("PAM user, default: " DEFAULT_USER), NULL },
9f2ebf
+        { "service", 's', POPT_ARG_STRING, &service, 0,
9f2ebf
+          _("PAM service, default: " DEFAULT_SERVICE), NULL },
9f2ebf
+        POPT_TABLEEND
9f2ebf
+    };
9f2ebf
+
9f2ebf
+    ret = sss_tool_popt_ex(cmdline, options, SSS_TOOL_OPT_OPTIONAL,
9f2ebf
+                           NULL, NULL, "DOMAIN", _("Specify domain name."),
9f2ebf
+                           &domname, NULL);
9f2ebf
+    if (ret != EOK) {
9f2ebf
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
9f2ebf
+        return ret;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    dom = find_domain_by_name(tool_ctx->domains, domname, true);
9f2ebf
+    if (dom == NULL) {
9f2ebf
+        ERROR("Cannot find domain %1$s\n", domname);
9f2ebf
+        return ERR_DOMAIN_NOT_FOUND;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    reporter = get_report_fn(dom->provider);
9f2ebf
+    if (reporter == NULL) {
9f2ebf
+        ERROR("Access report not implemented for domains of type %1$s\n",
9f2ebf
+              dom->provider);
9f2ebf
+        return ret;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    return reporter(tool_ctx, user, service, dom);
9f2ebf
+}
9f2ebf
-- 
9f2ebf
2.14.3
9f2ebf