43fe83
From 13572b2c50e079c5618870af7ae64bf3a73154d7 Mon Sep 17 00:00:00 2001
43fe83
Message-Id: <13572b2c50e079c5618870af7ae64bf3a73154d7.1379597659.git.jdenemar@redhat.com>
43fe83
From: "Daniel P. Berrange" <berrange@redhat.com>
43fe83
Date: Wed, 28 Aug 2013 15:25:40 +0100
43fe83
Subject: [PATCH] Add support for using 3-arg pkcheck syntax for process
43fe83
43fe83
CVE-2013-4311
43fe83
43fe83
With the existing pkcheck (pid, start time) tuple for identifying
43fe83
the process, there is a race condition, where a process can make
43fe83
a libvirt RPC call and in another thread exec a setuid application,
43fe83
causing it to change to effective UID 0. This in turn causes polkit
43fe83
to do its permission check based on the wrong UID.
43fe83
43fe83
To address this, libvirt must get the UID the caller had at time
43fe83
of connect() (from SO_PEERCRED) and pass a (pid, start time, uid)
43fe83
triple to the pkcheck program.
43fe83
43fe83
This fix requires that libvirt is re-built against a version of
43fe83
polkit that has the fix for its CVE-2013-4288, so that libvirt
43fe83
can see 'pkg-config --variable pkcheck_supports_uid polkit-gobject-1'
43fe83
43fe83
Signed-off-by: Colin Walters <walters@redhat.com>
43fe83
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
43fe83
(cherry picked from commit 922b7fda77b094dbf022d625238262ea05335666)
43fe83
---
43fe83
 configure.ac                       |  8 ++++++++
43fe83
 daemon/remote.c                    | 22 ++++++++++++++++++---
43fe83
 libvirt.spec.in                    |  7 ++++---
43fe83
 src/access/viraccessdriverpolkit.c | 40 +++++++++++++++++++++++++++++++++-----
43fe83
 4 files changed, 66 insertions(+), 11 deletions(-)
43fe83
43fe83
diff --git a/configure.ac b/configure.ac
43fe83
index 917db6a..7dd6ca3 100644
43fe83
--- a/configure.ac
43fe83
+++ b/configure.ac
43fe83
@@ -1166,6 +1166,14 @@ if test "x$with_polkit" = "xyes" || test "x$with_polkit" = "xcheck"; then
43fe83
   AC_PATH_PROG([PKCHECK_PATH],[pkcheck], [], [/usr/sbin:$PATH])
43fe83
   if test "x$PKCHECK_PATH" != "x" ; then
43fe83
     AC_DEFINE_UNQUOTED([PKCHECK_PATH],["$PKCHECK_PATH"],[Location of pkcheck program])
43fe83
+    AC_MSG_CHECKING([whether pkcheck supports uid value])
43fe83
+    pkcheck_supports_uid=`$PKG_CONFIG --variable pkcheck_supports_uid polkit-gobject-1`
43fe83
+    if test "x$pkcheck_supports_uid" = "xtrue"; then
43fe83
+      AC_MSG_RESULT([yes])
43fe83
+      AC_DEFINE_UNQUOTED([PKCHECK_SUPPORTS_UID], 1, [Pass uid to pkcheck])
43fe83
+    else
43fe83
+      AC_MSG_RESULT([no])
43fe83
+    fi
43fe83
     AC_DEFINE_UNQUOTED([WITH_POLKIT], 1,
43fe83
         [use PolicyKit for UNIX socket access checks])
43fe83
     AC_DEFINE_UNQUOTED([WITH_POLKIT1], 1,
43fe83
diff --git a/daemon/remote.c b/daemon/remote.c
43fe83
index 6ace7af..b5395dd 100644
43fe83
--- a/daemon/remote.c
43fe83
+++ b/daemon/remote.c
43fe83
@@ -2738,10 +2738,12 @@ remoteDispatchAuthPolkit(virNetServerPtr server ATTRIBUTE_UNUSED,
43fe83
     int status = -1;
43fe83
     char *ident = NULL;
43fe83
     bool authdismissed = 0;
43fe83
+    bool supportsuid = false;
43fe83
     char *pkout = NULL;
43fe83
     struct daemonClientPrivate *priv =
43fe83
         virNetServerClientGetPrivateData(client);
43fe83
     virCommandPtr cmd = NULL;
43fe83
+    static bool polkitInsecureWarned;
43fe83
 
43fe83
     virMutexLock(&priv->lock);
43fe83
     action = virNetServerClientGetReadonly(client) ?
43fe83
@@ -2763,14 +2765,28 @@ remoteDispatchAuthPolkit(virNetServerPtr server ATTRIBUTE_UNUSED,
43fe83
         goto authfail;
43fe83
     }
43fe83
 
43fe83
+    if (timestamp == 0) {
43fe83
+        VIR_WARN("Failing polkit auth due to missing client (pid=%lld) start time",
43fe83
+                 (long long)callerPid);
43fe83
+        goto authfail;
43fe83
+    }
43fe83
+
43fe83
     VIR_INFO("Checking PID %lld running as %d",
43fe83
              (long long) callerPid, callerUid);
43fe83
 
43fe83
     virCommandAddArg(cmd, "--process");
43fe83
-    if (timestamp != 0) {
43fe83
-        virCommandAddArgFormat(cmd, "%lld,%llu", (long long) callerPid, timestamp);
43fe83
+# ifdef PKCHECK_SUPPORTS_UID
43fe83
+    supportsuid = true;
43fe83
+# endif
43fe83
+    if (supportsuid) {
43fe83
+        virCommandAddArgFormat(cmd, "%lld,%llu,%lu",
43fe83
+                               (long long) callerPid, timestamp, (unsigned long) callerUid);
43fe83
     } else {
43fe83
-        virCommandAddArgFormat(cmd, "%lld", (long long) callerPid);
43fe83
+        if (!polkitInsecureWarned) {
43fe83
+            VIR_WARN("No support for caller UID with pkcheck. This deployment is known to be insecure.");
43fe83
+            polkitInsecureWarned = true;
43fe83
+        }
43fe83
+        virCommandAddArgFormat(cmd, "%lld,%llu", (long long) callerPid, timestamp);
43fe83
     }
43fe83
     virCommandAddArg(cmd, "--allow-user-interaction");
43fe83
 
43fe83
diff --git a/src/access/viraccessdriverpolkit.c b/src/access/viraccessdriverpolkit.c
43fe83
index b472bc3..ff82583 100644
43fe83
--- a/src/access/viraccessdriverpolkit.c
43fe83
+++ b/src/access/viraccessdriverpolkit.c
43fe83
@@ -72,8 +72,12 @@ static char *
43fe83
 virAccessDriverPolkitFormatProcess(const char *actionid)
43fe83
 {
43fe83
     virIdentityPtr identity = virIdentityGetCurrent();
43fe83
-    const char *process = NULL;
43fe83
+    const char *callerPid = NULL;
43fe83
+    const char *callerTime = NULL;
43fe83
+    const char *callerUid = NULL;
43fe83
     char *ret = NULL;
43fe83
+    bool supportsuid = false;
43fe83
+    static bool polkitInsecureWarned;
43fe83
 
43fe83
     if (!identity) {
43fe83
         virAccessError(VIR_ERR_ACCESS_DENIED,
43fe83
@@ -81,17 +85,43 @@ virAccessDriverPolkitFormatProcess(const char *actionid)
43fe83
                        actionid);
43fe83
         return NULL;
43fe83
     }
43fe83
-    if (virIdentityGetAttr(identity, VIR_IDENTITY_ATTR_UNIX_PROCESS_ID, &process) < 0)
43fe83
+    if (virIdentityGetAttr(identity, VIR_IDENTITY_ATTR_UNIX_PROCESS_ID, &callerPid) < 0)
43fe83
+        goto cleanup;
43fe83
+    if (virIdentityGetAttr(identity, VIR_IDENTITY_ATTR_UNIX_PROCESS_TIME, &callerTime) < 0)
43fe83
+        goto cleanup;
43fe83
+    if (virIdentityGetAttr(identity, VIR_IDENTITY_ATTR_UNIX_USER_ID, &callerUid) < 0)
43fe83
         goto cleanup;
43fe83
 
43fe83
-    if (!process) {
43fe83
+    if (!callerPid) {
43fe83
         virAccessError(VIR_ERR_INTERNAL_ERROR, "%s",
43fe83
                        _("No UNIX process ID available"));
43fe83
         goto cleanup;
43fe83
     }
43fe83
-
43fe83
-    if (VIR_STRDUP(ret, process) < 0)
43fe83
+    if (!callerTime) {
43fe83
+        virAccessError(VIR_ERR_INTERNAL_ERROR, "%s",
43fe83
+                       _("No UNIX process start time available"));
43fe83
+        goto cleanup;
43fe83
+    }
43fe83
+    if (!callerUid) {
43fe83
+        virAccessError(VIR_ERR_INTERNAL_ERROR, "%s",
43fe83
+                       _("No UNIX caller UID available"));
43fe83
         goto cleanup;
43fe83
+    }
43fe83
+
43fe83
+#ifdef PKCHECK_SUPPORTS_UID
43fe83
+    supportsuid = true;
43fe83
+#endif
43fe83
+    if (supportsuid) {
43fe83
+        if (virAsprintf(&ret, "%s,%s,%s", callerPid, callerTime, callerUid) < 0)
43fe83
+            goto cleanup;
43fe83
+    } else {
43fe83
+        if (!polkitInsecureWarned) {
43fe83
+            VIR_WARN("No support for caller UID with pkcheck. This deployment is known to be insecure.");
43fe83
+            polkitInsecureWarned = true;
43fe83
+        }
43fe83
+        if (virAsprintf(&ret, "%s,%s", callerPid, callerTime) < 0)
43fe83
+            goto cleanup;
43fe83
+    }
43fe83
 
43fe83
 cleanup:
43fe83
     virObjectUnref(identity);
43fe83
-- 
43fe83
1.8.3.2
43fe83