Matej Habrnal fa1950
From 49279f4030225cee166362d7222e97496022331f Mon Sep 17 00:00:00 2001
Matej Habrnal fa1950
From: Jakub Filak <jfilak@redhat.com>
Matej Habrnal fa1950
Date: Fri, 17 Apr 2015 14:40:20 +0200
Matej Habrnal fa1950
Subject: [PATCH] ccpp: do not use value of /proc/PID/cwd for chdir
Matej Habrnal fa1950
Matej Habrnal fa1950
Avoid symlink resolutions.
Matej Habrnal fa1950
Matej Habrnal fa1950
This issue was discovered by Florian Weimer of Red Hat Product Security.
Matej Habrnal fa1950
Matej Habrnal fa1950
Signed-off-by: Jakub Filak <jfilak@redhat.com>
Matej Habrnal fa1950
---
Matej Habrnal fa1950
 src/hooks/abrt-hook-ccpp.c | 116 ++++++++++++++++++++++++---------------------
Matej Habrnal fa1950
 1 file changed, 61 insertions(+), 55 deletions(-)
Matej Habrnal fa1950
Matej Habrnal fa1950
diff --git a/src/hooks/abrt-hook-ccpp.c b/src/hooks/abrt-hook-ccpp.c
Matej Habrnal fa1950
index b481421..8f3b2b0 100644
Matej Habrnal fa1950
--- a/src/hooks/abrt-hook-ccpp.c
Matej Habrnal fa1950
+++ b/src/hooks/abrt-hook-ccpp.c
Matej Habrnal fa1950
@@ -131,6 +131,7 @@ static off_t copyfd_sparse(int src_fd, int dst_fd1, int dst_fd2, off_t size2)
Matej Habrnal fa1950
 
Matej Habrnal fa1950
 /* Global data */
Matej Habrnal fa1950
 static char *user_pwd;
Matej Habrnal fa1950
+static DIR *proc_cwd;
Matej Habrnal fa1950
 static struct dump_dir *dd;
Matej Habrnal fa1950
 
Matej Habrnal fa1950
 /*
Matej Habrnal fa1950
@@ -149,22 +150,24 @@ static struct dump_dir *dd;
Matej Habrnal fa1950
  */
Matej Habrnal fa1950
 static const char percent_specifiers[] = "%scpugtei";
Matej Habrnal fa1950
 static char *core_basename = (char*) "core";
Matej Habrnal fa1950
-/*
Matej Habrnal fa1950
- * Used for error messages only.
Matej Habrnal fa1950
- * It is either the same as core_basename if it is absolute,
Matej Habrnal fa1950
- * or $PWD/core_basename.
Matej Habrnal fa1950
- */
Matej Habrnal fa1950
-static char *full_core_basename;
Matej Habrnal fa1950
+
Matej Habrnal fa1950
+static DIR *open_cwd(pid_t pid)
Matej Habrnal fa1950
+{
Matej Habrnal fa1950
+    char buf[sizeof("/proc/%lu/cwd") + sizeof(long)*3];
Matej Habrnal fa1950
+    sprintf(buf, "/proc/%lu/cwd", (long)pid);
Matej Habrnal fa1950
+
Matej Habrnal fa1950
+    DIR *cwd = opendir(buf);
Matej Habrnal fa1950
+    if (cwd == NULL)
Matej Habrnal fa1950
+        perror_msg("Can't open process's CWD for CompatCore");
Matej Habrnal fa1950
+
Matej Habrnal fa1950
+    return cwd;
Matej Habrnal fa1950
+}
Matej Habrnal fa1950
 
Matej Habrnal fa1950
 static int open_user_core(uid_t uid, uid_t fsuid, pid_t pid, char **percent_values)
Matej Habrnal fa1950
 {
Matej Habrnal fa1950
-    errno = 0;
Matej Habrnal fa1950
-    if (user_pwd == NULL
Matej Habrnal fa1950
-     || chdir(user_pwd) != 0
Matej Habrnal fa1950
-    ) {
Matej Habrnal fa1950
-        perror_msg("Can't cd to '%s'", user_pwd);
Matej Habrnal fa1950
+    proc_cwd = open_cwd(pid);
Matej Habrnal fa1950
+    if (proc_cwd == NULL)
Matej Habrnal fa1950
         return -1;
Matej Habrnal fa1950
-    }
Matej Habrnal fa1950
 
Matej Habrnal fa1950
     struct passwd* pw = getpwuid(uid);
Matej Habrnal fa1950
     gid_t gid = pw ? pw->pw_gid : uid;
Matej Habrnal fa1950
@@ -227,15 +230,10 @@ static int open_user_core(uid_t uid, uid_t fsuid, pid_t pid, char **percent_valu
Matej Habrnal fa1950
         }
Matej Habrnal fa1950
     }
Matej Habrnal fa1950
 
Matej Habrnal fa1950
-    full_core_basename = core_basename;
Matej Habrnal fa1950
-    if (core_basename[0] != '/')
Matej Habrnal fa1950
+    if (g_need_nonrelative && core_basename[0] != '/')
Matej Habrnal fa1950
     {
Matej Habrnal fa1950
-        if (g_need_nonrelative)
Matej Habrnal fa1950
-        {
Matej Habrnal fa1950
-            error_msg("Current suid_dumpable policy prevents from saving core dumps according to relative core_pattern");
Matej Habrnal fa1950
-            return -1;
Matej Habrnal fa1950
-        }
Matej Habrnal fa1950
-        core_basename = concat_path_file(user_pwd, core_basename);
Matej Habrnal fa1950
+        error_msg("Current suid_dumpable policy prevents from saving core dumps according to relative core_pattern");
Matej Habrnal fa1950
+        return -1;
Matej Habrnal fa1950
     }
Matej Habrnal fa1950
 
Matej Habrnal fa1950
     /* Open (create) compat core file.
Matej Habrnal fa1950
@@ -271,7 +269,7 @@ static int open_user_core(uid_t uid, uid_t fsuid, pid_t pid, char **percent_valu
Matej Habrnal fa1950
     struct stat sb;
Matej Habrnal fa1950
     errno = 0;
Matej Habrnal fa1950
     /* Do not O_TRUNC: if later checks fail, we do not want to have file already modified here */
Matej Habrnal fa1950
-    int user_core_fd = open(core_basename, O_WRONLY | O_CREAT | O_NOFOLLOW | g_user_core_flags, 0600); /* kernel makes 0600 too */
Matej Habrnal fa1950
+    int user_core_fd = openat(dirfd(proc_cwd), core_basename, O_WRONLY | O_CREAT | O_NOFOLLOW | g_user_core_flags, 0600); /* kernel makes 0600 too */
Matej Habrnal fa1950
     xsetegid(0);
Matej Habrnal fa1950
     xseteuid(0);
Matej Habrnal fa1950
     if (user_core_fd < 0
Matej Habrnal fa1950
@@ -281,15 +279,15 @@ static int open_user_core(uid_t uid, uid_t fsuid, pid_t pid, char **percent_valu
Matej Habrnal fa1950
      || sb.st_uid != fsuid
Matej Habrnal fa1950
     ) {
Matej Habrnal fa1950
         if (user_core_fd < 0)
Matej Habrnal fa1950
-            perror_msg("Can't open '%s'", full_core_basename);
Matej Habrnal fa1950
+            perror_msg("Can't open '%s' at '%s'", core_basename, user_pwd);
Matej Habrnal fa1950
         else
Matej Habrnal fa1950
-            perror_msg("'%s' is not a regular file with link count 1 owned by UID(%d)", full_core_basename, fsuid);
Matej Habrnal fa1950
+            perror_msg("'%s' at '%s' is not a regular file with link count 1 owned by UID(%d)", core_basename, user_pwd, fsuid);
Matej Habrnal fa1950
         return -1;
Matej Habrnal fa1950
     }
Matej Habrnal fa1950
     if (ftruncate(user_core_fd, 0) != 0) {
Matej Habrnal fa1950
         /* perror first, otherwise unlink may trash errno */
Matej Habrnal fa1950
-        perror_msg("Can't truncate '%s' to size 0", full_core_basename);
Matej Habrnal fa1950
-        unlink(core_basename);
Matej Habrnal fa1950
+        perror_msg("Can't truncate '%s' at '%s' to size 0", core_basename, user_pwd);
Matej Habrnal fa1950
+        unlinkat(dirfd(proc_cwd), core_basename, /*unlink file*/0);
Matej Habrnal fa1950
         return -1;
Matej Habrnal fa1950
     }
Matej Habrnal fa1950
 
Matej Habrnal fa1950
@@ -310,10 +308,8 @@ static int create_or_die(const char *filename, int user_core_fd)
Matej Habrnal fa1950
     if (dd)
Matej Habrnal fa1950
         dd_delete(dd);
Matej Habrnal fa1950
     if (user_core_fd >= 0)
Matej Habrnal fa1950
-    {
Matej Habrnal fa1950
-        xchdir(user_pwd);
Matej Habrnal fa1950
-        unlink(core_basename);
Matej Habrnal fa1950
-    }
Matej Habrnal fa1950
+        unlinkat(dirfd(proc_cwd), core_basename, /*unlink file*/0);
Matej Habrnal fa1950
+
Matej Habrnal fa1950
     errno = sv_errno;
Matej Habrnal fa1950
     perror_msg_and_die("Can't open '%s'", filename);
Matej Habrnal fa1950
 }
Matej Habrnal fa1950
@@ -341,27 +337,34 @@ static void create_core_backtrace(pid_t tid, const char *executable, int signal_
Matej Habrnal fa1950
 
Matej Habrnal fa1950
 static int create_user_core(int user_core_fd, pid_t pid, off_t ulimit_c)
Matej Habrnal fa1950
 {
Matej Habrnal fa1950
+    int err = 1;
Matej Habrnal fa1950
     if (user_core_fd >= 0)
Matej Habrnal fa1950
     {
Matej Habrnal fa1950
         off_t core_size = copyfd_size(STDIN_FILENO, user_core_fd, ulimit_c, COPYFD_SPARSE);
Matej Habrnal fa1950
         if (fsync(user_core_fd) != 0 || close(user_core_fd) != 0 || core_size < 0)
Matej Habrnal fa1950
         {
Matej Habrnal fa1950
             /* perror first, otherwise unlink may trash errno */
Matej Habrnal fa1950
-            perror_msg("Error writing '%s'", full_core_basename);
Matej Habrnal fa1950
-            xchdir(user_pwd);
Matej Habrnal fa1950
-            unlink(core_basename);
Matej Habrnal fa1950
-            return 1;
Matej Habrnal fa1950
+            perror_msg("Error writing '%s' at '%s'", core_basename, user_pwd);
Matej Habrnal fa1950
+            unlinkat(dirfd(proc_cwd), core_basename, /*only files*/0);
Matej Habrnal fa1950
+            goto finito;
Matej Habrnal fa1950
         }
Matej Habrnal fa1950
         if (ulimit_c == 0 || core_size > ulimit_c)
Matej Habrnal fa1950
         {
Matej Habrnal fa1950
-            xchdir(user_pwd);
Matej Habrnal fa1950
-            unlink(core_basename);
Matej Habrnal fa1950
-            return 1;
Matej Habrnal fa1950
+            unlinkat(dirfd(proc_cwd), core_basename, /*only files*/0);
Matej Habrnal fa1950
+            goto finito;
Matej Habrnal fa1950
         }
Matej Habrnal fa1950
-        log_notice("Saved core dump of pid %lu to %s (%llu bytes)", (long)pid, full_core_basename, (long long)core_size);
Matej Habrnal fa1950
+        log_notice("Saved core dump of pid %lu to %s at '%s' (%llu bytes)", (long)pid, core_basename, user_pwd, (long long)core_size);
Matej Habrnal fa1950
     }
Matej Habrnal fa1950
+    err = 0;
Matej Habrnal fa1950
 
Matej Habrnal fa1950
-    return 0;
Matej Habrnal fa1950
+finito:
Matej Habrnal fa1950
+    if (proc_cwd != NULL)
Matej Habrnal fa1950
+    {
Matej Habrnal fa1950
+        closedir(proc_cwd);
Matej Habrnal fa1950
+        proc_cwd = NULL;
Matej Habrnal fa1950
+    }
Matej Habrnal fa1950
+
Matej Habrnal fa1950
+    return err;
Matej Habrnal fa1950
 }
Matej Habrnal fa1950
 
Matej Habrnal fa1950
 static int test_configuration(bool setting_SaveFullCore, bool setting_CreateCoreBacktrace)
Matej Habrnal fa1950
@@ -417,6 +420,7 @@ int main(int argc, char** argv)
Matej Habrnal fa1950
     if (fd > 2)
Matej Habrnal fa1950
         close(fd);
Matej Habrnal fa1950
 
Matej Habrnal fa1950
+    int err = 1;
Matej Habrnal fa1950
     logmode = LOGMODE_JOURNAL;
Matej Habrnal fa1950
 
Matej Habrnal fa1950
     /* Parse abrt.conf */
Matej Habrnal fa1950
@@ -598,7 +602,8 @@ int main(int argc, char** argv)
Matej Habrnal fa1950
             error_msg_and_die("Error saving '%s'", path);
Matej Habrnal fa1950
         }
Matej Habrnal fa1950
         log_notice("Saved core dump of pid %lu (%s) to %s (%llu bytes)", (long)pid, executable, path, (long long)core_size);
Matej Habrnal fa1950
-        return 0;
Matej Habrnal fa1950
+        err = 0;
Matej Habrnal fa1950
+        goto cleanup_and_exit;
Matej Habrnal fa1950
     }
Matej Habrnal fa1950
 
Matej Habrnal fa1950
     unsigned path_len = snprintf(path, sizeof(path), "%s/ccpp-%s-%lu.new",
Matej Habrnal fa1950
@@ -669,6 +674,7 @@ int main(int argc, char** argv)
Matej Habrnal fa1950
             if (strcmp(rootdir, "/") != 0)
Matej Habrnal fa1950
                 dd_save_text(dd, FILENAME_ROOTDIR, rootdir);
Matej Habrnal fa1950
         }
Matej Habrnal fa1950
+        free(rootdir);
Matej Habrnal fa1950
 
Matej Habrnal fa1950
         char *reason = xasprintf("%s killed by SIG%s",
Matej Habrnal fa1950
                                  last_slash, signame ? signame : signal_str);
Matej Habrnal fa1950
@@ -701,7 +707,7 @@ int main(int argc, char** argv)
Matej Habrnal fa1950
             {
Matej Habrnal fa1950
                 error_msg("Error saving '%s'", path);
Matej Habrnal fa1950
 
Matej Habrnal fa1950
-                goto error_exit;
Matej Habrnal fa1950
+                goto cleanup_and_exit;
Matej Habrnal fa1950
             }
Matej Habrnal fa1950
         }
Matej Habrnal fa1950
 
Matej Habrnal fa1950
@@ -730,7 +736,7 @@ int main(int argc, char** argv)
Matej Habrnal fa1950
                  * but it does not log file name */
Matej Habrnal fa1950
                 error_msg("Error writing '%s'", path);
Matej Habrnal fa1950
 
Matej Habrnal fa1950
-                goto error_exit;
Matej Habrnal fa1950
+                goto cleanup_and_exit;
Matej Habrnal fa1950
             }
Matej Habrnal fa1950
             if (user_core_fd >= 0
Matej Habrnal fa1950
                 /* error writing user coredump? */
Matej Habrnal fa1950
@@ -740,8 +746,7 @@ int main(int argc, char** argv)
Matej Habrnal fa1950
                 )
Matej Habrnal fa1950
             ) {
Matej Habrnal fa1950
                 /* nuke it (silently) */
Matej Habrnal fa1950
-                xchdir(user_pwd);
Matej Habrnal fa1950
-                unlink(core_basename);
Matej Habrnal fa1950
+                unlinkat(dirfd(proc_cwd), core_basename, /*unlink file*/0);
Matej Habrnal fa1950
             }
Matej Habrnal fa1950
         }
Matej Habrnal fa1950
         else
Matej Habrnal fa1950
@@ -787,7 +792,7 @@ int main(int argc, char** argv)
Matej Habrnal fa1950
                 {
Matej Habrnal fa1950
                     error_msg("Error saving '%s'", path);
Matej Habrnal fa1950
 
Matej Habrnal fa1950
-                    goto error_exit;
Matej Habrnal fa1950
+                    goto cleanup_and_exit;
Matej Habrnal fa1950
                 }
Matej Habrnal fa1950
                 close(src_fd);
Matej Habrnal fa1950
             }
Matej Habrnal fa1950
@@ -833,22 +838,23 @@ int main(int argc, char** argv)
Matej Habrnal fa1950
             trim_problem_dirs(g_settings_dump_location, maxsize * (double)(1024*1024), path);
Matej Habrnal fa1950
         }
Matej Habrnal fa1950
 
Matej Habrnal fa1950
-        free(rootdir);
Matej Habrnal fa1950
-        return 0;
Matej Habrnal fa1950
+        err = 0;
Matej Habrnal fa1950
+    }
Matej Habrnal fa1950
+    else
Matej Habrnal fa1950
+    {
Matej Habrnal fa1950
+        /* We didn't create abrt dump, but may need to create compat coredump */
Matej Habrnal fa1950
+        return create_user_core(user_core_fd, pid, ulimit_c);
Matej Habrnal fa1950
     }
Matej Habrnal fa1950
 
Matej Habrnal fa1950
-    /* We didn't create abrt dump, but may need to create compat coredump */
Matej Habrnal fa1950
-    return create_user_core(user_core_fd, pid, ulimit_c);
Matej Habrnal fa1950
-
Matej Habrnal fa1950
-error_exit:
Matej Habrnal fa1950
+cleanup_and_exit:
Matej Habrnal fa1950
     if (dd)
Matej Habrnal fa1950
         dd_delete(dd);
Matej Habrnal fa1950
 
Matej Habrnal fa1950
     if (user_core_fd >= 0)
Matej Habrnal fa1950
-    {
Matej Habrnal fa1950
-        xchdir(user_pwd);
Matej Habrnal fa1950
-        unlink(core_basename);
Matej Habrnal fa1950
-    }
Matej Habrnal fa1950
+        unlinkat(dirfd(proc_cwd), core_basename, /*only files*/0);
Matej Habrnal fa1950
+
Matej Habrnal fa1950
+    if (proc_cwd != NULL)
Matej Habrnal fa1950
+        closedir(proc_cwd);
Matej Habrnal fa1950
 
Matej Habrnal fa1950
-    xfunc_die();
Matej Habrnal fa1950
+    return err;
Matej Habrnal fa1950
 }
Matej Habrnal fa1950
-- 
Matej Habrnal fa1950
2.1.0
Matej Habrnal fa1950