Matej Habrnal fa1950
From c8ab547f17910391d0cd66a9c6f1917c295480db Mon Sep 17 00:00:00 2001
Matej Habrnal fa1950
From: Jakub Filak <jfilak@redhat.com>
Matej Habrnal fa1950
Date: Wed, 29 Apr 2015 13:57:39 +0200
Matej Habrnal fa1950
Subject: [PATCH] a-a-i-d-t-a-cache: sanitize arguments
Matej Habrnal fa1950
Matej Habrnal fa1950
Parse command lines arguments and use them to create new arguments for
Matej Habrnal fa1950
exec(). No black listing algorithm would be safe enough. The only
Matej Habrnal fa1950
allowed arguments are the following:
Matej Habrnal fa1950
* v - verbose
Matej Habrnal fa1950
* y - noninteractive
Matej Habrnal fa1950
* repo - enable only repositories whose names match the pattern
Matej Habrnal fa1950
* exact - download packages for the specified files
Matej Habrnal fa1950
* ids - passed as magic proc fd path to the wrapped executable
Matej Habrnal fa1950
Matej Habrnal fa1950
The wrapper opens the list of needed build ids passes /proc/self/fd/[fd]
Matej Habrnal fa1950
to the wrapped process. This allows us to open the file with caller's
Matej Habrnal fa1950
UID/GID in order to avoid information disclosures.
Matej Habrnal fa1950
Matej Habrnal fa1950
Forbidden arguments:
Matej Habrnal fa1950
* cache - allows regular users to create a user writable dump directory
Matej Habrnal fa1950
* tmpdir - the same as above
Matej Habrnal fa1950
* size_mb - no need to allow users to fiddle with the cache size
Matej Habrnal fa1950
Matej Habrnal fa1950
Related: #1216962
Matej Habrnal fa1950
Matej Habrnal fa1950
Signed-off-by: Jakub Filak <jfilak@redhat.com>
Matej Habrnal fa1950
---
Matej Habrnal fa1950
 po/POTFILES.in                                     |   1 +
Matej Habrnal fa1950
 .../abrt-action-install-debuginfo-to-abrt-cache.c  | 137 ++++++++++++++++-----
Matej Habrnal fa1950
 2 files changed, 109 insertions(+), 29 deletions(-)
Matej Habrnal fa1950
Matej Habrnal fa1950
diff --git a/po/POTFILES.in b/po/POTFILES.in
Matej Habrnal fa1950
index 15b879f..bd76b87 100644
Matej Habrnal fa1950
--- a/po/POTFILES.in
Matej Habrnal fa1950
+++ b/po/POTFILES.in
Matej Habrnal fa1950
@@ -31,6 +31,7 @@ src/plugins/abrt-action-check-oops-for-hw-error.in
Matej Habrnal fa1950
 src/plugins/abrt-action-generate-backtrace.c
Matej Habrnal fa1950
 src/plugins/abrt-action-generate-core-backtrace.c
Matej Habrnal fa1950
 src/plugins/abrt-action-install-debuginfo.in
Matej Habrnal fa1950
+src/plugins/abrt-action-install-debuginfo-to-abrt-cache.c
Matej Habrnal fa1950
 src/plugins/abrt-action-perform-ccpp-analysis.in
Matej Habrnal fa1950
 src/plugins/abrt-action-trim-files.c
Matej Habrnal fa1950
 src/plugins/abrt-action-ureport
Matej Habrnal fa1950
diff --git a/src/plugins/abrt-action-install-debuginfo-to-abrt-cache.c b/src/plugins/abrt-action-install-debuginfo-to-abrt-cache.c
Matej Habrnal fa1950
index e0eccc0..4fa1783 100644
Matej Habrnal fa1950
--- a/src/plugins/abrt-action-install-debuginfo-to-abrt-cache.c
Matej Habrnal fa1950
+++ b/src/plugins/abrt-action-install-debuginfo-to-abrt-cache.c
Matej Habrnal fa1950
@@ -29,41 +29,120 @@
Matej Habrnal fa1950
  */
Matej Habrnal fa1950
 int main(int argc, char **argv)
Matej Habrnal fa1950
 {
Matej Habrnal fa1950
-    /*
Matej Habrnal fa1950
-     * We disallow passing of arguments which point to writable dirs
Matej Habrnal fa1950
-     * and other files possibly not accessible to calling user.
Matej Habrnal fa1950
-     * This way, the script will always use default values for these arguments.
Matej Habrnal fa1950
-     */
Matej Habrnal fa1950
-    char **pp = argv;
Matej Habrnal fa1950
-    char *arg;
Matej Habrnal fa1950
-    while ((arg = *++pp) != NULL)
Matej Habrnal fa1950
+    /* I18n */
Matej Habrnal fa1950
+    setlocale(LC_ALL, "");
Matej Habrnal fa1950
+#if ENABLE_NLS
Matej Habrnal fa1950
+    bindtextdomain(PACKAGE, LOCALEDIR);
Matej Habrnal fa1950
+    textdomain(PACKAGE);
Matej Habrnal fa1950
+#endif
Matej Habrnal fa1950
+
Matej Habrnal fa1950
+    abrt_init(argv);
Matej Habrnal fa1950
+
Matej Habrnal fa1950
+    /* Can't keep these strings/structs static: _() doesn't support that */
Matej Habrnal fa1950
+    const char *program_usage_string = _(
Matej Habrnal fa1950
+        "& [-y] [-i BUILD_IDS_FILE|-i -] [-e PATH[:PATH]...]\n"
Matej Habrnal fa1950
+        "\t[-r REPO]\n"
Matej Habrnal fa1950
+        "\n"
Matej Habrnal fa1950
+        "Installs debuginfo packages for all build-ids listed in BUILD_IDS_FILE to\n"
Matej Habrnal fa1950
+        "ABRT system cache."
Matej Habrnal fa1950
+    );
Matej Habrnal fa1950
+
Matej Habrnal fa1950
+    enum {
Matej Habrnal fa1950
+        OPT_v = 1 << 0,
Matej Habrnal fa1950
+        OPT_y = 1 << 1,
Matej Habrnal fa1950
+        OPT_i = 1 << 2,
Matej Habrnal fa1950
+        OPT_e = 1 << 3,
Matej Habrnal fa1950
+        OPT_r = 1 << 4,
Matej Habrnal fa1950
+        OPT_s = 1 << 5,
Matej Habrnal fa1950
+    };
Matej Habrnal fa1950
+
Matej Habrnal fa1950
+    const char *build_ids = "build_ids";
Matej Habrnal fa1950
+    const char *exact = NULL;
Matej Habrnal fa1950
+    const char *repo = NULL;
Matej Habrnal fa1950
+    const char *size_mb = NULL;
Matej Habrnal fa1950
+
Matej Habrnal fa1950
+    struct options program_options[] = {
Matej Habrnal fa1950
+        OPT__VERBOSE(&g_verbose),
Matej Habrnal fa1950
+        OPT_BOOL  ('y', "yes",         NULL,                   _("Noninteractive, assume 'Yes' to all questions")),
Matej Habrnal fa1950
+        OPT_STRING('i', "ids",   &build_ids, "BUILD_IDS_FILE", _("- means STDIN, default: build_ids")),
Matej Habrnal fa1950
+        OPT_STRING('e', "exact",     &exact, "EXACT",          _("Download only specified files")),
Matej Habrnal fa1950
+        OPT_STRING('r', "repo",       &repo, "REPO",           _("Pattern to use when searching for repos, default: *debug*")),
Matej Habrnal fa1950
+        OPT_STRING('s', "size_mb", &size_mb, "SIZE_MB",        _("Ignored option")),
Matej Habrnal fa1950
+        OPT_END()
Matej Habrnal fa1950
+    };
Matej Habrnal fa1950
+    const unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
Matej Habrnal fa1950
+
Matej Habrnal fa1950
+    const gid_t egid = getegid();
Matej Habrnal fa1950
+    const gid_t rgid = getgid();
Matej Habrnal fa1950
+    const uid_t euid = geteuid();
Matej Habrnal fa1950
+    const gid_t ruid = getuid();
Matej Habrnal fa1950
+
Matej Habrnal fa1950
+    /* We need to open the build ids file under the caller's UID/GID to avoid
Matej Habrnal fa1950
+     * information disclosures when reading files with changed UID.
Matej Habrnal fa1950
+     * Unfortunately, we cannot replace STDIN with the new fd because ABRT uses
Matej Habrnal fa1950
+     * STDIN to communicate with the caller. So, the following code opens a
Matej Habrnal fa1950
+     * dummy file descriptor to the build ids file and passes the new fd's proc
Matej Habrnal fa1950
+     * path to the wrapped program in the ids argument.
Matej Habrnal fa1950
+     * The new fd remains opened, the OS will close it for us. */
Matej Habrnal fa1950
+    char *build_ids_self_fd = NULL;
Matej Habrnal fa1950
+    if (strcmp("-", build_ids) != 0)
Matej Habrnal fa1950
+    {
Matej Habrnal fa1950
+        if (setregid(egid, rgid) < 0)
Matej Habrnal fa1950
+            perror_msg_and_die("setregid(egid, rgid)");
Matej Habrnal fa1950
+
Matej Habrnal fa1950
+        if (setreuid(euid, ruid) < 0)
Matej Habrnal fa1950
+            perror_msg_and_die("setreuid(euid, ruid)");
Matej Habrnal fa1950
+
Matej Habrnal fa1950
+        const int build_ids_fd = open(build_ids, O_RDONLY);
Matej Habrnal fa1950
+
Matej Habrnal fa1950
+        if (setregid(rgid, egid) < 0)
Matej Habrnal fa1950
+            perror_msg_and_die("setregid(rgid, egid)");
Matej Habrnal fa1950
+
Matej Habrnal fa1950
+        if (setreuid(ruid, euid) < 0 )
Matej Habrnal fa1950
+            perror_msg_and_die("setreuid(ruid, euid)");
Matej Habrnal fa1950
+
Matej Habrnal fa1950
+        if (build_ids_fd < 0)
Matej Habrnal fa1950
+            perror_msg_and_die("Failed to open file '%s'", build_ids);
Matej Habrnal fa1950
+
Matej Habrnal fa1950
+        /* We are not going to free this memory. There is no place to do so. */
Matej Habrnal fa1950
+        build_ids_self_fd = xasprintf("/proc/self/fd/%d", build_ids_fd);
Matej Habrnal fa1950
+    }
Matej Habrnal fa1950
+
Matej Habrnal fa1950
+    /* name, -v, --ids, -, -y, -e, EXACT, -r, REPO, --, NULL */
Matej Habrnal fa1950
+    const char *args[11];
Matej Habrnal fa1950
     {
Matej Habrnal fa1950
-        /* Allow taking ids from stdin */
Matej Habrnal fa1950
-        if (strcmp(arg, "--ids=-") == 0)
Matej Habrnal fa1950
-            continue;
Matej Habrnal fa1950
-
Matej Habrnal fa1950
-        if (strncmp(arg, "--exact", 7) == 0)
Matej Habrnal fa1950
-            continue;
Matej Habrnal fa1950
-
Matej Habrnal fa1950
-        if (strncmp(arg, "--cache", 7) == 0)
Matej Habrnal fa1950
-            error_msg_and_die("bad option %s", arg);
Matej Habrnal fa1950
-        if (strncmp(arg, "--tmpdir", 8) == 0)
Matej Habrnal fa1950
-            error_msg_and_die("bad option %s", arg);
Matej Habrnal fa1950
-        if (strncmp(arg, "--ids", 5) == 0)
Matej Habrnal fa1950
-            error_msg_and_die("bad option %s", arg);
Matej Habrnal fa1950
+        const char *verbs[] = { "", "-v", "-vv", "-vvv" };
Matej Habrnal fa1950
+        unsigned i = 0;
Matej Habrnal fa1950
+        args[i++] = EXECUTABLE;
Matej Habrnal fa1950
+        args[i++] = "--ids";
Matej Habrnal fa1950
+        args[i++] = (build_ids_self_fd != NULL) ? build_ids_self_fd : "-";
Matej Habrnal fa1950
+        if (g_verbose > 0)
Matej Habrnal fa1950
+            args[i++] = verbs[g_verbose <= 3 ? g_verbose : 3];
Matej Habrnal fa1950
+        if ((opts & OPT_y))
Matej Habrnal fa1950
+            args[i++] = "-y";
Matej Habrnal fa1950
+        if ((opts & OPT_e))
Matej Habrnal fa1950
+        {
Matej Habrnal fa1950
+            args[i++] = "--exact";
Matej Habrnal fa1950
+            args[i++] = exact;
Matej Habrnal fa1950
+        }
Matej Habrnal fa1950
+        if ((opts & OPT_r))
Matej Habrnal fa1950
+        {
Matej Habrnal fa1950
+            args[i++] = "--repo";
Matej Habrnal fa1950
+            args[i++] = repo;
Matej Habrnal fa1950
+        }
Matej Habrnal fa1950
+        args[i++] = "--";
Matej Habrnal fa1950
+        args[i] = NULL;
Matej Habrnal fa1950
     }
Matej Habrnal fa1950
 
Matej Habrnal fa1950
     /* Switch real user/group to effective ones.
Matej Habrnal fa1950
      * Otherwise yum library gets confused - gets EPERM (why??).
Matej Habrnal fa1950
      */
Matej Habrnal fa1950
-    gid_t g = getegid();
Matej Habrnal fa1950
     /* do setregid only if we have to, to not upset selinux needlessly */
Matej Habrnal fa1950
-    if (g != getgid())
Matej Habrnal fa1950
-        IGNORE_RESULT(setregid(g, g));
Matej Habrnal fa1950
-    uid_t u = geteuid();
Matej Habrnal fa1950
-    if (u != getuid())
Matej Habrnal fa1950
+    if (egid != rgid)
Matej Habrnal fa1950
+        IGNORE_RESULT(setregid(egid, egid));
Matej Habrnal fa1950
+    if (euid != ruid)
Matej Habrnal fa1950
     {
Matej Habrnal fa1950
-        IGNORE_RESULT(setreuid(u, u));
Matej Habrnal fa1950
+        IGNORE_RESULT(setreuid(euid, euid));
Matej Habrnal fa1950
         /* We are suid'ed! */
Matej Habrnal fa1950
         /* Prevent malicious user from messing up with suid'ed process: */
Matej Habrnal fa1950
 #if 1
Matej Habrnal fa1950
@@ -117,11 +196,11 @@ int main(int argc, char **argv)
Matej Habrnal fa1950
         // abrt-action-install-debuginfo doesn't fail when spawning
Matej Habrnal fa1950
         // abrt-action-trim-files
Matej Habrnal fa1950
         char path_env[] = "PATH=/usr/sbin:/sbin:/usr/bin:/bin:"BIN_DIR":"SBIN_DIR;
Matej Habrnal fa1950
-        if (u != 0)
Matej Habrnal fa1950
+        if (euid != 0)
Matej Habrnal fa1950
             strcpy(path_env, "PATH=/usr/bin:/bin:"BIN_DIR);
Matej Habrnal fa1950
         putenv(path_env);
Matej Habrnal fa1950
     }
Matej Habrnal fa1950
 
Matej Habrnal fa1950
-    execvp(EXECUTABLE, argv);
Matej Habrnal fa1950
+    execvp(EXECUTABLE, (char **)args);
Matej Habrnal fa1950
     error_msg_and_die("Can't execute %s", EXECUTABLE);
Matej Habrnal fa1950
 }
Matej Habrnal fa1950
-- 
Matej Habrnal fa1950
2.1.0
Matej Habrnal fa1950