qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH] qga: set umask 0077 when daemonizing (CVE-2013-2007)
@ 2013-05-07 11:47 Anthony Liguori
  2013-05-07 15:55 ` Eric Blake
                   ` (2 more replies)
  0 siblings, 3 replies; 17+ messages in thread
From: Anthony Liguori @ 2013-05-07 11:47 UTC (permalink / raw)
  To: qemu-devel; +Cc: Anthony Liguori, Laszlo Ersek

From: Laszlo Ersek <lersek@redhat.com>

The qemu guest agent creates a bunch of files with insecure permissions
when started in daemon mode. For example:

  -rw-rw-rw- 1 root root /var/log/qemu-ga.log
  -rw-rw-rw- 1 root root /var/run/qga.state
  -rw-rw-rw- 1 root root /var/log/qga-fsfreeze-hook.log

In addition, at least all files created with the "guest-file-open" QMP
command, and all files created with shell output redirection (or
otherwise) by utilities invoked by the fsfreeze hook script are affected.

For now mask all file mode bits for "group" and "others" in
become_daemon().

Temporarily, for compatibility reasons, stick with the 0666 file-mode in
case of files newly created by the "guest-file-open" QMP call. Do so
without changing the umask temporarily.

Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
---
 qga/commands-posix.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++++--
 qga/main.c           |   2 +-
 2 files changed, 120 insertions(+), 5 deletions(-)

diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 3b5c536..04c6951 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -18,6 +18,9 @@
 #include <unistd.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
 #include <inttypes.h>
 #include "qga/guest-agent-core.h"
 #include "qga-qmp-commands.h"
@@ -237,9 +240,122 @@ static GuestFileHandle *guest_file_handle_find(int64_t id, Error **err)
     return NULL;
 }
 
+typedef const char * const ccpc;
+
+/* http://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html */
+static const struct {
+    ccpc *forms;
+    int oflag_base;
+} guest_file_open_modes[] = {
+    { (ccpc[]){ "r",  "rb",         NULL }, O_RDONLY                      },
+    { (ccpc[]){ "w",  "wb",         NULL }, O_WRONLY | O_CREAT | O_TRUNC  },
+    { (ccpc[]){ "a",  "ab",         NULL }, O_WRONLY | O_CREAT | O_APPEND },
+    { (ccpc[]){ "r+", "rb+", "r+b", NULL }, O_RDWR                        },
+    { (ccpc[]){ "w+", "wb+", "w+b", NULL }, O_RDWR   | O_CREAT | O_TRUNC  },
+    { (ccpc[]){ "a+", "ab+", "a+b", NULL }, O_RDWR   | O_CREAT | O_APPEND }
+};
+
+static int
+find_open_flag(const char *mode_str, Error **err)
+{
+    unsigned mode;
+
+    for (mode = 0; mode < ARRAY_SIZE(guest_file_open_modes); ++mode) {
+        ccpc *form;
+
+        form = guest_file_open_modes[mode].forms;
+        while (*form != NULL && strcmp(*form, mode_str) != 0) {
+            ++form;
+        }
+        if (*form != NULL) {
+            break;
+        }
+    }
+
+    if (mode == ARRAY_SIZE(guest_file_open_modes)) {
+        error_setg(err, "invalid file open mode '%s'", mode_str);
+        return -1;
+    }
+    return guest_file_open_modes[mode].oflag_base | O_NOCTTY | O_NONBLOCK;
+}
+
+#define DEFAULT_NEW_FILE_MODE (S_IRUSR | S_IWUSR | \
+                               S_IRGRP | S_IWGRP | \
+                               S_IROTH | S_IWOTH)
+
+static FILE *
+safe_open_or_create(const char *path, const char *mode, Error **err)
+{
+    Error *local_err = NULL;
+    int oflag;
+
+    oflag = find_open_flag(mode, &local_err);
+    if (local_err == NULL) {
+        int fd;
+
+        /* If the caller wants / allows creation of a new file, we implement it
+         * with a two step process: open() + (open() / fchmod()).
+         *
+         * First we insist on creating the file exclusively as a new file. If
+         * that succeeds, we're free to set any file-mode bits on it. (The
+         * motivation is that we want to set those file-mode bits independently
+         * of the current umask.)
+         *
+         * If the exclusive creation fails because the file already exists
+         * (EEXIST is not possible for any other reason), we just attempt to
+         * open the file, but in this case we won't be allowed to change the
+         * file-mode bits on the preexistent file.
+         *
+         * The pathname should never disappear between the two open()s in
+         * practice. If it happens, then someone very likely tried to race us.
+         * In this case just go ahead and report the ENOENT from the second
+         * open() to the caller.
+         *
+         * If the caller wants to open a preexistent file, then the first
+         * open() is decisive and its third argument is ignored, and the second
+         * open() and the fchmod() are never called.
+         */
+        fd = open(path, oflag | ((oflag & O_CREAT) ? O_EXCL : 0), 0);
+        if (fd == -1 && errno == EEXIST) {
+            oflag &= ~(unsigned)O_CREAT;
+            fd = open(path, oflag);
+        }
+
+        if (fd == -1) {
+            error_setg_errno(&local_err, errno, "failed to open file '%s' "
+                             "(mode: '%s')", path, mode);
+        } else {
+            qemu_set_cloexec(fd);
+
+            if ((oflag & O_CREAT) && fchmod(fd, DEFAULT_NEW_FILE_MODE) == -1) {
+                error_setg_errno(&local_err, errno, "failed to set permission "
+                                 "0%03o on new file '%s' (mode: '%s')",
+                                 (unsigned)DEFAULT_NEW_FILE_MODE, path, mode);
+            } else {
+                FILE *f;
+
+                f = fdopen(fd, mode);
+                if (f == NULL) {
+                    error_setg_errno(&local_err, errno, "failed to associate "
+                                     "stdio stream with file descriptor %d, "
+                                     "file '%s' (mode: '%s')", fd, path, mode);
+                } else {
+                    return f;
+                }
+            }
+
+            close(fd);
+        }
+    }
+
+    error_propagate(err, local_err);
+    return NULL;
+}
+
 int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err)
 {
     FILE *fh;
+    Error *local_err = NULL;
     int fd;
     int64_t ret = -1, handle;
 
@@ -247,10 +363,9 @@ int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, E
         mode = "r";
     }
     slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
-    fh = fopen(path, mode);
-    if (!fh) {
-        error_setg_errno(err, errno, "failed to open file '%s' (mode: '%s')",
-                         path, mode);
+    fh = safe_open_or_create(path, mode, &local_err);
+    if (local_err != NULL) {
+        error_propagate(err, local_err);
         return -1;
     }
 
diff --git a/qga/main.c b/qga/main.c
index 1841759..44a2836 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -478,7 +478,7 @@ static void become_daemon(const char *pidfile)
         }
     }
 
-    umask(0);
+    umask(S_IRWXG | S_IRWXO);
     sid = setsid();
     if (sid < 0) {
         goto fail;
-- 
1.8.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread
* [Qemu-devel] [PULL for-1.5] qemu-ga CVE-2013-2007 addenda
@ 2013-05-13 15:08 Michael Roth
  2013-05-13 15:08 ` [Qemu-devel] [PATCH 1/2] qga: distinguish binary modes in "guest_file_open_modes" map Michael Roth
  0 siblings, 1 reply; 17+ messages in thread
From: Michael Roth @ 2013-05-13 15:08 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, lersek

Hi Anthony,

These are fix-ups for Laszlo's CVE-2013-2007 fix:

http://www.mail-archive.com/qemu-devel@nongnu.org/msg170944.html

The main effect is to avoid cluttering filesystems with empty files if
we hit an error path in the open/create/chmod path.

I'm unable to confirm whether or not these error paths can actually be
triggered in 1.5 or are just theoretical, but I plan to apply these to
1.4.2 to be sure and so I'm also submitting this for 1.5.

If you think it's too late in the cycle to warrant these for 1.5 I can
also cherry-pick them from my QGA tree for 1.4.2 instead.

The following changes since commit 38ebb396c955ceb2ef7e246248ceb7f8bfe1b774:

  target-i386: ROR r8/r16 imm instruction fix (2013-05-10 19:59:54 +0200)

are available in the git repository at:

  http://github.com/mdroth/qemu qga-pull-2013-05-13

for you to fetch changes up to 2b720018060179b394f8ce736983373ab80dd37c:

  qga: unlink just created guest-file if fchmod() or fdopen() fails on it (2013-05-13 09:45:49 -0500)

----------------------------------------------------------------
Laszlo Ersek (2):
      qga: distinguish binary modes in "guest_file_open_modes" map
      qga: unlink just created guest-file if fchmod() or fdopen() fails on it

 qga/commands-posix.c |   25 +++++++++++++++++++------
 1 file changed, 19 insertions(+), 6 deletions(-)

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2013-05-13 15:10 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-05-07 11:47 [Qemu-devel] [PATCH] qga: set umask 0077 when daemonizing (CVE-2013-2007) Anthony Liguori
2013-05-07 15:55 ` Eric Blake
2013-05-07 16:56   ` [Qemu-devel] [PATCH 1/2] qga: distinguish binary modes in "guest_file_open_modes" map Laszlo Ersek
2013-05-07 17:19     ` Eric Blake
2013-05-07 17:27     ` Eric Blake
2013-05-07 18:54       ` Peter Maydell
2013-05-07 20:10       ` mdroth
2013-05-07 16:56   ` [Qemu-devel] [PATCH 2/2] qga: try to unlink just created guest-file if fchmod() fails on it Laszlo Ersek
2013-05-07 17:30     ` Eric Blake
2013-05-08  0:35       ` Laszlo Ersek
2013-05-08  2:24         ` Eric Blake
2013-05-07 20:28   ` [Qemu-devel] [PATCH] qga: set umask 0077 when daemonizing (CVE-2013-2007) mdroth
2013-05-07 20:54     ` Eric Blake
2013-05-07 18:49 ` Anthony Liguori
2013-05-08  2:03   ` Anthony Liguori
2013-05-09 14:39 ` Bruce Rogers
  -- strict thread matches above, loose matches on Subject: below --
2013-05-13 15:08 [Qemu-devel] [PULL for-1.5] qemu-ga CVE-2013-2007 addenda Michael Roth
2013-05-13 15:08 ` [Qemu-devel] [PATCH 1/2] qga: distinguish binary modes in "guest_file_open_modes" map Michael Roth

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).