From: Laszlo Ersek <lersek@redhat.com>
To: "Andreas Färber" <afaerber@suse.de>
Cc: Anthony Liguori <aliguori@us.ibm.com>,
qemu-devel@nongnu.org, qemu-stable@nongnu.org
Subject: Re: [Qemu-devel] [PATCH stable-1.1] qga: set umask 0077 when daemonizing (CVE-2013-2007)
Date: Mon, 27 May 2013 02:12:35 +0200 [thread overview]
Message-ID: <51A2A4F3.5010006@redhat.com> (raw)
In-Reply-To: <51A2A4B6.2030400@redhat.com>
On 05/27/13 02:11, Laszlo Ersek wrote:
> On 05/26/13 15:34, Andreas Färber wrote:
>> 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>
>> (cherry picked from commit c689b4f1bac352dcfd6ecb9a1d45337de0f1de67)
>>
>> [AF: Use error_set() instead of error_setg*()]
>> Signed-off-by: Andreas Färber <afaerber@suse.de>
>> ---
>> qemu-ga.c | 2 +-
>> qga/commands-posix.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++++--
>> 2 files changed, 115 insertions(+), 4 deletions(-)
>>
>> diff --git a/qemu-ga.c b/qemu-ga.c
>> index 1b00c2f..086b1b9 100644
>> --- a/qemu-ga.c
>> +++ b/qemu-ga.c
>> @@ -424,7 +424,7 @@ static void become_daemon(const char *pidfile)
>> }
>> }
>>
>> - umask(0);
>> + umask(S_IRWXG | S_IRWXO);
>> sid = setsid();
>> if (sid < 0) {
>> goto fail;
>> diff --git a/qga/commands-posix.c b/qga/commands-posix.c
>> index 00d035d..c0a1bd4 100644
>> --- a/qga/commands-posix.c
>> +++ b/qga/commands-posix.c
>> @@ -15,6 +15,9 @@
>> #include <sys/types.h>
>> #include <sys/ioctl.h>
>> #include <sys/wait.h>
>> +#include <stdio.h>
>> +#include <string.h>
>> +#include <sys/stat.h>
>> #include "qga/guest-agent-core.h"
>> #include "qga-qmp-commands.h"
>> #include "qerror.h"
>> @@ -122,9 +125,117 @@ static GuestFileHandle *guest_file_handle_find(int64_t id)
>> 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_set(err, QERR_UNSUPPORTED);
>> + 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_set(&local_err, QERR_OPEN_FILE_FAILED, path);
>> + } else {
>> + qemu_set_cloexec(fd);
>> +
>> + if ((oflag & O_CREAT) && fchmod(fd, DEFAULT_NEW_FILE_MODE) == -1) {
>> + error_set(&local_err, QERR_IO_ERROR);
>> + } else {
>> + FILE *f;
>> +
>> + f = fdopen(fd, mode);
>> + if (f == NULL) {
>> + error_set(&local_err, QERR_IO_ERROR);
>> + } 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;
>>
>> @@ -132,9 +243,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_set(err, QERR_OPEN_FILE_FAILED, path);
>> + fh = safe_open_or_create(path, mode, &local_err);
>> + if (local_err != NULL) {
>> + error_propagate(err, local_err);
>> return -1;
>> }
>>
>>
>
> Looks good to me.
Forgot this one (not sure it was needed):
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
>
> Do you plan to backport
>
> 8fe6bbc qga: distinguish binary modes in "guest_file_open_modes" map
> 2b72001 qga: unlink just created guest-file if fchmod() or fdopen()
> fails on it
>
> too? These are considered polish for the CVE fix.
>
> Also, a side-note: existing world-writable log files etc. are not
> recreated nor have their modes changed, so maybe a release note or some
> such would be useful for admins ("delete your previous logfile &
> optional unix domain socket, or change their modes manually").
next prev parent reply other threads:[~2013-05-27 0:10 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-05-26 13:34 [Qemu-devel] [PATCH stable-1.1] qga: set umask 0077 when daemonizing (CVE-2013-2007) Andreas Färber
2013-05-27 0:11 ` Laszlo Ersek
2013-05-27 0:12 ` Laszlo Ersek [this message]
2013-05-27 0:19 ` Andreas Färber
2013-05-27 0:28 ` Laszlo Ersek
2013-05-27 18:33 ` Laszlo Ersek
2013-05-31 18:48 ` Anthony Liguori
2013-06-04 13:59 ` Andreas Färber
2013-06-04 14:23 ` Anthony Liguori
2013-06-05 8:33 ` [Qemu-devel] [Qemu-stable] " Michael Tokarev
2013-06-05 12:43 ` Anthony Liguori
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=51A2A4F3.5010006@redhat.com \
--to=lersek@redhat.com \
--cc=afaerber@suse.de \
--cc=aliguori@us.ibm.com \
--cc=qemu-devel@nongnu.org \
--cc=qemu-stable@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).