1
0
mirror of https://gitlab.com/libvirt/libvirt.git synced 2025-09-20 05:44:53 +03:00

Compare commits

...

10 Commits

Author SHA1 Message Date
Jiri Denemark
b869aab711 qemu: Let empty default VNC password work as documented
CVE-2016-5008

Setting an empty graphics password is documented as a way to disable
VNC/SPICE access, but QEMU does not always behaves like that. VNC would
happily accept the empty password. Let's enforce the behavior by setting
password expiration to "now".

https://bugzilla.redhat.com/show_bug.cgi?id=1180092

Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
(cherry picked from commit bb848feec0)
2016-06-30 12:51:38 +01:00
Eric Blake
3e6b40e5aa CVE-2015-5313: storage: don't allow '/' in filesystem volume names
The libvirt file system storage driver determines what file to
act on by concatenating the pool location with the volume name.
If a user is able to pick names like "../../../etc/passwd", then
they can escape the bounds of the pool.  For that matter,
virStoragePoolListVolumes() doesn't descend into subdirectories,
so a user really shouldn't use a name with a slash.

Normally, only privileged users can coerce libvirt into creating
or opening existing files using the virStorageVol APIs; and such
users already have full privilege to create any domain XML (so it
is not an escalation of privilege).  But in the case of
fine-grained ACLs, it is feasible that a user can be granted
storage_vol:create but not domain:write, and it violates
assumptions if such a user can abuse libvirt to access files
outside of the storage pool.

Therefore, prevent all use of volume names that contain "/",
whether or not such a name is actually attempting to escape the
pool.

This changes things from:

$ virsh vol-create-as default ../../../../../../etc/haha --capacity 128
Vol ../../../../../../etc/haha created
$ rm /etc/haha

to:

$ virsh vol-create-as default ../../../../../../etc/haha --capacity 128
error: Failed to create vol ../../../../../../etc/haha
error: Requested operation is not valid: volume name '../../../../../../etc/haha' cannot contain '/'

Signed-off-by: Eric Blake <eblake@redhat.com>
(cherry picked from commit 034e47c338)
2015-12-12 21:33:49 -07:00
Michal Privoznik
5837bb50ca remoteClientCloseFunc: Don't mangle connection object refcount
Well, in 8ad126e6 we tried to fix a memory corruption problem.
However, the fix was not as good as it could be. I mean, the
commit has one line more than it should. I've noticed this output
just recently:

  # ./run valgrind --leak-check=full --show-reachable=yes ./tools/virsh domblklist gentoo
  ==17019== Memcheck, a memory error detector
  ==17019== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
  ==17019== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
  ==17019== Command: /home/zippy/work/libvirt/libvirt.git/tools/.libs/virsh domblklist gentoo
  ==17019==
  Target     Source
  ------------------------------------------------
  fda        /var/lib/libvirt/images/fd.img
  vda        /var/lib/libvirt/images/gentoo.qcow2
  hdc        /home/zippy/tmp/install-amd64-minimal-20150402.iso

  ==17019== Thread 2:
  ==17019== Invalid read of size 4
  ==17019==    at 0x4EFF5B4: virObjectUnref (virobject.c:258)
  ==17019==    by 0x5038CFF: remoteClientCloseFunc (remote_driver.c:552)
  ==17019==    by 0x5069D57: virNetClientCloseLocked (virnetclient.c:685)
  ==17019==    by 0x506C848: virNetClientIncomingEvent (virnetclient.c:1852)
  ==17019==    by 0x5082136: virNetSocketEventHandle (virnetsocket.c:1913)
  ==17019==    by 0x4ECD64E: virEventPollDispatchHandles (vireventpoll.c:509)
  ==17019==    by 0x4ECDE02: virEventPollRunOnce (vireventpoll.c:658)
  ==17019==    by 0x4ECBF00: virEventRunDefaultImpl (virevent.c:308)
  ==17019==    by 0x130386: vshEventLoop (vsh.c:1864)
  ==17019==    by 0x4F1EB07: virThreadHelper (virthread.c:206)
  ==17019==    by 0xA8462D3: start_thread (in /lib64/libpthread-2.20.so)
  ==17019==    by 0xAB441FC: clone (in /lib64/libc-2.20.so)
  ==17019==  Address 0x139023f4 is 4 bytes inside a block of size 240 free'd
  ==17019==    at 0x4C2B1F0: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
  ==17019==    by 0x4EA8949: virFree (viralloc.c:582)
  ==17019==    by 0x4EFF6D0: virObjectUnref (virobject.c:273)
  ==17019==    by 0x4FE74D6: virConnectClose (libvirt.c:1390)
  ==17019==    by 0x13342A: virshDeinit (virsh.c:406)
  ==17019==    by 0x134A37: main (virsh.c:950)

The problem is, when registering remoteClientCloseFunc(), it's
conn->closeCallback which is ref'd. But in the function itself
it's conn->closeCallback->conn what is unref'd. This is causing
imbalance in reference counting. Moreover, there's no need for
the remote driver to increase/decrease conn refcount since it's
not used anywhere. It's just merely passed to client registered
callback. And for that purpose it's correctly ref'd in
virConnectRegisterCloseCallback() and then unref'd in
virConnectUnregisterCloseCallback().

Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
(cherry picked from commit e689300770)
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
2015-09-03 17:46:12 +02:00
John Ferlan
9e48400f46 storage: Correct the 'mode' check
Commit id '7c2d65dde2' changed the default value of mode to be -1 if not
supplied in the XML, which should cause creation of the volume using the
default mode of VIR_STORAGE_DEFAULT_VOL_PERM_MODE; however, the check
made was whether mode was '0' or not to use default or provided value.

This patch fixes the issue to check if the 'mode' was provided in the XML
and use that value.

(cherry picked from commit 691dd388ae)
2015-09-02 18:39:54 -04:00
John Ferlan
2f4b41861c storage: Handle failure from refreshVol
Commit id '155ca616' added the 'refreshVol' API. In an NFS root-squash
environment it was possible that if the just created volume from XML wasn't
properly created with the right uid/gid and/or mode, then the followup
refreshVol will fail to open the volume in order to get the allocation/
capacity values. This would leave the volume still on the server and
cause a libvirtd crash because 'voldef' would be in the pool list, but
the cleanup code would free it.

(cherry picked from commit db9277a39b)
2015-09-02 18:39:43 -04:00
John Ferlan
7f0505705c virfile: Introduce virFileUnlink
In an NFS root-squashed environment the 'vol-delete' command will fail to
'unlink' the target volume since it was created under a different uid:gid.

This code continues the concepts introduced in virFileOpenForked and
virDirCreate[NoFork] with respect to running the unlink command under
the uid/gid of the child. Unlike the other two, don't retry on EACCES
(that's why we're here doing this now).

(cherry picked from commit 35847860f6)
2015-09-02 18:39:36 -04:00
Jim Fehlig
bcad244cbc Revert "LXC: show used memory as 0 when domain is not active"
This reverts commit 1ce7c1d20c,
which introduced a significant semantic change to the
virDomainGetInfo() API. Additionally, the change was only
made to 2 of the 15 virt drivers.

Conflicts:
	src/qemu/qemu_driver.c

Signed-off-by: Jim Fehlig <jfehlig@suse.com>
(cherry picked from commit 60acb38abb)
2015-08-28 10:43:03 -06:00
Christophe Fergeau
9602dff96c storage: fs: Fix pool building when directory already exists
Currently, when trying to virsh pool-define/virsh pool-build a new
'dir' pool, if the target directory already exists, virsh
pool-build/virStoragePoolBuild will error out. This is a change of
behaviour compared to eg libvirt 1.2.13

This is caused by the wrong type being used for the dir_create_flags
variable in virStorageBackendFileSystemBuild , it's defined as a bool
but is used as a flag bit field so should be unsigned int (this matches
the type virDirCreate expects for this variable).

This should fix https://bugzilla.gnome.org/show_bug.cgi?id=752417 (GNOME
Boxes) and https://bugzilla.redhat.com/show_bug.cgi?id=1244080
(downstream virt-manager).
2015-07-17 15:29:20 +02:00
Michal Privoznik
85ee500a9e lxc: Don't pass a local variable address randomly
So, recently I was testing the LXC driver. You know, startup some
domains. But to my surprise, I was not able to start a single one:

  virsh # start --console test
  error: Reconnected to the hypervisor
  error: Failed to start domain test
  error: internal error: guest failed to start: unexpected exit status 125

So I've start digging. It turns out, that in virExec(), when I printed
out the @cmd, I got strange values: *(cmd->outfdptr) was certainly not
valid FD number: it has random value of several millions. This
obviously made prepareStdFd(childout, STDOUT_FILENO) fail (line 611).
But outfdptr is set in virCommandSetOutputFD(). The only place within
LXC driver where the function is called is in
virLXCProcessBuildControllerCmd(). If you take a closer look at the
function it looks like this:

static virCommandPtr
virLXCProcessBuildControllerCmd(virLXCDriverPtr driver,
                                ..
                                int logfd,
                                const char *pidfile)
{
    ...
    virCommandSetOutputFD(cmd, &logfd);
    virCommandSetErrorFD(cmd, &logfd);
    ...
}

Yes, you guessed it. @logfd is passed into the function by value.
However, in the function we try to get its address (an address of a
local variable) which is no longer valid once function is finished and
stack is cleaned. Therefore when cmd->outfdptr is evaluated at any
point after this function, we may get a random number, depending on
what's currently on the stack. Of course, this may work sometimes too
- it depends on the compiler how it arranges the code, when the stack
is wiped out.

In order to fix this, lets pass a pointer to @logfd instead of
figuring out (wrong) its value in a function.

The bug was introduced in e1de5521.

Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
(cherry picked from commit 302146b16d)
2015-07-02 08:40:05 +02:00
Eric W. Biederman
a99f087128 lxc: set nosuid+nodev+noexec flags on /proc/sys mount
Future kernels will mandate the use of nosuid+nodev+noexec
flags when mounting the /proc/sys filesystem. Unconditionally
add them now since they don't harm things regardless and could
mitigate future security attacks.

(cherry picked from commit 24710414d4)
2015-06-16 17:11:42 +01:00
12 changed files with 146 additions and 26 deletions

View File

@@ -1439,6 +1439,7 @@ virFileSanitizePath;
virFileSkipRoot;
virFileStripSuffix;
virFileTouch;
virFileUnlink;
virFileUnlock;
virFileUpdatePerm;
virFileWaitForDevices;

View File

@@ -850,7 +850,7 @@ typedef struct {
static const virLXCBasicMountInfo lxcBasicMounts[] = {
{ "proc", "/proc", "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, false, false, false },
{ "/proc/sys", "/proc/sys", NULL, MS_BIND|MS_RDONLY, false, false, false },
{ "/proc/sys", "/proc/sys", NULL, MS_BIND|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, false, false, false },
{ "/.oldroot/proc/sys/net/ipv4", "/proc/sys/net/ipv4", NULL, MS_BIND, false, false, true },
{ "/.oldroot/proc/sys/net/ipv6", "/proc/sys/net/ipv6", NULL, MS_BIND, false, false, true },
{ "sysfs", "/sys", "sysfs", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, false, false, false },
@@ -1030,7 +1030,7 @@ static int lxcContainerMountBasicFS(bool userns_enabled,
if (bindOverReadonly &&
mount(mnt_src, mnt->dst, NULL,
MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
MS_BIND|MS_REMOUNT|mnt_mflags|MS_RDONLY, NULL) < 0) {
virReportSystemError(errno,
_("Failed to re-mount %s on %s flags=%x"),
mnt_src, mnt->dst,

View File

@@ -597,7 +597,7 @@ static int lxcDomainGetInfo(virDomainPtr dom,
if (!virDomainObjIsActive(vm)) {
info->cpuTime = 0;
info->memory = 0;
info->memory = vm->def->mem.cur_balloon;
} else {
if (virCgroupGetCpuacctUsage(priv->cgroup, &(info->cpuTime)) < 0) {
virReportError(VIR_ERR_OPERATION_FAILED,

View File

@@ -750,7 +750,7 @@ virLXCProcessBuildControllerCmd(virLXCDriverPtr driver,
int *files,
size_t nfiles,
int handshakefd,
int logfd,
int * const logfd,
const char *pidfile)
{
size_t i;
@@ -820,8 +820,8 @@ virLXCProcessBuildControllerCmd(virLXCDriverPtr driver,
virCommandPassFD(cmd, handshakefd, 0);
virCommandDaemonize(cmd);
virCommandSetPidFile(cmd, pidfile);
virCommandSetOutputFD(cmd, &logfd);
virCommandSetErrorFD(cmd, &logfd);
virCommandSetOutputFD(cmd, logfd);
virCommandSetErrorFD(cmd, logfd);
/* So we can pause before exec'ing the controller to
* write the live domain status XML with the PID */
virCommandRequireHandshake(cmd);
@@ -1208,7 +1208,7 @@ int virLXCProcessStart(virConnectPtr conn,
ttyFDs, nttyFDs,
files, nfiles,
handshakefds[1],
logfd,
&logfd,
pidfile)))
goto cleanup;

View File

@@ -2694,7 +2694,7 @@ static int qemuDomainGetInfo(virDomainPtr dom,
info->memory = vm->def->mem.cur_balloon;
}
} else {
info->memory = 0;
info->memory = vm->def->mem.cur_balloon;
}
info->nrVirtCpu = vm->def->vcpus;

View File

@@ -4013,6 +4013,7 @@ qemuDomainChangeGraphicsPasswords(virQEMUDriverPtr driver,
time_t now = time(NULL);
char expire_time [64];
const char *connected = NULL;
const char *password;
int ret = -1;
virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
@@ -4020,16 +4021,14 @@ qemuDomainChangeGraphicsPasswords(virQEMUDriverPtr driver,
ret = 0;
goto cleanup;
}
password = auth->passwd ? auth->passwd : defaultPasswd;
if (auth->connected)
connected = virDomainGraphicsAuthConnectedTypeToString(auth->connected);
if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
goto cleanup;
ret = qemuMonitorSetPassword(priv->mon,
type,
auth->passwd ? auth->passwd : defaultPasswd,
connected);
ret = qemuMonitorSetPassword(priv->mon, type, password, connected);
if (ret == -2) {
if (type != VIR_DOMAIN_GRAPHICS_TYPE_VNC) {
@@ -4037,14 +4036,15 @@ qemuDomainChangeGraphicsPasswords(virQEMUDriverPtr driver,
_("Graphics password only supported for VNC"));
ret = -1;
} else {
ret = qemuMonitorSetVNCPassword(priv->mon,
auth->passwd ? auth->passwd : defaultPasswd);
ret = qemuMonitorSetVNCPassword(priv->mon, password);
}
}
if (ret != 0)
goto end_job;
if (auth->expires) {
if (password[0] == '\0') {
snprintf(expire_time, sizeof(expire_time), "now");
} else if (auth->expires) {
time_t lifetime = auth->validTo - now;
if (lifetime <= 0)
snprintf(expire_time, sizeof(expire_time), "now");

View File

@@ -546,10 +546,6 @@ remoteClientCloseFunc(virNetClientPtr client ATTRIBUTE_UNUSED,
cbdata->freeCallback = NULL;
}
virObjectUnlock(cbdata);
/* free the connection reference that comes along with the callback
* registration */
virObjectUnref(cbdata->conn);
}
/* helper macro to ease extraction of arguments from the URI */

View File

@@ -479,6 +479,7 @@ virStorageBackendCreateRaw(virConnectPtr conn ATTRIBUTE_UNUSED,
int fd = -1;
int operation_flags;
bool reflink_copy = false;
mode_t open_mode = VIR_STORAGE_DEFAULT_VOL_PERM_MODE;
virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA |
VIR_STORAGE_VOL_CREATE_REFLINK,
@@ -511,11 +512,12 @@ virStorageBackendCreateRaw(virConnectPtr conn ATTRIBUTE_UNUSED,
if (pool->def->type == VIR_STORAGE_POOL_NETFS)
operation_flags |= VIR_FILE_OPEN_FORK;
if (vol->target.perms->mode != (mode_t) -1)
open_mode = vol->target.perms->mode;
if ((fd = virFileOpenAs(vol->target.path,
O_RDWR | O_CREAT | O_EXCL,
(vol->target.perms->mode ?
VIR_STORAGE_DEFAULT_VOL_PERM_MODE :
vol->target.perms->mode),
open_mode,
vol->target.perms->uid,
vol->target.perms->gid,
operation_flags)) < 0) {

View File

@@ -1,7 +1,7 @@
/*
* storage_backend_fs.c: storage backend for FS and directory handling
*
* Copyright (C) 2007-2014 Red Hat, Inc.
* Copyright (C) 2007-2015 Red Hat, Inc.
* Copyright (C) 2007-2008 Daniel P. Berrange
*
* This library is free software; you can redistribute it and/or
@@ -770,7 +770,8 @@ virStorageBackendFileSystemBuild(virConnectPtr conn ATTRIBUTE_UNUSED,
char *parent = NULL;
char *p = NULL;
mode_t mode;
bool needs_create_as_uid, dir_create_flags;
bool needs_create_as_uid;
unsigned int dir_create_flags;
virCheckFlags(VIR_STORAGE_POOL_BUILD_OVERWRITE |
VIR_STORAGE_POOL_BUILD_NO_OVERWRITE, ret);
@@ -1039,6 +1040,14 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn ATTRIBUTE_UNUSED,
vol->type = VIR_STORAGE_VOL_FILE;
/* Volumes within a directory pools are not recursive; do not
* allow escape to ../ or a subdir */
if (strchr(vol->name, '/')) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("volume name '%s' cannot contain '/'"), vol->name);
return -1;
}
VIR_FREE(vol->target.path);
if (virAsprintf(&vol->target.path, "%s/%s",
pool->def->target.path,
@@ -1185,7 +1194,8 @@ virStorageBackendFileSystemVolDelete(virConnectPtr conn ATTRIBUTE_UNUSED,
switch ((virStorageVolType) vol->type) {
case VIR_STORAGE_VOL_FILE:
if (unlink(vol->target.path) < 0) {
if (virFileUnlink(vol->target.path, vol->target.perms->uid,
vol->target.perms->gid) < 0) {
/* Silently ignore failures where the vol has already gone away */
if (errno != ENOENT) {
virReportSystemError(errno,

View File

@@ -1872,8 +1872,12 @@ storageVolCreateXML(virStoragePoolPtr obj,
}
if (backend->refreshVol &&
backend->refreshVol(obj->conn, pool, voldef) < 0)
backend->refreshVol(obj->conn, pool, voldef) < 0) {
storageVolDeleteInternal(volobj, backend, pool, voldef,
0, false);
voldef = NULL;
goto cleanup;
}
/* Update pool metadata ignoring the disk backend since
* it updates the pool values.

View File

@@ -2280,6 +2280,112 @@ virFileOpenAs(const char *path, int openflags, mode_t mode,
return ret;
}
/* virFileUnlink:
* @path: file to unlink
* @uid: uid that was used to create the file (not required)
* @gid: gid that was used to create the file (not required)
*
* If a file/volume was created in an NFS root-squash environment,
* then we must 'unlink' the file in the same environment. Unlike
* the virFileOpenAs[Forked] and virDirCreate[NoFork], this code
* takes no extra flags and does not bother with EACCES failures
* from the child.
*/
int
virFileUnlink(const char *path,
uid_t uid,
gid_t gid)
{
pid_t pid;
int waitret;
int status, ret = 0;
gid_t *groups;
int ngroups;
/* If not running as root or if a non explicit uid/gid was being used for
* the file/volume, then use unlink directly
*/
if ((geteuid() != 0) ||
((uid == (uid_t) -1) && (gid == (gid_t) -1)))
return unlink(path);
/* Otherwise, we have to deal with the NFS root-squash craziness
* to run under the uid/gid that created the volume in order to
* perform the unlink of the volume.
*/
if (uid == (uid_t) -1)
uid = geteuid();
if (gid == (gid_t) -1)
gid = getegid();
ngroups = virGetGroupList(uid, gid, &groups);
if (ngroups < 0)
return -errno;
pid = virFork();
if (pid < 0) {
ret = -errno;
VIR_FREE(groups);
return ret;
}
if (pid) { /* parent */
/* wait for child to complete, and retrieve its exit code */
VIR_FREE(groups);
while ((waitret = waitpid(pid, &status, 0)) == -1 && errno == EINTR);
if (waitret == -1) {
ret = -errno;
virReportSystemError(errno,
_("failed to wait for child unlinking '%s'"),
path);
goto parenterror;
}
/*
* If waitpid succeeded, but if the child exited abnormally or
* reported non-zero status, report failure
*/
if (!WIFEXITED(status) || (WEXITSTATUS(status)) != 0) {
char *msg = virProcessTranslateStatus(status);
virReportError(VIR_ERR_INTERNAL_ERROR,
_("child failed to unlink '%s': %s"),
path, msg);
VIR_FREE(msg);
if (WIFEXITED(status))
ret = -WEXITSTATUS(status);
else
ret = -EACCES;
}
parenterror:
return ret;
}
/* child */
/* set desired uid/gid, then attempt to unlink the file */
if (virSetUIDGID(uid, gid, groups, ngroups) < 0) {
ret = errno;
goto childerror;
}
if (unlink(path) < 0) {
ret = errno;
goto childerror;
}
childerror:
if ((ret & 0xff) != ret) {
VIR_WARN("unable to pass desired return value %d", ret);
ret = 0xff;
}
_exit(ret);
}
/* return -errno on failure, or 0 on success */
static int
virDirCreateNoFork(const char *path,

View File

@@ -219,6 +219,7 @@ int virFileOpenAs(const char *path, int openflags, mode_t mode,
uid_t uid, gid_t gid,
unsigned int flags)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
int virFileUnlink(const char *path, uid_t uid, gid_t gid);
enum {
VIR_DIR_CREATE_NONE = 0,