From 4a07ff75df9e7fd39b14450637ed99af710d426c Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Mon, 12 Oct 2020 16:52:03 -0400 Subject: [PATCH] Fix: Use unix socket peercred for pid, uid, gid credentials Currently, the session daemon trust the pid, ppid, uid, and gid values passed by the application, but should really validate the uid using unix socket peercred. This fix uses the peercred values rather than the values provided by the application on registration for: - pid, uid and gid on Linux, - uid and gid on FreeBSD. This should improve how the session daemon deals with containerized applications on Linux as well. Applications are required to be either in the same pid namespace, or in a pid namespace nested within the pid namespace of the lttng-sessiond, so the session daemon can map the application pid to something meaningful within its own pid namespace. Applications in a unrelated (disjoint) pid namespace will be refused by the session daemon. About the uid and gid with user namespaces on Linux, those will provide meaningful IDs if the application user namespace is either the same as the user namespace of the session daemon, or a nested user namespace. Otherwise, the IDs will be that of /proc/sys/kernel/overflowuid and /proc/sys/kernel/overflowgid, which typically maps to nobody.nogroup on current distributions. Given that fetching the parent pid (ppid) of the application would require to use /proc//status (which is racy wrt pid reuse), expose the ppid provided by the application on registration instead, but only in situations where the application sits in the same pid namespace as the session daemon (on Linux), which is detected by checking if the pid provided by the application matches the pid obtained using unix socket credentials. The ppid is only used for logging/debugging purposes in the session daemon anyway, so it is OK to use the value provided by the application for that purpose. Fixes: #1286 Signed-off-by: Mathieu Desnoyers Change-Id: I94742e57dad642106908d09e2c7e395993c2c48f --- include/lttng/ust-error.h | 2 + liblttng-ust-comm/lttng-ust-comm.c | 2 + liblttng-ust-ctl/ustctl.c | 108 +++++++++++++++++++++++++++-- 3 files changed, 106 insertions(+), 6 deletions(-) diff --git a/include/lttng/ust-error.h b/include/lttng/ust-error.h index cbcb5ebd..8681cb04 100644 --- a/include/lttng/ust-error.h +++ b/include/lttng/ust-error.h @@ -50,6 +50,8 @@ enum lttng_ust_error_code { LTTNG_UST_ERR_INVAL_MAGIC = 1031, /* Invalid magic number */ LTTNG_UST_ERR_INVAL_SOCKET_TYPE = 1032, /* Invalid socket type */ LTTNG_UST_ERR_UNSUP_MAJOR = 1033, /* Unsupported major version */ + LTTNG_UST_ERR_PEERCRED = 1034, /* Cannot get unix socket peer credentials */ + LTTNG_UST_ERR_PEERCRED_PID = 1035, /* Peer credentials PID is invalid. Socket appears to belong to a distinct, non-nested pid namespace. */ /* MUST be last element */ LTTNG_UST_ERR_NR, /* Last element */ diff --git a/liblttng-ust-comm/lttng-ust-comm.c b/liblttng-ust-comm/lttng-ust-comm.c index 46f2e9e3..47b58cc4 100644 --- a/liblttng-ust-comm/lttng-ust-comm.c +++ b/liblttng-ust-comm/lttng-ust-comm.c @@ -71,6 +71,8 @@ static const char *ustcomm_readable_code[] = { [ USTCOMM_CODE_OFFSET(LTTNG_UST_ERR_INVAL_MAGIC) ] = "Invalid magic number", [ USTCOMM_CODE_OFFSET(LTTNG_UST_ERR_INVAL_SOCKET_TYPE) ] = "Invalid socket type", [ USTCOMM_CODE_OFFSET(LTTNG_UST_ERR_UNSUP_MAJOR) ] = "Unsupported major version", + [ USTCOMM_CODE_OFFSET(LTTNG_UST_ERR_PEERCRED) ] = "Cannot get unix socket peer credentials", + [ USTCOMM_CODE_OFFSET(LTTNG_UST_ERR_PEERCRED_PID) ] = "Peer credentials PID is invalid. Socket appears to belong to a distinct, non-nested pid namespace.", }; /* diff --git a/liblttng-ust-ctl/ustctl.c b/liblttng-ust-ctl/ustctl.c index a7bee029..048d0127 100644 --- a/liblttng-ust-ctl/ustctl.c +++ b/liblttng-ust-ctl/ustctl.c @@ -18,6 +18,9 @@ #define _GNU_SOURCE #include +#include +#include + #include #include #include @@ -1795,6 +1798,104 @@ int ustctl_has_perf_counters(void) #endif +#ifdef __linux__ +/* + * Override application pid/uid/gid with unix socket credentials. If + * the application announced a pid matching our view, it means it is + * within the same pid namespace, so expose the ppid provided by the + * application. + */ +static +int get_cred(int sock, + const struct ustctl_reg_msg *reg_msg, + uint32_t *pid, + uint32_t *ppid, + uint32_t *uid, + uint32_t *gid) +{ + struct ucred ucred; + socklen_t ucred_len = sizeof(struct ucred); + int ret; + + ret = getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &ucred_len); + if (ret) { + return -LTTNG_UST_ERR_PEERCRED; + } + DBG("Unix socket peercred [ pid: %u, uid: %u, gid: %u ], " + "application registered claiming [ pid: %u, ppid: %u, uid: %u, gid: %u ]", + ucred.pid, ucred.uid, ucred.gid, + reg_msg->pid, reg_msg->ppid, reg_msg->uid, reg_msg->gid); + if (!ucred.pid) { + ERR("Unix socket credential pid=0. Refusing application in distinct, non-nested pid namespace."); + return -LTTNG_UST_ERR_PEERCRED_PID; + } + *pid = ucred.pid; + *uid = ucred.uid; + *gid = ucred.gid; + if (ucred.pid == reg_msg->pid) { + *ppid = reg_msg->ppid; + } else { + *ppid = 0; + } + return 0; +} +#elif defined(__FreeBSD__) +#include + +/* + * Override application uid/gid with unix socket credentials. Use the + * first group of the cr_groups. + * Use the pid and ppid provided by the application on registration. + */ +static +int get_cred(int sock, + const struct ustctl_reg_msg *reg_msg, + uint32_t *pid, + uint32_t *ppid, + uint32_t *uid, + uint32_t *gid) +{ + struct xucred xucred; + socklen_t xucred_len = sizeof(struct xucred); + int ret; + + ret = getsockopt(sock, SOL_SOCKET, LOCAL_PEERCRED, &xucred, &xucred_len); + if (ret) { + return -LTTNG_UST_ERR_PEERCRED; + } + if (xucred.cr_version != XUCRED_VERSION || xucred.cr_ngroups < 1) { + return -LTTNG_UST_ERR_PEERCRED; + } + DBG("Unix socket peercred [ uid: %u, gid: %u ], " + "application registered claiming [ pid: %d, ppid: %d, uid: %u, gid: %u ]", + xucred.uid, xucred.cr_groups[0], + reg_msg->pid, reg_msg->ppid, reg_msg->uid, reg_msg->gid); + *pid = reg_msg->pid; + *ppid = reg_msg->ppid; + *uid = xucred.uid; + *gid = xucred.cr_groups[0]; + return 0; +} +#else +#warning "Using insecure fallback: trusting user id provided by registered applications. Please consider implementing use of unix socket credentials on your platform." +static +int get_cred(int sock, + const struct ustctl_reg_msg *reg_msg, + uint32_t *pid, + uint32_t *ppid, + uint32_t *uid, + uint32_t *gid) +{ + DBG("Application registered claiming [ pid: %u, ppid: %d, uid: %u, gid: %u ]", + reg_msg->pid, reg_msg->ppid, reg_msg->uid, reg_msg->gid); + *pid = reg_msg->pid; + *ppid = reg_msg->ppid; + *uid = reg_msg->uid; + *gid = reg_msg->gid; + return 0; +} +#endif + /* * Returns 0 on success, negative error value on error. */ @@ -1845,10 +1946,6 @@ int ustctl_recv_reg_msg(int sock, } *major = reg_msg.major; *minor = reg_msg.minor; - *pid = reg_msg.pid; - *ppid = reg_msg.ppid; - *uid = reg_msg.uid; - *gid = reg_msg.gid; *bits_per_long = reg_msg.bits_per_long; *uint8_t_alignment = reg_msg.uint8_t_alignment; *uint16_t_alignment = reg_msg.uint16_t_alignment; @@ -1859,8 +1956,7 @@ int ustctl_recv_reg_msg(int sock, if (reg_msg.major != LTTNG_UST_ABI_MAJOR_VERSION) { return -LTTNG_UST_ERR_UNSUP_MAJOR; } - - return 0; + return get_cred(sock, ®_msg, pid, ppid, uid, gid); } int ustctl_recv_notify(int sock, enum ustctl_notify_cmd *notify_cmd) -- 2.34.1