[libvirt] [v0.9.12-maint 7/9] Include process start time when doing polkit checks

Guido Günther agx at sigxcpu.org
Thu Sep 19 12:22:11 UTC 2013


On Wed, Sep 18, 2013 at 09:14:23PM -0600, Eric Blake wrote:
> From: "Daniel P. Berrange" <berrange at redhat.com>
> 
> 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 at redhat.com>
> (cherry picked from commit 979e9c56a7aadf2dcfbddd1abfbad594b78b4468)
> Signed-off-by: Eric Blake <eblake at redhat.com>
> 
> Conflicts:
> 	src/libvirt_private.syms - not backported
> 	src/locking/lock_daemon.c - not backported
> 	src/rpc/virnetserverclient.c
> 	src/rpc/virnetsocket.c
> 	src/rpc/virnetsocket.h
> 	src/util/viridentity.h - not backported
> 	src/util/virprocess.c
> 	src/util/virprocess.h
> 	src/util/virstring.c
> 	src/util/virstring.h
> 
> Most conflicts were contextual (this patch adds new functions,
> but upstream intermediate patches not backported here also added
> new features, and the resolution was picking out just the portions
> needed by this commit).  virnetsocket.c also had slightly
> different locking semantics.
> ---
>  daemon/remote.c              |  12 +++--
>  src/rpc/virnetserverclient.c |   8 ++-
>  src/rpc/virnetserverclient.h |   3 +-
>  src/rpc/virnetsocket.c       |  19 +++++--
>  src/rpc/virnetsocket.h       |   3 +-
>  src/util/virprocess.c        | 118 +++++++++++++++++++++++++++++++++++++++++++
>  src/util/virprocess.h        |   3 ++
>  src/util/virstring.c         |  11 ++++
>  src/util/virstring.h         |   2 +
>  9 files changed, 167 insertions(+), 12 deletions(-)
> 
> diff --git a/daemon/remote.c b/daemon/remote.c
> index 435f663..c65e4e4 100644
> --- a/daemon/remote.c
> +++ b/daemon/remote.c
> @@ -2119,6 +2119,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
> @@ -2126,7 +2127,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();
> @@ -2554,6 +2555,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;
> @@ -2579,7 +2581,7 @@ remoteDispatchAuthPolkit(virNetServerPtr server ATTRIBUTE_UNUSED,
>      }
> 
>      if (virNetServerClientGetUNIXIdentity(client, &callerUid, &callerGid,
> -                                          &callerPid) < 0) {
> +                                          &callerPid, &timestamp) < 0) {
>          goto authfail;
>      }
> 
> @@ -2587,7 +2589,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",
> diff --git a/src/rpc/virnetserverclient.c b/src/rpc/virnetserverclient.c
> index 3838136..73aa28d 100644
> --- a/src/rpc/virnetserverclient.c
> +++ b/src/rpc/virnetserverclient.c
> @@ -448,16 +448,20 @@ int virNetServerClientGetFD(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;
>      virNetServerClientLock(client);
>      if (client->sock)
> -        ret = virNetSocketGetUNIXIdentity(client->sock, uid, gid, pid);
> +        ret = virNetSocketGetUNIXIdentity(client->sock,
> +                                          uid, gid, pid,
> +                                          timestamp);
>      virNetServerClientUnlock(client);
>      return ret;
>  }
> 
> +
>  bool virNetServerClientIsSecure(virNetServerClientPtr client)
>  {
>      bool secure = false;
> diff --git a/src/rpc/virnetserverclient.h b/src/rpc/virnetserverclient.h
> index 154a160..9fc05c2 100644
> --- a/src/rpc/virnetserverclient.h
> +++ b/src/rpc/virnetserverclient.h
> @@ -71,7 +71,8 @@ int virNetServerClientSetIdentity(virNetServerClientPtr client,
>  const char *virNetServerClientGetIdentity(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);
> 
>  void virNetServerClientRef(virNetServerClientPtr client);
> 
> diff --git a/src/rpc/virnetsocket.c b/src/rpc/virnetsocket.c
> index e82f375..1b4f894 100644
> --- a/src/rpc/virnetsocket.c
> +++ b/src/rpc/virnetsocket.c
> @@ -828,31 +828,40 @@ 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;
> +
>      virMutexLock(&sock->lock);
> 
>      if (getsockopt(sock->fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) < 0) {
>          virReportSystemError(errno, "%s",
>                               _("Failed to get client socket identity"));
> -        virMutexUnlock(&sock->lock);
> -        return -1;
> +        goto cleanup;
>      }
> 
> +    if (virProcessGetStartTime(cr.pid, timestamp) < 0)
> +        goto cleanup;
> +
>      *pid = cr.pid;
>      *uid = cr.uid;
>      *gid = cr.gid;
> 
> +    ret = 0;
> +
> +cleanup:
>      virMutexUnlock(&sock->lock);
> -    return 0;
> +    return ret;
>  }
>  #else
>  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",
> diff --git a/src/rpc/virnetsocket.h b/src/rpc/virnetsocket.h
> index 5ba7c8f..4eaf951 100644
> --- a/src/rpc/virnetsocket.h
> +++ b/src/rpc/virnetsocket.h
> @@ -89,7 +89,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 virNetSocketSetBlocking(virNetSocketPtr sock,
>                              bool blocking);
> diff --git a/src/util/virprocess.c b/src/util/virprocess.c
> index ac78f1d..f05f8f3 100644
> --- a/src/util/virprocess.c
> +++ b/src/util/virprocess.c
> @@ -26,11 +26,19 @@
>  #include <errno.h>
>  #include <sys/wait.h>
> 
> +#ifdef __FreeBSD__
> +# include <sys/param.h>
> +# include <sys/sysctl.h>
> +# include <sys/user.h>
> +#endif
> +
> +#include "viratomic.h"
>  #include "virprocess.h"
>  #include "virterror_internal.h"
>  #include "memory.h"
>  #include "logging.h"
>  #include "util.h"
> +#include "virstring.h"
>  #include "ignore-value.h"
>  #define virReportError(code, ...)                                      \
>      virReportErrorHelper(VIR_FROM_NONE, code, __FILE__,                 \
> @@ -239,3 +247,113 @@ int virProcessKill(pid_t pid, int sig)
>      return kill(pid, sig);
>  #endif
>  }
> +
> +
> +#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
> diff --git a/src/util/virprocess.h b/src/util/virprocess.h
> index 048a73c..30ca21f 100644
> --- a/src/util/virprocess.h
> +++ b/src/util/virprocess.h
> @@ -39,4 +39,7 @@ virProcessWait(pid_t pid, int *exitstatus)
>  int virProcessKill(pid_t pid, int sig);
> 
> 
> +int virProcessGetStartTime(pid_t pid,
> +                           unsigned long long *timestamp);
> +
>  #endif /* __VIR_PROCESS_H__ */
> diff --git a/src/util/virstring.c b/src/util/virstring.c
> index 1917e9a..92289eb 100644
> --- a/src/util/virstring.c
> +++ b/src/util/virstring.c
> @@ -166,3 +166,14 @@ void virStringFreeList(char **strings)
>      }
>      VIR_FREE(strings);
>  }
> +
> +
> +size_t virStringListLength(char **strings)
> +{
> +    size_t i = 0;
> +
> +    while (strings && strings[i])
> +        i++;
> +
> +    return i;
> +}

This looks a bit as if it could go into a separate commit since it adds
a new utility function, but that's minor. Otherwise ACK to the whole
series.
Cheers,
 -- Guido

> diff --git a/src/util/virstring.h b/src/util/virstring.h
> index a569fe0..d68ed2f 100644
> --- a/src/util/virstring.h
> +++ b/src/util/virstring.h
> @@ -35,4 +35,6 @@ char *virStringJoin(const char **strings,
> 
>  void virStringFreeList(char **strings);
> 
> +size_t virStringListLength(char **strings);
> +
>  #endif /* __VIR_STRING_H__ */
> -- 
> 1.8.3.1
> 




More information about the libvir-list mailing list