Discussion:
[libvirt] [PATCH 00/18] Implement original label remembering
Michal Privoznik
2018-11-23 08:43:18 UTC
Permalink
Dear list,

there were several attempts in the past to implement this feature, but
none of them was successful. The problem is that we change security
labels when starting a domain but never record the original labels
therefore when restoring the labels back in domain shutdown phase we
have to go with root:root or restorecon. This is not user friendly.

Now that we have metadata locking implemented we have exclusive access
to the files we are touching and therefore can call functions to record
the original owner. Since this database needs to be distributed
(consider multiple daemons and an network file system) it can't be
stored inside a daemon (libvirtd knows nothing about other daemons
running on distant hosts). Therefore the next option is to store it with
the files themselves - in XATTRs.

There is one caveat though. A file can be passed to multiple domains at
the same time (for instance an installation ISO), therefore we need a
reference counter so that the only the last label restore call actually
restores the original owner. A picture is worth more than a thousand
words:

# chown 5:6 /var/lib/libvirt/images/fd.img

# ls -ln /var/lib/libvirt/images/fd.img
-rw-r--r-- 1 5 6 2097152 Mar 17 2018 /var/lib/libvirt/images/fd.img

# getfattr -d -m - /var/lib/libvirt/images/fd.img
(no output)

# virsh domblklist fedora
Target Source
------------------------------------------------
sda /var/lib/libvirt/images/fedora.qcow2
sdb /var/lib/libvirt/images/fd.img

# virsh domblklist gentoo
Target Source
----------------------------------------------------------------------
fda /var/lib/libvirt/images/fd.img
sda /var/lib/libvirt/images/gentoo.qcow2

# virsh start fedora
Domain fedora started

# getfattr -d -m - /var/lib/libvirt/images/fd.img
trusted.libvirt.security.dac="+5:+6"
trusted.libvirt.security.ref_dac="1"

# virsh start gentoo
Domain gentoo started

# getfattr -d -m - /var/lib/libvirt/images/fd.img
trusted.libvirt.security.dac="+5:+6"
trusted.libvirt.security.ref_dac="2"

# virsh shutdown --domain fedora
Domain fedora is being shutdown

# ls -ln /var/lib/libvirt/images/fd.img
-rw-r--r-- 1 0 0 2097152 Mar 17 2018 /var/lib/libvirt/images/fd.img

# getfattr -d -m - /var/lib/libvirt/images/fd.img
trusted.libvirt.security.dac="+5:+6"
trusted.libvirt.security.ref_dac="1"

# virsh shutdown --domain gentoo
Domain gentoo is being shutdown

# getfattr -d -m - /var/lib/libvirt/images/fd.img
(no output)

# ls -ln /var/lib/libvirt/images/fd.img
-rw-r--r-- 1 5 6 2097152 Mar 17 2018 /var/lib/libvirt/images/fd.img


Even though I'm showing DAC only in my example, it's the same story with
SELinux.

Of course, this plays nicely with filesystems that don't support XATTRs,
which there are not that much, but unfortunately NFS is one of them :(


Michal Prívozník (18):
security: Unify header conditionals
util: Introduce xattr getter/setter/remover
security: Include security_util
security_dac: Restore label on failed chown() attempt
virSecurityDACTransactionRun: Implement rollback
virSecurityDACRestoreAllLabel: Reorder device relabeling
virSecurityDACRestoreAllLabel: Restore more labels
security_dac: Allow callers to enable/disable label remembering/recall
security_dac: Remember old labels
virSecurityDACRestoreImageLabelInt: Restore even shared/RO disks
security_selinux: Track if transaction is restore
security_selinux: Remember old labels
security_selinux: Restore label on failed setfilecon() attempt
virSecuritySELinuxTransactionRun: Implement rollback
virSecuritySELinuxRestoreAllLabel: Reorder device relabeling
virSecuritySELinuxRestoreAllLabel: Restore more labels
tools: Provide a script to recover fubar'ed XATTRs setup
qemu.conf: Allow users to enable/disable label remembering

src/libvirt_private.syms | 3 +
src/qemu/libvirtd_qemu.aug | 1 +
src/qemu/qemu.conf | 6 +
src/qemu/qemu_conf.c | 4 +
src/qemu/test_libvirtd_qemu.aug.in | 1 +
src/security/Makefile.inc.am | 2 +
src/security/security_apparmor.h | 6 +-
src/security/security_dac.c | 212 +++++++++++++++++-------
src/security/security_dac.h | 6 +-
src/security/security_driver.h | 6 +-
src/security/security_manager.h | 6 +-
src/security/security_nop.h | 6 +-
src/security/security_selinux.c | 256 +++++++++++++++++++++--------
src/security/security_selinux.h | 6 +-
src/security/security_stack.h | 6 +-
src/security/security_util.c | 198 ++++++++++++++++++++++
src/security/security_util.h | 32 ++++
src/util/virfile.c | 121 ++++++++++++++
src/util/virfile.h | 11 ++
tools/Makefile.am | 1 +
tools/libvirt_recover_xattrs.sh | 89 ++++++++++
21 files changed, 829 insertions(+), 150 deletions(-)
create mode 100644 src/security/security_util.c
create mode 100644 src/security/security_util.h
create mode 100755 tools/libvirt_recover_xattrs.sh
--
2.18.1
Michal Privoznik
2018-11-23 08:43:19 UTC
Permalink
To avoid including a header file twice the following pattern is
used:

#ifndef __SOMETHING__
# define __SOMETHING__

where __SOMETHING__ should correspond to the header file name.
However, some of our header files break that pattern.

Signed-off-by: Michal Privoznik <***@redhat.com>
---
src/security/security_apparmor.h | 6 +++---
src/security/security_dac.h | 6 +++---
src/security/security_driver.h | 6 +++---
src/security/security_manager.h | 6 +++---
src/security/security_nop.h | 6 +++---
src/security/security_selinux.h | 6 +++---
src/security/security_stack.h | 6 +++---
7 files changed, 21 insertions(+), 21 deletions(-)

diff --git a/src/security/security_apparmor.h b/src/security/security_apparmor.h
index 7872588f64..6b454d1b5c 100644
--- a/src/security/security_apparmor.h
+++ b/src/security/security_apparmor.h
@@ -19,8 +19,8 @@
* Jamie Strandboge <***@canonical.com>
*
*/
-#ifndef __VIR_SECURITY_APPARMOR_H__
-# define __VIR_SECURITY_APPARMOR_H__
+#ifndef __SECURITY_APPARMOR_H__
+# define __SECURITY_APPARMOR_H__

# include "security_driver.h"

@@ -30,4 +30,4 @@ extern virSecurityDriver virAppArmorSecurityDriver;
# define PROFILE_NAME_SIZE 8 + VIR_UUID_STRING_BUFLEN /* AA_PREFIX + uuid */
# define MAX_FILE_LEN (1024*1024*10) /* 10MB limit for sanity check */

-#endif /* __VIR_SECURITY_APPARMOR_H__ */
+#endif /* __SECURITY_APPARMOR_H__ */
diff --git a/src/security/security_dac.h b/src/security/security_dac.h
index 97681c9610..8007bde000 100644
--- a/src/security/security_dac.h
+++ b/src/security/security_dac.h
@@ -20,8 +20,8 @@

#include "security_driver.h"

-#ifndef __VIR_SECURITY_DAC
-# define __VIR_SECURITY_DAC
+#ifndef __SECURITY_DAC__
+# define __SECURITY_DAC__

extern virSecurityDriver virSecurityDriverDAC;

@@ -38,4 +38,4 @@ void virSecurityDACSetMountNamespace(virSecurityManagerPtr mgr,
void virSecurityDACSetChownCallback(virSecurityManagerPtr mgr,
virSecurityManagerDACChownCallback chownCallback);

-#endif /* __VIR_SECURITY_DAC */
+#endif /* __SECURITY_DAC__ */
diff --git a/src/security/security_driver.h b/src/security/security_driver.h
index cd221f1c78..25d49bb0f4 100644
--- a/src/security/security_driver.h
+++ b/src/security/security_driver.h
@@ -19,8 +19,8 @@
* James Morris <***@namei.org>
*
*/
-#ifndef __VIR_SECURITY_H__
-# define __VIR_SECURITY_H__
+#ifndef __SECURITY_DRIVER_H__
+# define __SECURITY_DRIVER_H__

# include "internal.h"
# include "domain_conf.h"
@@ -226,4 +226,4 @@ struct _virSecurityDriver {
virSecurityDriverPtr virSecurityDriverLookup(const char *name,
const char *virtDriver);

-#endif /* __VIR_SECURITY_H__ */
+#endif /* __SECURITY_DRIVER_H__ */
diff --git a/src/security/security_manager.h b/src/security/security_manager.h
index 7e82304689..139b70ec10 100644
--- a/src/security/security_manager.h
+++ b/src/security/security_manager.h
@@ -20,8 +20,8 @@
* Author: Daniel P. Berrange <***@redhat.com>
*/

-#ifndef VIR_SECURITY_MANAGER_H__
-# define VIR_SECURITY_MANAGER_H__
+#ifndef __SECURITY_MANAGER_H__
+# define __SECURITY_MANAGER_H__

# include "domain_conf.h"
# include "vircommand.h"
@@ -210,4 +210,4 @@ void
virSecurityManagerMetadataUnlock(virSecurityManagerPtr mgr,
virSecurityManagerMetadataLockStatePtr *state);

-#endif /* VIR_SECURITY_MANAGER_H__ */
+#endif /* __SECURITY_MANAGER_H__ */
diff --git a/src/security/security_nop.h b/src/security/security_nop.h
index 514b339467..7b2ded2292 100644
--- a/src/security/security_nop.h
+++ b/src/security/security_nop.h
@@ -17,11 +17,11 @@
*
*/

-#ifndef __VIR_SECURITY_NOP_H__
-# define __VIR_SECURITY_NOP_H__
+#ifndef __SECURITY_NOP_H__
+# define __SECURITY_NOP_H__

# include "security_driver.h"

extern virSecurityDriver virSecurityDriverNop;

-#endif /* __VIR_SECURITY_NOP_H__ */
+#endif /* __SECURITY_NOP_H__ */
diff --git a/src/security/security_selinux.h b/src/security/security_selinux.h
index 1700d8c661..11b62acb52 100644
--- a/src/security/security_selinux.h
+++ b/src/security/security_selinux.h
@@ -19,9 +19,9 @@
* James Morris <***@namei.org>
*
*/
-#ifndef __VIR_SECURITY_SELINUX_H__
-# define __VIR_SECURITY_SELINUX_H__
+#ifndef __SECURITY_SELINUX_H__
+# define __SECURITY_SELINUX_H__

extern virSecurityDriver virSecurityDriverSELinux;

-#endif /* __VIR_SECURITY_SELINUX_H__ */
+#endif /* __SECURITY_SELINUX_H__ */
diff --git a/src/security/security_stack.h b/src/security/security_stack.h
index b38f9a9481..7e6ab3d93e 100644
--- a/src/security/security_stack.h
+++ b/src/security/security_stack.h
@@ -20,8 +20,8 @@

#include "security_driver.h"

-#ifndef __VIR_SECURITY_STACK
-# define __VIR_SECURITY_STACK
+#ifndef __SECURITY_STACK__
+# define __SECURITY_STACK__

extern virSecurityDriver virSecurityDriverStack;

@@ -35,4 +35,4 @@ virSecurityStackGetPrimary(virSecurityManagerPtr mgr);
virSecurityManagerPtr*
virSecurityStackGetNested(virSecurityManagerPtr mgr);

-#endif /* __VIR_SECURITY_STACK */
+#endif /* __SECURITY_STACK__ */
--
2.18.1
Michal Privoznik
2018-11-23 08:43:20 UTC
Permalink
Signed-off-by: Michal Privoznik <***@redhat.com>
---
src/libvirt_private.syms | 3 +
src/util/virfile.c | 121 +++++++++++++++++++++++++++++++++++++++
src/util/virfile.h | 11 ++++
3 files changed, 135 insertions(+)

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 8889aaa379..85580beb58 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1827,6 +1827,7 @@ virFileGetACLs;
virFileGetHugepageSize;
virFileGetMountReverseSubtree;
virFileGetMountSubtree;
+virFileGetXAtrr;
virFileHasSuffix;
virFileInData;
virFileIsAbsPath;
@@ -1866,6 +1867,7 @@ virFileReadValueUint;
virFileRelLinkPointsTo;
virFileRemove;
virFileRemoveLastComponent;
+virFileRemoveXAttr;
virFileResolveAllLinks;
virFileResolveLink;
virFileRewrite;
@@ -1873,6 +1875,7 @@ virFileRewriteStr;
virFileSanitizePath;
virFileSetACLs;
virFileSetupDev;
+virFileSetXAtrr;
virFileSkipRoot;
virFileStripSuffix;
virFileTouch;
diff --git a/src/util/virfile.c b/src/util/virfile.c
index f6f9e4ceda..9df5f70c60 100644
--- a/src/util/virfile.c
+++ b/src/util/virfile.c
@@ -64,6 +64,10 @@
# include <linux/cdrom.h>
#endif

+#if HAVE_LIBATTR
+# include <sys/xattr.h>
+#endif
+
#include "configmake.h"
#include "intprops.h"
#include "vircommand.h"
@@ -4354,3 +4358,120 @@ virFileWaitForExists(const char *path,

return 0;
}
+
+
+#if HAVE_LIBATTR
+/**
+ * virFileGetXAtrr;
+ * @path: a filename
+ * @name: name of xattr
+ * @value: read value
+ *
+ * Reads xattr with @name for given @path and stores it into
+ * @value. Caller is responsible for freeing @value.
+ *
+ * Returns: 0 on success,
+ * -1 otherwise (with errno set).
+ */
+int
+virFileGetXAtrr(const char *path,
+ const char *name,
+ char **value)
+{
+ char *buf = NULL;
+ int ret = -1;
+
+ /* We might be racing with somebody who sets the same attribute. */
+ do {
+ ssize_t need;
+ ssize_t got;
+
+ /* The first call determines how many bytes we need to allocate. */
+ if ((need = getxattr(path, name, NULL, 0)) < 0)
+ goto cleanup;
+
+ if (VIR_REALLOC_N_QUIET(buf, need + 1) < 0)
+ goto cleanup;
+
+ if ((got = getxattr(path, name, buf, need)) < 0) {
+ if (errno == ERANGE)
+ continue;
+ goto cleanup;
+ }
+
+ buf[got] = '\0';
+ break;
+ } while (1);
+
+ VIR_STEAL_PTR(*value, buf);
+ ret = 0;
+ cleanup:
+ VIR_FREE(buf);
+ return ret;
+}
+
+/**
+ * virFileSetXAtrr:
+ * @path: a filename
+ * @name: name of xattr
+ * @value: value to set
+ *
+ * Sets xattr of @name and @value on @path.
+ *
+ * Returns: 0 on success,
+ * -1 otherwise (with errno set).
+ */
+int
+virFileSetXAtrr(const char *path,
+ const char *name,
+ const char *value)
+{
+ return setxattr(path, name, value, strlen(value), 0);
+}
+
+/**
+ * virFileRemoveXAttr:
+ * @path: a filename
+ * @name: name of xattr
+ *
+ * Remove xattr of @name on @path.
+ *
+ * Returns: 0 on success,
+ * -1 otherwise (with errno set).
+ */
+int
+virFileRemoveXAttr(const char *path,
+ const char *name)
+{
+ return removexattr(path, name);
+}
+
+#else /* !HAVE_LIBATTR */
+
+int
+virFileGetXAtrr(const char *path ATTRIBUTE_UNUSED,
+ const char *name ATTRIBUTE_UNUSED,
+ char **value ATTRIBUTE_UNUSED)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int
+virFileSetXAtrr(const char *path ATTRIBUTE_UNUSED,
+ const char *name ATTRIBUTE_UNUSED,
+ const char *value ATTRIBUTE_UNUSED)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int
+virFileRemoveXAttr(const char *path ATTRIBUTE_UNUSED,
+ const char *name ATTRIBUTE_UNUSED)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+#endif /* HAVE_LIBATTR */
diff --git a/src/util/virfile.h b/src/util/virfile.h
index 0f7dece958..9cd1bc3a5f 100644
--- a/src/util/virfile.h
+++ b/src/util/virfile.h
@@ -383,4 +383,15 @@ int virFileInData(int fd,

VIR_DEFINE_AUTOPTR_FUNC(virFileWrapperFd, virFileWrapperFdFree)

+int virFileGetXAtrr(const char *path,
+ const char *name,
+ char **value);
+
+int virFileSetXAtrr(const char *path,
+ const char *name,
+ const char *value);
+
+int virFileRemoveXAttr(const char *path,
+ const char *name);
+
#endif /* __VIR_FILE_H */
--
2.18.1
Michal Privoznik
2018-11-23 08:43:22 UTC
Permalink
It's important to keep XATTRs untouched (well, in the same state
they were in when entering the function). Otherwise our
refcounting would be messed up.

Signed-off-by: Michal Privoznik <***@redhat.com>
---
src/security/security_dac.c | 20 +++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/src/security/security_dac.c b/src/security/security_dac.c
index 6b64d2c07a..8155c6d58a 100644
--- a/src/security/security_dac.c
+++ b/src/security/security_dac.c
@@ -718,7 +718,25 @@ virSecurityDACSetOwnership(virSecurityManagerPtr mgr,
VIR_INFO("Setting DAC user and group on '%s' to '%ld:%ld'",
NULLSTR(src ? src->path : path), (long)uid, (long)gid);

- return virSecurityDACSetOwnershipInternal(priv, src, path, uid, gid);
+ if (virSecurityDACSetOwnershipInternal(priv, src, path, uid, gid) < 0) {
+ virErrorPtr origerr;
+
+ virErrorPreserveLast(&origerr);
+ /* Try to restore the label. This is done so that XATTRs
+ * are left in the same state as when the control entered
+ * this function. However, if our attempt fails, there's
+ * not much we can do. XATTRs refcounting is fubar'ed and
+ * the only option we have is warn users. */
+ if (virSecurityDACRestoreFileLabelInternal(mgr, src, path) < 0)
+ VIR_WARN("Unable to restore label on '%s'. "
+ "XATTRs might have been left in inconsistent state.",
+ NULLSTR(src ? src->path : path));
+
+ virErrorRestore(&origerr);
+ return -1;
+ }
+
+ return 0;
}
--
2.18.1
Michal Privoznik
2018-11-23 08:43:26 UTC
Permalink
Because the implementation that will be used for label
remembering/recall is not atomic we have to give callers a chance
to enable or disable it. That is, enable it if and only if
metadata locking is enabled. Otherwise the feature MUST be turned
off.

Signed-off-by: Michal Privoznik <***@redhat.com>
---
src/security/security_dac.c | 74 ++++++++++++++++++++++---------------
1 file changed, 45 insertions(+), 29 deletions(-)

diff --git a/src/security/security_dac.c b/src/security/security_dac.c
index de12a1e351..cdbe07543c 100644
--- a/src/security/security_dac.c
+++ b/src/security/security_dac.c
@@ -182,11 +182,13 @@ static int virSecurityDACSetOwnership(virSecurityManagerPtr mgr,
const virStorageSource *src,
const char *path,
uid_t uid,
- gid_t gid);
+ gid_t gid,
+ bool remember);

static int virSecurityDACRestoreFileLabelInternal(virSecurityManagerPtr mgr,
const virStorageSource *src,
- const char *path);
+ const char *path,
+ bool recall);
/**
* virSecurityDACTransactionRun:
* @pid: process pid
@@ -234,11 +236,13 @@ virSecurityDACTransactionRun(pid_t pid ATTRIBUTE_UNUSED,
item->src,
item->path,
item->uid,
- item->gid);
+ item->gid,
+ list->lock);
} else {
rv = virSecurityDACRestoreFileLabelInternal(list->manager,
item->src,
- item->path);
+ item->path,
+ list->lock);
}

if (rv < 0)
@@ -251,7 +255,8 @@ virSecurityDACTransactionRun(pid_t pid ATTRIBUTE_UNUSED,
if (!item->restore) {
virSecurityDACRestoreFileLabelInternal(list->manager,
item->src,
- item->path);
+ item->path,
+ list->lock);
} else {
VIR_WARN("Ignoring failed restore attempt on %s",
NULLSTR(item->src ? item->src->path : item->path));
@@ -699,7 +704,8 @@ virSecurityDACSetOwnership(virSecurityManagerPtr mgr,
const virStorageSource *src,
const char *path,
uid_t uid,
- gid_t gid)
+ gid_t gid,
+ bool remember)
{
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
struct stat sb;
@@ -717,7 +723,7 @@ virSecurityDACSetOwnership(virSecurityManagerPtr mgr,
else if (rc > 0)
return 0;

- if (path) {
+ if (remember && path) {
if (stat(path, &sb) < 0) {
virReportSystemError(errno, _("unable to stat: %s"), path);
return -1;
@@ -739,7 +745,7 @@ virSecurityDACSetOwnership(virSecurityManagerPtr mgr,
* this function. However, if our attempt fails, there's
* not much we can do. XATTRs refcounting is fubar'ed and
* the only option we have is warn users. */
- if (virSecurityDACRestoreFileLabelInternal(mgr, src, path) < 0)
+ if (virSecurityDACRestoreFileLabelInternal(mgr, src, path, remember) < 0)
VIR_WARN("Unable to restore label on '%s'. "
"XATTRs might have been left in inconsistent state.",
NULLSTR(src ? src->path : path));
@@ -755,7 +761,8 @@ virSecurityDACSetOwnership(virSecurityManagerPtr mgr,
static int
virSecurityDACRestoreFileLabelInternal(virSecurityManagerPtr mgr,
const virStorageSource *src,
- const char *path)
+ const char *path,
+ bool recall)
{
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
int rv;
@@ -774,7 +781,7 @@ virSecurityDACRestoreFileLabelInternal(virSecurityManagerPtr mgr,
else if (rv > 0)
return 0;

- if (path) {
+ if (recall && path) {
rv = virSecurityDACRecallLabel(priv, path, &uid, &gid);
if (rv < 0)
return -1;
@@ -793,7 +800,7 @@ static int
virSecurityDACRestoreFileLabel(virSecurityManagerPtr mgr,
const char *path)
{
- return virSecurityDACRestoreFileLabelInternal(mgr, NULL, path);
+ return virSecurityDACRestoreFileLabelInternal(mgr, NULL, path, false);
}


@@ -840,7 +847,7 @@ virSecurityDACSetImageLabelInternal(virSecurityManagerPtr mgr,
return -1;
}

- return virSecurityDACSetOwnership(mgr, src, NULL, user, group);
+ return virSecurityDACSetOwnership(mgr, src, NULL, user, group, false);
}


@@ -920,7 +927,7 @@ virSecurityDACRestoreImageLabelInt(virSecurityManagerPtr mgr,
}
}

- return virSecurityDACRestoreFileLabelInternal(mgr, src, NULL);
+ return virSecurityDACRestoreFileLabelInternal(mgr, src, NULL, false);
}


@@ -956,7 +963,7 @@ virSecurityDACSetHostdevLabelHelper(const char *file,
if (virSecurityDACGetIds(secdef, priv, &user, &group, NULL, NULL) < 0)
return -1;

- return virSecurityDACSetOwnership(mgr, NULL, file, user, group);
+ return virSecurityDACSetOwnership(mgr, NULL, file, user, group, false);
}


@@ -1332,7 +1339,7 @@ virSecurityDACSetChardevLabel(virSecurityManagerPtr mgr,
case VIR_DOMAIN_CHR_TYPE_FILE:
ret = virSecurityDACSetOwnership(mgr, NULL,
dev_source->data.file.path,
- user, group);
+ user, group, false);
break;

case VIR_DOMAIN_CHR_TYPE_PIPE:
@@ -1340,12 +1347,12 @@ virSecurityDACSetChardevLabel(virSecurityManagerPtr mgr,
virAsprintf(&out, "%s.out", dev_source->data.file.path) < 0)
goto done;
if (virFileExists(in) && virFileExists(out)) {
- if (virSecurityDACSetOwnership(mgr, NULL, in, user, group) < 0 ||
- virSecurityDACSetOwnership(mgr, NULL, out, user, group) < 0)
+ if (virSecurityDACSetOwnership(mgr, NULL, in, user, group, false) < 0 ||
+ virSecurityDACSetOwnership(mgr, NULL, out, user, group, false) < 0)
goto done;
} else if (virSecurityDACSetOwnership(mgr, NULL,
dev_source->data.file.path,
- user, group) < 0) {
+ user, group, false) < 0) {
goto done;
}
ret = 0;
@@ -1360,7 +1367,7 @@ virSecurityDACSetChardevLabel(virSecurityManagerPtr mgr,
* and passed via FD */
if (virSecurityDACSetOwnership(mgr, NULL,
dev_source->data.nix.path,
- user, group) < 0)
+ user, group, false) < 0)
goto done;
}
ret = 0;
@@ -1543,7 +1550,7 @@ virSecurityDACSetGraphicsLabel(virSecurityManagerPtr mgr,
gfx->data.spice.rendernode) {
if (virSecurityDACSetOwnership(mgr, NULL,
gfx->data.spice.rendernode,
- user, group) < 0)
+ user, group, false) < 0)
return -1;
}

@@ -1585,7 +1592,9 @@ virSecurityDACSetInputLabel(virSecurityManagerPtr mgr,
if (virSecurityDACGetIds(seclabel, priv, &user, &group, NULL, NULL) < 0)
return -1;

- ret = virSecurityDACSetOwnership(mgr, NULL, input->source.evdev, user, group);
+ ret = virSecurityDACSetOwnership(mgr, NULL,
+ input->source.evdev,
+ user, group, false);
break;

case VIR_DOMAIN_INPUT_TYPE_MOUSE:
@@ -1773,7 +1782,9 @@ virSecurityDACSetMemoryLabel(virSecurityManagerPtr mgr,
if (virSecurityDACGetIds(seclabel, priv, &user, &group, NULL, NULL) < 0)
return -1;

- ret = virSecurityDACSetOwnership(mgr, NULL, mem->nvdimmPath, user, group);
+ ret = virSecurityDACSetOwnership(mgr, NULL,
+ mem->nvdimmPath,
+ user, group, false);
break;

case VIR_DOMAIN_MEMORY_MODEL_DIMM:
@@ -1862,27 +1873,32 @@ virSecurityDACSetAllLabel(virSecurityManagerPtr mgr,

if (def->os.loader && def->os.loader->nvram &&
virSecurityDACSetOwnership(mgr, NULL,
- def->os.loader->nvram, user, group) < 0)
+ def->os.loader->nvram,
+ user, group, false) < 0)
return -1;

if (def->os.kernel &&
virSecurityDACSetOwnership(mgr, NULL,
- def->os.kernel, user, group) < 0)
+ def->os.kernel,
+ user, group, false) < 0)
return -1;

if (def->os.initrd &&
virSecurityDACSetOwnership(mgr, NULL,
- def->os.initrd, user, group) < 0)
+ def->os.initrd,
+ user, group, false) < 0)
return -1;

if (def->os.dtb &&
virSecurityDACSetOwnership(mgr, NULL,
- def->os.dtb, user, group) < 0)
+ def->os.dtb,
+ user, group, false) < 0)
return -1;

if (def->os.slic_table &&
virSecurityDACSetOwnership(mgr, NULL,
- def->os.slic_table, user, group) < 0)
+ def->os.slic_table,
+ user, group, false) < 0)
return -1;

return 0;
@@ -1904,7 +1920,7 @@ virSecurityDACSetSavedStateLabel(virSecurityManagerPtr mgr,
if (virSecurityDACGetImageIds(secdef, priv, &user, &group) < 0)
return -1;

- return virSecurityDACSetOwnership(mgr, NULL, savefile, user, group);
+ return virSecurityDACSetOwnership(mgr, NULL, savefile, user, group, false);
}


@@ -2224,7 +2240,7 @@ virSecurityDACDomainSetPathLabel(virSecurityManagerPtr mgr,
if (virSecurityDACGetIds(seclabel, priv, &user, &group, NULL, NULL) < 0)
return -1;

- return virSecurityDACSetOwnership(mgr, NULL, path, user, group);
+ return virSecurityDACSetOwnership(mgr, NULL, path, user, group, false);
}

virSecurityDriver virSecurityDriverDAC = {
--
2.18.1
Michal Privoznik
2018-11-23 08:43:21 UTC
Permalink
Signed-off-by: Michal Privoznik <***@redhat.com>
---
src/security/Makefile.inc.am | 2 +
src/security/security_util.c | 198 +++++++++++++++++++++++++++++++++++
src/security/security_util.h | 32 ++++++
3 files changed, 232 insertions(+)
create mode 100644 src/security/security_util.c
create mode 100644 src/security/security_util.h

diff --git a/src/security/Makefile.inc.am b/src/security/Makefile.inc.am
index f88b82df7b..0ade97d355 100644
--- a/src/security/Makefile.inc.am
+++ b/src/security/Makefile.inc.am
@@ -14,6 +14,8 @@ SECURITY_DRIVER_SOURCES = \
security/security_dac.c \
security/security_manager.h \
security/security_manager.c \
+ security/security_util.h \
+ security/security_util.c \
$(NULL)

SECURITY_DRIVER_SELINUX_SOURCES = \
diff --git a/src/security/security_util.c b/src/security/security_util.c
new file mode 100644
index 0000000000..4178fdff81
--- /dev/null
+++ b/src/security/security_util.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "viralloc.h"
+#include "virfile.h"
+#include "virstring.h"
+
+#include "security_util.h"
+
+#define VIR_FROM_THIS VIR_FROM_SECURITY
+
+/* There are four namespaces available (xattr(7)):
+ *
+ * user - can be modified by anybody,
+ * system - used by ACLs
+ * security - used by SELinux
+ * trusted - accessibly by CAP_SYS_ADMIN processes only
+ *
+ * Looks like the last one is way to go.
+ */
+#define XATTR_NAMESPACE "trusted"
+
+static char *
+virSecurityGetAttrName(const char *name)
+{
+ char *ret;
+ ignore_value(virAsprintf(&ret, XATTR_NAMESPACE".libvirt.security.%s", name));
+ return ret;
+}
+
+
+static char *
+virSecurityGetRefCountAttrName(const char *name)
+{
+ char *ret;
+ ignore_value(virAsprintf(&ret, XATTR_NAMESPACE".libvirt.security.ref_%s", name));
+ return ret;
+}
+
+
+/**
+ * virSecurityGetRememberedLabel:
+ * @name: security driver name
+ * @path: file name
+ * @label: label
+ *
+ * For given @path and security driver (@name) fetch remembered
+ * @label. The caller must not restore label if an error is
+ * indicated or if @label is NULL upon return.
+ *
+ * Returns: 0 on success,
+ * -1 otherwise (with error reported)
+ */
+int
+virSecurityGetRememberedLabel(const char *name,
+ const char *path,
+ char **label)
+{
+ char *ref_name = NULL;
+ char *attr_name = NULL;
+ char *value = NULL;
+ unsigned int refcount = 0;
+ int ret = -1;
+
+ *label = NULL;
+
+ if (!(ref_name = virSecurityGetRefCountAttrName(name)))
+ goto cleanup;
+
+ if (virFileGetXAtrr(path, ref_name, &value) < 0) {
+ if (errno == ENOSYS || errno == ENODATA || errno == ENOTSUP) {
+ ret = 0;
+ } else {
+ virReportSystemError(errno,
+ _("Unable to get XATTR %s on %s"),
+ ref_name,
+ path);
+ }
+ goto cleanup;
+ }
+
+ if (virStrToLong_ui(value, NULL, 10, &refcount) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("malformed refcount %s on %s"),
+ value, path);
+ goto cleanup;
+ }
+
+ VIR_FREE(value);
+
+ refcount--;
+
+ if (refcount > 0) {
+ if (virAsprintf(&value, "%u", refcount) < 0)
+ goto cleanup;
+
+ if (virFileSetXAtrr(path, ref_name, value) < 0)
+ goto cleanup;
+ } else {
+ if (virFileRemoveXAttr(path, ref_name) < 0)
+ goto cleanup;
+
+ if (!(attr_name = virSecurityGetAttrName(name)))
+ goto cleanup;
+
+ if (virFileGetXAtrr(path, attr_name, label) < 0)
+ goto cleanup;
+
+ if (virFileRemoveXAttr(path, attr_name) < 0)
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ VIR_FREE(value);
+ VIR_FREE(attr_name);
+ VIR_FREE(ref_name);
+ return ret;
+}
+
+
+int
+virSecuritySetRememberedLabel(const char *name,
+ const char *path,
+ const char *label)
+{
+ char *ref_name = NULL;
+ char *attr_name = NULL;
+ char *value = NULL;
+ unsigned int refcount = 0;
+ int ret = -1;
+
+ if (!(ref_name = virSecurityGetRefCountAttrName(name)))
+ goto cleanup;
+
+ if (virFileGetXAtrr(path, ref_name, &value) < 0) {
+ if (errno == ENOSYS || errno == ENOTSUP) {
+ ret = 0;
+ goto cleanup;
+ } else if (errno != ENODATA) {
+ virReportSystemError(errno,
+ _("Unable to get XATTR %s on %s"),
+ ref_name,
+ path);
+ goto cleanup;
+ }
+ }
+
+ if (value &&
+ virStrToLong_ui(value, NULL, 10, &refcount) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("malformed refcount %s on %s"),
+ value, path);
+ goto cleanup;
+ }
+
+ VIR_FREE(value);
+
+ refcount++;
+
+ if (refcount == 1) {
+ if (!(attr_name = virSecurityGetAttrName(name)))
+ goto cleanup;
+
+ if (virFileSetXAtrr(path, attr_name, label) < 0)
+ goto cleanup;
+ }
+
+ if (virAsprintf(&value, "%u", refcount) < 0)
+ goto cleanup;
+
+ if (virFileSetXAtrr(path, ref_name, value) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ VIR_FREE(value);
+ VIR_FREE(attr_name);
+ VIR_FREE(ref_name);
+ return ret;
+}
diff --git a/src/security/security_util.h b/src/security/security_util.h
new file mode 100644
index 0000000000..a6e67f4390
--- /dev/null
+++ b/src/security/security_util.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SECURITY_UTIL_H__
+# define __SECURITY_UTIL_H__
+
+int
+virSecurityGetRememberedLabel(const char *name,
+ const char *path,
+ char **label);
+
+int
+virSecuritySetRememberedLabel(const char *name,
+ const char *path,
+ const char *label);
+
+#endif /* __SECURITY_UTIL_H__ */
--
2.18.1
Daniel P. Berrangé
2018-11-27 15:20:02 UTC
Permalink
s/available/available on Linux/

FreeBSD only supports 'user' and 'system' namespaces
Post by Michal Privoznik
+ *
+ * user - can be modified by anybody,
+ * system - used by ACLs
+ * security - used by SELinux
+ * trusted - accessibly by CAP_SYS_ADMIN processes only
+ *
+ * Looks like the last one is way to go.
That prevents the QEMU driver using this functionality on any
non-Linux host.

The key problem we obviously face is that of the QEMU process
being able to modify the xattrs maliciously. 'trusted' namespace
solves this for Linux but unsolved for BSD/macOS.

I can only think of two alternative ways to deal with this

- Use a sidecar file. eg $FILEPATH.libvirt.json
Works ok for plain files. Troublesome for device nodes.
Would have to use a file in /var/run/libvirt/devs/$DEVNODE
perhaps ?

- Use 'user' label but add a cryptographic signature
as a further attribute. Doesn't prevent tampering
but lets us throw away the data when tempering is
detected.

Did you consider either of these, or any other possible
options ? I'm still loathe to bake in a solution that will
only work on Linux, despite 99% of our userbase being Linux.

Regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
Michal Privoznik
2018-11-28 08:35:49 UTC
Permalink
Post by Daniel P. Berrangé
s/available/available on Linux/
FreeBSD only supports 'user' and 'system' namespaces
D'oh!
Post by Daniel P. Berrangé
Post by Michal Privoznik
+ *
+ * user - can be modified by anybody,
+ * system - used by ACLs
+ * security - used by SELinux
+ * trusted - accessibly by CAP_SYS_ADMIN processes only
+ *
+ * Looks like the last one is way to go.
That prevents the QEMU driver using this functionality on any
non-Linux host.
The key problem we obviously face is that of the QEMU process
being able to modify the xattrs maliciously. 'trusted' namespace
solves this for Linux but unsolved for BSD/macOS.
I can only think of two alternative ways to deal with this
- Use a sidecar file. eg $FILEPATH.libvirt.json
Works ok for plain files. Troublesome for device nodes.
Would have to use a file in /var/run/libvirt/devs/$DEVNODE
perhaps ?
I rather not create files. If there is a bug and libvirt doesn't remove
the file on the last restore (or it gets refcounting wrong or something)
then it requires user intervention.

Not only that, but the file would need to be owned by root:root (in
order to avoid malicious users mangling its contents) which might not be
possible at all times (e.g. root squashed NFS). On the other hand, NFS
itself doesn't support XATTRs currently so these patches do not with
that filesystem.
Post by Daniel P. Berrangé
- Use 'user' label but add a cryptographic signature
as a further attribute. Doesn't prevent tampering
but lets us throw away the data when tempering is
detected.
I'm not sure how this would work with multiple daemons. They would have
to have the key shared among them. I mean, if one daemon sets the
attribute and signs it with its own key and then comes another daemon
and updates the attribute it needs to sign it with the very same key
otherwise the signature would be invalidated from the first daemon POV.
Post by Daniel P. Berrangé
Did you consider either of these, or any other possible
options ? I'm still loathe to bake in a solution that will
only work on Linux, despite 99% of our userbase being Linux.
Not really. I focused on Linux and haven't realized BSD doesn't support
trusted.

Now that I am playing with this it looks like 'system' while being
available on both Linux and BSD can't really be set on Linux. But it can
be set on BSD (for privileged user only of course). I'm wondering if
using 'trusted' on Linux and 'system' on BSD is the way to go. This
could work except for cases where BSD and Linux meet, which can be only
on NFS (right?) which doesn't support XATTRs anyway. Therefore I think
we are safe, aren't we?

Michal
Daniel P. Berrangé
2018-11-28 10:00:16 UTC
Permalink
Post by Michal Privoznik
Post by Daniel P. Berrangé
s/available/available on Linux/
FreeBSD only supports 'user' and 'system' namespaces
D'oh!
Post by Daniel P. Berrangé
Post by Michal Privoznik
+ *
+ * user - can be modified by anybody,
+ * system - used by ACLs
+ * security - used by SELinux
+ * trusted - accessibly by CAP_SYS_ADMIN processes only
+ *
+ * Looks like the last one is way to go.
That prevents the QEMU driver using this functionality on any
non-Linux host.
The key problem we obviously face is that of the QEMU process
being able to modify the xattrs maliciously. 'trusted' namespace
solves this for Linux but unsolved for BSD/macOS.
I can only think of two alternative ways to deal with this
- Use a sidecar file. eg $FILEPATH.libvirt.json
Works ok for plain files. Troublesome for device nodes.
Would have to use a file in /var/run/libvirt/devs/$DEVNODE
perhaps ?
I rather not create files. If there is a bug and libvirt doesn't remove
the file on the last restore (or it gets refcounting wrong or something)
then it requires user intervention.
Isn't that true of this series too - ie if we update the xattr for
refcount wrong, or fail to remove the xattr on last restore ?

Feels like we have the same possible source of bugs, just against
an unlink() call rather than a setxattr() call.

The existance of libvirt's sidecar files on disk would be more
obviously to an admin.

That said tellin people to "rm" the file is kinda dangerous if
they typo and rm the real disk file that's alongside. Using a
completely 100% separate directory would solve that.
Post by Michal Privoznik
Not only that, but the file would need to be owned by root:root (in
order to avoid malicious users mangling its contents) which might not be
possible at all times (e.g. root squashed NFS). On the other hand, NFS
itself doesn't support XATTRs currently so these patches do not with
that filesystem.
Yeah, root sqaush NFS is horrible :-(
Post by Michal Privoznik
Post by Daniel P. Berrangé
- Use 'user' label but add a cryptographic signature
as a further attribute. Doesn't prevent tampering
but lets us throw away the data when tempering is
detected.
I'm not sure how this would work with multiple daemons. They would have
to have the key shared among them. I mean, if one daemon sets the
attribute and signs it with its own key and then comes another daemon
and updates the attribute it needs to sign it with the very same key
otherwise the signature would be invalidated from the first daemon POV.
Yeah, it would need shared keys in some manner which is a very big
pain point.
Post by Michal Privoznik
Post by Daniel P. Berrangé
Did you consider either of these, or any other possible
options ? I'm still loathe to bake in a solution that will
only work on Linux, despite 99% of our userbase being Linux.
Not really. I focused on Linux and haven't realized BSD doesn't support
trusted.
Now that I am playing with this it looks like 'system' while being
available on both Linux and BSD can't really be set on Linux. But it can
be set on BSD (for privileged user only of course). I'm wondering if
using 'trusted' on Linux and 'system' on BSD is the way to go. This
could work except for cases where BSD and Linux meet, which can be only
on NFS (right?) which doesn't support XATTRs anyway. Therefore I think
we are safe, aren't we?
I expect eventually NFSv4 may get xattr support - there's a 1 year old
RFC for it:

https://tools.ietf.org/html/rfc8276

Any app using xattr is going to face this same problem unless they only
use the common "user" namespace

Maybe justusing trusted (Linux) and system (BSD) is good enough for
us to get started, and we can allow for a different system (side car
files, or attribute signatures) in future if it becomes needed.

Regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
Michal Privoznik
2018-11-28 11:48:57 UTC
Permalink
Post by Daniel P. Berrangé
Post by Michal Privoznik
Post by Daniel P. Berrangé
s/available/available on Linux/
FreeBSD only supports 'user' and 'system' namespaces
D'oh!
Post by Daniel P. Berrangé
Post by Michal Privoznik
+ *
+ * user - can be modified by anybody,
+ * system - used by ACLs
+ * security - used by SELinux
+ * trusted - accessibly by CAP_SYS_ADMIN processes only
+ *
+ * Looks like the last one is way to go.
That prevents the QEMU driver using this functionality on any
non-Linux host.
The key problem we obviously face is that of the QEMU process
being able to modify the xattrs maliciously. 'trusted' namespace
solves this for Linux but unsolved for BSD/macOS.
I can only think of two alternative ways to deal with this
- Use a sidecar file. eg $FILEPATH.libvirt.json
Works ok for plain files. Troublesome for device nodes.
Would have to use a file in /var/run/libvirt/devs/$DEVNODE
perhaps ?
I rather not create files. If there is a bug and libvirt doesn't remove
the file on the last restore (or it gets refcounting wrong or something)
then it requires user intervention.
Isn't that true of this series too - ie if we update the xattr for
refcount wrong, or fail to remove the xattr on last restore ?
Yes, this the same. If there is a bug then unlink() and setxattr() don't
differ from this POV.
Post by Daniel P. Berrangé
Feels like we have the same possible source of bugs, just against
an unlink() call rather than a setxattr() call.
The existance of libvirt's sidecar files on disk would be more
obviously to an admin.
That said tellin people to "rm" the file is kinda dangerous if
they typo and rm the real disk file that's alongside. Using a
completely 100% separate directory would solve that.
Except we can't do that. If there is a file accessible to two daemons
(e.g. running on the same host in two different containers) the daemons
can't have their private DB, it has to be shared -> because of
refcounting. If the DB was private then one daemon could not
increment/decrement the refcounter inside the other daemon (consider two
daemons, one starting domain A, the other starting domain B, then the
first destroying domain A).

I mean, this is the sole reason I chose XATTRs and not some in memory
list/mapping of original owners.
Post by Daniel P. Berrangé
Post by Michal Privoznik
Not only that, but the file would need to be owned by root:root (in
order to avoid malicious users mangling its contents) which might not be
possible at all times (e.g. root squashed NFS). On the other hand, NFS
itself doesn't support XATTRs currently so these patches do not with
that filesystem.
Yeah, root sqaush NFS is horrible :-(
Post by Michal Privoznik
Post by Daniel P. Berrangé
- Use 'user' label but add a cryptographic signature
as a further attribute. Doesn't prevent tampering
but lets us throw away the data when tempering is
detected.
I'm not sure how this would work with multiple daemons. They would have
to have the key shared among them. I mean, if one daemon sets the
attribute and signs it with its own key and then comes another daemon
and updates the attribute it needs to sign it with the very same key
otherwise the signature would be invalidated from the first daemon POV.
Yeah, it would need shared keys in some manner which is a very big
pain point.
Post by Michal Privoznik
Post by Daniel P. Berrangé
Did you consider either of these, or any other possible
options ? I'm still loathe to bake in a solution that will
only work on Linux, despite 99% of our userbase being Linux.
Not really. I focused on Linux and haven't realized BSD doesn't support
trusted.
Now that I am playing with this it looks like 'system' while being
available on both Linux and BSD can't really be set on Linux. But it can
be set on BSD (for privileged user only of course). I'm wondering if
using 'trusted' on Linux and 'system' on BSD is the way to go. This
could work except for cases where BSD and Linux meet, which can be only
on NFS (right?) which doesn't support XATTRs anyway. Therefore I think
we are safe, aren't we?
I expect eventually NFSv4 may get xattr support - there's a 1 year old
https://tools.ietf.org/html/rfc8276
Any app using xattr is going to face this same problem unless they only
use the common "user" namespace
Maybe justusing trusted (Linux) and system (BSD) is good enough for
us to get started, and we can allow for a different system (side car
files, or attribute signatures) in future if it becomes needed.
Agreed.

Michal

Michal Privoznik
2018-11-23 08:43:25 UTC
Permalink
We are setting label on kernel, initrd, dtb and slic_table files.
But we never restored it.

Signed-off-by: Michal Privoznik <***@redhat.com>
---
src/security/security_dac.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)

diff --git a/src/security/security_dac.c b/src/security/security_dac.c
index 9b3069e60c..de12a1e351 100644
--- a/src/security/security_dac.c
+++ b/src/security/security_dac.c
@@ -1720,6 +1720,22 @@ virSecurityDACRestoreAllLabel(virSecurityManagerPtr mgr,
virSecurityDACRestoreFileLabel(mgr, def->os.loader->nvram) < 0)
rc = -1;

+ if (def->os.kernel &&
+ virSecurityDACRestoreFileLabel(mgr, def->os.kernel) < 0)
+ rc = -1;
+
+ if (def->os.initrd &&
+ virSecurityDACRestoreFileLabel(mgr, def->os.initrd) < 0)
+ rc = -1;
+
+ if (def->os.dtb &&
+ virSecurityDACRestoreFileLabel(mgr, def->os.dtb) < 0)
+ rc = -1;
+
+ if (def->os.slic_table &&
+ virSecurityDACRestoreFileLabel(mgr, def->os.slic_table) < 0)
+ rc = -1;
+
return rc;
}
--
2.18.1
Michal Privoznik
2018-11-23 08:43:30 UTC
Permalink
Signed-off-by: Michal Privoznik <***@redhat.com>
---
src/security/security_selinux.c | 161 ++++++++++++++++++++++----------
1 file changed, 114 insertions(+), 47 deletions(-)

diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c
index 715d9a428b..4990d94b5f 100644
--- a/src/security/security_selinux.c
+++ b/src/security/security_selinux.c
@@ -33,6 +33,7 @@

#include "security_driver.h"
#include "security_selinux.h"
+#include "security_util.h"
#include "virerror.h"
#include "viralloc.h"
#include "virlog.h"
@@ -197,14 +198,40 @@ virSecuritySELinuxTransactionAppend(const char *path,
}


+static int
+virSecuritySELinuxRememberLabel(const char *path,
+ const security_context_t con)
+{
+ return virSecuritySetRememberedLabel(SECURITY_SELINUX_NAME,
+ path, con);
+}
+
+
+static int
+virSecuritySELinuxRecallLabel(const char *path,
+ security_context_t *con)
+{
+ if (virSecurityGetRememberedLabel(SECURITY_SELINUX_NAME,
+ path, con) < 0)
+ return -1;
+
+ if (!con)
+ return 1;
+
+ return 0;
+}
+
+
static int virSecuritySELinuxSetFileconHelper(const char *path,
const char *tcon,
bool optional,
- bool privileged);
+ bool privileged,
+ bool remember);


static int virSecuritySELinuxRestoreFileLabel(virSecurityManagerPtr mgr,
- const char *path);
+ const char *path,
+ bool recall);


/**
@@ -255,10 +282,12 @@ virSecuritySELinuxTransactionRun(pid_t pid ATTRIBUTE_UNUSED,
rv = virSecuritySELinuxSetFileconHelper(item->path,
item->tcon,
item->optional,
- privileged);
+ privileged,
+ list->lock);
} else {
rv = virSecuritySELinuxRestoreFileLabel(list->manager,
- item->path);
+ item->path,
+ list->lock);
}

if (rv < 0)
@@ -1275,16 +1304,38 @@ virSecuritySELinuxSetFileconImpl(const char *path, const char *tcon,

static int
virSecuritySELinuxSetFileconHelper(const char *path, const char *tcon,
- bool optional, bool privileged)
+ bool optional, bool privileged, bool remember)
{
+ security_context_t econ = NULL;
int rc;
+ int ret = -1;

if ((rc = virSecuritySELinuxTransactionAppend(path, tcon, optional, false)) < 0)
return -1;
else if (rc > 0)
return 0;

- return virSecuritySELinuxSetFileconImpl(path, tcon, optional, privileged);
+ if (remember) {
+ if (getfilecon_raw(path, &econ) < 0 &&
+ errno != ENOTSUP && errno != ENODATA) {
+ virReportSystemError(errno,
+ _("unable to get SELinux context of %s"),
+ path);
+ goto cleanup;
+ }
+
+ if (econ &&
+ virSecuritySELinuxRememberLabel(path, econ) < 0)
+ goto cleanup;
+ }
+
+ if (virSecuritySELinuxSetFileconImpl(path, tcon, optional, privileged) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ freecon(econ);
+ return ret;
}


@@ -1293,7 +1344,7 @@ virSecuritySELinuxSetFileconOptional(virSecurityManagerPtr mgr,
const char *path, const char *tcon)
{
bool privileged = virSecurityManagerGetPrivileged(mgr);
- return virSecuritySELinuxSetFileconHelper(path, tcon, true, privileged);
+ return virSecuritySELinuxSetFileconHelper(path, tcon, true, privileged, false);
}

static int
@@ -1301,7 +1352,7 @@ virSecuritySELinuxSetFilecon(virSecurityManagerPtr mgr,
const char *path, const char *tcon)
{
bool privileged = virSecurityManagerGetPrivileged(mgr);
- return virSecuritySELinuxSetFileconHelper(path, tcon, false, privileged);
+ return virSecuritySELinuxSetFileconHelper(path, tcon, false, privileged, false);
}

static int
@@ -1362,7 +1413,8 @@ getContext(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
* errors that the caller(s) are already dealing with */
static int
virSecuritySELinuxRestoreFileLabel(virSecurityManagerPtr mgr,
- const char *path)
+ const char *path,
+ bool recall)
{
bool privileged = virSecurityManagerGetPrivileged(mgr);
struct stat buf;
@@ -1386,26 +1438,35 @@ virSecuritySELinuxRestoreFileLabel(virSecurityManagerPtr mgr,
goto cleanup;
}

- if (stat(newpath, &buf) != 0) {
- VIR_WARN("cannot stat %s: %s", newpath,
- virStrerror(errno, ebuf, sizeof(ebuf)));
- goto cleanup;
- }
-
- if (getContext(mgr, newpath, buf.st_mode, &fcon) < 0) {
- /* Any user created path likely does not have a default label,
- * which makes this an expected non error
- */
- VIR_WARN("cannot lookup default selinux label for %s", newpath);
- ret = 0;
- goto cleanup;
- }
-
- if ((rc = virSecuritySELinuxTransactionAppend(path, fcon, false, true)) < 0)
+ if ((rc = virSecuritySELinuxTransactionAppend(path, NULL, false, true)) < 0)
return -1;
else if (rc > 0)
return 0;

+ if (recall) {
+ if ((rc = virSecuritySELinuxRecallLabel(newpath, &fcon)) < 0) {
+ goto cleanup;
+ } else if (rc > 0) {
+ ret = 0;
+ goto cleanup;
+ }
+ } else {
+ if (stat(newpath, &buf) != 0) {
+ VIR_WARN("cannot stat %s: %s", newpath,
+ virStrerror(errno, ebuf, sizeof(ebuf)));
+ goto cleanup;
+ }
+
+ if (getContext(mgr, newpath, buf.st_mode, &fcon) < 0) {
+ /* Any user created path likely does not have a default label,
+ * which makes this an expected non error
+ */
+ VIR_WARN("cannot lookup default selinux label for %s", newpath);
+ ret = 0;
+ goto cleanup;
+ }
+ }
+
if (virSecuritySELinuxSetFileconImpl(newpath, fcon, false, privileged) < 0)
goto cleanup;

@@ -1460,7 +1521,7 @@ virSecuritySELinuxRestoreInputLabel(virSecurityManagerPtr mgr,

switch ((virDomainInputType)input->type) {
case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH:
- rc = virSecuritySELinuxRestoreFileLabel(mgr, input->source.evdev);
+ rc = virSecuritySELinuxRestoreFileLabel(mgr, input->source.evdev, false);
break;

case VIR_DOMAIN_INPUT_TYPE_MOUSE:
@@ -1516,7 +1577,7 @@ virSecuritySELinuxRestoreMemoryLabel(virSecurityManagerPtr mgr,
if (!seclabel || !seclabel->relabel)
return 0;

- ret = virSecuritySELinuxRestoreFileLabel(mgr, mem->nvdimmPath);
+ ret = virSecuritySELinuxRestoreFileLabel(mgr, mem->nvdimmPath, false);
break;

case VIR_DOMAIN_MEMORY_MODEL_DIMM:
@@ -1595,10 +1656,10 @@ virSecuritySELinuxRestoreTPMFileLabelInt(virSecurityManagerPtr mgr,
switch (tpm->type) {
case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH:
tpmdev = tpm->data.passthrough.source.data.file.path;
- rc = virSecuritySELinuxRestoreFileLabel(mgr, tpmdev);
+ rc = virSecuritySELinuxRestoreFileLabel(mgr, tpmdev, false);

if ((cancel_path = virTPMCreateCancelPath(tpmdev)) != NULL) {
- if (virSecuritySELinuxRestoreFileLabel(mgr, cancel_path) < 0)
+ if (virSecuritySELinuxRestoreFileLabel(mgr, cancel_path, false) < 0)
rc = -1;
VIR_FREE(cancel_path);
}
@@ -1665,7 +1726,7 @@ virSecuritySELinuxRestoreImageLabelInt(virSecurityManagerPtr mgr,
}
}

- return virSecuritySELinuxRestoreFileLabel(mgr, src->path);
+ return virSecuritySELinuxRestoreFileLabel(mgr, src->path, false);
}


@@ -2053,7 +2114,7 @@ virSecuritySELinuxRestorePCILabel(virPCIDevicePtr dev ATTRIBUTE_UNUSED,
{
virSecurityManagerPtr mgr = opaque;

- return virSecuritySELinuxRestoreFileLabel(mgr, file);
+ return virSecuritySELinuxRestoreFileLabel(mgr, file, false);
}

static int
@@ -2063,7 +2124,7 @@ virSecuritySELinuxRestoreUSBLabel(virUSBDevicePtr dev ATTRIBUTE_UNUSED,
{
virSecurityManagerPtr mgr = opaque;

- return virSecuritySELinuxRestoreFileLabel(mgr, file);
+ return virSecuritySELinuxRestoreFileLabel(mgr, file, false);
}


@@ -2080,7 +2141,7 @@ virSecuritySELinuxRestoreSCSILabel(virSCSIDevicePtr dev,
if (virSCSIDeviceGetShareable(dev) || virSCSIDeviceGetReadonly(dev))
return 0;

- return virSecuritySELinuxRestoreFileLabel(mgr, file);
+ return virSecuritySELinuxRestoreFileLabel(mgr, file, false);
}

static int
@@ -2090,7 +2151,7 @@ virSecuritySELinuxRestoreHostLabel(virSCSIVHostDevicePtr dev ATTRIBUTE_UNUSED,
{
virSecurityManagerPtr mgr = opaque;

- return virSecuritySELinuxRestoreFileLabel(mgr, file);
+ return virSecuritySELinuxRestoreFileLabel(mgr, file, false);
}


@@ -2194,7 +2255,7 @@ virSecuritySELinuxRestoreHostdevSubsysLabel(virSecurityManagerPtr mgr,
if (!(vfiodev = virMediatedDeviceGetIOMMUGroupDev(mdevsrc->uuidstr)))
goto done;

- ret = virSecuritySELinuxRestoreFileLabel(mgr, vfiodev);
+ ret = virSecuritySELinuxRestoreFileLabel(mgr, vfiodev, false);

VIR_FREE(vfiodev);
break;
@@ -2228,7 +2289,7 @@ virSecuritySELinuxRestoreHostdevCapsLabel(virSecurityManagerPtr mgr,
if (VIR_STRDUP(path, dev->source.caps.u.storage.block) < 0)
return -1;
}
- ret = virSecuritySELinuxRestoreFileLabel(mgr, path);
+ ret = virSecuritySELinuxRestoreFileLabel(mgr, path, false);
VIR_FREE(path);
break;
}
@@ -2242,7 +2303,7 @@ virSecuritySELinuxRestoreHostdevCapsLabel(virSecurityManagerPtr mgr,
if (VIR_STRDUP(path, dev->source.caps.u.misc.chardev) < 0)
return -1;
}
- ret = virSecuritySELinuxRestoreFileLabel(mgr, path);
+ ret = virSecuritySELinuxRestoreFileLabel(mgr, path, false);
VIR_FREE(path);
break;
}
@@ -2390,14 +2451,18 @@ virSecuritySELinuxRestoreChardevLabel(virSecurityManagerPtr mgr,
switch (dev_source->type) {
case VIR_DOMAIN_CHR_TYPE_DEV:
case VIR_DOMAIN_CHR_TYPE_FILE:
- if (virSecuritySELinuxRestoreFileLabel(mgr, dev_source->data.file.path) < 0)
+ if (virSecuritySELinuxRestoreFileLabel(mgr,
+ dev_source->data.file.path,
+ false) < 0)
goto done;
ret = 0;
break;

case VIR_DOMAIN_CHR_TYPE_UNIX:
if (!dev_source->data.nix.listen) {
- if (virSecuritySELinuxRestoreFileLabel(mgr, dev_source->data.file.path) < 0)
+ if (virSecuritySELinuxRestoreFileLabel(mgr,
+ dev_source->data.file.path,
+ false) < 0)
goto done;
}
ret = 0;
@@ -2408,11 +2473,13 @@ virSecuritySELinuxRestoreChardevLabel(virSecurityManagerPtr mgr,
(virAsprintf(&in, "%s.in", dev_source->data.file.path) < 0))
goto done;
if (virFileExists(in) && virFileExists(out)) {
- if ((virSecuritySELinuxRestoreFileLabel(mgr, out) < 0) ||
- (virSecuritySELinuxRestoreFileLabel(mgr, in) < 0)) {
+ if ((virSecuritySELinuxRestoreFileLabel(mgr, out, false) < 0) ||
+ (virSecuritySELinuxRestoreFileLabel(mgr, in, false) < 0)) {
goto done;
}
- } else if (virSecuritySELinuxRestoreFileLabel(mgr, dev_source->data.file.path) < 0) {
+ } else if (virSecuritySELinuxRestoreFileLabel(mgr,
+ dev_source->data.file.path,
+ false) < 0) {
goto done;
}
ret = 0;
@@ -2464,7 +2531,7 @@ virSecuritySELinuxRestoreSecuritySmartcardCallback(virDomainDefPtr def,
database = dev->data.cert.database;
if (!database)
database = VIR_DOMAIN_SMARTCARD_DEFAULT_DATABASE;
- return virSecuritySELinuxRestoreFileLabel(mgr, database);
+ return virSecuritySELinuxRestoreFileLabel(mgr, database, false);

case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH:
return virSecuritySELinuxRestoreChardevLabel(mgr, def,
@@ -2559,7 +2626,7 @@ virSecuritySELinuxRestoreAllLabel(virSecurityManagerPtr mgr,
rc = -1;

if (def->os.loader && def->os.loader->nvram &&
- virSecuritySELinuxRestoreFileLabel(mgr, def->os.loader->nvram) < 0)
+ virSecuritySELinuxRestoreFileLabel(mgr, def->os.loader->nvram, false) < 0)
rc = -1;

return rc;
@@ -2619,7 +2686,7 @@ virSecuritySELinuxRestoreSavedStateLabel(virSecurityManagerPtr mgr,
if (!secdef || !secdef->relabel)
return 0;

- return virSecuritySELinuxRestoreFileLabel(mgr, savefile);
+ return virSecuritySELinuxRestoreFileLabel(mgr, savefile, false);
}


@@ -3214,7 +3281,7 @@ virSecuritySELinuxRestoreFileLabels(virSecurityManagerPtr mgr,
char *filename = NULL;
DIR *dir;

- if ((ret = virSecuritySELinuxRestoreFileLabel(mgr, path)))
+ if ((ret = virSecuritySELinuxRestoreFileLabel(mgr, path, false)))
return ret;

if (!virFileIsDir(path))
@@ -3231,7 +3298,7 @@ virSecuritySELinuxRestoreFileLabels(virSecurityManagerPtr mgr,
ret = -1;
break;
}
- ret = virSecuritySELinuxRestoreFileLabel(mgr, filename);
+ ret = virSecuritySELinuxRestoreFileLabel(mgr, filename, false);
VIR_FREE(filename);
if (ret < 0)
break;
--
2.18.1
Michal Privoznik
2018-11-23 08:43:23 UTC
Permalink
When iterating over list of paths/disk sources to relabel it may
happen that the process fails at some point. In that case, for
the sake of keeping seclabel refcount (stored in XATTRs) in sync
with reality we have to perform rollback. However, if that fails
too the only thing we can do is warn user.

Signed-off-by: Michal Privoznik <***@redhat.com>
---
src/security/security_dac.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/src/security/security_dac.c b/src/security/security_dac.c
index 8155c6d58a..82b16f96ee 100644
--- a/src/security/security_dac.c
+++ b/src/security/security_dac.c
@@ -229,7 +229,6 @@ virSecurityDACTransactionRun(pid_t pid ATTRIBUTE_UNUSED,
for (i = 0; i < list->nItems; i++) {
virSecurityDACChownItemPtr item = list->items[i];

- /* TODO Implement rollback */
if (!item->restore) {
rv = virSecurityDACSetOwnership(list->manager,
item->src,
@@ -246,6 +245,19 @@ virSecurityDACTransactionRun(pid_t pid ATTRIBUTE_UNUSED,
break;
}

+ for (; rv < 0 && i > 0; i--) {
+ virSecurityDACChownItemPtr item = list->items[i - 1];
+
+ if (!item->restore) {
+ virSecurityDACRestoreFileLabelInternal(list->manager,
+ item->src,
+ item->path);
+ } else {
+ VIR_WARN("Ignoring failed restore attempt on %s",
+ NULLSTR(item->src ? item->src->path : item->path));
+ }
+ }
+
if (list->lock)
virSecurityManagerMetadataUnlock(list->manager, &state);
--
2.18.1
Michal Privoznik
2018-11-23 08:43:28 UTC
Permalink
Now that we have seclabel remembering we can safely restore
labels for shared and RO disks. In fact we need to do that to
keep seclabel refcount stored in XATTRs in sync with reality.

Signed-off-by: Michal Privoznik <***@redhat.com>
---
src/security/security_dac.c | 8 --------
1 file changed, 8 deletions(-)

diff --git a/src/security/security_dac.c b/src/security/security_dac.c
index 9d31faa9d4..60adfaf526 100644
--- a/src/security/security_dac.c
+++ b/src/security/security_dac.c
@@ -921,14 +921,6 @@ virSecurityDACRestoreImageLabelInt(virSecurityManagerPtr mgr,
if (!priv->dynamicOwnership)
return 0;

- /* Don't restore labels on readoly/shared disks, because other VMs may
- * still be accessing these. Alternatively we could iterate over all
- * running domains and try to figure out if it is in use, but this would
- * not work for clustered filesystems, since we can't see running VMs using
- * the file on other nodes. Safest bet is thus to skip the restore step. */
- if (src->readonly || src->shared)
- return 0;
-
secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME);
if (secdef && !secdef->relabel)
return 0;
--
2.18.1
Michal Privoznik
2018-11-23 08:43:24 UTC
Permalink
It helps whe trying to match calls with virSecurityDACSetAllLabel
if the order in which devices are set/restored is the same in
both functions.

Signed-off-by: Michal Privoznik <***@redhat.com>
---
src/security/security_dac.c | 36 ++++++++++++++++++------------------
1 file changed, 18 insertions(+), 18 deletions(-)

diff --git a/src/security/security_dac.c b/src/security/security_dac.c
index 82b16f96ee..9b3069e60c 100644
--- a/src/security/security_dac.c
+++ b/src/security/security_dac.c
@@ -1665,24 +1665,6 @@ virSecurityDACRestoreAllLabel(virSecurityManagerPtr mgr,
VIR_DEBUG("Restoring security label on %s migrated=%d",
def->name, migrated);

- for (i = 0; i < def->nhostdevs; i++) {
- if (virSecurityDACRestoreHostdevLabel(mgr,
- def,
- def->hostdevs[i],
- NULL) < 0)
- rc = -1;
- }
-
- for (i = 0; i < def->ngraphics; i++) {
- if (virSecurityDACRestoreGraphicsLabel(mgr, def, def->graphics[i]) < 0)
- return -1;
- }
-
- for (i = 0; i < def->ninputs; i++) {
- if (virSecurityDACRestoreInputLabel(mgr, def, def->inputs[i]) < 0)
- rc = -1;
- }
-
for (i = 0; i < def->ndisks; i++) {
if (virSecurityDACRestoreImageLabelInt(mgr,
def,
@@ -1691,6 +1673,24 @@ virSecurityDACRestoreAllLabel(virSecurityManagerPtr mgr,
rc = -1;
}

+ for (i = 0; i < def->ngraphics; i++) {
+ if (virSecurityDACRestoreGraphicsLabel(mgr, def, def->graphics[i]) < 0)
+ return -1;
+ }
+
+ for (i = 0; i < def->ninputs; i++) {
+ if (virSecurityDACRestoreInputLabel(mgr, def, def->inputs[i]) < 0)
+ rc = -1;
+ }
+
+ for (i = 0; i < def->nhostdevs; i++) {
+ if (virSecurityDACRestoreHostdevLabel(mgr,
+ def,
+ def->hostdevs[i],
+ NULL) < 0)
+ rc = -1;
+ }
+
for (i = 0; i < def->nmems; i++) {
if (virSecurityDACRestoreMemoryLabel(mgr,
def,
--
2.18.1
Michal Privoznik
2018-11-23 08:43:32 UTC
Permalink
When iterating over list of paths/disk sources to relabel it may
happen that the process fails at some point. In that case, for
the sake of keeping seclabel refcount (stored in XATTRs) in sync
with reality we have to perform rollback. However, if that fails
too the only thing we can do is warn user.

Signed-off-by: Michal Privoznik <***@redhat.com>
---
src/security/security_selinux.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c
index 290faba9d6..0cf8164265 100644
--- a/src/security/security_selinux.c
+++ b/src/security/security_selinux.c
@@ -276,7 +276,6 @@ virSecuritySELinuxTransactionRun(pid_t pid ATTRIBUTE_UNUSED,
for (i = 0; i < list->nItems; i++) {
virSecuritySELinuxContextItemPtr item = list->items[i];

- /* TODO Implement rollback */
if (!item->restore) {
rv = virSecuritySELinuxSetFileconHelper(list->manager,
item->path,
@@ -293,6 +292,18 @@ virSecuritySELinuxTransactionRun(pid_t pid ATTRIBUTE_UNUSED,
break;
}

+ for (; rv < 0 && i > 0; i--) {
+ virSecuritySELinuxContextItemPtr item = list->items[i - 1];
+
+ if (!item->restore) {
+ virSecuritySELinuxRestoreFileLabel(list->manager,
+ item->path,
+ list->lock);
+ } else {
+ VIR_WARN("Ignoring failed restore attempt on %s", item->path);
+ }
+ }
+
if (list->lock)
virSecurityManagerMetadataUnlock(list->manager, &state);
--
2.18.1
Michal Privoznik
2018-11-23 08:43:34 UTC
Permalink
We are setting label on kernel, initrd, dtb and slic_table files.
But we never restored it.

Signed-off-by: Michal Privoznik <***@redhat.com>
---
src/security/security_selinux.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)

diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c
index 553fc852db..5f2fab73bc 100644
--- a/src/security/security_selinux.c
+++ b/src/security/security_selinux.c
@@ -2656,6 +2656,22 @@ virSecuritySELinuxRestoreAllLabel(virSecurityManagerPtr mgr,
virSecuritySELinuxRestoreFileLabel(mgr, def->os.loader->nvram, false) < 0)
rc = -1;

+ if (def->os.kernel &&
+ virSecuritySELinuxRestoreFileLabel(mgr, def->os.kernel, false) < 0)
+ rc = -1;
+
+ if (def->os.initrd &&
+ virSecuritySELinuxRestoreFileLabel(mgr, def->os.initrd, false) < 0)
+ rc = -1;
+
+ if (def->os.dtb &&
+ virSecuritySELinuxRestoreFileLabel(mgr, def->os.dtb, false) < 0)
+ rc = -1;
+
+ if (def->os.slic_table &&
+ virSecuritySELinuxRestoreFileLabel(mgr, def->os.slic_table, false) < 0)
+ rc = -1;
+
return rc;
}
--
2.18.1
Michal Privoznik
2018-11-23 08:43:27 UTC
Permalink
Signed-off-by: Michal Privoznik <***@redhat.com>
---
src/security/security_dac.c | 48 ++++++++++++++++++++++++++++++-------
1 file changed, 40 insertions(+), 8 deletions(-)

diff --git a/src/security/security_dac.c b/src/security/security_dac.c
index cdbe07543c..9d31faa9d4 100644
--- a/src/security/security_dac.c
+++ b/src/security/security_dac.c
@@ -29,6 +29,7 @@
#endif

#include "security_dac.h"
+#include "security_util.h"
#include "virerror.h"
#include "virfile.h"
#include "viralloc.h"
@@ -415,11 +416,26 @@ virSecurityDACGetImageIds(virSecurityLabelDefPtr seclabel,
*/
static int
virSecurityDACRememberLabel(virSecurityDACDataPtr priv ATTRIBUTE_UNUSED,
- const char *path ATTRIBUTE_UNUSED,
- uid_t uid ATTRIBUTE_UNUSED,
- gid_t gid ATTRIBUTE_UNUSED)
+ const char *path,
+ uid_t uid,
+ gid_t gid)
{
- return 0;
+ char *label = NULL;
+ int ret = -1;
+
+ if (virAsprintf(&label, "+%u:+%u",
+ (unsigned int)uid,
+ (unsigned int)gid) < 0)
+ goto cleanup;
+
+ if (virSecuritySetRememberedLabel(SECURITY_DAC_NAME,
+ path, label) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ VIR_FREE(label);
+ return ret;
}

/**
@@ -439,11 +455,27 @@ virSecurityDACRememberLabel(virSecurityDACDataPtr priv ATTRIBUTE_UNUSED,
*/
static int
virSecurityDACRecallLabel(virSecurityDACDataPtr priv ATTRIBUTE_UNUSED,
- const char *path ATTRIBUTE_UNUSED,
- uid_t *uid ATTRIBUTE_UNUSED,
- gid_t *gid ATTRIBUTE_UNUSED)
+ const char *path,
+ uid_t *uid,
+ gid_t *gid)
{
- return 0;
+ char *label;
+ int ret = -1;
+
+ if (virSecurityGetRememberedLabel(SECURITY_DAC_NAME,
+ path, &label) < 0)
+ goto cleanup;
+
+ if (!label)
+ return 1;
+
+ if (virParseOwnershipIds(label, uid, gid) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ VIR_FREE(label);
+ return ret;
}

static virSecurityDriverStatus
--
2.18.1
Michal Privoznik
2018-11-23 08:43:33 UTC
Permalink
It helps whe trying to match calls with virSecuritySELinuxSetAllLabel
if the order in which devices are set/restored is the same in
both functions.

Signed-off-by: Michal Privoznik <***@redhat.com>
---
src/security/security_selinux.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c
index 0cf8164265..553fc852db 100644
--- a/src/security/security_selinux.c
+++ b/src/security/security_selinux.c
@@ -2604,8 +2604,11 @@ virSecuritySELinuxRestoreAllLabel(virSecurityManagerPtr mgr,
if (!secdef || !secdef->relabel || data->skipAllLabel)
return 0;

- if (def->tpm) {
- if (virSecuritySELinuxRestoreTPMFileLabelInt(mgr, def, def->tpm) < 0)
+ for (i = 0; i < def->ndisks; i++) {
+ virDomainDiskDefPtr disk = def->disks[i];
+
+ if (virSecuritySELinuxRestoreImageLabelInt(mgr, def, disk->src,
+ migrated) < 0)
rc = -1;
}

@@ -2627,11 +2630,8 @@ virSecuritySELinuxRestoreAllLabel(virSecurityManagerPtr mgr,
return -1;
}

- for (i = 0; i < def->ndisks; i++) {
- virDomainDiskDefPtr disk = def->disks[i];
-
- if (virSecuritySELinuxRestoreImageLabelInt(mgr, def, disk->src,
- migrated) < 0)
+ if (def->tpm) {
+ if (virSecuritySELinuxRestoreTPMFileLabelInt(mgr, def, def->tpm) < 0)
rc = -1;
}
--
2.18.1
Michal Privoznik
2018-11-23 08:43:29 UTC
Permalink
It is going to be important to know if the current transaction we
are running is a restore operation or set label operation.

Signed-off-by: Michal Privoznik <***@redhat.com>
---
src/security/security_selinux.c | 36 +++++++++++++++++++++++----------
1 file changed, 25 insertions(+), 11 deletions(-)

diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c
index 95e9a1b0c7..715d9a428b 100644
--- a/src/security/security_selinux.c
+++ b/src/security/security_selinux.c
@@ -85,6 +85,7 @@ struct _virSecuritySELinuxContextItem {
char *path;
char *tcon;
bool optional;
+ bool restore;
};

typedef struct _virSecuritySELinuxContextList virSecuritySELinuxContextList;
@@ -123,7 +124,8 @@ static int
virSecuritySELinuxContextListAppend(virSecuritySELinuxContextListPtr list,
const char *path,
const char *tcon,
- bool optional)
+ bool optional,
+ bool restore)
{
int ret = -1;
virSecuritySELinuxContextItemPtr item = NULL;
@@ -135,6 +137,7 @@ virSecuritySELinuxContextListAppend(virSecuritySELinuxContextListPtr list,
goto cleanup;

item->optional = optional;
+ item->restore = restore;

if (VIR_APPEND_ELEMENT(list->items, list->nItems, item) < 0)
goto cleanup;
@@ -178,7 +181,8 @@ virSecuritySELinuxContextListFree(void *opaque)
static int
virSecuritySELinuxTransactionAppend(const char *path,
const char *tcon,
- bool optional)
+ bool optional,
+ bool restore)
{
virSecuritySELinuxContextListPtr list;

@@ -186,7 +190,7 @@ virSecuritySELinuxTransactionAppend(const char *path,
if (!list)
return 0;

- if (virSecuritySELinuxContextListAppend(list, path, tcon, optional) < 0)
+ if (virSecuritySELinuxContextListAppend(list, path, tcon, optional, restore) < 0)
return -1;

return 1;
@@ -198,6 +202,11 @@ static int virSecuritySELinuxSetFileconHelper(const char *path,
bool optional,
bool privileged);

+
+static int virSecuritySELinuxRestoreFileLabel(virSecurityManagerPtr mgr,
+ const char *path);
+
+
/**
* virSecuritySELinuxTransactionRun:
* @pid: process pid
@@ -242,13 +251,18 @@ virSecuritySELinuxTransactionRun(pid_t pid ATTRIBUTE_UNUSED,
virSecuritySELinuxContextItemPtr item = list->items[i];

/* TODO Implement rollback */
- if (virSecuritySELinuxSetFileconHelper(item->path,
- item->tcon,
- item->optional,
- privileged) < 0) {
- rv = -1;
- break;
+ if (!item->restore) {
+ rv = virSecuritySELinuxSetFileconHelper(item->path,
+ item->tcon,
+ item->optional,
+ privileged);
+ } else {
+ rv = virSecuritySELinuxRestoreFileLabel(list->manager,
+ item->path);
}
+
+ if (rv < 0)
+ break;
}

if (list->lock)
@@ -1265,7 +1279,7 @@ virSecuritySELinuxSetFileconHelper(const char *path, const char *tcon,
{
int rc;

- if ((rc = virSecuritySELinuxTransactionAppend(path, tcon, optional)) < 0)
+ if ((rc = virSecuritySELinuxTransactionAppend(path, tcon, optional, false)) < 0)
return -1;
else if (rc > 0)
return 0;
@@ -1387,7 +1401,7 @@ virSecuritySELinuxRestoreFileLabel(virSecurityManagerPtr mgr,
goto cleanup;
}

- if ((rc = virSecuritySELinuxTransactionAppend(path, fcon, false)) < 0)
+ if ((rc = virSecuritySELinuxTransactionAppend(path, fcon, false, true)) < 0)
return -1;
else if (rc > 0)
return 0;
--
2.18.1
Michal Privoznik
2018-11-23 08:43:36 UTC
Permalink
Signed-off-by: Michal Privoznik <***@redhat.com>
---
src/qemu/libvirtd_qemu.aug | 1 +
src/qemu/qemu.conf | 6 ++++++
src/qemu/qemu_conf.c | 4 ++++
src/qemu/test_libvirtd_qemu.aug.in | 1 +
4 files changed, 12 insertions(+)

diff --git a/src/qemu/libvirtd_qemu.aug b/src/qemu/libvirtd_qemu.aug
index ddc4bbfd1d..8a5b39e568 100644
--- a/src/qemu/libvirtd_qemu.aug
+++ b/src/qemu/libvirtd_qemu.aug
@@ -71,6 +71,7 @@ module Libvirtd_qemu =
| str_entry "user"
| str_entry "group"
| bool_entry "dynamic_ownership"
+ | bool_entry "remember_owner"
| str_array_entry "cgroup_controllers"
| str_array_entry "cgroup_device_acl"
| int_entry "seccomp_sandbox"
diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf
index 8391332cb4..31e8d8476b 100644
--- a/src/qemu/qemu.conf
+++ b/src/qemu/qemu.conf
@@ -450,6 +450,12 @@
# Set to 0 to disable file ownership changes.
#dynamic_ownership = 1

+# Whether libvirt should remember and restore the original
+# ownership over files it is relabeling. Be aware that with the
+# current implementation this requires exclusive access to the
+# files which might hurt performance a bit in some cases.
+# Defaults to 1, set to 0 to disable the feature.
+#remember_owner = 1

# What cgroup controllers to make use of with QEMU guests
#
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index a946b05d5d..89491a37b7 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -147,6 +147,7 @@ virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged)
cfg->group = (gid_t)-1;
}
cfg->dynamicOwnership = privileged;
+ cfg->rememberOwner = true;

cfg->cgroupControllers = -1; /* -1 == auto-detect */

@@ -730,6 +731,9 @@ int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr cfg,
if (virConfGetValueBool(conf, "dynamic_ownership", &cfg->dynamicOwnership) < 0)
goto cleanup;

+ if (virConfGetValueBool(conf, "remember_owner", &cfg->rememberOwner) < 0)
+ goto cleanup;
+
if (virConfGetValueStringList(conf, "cgroup_controllers", false,
&controllers) < 0)
goto cleanup;
diff --git a/src/qemu/test_libvirtd_qemu.aug.in b/src/qemu/test_libvirtd_qemu.aug.in
index f1e8806ad2..92a8ae1192 100644
--- a/src/qemu/test_libvirtd_qemu.aug.in
+++ b/src/qemu/test_libvirtd_qemu.aug.in
@@ -43,6 +43,7 @@ module Test_libvirtd_qemu =
{ "user" = "root" }
{ "group" = "root" }
{ "dynamic_ownership" = "1" }
+{ "remember_owner" = "1" }
{ "cgroup_controllers"
{ "1" = "cpu" }
{ "2" = "devices" }
--
2.18.1
Michal Privoznik
2018-11-23 08:43:31 UTC
Permalink
It's important to keep XATTRs untouched (well, in the same state
they were in when entering the function). Otherwise our
refcounting would be messed up.

Signed-off-by: Michal Privoznik <***@redhat.com>
---
src/security/security_selinux.c | 40 +++++++++++++++++++++++----------
1 file changed, 28 insertions(+), 12 deletions(-)

diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c
index 4990d94b5f..290faba9d6 100644
--- a/src/security/security_selinux.c
+++ b/src/security/security_selinux.c
@@ -222,10 +222,10 @@ virSecuritySELinuxRecallLabel(const char *path,
}


-static int virSecuritySELinuxSetFileconHelper(const char *path,
+static int virSecuritySELinuxSetFileconHelper(virSecurityManagerPtr mgr,
+ const char *path,
const char *tcon,
bool optional,
- bool privileged,
bool remember);


@@ -252,7 +252,6 @@ virSecuritySELinuxTransactionRun(pid_t pid ATTRIBUTE_UNUSED,
{
virSecuritySELinuxContextListPtr list = opaque;
virSecurityManagerMetadataLockStatePtr state;
- bool privileged = virSecurityManagerGetPrivileged(list->manager);
const char **paths = NULL;
size_t npaths = 0;
size_t i;
@@ -279,10 +278,10 @@ virSecuritySELinuxTransactionRun(pid_t pid ATTRIBUTE_UNUSED,

/* TODO Implement rollback */
if (!item->restore) {
- rv = virSecuritySELinuxSetFileconHelper(item->path,
+ rv = virSecuritySELinuxSetFileconHelper(list->manager,
+ item->path,
item->tcon,
item->optional,
- privileged,
list->lock);
} else {
rv = virSecuritySELinuxRestoreFileLabel(list->manager,
@@ -1303,9 +1302,13 @@ virSecuritySELinuxSetFileconImpl(const char *path, const char *tcon,


static int
-virSecuritySELinuxSetFileconHelper(const char *path, const char *tcon,
- bool optional, bool privileged, bool remember)
+virSecuritySELinuxSetFileconHelper(virSecurityManagerPtr mgr,
+ const char *path,
+ const char *tcon,
+ bool optional,
+ bool remember)
{
+ bool privileged = virSecurityManagerGetPrivileged(mgr);
security_context_t econ = NULL;
int rc;
int ret = -1;
@@ -1329,8 +1332,23 @@ virSecuritySELinuxSetFileconHelper(const char *path, const char *tcon,
goto cleanup;
}

- if (virSecuritySELinuxSetFileconImpl(path, tcon, optional, privileged) < 0)
+ if (virSecuritySELinuxSetFileconImpl(path, tcon, optional, privileged) < 0) {
+ virErrorPtr origerr;
+
+ virErrorPreserveLast(&origerr);
+ /* Try to restore the label. This is done so that XATTRs
+ * are left in the same state as when the control entered
+ * this function. However, if our attempt fails, there's
+ * not much we can do. XATTRs refcounting is fubar'ed and
+ * the only option we have is warn users. */
+ if (virSecuritySELinuxRestoreFileLabel(mgr, path, remember) < 0)
+ VIR_WARN("Unable to restore label on '%s'. "
+ "XATTRs might have been left in inconsistent state.",
+ path);
+
+ virErrorRestore(&origerr);
goto cleanup;
+ }

ret = 0;
cleanup:
@@ -1343,16 +1361,14 @@ static int
virSecuritySELinuxSetFileconOptional(virSecurityManagerPtr mgr,
const char *path, const char *tcon)
{
- bool privileged = virSecurityManagerGetPrivileged(mgr);
- return virSecuritySELinuxSetFileconHelper(path, tcon, true, privileged, false);
+ return virSecuritySELinuxSetFileconHelper(mgr, path, tcon, true, false);
}

static int
virSecuritySELinuxSetFilecon(virSecurityManagerPtr mgr,
const char *path, const char *tcon)
{
- bool privileged = virSecurityManagerGetPrivileged(mgr);
- return virSecuritySELinuxSetFileconHelper(path, tcon, false, privileged, false);
+ return virSecuritySELinuxSetFileconHelper(mgr, path, tcon, false, false);
}

static int
--
2.18.1
Michal Privoznik
2018-11-23 08:43:35 UTC
Permalink
Our code is not bug free. The refcounting I introduced will
almost certainly not work in some use cases. Provide a script
that will remove all the XATTRs set by libvirt so that it can
start cleanly.

Signed-off-by: Michal Privoznik <***@redhat.com>
---
tools/Makefile.am | 1 +
tools/libvirt_recover_xattrs.sh | 89 +++++++++++++++++++++++++++++++++
2 files changed, 90 insertions(+)
create mode 100755 tools/libvirt_recover_xattrs.sh

diff --git a/tools/Makefile.am b/tools/Makefile.am
index f069167acc..1dc009c4fb 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -75,6 +75,7 @@ EXTRA_DIST = \
virt-login-shell.conf \
virsh-edit.c \
bash-completion/vsh \
+ libvirt_recover_xattrs.sh \
$(PODFILES) \
$(MANINFILES) \
$(NULL)
diff --git a/tools/libvirt_recover_xattrs.sh b/tools/libvirt_recover_xattrs.sh
new file mode 100755
index 0000000000..c4a8b27cbc
--- /dev/null
+++ b/tools/libvirt_recover_xattrs.sh
@@ -0,0 +1,89 @@
+#!/bin/bash
+
+function die {
+ echo $@ >&2
+ exit 1
+}
+
+function show_help {
+ cat << EOF
+Usage: ${0##*/} -[hqn] [PATH]
+
+Clear out any XATTRs set by libvirt on all files that have them.
+The idea is to reset refcounting, should it break.
+
+ -h display this help and exit
+ -q quiet (don't print which files are being fixed)
+ -n dry run; don't remove any XATTR just report the file name
+
+PATH can be specified to refine search to only to given path
+instead of whole root ('/'), which is the default.
+EOF
+}
+
+QUIET=0
+DRY_RUN=0
+P="/"
+
+# So far only qemu and lxc drivers use security driver.
+URI=("qemu:///system"
+ "qemu:///session"
+ "lxc:///system")
+
+LIBVIRT_XATTR_PREFIX="trusted.libvirt.security"
+
+if [ `whoami` != "root" ]; then
+ die "Must be run as root"
+fi
+
+while getopts hqn opt; do
+ case $opt in
+ h)
+ show_help
+ exit 0
+ ;;
+ q)
+ QUIET=1
+ ;;
+ n)
+ DRY_RUN=1
+ ;;
+ *)
+ show_help >&2
+ exit 1
+ ;;
+ esac
+done
+
+shift $((OPTIND - 1))
+if [ $# -gt 0 ]; then
+ P=$1
+fi
+
+if [ ${DRY_RUN} -eq 0 ]; then
+ for u in ${URI[*]} ; do
+ if [ -n "`virsh -q -c $u list 2>/dev/null`" ]; then
+ die "There are still some domains running for $u"
+ fi
+ done
+fi
+
+XATTRS=("trusted.libvirt.security.dac"
+ "trusted.libvirt.security.ref_dac"
+ "trusted.libvirt.security.selinux"
+ "trusted.libvirt.security.ref_selinux")
+
+for i in $(getfattr -R -d -m ${LIBVIRT_XATTR_PREFIX} --absolute-names ${P} 2>/dev/null | grep "^# file:" | cut -d':' -f 2); do
+ if [ ${DRY_RUN} -ne 0 ]; then
+ echo $i
+ getfattr -d -m ${LIBVIRT_XATTR_PREFIX} $i
+ continue
+ fi
+
+ if [ ${QUIET} -eq 0 ]; then
+ echo "Fixing $i";
+ fi
+ for x in ${XATTRS[*]}; do
+ setfattr -x $x $i
+ done
+done
--
2.18.1
Continue reading on narkive:
Loading...