|
|
c401cc |
From 0a3ac2c40b669c4631bf25c7069916cab1a1be97 Mon Sep 17 00:00:00 2001
|
|
|
c401cc |
Message-Id: <0a3ac2c40b669c4631bf25c7069916cab1a1be97@dist-git>
|
|
|
c401cc |
From: "Daniel P. Berrange" <berrange@redhat.com>
|
|
|
c401cc |
Date: Tue, 18 Feb 2014 15:45:38 -0700
|
|
|
c401cc |
Subject: [PATCH] Add helper for running code in separate namespaces
|
|
|
c401cc |
|
|
|
c401cc |
https://bugzilla.redhat.com/show_bug.cgi?id=1045643
|
|
|
c401cc |
prereq of CVE-2013-6456
|
|
|
c401cc |
|
|
|
c401cc |
Implement virProcessRunInMountNamespace, which runs callback of type
|
|
|
c401cc |
virProcessNamespaceCallback in a container namespace. This uses a
|
|
|
c401cc |
child process to run the callback, since you can't change the mount
|
|
|
c401cc |
namespace of a thread. This implies that callbacks have to be careful
|
|
|
c401cc |
about what code they run due to async safety rules.
|
|
|
c401cc |
|
|
|
c401cc |
Idea by Dan Berrange, based on an initial report by Reco
|
|
|
c401cc |
<recoverym4n@gmail.com> at
|
|
|
c401cc |
http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=732394
|
|
|
c401cc |
|
|
|
c401cc |
Signed-off-by: Daniel Berrange <berrange@redhat.com>
|
|
|
c401cc |
Signed-off-by: Eric Blake <eblake@redhat.com>
|
|
|
c401cc |
(cherry picked from commit 7c72ef6f555f1f9844d51be2f38f078bc908652c)
|
|
|
c401cc |
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
|
|
|
c401cc |
---
|
|
|
c401cc |
src/libvirt_private.syms | 1 +
|
|
|
c401cc |
src/util/virprocess.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
c401cc |
src/util/virprocess.h | 11 +++++
|
|
|
c401cc |
3 files changed, 118 insertions(+)
|
|
|
c401cc |
|
|
|
c401cc |
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
|
|
|
c401cc |
index e5f80e1..12f70cb 100644
|
|
|
c401cc |
--- a/src/libvirt_private.syms
|
|
|
c401cc |
+++ b/src/libvirt_private.syms
|
|
|
c401cc |
@@ -1829,6 +1829,7 @@ virProcessGetNamespaces;
|
|
|
c401cc |
virProcessGetStartTime;
|
|
|
c401cc |
virProcessKill;
|
|
|
c401cc |
virProcessKillPainfully;
|
|
|
c401cc |
+virProcessRunInMountNamespace;
|
|
|
c401cc |
virProcessSetAffinity;
|
|
|
c401cc |
virProcessSetMaxFiles;
|
|
|
c401cc |
virProcessSetMaxMemLock;
|
|
|
c401cc |
diff --git a/src/util/virprocess.c b/src/util/virprocess.c
|
|
|
c401cc |
index 9fc3207..868dd04 100644
|
|
|
c401cc |
--- a/src/util/virprocess.c
|
|
|
c401cc |
+++ b/src/util/virprocess.c
|
|
|
c401cc |
@@ -46,6 +46,7 @@
|
|
|
c401cc |
#include "virlog.h"
|
|
|
c401cc |
#include "virutil.h"
|
|
|
c401cc |
#include "virstring.h"
|
|
|
c401cc |
+#include "vircommand.h"
|
|
|
c401cc |
|
|
|
c401cc |
#define VIR_FROM_THIS VIR_FROM_NONE
|
|
|
c401cc |
|
|
|
c401cc |
@@ -847,3 +848,108 @@ int virProcessGetStartTime(pid_t pid,
|
|
|
c401cc |
return 0;
|
|
|
c401cc |
}
|
|
|
c401cc |
#endif
|
|
|
c401cc |
+
|
|
|
c401cc |
+
|
|
|
c401cc |
+#ifdef HAVE_SETNS
|
|
|
c401cc |
+static int virProcessNamespaceHelper(int errfd,
|
|
|
c401cc |
+ pid_t pid,
|
|
|
c401cc |
+ virProcessNamespaceCallback cb,
|
|
|
c401cc |
+ void *opaque)
|
|
|
c401cc |
+{
|
|
|
c401cc |
+ char *path;
|
|
|
c401cc |
+ int fd = -1;
|
|
|
c401cc |
+ int ret = -1;
|
|
|
c401cc |
+
|
|
|
c401cc |
+ if (virAsprintf(&path, "/proc/%llu/ns/mnt", (unsigned long long)pid) < 0)
|
|
|
c401cc |
+ goto cleanup;
|
|
|
c401cc |
+
|
|
|
c401cc |
+ if ((fd = open(path, O_RDONLY)) < 0) {
|
|
|
c401cc |
+ virReportSystemError(errno, "%s",
|
|
|
c401cc |
+ _("Kernel does not provide mount namespace"));
|
|
|
c401cc |
+ goto cleanup;
|
|
|
c401cc |
+ }
|
|
|
c401cc |
+
|
|
|
c401cc |
+ if (setns(fd, 0) < 0) {
|
|
|
c401cc |
+ virReportSystemError(errno, "%s",
|
|
|
c401cc |
+ _("Unable to enter mount namespace"));
|
|
|
c401cc |
+ goto cleanup;
|
|
|
c401cc |
+ }
|
|
|
c401cc |
+
|
|
|
c401cc |
+ ret = cb(pid, opaque);
|
|
|
c401cc |
+
|
|
|
c401cc |
+ cleanup:
|
|
|
c401cc |
+ if (ret < 0) {
|
|
|
c401cc |
+ virErrorPtr err = virGetLastError();
|
|
|
c401cc |
+ if (err) {
|
|
|
c401cc |
+ size_t len = strlen(err->message) + 1;
|
|
|
c401cc |
+ ignore_value(safewrite(errfd, err->message, len));
|
|
|
c401cc |
+ }
|
|
|
c401cc |
+ }
|
|
|
c401cc |
+ VIR_FREE(path);
|
|
|
c401cc |
+ VIR_FORCE_CLOSE(fd);
|
|
|
c401cc |
+ return ret;
|
|
|
c401cc |
+}
|
|
|
c401cc |
+
|
|
|
c401cc |
+/* Run cb(opaque) in the mount namespace of pid. Return -1 with error
|
|
|
c401cc |
+ * message raised if we fail to run the child, if the child dies from
|
|
|
c401cc |
+ * a signal, or if the child has status 1; otherwise return the exit
|
|
|
c401cc |
+ * status of the child. The callback will be run in a child process
|
|
|
c401cc |
+ * so must be careful to only use async signal safe functions.
|
|
|
c401cc |
+ */
|
|
|
c401cc |
+int
|
|
|
c401cc |
+virProcessRunInMountNamespace(pid_t pid,
|
|
|
c401cc |
+ virProcessNamespaceCallback cb,
|
|
|
c401cc |
+ void *opaque)
|
|
|
c401cc |
+{
|
|
|
c401cc |
+ int ret = -1;
|
|
|
c401cc |
+ pid_t child = -1;
|
|
|
c401cc |
+ int errfd[2] = { -1, -1 };
|
|
|
c401cc |
+
|
|
|
c401cc |
+ if (pipe(errfd) < 0) {
|
|
|
c401cc |
+ virReportSystemError(errno, "%s",
|
|
|
c401cc |
+ _("Cannot create pipe for child"));
|
|
|
c401cc |
+ return -1;
|
|
|
c401cc |
+ }
|
|
|
c401cc |
+
|
|
|
c401cc |
+ ret = virFork(&child);
|
|
|
c401cc |
+
|
|
|
c401cc |
+ if (ret < 0 || child < 0) {
|
|
|
c401cc |
+ if (child == 0)
|
|
|
c401cc |
+ _exit(1);
|
|
|
c401cc |
+
|
|
|
c401cc |
+ /* parent */
|
|
|
c401cc |
+ virProcessAbort(child);
|
|
|
c401cc |
+ goto cleanup;
|
|
|
c401cc |
+ }
|
|
|
c401cc |
+
|
|
|
c401cc |
+ if (child == 0) {
|
|
|
c401cc |
+ VIR_FORCE_CLOSE(errfd[0]);
|
|
|
c401cc |
+ ret = virProcessNamespaceHelper(errfd[1], pid,
|
|
|
c401cc |
+ cb, opaque);
|
|
|
c401cc |
+ VIR_FORCE_CLOSE(errfd[1]);
|
|
|
c401cc |
+ _exit(ret < 0 ? 1 : 0);
|
|
|
c401cc |
+ } else {
|
|
|
c401cc |
+ char *buf = NULL;
|
|
|
c401cc |
+ VIR_FORCE_CLOSE(errfd[1]);
|
|
|
c401cc |
+
|
|
|
c401cc |
+ ignore_value(virFileReadHeaderFD(errfd[0], 1024, &buf));
|
|
|
c401cc |
+ ret = virProcessWait(child, NULL);
|
|
|
c401cc |
+ VIR_FREE(buf);
|
|
|
c401cc |
+ }
|
|
|
c401cc |
+
|
|
|
c401cc |
+cleanup:
|
|
|
c401cc |
+ VIR_FORCE_CLOSE(errfd[0]);
|
|
|
c401cc |
+ VIR_FORCE_CLOSE(errfd[1]);
|
|
|
c401cc |
+ return ret;
|
|
|
c401cc |
+}
|
|
|
c401cc |
+#else /* !HAVE_SETNS */
|
|
|
c401cc |
+int
|
|
|
c401cc |
+virProcessRunInMountNamespace(pid_t pid ATTRIBUTE_UNUSED,
|
|
|
c401cc |
+ virProcessNamespaceCallback cb ATTRIBUTE_UNUSED,
|
|
|
c401cc |
+ void *opaque ATTRIBUTE_UNUSED)
|
|
|
c401cc |
+{
|
|
|
c401cc |
+ virReportSystemError(ENOSYS, "%s",
|
|
|
c401cc |
+ _("Mount namespaces are not available on this platform"));
|
|
|
c401cc |
+ return -1;
|
|
|
c401cc |
+}
|
|
|
c401cc |
+#endif
|
|
|
c401cc |
diff --git a/src/util/virprocess.h b/src/util/virprocess.h
|
|
|
c401cc |
index 9f77bc5..5c173b0 100644
|
|
|
c401cc |
--- a/src/util/virprocess.h
|
|
|
c401cc |
+++ b/src/util/virprocess.h
|
|
|
c401cc |
@@ -60,4 +60,15 @@ int virProcessSetNamespaces(size_t nfdlist,
|
|
|
c401cc |
int virProcessSetMaxMemLock(pid_t pid, unsigned long long bytes);
|
|
|
c401cc |
int virProcessSetMaxProcesses(pid_t pid, unsigned int procs);
|
|
|
c401cc |
int virProcessSetMaxFiles(pid_t pid, unsigned int files);
|
|
|
c401cc |
+
|
|
|
c401cc |
+/* Callback to run code within the mount namespace tied to the given
|
|
|
c401cc |
+ * pid. This function must use only async-signal-safe functions, as
|
|
|
c401cc |
+ * it gets run after a fork of a multi-threaded process. The return
|
|
|
c401cc |
+ * value of this function is passed to _exit(), except that a
|
|
|
c401cc |
+ * negative value is treated as an error. */
|
|
|
c401cc |
+typedef int (*virProcessNamespaceCallback)(pid_t pid, void *opaque);
|
|
|
c401cc |
+
|
|
|
c401cc |
+int virProcessRunInMountNamespace(pid_t pid,
|
|
|
c401cc |
+ virProcessNamespaceCallback cb,
|
|
|
c401cc |
+ void *opaque);
|
|
|
c401cc |
#endif /* __VIR_PROCESS_H__ */
|
|
|
c401cc |
--
|
|
|
c401cc |
1.9.0
|
|
|
c401cc |
|