Blame SOURCES/0106-lib-add-functions-validating-dump-dir.patch

a60cd7
From b7f8bd20b7fb5b72f003ae3fa647c1d75f4218b7 Mon Sep 17 00:00:00 2001
a60cd7
From: Jakub Filak <jfilak@redhat.com>
a60cd7
Date: Thu, 23 Apr 2015 14:40:18 +0200
a60cd7
Subject: [ABRT PATCH] lib: add functions validating dump dir
a60cd7
a60cd7
Move the code from abrt-server to shared library and fix the condition
a60cd7
validating dump dir's path.
a60cd7
a60cd7
As of now, abrt is allowed to process only direct sub-directories of the
a60cd7
dump locations.
a60cd7
a60cd7
Signed-off-by: Jakub Filak <jfilak@redhat.com>
a60cd7
---
a60cd7
 src/daemon/abrt-server.c | 42 ++++++------------------
a60cd7
 src/include/libabrt.h    |  4 +++
a60cd7
 src/lib/hooklib.c        | 56 +++++++++++++++++++++++++++++++
a60cd7
 tests/Makefile.am        |  3 +-
a60cd7
 tests/hooklib.at         | 85 ++++++++++++++++++++++++++++++++++++++++++++++++
a60cd7
 tests/testsuite.at       |  1 +
a60cd7
 6 files changed, 158 insertions(+), 33 deletions(-)
a60cd7
 create mode 100644 tests/hooklib.at
a60cd7
a60cd7
diff --git a/src/daemon/abrt-server.c b/src/daemon/abrt-server.c
a60cd7
index 4d486d4..1030461 100644
a60cd7
--- a/src/daemon/abrt-server.c
a60cd7
+++ b/src/daemon/abrt-server.c
a60cd7
@@ -76,20 +76,6 @@ static unsigned total_bytes_read = 0;
a60cd7
 static uid_t client_uid = (uid_t)-1L;
a60cd7
 
a60cd7
 
a60cd7
-static bool dir_is_in_dump_location(const char *dump_dir_name)
a60cd7
-{
a60cd7
-    unsigned len = strlen(g_settings_dump_location);
a60cd7
-
a60cd7
-    if (strncmp(dump_dir_name, g_settings_dump_location, len) == 0
a60cd7
-     && dump_dir_name[len] == '/'
a60cd7
-    /* must not contain "/." anywhere (IOW: disallow ".." component) */
a60cd7
-     && !strstr(dump_dir_name + len, "/.")
a60cd7
-    ) {
a60cd7
-        return 1;
a60cd7
-    }
a60cd7
-    return 0;
a60cd7
-}
a60cd7
-
a60cd7
 /* Remove dump dir */
a60cd7
 static int delete_path(const char *dump_dir_name)
a60cd7
 {
a60cd7
@@ -100,6 +86,11 @@ static int delete_path(const char *dump_dir_name)
a60cd7
         error_msg("Bad problem directory name '%s', should start with: '%s'", dump_dir_name, g_settings_dump_location);
a60cd7
         return 400; /* Bad Request */
a60cd7
     }
a60cd7
+    if (!dir_has_correct_permissions(dump_dir_name))
a60cd7
+    {
a60cd7
+        error_msg("Problem directory '%s' isn't owned by root:abrt or others are not restricted from access", dump_dir_name);
a60cd7
+        return 400; /*  */
a60cd7
+    }
a60cd7
     if (!dump_dir_accessible_by_uid(dump_dir_name, client_uid))
a60cd7
     {
a60cd7
         if (errno == ENOTDIR)
a60cd7
@@ -154,26 +145,13 @@ static int run_post_create(const char *dirname)
a60cd7
         error_msg("Bad problem directory name '%s', should start with: '%s'", dirname, g_settings_dump_location);
a60cd7
         return 400; /* Bad Request */
a60cd7
     }
a60cd7
+    if (!dir_has_correct_permissions(dirname))
a60cd7
+    {
a60cd7
+        error_msg("Problem directory '%s' isn't owned by root:abrt or others are not restricted from access", dirname);
a60cd7
+        return 400; /*  */
a60cd7
+    }
a60cd7
     if (g_settings_privatereports)
a60cd7
     {
a60cd7
-        struct stat statbuf;
a60cd7
-        if (lstat(dirname, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
a60cd7
-        {
a60cd7
-            error_msg("Path '%s' isn't directory", dirname);
a60cd7
-            return 404; /* Not Found */
a60cd7
-        }
a60cd7
-        /* Get ABRT's group gid */
a60cd7
-        struct group *gr = getgrnam("abrt");
a60cd7
-        if (!gr)
a60cd7
-        {
a60cd7
-            error_msg("Group 'abrt' does not exist");
a60cd7
-            return 500;
a60cd7
-        }
a60cd7
-        if (statbuf.st_uid != 0 || !(statbuf.st_gid == 0 || statbuf.st_gid == gr->gr_gid) || statbuf.st_mode & 07)
a60cd7
-        {
a60cd7
-            error_msg("Problem directory '%s' isn't owned by root:abrt or others are not restricted from access", dirname);
a60cd7
-            return 403;
a60cd7
-        }
a60cd7
         struct dump_dir *dd = dd_opendir(dirname, DD_OPEN_READONLY);
a60cd7
         const bool complete = dd && problem_dump_dir_is_complete(dd);
a60cd7
         dd_close(dd);
a60cd7
diff --git a/src/include/libabrt.h b/src/include/libabrt.h
a60cd7
index 0320c5b..5bf2397 100644
a60cd7
--- a/src/include/libabrt.h
a60cd7
+++ b/src/include/libabrt.h
a60cd7
@@ -47,6 +47,10 @@ char *run_unstrip_n(const char *dump_dir_name, unsigned timeout_sec);
a60cd7
 #define get_backtrace abrt_get_backtrace
a60cd7
 char *get_backtrace(const char *dump_dir_name, unsigned timeout_sec, const char *debuginfo_dirs);
a60cd7
 
a60cd7
+#define dir_is_in_dump_location abrt_dir_is_in_dump_location
a60cd7
+bool dir_is_in_dump_location(const char *dir_name);
a60cd7
+#define dir_has_correct_permissions abrt_dir_has_correct_permissions
a60cd7
+bool dir_has_correct_permissions(const char *dir_name);
a60cd7
 
a60cd7
 #define g_settings_nMaxCrashReportsSize abrt_g_settings_nMaxCrashReportsSize
a60cd7
 extern unsigned int  g_settings_nMaxCrashReportsSize;
a60cd7
diff --git a/src/lib/hooklib.c b/src/lib/hooklib.c
a60cd7
index fb7750d..4b20025 100644
a60cd7
--- a/src/lib/hooklib.c
a60cd7
+++ b/src/lib/hooklib.c
a60cd7
@@ -427,3 +427,59 @@ char* problem_data_save(problem_data_t *pd)
a60cd7
     log_info("problem id: '%s'", problem_id);
a60cd7
     return problem_id;
a60cd7
 }
a60cd7
+
a60cd7
+bool dir_is_in_dump_location(const char *dir_name)
a60cd7
+{
a60cd7
+    unsigned len = strlen(g_settings_dump_location);
a60cd7
+
a60cd7
+    /* The path must start with "g_settings_dump_location" */
a60cd7
+    if (strncmp(dir_name, g_settings_dump_location, len) != 0)
a60cd7
+    {
a60cd7
+        log_debug("Bad parent directory: '%s' not in '%s'", g_settings_dump_location, dir_name);
a60cd7
+        return false;
a60cd7
+    }
a60cd7
+
a60cd7
+    /* and must be a sub-directory of the g_settings_dump_location dir */
a60cd7
+    const char *base_name = dir_name + len;
a60cd7
+    while (*base_name && *base_name == '/')
a60cd7
+        ++base_name;
a60cd7
+
a60cd7
+    if (*(base_name - 1) != '/' || !str_is_correct_filename(base_name))
a60cd7
+    {
a60cd7
+        log_debug("Invalid dump directory name: '%s'", base_name);
a60cd7
+        return false;
a60cd7
+    }
a60cd7
+
a60cd7
+    /* and we are sure it is a directory */
a60cd7
+    struct stat sb;
a60cd7
+    if (lstat(dir_name, &sb) < 0)
a60cd7
+    {
a60cd7
+        VERB2 perror_msg("stat('%s')", dir_name);
a60cd7
+        return errno== ENOENT;
a60cd7
+    }
a60cd7
+
a60cd7
+    return S_ISDIR(sb.st_mode);
a60cd7
+}
a60cd7
+
a60cd7
+bool dir_has_correct_permissions(const char *dir_name)
a60cd7
+{
a60cd7
+    if (g_settings_privatereports)
a60cd7
+    {
a60cd7
+        struct stat statbuf;
a60cd7
+        if (lstat(dir_name, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
a60cd7
+        {
a60cd7
+            error_msg("Path '%s' isn't directory", dir_name);
a60cd7
+            return false;
a60cd7
+        }
a60cd7
+        /* Get ABRT's group gid */
a60cd7
+        struct group *gr = getgrnam("abrt");
a60cd7
+        if (!gr)
a60cd7
+        {
a60cd7
+            error_msg("Group 'abrt' does not exist");
a60cd7
+            return false;
a60cd7
+        }
a60cd7
+        if (statbuf.st_uid != 0 || !(statbuf.st_gid == 0 || statbuf.st_gid == gr->gr_gid) || statbuf.st_mode & 07)
a60cd7
+            return false;
a60cd7
+    }
a60cd7
+    return true;
a60cd7
+}
a60cd7
diff --git a/tests/Makefile.am b/tests/Makefile.am
a60cd7
index 5ef08a0..416f579 100644
a60cd7
--- a/tests/Makefile.am
a60cd7
+++ b/tests/Makefile.am
a60cd7
@@ -29,7 +29,8 @@ TESTSUITE_AT = \
a60cd7
   testsuite.at \
a60cd7
   pyhook.at \
a60cd7
   koops-parser.at \
a60cd7
-  ignored_problems.at
a60cd7
+  ignored_problems.at \
a60cd7
+  hooklib.at
a60cd7
 
a60cd7
 EXTRA_DIST += $(TESTSUITE_AT)
a60cd7
 TESTSUITE = $(srcdir)/testsuite
a60cd7
diff --git a/tests/hooklib.at b/tests/hooklib.at
a60cd7
new file mode 100644
a60cd7
index 0000000..70631c6
a60cd7
--- /dev/null
a60cd7
+++ b/tests/hooklib.at
a60cd7
@@ -0,0 +1,85 @@
a60cd7
+# -*- Autotest -*-
a60cd7
+
a60cd7
+AT_BANNER([hooklib])
a60cd7
+
a60cd7
+AT_TESTFUN([dir_is_in_dump_location],
a60cd7
+[[
a60cd7
+#include "libabrt.h"
a60cd7
+#include <assert.h>
a60cd7
+
a60cd7
+void test(char *name, bool expected)
a60cd7
+{
a60cd7
+    if (dir_is_in_dump_location(name) != expected)
a60cd7
+    {
a60cd7
+        fprintf(stderr, "Bad: %s", name);
a60cd7
+        abort();
a60cd7
+    }
a60cd7
+
a60cd7
+    free(name);
a60cd7
+}
a60cd7
+
a60cd7
+int main(void)
a60cd7
+{
a60cd7
+    g_verbose = 3;
a60cd7
+    load_abrt_conf();
a60cd7
+
a60cd7
+    g_verbose = 3;
a60cd7
+
a60cd7
+    char *name;
a60cd7
+
a60cd7
+    assert(dir_is_in_dump_location("/") == false);
a60cd7
+
a60cd7
+    asprintf(&name, "%s", g_settings_dump_location);
a60cd7
+    test(name, false);
a60cd7
+
a60cd7
+    asprintf(&name, "%s..evil", g_settings_dump_location);
a60cd7
+    test(name, false);
a60cd7
+
a60cd7
+    asprintf(&name, "%s/", g_settings_dump_location);
a60cd7
+    test(name, false);
a60cd7
+
a60cd7
+    asprintf(&name, "%s///", g_settings_dump_location);
a60cd7
+    test(name, false);
a60cd7
+
a60cd7
+    asprintf(&name, "%s/.", g_settings_dump_location);
a60cd7
+    test(name, false);
a60cd7
+
a60cd7
+    asprintf(&name, "%s///.", g_settings_dump_location);
a60cd7
+    test(name, false);
a60cd7
+
a60cd7
+    asprintf(&name, "%s/./", g_settings_dump_location);
a60cd7
+    test(name, false);
a60cd7
+
a60cd7
+    asprintf(&name, "%s/.///", g_settings_dump_location);
a60cd7
+    test(name, false);
a60cd7
+
a60cd7
+    asprintf(&name, "%s/..", g_settings_dump_location);
a60cd7
+    test(name, false);
a60cd7
+
a60cd7
+    asprintf(&name, "%s///..", g_settings_dump_location);
a60cd7
+    test(name, false);
a60cd7
+
a60cd7
+    asprintf(&name, "%s/../", g_settings_dump_location);
a60cd7
+    test(name, false);
a60cd7
+
a60cd7
+    asprintf(&name, "%s/..///", g_settings_dump_location);
a60cd7
+    test(name, false);
a60cd7
+
a60cd7
+    asprintf(&name, "%s/good/../../../evil", g_settings_dump_location);
a60cd7
+    test(name, false);
a60cd7
+
a60cd7
+    asprintf(&name, "%s/good..still", g_settings_dump_location);
a60cd7
+    test(name, true);
a60cd7
+
a60cd7
+    asprintf(&name, "%s/good.new", g_settings_dump_location);
a60cd7
+    test(name, true);
a60cd7
+
a60cd7
+    asprintf(&name, "%s/.meta", g_settings_dump_location);
a60cd7
+    test(name, true);
a60cd7
+
a60cd7
+    asprintf(&name, "%s/..data", g_settings_dump_location);
a60cd7
+    test(name, true);
a60cd7
+
a60cd7
+    return 0;
a60cd7
+}
a60cd7
+]])
a60cd7
diff --git a/tests/testsuite.at b/tests/testsuite.at
a60cd7
index b8f363d..765de2a 100644
a60cd7
--- a/tests/testsuite.at
a60cd7
+++ b/tests/testsuite.at
a60cd7
@@ -4,3 +4,4 @@
a60cd7
 m4_include([koops-parser.at])
a60cd7
 m4_include([pyhook.at])
a60cd7
 m4_include([ignored_problems.at])
a60cd7
+m4_include([hooklib.at])
a60cd7
-- 
a60cd7
1.8.3.1
a60cd7