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