diff --git a/daemon/remote.c b/daemon/remote.c index e879941fc0f28099038406e8f085727549f7468d..90aa178fdc270232d2a35bee8e89e1d7ce67cb04 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -2372,6 +2372,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 @@ -2379,7 +2380,7 @@ remoteDispatchAuthList(virNetServerPtr server ATTRIBUTE_UNUSED, */ if (auth == VIR_NET_SERVER_SERVICE_AUTH_POLKIT) { if (virNetServerClientGetUNIXIdentity(client, &callerUid, &callerGid, - &callerPid) < 0) { + &callerPid, ×tamp) < 0) { /* Don't do anything on error - it'll be validated at next * phase of auth anyway */ virResetLastError(); @@ -2809,6 +2810,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; @@ -2834,7 +2836,7 @@ remoteDispatchAuthPolkit(virNetServerPtr server ATTRIBUTE_UNUSED, } if (virNetServerClientGetUNIXIdentity(client, &callerUid, &callerGid, - &callerPid) < 0) { + &callerPid, ×tamp) < 0) { goto authfail; } @@ -2842,7 +2844,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/libvirt_private.syms b/src/libvirt_private.syms index ed464798fd54227099730cb177adfe7459915a55..dd44ba61c01e7851928870bf76e6651a7cd3e901 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1720,6 +1720,7 @@ virStorageFileResize; # util/virstring.h virStringFreeList; virStringJoin; +virStringListLength; virStringSplit; diff --git a/src/locking/lock_daemon.c b/src/locking/lock_daemon.c index 97e5d749c2b1c0a9198c2a1d05803a46558c8dcd..bf0ee8124e705bf5d551ef46e29b77e0a3013505 100644 --- a/src/locking/lock_daemon.c +++ b/src/locking/lock_daemon.c @@ -782,6 +782,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) { @@ -798,7 +799,8 @@ virLockDaemonClientNew(virNetServerClientPtr client, if (virNetServerClientGetUNIXIdentity(client, &clientuid, &clientgid, - &priv->clientPid) < 0) + &priv->clientPid, + ×tamp) < 0) goto error; VIR_DEBUG("New client pid %llu uid %llu", diff --git a/src/rpc/virnetserverclient.c b/src/rpc/virnetserverclient.c index 446e1e91682fc626211bd5c3a25f9663d91449c5..8fec7e6758ab9b64170467e090c8d4e9dba2f00e 100644 --- a/src/rpc/virnetserverclient.c +++ b/src/rpc/virnetserverclient.c @@ -618,12 +618,15 @@ 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; virObjectLock(client); if (client->sock) - ret = virNetSocketGetUNIXIdentity(client->sock, uid, gid, pid); + ret = virNetSocketGetUNIXIdentity(client->sock, + uid, gid, pid, + timestamp); virObjectUnlock(client); return ret; } diff --git a/src/rpc/virnetserverclient.h b/src/rpc/virnetserverclient.h index 325f5d9c4e8bf1d7446695f19408e81e3e925044..053b54484dc06ddb94e05c08b2199441e7a07be2 100644 --- a/src/rpc/virnetserverclient.h +++ b/src/rpc/virnetserverclient.h @@ -97,7 +97,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 *virNetServerClientGetPrivateData(virNetServerClientPtr client); diff --git a/src/rpc/virnetsocket.c b/src/rpc/virnetsocket.c index 93980d65d40bddbc39e692948b60b665def9ccc5..7ad25e32eb79c4175aa160cf574a2c3f54ada052 100644 --- a/src/rpc/virnetsocket.c +++ b/src/rpc/virnetsocket.c @@ -1097,31 +1097,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); @@ -1145,7 +1155,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", diff --git a/src/rpc/virnetsocket.h b/src/rpc/virnetsocket.h index 13583f846cb19494b4ce61afe3f4d380d0f037ea..e04a67a5436459b02ea4ab23ca900dc689bcdbcc 100644 --- a/src/rpc/virnetsocket.h +++ b/src/rpc/virnetsocket.h @@ -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 virNetSocketSetBlocking(virNetSocketPtr sock, bool blocking); diff --git a/src/util/virprocess.c b/src/util/virprocess.c index a492bd11f77103b3bb5f37773010faf1fadadfb6..4202d9c5d7c69feeec3f7679eb49d4a8c921e7b6 100644 --- a/src/util/virprocess.c +++ b/src/util/virprocess.c @@ -29,12 +29,20 @@ #include #include +#ifdef __FreeBSD__ +# include +# include +# include +#endif + +#include "viratomic.h" #include "virprocess.h" #include "virerror.h" #include "viralloc.h" #include "virfile.h" #include "virlog.h" #include "virutil.h" +#include "virstring.h" #define VIR_FROM_THIS VIR_FROM_NONE @@ -605,3 +613,112 @@ int virProcessSetNamespaces(size_t nfdlist ATTRIBUTE_UNUSED, return -1; } #endif /* ! HAVE_SETNS */ + +#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 53475d3cc3ff3410d6c13f6c9cfe0dd427616ba0..149d451a61f010dc6b1b7dd3e47e35790320dc86 100644 --- a/src/util/virprocess.h +++ b/src/util/virprocess.h @@ -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); diff --git a/src/util/virstring.c b/src/util/virstring.c index 0420ca37f9d2fcc5829a06a2fa1ec270f73deee6..1fc24de9a184c9f8933ff673f550fbe61d17f23e 100644 --- a/src/util/virstring.c +++ b/src/util/virstring.c @@ -166,3 +166,13 @@ void virStringFreeList(char **strings) } VIR_FREE(strings); } + +size_t virStringListLength(char **strings) +{ + size_t i = 0; + + while (strings && strings[i]) + i++; + + return i; +} diff --git a/src/util/virstring.h b/src/util/virstring.h index a569fe080a6262e5af1b49452646a8b2b2a72f3b..d68ed2f68972e156c38120877cdb3b3e2b71c1e1 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__ */