summaryrefslogtreecommitdiffstats
path: root/src/tools/sssctl
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
commit74aa0bc6779af38018a03fd2cf4419fe85917904 (patch)
tree9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/tools/sssctl
parentInitial commit. (diff)
downloadsssd-74aa0bc6779af38018a03fd2cf4419fe85917904.tar.xz
sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.zip
Adding upstream version 2.9.4.upstream/2.9.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/sssctl')
-rw-r--r--src/tools/sssctl/sssctl.c354
-rw-r--r--src/tools/sssctl/sssctl.h152
-rw-r--r--src/tools/sssctl/sssctl_access_report.c422
-rw-r--r--src/tools/sssctl/sssctl_cache.c718
-rw-r--r--src/tools/sssctl/sssctl_cert.c289
-rw-r--r--src/tools/sssctl/sssctl_config.c201
-rw-r--r--src/tools/sssctl/sssctl_data.c538
-rw-r--r--src/tools/sssctl/sssctl_domains.c408
-rw-r--r--src/tools/sssctl/sssctl_logs.c608
-rw-r--r--src/tools/sssctl/sssctl_passkey.c42
-rw-r--r--src/tools/sssctl/sssctl_systemd.c95
-rw-r--r--src/tools/sssctl/sssctl_user_checks.c321
12 files changed, 4148 insertions, 0 deletions
diff --git a/src/tools/sssctl/sssctl.c b/src/tools/sssctl/sssctl.c
new file mode 100644
index 0000000..85d0d09
--- /dev/null
+++ b/src/tools/sssctl/sssctl.c
@@ -0,0 +1,354 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 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 <stdio.h>
+#include <sys/wait.h>
+
+#include "util/util.h"
+#include "tools/sssctl/sssctl.h"
+#include "tools/common/sss_tools.h"
+#include "tools/common/sss_process.h"
+
+static const char *
+sssctl_prompt_str(enum sssctl_prompt_result result)
+{
+ switch (result) {
+ case SSSCTL_PROMPT_YES:
+ return _("yes");
+ case SSSCTL_PROMPT_NO:
+ return _("no");
+ case SSSCTL_PROMPT_ERROR:
+ return _("error");
+ }
+
+ return _("Invalid result.");
+}
+
+enum sssctl_prompt_result
+sssctl_prompt(const char *message,
+ enum sssctl_prompt_result defval)
+{
+ char answer[255] = {0};
+ int c;
+ const char *yes = sssctl_prompt_str(SSSCTL_PROMPT_YES);
+ const char *no = sssctl_prompt_str(SSSCTL_PROMPT_NO);
+ int attempts = 0;
+ int ret;
+
+ do {
+ if (defval != SSSCTL_PROMPT_ERROR) {
+ printf("%s (%s/%s) [%s] ", message, yes, no,
+ sssctl_prompt_str(defval));
+
+ /* Detect empty line. */
+ c = getchar();
+ if (c == '\n') {
+ return defval;
+ } else {
+ ungetc(c, stdin);
+ }
+ } else {
+ printf("%s (%s/%s)", message, yes, no);
+ }
+
+ ret = scanf("%254s", answer);
+
+ /* Clear stdin. */
+ while ((c = getchar()) != '\n' && c != EOF);
+
+ if (ret != 1) {
+ ERROR("Unable to read user input\n");
+ return SSSCTL_PROMPT_ERROR;
+ }
+
+
+ if (strcasecmp(yes, answer) == 0) {
+ return SSSCTL_PROMPT_YES;
+ }
+
+ if (strcasecmp(no, answer) == 0) {
+ return SSSCTL_PROMPT_NO;
+ }
+
+ ERROR("Invalid input, please provide either "
+ "'%s' or '%s'.\n", yes, no);
+
+ attempts++;
+ } while (attempts < 3);
+
+ return SSSCTL_PROMPT_ERROR;
+}
+
+errno_t sssctl_wrap_command(const char *command,
+ const char *subcommand,
+ struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ errno_t ret;
+
+ if (subcommand != NULL) {
+ cmdline->argc++;
+ }
+
+ const char **args = talloc_array_size(tool_ctx,
+ sizeof(char *),
+ cmdline->argc + 2);
+ if (!args) {
+ return ENOMEM;
+ }
+
+ args[0] = command;
+
+ if (subcommand != NULL) {
+ args[1] = subcommand;
+ memcpy(&args[2], cmdline->argv, sizeof(char *) * cmdline->argc);
+ } else {
+ memcpy(&args[1], cmdline->argv, sizeof(char *) * cmdline->argc);
+ }
+
+ args[cmdline->argc + 1] = NULL;
+
+ ret = sssctl_run_command(args);
+
+ talloc_free(args);
+
+ return ret;
+}
+
+errno_t sssctl_run_command(const char *const argv[])
+{
+ int ret;
+ int wstatus;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Running '%s'\n", argv[0]);
+
+ ret = fork();
+ if (ret == -1) {
+ ERROR("Error while executing external command\n");
+ return EFAULT;
+ }
+
+ if (ret == 0) {
+ /* cast is safe - see
+ https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
+ "The statement about argv[] and envp[] being constants ... "
+ */
+ execvp(argv[0], discard_const_p(char * const, argv));
+ ERROR("Error while executing external command\n");
+ _exit(1);
+ } else {
+ if (waitpid(ret, &wstatus, 0) == -1) {
+ ERROR("Error while executing external command '%s'\n", argv[0]);
+ return EFAULT;
+ } else if (WEXITSTATUS(wstatus) != 0) {
+ ERROR("Command '%s' failed with [%d]\n",
+ argv[0], WEXITSTATUS(wstatus));
+ return EIO;
+ }
+ }
+
+ return EOK;
+}
+
+static errno_t sssctl_manage_service(enum sssctl_svc_action action)
+{
+#ifdef HAVE_SYSTEMD
+ switch (action) {
+ case SSSCTL_SVC_START:
+ return sssctl_systemd_start();
+ case SSSCTL_SVC_STOP:
+ return sssctl_systemd_stop();
+ case SSSCTL_SVC_RESTART:
+ return sssctl_systemd_restart();
+ }
+#elif defined(HAVE_SERVICE)
+ switch (action) {
+ case SSSCTL_SVC_START:
+ return sssctl_run_command(
+ (const char *[]){SERVICE_PATH, "sssd", "start", NULL});
+ case SSSCTL_SVC_STOP:
+ return sssctl_run_command(
+ (const char *[]){SERVICE_PATH, "sssd", "stop", NULL});
+ case SSSCTL_SVC_RESTART:
+ return sssctl_run_command(
+ (const char *[]){SERVICE_PATH, "sssd", "restart", NULL});
+ }
+#endif
+
+ return ENOSYS;
+}
+
+bool sssctl_start_sssd(bool force)
+{
+ enum sssctl_prompt_result prompt;
+ errno_t ret;
+
+ if (sss_daemon_running()) {
+ return true;
+ }
+
+ if (!force) {
+ prompt = sssctl_prompt(_("SSSD needs to be running. Start SSSD now?"),
+ SSSCTL_PROMPT_YES);
+ switch (prompt) {
+ case SSSCTL_PROMPT_YES:
+ /* continue */
+ break;
+ case SSSCTL_PROMPT_NO:
+ case SSSCTL_PROMPT_ERROR:
+ return false;
+ }
+ }
+
+ ret = sssctl_manage_service(SSSCTL_SVC_START);
+ switch(ret) {
+ case EOK:
+ return true;
+ case ENOSYS:
+ fprintf(stderr, "Starting SSSD automatically is not supported "
+ "on this platform, please start the service "
+ "manually\n");
+ return false;
+ default:
+ fprintf(stderr, "Unable to start SSSD!\n");
+ return false;
+ }
+
+ return true;
+}
+
+bool sssctl_stop_sssd(bool force)
+{
+ enum sssctl_prompt_result prompt;
+ errno_t ret;
+
+ if (!sss_daemon_running()) {
+ return true;
+ }
+
+ if (!force) {
+ prompt = sssctl_prompt(_("SSSD must not be running. Stop SSSD now?"),
+ SSSCTL_PROMPT_YES);
+ switch (prompt) {
+ case SSSCTL_PROMPT_YES:
+ /* continue */
+ break;
+ case SSSCTL_PROMPT_NO:
+ case SSSCTL_PROMPT_ERROR:
+ return false;
+ }
+ }
+
+ ret = sssctl_manage_service(SSSCTL_SVC_STOP);
+ switch(ret) {
+ case EOK:
+ return true;
+ case ENOSYS:
+ fprintf(stderr, "Stopping SSSD automatically is not supported "
+ "on this platform, please stop the service "
+ "manually\n");
+ return false;
+ default:
+ fprintf(stderr, "Unable to stop SSSD!\n");
+ return false;
+ }
+
+
+ return true;
+}
+
+bool sssctl_restart_sssd(bool force)
+{
+ enum sssctl_prompt_result prompt;
+ errno_t ret;
+
+ if (!force) {
+ prompt = sssctl_prompt(_("SSSD needs to be restarted. Restart SSSD now?"),
+ SSSCTL_PROMPT_YES);
+ switch (prompt) {
+ case SSSCTL_PROMPT_YES:
+ /* continue */
+ break;
+ case SSSCTL_PROMPT_NO:
+ case SSSCTL_PROMPT_ERROR:
+ return false;
+ }
+ }
+
+ ret = sssctl_manage_service(SSSCTL_SVC_RESTART);
+ switch(ret) {
+ case EOK:
+ return true;
+ case ENOSYS:
+ fprintf(stderr, "Restarting SSSD automatically is not supported "
+ "on this platform, please restart the service "
+ "manually\n");
+ return false;
+ default:
+ fprintf(stderr, "Unable to restart SSSD!\n");
+ return false;
+ }
+
+ return true;
+}
+
+int main(int argc, const char **argv)
+{
+ struct sss_route_cmd commands[] = {
+ SSS_TOOL_DELIMITER("SSSD Status:"),
+ 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_FLAGS("user-checks", "Print information about a user and check authentication", 0, sssctl_user_checks, SSS_TOOL_FLAG_SKIP_CMD_INIT|SSS_TOOL_FLAG_SKIP_ROOT_CHECK),
+ 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),
+ SSS_TOOL_COMMAND("netgroup-show", "Information about cached netgroup", 0, sssctl_netgroup_show),
+ SSS_TOOL_DELIMITER("Local data tools:"),
+ SSS_TOOL_COMMAND("client-data-backup", "Backup local data", 0, sssctl_client_data_backup),
+ SSS_TOOL_COMMAND("client-data-restore", "Restore local data from backup", 0, sssctl_client_data_restore),
+ SSS_TOOL_COMMAND("cache-remove", "Backup local data and remove cached content", 0, sssctl_cache_remove),
+ SSS_TOOL_COMMAND("cache-upgrade", "Perform cache upgrade", ERR_SYSDB_VERSION_TOO_OLD, sssctl_cache_upgrade),
+ SSS_TOOL_COMMAND("cache-expire", "Invalidate cached objects", 0, sssctl_cache_expire),
+ SSS_TOOL_COMMAND("cache-index", "Manage cache indexes", 0, sssctl_cache_index),
+ SSS_TOOL_DELIMITER("Log files tools:"),
+ SSS_TOOL_COMMAND("logs-remove", "Remove existing SSSD log files", 0, sssctl_logs_remove),
+ SSS_TOOL_COMMAND("logs-fetch", "Archive SSSD log files in tarball", 0, sssctl_logs_fetch),
+ SSS_TOOL_COMMAND("debug-level", "Change or print information about SSSD debug level", 0, sssctl_debug_level),
+ SSS_TOOL_COMMAND_FLAGS("analyze", "Analyze logged data", 0, sssctl_analyze, SSS_TOOL_FLAG_SKIP_CMD_INIT|SSS_TOOL_FLAG_SKIP_ROOT_CHECK),
+#ifdef HAVE_LIBINI_CONFIG_V1_3
+ SSS_TOOL_DELIMITER("Configuration files tools:"),
+ SSS_TOOL_COMMAND_FLAGS("config-check", "Perform static analysis of SSSD configuration", 0, sssctl_config_check, SSS_TOOL_FLAG_SKIP_CMD_INIT),
+#endif
+ SSS_TOOL_DELIMITER("Certificate related tools:"),
+ SSS_TOOL_COMMAND_FLAGS("cert-show", "Print information about the certificate", 0, sssctl_cert_show, SSS_TOOL_FLAG_SKIP_CMD_INIT|SSS_TOOL_FLAG_SKIP_ROOT_CHECK),
+ SSS_TOOL_COMMAND("cert-map", "Show users mapped to the certificate", 0, sssctl_cert_map),
+ SSS_TOOL_COMMAND_FLAGS("cert-eval-rule", "Check mapping and matching rule with a certificate", 0, sssctl_cert_eval_rule, SSS_TOOL_FLAG_SKIP_CMD_INIT|SSS_TOOL_FLAG_SKIP_ROOT_CHECK),
+#ifdef BUILD_PASSKEY
+ SSS_TOOL_DELIMITER("Passkey related tools:"),
+ SSS_TOOL_COMMAND_FLAGS("passkey-register", "Perform passkey registration", 0, sssctl_passkey_register, SSS_TOOL_FLAG_SKIP_CMD_INIT|SSS_TOOL_FLAG_SKIP_ROOT_CHECK),
+#endif
+ SSS_TOOL_LAST
+ };
+
+ return sss_tool_main(argc, argv, commands, NULL);
+}
diff --git a/src/tools/sssctl/sssctl.h b/src/tools/sssctl/sssctl.h
new file mode 100644
index 0000000..3a53a89
--- /dev/null
+++ b/src/tools/sssctl/sssctl.h
@@ -0,0 +1,152 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 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 _SSSCTL_H_
+#define _SSSCTL_H_
+
+#include "tools/common/sss_tools.h"
+
+#define PRINT_IFP_WARNING(ret) do { \
+ if (ret == ERR_SBUS_UNKNOWN_SERVICE || ret == ERR_SBUS_NO_REPLY || ret == ETIMEDOUT) { \
+ fprintf(stderr, _("InfoPipe operation failed. Check that SSSD " \
+ "is running and the InfoPipe responder is enabled. Make sure " \
+ "'ifp' is listed in the 'services' option in sssd.conf.")); \
+ } \
+} while (0)
+
+enum sssctl_prompt_result {
+ SSSCTL_PROMPT_YES,
+ SSSCTL_PROMPT_NO,
+ SSSCTL_PROMPT_ERROR
+};
+
+enum sssctl_svc_action {
+ SSSCTL_SVC_START,
+ SSSCTL_SVC_STOP,
+ SSSCTL_SVC_RESTART
+};
+
+enum sssctl_prompt_result
+sssctl_prompt(const char *message,
+ enum sssctl_prompt_result defval);
+
+errno_t sssctl_wrap_command(const char *command,
+ const char *subcommand,
+ struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+errno_t sssctl_run_command(const char *const argv[]); /* argv[0] - command */
+bool sssctl_start_sssd(bool force);
+bool sssctl_stop_sssd(bool force);
+bool sssctl_restart_sssd(bool force);
+
+errno_t sssctl_systemd_start(void);
+errno_t sssctl_systemd_stop(void);
+errno_t sssctl_systemd_restart(void);
+
+errno_t sssctl_domain_list(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_domain_status(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_client_data_backup(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_client_data_restore(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_cache_remove(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_cache_upgrade(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_cache_expire(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_cache_index(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_logs_remove(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_logs_fetch(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_debug_level(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_analyze(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_user_show(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_group_show(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_netgroup_show(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_config_check(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+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);
+
+errno_t sssctl_cert_show(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_cert_map(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+#ifdef BUILD_PASSKEY
+errno_t sssctl_passkey_register(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+#endif /* BUILD_PASSKEY */
+
+errno_t sssctl_cert_eval_rule(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 0000000..5ae873d
--- /dev/null
+++ b/src/tools/sssctl/sssctl_access_report.c
@@ -0,0 +1,422 @@
+/*
+ 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 "util/util.h"
+#include "tools/common/sss_tools.h"
+#include "tools/sssctl/sssctl.h"
+#include "sbus/sbus_opath.h"
+#include "responder/ifp/ifp_iface/ifp_iface_sync.h"
+
+/*
+ * We're searching the cache directly..
+ */
+#include "providers/ipa/ipa_hbac_private.h"
+#include "providers/ipa/ipa_rules_common.h"
+
+typedef errno_t (*sssctl_dom_access_reporter_fn)(struct sss_tool_ctx *tool_ctx,
+ struct sss_domain_info *domain);
+
+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 refresh_hbac_rules(struct sss_tool_ctx *tool_ctx,
+ struct sss_domain_info *domain)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sbus_sync_connection *conn;
+ const char *path;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ path = sbus_opath_compose(tmp_ctx, IFP_PATH_DOMAINS, domain->name);
+ if (path == NULL) {
+ PRINT("Out of memory!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ conn = sbus_sync_connect_system(tmp_ctx, NULL);
+ if (conn == NULL) {
+ ERROR("Unable to connect to system bus!\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = sbus_call_ifp_domain_RefreshAccessRules(conn, IFP_BUS, path);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to refresh HBAC rules [%d]: %s\n",
+ ret, sss_strerror(ret));
+ PRINT_IFP_WARNING(ret);
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t sssctl_ipa_access_report(struct sss_tool_ctx *tool_ctx,
+ 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 = refresh_hbac_rules(tool_ctx, domain);
+ if (ret != EOK) {
+ ERROR("Unable to refresh HBAC rules, using cached content\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;
+
+ ret = sss_tool_popt_ex(cmdline, NULL, SSS_TOOL_OPT_OPTIONAL,
+ NULL, NULL, "DOMAIN", _("Specify domain name."),
+ SSS_TOOL_OPT_REQUIRED, &domname, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ goto done;
+ }
+
+ dom = find_domain_by_name(tool_ctx->domains, domname, true);
+ if (dom == NULL) {
+ ERROR("Cannot find domain %1$s\n", domname);
+ ret = ERR_DOMAIN_NOT_FOUND;
+ goto done;
+ }
+
+ reporter = get_report_fn(dom->provider);
+ if (reporter == NULL) {
+ ERROR("Access report not implemented for domains of type %1$s\n",
+ dom->provider);
+ goto done;
+ }
+
+ ret = reporter(tool_ctx, dom);
+
+done:
+ free(discard_const(domname));
+
+ return ret;
+}
diff --git a/src/tools/sssctl/sssctl_cache.c b/src/tools/sssctl/sssctl_cache.c
new file mode 100644
index 0000000..5a62a26
--- /dev/null
+++ b/src/tools/sssctl/sssctl_cache.c
@@ -0,0 +1,718 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 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 <popt.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+
+#include "util/util.h"
+#include "db/sysdb.h"
+#include "tools/common/sss_tools.h"
+#include "tools/sssctl/sssctl.h"
+
+#define NOT_FOUND_MSG(obj) _(obj " %s is not present in cache.\n")
+
+#define SSSCTL_CACHE_NAME {_("Name"), SYSDB_NAME, get_attr_name}
+#define SSSCTL_CACHE_CREATE {_("Cache entry creation date"), SYSDB_CREATE_TIME, get_attr_time}
+#define SSSCTL_CACHE_UPDATE {_("Cache entry last update time"), SYSDB_LAST_UPDATE, get_attr_time}
+#define SSSCTL_CACHE_EXPIRE {_("Cache entry expiration time"), SYSDB_CACHE_EXPIRE, get_attr_expire}
+#define SSSCTL_CACHE_IFP {_("Cached in InfoPipe"), SYSDB_IFP_CACHED, get_attr_yesno}
+#define SSSCTL_CACHE_NULL {NULL, NULL, NULL}
+
+enum cache_object {
+ CACHED_USER,
+ CACHED_GROUP,
+ CACHED_NETGROUP,
+};
+
+typedef errno_t (*sssctl_attr_fn)(TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs *entry,
+ struct sss_domain_info *dom,
+ const char *attr,
+ const char **_value);
+
+typedef struct ldb_dn *(*sssctl_basedn_fn)(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain);
+
+struct sssctl_object_info {
+ const char *msg;
+ const char *attr;
+ sssctl_attr_fn attr_fn;
+};
+
+static errno_t time_to_string(TALLOC_CTX *mem_ctx,
+ time_t timestamp,
+ const char **_value)
+{
+ const char *value;
+ struct tm *tm;
+ char str[255];
+ size_t ret;
+
+ tm = localtime(&timestamp);
+ if (tm == NULL) {
+ return ENOMEM;
+ }
+
+ ret = strftime(str, 255, "%x %X", tm);
+ if (ret == 0) {
+ return ERANGE;
+ }
+
+ value = talloc_strdup(mem_ctx, str);
+ if (value == NULL) {
+ return ENOMEM;
+ }
+
+ *_value = value;
+
+ return EOK;
+}
+
+static errno_t get_attr_name(TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs *entry,
+ struct sss_domain_info *dom,
+ const char *attr,
+ const char **_value)
+{
+ errno_t ret;
+ const char *orig_name;
+ char *tmp_name;
+ char *outname;
+
+ ret = sysdb_attrs_get_string(entry, attr, &orig_name);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ tmp_name = sss_output_name(mem_ctx, orig_name, dom->case_preserve, 0);
+ if (tmp_name == NULL) {
+ return ENOMEM;
+ }
+
+ if (dom->fqnames) {
+ outname = sss_tc_fqname(mem_ctx, dom->names, dom, tmp_name);
+ talloc_free(tmp_name);
+ if (outname == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_tc_fqname() failed\n");
+ return ENOMEM;
+ }
+ } else {
+ outname = tmp_name;
+ }
+
+ *_value = outname;
+ return EOK;
+}
+
+static errno_t get_attr_time(TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs *entry,
+ struct sss_domain_info *dom,
+ const char *attr,
+ const char **_value)
+{
+ uint32_t value;
+ errno_t ret;
+
+ ret = sysdb_attrs_get_uint32_t(entry, attr, &value);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return time_to_string(mem_ctx, value, _value);
+}
+
+static errno_t get_attr_expire(TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs *entry,
+ struct sss_domain_info *dom,
+ const char *attr,
+ const char **_value)
+{
+ uint32_t value;
+ errno_t ret;
+
+ ret = sysdb_attrs_get_uint32_t(entry, attr, &value);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ if (is_files_provider(dom)) {
+ *_value = "Never";
+ return EOK;
+ }
+
+ if (value < time(NULL)) {
+ *_value = "Expired";
+ return EOK;
+ }
+
+ return time_to_string(mem_ctx, value, _value);
+}
+
+static errno_t attr_initgr(TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs *entry,
+ struct sss_domain_info *dom,
+ const char *attr,
+ const char **_value)
+{
+ uint32_t value;
+ errno_t ret;
+
+ ret = sysdb_attrs_get_uint32_t(entry, attr, &value);
+ if (ret == ENOENT || (ret == EOK && value == 0)) {
+ *_value = "Initgroups were not yet performed";
+ return EOK;
+ } else if (ret != EOK) {
+ return ret;
+ }
+
+ if (is_files_provider(dom)) {
+ *_value = "Never";
+ return EOK;
+ }
+
+ if (value < time(NULL)) {
+ *_value = "Expired";
+ return EOK;
+ }
+
+ return time_to_string(mem_ctx, value, _value);
+}
+
+static errno_t get_attr_yesno(TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs *entry,
+ struct sss_domain_info *dom,
+ const char *attr,
+ const char **_value)
+{
+ errno_t ret;
+ bool val;
+
+ ret = sysdb_attrs_get_bool(entry, attr, &val);
+ if (ret == ENOENT) {
+ val = 0;
+ } else if (ret != EOK) {
+ return ret;
+ }
+
+ *_value = val ? "Yes" : "No";
+
+ return EOK;
+}
+
+static const char **sssctl_build_attrs(TALLOC_CTX *mem_ctx,
+ struct sssctl_object_info *info)
+{
+ const char **attrs;
+ size_t count;
+ int i;
+
+ for (count = 0; info[count].attr != NULL; count++) {
+ /* no op */
+ }
+
+ attrs = talloc_zero_array(mem_ctx, const char *, count + 1);
+ if (attrs == NULL) {
+ return NULL;
+ }
+
+ for (i = 0; i < count; i++) {
+ attrs[i] = talloc_strdup(attrs, info[i].attr);
+ if (attrs[i] == NULL) {
+ talloc_free(attrs);
+ return NULL;
+ }
+ }
+
+ return attrs;
+}
+
+static errno_t sssctl_query_cache(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct ldb_dn *base_dn,
+ const char *filter,
+ const char **attrs,
+ struct sysdb_attrs **_entry)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sysdb_attrs **sysdb_attrs;
+ struct ldb_message **msgs;
+ size_t count;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ ret = sysdb_search_entry(tmp_ctx, sysdb, base_dn, LDB_SCOPE_SUBTREE,
+ filter, attrs, &count, &msgs);
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No result\n");
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to search sysdb "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (count != 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Search returned more than one result!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = sysdb_msg2attrs(tmp_ctx, count, msgs, &sysdb_attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to convert message to sysdb attrs "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ *_entry = talloc_steal(mem_ctx, sysdb_attrs[0]);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static const char *sssctl_create_filter(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ enum cache_object obj_type,
+ const char *attr_name,
+ const char *attr_value)
+{
+ const char *class;
+ const char *filter;
+ char *filter_value;
+ bool qualify_attr = false;
+
+ if (strcmp(attr_name, SYSDB_NAME) == 0) {
+ if (obj_type == CACHED_USER || obj_type == CACHED_GROUP) {
+ qualify_attr = true;
+ }
+ }
+
+ switch (obj_type) {
+ case CACHED_USER:
+ class = SYSDB_USER_CLASS;
+ break;
+ case CACHED_GROUP:
+ class = SYSDB_GROUP_CLASS;
+ break;
+ case CACHED_NETGROUP:
+ class = SYSDB_NETGROUP_CLASS;
+ break;
+ default:
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "sssctl doesn't handle this object type (type=%d)\n", obj_type);
+ return NULL;
+ }
+
+ if (qualify_attr) {
+ filter_value = sss_create_internal_fqname(NULL, attr_value, dom->name);
+ } else {
+ filter_value = talloc_strdup(NULL, attr_value);
+ }
+ if (filter_value == NULL) {
+ return NULL;
+ }
+
+ if (dom->case_sensitive == false) {
+ char *filter_value_old;
+
+ filter_value_old = filter_value;
+ filter_value = sss_tc_utf8_str_tolower(mem_ctx, filter_value_old);
+ talloc_free(filter_value_old);
+ }
+
+ filter = talloc_asprintf(mem_ctx, "(&(%s=%s)(|(%s=%s)(%s=%s)))",
+ obj_type == CACHED_NETGROUP ? SYSDB_OBJECTCLASS : SYSDB_OBJECTCATEGORY,
+ class, attr_name, filter_value,
+ SYSDB_NAME_ALIAS, filter_value);
+
+ talloc_free(filter_value);
+
+ return filter;
+}
+
+static errno_t sssctl_find_object(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domains,
+ struct sss_domain_info *domain,
+ sssctl_basedn_fn basedn_fn,
+ enum cache_object obj_type,
+ const char *attr_name,
+ const char *attr_value,
+ const char **attrs,
+ struct sysdb_attrs **_entry,
+ struct sss_domain_info **_dom)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sss_domain_info *dom;
+ struct sysdb_attrs *entry = NULL;
+ struct ldb_dn *base_dn;
+ bool fqn_provided;
+ const char *filter;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ dom = domain == NULL ? domains : domain;
+ fqn_provided = domain == NULL ? false : true;
+ while (dom != NULL) {
+ if (!fqn_provided && dom->fqnames) {
+ dom = get_next_domain(dom, 0);
+ continue;
+ }
+
+ base_dn = basedn_fn(tmp_ctx, dom);
+ if (base_dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ filter = sssctl_create_filter(tmp_ctx, dom, obj_type,
+ attr_name, attr_value);
+ if (filter == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create filter\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sssctl_query_cache(tmp_ctx, dom->sysdb, base_dn, filter,
+ attrs, &entry);
+ switch(ret) {
+ case EOK:
+ /* Entry was found. */
+ *_entry = talloc_steal(mem_ctx, entry);
+ *_dom = dom;
+ goto done;
+ case ENOENT:
+ if (fqn_provided) {
+ /* Not found but a domain was provided in input. We're done. */
+ goto done;
+ }
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to query cache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ dom = get_next_domain(dom, 0);
+ }
+
+ ret = ENOENT;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t sssctl_fetch_object(TALLOC_CTX *mem_ctx,
+ struct sssctl_object_info *info,
+ struct sss_domain_info *domains,
+ struct sss_domain_info *domain,
+ sssctl_basedn_fn basedn_fn,
+ enum cache_object obj_type,
+ const char *attr_name,
+ const char *attr_value,
+ struct sysdb_attrs **_entry,
+ struct sss_domain_info **_dom)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sysdb_attrs *entry = NULL;
+ struct sss_domain_info *dom = NULL;
+ const char **attrs;
+ char *sanitized;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ ret = sss_filter_sanitize(tmp_ctx, attr_value, &sanitized);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to sanitize input [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ attrs = sssctl_build_attrs(tmp_ctx, info);
+ if (attrs == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get attribute list!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sssctl_find_object(tmp_ctx, domains, domain, basedn_fn,
+ obj_type, attr_name, sanitized, attrs,
+ &entry, &dom);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to query cache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ *_entry = talloc_steal(mem_ctx, entry);
+ *_dom = dom;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t sssctl_print_object(struct sssctl_object_info *info,
+ struct sss_domain_info *domains,
+ struct sss_domain_info *domain,
+ sssctl_basedn_fn basedn_fn,
+ const char *noent_fmt,
+ enum cache_object obj_type,
+ const char *attr_name,
+ const char *attr_value)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sysdb_attrs *entry = NULL;
+ const char *value;
+ errno_t ret;
+ int i;
+ struct sss_domain_info *dom = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ ret = sssctl_fetch_object(tmp_ctx, info, domains, domain, basedn_fn,
+ obj_type, attr_name, attr_value,
+ &entry, &dom);
+ if (ret == ENOENT) {
+ printf(noent_fmt, attr_value);
+ ret = EOK;
+ goto done;
+ } else if (ret != EOK) {
+ ERROR("Error: Unable to get object [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (dom == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not determine object domain\n");
+ ret = ERR_DOMAIN_NOT_FOUND;
+ goto done;
+ }
+
+ for (i = 0; info[i].attr != NULL; i++) {
+ ret = info[i].attr_fn(tmp_ctx, entry, dom, info[i].attr, &value);
+ if (ret == ENOENT) {
+ continue;
+ } else if (ret != EOK) {
+ ERROR("%s: Unable to read value [%d]: %s\n",
+ info[i].msg, ret, sss_strerror(ret));
+ continue;
+ }
+
+ printf("%s: %s\n", info[i].msg, value);
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t parse_cmdline(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ struct poptOption *options,
+ const char **_orig_name,
+ struct sss_domain_info **_domain)
+{
+ const char *input_name = NULL;
+ const char *orig_name;
+ struct sss_domain_info *domain;
+ int ret;
+
+ ret = sss_tool_popt_ex(cmdline, options, SSS_TOOL_OPT_OPTIONAL,
+ NULL, NULL, "NAME", _("Specify name."),
+ SSS_TOOL_OPT_REQUIRED, &input_name, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ goto done;
+ }
+
+ ret = sss_tool_parse_name(tool_ctx, tool_ctx, input_name,
+ &orig_name, &domain);
+ if (ret != EOK) {
+ ERROR("Unable to parse name %s.\n", input_name);
+ goto done;
+ }
+
+ *_orig_name = orig_name;
+ *_domain = domain;
+
+done:
+ free(discard_const(input_name));
+
+ return ret;
+}
+
+struct sssctl_cache_opts {
+ struct sss_domain_info *domain;
+ const char *value;
+ int sid;
+ int id;
+};
+
+errno_t sssctl_user_show(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ struct sssctl_cache_opts opts = {0};
+ const char *attr;
+ errno_t ret;
+
+ struct poptOption options[] = {
+ {"sid", 's', POPT_ARG_NONE , &opts.sid, 0, _("Search by SID"), NULL },
+ {"uid", 'u', POPT_ARG_NONE, &opts.id, 0, _("Search by user ID"), NULL },
+ POPT_TABLEEND
+ };
+
+ struct sssctl_object_info info[] = {
+ SSSCTL_CACHE_NAME,
+ SSSCTL_CACHE_CREATE,
+ SSSCTL_CACHE_UPDATE,
+ SSSCTL_CACHE_EXPIRE,
+ {_("Initgroups expiration time"), SYSDB_INITGR_EXPIRE, attr_initgr},
+ SSSCTL_CACHE_IFP,
+ SSSCTL_CACHE_NULL
+ };
+
+ ret = parse_cmdline(cmdline, tool_ctx, options, &opts.value, &opts.domain);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ attr = SYSDB_NAME;
+ if (opts.sid) {
+ attr = SYSDB_SID;
+ } else if (opts.id) {
+ attr = SYSDB_UIDNUM;
+ }
+
+ ret = sssctl_print_object(info, tool_ctx->domains, opts.domain,
+ sysdb_user_base_dn, NOT_FOUND_MSG("User"),
+ CACHED_USER, attr, opts.value);
+ if (ret != EOK) {
+ return ret;
+ }
+
+
+ return EOK;
+}
+
+errno_t sssctl_group_show(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ struct sssctl_cache_opts opts = {0};
+ const char *attr;
+ errno_t ret;
+
+ struct poptOption options[] = {
+ {"sid", 's', POPT_ARG_NONE , &opts.sid, 0, _("Search by SID"), NULL },
+ {"gid", 'g', POPT_ARG_NONE, &opts.id, 0, _("Search by group ID"), NULL },
+ POPT_TABLEEND
+ };
+
+ struct sssctl_object_info info[] = {
+ SSSCTL_CACHE_NAME,
+ SSSCTL_CACHE_CREATE,
+ SSSCTL_CACHE_UPDATE,
+ SSSCTL_CACHE_EXPIRE,
+ SSSCTL_CACHE_IFP,
+ SSSCTL_CACHE_NULL
+ };
+
+ ret = parse_cmdline(cmdline, tool_ctx, options, &opts.value, &opts.domain);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ attr = SYSDB_NAME;
+ if (opts.sid) {
+ attr = SYSDB_SID;
+ } else if (opts.id) {
+ attr = SYSDB_GIDNUM;
+ }
+
+ ret = sssctl_print_object(info, tool_ctx->domains, opts.domain,
+ sysdb_group_base_dn, NOT_FOUND_MSG("Group"),
+ CACHED_GROUP, attr, opts.value);
+ if (ret != EOK) {
+ return ret;
+ }
+
+
+ return EOK;
+}
+
+errno_t sssctl_netgroup_show(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ struct sssctl_cache_opts opts = {0};
+ errno_t ret;
+
+ struct sssctl_object_info info[] = {
+ SSSCTL_CACHE_NAME,
+ SSSCTL_CACHE_CREATE,
+ SSSCTL_CACHE_UPDATE,
+ SSSCTL_CACHE_EXPIRE,
+ SSSCTL_CACHE_NULL
+ };
+
+ ret = parse_cmdline(cmdline, tool_ctx, NULL, &opts.value, &opts.domain);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sssctl_print_object(info, tool_ctx->domains, opts.domain,
+ sysdb_netgroup_base_dn, NOT_FOUND_MSG("Netgroup"),
+ CACHED_NETGROUP, SYSDB_NAME, opts.value);
+ if (ret != EOK) {
+ return ret;
+ }
+
+
+ return EOK;
+}
diff --git a/src/tools/sssctl/sssctl_cert.c b/src/tools/sssctl/sssctl_cert.c
new file mode 100644
index 0000000..d2cdc2d
--- /dev/null
+++ b/src/tools/sssctl/sssctl_cert.c
@@ -0,0 +1,289 @@
+/*
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Certificate related utilities
+
+ Copyright (C) 2018 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 <popt.h>
+#include <stdio.h>
+#include <talloc.h>
+
+#include "util/util.h"
+#include "tools/common/sss_tools.h"
+#include "tools/sssctl/sssctl.h"
+#include "lib/certmap/sss_certmap.h"
+#include "util/crypto/sss_crypto.h"
+#include "responder/ifp/ifp_iface/ifp_iface_sync.h"
+
+#define PEM_HEAD "-----BEGIN CERTIFICATE-----\n"
+#define PEM_FOOT "-----END CERTIFICATE-----"
+
+errno_t sssctl_cert_show(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ errno_t ret;
+ int verbose = 0;
+ const char *cert_b64 = NULL;
+ char *desc;
+ uint8_t *der_cert = NULL;
+ size_t der_size;
+
+ /* Parse command line. */
+ struct poptOption options[] = {
+ {"verbose", 'v', POPT_ARG_NONE, &verbose, 0, _("Show debug information"), NULL },
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt_ex(cmdline, options, SSS_TOOL_OPT_OPTIONAL,
+ NULL, NULL, "CERTIFICATE-BASE64-ENCODED",
+ _("Specify base64 encoded certificate."),
+ SSS_TOOL_OPT_REQUIRED, &cert_b64, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ goto done;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ der_cert = sss_base64_decode(tmp_ctx, cert_b64, &der_size);
+ if (der_cert == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to decode base64 string.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = sss_certmap_display_cert_content(tmp_ctx, der_cert, der_size, &desc);
+ if (ret != 0) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to parsed certificate.\n");
+ goto done;
+ }
+
+ printf("%s\n", desc);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ free(discard_const(cert_b64));
+
+ return ret;
+}
+
+errno_t sssctl_cert_map(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ errno_t ret;
+ int verbose = 0;
+ const char *cert_b64 = NULL;
+ char *cert_pem = NULL;
+ struct sbus_sync_connection *conn;
+ const char **paths;
+ size_t c;
+ const char *name;
+
+ /* Parse command line. */
+ struct poptOption options[] = {
+ {"verbose", 'v', POPT_ARG_NONE, &verbose, 0, _("Show debug information"), NULL },
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt_ex(cmdline, options, SSS_TOOL_OPT_OPTIONAL,
+ NULL, NULL, "CERTIFICATE-BASE64-ENCODED",
+ _("Specify base64 encoded certificate."),
+ SSS_TOOL_OPT_REQUIRED, &cert_b64, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ goto done;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ cert_pem = talloc_asprintf(tmp_ctx, "%s%s\n%s",
+ PEM_HEAD, cert_b64, PEM_FOOT);
+ if (cert_pem == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ conn = sbus_sync_connect_system(tmp_ctx, NULL);
+ if (conn == NULL) {
+ ERROR("Unable to connect to system bus!\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = sbus_call_ifp_users_ListByCertificate(tmp_ctx, conn, IFP_BUS,
+ IFP_PATH_USERS, cert_pem, -1,
+ &paths);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to map certificate [%d]: %s\n",
+ ret, sss_strerror(ret));
+ PRINT_IFP_WARNING(ret);
+ goto done;
+ }
+
+ if (paths != NULL) {
+ for (c = 0; paths[c] != NULL; c++) {
+ ret = sbus_get_ifp_user_name(tmp_ctx, conn, IFP_BUS, paths[c],
+ &name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ puts(name);
+ }
+ } else {
+ PRINT(" - no mapped users found -");
+ }
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ free(discard_const(cert_b64));
+
+ return ret;
+}
+
+struct priv_sss_debug {
+ bool verbose;
+};
+
+void certmap_ext_debug(void *private, const char *file, long line,
+ const char *function, const char *format, ...)
+{
+ va_list ap;
+ struct priv_sss_debug *data = private;
+
+ if (data != NULL && data->verbose) {
+ va_start(ap, format);
+ fprintf(stdout, "%s:%ld [%s]: ", file, line, function);
+ vfprintf(stdout, format, ap);
+ fprintf(stdout, "\n");
+ va_end(ap);
+ }
+}
+
+errno_t sssctl_cert_eval_rule(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ errno_t ret;
+ int verbose = 0;
+ const char *cert_b64 = NULL;
+ const char *map = NULL;
+ const char *match = NULL;
+ struct sss_certmap_ctx *sss_certmap_ctx = NULL;
+ struct priv_sss_debug priv_sss_debug;
+ uint8_t *der_cert = NULL;
+ size_t der_size;
+ char *filter = NULL;
+ char **domains = NULL;
+
+ /* Parse command line. */
+ struct poptOption options[] = {
+ {"map", 'p', POPT_ARG_STRING, &map, 0, _("Mapping rule"), NULL },
+ {"match", 't', POPT_ARG_STRING, &match, 0, _("Matching rule"), NULL },
+ {"verbose", 'v', POPT_ARG_NONE, &verbose, 0, _("Show debug information"), NULL },
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt_ex(cmdline, options, SSS_TOOL_OPT_OPTIONAL,
+ NULL, NULL, "CERTIFICATE-BASE64-ENCODED",
+ _("Specify base64 encoded certificate."),
+ SSS_TOOL_OPT_REQUIRED, &cert_b64, NULL);
+ if (ret != EOK) {
+ ERROR("Unable to parse command arguments\n");
+ return ret;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ERROR("Out of memory!\n");
+ return ENOMEM;
+ }
+
+ priv_sss_debug.verbose = (verbose != 0);
+
+ ret = sss_certmap_init(tmp_ctx, certmap_ext_debug, &priv_sss_debug,
+ &sss_certmap_ctx);
+ if (ret != EOK) {
+ ERROR("Failed to setup certmap context.\n");
+ goto done;
+ }
+
+ ret = sss_certmap_add_rule(sss_certmap_ctx, 1, match, map, NULL);
+ if (ret != EOK) {
+ ERROR("Failed to add mapping and matching rules with error [%d][%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ der_cert = sss_base64_decode(tmp_ctx, cert_b64, &der_size);
+ if (der_cert == NULL) {
+ ERROR("Failed to decode base64 string.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = sss_certmap_match_cert(sss_certmap_ctx, der_cert, der_size);
+ switch (ret) {
+ case 0:
+ PRINT("Certificate matches rule.\n");
+ break;
+ case ENOENT:
+ PRINT("Certificate does not match rule.\n");
+ break;
+ default:
+ ERROR("Error during certificate matching [%d][%s].\n",
+ ret, sss_strerror(ret));
+ }
+
+ ret = sss_certmap_get_search_filter(sss_certmap_ctx, der_cert, der_size,
+ &filter, &domains);
+ if (ret != 0) {
+ ERROR("Failed to generate mapping filter [%d][%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+ PRINT("Mapping filter:\n\n %s\n\n", filter);
+ sss_certmap_free_filter_and_domains(filter, domains);
+
+ ret = EOK;
+
+done:
+
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
diff --git a/src/tools/sssctl/sssctl_config.c b/src/tools/sssctl/sssctl_config.c
new file mode 100644
index 0000000..f009151
--- /dev/null
+++ b/src/tools/sssctl/sssctl_config.c
@@ -0,0 +1,201 @@
+/*
+ Authors:
+ Michal Židek <mzidek@redhat.com>
+
+ Copyright (C) 2016 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 "config.h"
+
+#include <popt.h>
+#include <stdio.h>
+#include <ini_configobj.h>
+
+#include "util/util.h"
+#include "util/sss_ini.h"
+#include "tools/common/sss_tools.h"
+#include "tools/common/sss_process.h"
+#include "tools/sssctl/sssctl.h"
+#include "confdb/confdb.h"
+
+
+
+#ifdef HAVE_LIBINI_CONFIG_V1_3
+
+static char *sssctl_config_snippet_path(TALLOC_CTX *ctx, const char *path)
+{
+ char *tmp = NULL;
+ const char delimiter = '/';
+ char *dpos = NULL;
+
+ tmp = talloc_strdup(ctx, path);
+ if (!tmp) {
+ return NULL;
+ }
+
+ dpos = strrchr(tmp, delimiter);
+ if (dpos != NULL) {
+ ++dpos;
+ *dpos = '\0';
+ } else {
+ *tmp = '\0';
+ }
+
+ return talloc_strdup_append(tmp, CONFDB_DEFAULT_CONFIG_DIR_NAME);
+}
+
+errno_t sssctl_config_check(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ errno_t ret;
+ struct sss_ini *init_data;
+ struct ref_array *ra_error, *ra_success;
+ char *msg;
+ uint32_t i = 0;
+ size_t num_errors;
+ size_t num_ra_error, num_ra_success;
+ char **strs = NULL;
+ TALLOC_CTX *tmp_ctx = NULL;
+ const char *config_path = NULL;
+ const char *config_snippet_path = NULL;
+ struct poptOption long_options[] = {
+ {"config", 'c', POPT_ARG_STRING, &config_path,
+ 0, _("Specify a non-default config file"), NULL},
+ {"snippet", 's', POPT_ARG_STRING, &config_snippet_path,
+ 0, _("Specify a non-default snippet dir (The default is to look in "
+ "the same place where the main config file is located. For "
+ "example if the config is set to \"/my/path/sssd.conf\", "
+ "the snippet dir \"/my/path/conf.d\" is used)"), NULL},
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt(cmdline, long_options, SSS_TOOL_OPT_OPTIONAL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ return ret;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ init_data = sss_ini_new(tmp_ctx);
+ if (!init_data) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (config_path == NULL) {
+ config_path = SSSD_CONFIG_FILE;
+ }
+
+ if (config_snippet_path == NULL) {
+ config_snippet_path = sssctl_config_snippet_path(tmp_ctx, config_path);
+ if (config_snippet_path == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create snippet path\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = sss_ini_read_sssd_conf(init_data,
+ config_path,
+ config_snippet_path);
+
+ if (ret == ERR_INI_OPEN_FAILED) {
+ PRINT("Failed to open %s\n", config_path);
+ goto done;
+ }
+
+ if (!sss_ini_exists(init_data)) {
+ PRINT("File %1$s does not exist.\n", config_path);
+ }
+
+ if (ret == ERR_INI_INVALID_PERMISSION) {
+ PRINT("File ownership and permissions check failed. "
+ "Expected root:root and 0600.\n");
+ goto done;
+ }
+
+ if (ret == ERR_INI_PARSE_FAILED) {
+ PRINT("Failed to load configuration from %s.\n",
+ config_path);
+ goto done;
+ }
+
+ if (ret == ERR_INI_ADD_SNIPPETS_FAILED) {
+ PRINT("Error while reading configuration directory.\n");
+ goto done;
+ }
+
+ /* Used snippet files */
+ ra_success = sss_ini_get_ra_success_list(init_data);
+ num_ra_success = ref_array_len(ra_success);
+ if ((sss_ini_exists(init_data) == false) && (num_ra_success == 0)) {
+ PRINT("There is no configuration.\n");
+ ret = ERR_INI_OPEN_FAILED;
+ goto done;
+ }
+
+ /* Run validators */
+ ret = sss_ini_call_validators_strs(tmp_ctx, init_data,
+ SSSDDATADIR"/cfg_rules.ini",
+ &strs, &num_errors);
+ if (ret) {
+ PRINT("Failed to run validators");
+ goto done;
+ }
+
+ PRINT("Issues identified by validators: %zu\n", num_errors);
+ for (i = 0; i < num_errors; i++) {
+ printf("%s\n", strs[i]);
+ }
+
+ printf("\n");
+
+ /* Merging issues */
+ ra_error = sss_ini_get_ra_error_list(init_data);
+ num_ra_error = ref_array_len(ra_error);
+
+ PRINT("Messages generated during configuration merging: %zu\n", num_ra_error);
+
+ i = 0;
+ while (ref_array_get(ra_error, i, &msg) != NULL) {
+ printf("%s\n", msg);
+ i++;
+ }
+
+ printf("\n");
+
+ /* Used snippets */
+ PRINT("Used configuration snippet files: %zu\n", num_ra_success);
+
+ i = 0;
+ while (ref_array_get(ra_success, i, &msg) != NULL) {
+ printf("%s\n", msg);
+ i++;
+ }
+
+ if (num_errors != 0 || num_ra_error != 0) {
+ ret = EINVAL;
+ } else {
+ ret = EOK;
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+#endif /* HAVE_LIBINI_CONFIG_V1_3 */
diff --git a/src/tools/sssctl/sssctl_data.c b/src/tools/sssctl/sssctl_data.c
new file mode 100644
index 0000000..82f80c6
--- /dev/null
+++ b/src/tools/sssctl/sssctl_data.c
@@ -0,0 +1,538 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 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 <popt.h>
+#include <stdio.h>
+
+#include "util/util.h"
+#include "db/sysdb.h"
+#include "db/sysdb_private.h"
+#include "confdb/confdb.h"
+#include "confdb/confdb_private.h"
+#include "tools/common/sss_process.h"
+#include "tools/sssctl/sssctl.h"
+
+#define SSS_BACKUP_DIR SSS_STATEDIR "/backup"
+#define SSS_BACKUP_USER_OVERRIDES SSS_BACKUP_DIR "/sssd_user_overrides.bak"
+#define SSS_BACKUP_GROUP_OVERRIDES SSS_BACKUP_DIR "/sssd_group_overrides.bak"
+#define SSS_CACHE "sss_cache"
+
+struct sssctl_data_opts {
+ int override;
+ int restore;
+ int start;
+ int stop;
+ int restart;
+};
+
+static errno_t sssctl_create_backup_dir(const char *path)
+{
+ mode_t old_umask;
+ errno_t ret;
+
+ old_umask = umask(SSS_DFL_X_UMASK);
+ ret = mkdir(path, 0700);
+ umask(old_umask);
+ if (ret != EOK && errno != EEXIST) {
+ ret = errno;
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to create backup directory "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ return ret;
+ }
+
+ return EOK;
+}
+
+static bool sssctl_backup_file_exists(const char *file)
+{
+ return access(file, F_OK) == 0;
+}
+
+static bool sssctl_backup_exist(const char **files)
+{
+ int i;
+
+ for (i = 0; files[i] != NULL; i++) {
+ if (sssctl_backup_file_exists(files[i])) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static errno_t sssctl_backup(bool force)
+{
+ const char *files[] = {SSS_BACKUP_USER_OVERRIDES,
+ SSS_BACKUP_GROUP_OVERRIDES,
+ NULL};
+ enum sssctl_prompt_result prompt;
+ errno_t ret;
+
+ ret = sssctl_create_backup_dir(SSS_BACKUP_DIR);
+ if (ret != EOK) {
+ ERROR("Unable to create backup directory [%d]: %s",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ if (sssctl_backup_exist(files) && !force) {
+ prompt = sssctl_prompt(_("SSSD backup of local data already exists, "
+ "override?"), SSSCTL_PROMPT_NO);
+ switch (prompt) {
+ case SSSCTL_PROMPT_YES:
+ /* continue */
+ break;
+ case SSSCTL_PROMPT_NO:
+ return EEXIST;
+ case SSSCTL_PROMPT_ERROR:
+ return EIO;
+ }
+ }
+
+ ret = sssctl_run_command((const char *[]){"sss_override", "user-export",
+ SSS_BACKUP_USER_OVERRIDES, NULL});
+ if (ret != EOK) {
+ ERROR("Unable to export user overrides\n");
+ return ret;
+ }
+
+ ret = sssctl_run_command((const char *[]){"sss_override", "group-export",
+ SSS_BACKUP_GROUP_OVERRIDES, NULL});
+ if (ret != EOK) {
+ ERROR("Unable to export group overrides\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+errno_t sssctl_client_data_backup(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ struct sssctl_data_opts opts = {0};
+ errno_t ret;
+
+ /* Parse command line. */
+ struct poptOption options[] = {
+ {"override", 'o', POPT_ARG_NONE, &opts.override, 0, _("Override existing backup"), NULL },
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt(cmdline, options, SSS_TOOL_OPT_OPTIONAL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ return ret;
+ }
+
+ ret = sssctl_backup(opts.override);
+ if (ret == EEXIST) {
+ return EOK;
+ }
+
+ return ret;
+}
+
+static errno_t sssctl_restore(bool force_start, bool force_restart)
+{
+ errno_t ret;
+
+ if (!sssctl_start_sssd(force_start)) {
+ return ERR_SSSD_NOT_RUNNING;
+ }
+
+ if (sssctl_backup_file_exists(SSS_BACKUP_USER_OVERRIDES)) {
+ ret = sssctl_run_command((const char *[]){"sss_override", "user-import",
+ SSS_BACKUP_USER_OVERRIDES, NULL});
+ if (ret != EOK) {
+ ERROR("Unable to import user overrides\n");
+ return ret;
+ }
+ }
+
+ if (sssctl_backup_file_exists(SSS_BACKUP_USER_OVERRIDES)) {
+ ret = sssctl_run_command((const char *[]){"sss_override", "group-import",
+ SSS_BACKUP_GROUP_OVERRIDES, NULL});
+ if (ret != EOK) {
+ ERROR("Unable to import group overrides\n");
+ return ret;
+ }
+ }
+
+ sssctl_restart_sssd(force_restart);
+
+ ret = EOK;
+
+ return ret;
+}
+
+errno_t sssctl_client_data_restore(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ struct sssctl_data_opts opts = {0};
+ errno_t ret;
+
+ /* Parse command line. */
+ struct poptOption options[] = {
+ {"start", 's', POPT_ARG_NONE, &opts.start, 0, _("Start SSSD if it is not running"), NULL },
+ {"restart", 'r', POPT_ARG_NONE, &opts.restart, 0, _("Restart SSSD after data import"), NULL },
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt(cmdline, options, SSS_TOOL_OPT_OPTIONAL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ return ret;
+ }
+
+ return sssctl_restore(opts.start, opts.restart);
+}
+
+errno_t sssctl_cache_remove(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ struct sssctl_data_opts opts = {0};
+ errno_t ret;
+
+ /* Parse command line. */
+ struct poptOption options[] = {
+ {"override", 'o', POPT_ARG_NONE, &opts.override, 0, _("Override existing backup"), NULL },
+ {"restore", 'r', POPT_ARG_NONE, &opts.restore, 0, _("Create clean cache files and import local data"), NULL },
+ {"stop", 'p', POPT_ARG_NONE, &opts.stop, 0, _("Stop SSSD before removing the cache"), NULL },
+ {"start", 's', POPT_ARG_NONE, &opts.start, 0, _("Start SSSD when the cache is removed"), NULL },
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt(cmdline, options, SSS_TOOL_OPT_OPTIONAL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ return ret;
+ }
+
+ if (!sssctl_stop_sssd(opts.stop)) {
+ fprintf(stderr, "Unable to remove the cache unless SSSD is stopped.\n");
+ return ERR_SSSD_RUNNING;
+ }
+
+ PRINT("Creating backup of local data...\n");
+ ret = sssctl_backup(opts.override);
+ if (ret != EOK) {
+ ERROR("Unable to create backup of local data,"
+ " can not remove the cache.\n");
+ return ret;
+ }
+
+ PRINT("Removing cache files...\n");
+ ret = sss_remove_subtree(DB_PATH);
+ if (ret != EOK) {
+ ERROR("Unable to remove cache files\n");
+ return ret;
+ }
+
+ if (opts.restore) {
+ PRINT("Restoring local data...\n");
+ sssctl_restore(opts.start, opts.start);
+ } else {
+ sssctl_start_sssd(opts.start);
+ }
+
+ return EOK;
+}
+
+errno_t sssctl_cache_upgrade(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ struct sysdb_upgrade_ctx db_up_ctx;
+ errno_t ret;
+
+ ret = sss_tool_popt(cmdline, NULL, SSS_TOOL_OPT_OPTIONAL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ return ret;
+ }
+
+ if (sss_daemon_running()) {
+ return ERR_SSSD_RUNNING;
+ }
+
+ ret = confdb_get_domains(tool_ctx->confdb, &tool_ctx->domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "No domains configured.\n");
+ return ret;
+ }
+
+ db_up_ctx.cdb = tool_ctx->confdb;
+ ret = sysdb_init_ext(tool_ctx, tool_ctx->domains, &db_up_ctx,
+ true, 0, 0);
+ if (ret != EOK) {
+ SYSDB_VERSION_ERROR_DAEMON(ret);
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t sssctl_cache_expire(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ errno_t ret;
+
+ const char **args = talloc_array_size(tool_ctx,
+ sizeof(char *),
+ cmdline->argc + 2);
+ if (!args) {
+ return ENOMEM;
+ }
+ memcpy(&args[1], cmdline->argv, sizeof(char *) * cmdline->argc);
+ args[0] = SSS_CACHE;
+ args[cmdline->argc + 1] = NULL;
+
+ ret = sssctl_run_command(args);
+
+ talloc_free(args);
+ return ret;
+}
+
+errno_t get_confdb_domains(TALLOC_CTX *ctx, struct confdb_ctx *confdb,
+ char ***_domains)
+{
+ int ret;
+ int domain_count = 0;
+ int i;
+ struct sss_domain_info *domain = NULL;
+ struct sss_domain_info *domain_list = NULL;
+ char **domains;
+
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ /* get domains */
+ ret = confdb_get_domains(confdb, &domain_list);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get domain list\n");
+ goto done;
+ }
+
+ for (domain = domain_list;
+ domain;
+ domain = get_next_domain(domain, 0)) {
+ domain_count++;
+ }
+
+ /* allocate output space */
+ domains = talloc_array(tmp_ctx, char *, domain_count + 1);
+ if (domains == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not allocate memory for domains\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (domain = domain_list, i = 0;
+ domain != NULL;
+ domain = get_next_domain(domain, 0), i++) {
+ domains[i] = talloc_asprintf(domains, "%s", domain->name);
+ if (domains[i] == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ /* add NULL to the end */
+ domains[i] = NULL;
+
+ *_domains = talloc_steal(ctx, domains);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t sssctl_cache_index_action(enum sysdb_index_actions action,
+ const char **domains,
+ const char *attr)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct confdb_ctx *confdb = NULL;
+ char *cache;
+ const char **domain;
+ const char **index;
+ const char **indexes = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to allocate the context\n");
+ return ENOMEM;
+ }
+
+ if (domains == NULL) {
+ /* If the user selected no domain, act on all of them */
+ ret = sss_tool_connect_to_confdb(tmp_ctx, &confdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not connect to configuration database.\n");
+ goto done;
+ }
+
+ ret = get_confdb_domains(tmp_ctx, confdb, discard_const(&domains));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not list all the domains.\n");
+ goto done;
+ }
+ }
+
+ for (domain = domains; *domain != NULL; domain++) {
+ if (action == SYSDB_IDX_CREATE) {
+ PRINT("Creating cache index for domain %1$s\n", *domain);
+ } else if (action == SYSDB_IDX_DELETE) {
+ PRINT("Deleting cache index for domain %1$s\n", *domain);
+ } else if (action == SYSDB_IDX_LIST) {
+ PRINT("Indexes for domain %1$s:\n", *domain);
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid action: %i\n", action);
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = sysdb_get_db_file(tmp_ctx, NULL, *domain, DB_PATH, &cache, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to get the cache db name\n");
+ goto done;
+ }
+
+ ret = sysdb_manage_index(tmp_ctx, action, cache, attr, &indexes);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (action == SYSDB_IDX_LIST) {
+ for (index = indexes; *index != NULL; index++) {
+ PRINT(" Attribute: %1$s\n", *index);
+ }
+ talloc_zfree(indexes);
+ }
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t sssctl_cache_index(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ const char *attr = NULL;
+ const char *action_str = NULL;
+ const char **domains = NULL;
+ const char **p;
+ enum sysdb_index_actions action;
+ errno_t ret;
+
+ /* Parse command line. */
+ struct poptOption options[] = {
+ { "domain", 'd', POPT_ARG_ARGV, &domains,
+ 0, _("Target a specific domain"), _("domain") },
+ { "attribute", 'a', POPT_ARG_STRING, &attr,
+ 0, _("Attribute to index"), _("attribute") },
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt_ex(cmdline, options, SSS_TOOL_OPT_OPTIONAL, NULL, NULL,
+ "ACTION", "create | delete | list",
+ SSS_TOOL_OPT_REQUIRED, &action_str, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ goto done;
+ }
+
+ if (action_str == NULL) {
+ ERROR("Action not provided\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (strcmp(action_str, "list") == 0) {
+ action = SYSDB_IDX_LIST;
+ } else {
+ if (strcmp(action_str, "create") == 0) {
+ action = SYSDB_IDX_CREATE;
+ } else if (strcmp(action_str, "delete") == 0) {
+ action = SYSDB_IDX_DELETE;
+ } else {
+ ERROR("Unknown action: %1$s\nValid actions are "
+ "\"%2$s\", \"%3$s and \"%4$s\"\n",
+ action_str, "create", "delete", "list");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (attr == NULL) {
+ ERROR("Attribute (-a) not provided\n");
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ ret = sssctl_cache_index_action(action, domains, attr);
+ if (ret == ENOENT) {
+ ERROR("Attribute %1$s not indexed.\n", attr);
+ goto done;
+ } if (ret == EEXIST) {
+ ERROR("Attribute %1$s already indexed.\n", attr);
+ goto done;
+ } else if (ret != EOK) {
+ ERROR("Index operation failed: %1$s\n", sss_strerror(ret));
+ goto done;
+ }
+
+ if (action != SYSDB_IDX_LIST) {
+ PRINT("Don't forget to also update the indexes on the remote providers.\n");
+ }
+
+ ret = EOK;
+
+done:
+ free(discard_const(action_str));
+ free(discard_const(attr));
+ if (domains != NULL) {
+ for (p = domains; *p != NULL; p++) {
+ free(discard_const(*p));
+ }
+ free(discard_const(domains));
+ }
+
+ return ret;
+}
diff --git a/src/tools/sssctl/sssctl_domains.c b/src/tools/sssctl/sssctl_domains.c
new file mode 100644
index 0000000..ee8cb13
--- /dev/null
+++ b/src/tools/sssctl/sssctl_domains.c
@@ -0,0 +1,408 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 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 <popt.h>
+#include <stdio.h>
+#include <talloc.h>
+
+#include "util/util.h"
+#include "tools/common/sss_tools.h"
+#include "tools/sssctl/sssctl.h"
+#include "sbus/sbus_opath.h"
+#include "responder/ifp/ifp_iface/ifp_iface_sync.h"
+
+static errno_t
+sssctl_domain_list_get_properties(TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *path,
+ const char **_name,
+ bool *_is_subdom)
+{
+ errno_t ret;
+
+ if (_name != NULL) {
+ ret = sbus_get_ifp_domains_name(mem_ctx, conn, IFP_BUS, path, _name);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (_is_subdom != NULL) {
+ ret = sbus_get_ifp_domains_subdomain(conn, IFP_BUS, path, _is_subdom);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get domain property [%d]: %s\n",
+ ret, sss_strerror(ret));
+ PRINT_IFP_WARNING(ret);
+ }
+
+ return ret;
+}
+
+errno_t sssctl_domain_list(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sbus_sync_connection *conn;
+ const char **paths;
+ const char *name;
+ bool is_subdom;
+ int start = 0;
+ int verbose = 0;
+ errno_t ret;
+ int i;
+
+ /* Parse command line. */
+ struct poptOption options[] = {
+ {"start", 's', POPT_ARG_NONE, &start, 0, _("Start SSSD if it is not running"), NULL },
+ {"verbose", 'v', POPT_ARG_NONE, &verbose, 0, _("Show domain list including primary or trusted domain type"), NULL },
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt(cmdline, options, SSS_TOOL_OPT_OPTIONAL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ return ret;
+ }
+
+ if (!sssctl_start_sssd(start)) {
+ return ERR_SSSD_NOT_RUNNING;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ conn = sbus_sync_connect_system(tmp_ctx, NULL);
+ if (conn == NULL) {
+ ERROR("Unable to connect to system bus!\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = sbus_call_ifp_ListDomains(tmp_ctx, conn, IFP_BUS, IFP_PATH, &paths);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to list domains [%d]: %s\n",
+ ret, sss_strerror(ret));
+ PRINT_IFP_WARNING(ret);
+ goto done;
+ }
+
+ if (verbose) {
+ for (i = 0; paths[i] != NULL; i++) {
+ ret = sssctl_domain_list_get_properties(tmp_ctx, conn, paths[i],
+ &name, &is_subdom);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (is_subdom) {
+ printf("Trusted domain: %s\n", name);
+ } else {
+ printf("Primary domain: %s\n", name);
+ }
+ }
+
+ return EOK;
+ }
+
+ for (i = 0; paths[i] != NULL; i++) {
+ ret = sssctl_domain_list_get_properties(tmp_ctx, conn, paths[i],
+ &name, NULL);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ puts(name);
+ }
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sssctl_domain_status_online(struct sbus_sync_connection *conn,
+ const char *domain_path)
+{
+ bool is_online;
+ errno_t ret;
+
+ ret = sbus_call_ifp_domain_IsOnline(conn, IFP_BUS, domain_path, &is_online);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get domain status [%d]: %s\n",
+ ret, sss_strerror(ret));
+ PRINT_IFP_WARNING(ret);
+ return ret;
+ }
+
+ PRINT("Online status: %s\n", is_online ? _("Online") : _("Offline"));
+
+ return EOK;
+}
+
+static const char *proper_service_name(const char *service)
+{
+ if (strcasecmp(service, "AD_GC") == 0) {
+ return "AD Global Catalog";
+ } else if (strcasecmp(service, "AD") == 0) {
+ return "AD Domain Controller";
+ } else if (strncasecmp(service, "sd_gc_", strlen("sd_gc_")) == 0) {
+ return "AD Global Catalog";
+ } else if (strncasecmp(service, "sd_", strlen("sd_")) == 0) {
+ return "AD Domain Controller";
+ }
+
+ return service;
+}
+
+static errno_t
+sssctl_domain_status_active_server(struct sbus_sync_connection *conn,
+ const char *domain_path)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *server;
+ const char **services;
+ errno_t ret;
+ int i;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ ret = sbus_call_ifp_domain_ListServices(tmp_ctx, conn, IFP_BUS,
+ domain_path, &services);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get domain services [%d]: %s\n",
+ ret, sss_strerror(ret));
+ PRINT_IFP_WARNING(ret);
+ goto done;
+ }
+
+ if (services == NULL) {
+ PRINT("This domain has no active servers.\n");
+ ret = EOK;
+ goto done;
+ }
+
+ PRINT("Active servers:\n");
+ for (i = 0; services[i] != NULL; i++) {
+ ret = sbus_call_ifp_domain_ActiveServer(tmp_ctx, conn, IFP_BUS,
+ domain_path, services[i], &server);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get active server [%d]: %s\n",
+ ret, sss_strerror(ret));
+ PRINT_IFP_WARNING(ret);
+ goto done;
+ }
+
+ /* SBUS_REQ_STRING_DEFAULT handles (server == NULL) case gracefully */
+ server = SBUS_REQ_STRING_DEFAULT(server, _("not connected"));
+ printf("%s: %s\n", proper_service_name(services[i]), server);
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t
+sssctl_domain_status_server_list(struct sbus_sync_connection *conn,
+ const char *domain_path)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char **servers;
+ const char **services;
+ errno_t ret;
+ int i, j;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ ret = sbus_call_ifp_domain_ListServices(tmp_ctx, conn, IFP_BUS,
+ domain_path, &services);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get domain services [%d]: %s\n",
+ ret, sss_strerror(ret));
+ PRINT_IFP_WARNING(ret);
+ goto done;
+ }
+
+ if (services == NULL) {
+ PRINT("No servers discovered.\n");
+ ret = EOK;
+ goto done;
+ }
+
+ for (i = 0; services[i] != NULL; i++) {
+ PRINT("Discovered %s servers:\n", proper_service_name(services[i]));
+
+ ret = sbus_call_ifp_domain_ListServers(tmp_ctx, conn, IFP_BUS,
+ domain_path, services[i], &servers);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get domain servers [%d]: %s\n",
+ ret, sss_strerror(ret));
+ PRINT_IFP_WARNING(ret);
+ goto done;
+ }
+
+ if (servers == NULL || servers[0] == NULL) {
+ PRINT("None so far.\n");
+ continue;
+ }
+
+ for (j = 0; servers[j] != NULL; j++) {
+ printf("- %s\n", servers[j]);
+ }
+
+ printf("\n");
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+struct sssctl_domain_status_opts {
+ const char *domain;
+ int online;
+ int last;
+ int active;
+ int servers;
+ int force_start;
+};
+
+errno_t sssctl_domain_status(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct sssctl_domain_status_opts opts = {0};
+ struct sbus_sync_connection *conn;
+ const char *path;
+ bool opt_set;
+ errno_t ret;
+
+ /* Parse command line. */
+ struct poptOption options[] = {
+ {"online", 'o', POPT_ARG_NONE , &opts.online, 0, _("Show online status"), NULL },
+ {"active-server", 'a', POPT_ARG_NONE, &opts.active, 0, _("Show information about active server"), NULL },
+ {"servers", 'r', POPT_ARG_NONE, &opts.servers, 0, _("Show list of discovered servers"), NULL },
+ {"start", 's', POPT_ARG_NONE, &opts.force_start, 0, _("Start SSSD if it is not running"), NULL },
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt_ex(cmdline, options, SSS_TOOL_OPT_OPTIONAL,
+ NULL, NULL, "DOMAIN", _("Specify domain name."),
+ SSS_TOOL_OPT_REQUIRED, &opts.domain, &opt_set);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ goto done;
+ }
+
+ if (opt_set == false) {
+ opts.online = true;
+ opts.last = true;
+ opts.active = true;
+ opts.servers = true;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ path = sbus_opath_compose(tmp_ctx, IFP_PATH_DOMAINS, opts.domain);
+ if (path == NULL) {
+ PRINT("Out of memory!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (!sssctl_start_sssd(opts.force_start)) {
+ ret = ERR_SSSD_NOT_RUNNING;
+ goto done;
+ }
+
+ conn = sbus_sync_connect_system(tmp_ctx, NULL);
+ if (conn == NULL) {
+ ERROR("Unable to connect to system bus!\n");
+ ret = EIO;
+ goto done;
+ }
+
+ if (opts.online) {
+ ret = sssctl_domain_status_online(conn, path);
+ if (ret != EOK) {
+ ERROR("Unable to get online status\n");
+ goto done;
+ }
+
+ printf("\n");
+ }
+
+ if (opts.active) {
+ ret = sssctl_domain_status_active_server(conn, path);
+ if (ret != EOK) {
+ ERROR("Unable to get online status\n");
+ goto done;
+ }
+
+ printf("\n");
+ }
+
+ if (opts.servers) {
+ ret = sssctl_domain_status_server_list(conn, path);
+ if (ret != EOK) {
+ ERROR("Unable to get server list\n");
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ free(discard_const(opts.domain));
+
+ return ret;
+}
diff --git a/src/tools/sssctl/sssctl_logs.c b/src/tools/sssctl/sssctl_logs.c
new file mode 100644
index 0000000..f8f5a65
--- /dev/null
+++ b/src/tools/sssctl/sssctl_logs.c
@@ -0,0 +1,608 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 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 "config.h"
+
+#include <stdlib.h>
+#include <limits.h>
+#include <talloc.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <signal.h>
+#include <utime.h>
+#include <ldb.h>
+#include <popt.h>
+#include <stdio.h>
+#include <glob.h>
+
+#include "util/util.h"
+#include "tools/common/sss_tools.h"
+#include "tools/common/sss_process.h"
+#include "tools/sssctl/sssctl.h"
+#include "tools/tools_util.h"
+#include "confdb/confdb.h"
+#include "sss_iface/sss_iface_sync.h"
+#include "responder/ifp/ifp_iface/ifp_iface_sync.h"
+
+#define LOG_FILE(file) " " LOG_PATH "/" file
+#define LOG_FILES LOG_FILE("*.log")
+#define SSS_ANALYZE SSSD_LIBEXEC_PATH"/sss_analyze"
+
+#define CHECK(expr, done, msg) do { \
+ if (expr) { \
+ ERROR(msg "\n"); \
+ goto done; \
+ } \
+} while(0)
+
+#define POPT_SERV_OPTION(NAME, VAR, DESC) \
+ {services[SERV_ ## NAME].name, '\0', POPT_BIT_SET, &VAR, \
+ services[SERV_ ## NAME].mask, DESC, NULL}
+
+#define STARTS_WITH(s, p) (strncmp((s), (p), strlen(p)) == 0)
+#define REMOVE_PREFIX(s, p) (STARTS_WITH(s, p) ? (s) + strlen(p) : (s))
+#define IS_DOMAIN(c) STARTS_WITH((c), "domain/")
+#define DOMAIN_NAME(c) REMOVE_PREFIX((c), "domain/")
+#define EMPTY_TARGETS(t) ((t)[0] == NULL)
+
+enum debug_level_action {
+ ACTION_SET,
+ ACTION_GET
+};
+
+struct debuglevel_tool_ctx {
+ struct confdb_ctx *confdb;
+ char **sections;
+};
+
+struct sssctl_logs_opts {
+ int delete;
+ int archived;
+};
+
+struct sssctl_service_desc {
+ const char *name;
+ int mask;
+};
+
+enum serv_idx {
+ SERV_SSSD,
+ SERV_NSS,
+ SERV_PAM,
+ SERV_SUDO,
+ SERV_AUTOFS,
+ SERV_SSH,
+ SERV_PAC,
+ SERV_IFP,
+ SERV_COUNT
+};
+
+struct sssctl_service_desc services[] = {
+ { "sssd", 1U << SERV_SSSD },
+ { "nss", 1U << SERV_NSS },
+ { "pam", 1U << SERV_PAM },
+ { "sudo", 1U << SERV_SUDO },
+ { "autofs", 1U << SERV_AUTOFS},
+ { "ssh", 1U << SERV_SSH },
+ { "pac", 1U << SERV_PAC },
+ { "ifp", 1U << SERV_IFP }
+};
+
+static struct sbus_sync_connection *connect_to_sbus(TALLOC_CTX *mem_ctx)
+{
+ struct sbus_sync_connection *conn;
+
+ conn = sbus_sync_connect_private(mem_ctx, SSS_MONITOR_ADDRESS, NULL);
+ if (conn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to connect to the sbus monitor\n");
+ }
+
+ return conn;
+}
+
+static const char *get_busname(TALLOC_CTX *mem_ctx, struct confdb_ctx *confdb,
+ const char *component)
+{
+ errno_t ret;
+ const char *busname;
+ struct sss_domain_info *domain;
+
+ if (strcmp(component, "sssd") == 0) {
+ busname = talloc_strdup(mem_ctx, SSS_BUS_MONITOR);
+ } else if (IS_DOMAIN(component)) {
+ ret = confdb_get_domain(confdb, DOMAIN_NAME(component), &domain);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unknown domain: %s\n", component);
+ busname = NULL;
+ goto done;
+ }
+
+ busname = sss_iface_domain_bus(mem_ctx, domain);
+ } else {
+ busname = talloc_asprintf(mem_ctx, "sssd.%s", component);
+ }
+
+done:
+ return busname;
+}
+
+/* in_out_value is an input argument when action is ACTION_SET; it is an output
+ * argument when action is ACTION_GET. */
+static errno_t do_debug_level(enum debug_level_action action,
+ struct sbus_sync_connection *conn,
+ struct confdb_ctx *confdb,
+ const char *component,
+ uint32_t *in_out_value)
+{
+ errno_t ret;
+ uint32_t value;
+ const char *busname;
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ busname = get_busname(tmp_ctx, confdb, component);
+ if (busname == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create the bus name for %s\n",
+ component);
+ }
+
+ if (action == ACTION_GET) {
+ ret = sbus_get_service_debug_level(conn, busname, SSS_BUS_PATH, &value);
+ if (ret != EOK) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ *in_out_value = value;
+ } else {
+ ret = sbus_set_service_debug_level(conn, busname, SSS_BUS_PATH,
+ *in_out_value);
+ if (ret != EOK) {
+ ret = ENOENT;
+ goto done;
+ }
+ }
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t sssctl_do_debug_level(enum debug_level_action action,
+ struct debuglevel_tool_ctx *tool_ctx,
+ const char **targets,
+ uint32_t debug_to_set)
+{
+ bool all_targets = EMPTY_TARGETS(targets);
+ errno_t ret = EOK;
+ errno_t final_ret = EOK;
+ uint32_t current_level = SSSDBG_INVALID;
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ const char *stripped_target;
+ const char **curr_target;
+ struct sbus_sync_connection *conn;
+
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ conn = connect_to_sbus(tmp_ctx);
+ if (conn == NULL) {
+ ERROR("SSSD is not running.\n");
+ ret = EIO;
+ goto fini;
+ }
+
+ curr_target = (all_targets ?
+ discard_const_p(const char *, tool_ctx->sections) : targets);
+ while (*curr_target != NULL) {
+ stripped_target = REMOVE_PREFIX(*curr_target, "config/");
+
+ if (action == ACTION_GET) {
+ ret = do_debug_level(ACTION_GET, conn, tool_ctx->confdb,
+ stripped_target, &current_level);
+ CHECK(ret != EOK && ret != ENOENT, fini,
+ "Could not read the debug level.");
+
+ if (ret == EOK) {
+ PRINT(_("%1$-25s %2$#.4x\n"), stripped_target, current_level);
+ } else {
+ if (!all_targets) {
+ if (IS_DOMAIN(stripped_target)) {
+ PRINT(_("%1$-25s Unknown domain\n"), stripped_target);
+ } else {
+ PRINT(_("%1$-25s Unreachable service\n"), stripped_target);
+ }
+ final_ret = ENOENT;
+ }
+ }
+ } else {
+ ret = do_debug_level(ACTION_SET, conn, tool_ctx->confdb,
+ stripped_target, &debug_to_set);
+ CHECK(ret != EOK && ret != ENOENT, fini,
+ "Could not set the debug level.");
+ if (ret == ENOENT && !all_targets) {
+ final_ret = ret;
+ }
+ }
+ curr_target++;
+ }
+
+ if (ret == EOK) {
+ ret = final_ret;
+ }
+
+fini:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t get_confdb_sections(TALLOC_CTX *ctx, struct confdb_ctx *confdb,
+ char ***output_sections)
+{
+ int ret;
+ int domain_count = 0;
+ int i = 0;
+ struct sss_domain_info *domain = NULL;
+ struct sss_domain_info *domain_list = NULL;
+ char **sections;
+ const char *known_services[] = {
+ CONFDB_MONITOR_CONF_ENTRY,
+ CONFDB_NSS_CONF_ENTRY,
+ CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAC_CONF_ENTRY,
+ CONFDB_SSH_CONF_ENTRY,
+ CONFDB_SUDO_CONF_ENTRY,
+ CONFDB_AUTOFS_CONF_ENTRY,
+ CONFDB_IFP_CONF_ENTRY,
+ };
+ static const int known_services_count = sizeof(known_services)
+ / sizeof(*known_services);
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ /* get domains */
+ ret = confdb_get_domains(confdb, &domain_list);
+ if (ret != EOK)
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get domain list\n");
+
+ for (domain = domain_list;
+ domain;
+ domain = get_next_domain(domain, 0)) {
+ domain_count++;
+ }
+
+ /* allocate output space */
+ sections = talloc_array(ctx, char *,
+ domain_count + known_services_count + 1);
+ if (sections == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not allocate memory for sections\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ for (i = 0; i < known_services_count; i++) {
+ sections[i] = talloc_strdup(tmp_ctx, known_services[i]);
+ if (sections[i] == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+ }
+
+ for (domain = domain_list;
+ domain;
+ domain = get_next_domain(domain, 0), i++) {
+ sections[i] = talloc_asprintf(tmp_ctx, CONFDB_DOMAIN_PATH_TMPL,
+ domain->name);
+ if (sections[i] == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+ }
+
+ /* add NULL to the end */
+ sections[i] = NULL;
+
+ *output_sections = talloc_steal(ctx, sections);
+
+ return EOK;
+fail:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static const char **get_targets(TALLOC_CTX *mem_ctx, int services_mask,
+ const char **domainv)
+{
+ int i;
+ int count = 1;
+ const char **targets = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return NULL;
+ }
+
+ targets = talloc_zero_array(tmp_ctx, const char *, count);
+ if (targets == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not allocate memory for the list of targets\n");
+ goto done;
+ }
+
+ if (services_mask != 0) {
+ for (i = 0; i < SERV_COUNT; i++) {
+ if (services_mask == 0 || (services_mask & services[i].mask) != 0) {
+ targets = talloc_realloc(tmp_ctx, targets, const char *, count + 1);
+ if (targets == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not allocate memory for the list of targets\n");
+ goto done;
+ }
+ targets[count - 1] = talloc_strdup(tmp_ctx, services[i].name);
+ targets[count++] = NULL;
+ }
+ }
+ }
+
+ if (domainv != NULL) {
+ for (; *domainv != NULL; domainv++) {
+ targets = talloc_realloc(tmp_ctx, targets, const char *, count + 1);
+ if (targets == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not allocate memory for the list of targets\n");
+ goto done;
+ }
+ if (IS_DOMAIN(*domainv)) {
+ targets[count - 1] = talloc_strdup(tmp_ctx, *domainv);
+ } else {
+ targets[count - 1] = talloc_asprintf(tmp_ctx, "domain/%s", *domainv);
+ }
+ targets[count++] = NULL;
+ }
+ }
+
+ targets = talloc_steal(mem_ctx, targets);
+ for (i = 0; i < count; i++) {
+ targets[i] = talloc_steal(mem_ctx, targets[i]);
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return targets;
+}
+
+int parse_debug_level(const char *strlevel)
+{
+ long value;
+ char *endptr;
+
+ errno = 0;
+ value = strtol(strlevel, &endptr, 0);
+ if ((errno != 0) || (endptr == strlevel) || (*endptr != '\0')) {
+ return SSSDBG_INVALID;
+ }
+
+ return debug_convert_old_level(value);
+}
+
+errno_t sssctl_logs_remove(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ struct sssctl_logs_opts opts = {0};
+ errno_t ret;
+ glob_t globbuf;
+
+ /* Parse command line. */
+ struct poptOption options[] = {
+ {"delete", 'd', POPT_ARG_NONE, &opts.delete, 0, _("Delete log files instead of truncating"), NULL },
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt(cmdline, options, SSS_TOOL_OPT_OPTIONAL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ return ret;
+ }
+
+ if (opts.delete) {
+ PRINT("Deleting log files...\n");
+ ret = sss_remove_subtree(LOG_PATH);
+ if (ret != EOK) {
+ ERROR("Unable to remove log files\n");
+ return ret;
+ }
+
+ sss_signal(SIGHUP);
+ } else {
+ globbuf.gl_offs = 4;
+ ret = glob(LOG_PATH"/*.log", GLOB_ERR|GLOB_DOOFFS, NULL, &globbuf);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to expand log files list\n");
+ return ret;
+ }
+ globbuf.gl_pathv[0] = discard_const_p(char, "truncate");
+ globbuf.gl_pathv[1] = discard_const_p(char, "--no-create");
+ globbuf.gl_pathv[2] = discard_const_p(char, "--size");
+ globbuf.gl_pathv[3] = discard_const_p(char, "0");
+
+ PRINT("Truncating log files...\n");
+ ret = sssctl_run_command((const char * const*)globbuf.gl_pathv);
+ globfree(&globbuf);
+ if (ret != EOK) {
+ ERROR("Unable to truncate log files\n");
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+errno_t sssctl_logs_fetch(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ const char *file = NULL;
+ errno_t ret;
+ glob_t globbuf;
+
+ /* Parse command line. */
+ ret = sss_tool_popt_ex(cmdline, NULL, SSS_TOOL_OPT_OPTIONAL, NULL, NULL,
+ "FILE", "Output file", SSS_TOOL_OPT_REQUIRED,
+ &file, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ goto done;
+ }
+
+ globbuf.gl_offs = 3;
+ ret = glob(LOG_PATH"/*.log", GLOB_ERR|GLOB_DOOFFS, NULL, &globbuf);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to expand log files list\n");
+ goto done;
+ }
+ globbuf.gl_pathv[0] = discard_const_p(char, "tar");
+ globbuf.gl_pathv[1] = discard_const_p(char, "-czf");
+ globbuf.gl_pathv[2] = discard_const_p(char, file);
+
+ PRINT("Archiving log files into %s...\n", file);
+ ret = sssctl_run_command((const char * const*)globbuf.gl_pathv);
+ globfree(&globbuf);
+ if (ret != EOK) {
+ ERROR("Unable to archive log files\n");
+ goto done;
+ }
+
+done:
+ free(discard_const(file));
+
+ return ret;
+}
+
+errno_t sssctl_debug_level(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ int ret;
+ int pc_services = 0;
+ uint32_t debug_to_set = SSSDBG_INVALID;
+ const char **pc_domains = NULL;
+ const char **targets = NULL;
+ const char *debug_as_string = NULL;
+
+ struct debuglevel_tool_ctx *ctx = NULL;
+ struct poptOption long_options[] = {
+ {"domain", '\0', POPT_ARG_ARGV, &pc_domains,
+ 0, _("Target a specific domain"), _("domain")},
+ POPT_SERV_OPTION(SSSD, pc_services, _("Target the SSSD service")),
+ POPT_SERV_OPTION(NSS, pc_services, _("Target the NSS service")),
+ POPT_SERV_OPTION(PAM, pc_services, _("Target the PAM service")),
+ POPT_SERV_OPTION(SUDO, pc_services, _("Target the SUDO service")),
+ POPT_SERV_OPTION(AUTOFS, pc_services, _("Target the AUTOFS service")),
+ POPT_SERV_OPTION(SSH, pc_services, _("Target the SSH service")),
+ POPT_SERV_OPTION(PAC, pc_services, _("Target the PAC service")),
+ POPT_SERV_OPTION(IFP, pc_services, _("Target the IFP service")),
+ POPT_TABLEEND
+ };
+
+ /* allocate context */
+ ctx = talloc_zero(NULL, struct debuglevel_tool_ctx);
+ if (ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not allocate memory for tools context\n");
+ ret = ENOMEM;
+ goto fini;
+ }
+
+ ret = sss_tool_popt_ex(cmdline, long_options, SSS_TOOL_OPT_OPTIONAL, NULL,
+ NULL, "DEBUG_LEVEL_TO_SET",
+ _("Specify debug level you want to set"),
+ SSS_TOOL_OPT_OPTIONAL, &debug_as_string, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ goto fini;
+ }
+
+ CHECK_ROOT(ret, debug_prg_name);
+
+ if (debug_as_string != NULL) {
+ debug_to_set = (uint32_t) parse_debug_level(debug_as_string);
+ CHECK(debug_to_set == SSSDBG_INVALID, fini, "Invalid debug level.");
+ }
+
+ /* Create a list with all the target names (services + domains) */
+ targets = get_targets(ctx, pc_services, pc_domains);
+ CHECK(targets == NULL, fini, "Could not allocate memory.");
+
+ ret = sss_tool_connect_to_confdb(ctx, &ctx->confdb);
+ CHECK(ret != EOK, fini, "Could not connect to configuration database.");
+
+ ret = get_confdb_sections(ctx, ctx->confdb, &ctx->sections);
+ CHECK(ret != EOK, fini, "Could not get all configuration sections.");
+
+ if (debug_as_string == NULL) {
+ ret = sssctl_do_debug_level(ACTION_GET, ctx, targets, 0);
+ } else {
+ ret = sssctl_do_debug_level(ACTION_SET, ctx, targets, debug_to_set);
+ }
+
+ /* Only report missing components that the user requested,
+ except for the monitor (sssd not running) */
+ if (ret != ENOENT && ret != EIO && EMPTY_TARGETS(targets)) {
+ ret = EOK;
+ }
+
+fini:
+ talloc_free(ctx);
+ free(discard_const(debug_as_string));
+
+ return ret;
+}
+
+errno_t sssctl_analyze(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+#ifndef BUILD_CHAIN_ID
+ PRINT("ERROR: Tevent chain ID support missing, log analyzer is unsupported.\n");
+ return EOK;
+#endif
+ errno_t ret;
+
+ ret = sssctl_wrap_command(SSS_ANALYZE, NULL, cmdline, tool_ctx, pvt);
+
+ return ret;
+}
diff --git a/src/tools/sssctl/sssctl_passkey.c b/src/tools/sssctl/sssctl_passkey.c
new file mode 100644
index 0000000..34cab00
--- /dev/null
+++ b/src/tools/sssctl/sssctl_passkey.c
@@ -0,0 +1,42 @@
+/*
+ Authors:
+ Justin Stephenson <jstephen@redhat.com>
+
+ Passkey related utilities
+
+ Copyright (C) 2022 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 <popt.h>
+#include <stdio.h>
+#include <talloc.h>
+
+#include "util/util.h"
+#include "tools/common/sss_tools.h"
+#include "tools/sssctl/sssctl.h"
+
+#define SSS_PASSKEY_CHILD SSSD_LIBEXEC_PATH"/passkey_child"
+
+errno_t sssctl_passkey_register(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ errno_t ret;
+
+ ret = sssctl_wrap_command(SSS_PASSKEY_CHILD, "--register", cmdline, tool_ctx, pvt);
+
+ return ret;
+}
diff --git a/src/tools/sssctl/sssctl_systemd.c b/src/tools/sssctl/sssctl_systemd.c
new file mode 100644
index 0000000..1d30558
--- /dev/null
+++ b/src/tools/sssctl/sssctl_systemd.c
@@ -0,0 +1,95 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 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 <talloc.h>
+#include <dbus/dbus.h>
+#include <errno.h>
+
+#include "util/util.h"
+#include "tools/sssctl/sssctl.h"
+#include "sss_iface/sss_iface_sync.h"
+
+#define SSS_SYSTEMD_BUS "org.freedesktop.systemd1"
+#define SSS_SYSTEMD_PATH "/org/freedesktop/systemd1"
+#define SSS_SYSTEMD_UNIT "sssd.service"
+#define SSS_SYSTEMD_MODE "replace" /* replace queued job if present */
+
+typedef errno_t
+(*systemd_method)(TALLOC_CTX *, struct sbus_sync_connection *,
+ const char *, const char *, const char *, const char *,
+ const char **);
+
+static errno_t sssctl_systemd_call(systemd_method method)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sbus_sync_connection *conn;
+ const char *job;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ conn = sbus_sync_connect_system(tmp_ctx, NULL);
+ if (conn == NULL) {
+ ERROR("Unable to connect to system bus!\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = method(tmp_ctx, conn, SSS_SYSTEMD_BUS,
+ SSS_SYSTEMD_PATH, SSS_SYSTEMD_UNIT,
+ SSS_SYSTEMD_MODE, &job);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "systemd operation failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "New systemd job created: %s\n", job);
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t sssctl_systemd_start(void)
+{
+ DEBUG(SSSDBG_TRACE_FUNC, "Starting SSSD via systemd...\n");
+
+ return sssctl_systemd_call(sbus_call_systemd_StartUnit);
+}
+
+errno_t sssctl_systemd_stop(void)
+{
+ DEBUG(SSSDBG_TRACE_FUNC, "Stopping SSSD via systemd...\n");
+
+ return sssctl_systemd_call(sbus_call_systemd_StopUnit);
+}
+
+errno_t sssctl_systemd_restart(void)
+{
+ DEBUG(SSSDBG_TRACE_FUNC, "Restarting SSSD via systemd...\n");
+
+ return sssctl_systemd_call(sbus_call_systemd_RestartUnit);
+}
diff --git a/src/tools/sssctl/sssctl_user_checks.c b/src/tools/sssctl/sssctl_user_checks.c
new file mode 100644
index 0000000..41cf765
--- /dev/null
+++ b/src/tools/sssctl/sssctl_user_checks.c
@@ -0,0 +1,321 @@
+/*
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2009 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU 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/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <nss.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <security/pam_appl.h>
+
+#include "util/util.h"
+#include "tools/common/sss_tools.h"
+#include "tools/sssctl/sssctl.h"
+#include "responder/ifp/ifp_iface/ifp_iface_sync.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
+
+#define DEFAULT_ACTION "acct"
+#define DEFAULT_SERVICE "system-auth"
+
+#define DEFAULT_BUFSIZE 4096
+
+#define PRINT_IFP_PROPERTY(all, name, fmt) do { \
+ if (all->name.is_set) { \
+ fprintf(stdout, " - %s: %" fmt "\n", #name, user->name.value); \
+ } else { \
+ fprintf(stdout, " - %s: not set\n", #name); \
+ } \
+} while (0)
+
+static errno_t get_ifp_user(const char *username)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sbus_sync_connection *conn;
+ struct sbus_all_ifp_user *user;
+ const char *path;
+ struct hash_iter_context_t *extra_iter;
+ char **extra_values;
+ hash_entry_t *extra_entry;
+ int extra_idx;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ conn = sbus_sync_connect_system(tmp_ctx, NULL);
+ if (conn == NULL) {
+ ERROR("Unable to connect to system bus!\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = sbus_call_ifp_users_FindByName(tmp_ctx, conn, IFP_BUS, IFP_PATH_USERS,
+ username, &path);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to find user by name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ PRINT_IFP_WARNING(ret);
+ goto done;
+ }
+
+ ret = sbus_getall_ifp_user(tmp_ctx, conn, IFP_BUS, path, &user);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get user properties [%d]: %s\n",
+ ret, sss_strerror(ret));
+ PRINT_IFP_WARNING(ret);
+ goto done;
+ }
+
+ PRINT("SSSD InfoPipe user lookup result:\n");
+ PRINT_IFP_PROPERTY(user, name, "s");
+ PRINT_IFP_PROPERTY(user, uidNumber, PRIu32);
+ PRINT_IFP_PROPERTY(user, gidNumber, PRIu32);
+ PRINT_IFP_PROPERTY(user, gecos, "s");
+ PRINT_IFP_PROPERTY(user, homeDirectory, "s");
+ PRINT_IFP_PROPERTY(user, loginShell, "s");
+
+ /* print extra attributes */
+ if (user->extraAttributes.is_set) {
+ extra_iter = new_hash_iter_context(user->extraAttributes.value);
+ if (extra_iter == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "new_hash_iter_context failed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ while ((extra_entry = extra_iter->next(extra_iter)) != NULL) {
+ extra_values = extra_entry->value.ptr;
+ for(extra_idx = 0; extra_values[extra_idx] != NULL; ++extra_idx) {
+ fprintf(stdout, " - %s: %s\n", extra_entry->key.str, extra_values[extra_idx]);
+ }
+ }
+ }
+
+ fprintf(stdout, "\n");
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static int sss_getpwnam_check(const char *user)
+{
+ void *dl_handle = NULL;
+ enum nss_status (*sss_getpwnam_r)(const char *name, struct passwd *result,
+ char *buffer, size_t buflen,
+ int *errnop);
+ struct passwd pwd = { 0 };
+ enum nss_status status;
+ char *buffer = NULL;
+ size_t buflen;
+ int nss_errno;
+ int ret;
+
+ dl_handle = dlopen("libnss_sss.so.2", RTLD_NOW);
+ if (dl_handle == NULL) {
+ ERROR("dlopen failed with [%s].\n", dlerror());
+ ret = EIO;
+ goto done;
+ }
+
+ sss_getpwnam_r = dlsym(dl_handle, "_nss_sss_getpwnam_r");
+ if (sss_getpwnam_r == NULL) {
+ ERROR("dlsym failed with [%s].\n", dlerror());
+ ret = EIO;
+ goto done;
+ }
+
+ buflen = DEFAULT_BUFSIZE;
+ buffer = malloc(buflen);
+ if (buffer == NULL) {
+ ERROR("malloc failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ status = sss_getpwnam_r(user, &pwd, buffer, buflen, &nss_errno);
+ if (status != NSS_STATUS_SUCCESS) {
+ ERROR("sss_getpwnam_r failed with [%d].\n", status);
+ ret = EIO;
+ goto done;
+ }
+
+ PRINT("SSSD nss user lookup result:\n");
+ PRINT(" - user name: %s\n", pwd.pw_name);
+ PRINT(" - user id: %d\n", pwd.pw_uid);
+ PRINT(" - group id: %d\n", pwd.pw_gid);
+ PRINT(" - gecos: %s\n", pwd.pw_gecos);
+ PRINT(" - home directory: %s\n", pwd.pw_dir);
+ PRINT(" - shell: %s\n\n", pwd.pw_shell);
+
+ ret = 0;
+
+done:
+ if (dl_handle != NULL) {
+ dlclose(dl_handle);
+ }
+
+ free(buffer);
+
+ return ret;
+}
+
+errno_t sssctl_user_checks(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+
+ pam_handle_t *pamh;
+ const char *user = NULL;
+ const char *action = DEFAULT_ACTION;
+ const char *service = DEFAULT_SERVICE;
+ int ret;
+ int pret;
+ const char *pam_user = NULL;
+ size_t c;
+ char **pam_env;
+
+ /* Parse command line. */
+ struct poptOption options[] = {
+ { "action", 'a', POPT_ARG_STRING, &action, 0,
+ _("PAM action [auth|acct|setc|chau|open|clos], default: "
+ DEFAULT_ACTION), 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, "USERNAME", _("Specify user name."),
+ SSS_TOOL_OPT_REQUIRED, &user, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ goto done;
+ }
+
+ PRINT("user: %s\naction: %s\nservice: %s\n\n", user, action, service);
+
+ if (*user != '\0') {
+ ret = sss_getpwnam_check(user);
+ if (ret != 0) {
+ ERROR("User name lookup with [%s] failed.\n", user);
+ }
+
+ ret = get_ifp_user(user);
+ if (ret != 0) {
+ ERROR("InfoPipe User lookup with [%s] failed.\n", user);
+ }
+ }
+
+ ret = pam_start(service, user, &conv, &pamh);
+ if (ret != PAM_SUCCESS) {
+ ERROR("pam_start failed: %s\n", pam_strerror(pamh, ret));
+ ret = EPERM;
+ goto done;
+ }
+
+ if ( strncmp(action, "auth", 4)== 0 ) {
+ PRINT("testing pam_authenticate\n\n");
+ ret = pam_authenticate(pamh, 0);
+ pret = pam_get_item(pamh, PAM_USER, (const void **) &pam_user);
+ if (pret != PAM_SUCCESS) {
+ ERROR("pam_get_item failed: %s\n", pam_strerror(pamh, pret));
+ pam_user = "- not available -";
+ }
+ ERROR("pam_authenticate for user [%s]: %s\n\n", pam_user,
+ pam_strerror(pamh, ret));
+ } else if ( strncmp(action, "chau", 4)== 0 ) {
+ PRINT("testing pam_chauthtok\n\n");
+ ret = pam_chauthtok(pamh, 0);
+ ERROR("pam_chauthtok: %s\n\n", pam_strerror(pamh, ret));
+ } else if ( strncmp(action, "acct", 4)== 0 ) {
+ PRINT("testing pam_acct_mgmt\n\n");
+ ret = pam_acct_mgmt(pamh, 0);
+ ERROR("pam_acct_mgmt: %s\n\n", pam_strerror(pamh, ret));
+ } else if ( strncmp(action, "setc", 4)== 0 ) {
+ PRINT("testing pam_setcred\n\n");
+ ret = pam_setcred(pamh, 0);
+ ERROR("pam_setcred: [%s]\n\n", pam_strerror(pamh, ret));
+ } else if ( strncmp(action, "open", 4)== 0 ) {
+ PRINT("testing pam_open_session\n\n");
+ ret = pam_open_session(pamh, 0);
+ ERROR("pam_open_session: %s\n\n", pam_strerror(pamh, ret));
+ } else if ( strncmp(action, "clos", 4)== 0 ) {
+ PRINT("testing pam_close_session\n\n");
+ ret = pam_close_session(pamh, 0);
+ ERROR("pam_close_session: %s\n\n", pam_strerror(pamh, ret));
+ } else {
+ ERROR("unknown action\n");
+ }
+
+ ERROR("PAM Environment:\n");
+ pam_env = pam_getenvlist(pamh);
+ if (pam_env != NULL && pam_env[0] != NULL) {
+ for (c = 0; pam_env[c] != NULL; c++) {
+ fprintf(stderr, " - %s\n", pam_env[c]);
+ free(pam_env[c]);
+ }
+ } else {
+ ERROR(" - no env -\n");
+ }
+ free(pam_env);
+
+ pam_end(pamh, ret);
+ ret = EOK;
+
+done:
+ free(discard_const(user));
+
+ return ret;
+}