render / rpms / libvirt

Forked from rpms/libvirt 9 months ago
Clone
c401cc
From 30d4034b0903d7828dddd874a035cdc002ecc5be Mon Sep 17 00:00:00 2001
c401cc
Message-Id: <30d4034b0903d7828dddd874a035cdc002ecc5be@dist-git>
c401cc
From: Eric Blake <eblake@redhat.com>
c401cc
Date: Tue, 18 Feb 2014 15:45:39 -0700
c401cc
Subject: [PATCH] CVE-2013-6456: Avoid unsafe use of /proc/$PID/root in LXC
c401cc
 shutdown/reboot code
c401cc
c401cc
Use helper virProcessRunInMountNamespace in lxcDomainShutdownFlags and
c401cc
lxcDomainReboot.  Otherwise, a malicious guest could use symlinks
c401cc
to force the host to manipulate the wrong file in the host's namespace.
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: Eric Blake <eblake@redhat.com>
c401cc
(cherry picked from commit aebbcdd33c8c18891f0bdbbf8924599a28152c9c)
c401cc
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
c401cc
---
c401cc
 src/lxc/lxc_driver.c  | 38 ++++++++++++++++++++------------------
c401cc
 src/util/virinitctl.c | 26 ++++++++++----------------
c401cc
 src/util/virinitctl.h |  5 ++---
c401cc
 3 files changed, 32 insertions(+), 37 deletions(-)
c401cc
c401cc
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c
c401cc
index 33169c8..cf45d8d 100644
c401cc
--- a/src/lxc/lxc_driver.c
c401cc
+++ b/src/lxc/lxc_driver.c
c401cc
@@ -2742,12 +2742,20 @@ lxcConnectListAllDomains(virConnectPtr conn,
c401cc
 
c401cc
 
c401cc
 static int
c401cc
+lxcDomainInitctlCallback(pid_t pid ATTRIBUTE_UNUSED,
c401cc
+                         void *opaque)
c401cc
+{
c401cc
+    int *command = opaque;
c401cc
+    return virInitctlSetRunLevel(*command);
c401cc
+}
c401cc
+
c401cc
+
c401cc
+static int
c401cc
 lxcDomainShutdownFlags(virDomainPtr dom,
c401cc
                        unsigned int flags)
c401cc
 {
c401cc
     virLXCDomainObjPrivatePtr priv;
c401cc
     virDomainObjPtr vm;
c401cc
-    char *vroot = NULL;
c401cc
     int ret = -1;
c401cc
     int rc;
c401cc
 
c401cc
@@ -2774,16 +2782,14 @@ lxcDomainShutdownFlags(virDomainPtr dom,
c401cc
         goto cleanup;
c401cc
     }
c401cc
 
c401cc
-    if (virAsprintf(&vroot, "/proc/%llu/root",
c401cc
-                    (unsigned long long)priv->initpid) < 0)
c401cc
-        goto cleanup;
c401cc
-
c401cc
     if (flags == 0 ||
c401cc
         (flags & VIR_DOMAIN_SHUTDOWN_INITCTL)) {
c401cc
-        if ((rc = virInitctlSetRunLevel(VIR_INITCTL_RUNLEVEL_POWEROFF,
c401cc
-                                        vroot)) < 0) {
c401cc
+        int command = VIR_INITCTL_RUNLEVEL_POWEROFF;
c401cc
+
c401cc
+        if ((rc = virProcessRunInMountNamespace(priv->initpid,
c401cc
+                                                lxcDomainInitctlCallback,
c401cc
+                                                &command)) < 0)
c401cc
             goto cleanup;
c401cc
-        }
c401cc
         if (rc == 0 && flags != 0 &&
c401cc
             ((flags & ~VIR_DOMAIN_SHUTDOWN_INITCTL) == 0)) {
c401cc
             virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
c401cc
@@ -2809,7 +2815,6 @@ lxcDomainShutdownFlags(virDomainPtr dom,
c401cc
     ret = 0;
c401cc
 
c401cc
 cleanup:
c401cc
-    VIR_FREE(vroot);
c401cc
     if (vm)
c401cc
         virObjectUnlock(vm);
c401cc
     return ret;
c401cc
@@ -2821,13 +2826,13 @@ lxcDomainShutdown(virDomainPtr dom)
c401cc
     return lxcDomainShutdownFlags(dom, 0);
c401cc
 }
c401cc
 
c401cc
+
c401cc
 static int
c401cc
 lxcDomainReboot(virDomainPtr dom,
c401cc
                 unsigned int flags)
c401cc
 {
c401cc
     virLXCDomainObjPrivatePtr priv;
c401cc
     virDomainObjPtr vm;
c401cc
-    char *vroot = NULL;
c401cc
     int ret = -1;
c401cc
     int rc;
c401cc
 
c401cc
@@ -2854,16 +2859,14 @@ lxcDomainReboot(virDomainPtr dom,
c401cc
         goto cleanup;
c401cc
     }
c401cc
 
c401cc
-    if (virAsprintf(&vroot, "/proc/%llu/root",
c401cc
-                    (unsigned long long)priv->initpid) < 0)
c401cc
-        goto cleanup;
c401cc
-
c401cc
     if (flags == 0 ||
c401cc
         (flags & VIR_DOMAIN_REBOOT_INITCTL)) {
c401cc
-        if ((rc = virInitctlSetRunLevel(VIR_INITCTL_RUNLEVEL_REBOOT,
c401cc
-                                        vroot)) < 0) {
c401cc
+        int command = VIR_INITCTL_RUNLEVEL_REBOOT;
c401cc
+
c401cc
+        if ((rc = virProcessRunInMountNamespace(priv->initpid,
c401cc
+                                                lxcDomainInitctlCallback,
c401cc
+                                                &command)) < 0)
c401cc
             goto cleanup;
c401cc
-        }
c401cc
         if (rc == 0 && flags != 0 &&
c401cc
             ((flags & ~VIR_DOMAIN_SHUTDOWN_INITCTL) == 0)) {
c401cc
             virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
c401cc
@@ -2889,7 +2892,6 @@ lxcDomainReboot(virDomainPtr dom,
c401cc
     ret = 0;
c401cc
 
c401cc
 cleanup:
c401cc
-    VIR_FREE(vroot);
c401cc
     if (vm)
c401cc
         virObjectUnlock(vm);
c401cc
     return ret;
c401cc
diff --git a/src/util/virinitctl.c b/src/util/virinitctl.c
c401cc
index 64bc23a..4deeb19 100644
c401cc
--- a/src/util/virinitctl.c
c401cc
+++ b/src/util/virinitctl.c
c401cc
@@ -111,16 +111,18 @@ struct virInitctlRequest {
c401cc
 # endif
c401cc
 
c401cc
 /*
c401cc
- * Send a message to init to change the runlevel
c401cc
+ * Send a message to init to change the runlevel. This function is
c401cc
+ * asynchronous-signal-safe (thus safe to use after fork of a
c401cc
+ * multithreaded parent) - which is good, because it should only be
c401cc
+ * used after forking and entering correct namespace.
c401cc
  *
c401cc
  * Returns 1 on success, 0 if initctl does not exist, -1 on error
c401cc
  */
c401cc
-int virInitctlSetRunLevel(virInitctlRunLevel level,
c401cc
-                          const char *vroot)
c401cc
+int
c401cc
+virInitctlSetRunLevel(virInitctlRunLevel level)
c401cc
 {
c401cc
     struct virInitctlRequest req;
c401cc
     int fd = -1;
c401cc
-    char *path = NULL;
c401cc
     int ret = -1;
c401cc
 
c401cc
     memset(&req, 0, sizeof(req));
c401cc
@@ -131,36 +133,28 @@ int virInitctlSetRunLevel(virInitctlRunLevel level,
c401cc
     /* Yes it is an 'int' field, but wants a numeric character. Go figure */
c401cc
     req.runlevel = '0' + level;
c401cc
 
c401cc
-    if (vroot) {
c401cc
-        if (virAsprintf(&path, "%s/%s", vroot, VIR_INITCTL_FIFO) < 0)
c401cc
-            return -1;
c401cc
-    } else {
c401cc
-        if (VIR_STRDUP(path, VIR_INITCTL_FIFO) < 0)
c401cc
-            return -1;
c401cc
-    }
c401cc
-
c401cc
-    if ((fd = open(path, O_WRONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY)) < 0) {
c401cc
+    if ((fd = open(VIR_INITCTL_FIFO,
c401cc
+                   O_WRONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY)) < 0) {
c401cc
         if (errno == ENOENT) {
c401cc
             ret = 0;
c401cc
             goto cleanup;
c401cc
         }
c401cc
         virReportSystemError(errno,
c401cc
                              _("Cannot open init control %s"),
c401cc
-                             path);
c401cc
+                             VIR_INITCTL_FIFO);
c401cc
         goto cleanup;
c401cc
     }
c401cc
 
c401cc
     if (safewrite(fd, &req, sizeof(req)) != sizeof(req)) {
c401cc
         virReportSystemError(errno,
c401cc
                              _("Failed to send request to init control %s"),
c401cc
-                             path);
c401cc
+                             VIR_INITCTL_FIFO);
c401cc
         goto cleanup;
c401cc
     }
c401cc
 
c401cc
     ret = 1;
c401cc
 
c401cc
 cleanup:
c401cc
-    VIR_FREE(path);
c401cc
     VIR_FORCE_CLOSE(fd);
c401cc
     return ret;
c401cc
 }
c401cc
diff --git a/src/util/virinitctl.h b/src/util/virinitctl.h
c401cc
index 862539f..6aeb1a6 100644
c401cc
--- a/src/util/virinitctl.h
c401cc
+++ b/src/util/virinitctl.h
c401cc
@@ -1,7 +1,7 @@
c401cc
 /*
c401cc
  * virinitctl.h: API for talking to init systems via initctl
c401cc
  *
c401cc
- * Copyright (C) 2012 Red Hat, Inc.
c401cc
+ * Copyright (C) 2012-2014 Red Hat, Inc.
c401cc
  *
c401cc
  * This library is free software; you can redistribute it and/or
c401cc
  * modify it under the terms of the GNU Lesser General Public
c401cc
@@ -37,7 +37,6 @@ enum virInitctlRunLevel {
c401cc
     VIR_INITCTL_RUNLEVEL_LAST
c401cc
 };
c401cc
 
c401cc
-int virInitctlSetRunLevel(virInitctlRunLevel level,
c401cc
-                          const char *vroot);
c401cc
+int virInitctlSetRunLevel(virInitctlRunLevel level);
c401cc
 
c401cc
 #endif
c401cc
-- 
c401cc
1.9.0
c401cc