Blame SOURCES/0002-TOOLS-replace-system-with-execvp-to-avoid-execution-.patch

a49733
From 7ab83f97e1cbefb78ece17232185bdd2985f0bbe Mon Sep 17 00:00:00 2001
a49733
From: Alexey Tikhonov <atikhono@redhat.com>
a49733
Date: Fri, 18 Jun 2021 13:17:19 +0200
a49733
Subject: [PATCH] TOOLS: replace system() with execvp() to avoid execution of
a49733
 user supplied command
a49733
MIME-Version: 1.0
a49733
Content-Type: text/plain; charset=UTF-8
a49733
Content-Transfer-Encoding: 8bit
a49733
a49733
:relnote: A flaw was found in SSSD, where the sssctl command was
a49733
vulnerable to shell command injection via the logs-fetch and
a49733
cache-expire subcommands. This flaw allows an attacker to trick
a49733
the root user into running a specially crafted sssctl command,
a49733
such as via sudo, to gain root access. The highest threat from this
a49733
vulnerability is to confidentiality, integrity, as well as system
a49733
availability.
a49733
This patch fixes a flaw by replacing system() with execvp().
a49733
a49733
:fixes: CVE-2021-3621
a49733
a49733
Reviewed-by: Pavel Březina <pbrezina@redhat.com>
a49733
---
a49733
 src/tools/sssctl/sssctl.c      | 39 ++++++++++++++++-------
a49733
 src/tools/sssctl/sssctl.h      |  2 +-
a49733
 src/tools/sssctl/sssctl_data.c | 57 +++++++++++-----------------------
a49733
 src/tools/sssctl/sssctl_logs.c | 32 +++++++++++++++----
a49733
 4 files changed, 73 insertions(+), 57 deletions(-)
a49733
a49733
diff --git a/src/tools/sssctl/sssctl.c b/src/tools/sssctl/sssctl.c
a49733
index 2997dbf96..8adaf3091 100644
a49733
--- a/src/tools/sssctl/sssctl.c
a49733
+++ b/src/tools/sssctl/sssctl.c
a49733
@@ -97,22 +97,36 @@ sssctl_prompt(const char *message,
a49733
     return SSSCTL_PROMPT_ERROR;
a49733
 }
a49733
 
a49733
-errno_t sssctl_run_command(const char *command)
a49733
+errno_t sssctl_run_command(const char *const argv[])
a49733
 {
a49733
     int ret;
a49733
+    int wstatus;
a49733
 
a49733
-    DEBUG(SSSDBG_TRACE_FUNC, "Running %s\n", command);
a49733
+    DEBUG(SSSDBG_TRACE_FUNC, "Running '%s'\n", argv[0]);
a49733
 
a49733
-    ret = system(command);
a49733
+    ret = fork();
a49733
     if (ret == -1) {
a49733
-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to execute %s\n", command);
a49733
         ERROR("Error while executing external command\n");
a49733
         return EFAULT;
a49733
-    } else if (WEXITSTATUS(ret) != 0) {
a49733
-        DEBUG(SSSDBG_CRIT_FAILURE, "Command %s failed with [%d]\n",
a49733
-              command, WEXITSTATUS(ret));
a49733
+    }
a49733
+
a49733
+    if (ret == 0) {
a49733
+        /* cast is safe - see
a49733
+        https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
a49733
+        "The statement about argv[] and envp[] being constants ... "
a49733
+        */
a49733
+        execvp(argv[0], discard_const_p(char * const, argv));
a49733
         ERROR("Error while executing external command\n");
a49733
-        return EIO;
a49733
+        _exit(1);
a49733
+    } else {
a49733
+        if (waitpid(ret, &wstatus, 0) == -1) {
a49733
+            ERROR("Error while executing external command '%s'\n", argv[0]);
a49733
+            return EFAULT;
a49733
+        } else if (WEXITSTATUS(wstatus) != 0) {
a49733
+            ERROR("Command '%s' failed with [%d]\n",
a49733
+                  argv[0], WEXITSTATUS(wstatus));
a49733
+            return EIO;
a49733
+        }
a49733
     }
a49733
 
a49733
     return EOK;
a49733
@@ -132,11 +146,14 @@ static errno_t sssctl_manage_service(enum sssctl_svc_action action)
a49733
 #elif defined(HAVE_SERVICE)
a49733
     switch (action) {
a49733
     case SSSCTL_SVC_START:
a49733
-        return sssctl_run_command(SERVICE_PATH" sssd start");
a49733
+        return sssctl_run_command(
a49733
+                      (const char *[]){SERVICE_PATH, "sssd", "start", NULL});
a49733
     case SSSCTL_SVC_STOP:
a49733
-        return sssctl_run_command(SERVICE_PATH" sssd stop");
a49733
+        return sssctl_run_command(
a49733
+                      (const char *[]){SERVICE_PATH, "sssd", "stop", NULL});
a49733
     case SSSCTL_SVC_RESTART:
a49733
-        return sssctl_run_command(SERVICE_PATH" sssd restart");
a49733
+        return sssctl_run_command(
a49733
+                      (const char *[]){SERVICE_PATH, "sssd", "restart", NULL});
a49733
     }
a49733
 #endif
a49733
 
a49733
diff --git a/src/tools/sssctl/sssctl.h b/src/tools/sssctl/sssctl.h
a49733
index 0115b2457..599ef6519 100644
a49733
--- a/src/tools/sssctl/sssctl.h
a49733
+++ b/src/tools/sssctl/sssctl.h
a49733
@@ -47,7 +47,7 @@ enum sssctl_prompt_result
a49733
 sssctl_prompt(const char *message,
a49733
               enum sssctl_prompt_result defval);
a49733
 
a49733
-errno_t sssctl_run_command(const char *command);
a49733
+errno_t sssctl_run_command(const char *const argv[]); /* argv[0] - command */
a49733
 bool sssctl_start_sssd(bool force);
a49733
 bool sssctl_stop_sssd(bool force);
a49733
 bool sssctl_restart_sssd(bool force);
a49733
diff --git a/src/tools/sssctl/sssctl_data.c b/src/tools/sssctl/sssctl_data.c
a49733
index 8d79b977f..bf2291341 100644
a49733
--- a/src/tools/sssctl/sssctl_data.c
a49733
+++ b/src/tools/sssctl/sssctl_data.c
a49733
@@ -105,15 +105,15 @@ static errno_t sssctl_backup(bool force)
a49733
         }
a49733
     }
a49733
 
a49733
-    ret = sssctl_run_command("sss_override user-export "
a49733
-                             SSS_BACKUP_USER_OVERRIDES);
a49733
+    ret = sssctl_run_command((const char *[]){"sss_override", "user-export",
a49733
+                                              SSS_BACKUP_USER_OVERRIDES, NULL});
a49733
     if (ret != EOK) {
a49733
         ERROR("Unable to export user overrides\n");
a49733
         return ret;
a49733
     }
a49733
 
a49733
-    ret = sssctl_run_command("sss_override group-export "
a49733
-                             SSS_BACKUP_GROUP_OVERRIDES);
a49733
+    ret = sssctl_run_command((const char *[]){"sss_override", "group-export",
a49733
+                                              SSS_BACKUP_GROUP_OVERRIDES, NULL});
a49733
     if (ret != EOK) {
a49733
         ERROR("Unable to export group overrides\n");
a49733
         return ret;
a49733
@@ -158,8 +158,8 @@ static errno_t sssctl_restore(bool force_start, bool force_restart)
a49733
     }
a49733
 
a49733
     if (sssctl_backup_file_exists(SSS_BACKUP_USER_OVERRIDES)) {
a49733
-        ret = sssctl_run_command("sss_override user-import "
a49733
-                                 SSS_BACKUP_USER_OVERRIDES);
a49733
+        ret = sssctl_run_command((const char *[]){"sss_override", "user-import",
a49733
+                                                  SSS_BACKUP_USER_OVERRIDES, NULL});
a49733
         if (ret != EOK) {
a49733
             ERROR("Unable to import user overrides\n");
a49733
             return ret;
a49733
@@ -167,8 +167,8 @@ static errno_t sssctl_restore(bool force_start, bool force_restart)
a49733
     }
a49733
 
a49733
     if (sssctl_backup_file_exists(SSS_BACKUP_USER_OVERRIDES)) {
a49733
-        ret = sssctl_run_command("sss_override group-import "
a49733
-                                 SSS_BACKUP_GROUP_OVERRIDES);
a49733
+        ret = sssctl_run_command((const char *[]){"sss_override", "group-import",
a49733
+                                                  SSS_BACKUP_GROUP_OVERRIDES, NULL});
a49733
         if (ret != EOK) {
a49733
             ERROR("Unable to import group overrides\n");
a49733
             return ret;
a49733
@@ -296,40 +296,19 @@ errno_t sssctl_cache_expire(struct sss_cmdline *cmdline,
a49733
                             void *pvt)
a49733
 {
a49733
     errno_t ret;
a49733
-    char *cmd_args = NULL;
a49733
-    const char *cachecmd = SSS_CACHE;
a49733
-    char *cmd = NULL;
a49733
-    int i;
a49733
-
a49733
-    if (cmdline->argc == 0) {
a49733
-        ret = sssctl_run_command(cachecmd);
a49733
-        goto done;
a49733
-    }
a49733
 
a49733
-    cmd_args = talloc_strdup(tool_ctx, "");
a49733
-    if (cmd_args == NULL) {
a49733
-        ret = ENOMEM;
a49733
-        goto done;
a49733
+    const char **args = talloc_array_size(tool_ctx,
a49733
+                                          sizeof(char *),
a49733
+                                          cmdline->argc + 2);
a49733
+    if (!args) {
a49733
+        return ENOMEM;
a49733
     }
a49733
+    memcpy(&args[1], cmdline->argv, sizeof(char *) * cmdline->argc);
a49733
+    args[0] = SSS_CACHE;
a49733
+    args[cmdline->argc + 1] = NULL;
a49733
 
a49733
-    for (i = 0; i < cmdline->argc; i++) {
a49733
-        cmd_args = talloc_strdup_append(cmd_args, cmdline->argv[i]);
a49733
-        if (i != cmdline->argc - 1) {
a49733
-            cmd_args = talloc_strdup_append(cmd_args, " ");
a49733
-        }
a49733
-    }
a49733
-
a49733
-    cmd = talloc_asprintf(tool_ctx, "%s %s", cachecmd, cmd_args);
a49733
-    if (cmd == NULL) {
a49733
-        ret = ENOMEM;
a49733
-        goto done;
a49733
-    }
a49733
-
a49733
-    ret = sssctl_run_command(cmd);
a49733
-
a49733
-done:
a49733
-    talloc_free(cmd_args);
a49733
-    talloc_free(cmd);
a49733
+    ret = sssctl_run_command(args);
a49733
 
a49733
+    talloc_free(args);
a49733
     return ret;
a49733
 }
a49733
diff --git a/src/tools/sssctl/sssctl_logs.c b/src/tools/sssctl/sssctl_logs.c
a49733
index 9ff2be05b..ebb2c4571 100644
a49733
--- a/src/tools/sssctl/sssctl_logs.c
a49733
+++ b/src/tools/sssctl/sssctl_logs.c
a49733
@@ -31,6 +31,7 @@
a49733
 #include <ldb.h>
a49733
 #include <popt.h>
a49733
 #include <stdio.h>
a49733
+#include <glob.h>
a49733
 
a49733
 #include "util/util.h"
a49733
 #include "tools/common/sss_process.h"
a49733
@@ -230,6 +231,7 @@ errno_t sssctl_logs_remove(struct sss_cmdline *cmdline,
a49733
 {
a49733
     struct sssctl_logs_opts opts = {0};
a49733
     errno_t ret;
a49733
+    glob_t globbuf;
a49733
 
a49733
     /* Parse command line. */
a49733
     struct poptOption options[] = {
a49733
@@ -253,8 +255,20 @@ errno_t sssctl_logs_remove(struct sss_cmdline *cmdline,
a49733
 
a49733
         sss_signal(SIGHUP);
a49733
     } else {
a49733
+        globbuf.gl_offs = 4;
a49733
+        ret = glob(LOG_PATH"/*.log", GLOB_ERR|GLOB_DOOFFS, NULL, &globbuf);
a49733
+        if (ret != 0) {
a49733
+            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to expand log files list\n");
a49733
+            return ret;
a49733
+        }
a49733
+        globbuf.gl_pathv[0] = discard_const_p(char, "truncate");
a49733
+        globbuf.gl_pathv[1] = discard_const_p(char, "--no-create");
a49733
+        globbuf.gl_pathv[2] = discard_const_p(char, "--size");
a49733
+        globbuf.gl_pathv[3] = discard_const_p(char, "0");
a49733
+
a49733
         PRINT("Truncating log files...\n");
a49733
-        ret = sssctl_run_command("truncate --no-create --size 0 " LOG_FILES);
a49733
+        ret = sssctl_run_command((const char * const*)globbuf.gl_pathv);
a49733
+        globfree(&globbuf);
a49733
         if (ret != EOK) {
a49733
             ERROR("Unable to truncate log files\n");
a49733
             return ret;
a49733
@@ -269,8 +283,8 @@ errno_t sssctl_logs_fetch(struct sss_cmdline *cmdline,
a49733
                           void *pvt)
a49733
 {
a49733
     const char *file;
a49733
-    const char *cmd;
a49733
     errno_t ret;
a49733
+    glob_t globbuf;
a49733
 
a49733
     /* Parse command line. */
a49733
     ret = sss_tool_popt_ex(cmdline, NULL, SSS_TOOL_OPT_OPTIONAL, NULL, NULL,
a49733
@@ -280,13 +294,19 @@ errno_t sssctl_logs_fetch(struct sss_cmdline *cmdline,
a49733
         return ret;
a49733
     }
a49733
 
a49733
-    cmd = talloc_asprintf(tool_ctx, "tar -czf %s %s", file, LOG_FILES);
a49733
-    if (cmd == NULL) {
a49733
-        ERROR("Out of memory!");
a49733
+    globbuf.gl_offs = 3;
a49733
+    ret = glob(LOG_PATH"/*.log", GLOB_ERR|GLOB_DOOFFS, NULL, &globbuf);
a49733
+    if (ret != 0) {
a49733
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to expand log files list\n");
a49733
+        return ret;
a49733
     }
a49733
+    globbuf.gl_pathv[0] = discard_const_p(char, "tar");
a49733
+    globbuf.gl_pathv[1] = discard_const_p(char, "-czf");
a49733
+    globbuf.gl_pathv[2] = discard_const_p(char, file);
a49733
 
a49733
     PRINT("Archiving log files into %s...\n", file);
a49733
-    ret = sssctl_run_command(cmd);
a49733
+    ret = sssctl_run_command((const char * const*)globbuf.gl_pathv);
a49733
+    globfree(&globbuf);
a49733
     if (ret != EOK) {
a49733
         ERROR("Unable to archive log files\n");
a49733
         return ret;
a49733
-- 
a49733
2.26.3
a49733