diff mbox

[stable-1.1] qga: set umask 0077 when daemonizing (CVE-2013-2007)

Message ID 1369575248-31854-1-git-send-email-afaerber@suse.de
State New
Headers show

Commit Message

Andreas Färber May 26, 2013, 1:34 p.m. UTC
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(-)

Comments

Laszlo Ersek May 27, 2013, 12:11 a.m. UTC | #1
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.

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").

Laszlo
Laszlo Ersek May 27, 2013, 12:12 a.m. UTC | #2
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").
Andreas Färber May 27, 2013, 12:19 a.m. UTC | #3
Am 27.05.2013 02:11, schrieb Laszlo Ersek:
> 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(-)
[...]
> Looks good to me.
> 
> 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.

I did backport both to openSUSE 12.2 - they apply without conflicts. :)
I mainly posted this one to check if there are better QERRs to use.

> 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").

Feel free to add a note to the 1.5 Release Notes - it can then be copied
to the previous releases we backport this fix to.

Apart from 1.1 I backported to our 1.3 branch.

Andreas
Laszlo Ersek May 27, 2013, 12:28 a.m. UTC | #4
On 05/27/13 02:19, Andreas Färber wrote:
> Am 27.05.2013 02:11, schrieb Laszlo Ersek:

>> 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.
> 
> I did backport both to openSUSE 12.2 - they apply without conflicts. :)
> I mainly posted this one to check if there are better QERRs to use.

I think your choices were apt.

>> 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").
> 
> Feel free to add a note to the 1.5 Release Notes - it can then be copied
> to the previous releases we backport this fix to.

Do you mean in the wiki? I'll need an account first :)

Thanks,
Laszlo
Laszlo Ersek May 27, 2013, 6:33 p.m. UTC | #5
On 05/27/13 02:19, Andreas Färber wrote:
> Am 27.05.2013 02:11, schrieb Laszlo Ersek:

>> 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").
> 
> Feel free to add a note to the 1.5 Release Notes - it can then be copied
> to the previous releases we backport this fix to.

Done: http://wiki.qemu.org/ChangeLog/1.5#Guest_agent

If too verbose or not precise enough, please go ahead and edit it.

Thanks,
Laszlo
Anthony Liguori May 31, 2013, 6:48 p.m. UTC | #6
Applied.  Thanks.

Regards,

Anthony Liguori
Andreas Färber June 4, 2013, 1:59 p.m. UTC | #7
Anthony,

Am 31.05.2013 20:48, schrieb Anthony Liguori:
> Applied.  Thanks.

I don't see this applied, neither in qemu-stable-1.1.git nor in qemu.git
stable-1.1 branch?

Do we have an active maintainer for 1.1 or 1.3? Unfortunately qemu.git
MAINTAINERS has not been updated with stable branches and maintainers
for some time. If not, I'd be willing to step up for security fixes at
least.

Regards,
Andreas
Anthony Liguori June 4, 2013, 2:23 p.m. UTC | #8
Andreas Färber <afaerber@suse.de> writes:

> Anthony,
>
> Am 31.05.2013 20:48, schrieb Anthony Liguori:
>> Applied.  Thanks.
>
> I don't see this applied, neither in qemu-stable-1.1.git nor in qemu.git
> stable-1.1 branch?

Right, 1.1 is EOL.

> Do we have an active maintainer for 1.1 or 1.3? Unfortunately qemu.git
> MAINTAINERS has not been updated with stable branches and maintainers
> for some time. If not, I'd be willing to step up for security fixes at
> least.

Can you setup a tree and do a pull request, also with a patch to set
yourself up as maintainer?

Thanks!

Regards,

Anthony Liguori



>
> Regards,
> Andreas
>
> -- 
> SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
> GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg
Michael Tokarev June 5, 2013, 8:33 a.m. UTC | #9
04.06.2013 18:23, Anthony Liguori wrote:
> Andreas Färber <afaerber@suse.de> writes:
[]
>> Do we have an active maintainer for 1.1 or 1.3? Unfortunately qemu.git
>> MAINTAINERS has not been updated with stable branches and maintainers
>> for some time. If not, I'd be willing to step up for security fixes at
>> least.

I tried maintaining 1.1 (and I continue maintaining it in debian, but there
i'm not really interested in unrelated things), but after multiple promises
to get the tree published @qemu.org I gave up.

/mjt

> Can you setup a tree and do a pull request, also with a patch to set
> yourself up as maintainer?
> 
> Thanks!
> 
> Regards,
> 
> Anthony Liguori
> 
> 
> 
>>
>> Regards,
>> Andreas
>>
>> -- 
>> SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
>> GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg
> 
>
Anthony Liguori June 5, 2013, 12:43 p.m. UTC | #10
Michael Tokarev <mjt@tls.msk.ru> writes:

> 04.06.2013 18:23, Anthony Liguori wrote:
>> Andreas Färber <afaerber@suse.de> writes:
> []
>>> Do we have an active maintainer for 1.1 or 1.3? Unfortunately qemu.git
>>> MAINTAINERS has not been updated with stable branches and maintainers
>>> for some time. If not, I'd be willing to step up for security fixes at
>>> least.
>
> I tried maintaining 1.1 (and I continue maintaining it in debian, but there
> i'm not really interested in unrelated things), but after multiple promises
> to get the tree published @qemu.org I gave up.

After a lot of thought, I don't want to do releases from previous stable
trees.

However, if someone wants to maintain a tree, I'm willing to accept pull
requests for it.  In fact, I'd like to phase out the separate stable
trees on qemu.org entirely and just move to a pull request based model.

Regards,

Anthony Liguori

>
> /mjt
>
>> Can you setup a tree and do a pull request, also with a patch to set
>> yourself up as maintainer?
>> 
>> Thanks!
>> 
>> Regards,
>> 
>> Anthony Liguori
>> 
>> 
>> 
>>>
>>> Regards,
>>> Andreas
>>>
>>> -- 
>>> SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
>>> GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg
>> 
>>
diff mbox

Patch

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;
     }