]> xenbits.xensource.com Git - libvirt.git/commitdiff
Include process start time when doing polkit checks
authorDaniel P. Berrange <berrange@redhat.com>
Thu, 25 Apr 2013 16:05:00 +0000 (17:05 +0100)
committerDaniel P. Berrange <berrange@redhat.com>
Wed, 8 May 2013 09:47:45 +0000 (10:47 +0100)
Since PIDs can be reused, polkit prefers to be given
a (PID,start time) pair. If given a PID on its own,
it will attempt to lookup the start time in /proc/pid/stat,
though this is subject to races.

It is safer if the client app resolves the PID start
time itself, because as long as the app has the client
socket open, the client PID won't be reused.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
12 files changed:
daemon/remote.c
src/libvirt_private.syms
src/locking/lock_daemon.c
src/rpc/virnetserverclient.c
src/rpc/virnetserverclient.h
src/rpc/virnetsocket.c
src/rpc/virnetsocket.h
src/util/viridentity.h
src/util/virprocess.c
src/util/virprocess.h
src/util/virstring.c
src/util/virstring.h

index 1f7cbe98732773e96418f2e58d6f17da024c0440..3b6446d50b3cfbc0228b9de34f942ae98c8c3fca 100644 (file)
@@ -2339,6 +2339,7 @@ remoteDispatchAuthList(virNetServerPtr server ATTRIBUTE_UNUSED,
     uid_t callerUid;
     gid_t callerGid;
     pid_t callerPid;
+    unsigned long long timestamp;
 
     /* If the client is root then we want to bypass the
      * policykit auth to avoid root being denied if
@@ -2346,7 +2347,7 @@ remoteDispatchAuthList(virNetServerPtr server ATTRIBUTE_UNUSED,
      */
     if (auth == VIR_NET_SERVER_SERVICE_AUTH_POLKIT) {
         if (virNetServerClientGetUNIXIdentity(client, &callerUid, &callerGid,
-                                              &callerPid) < 0) {
+                                              &callerPid, &timestamp) < 0) {
             /* Don't do anything on error - it'll be validated at next
              * phase of auth anyway */
             virResetLastError();
@@ -2772,6 +2773,7 @@ remoteDispatchAuthPolkit(virNetServerPtr server ATTRIBUTE_UNUSED,
     pid_t callerPid = -1;
     gid_t callerGid = -1;
     uid_t callerUid = -1;
+    unsigned long long timestamp;
     const char *action;
     int status = -1;
     char *ident = NULL;
@@ -2797,7 +2799,7 @@ remoteDispatchAuthPolkit(virNetServerPtr server ATTRIBUTE_UNUSED,
     }
 
     if (virNetServerClientGetUNIXIdentity(client, &callerUid, &callerGid,
-                                          &callerPid) < 0) {
+                                          &callerPid, &timestamp) < 0) {
         goto authfail;
     }
 
@@ -2805,7 +2807,11 @@ remoteDispatchAuthPolkit(virNetServerPtr server ATTRIBUTE_UNUSED,
              (long long) callerPid, callerUid);
 
     virCommandAddArg(cmd, "--process");
-    virCommandAddArgFormat(cmd, "%lld", (long long) callerPid);
+    if (timestamp != 0) {
+        virCommandAddArgFormat(cmd, "%lld,%llu", (long long) callerPid, timestamp);
+    } else {
+        virCommandAddArgFormat(cmd, "%lld", (long long) callerPid);
+    }
     virCommandAddArg(cmd, "--allow-user-interaction");
 
     if (virAsprintf(&ident, "pid:%lld,uid:%d",
index babdde7c5b0d95e0991264c1d709689c661fc2fc..bb70595e0b4a258f3b567019cac671e80222b3b2 100644 (file)
@@ -1755,6 +1755,7 @@ virStrdup;
 virStringArrayHasString;
 virStringFreeList;
 virStringJoin;
+virStringListLength;
 virStringSplit;
 virStrncpy;
 virStrndup;
index 0d8ef399ff355ec5bbd2b77671597b839720a568..20e2c04dc3075a6b8dbc0b307dd6c4b7cceab249 100644 (file)
@@ -783,6 +783,7 @@ virLockDaemonClientNew(virNetServerClientPtr client,
     virLockDaemonClientPtr priv;
     uid_t clientuid;
     gid_t clientgid;
+    unsigned long long timestamp;
     bool privileged = opaque != NULL;
 
     if (VIR_ALLOC(priv) < 0) {
@@ -799,7 +800,8 @@ virLockDaemonClientNew(virNetServerClientPtr client,
     if (virNetServerClientGetUNIXIdentity(client,
                                           &clientuid,
                                           &clientgid,
-                                          &priv->clientPid) < 0)
+                                          &priv->clientPid,
+                                          &timestamp) < 0)
         goto error;
 
     VIR_DEBUG("New client pid %llu uid %llu",
index 5abf7b0d9a286e596898e2cbfa8cfe11127e7f73..6d7c439f35258b6e3bcdb6fdc5ee776451271a78 100644 (file)
@@ -636,12 +636,15 @@ bool virNetServerClientIsLocal(virNetServerClientPtr client)
 
 
 int virNetServerClientGetUNIXIdentity(virNetServerClientPtr client,
-                                      uid_t *uid, gid_t *gid, pid_t *pid)
+                                      uid_t *uid, gid_t *gid, pid_t *pid,
+                                      unsigned long long *timestamp)
 {
     int ret = -1;
     virObjectLock(client);
     if (client->sock)
-        ret = virNetSocketGetUNIXIdentity(client->sock, uid, gid, pid);
+        ret = virNetSocketGetUNIXIdentity(client->sock,
+                                          uid, gid, pid,
+                                          timestamp);
     virObjectUnlock(client);
     return ret;
 }
@@ -651,6 +654,7 @@ static virIdentityPtr
 virNetServerClientCreateIdentity(virNetServerClientPtr client)
 {
     char *processid = NULL;
+    char *processtime = NULL;
     char *username = NULL;
     char *groupname = NULL;
 #if WITH_SASL
@@ -664,15 +668,23 @@ virNetServerClientCreateIdentity(virNetServerClientPtr client)
         gid_t gid;
         uid_t uid;
         pid_t pid;
-        if (virNetSocketGetUNIXIdentity(client->sock, &uid, &gid, &pid) < 0)
+        unsigned long long timestamp;
+        if (virNetSocketGetUNIXIdentity(client->sock,
+                                        &uid, &gid, &pid,
+                                        &timestamp) < 0)
             goto cleanup;
 
         if (!(username = virGetUserName(uid)))
             goto cleanup;
         if (!(groupname = virGetGroupName(gid)))
             goto cleanup;
-        if (virAsprintf(&processid, "%lld",
-                        (long long)pid) < 0) {
+        if (virAsprintf(&processid, "%llu",
+                        (unsigned long long)pid) < 0) {
+            virReportOOMError();
+            goto cleanup;
+        }
+        if (virAsprintf(&processtime, "%llu",
+                        timestamp) < 0) {
             virReportOOMError();
             goto cleanup;
         }
@@ -722,6 +734,11 @@ virNetServerClientCreateIdentity(virNetServerClientPtr client)
                            VIR_IDENTITY_ATTR_UNIX_PROCESS_ID,
                            processid) < 0)
         goto error;
+    if (processtime &&
+        virIdentitySetAttr(ret,
+                           VIR_IDENTITY_ATTR_UNIX_PROCESS_TIME,
+                           processtime) < 0)
+        goto error;
 #if HAVE_SASL
     if (saslname &&
         virIdentitySetAttr(ret,
@@ -744,6 +761,7 @@ cleanup:
     VIR_FREE(username);
     VIR_FREE(groupname);
     VIR_FREE(processid);
+    VIR_FREE(processtime);
     VIR_FREE(seccontext);
 #if HAVE_SASL
     VIR_FREE(saslname);
index 85369941f938849a1eb99d1e8917fec91b85292a..8d0fd18fe35b1fa0c1c234f9b5590242d8dd35e7 100644 (file)
@@ -99,7 +99,8 @@ bool virNetServerClientIsSecure(virNetServerClientPtr client);
 bool virNetServerClientIsLocal(virNetServerClientPtr client);
 
 int virNetServerClientGetUNIXIdentity(virNetServerClientPtr client,
-                                      uid_t *uid, gid_t *gid, pid_t *pid);
+                                      uid_t *uid, gid_t *gid, pid_t *pid,
+                                      unsigned long long *timestamp);
 
 int virNetServerClientGetSELinuxContext(virNetServerClientPtr client,
                                         char **context);
index eb88e274656aced0294d4e0d5b8b619bddcb53b9..67eb496a9effc106d7faeb12248b888df078973f 100644 (file)
@@ -1103,31 +1103,41 @@ int virNetSocketGetPort(virNetSocketPtr sock)
 int virNetSocketGetUNIXIdentity(virNetSocketPtr sock,
                                 uid_t *uid,
                                 gid_t *gid,
-                                pid_t *pid)
+                                pid_t *pid,
+                                unsigned long long *timestamp)
 {
     struct ucred cr;
     socklen_t cr_len = sizeof(cr);
+    int ret = -1;
+
     virObjectLock(sock);
 
     if (getsockopt(sock->fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) < 0) {
         virReportSystemError(errno, "%s",
                              _("Failed to get client socket identity"));
-        virObjectUnlock(sock);
-        return -1;
+        goto cleanup;
     }
 
+    if (virProcessGetStartTime(cr.pid, timestamp) < 0)
+        goto cleanup;
+
     *pid = cr.pid;
     *uid = cr.uid;
     *gid = cr.gid;
 
+    ret = 0;
+
+cleanup:
     virObjectUnlock(sock);
-    return 0;
+    return ret;
 }
 #elif defined(LOCAL_PEERCRED)
+
 int virNetSocketGetUNIXIdentity(virNetSocketPtr sock,
                                 uid_t *uid,
                                 gid_t *gid,
-                                pid_t *pid)
+                                pid_t *pid,
+                                unsigned long long *timestamp ATTRIBUTE_UNUSED)
 {
     struct xucred cr;
     socklen_t cr_len = sizeof(cr);
@@ -1151,7 +1161,8 @@ int virNetSocketGetUNIXIdentity(virNetSocketPtr sock,
 int virNetSocketGetUNIXIdentity(virNetSocketPtr sock ATTRIBUTE_UNUSED,
                                 uid_t *uid ATTRIBUTE_UNUSED,
                                 gid_t *gid ATTRIBUTE_UNUSED,
-                                pid_t *pid ATTRIBUTE_UNUSED)
+                                pid_t *pid ATTRIBUTE_UNUSED,
+                                unsigned long long *timestamp ATTRIBUTE_UNUSED)
 {
     /* XXX Many more OS support UNIX socket credentials we could port to. See dbus ....*/
     virReportSystemError(ENOSYS, "%s",
index 7bced141e7d4240a8761d9d4f7178b653e49fdc8..ea42081d49ebbb02e7e7016a943fadbd14b36cdf 100644 (file)
@@ -113,7 +113,8 @@ int virNetSocketGetPort(virNetSocketPtr sock);
 int virNetSocketGetUNIXIdentity(virNetSocketPtr sock,
                                 uid_t *uid,
                                 gid_t *gid,
-                                pid_t *pid);
+                                pid_t *pid,
+                                unsigned long long *timestamp);
 int virNetSocketGetSELinuxContext(virNetSocketPtr sock,
                                   char **context);
 
index d7293be4433de766807b5978e358c024e9ad777d..4bae8d63d7cb900f14e7b1192c4b9ce4dafdaedb 100644 (file)
@@ -31,6 +31,7 @@ typedef enum {
       VIR_IDENTITY_ATTR_UNIX_USER_NAME,
       VIR_IDENTITY_ATTR_UNIX_GROUP_NAME,
       VIR_IDENTITY_ATTR_UNIX_PROCESS_ID,
+      VIR_IDENTITY_ATTR_UNIX_PROCESS_TIME,
       VIR_IDENTITY_ATTR_SASL_USER_NAME,
       VIR_IDENTITY_ATTR_X509_DISTINGUISHED_NAME,
       VIR_IDENTITY_ATTR_SELINUX_CONTEXT,
index ac93c4a7b8993651c578a1cd9c76a34968736f23..99db00335228a0ff6f5772b63d34222dda0a3ec8 100644 (file)
 #endif
 #include <sched.h>
 
+#ifdef __FreeBSD__
+# include <sys/param.h>
+# include <sys/sysctl.h>
+# include <sys/user.h>
+#endif
+
+#include "viratomic.h"
 #include "virprocess.h"
 #include "virerror.h"
 #include "viralloc.h"
@@ -756,3 +763,112 @@ virProcessSetMaxFiles(pid_t pid ATTRIBUTE_UNUSED, unsigned int files)
     return -1;
 }
 #endif /* ! (HAVE_SETRLIMIT && defined(RLIMIT_NOFILE)) */
+
+#ifdef __linux__
+/*
+ * Port of code from polkitunixprocess.c under terms
+ * of the LGPLv2+
+ */
+int virProcessGetStartTime(pid_t pid,
+                           unsigned long long *timestamp)
+{
+    char *filename = NULL;
+    char *buf = NULL;
+    char *tmp;
+    int ret = -1;
+    int len;
+    char **tokens = NULL;
+
+    if (virAsprintf(&filename, "/proc/%llu/stat",
+                    (unsigned long long)pid) < 0) {
+        virReportOOMError();
+        return -1;
+    }
+
+    if ((len = virFileReadAll(filename, 1024, &buf)) < 0)
+        goto cleanup;
+
+    /* start time is the token at index 19 after the '(process name)' entry - since only this
+     * field can contain the ')' character, search backwards for this to avoid malicious
+     * processes trying to fool us
+     */
+
+    if (!(tmp = strrchr(buf, ')'))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Cannot find start time in %s"),
+                       filename);
+        goto cleanup;
+    }
+    tmp += 2; /* skip ') ' */
+    if ((tmp - buf) >= len) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Cannot find start time in %s"),
+                       filename);
+        goto cleanup;
+    }
+
+    tokens = virStringSplit(tmp, " ", 0);
+
+    if (virStringListLength(tokens) < 20) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Cannot find start time in %s"),
+                       filename);
+        goto cleanup;
+    }
+
+    if (virStrToLong_ull(tokens[19],
+                         NULL,
+                         10,
+                         timestamp) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Cannot parse start time %s in %s"),
+                       tokens[19], filename);
+        goto cleanup;
+    }
+
+    ret = 0;
+
+cleanup:
+    virStringFreeList(tokens);
+    VIR_FREE(filename);
+    VIR_FREE(buf);
+    return ret;
+}
+#elif defined(__FreeBSD__)
+int virProcessGetStartTime(pid_t pid,
+                           unsigned long long *timestamp)
+{
+    struct kinfo_proc p;
+    int mib[4];
+    size_t len = 4;
+
+    sysctlnametomib("kern.proc.pid", mib, &len);
+
+    len = sizeof(struct kinfo_proc);
+    mib[3] = pid;
+
+    if (sysctl(mib, 4, &p, &len, NULL, 0) < 0) {
+        virReportSystemError(errno, "%s",
+                             _("Unable to query process ID start time"));
+        return -1;
+    }
+
+    *timestamp = (unsigned long long)p.ki_start.tv_sec;
+
+    return 0;
+
+}
+#else
+int virProcessGetStartTime(pid_t pid,
+                           unsigned long long *timestamp)
+{
+    static int warned = 0;
+    if (virAtomicIntInc(&warned) == 1) {
+        VIR_WARN("Process start time of pid %llu not available on this platform",
+                 (unsigned long long)pid);
+        warned = true;
+    }
+    *timestamp = 0;
+    return 0;
+}
+#endif
index 5dea3349028d4c21ef9ca1b15e6a74e011ce0527..9f77bc54916912676388e4d0919360f9d2110643 100644 (file)
@@ -47,6 +47,9 @@ int virProcessGetAffinity(pid_t pid,
                           virBitmapPtr *map,
                           int maxcpu);
 
+int virProcessGetStartTime(pid_t pid,
+                           unsigned long long *timestamp);
+
 int virProcessGetNamespaces(pid_t pid,
                             size_t *nfdlist,
                             int **fdlist);
index ec124623b04b184e78e3f5e4c526b5365c809d59..1640d8c02aa75afc2e2de6f448255b374a38769a 100644 (file)
@@ -593,3 +593,14 @@ virStrndup(char **dest,
 
    return 1;
 }
+
+
+size_t virStringListLength(char **strings)
+{
+    size_t i = 0;
+
+    while (strings && strings[i])
+        i++;
+
+    return i;
+}
index 5bd6c185b0467d40a00afa168796bfb02f4d920c..534ce9139e94a37be675e25be2c1325186fc25e7 100644 (file)
@@ -88,7 +88,6 @@ char *virStrcpy(char *dest, const char *src, size_t destbytes)
     ATTRIBUTE_RETURN_CHECK;
 # define virStrcpyStatic(dest, src) virStrcpy((dest), (src), sizeof(dest))
 
-
 /* Don't call these directly - use the macros below */
 int virStrdup(char **dest, const char *src, bool report, int domcode,
               const char *filename, const char *funcname, size_t linenr)
@@ -159,4 +158,7 @@ int virStrndup(char **dest, const char *src, size_t n, bool report, int domcode,
  */
 # define VIR_STRNDUP_QUIET(dst, src, n) virStrndup(&(dst), src, n, false, \
                                                    0, NULL, NULL, 0)
+
+size_t virStringListLength(char **strings);
+
 #endif /* __VIR_STRING_H__ */