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