diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
commit | 74aa0bc6779af38018a03fd2cf4419fe85917904 (patch) | |
tree | 9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/tools/sssctl | |
parent | Initial commit. (diff) | |
download | sssd-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.c | 354 | ||||
-rw-r--r-- | src/tools/sssctl/sssctl.h | 152 | ||||
-rw-r--r-- | src/tools/sssctl/sssctl_access_report.c | 422 | ||||
-rw-r--r-- | src/tools/sssctl/sssctl_cache.c | 718 | ||||
-rw-r--r-- | src/tools/sssctl/sssctl_cert.c | 289 | ||||
-rw-r--r-- | src/tools/sssctl/sssctl_config.c | 201 | ||||
-rw-r--r-- | src/tools/sssctl/sssctl_data.c | 538 | ||||
-rw-r--r-- | src/tools/sssctl/sssctl_domains.c | 408 | ||||
-rw-r--r-- | src/tools/sssctl/sssctl_logs.c | 608 | ||||
-rw-r--r-- | src/tools/sssctl/sssctl_passkey.c | 42 | ||||
-rw-r--r-- | src/tools/sssctl/sssctl_systemd.c | 95 | ||||
-rw-r--r-- | src/tools/sssctl/sssctl_user_checks.c | 321 |
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(×tamp); + 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, ¤t_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; +} |