Matej Habrnal 34dad7
From 3746b7627218438ae7d781fc8b18a221454e9091 Mon Sep 17 00:00:00 2001
Matej Habrnal 34dad7
From: Jakub Filak <jfilak@redhat.com>
Matej Habrnal 34dad7
Date: Mon, 20 Apr 2015 15:15:40 +0200
Matej Habrnal 34dad7
Subject: [PATCH] upload: validate and sanitize uploaded dump directories
Matej Habrnal 34dad7
Matej Habrnal 34dad7
It was discovered that, when moving problem reports from
Matej Habrnal 34dad7
/var/spool/abrt-upload to /var/spool/abrt or /var/tmp/abrt,
Matej Habrnal 34dad7
abrt-handle-upload does not verify that the new problem directory
Matej Habrnal 34dad7
has appropriate permissions and does not contain symbolic links.  A
Matej Habrnal 34dad7
crafted problem report exposes other parts of abrt to attack, and
Matej Habrnal 34dad7
the abrt-handle-upload script allows to overwrite arbitrary files.
Matej Habrnal 34dad7
Matej Habrnal 34dad7
Acknowledgement:
Matej Habrnal 34dad7
Matej Habrnal 34dad7
This issue was discovered by Florian Weimer of Red Hat Product Security.
Matej Habrnal 34dad7
Matej Habrnal 34dad7
Related: #1212953
Matej Habrnal 34dad7
Matej Habrnal 34dad7
Signed-off-by: Jakub Filak <jfilak@redhat.com>
Matej Habrnal 34dad7
---
Matej Habrnal 34dad7
 src/daemon/abrt-handle-upload.in | 78 +++++++++++++++++++++++++++++++++++-----
Matej Habrnal 34dad7
 1 file changed, 70 insertions(+), 8 deletions(-)
Matej Habrnal 34dad7
Matej Habrnal 34dad7
diff --git a/src/daemon/abrt-handle-upload.in b/src/daemon/abrt-handle-upload.in
Matej Habrnal 34dad7
index 45ba72d..e812ef0 100755
Matej Habrnal 34dad7
--- a/src/daemon/abrt-handle-upload.in
Matej Habrnal 34dad7
+++ b/src/daemon/abrt-handle-upload.in
Matej Habrnal 34dad7
@@ -10,6 +10,7 @@ import getopt
Matej Habrnal 34dad7
 import tempfile
Matej Habrnal 34dad7
 import shutil
Matej Habrnal 34dad7
 import datetime
Matej Habrnal 34dad7
+import grp
Matej Habrnal 34dad7
 
Matej Habrnal 34dad7
 from reportclient import set_verbosity, error_msg_and_die, error_msg, log
Matej Habrnal 34dad7
 
Matej Habrnal 34dad7
@@ -36,12 +37,77 @@ def init_gettext():
Matej Habrnal 34dad7
 
Matej Habrnal 34dad7
 import problem
Matej Habrnal 34dad7
 
Matej Habrnal 34dad7
-def write_str_to(filename, s):
Matej Habrnal 34dad7
-    fd = os.open(filename, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, @DEFAULT_DUMP_DIR_MODE@ | stat.S_IROTH)
Matej Habrnal 34dad7
+def write_str_to(filename, s, uid, gid, mode):
Matej Habrnal 34dad7
+    fd = os.open(filename, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, mode)
Matej Habrnal 34dad7
     if fd >= 0:
Matej Habrnal 34dad7
+        os.fchown(fd, uid, gid)
Matej Habrnal 34dad7
         os.write(fd, s)
Matej Habrnal 34dad7
         os.close(fd)
Matej Habrnal 34dad7
 
Matej Habrnal 34dad7
+
Matej Habrnal 34dad7
+def validate_transform_move_and_notify(uploaded_dir_path, problem_dir_path, dest=None):
Matej Habrnal 34dad7
+    fsuid = 0
Matej Habrnal 34dad7
+    fsgid = 0
Matej Habrnal 34dad7
+
Matej Habrnal 34dad7
+    try:
Matej Habrnal 34dad7
+        gabrt = grp.getgrnam("abrt")
Matej Habrnal 34dad7
+        fsgid = gabrt.gr_gid
Matej Habrnal 34dad7
+    except KeyError as ex:
Matej Habrnal 34dad7
+        error_msg("Failed to get GID of 'abrt' (using 0 instead): {0}'".format(str(ex)))
Matej Habrnal 34dad7
+
Matej Habrnal 34dad7
+    try:
Matej Habrnal 34dad7
+        # give the uploaded directory to 'root:abrt' or 'root:root'
Matej Habrnal 34dad7
+        os.chown(uploaded_dir_path, fsuid, fsgid)
Matej Habrnal 34dad7
+        # set the right permissions for this machine
Matej Habrnal 34dad7
+        # (allow the owner and the group to access problem elements,
Matej Habrnal 34dad7
+        #  the default dump dir mode lacks x bit for both)
Matej Habrnal 34dad7
+        os.chmod(uploaded_dir_path, @DEFAULT_DUMP_DIR_MODE@ | stat.S_IXUSR | stat.S_IXGRP)
Matej Habrnal 34dad7
+
Matej Habrnal 34dad7
+        # sanitize problem elements
Matej Habrnal 34dad7
+        for item in os.listdir(uploaded_dir_path):
Matej Habrnal 34dad7
+            apath = os.path.join(uploaded_dir_path, item)
Matej Habrnal 34dad7
+            if os.path.islink(apath):
Matej Habrnal 34dad7
+                # remove symbolic links
Matej Habrnal 34dad7
+                os.remove(apath)
Matej Habrnal 34dad7
+            elif os.path.isdir(apath):
Matej Habrnal 34dad7
+                # remove directories
Matej Habrnal 34dad7
+                shutil.rmtree(apath)
Matej Habrnal 34dad7
+            elif os.path.isfile(apath):
Matej Habrnal 34dad7
+                # set file ownership to 'root:abrt' or 'root:root'
Matej Habrnal 34dad7
+                os.chown(apath, fsuid, fsgid)
Matej Habrnal 34dad7
+                # set the right file permissions for this machine
Matej Habrnal 34dad7
+                os.chmod(apath, @DEFAULT_DUMP_DIR_MODE@)
Matej Habrnal 34dad7
+            else:
Matej Habrnal 34dad7
+                # remove things that are neither files, symlinks nor directories
Matej Habrnal 34dad7
+                os.remove(apath)
Matej Habrnal 34dad7
+    except OSError as ex:
Matej Habrnal 34dad7
+        error_msg("Removing uploaded dir '{0}': '{1}'".format(uploaded_dir_path, str(ex)))
Matej Habrnal 34dad7
+        try:
Matej Habrnal 34dad7
+            shutil.rmtree(uploaded_dir_path)
Matej Habrnal 34dad7
+        except OSError as ex2:
Matej Habrnal 34dad7
+            error_msg_and_die("Failed to clean up dir '{0}': '{1}'".format(uploaded_dir_path, str(ex2)))
Matej Habrnal 34dad7
+        return
Matej Habrnal 34dad7
+
Matej Habrnal 34dad7
+    # overwrite remote if it exists
Matej Habrnal 34dad7
+    remote_path = os.path.join(uploaded_dir_path, "remote")
Matej Habrnal 34dad7
+    write_str_to(remote_path, "1", fsuid, fsgid, @DEFAULT_DUMP_DIR_MODE@)
Matej Habrnal 34dad7
+
Matej Habrnal 34dad7
+    # abrtd would increment count value and abrt-server refuses to process
Matej Habrnal 34dad7
+    # problem directories containing 'count' element when PrivateReports is on.
Matej Habrnal 34dad7
+    count_path = os.path.join(uploaded_dir_path, "count")
Matej Habrnal 34dad7
+    if os.path.exists(count_path):
Matej Habrnal 34dad7
+        # overwrite remote_count if it exists
Matej Habrnal 34dad7
+        remote_count_path = os.path.join(uploaded_dir_path, "remote_count")
Matej Habrnal 34dad7
+        os.rename(count_path, remote_count_path)
Matej Habrnal 34dad7
+
Matej Habrnal 34dad7
+    if not dest:
Matej Habrnal 34dad7
+        dest = problem_dir_path
Matej Habrnal 34dad7
+
Matej Habrnal 34dad7
+    shutil.move(uploaded_dir_path, dest)
Matej Habrnal 34dad7
+
Matej Habrnal 34dad7
+    problem.notify_new_path(problem_dir_path)
Matej Habrnal 34dad7
+
Matej Habrnal 34dad7
+
Matej Habrnal 34dad7
 if __name__ == "__main__":
Matej Habrnal 34dad7
 
Matej Habrnal 34dad7
     # Helper: exit with cleanup
Matej Habrnal 34dad7
@@ -177,21 +243,17 @@ if __name__ == "__main__":
Matej Habrnal 34dad7
         # or one or more complete problem data directories.
Matej Habrnal 34dad7
         # Checking second possibility first.
Matej Habrnal 34dad7
         if (os.path.exists(tempdir+"/analyzer") or os.path.exists(tempdir+"/type")) and os.path.exists(tempdir+"/time"):
Matej Habrnal 34dad7
-            write_str_to(tempdir+"/remote", "1")
Matej Habrnal 34dad7
-            shutil.move(tempdir, abrt_dir)
Matej Habrnal 34dad7
-            problem.notify_new_path(abrt_dir+"/"+os.path.basename(tempdir))
Matej Habrnal 34dad7
+            validate_transform_move_and_notify(tempdir, abrt_dir+"/"+os.path.basename(tempdir), dest=abrt_dir)
Matej Habrnal 34dad7
         else:
Matej Habrnal 34dad7
             for d in os.listdir(tempdir):
Matej Habrnal 34dad7
                 if not os.path.isdir(tempdir+"/"+d):
Matej Habrnal 34dad7
                     continue
Matej Habrnal 34dad7
-                write_str_to(tempdir+"/"+d+"/remote", "1")
Matej Habrnal 34dad7
                 dst = abrt_dir+"/"+d
Matej Habrnal 34dad7
                 if os.path.exists(dst):
Matej Habrnal 34dad7
                     dst += "."+str(os.getpid())
Matej Habrnal 34dad7
                 if os.path.exists(dst):
Matej Habrnal 34dad7
                     continue
Matej Habrnal 34dad7
-                shutil.move(tempdir+"/"+d, dst)
Matej Habrnal 34dad7
-                problem.notify_new_path(dst)
Matej Habrnal 34dad7
+                validate_transform_move_and_notify(tempdir+"/"+d, dst)
Matej Habrnal 34dad7
 
Matej Habrnal 34dad7
         die_exitcode = 0
Matej Habrnal 34dad7
         # This deletes working_dir (== delete_on_exit)
Matej Habrnal 34dad7
-- 
Matej Habrnal 34dad7
2.4.1
Matej Habrnal 34dad7