From eff05270986a13e7de93ae16311f654d3f7c166f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 24 Dec 2013 15:53:04 +0100 Subject: [PATCH] util: unify SO_PEERCRED/SO_PEERSEC invocations Introduce new call getpeercred() which internally just uses SO_PEERCRED but checks if the returned data is actually useful due to namespace quirks. --- src/bus-proxyd/bus-proxyd.c | 45 +------------------------ src/core/socket.c | 7 ++-- src/journal/journald-stream.c | 5 ++- src/libsystemd-bus/bus-socket.c | 6 +--- src/login/pam-module.c | 6 ++-- src/shared/socket-util.c | 7 ++-- src/shared/util.c | 58 +++++++++++++++++++++++++++++++++ src/shared/util.h | 4 +++ src/udev/udev-ctrl.c | 10 +++--- 9 files changed, 81 insertions(+), 67 deletions(-) diff --git a/src/bus-proxyd/bus-proxyd.c b/src/bus-proxyd/bus-proxyd.c index b87dffe0e8b..60490d51663 100644 --- a/src/bus-proxyd/bus-proxyd.c +++ b/src/bus-proxyd/bus-proxyd.c @@ -358,40 +358,6 @@ static int process_hello(sd_bus *a, sd_bus *b, sd_bus_message *m, bool *got_hell return 1; } -static int getpeersec(int fd, char **ret) { - socklen_t n = 64; - char *s; - int r; - - assert(fd >= 0); - assert(ret); - - s = new0(char, n); - if (!s) - return -ENOMEM; - - r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n); - if (r < 0) { - free(s); - - if (errno != ERANGE) - return r; - - s = new0(char, n); - if (!s) - return -ENOMEM; - - r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n); - if (r < 0) { - free(s); - return r; - } - } - - *ret = s; - return 0; -} - int main(int argc, char *argv[]) { _cleanup_bus_unref_ sd_bus *a = NULL, *b = NULL; @@ -427,16 +393,7 @@ int main(int argc, char *argv[]) { sd_is_socket(out_fd, AF_UNIX, 0, 0) > 0; if (is_unix) { - socklen_t l = sizeof(ucred); - - r = getsockopt(in_fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l); - if (r < 0) { - r = -errno; - goto finish; - } - - assert(l == sizeof(ucred)); - + getpeercred(in_fd, &ucred); getpeersec(in_fd, &peersec); } diff --git a/src/core/socket.c b/src/core/socket.c index 31fc2a25269..88599ca9c16 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -671,10 +671,11 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) { case AF_UNIX: { struct ucred ucred; + int k; - l = sizeof(ucred); - if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0) - return -errno; + k = getpeercred(fd, &ucred); + if (k < 0) + return k; if (asprintf(&r, "%u-%lu-%lu", diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c index aba9054b271..c032ee4a0ef 100644 --- a/src/journal/journald-stream.c +++ b/src/journal/journald-stream.c @@ -354,7 +354,6 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent Server *s = userdata; StdoutStream *stream; int fd, r; - socklen_t len; assert(s); @@ -386,8 +385,8 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent stream->fd = fd; - len = sizeof(stream->ucred); - if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &stream->ucred, &len) < 0) { + r = getpeercred(fd, &stream->ucred); + if (r < 0) { log_error("Failed to determine peer credentials: %m"); goto fail; } diff --git a/src/libsystemd-bus/bus-socket.c b/src/libsystemd-bus/bus-socket.c index 66924b29fac..0c4b6af4472 100644 --- a/src/libsystemd-bus/bus-socket.c +++ b/src/libsystemd-bus/bus-socket.c @@ -625,14 +625,10 @@ void bus_socket_setup(sd_bus *b) { } static void bus_get_peercred(sd_bus *b) { - socklen_t l; - assert(b); /* Get the peer for socketpair() sockets */ - l = sizeof(b->ucred); - if (getsockopt(b->input_fd, SOL_SOCKET, SO_PEERCRED, &b->ucred, &l) >= 0 && l >= sizeof(b->ucred)) - b->ucred_valid = b->ucred.pid > 0; + b->ucred_valid = getpeercred(b->input_fd, &b->ucred) >= 0; } static int bus_socket_start_auth_client(sd_bus *b) { diff --git a/src/login/pam-module.c b/src/login/pam-module.c index 45428a090f4..89623aa7657 100644 --- a/src/login/pam-module.c +++ b/src/login/pam-module.c @@ -120,7 +120,6 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_ _cleanup_free_ char *p = NULL, *tty = NULL; _cleanup_close_ int fd = -1; struct ucred ucred; - socklen_t l; int v, r; assert(display); @@ -144,10 +143,9 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_ if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) return -errno; - l = sizeof(ucred); - r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l); + r = getpeercred(fd, &ucred); if (r < 0) - return -errno; + return r; r = get_ctty(ucred.pid, NULL, &tty); if (r < 0) diff --git a/src/shared/socket-util.c b/src/shared/socket-util.c index 75c47d1f76d..45ada7eb3ff 100644 --- a/src/shared/socket-util.c +++ b/src/shared/socket-util.c @@ -579,6 +579,7 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ int getpeername_pretty(int fd, char **ret) { union sockaddr_union sa; socklen_t salen; + int r; assert(fd >= 0); assert(ret); @@ -593,9 +594,9 @@ int getpeername_pretty(int fd, char **ret) { /* UNIX connection sockets are anonymous, so let's use * PID/UID as pretty credentials instead */ - salen = sizeof(ucred); - if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &salen) < 0) - return -errno; + r = getpeercred(fd, &ucred); + if (r < 0) + return r; if (asprintf(ret, "PID %lu/UID %lu", (unsigned long) ucred.pid, (unsigned long) ucred.pid) < 0) return -ENOMEM; diff --git a/src/shared/util.c b/src/shared/util.c index 8d7cf5398f9..6b6722c2780 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -6117,3 +6117,61 @@ bool pid_valid(pid_t pid) { return errno != ESRCH; } + +int getpeercred(int fd, struct ucred *ucred) { + socklen_t n = sizeof(struct ucred); + struct ucred u; + int r; + + assert(fd >= 0); + assert(ucred); + + r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n); + if (r < 0) + return -errno; + + if (n != sizeof(struct ucred)) + return -EIO; + + /* Check if the data is actually useful and not suppressed due + * to namespacing issues */ + if (u.pid <= 0) + return -ENODATA; + + *ucred = u; + return 0; +} + +int getpeersec(int fd, char **ret) { + socklen_t n = 64; + char *s; + int r; + + assert(fd >= 0); + assert(ret); + + s = new0(char, n); + if (!s) + return -ENOMEM; + + r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n); + if (r < 0) { + free(s); + + if (errno != ERANGE) + return -errno; + + s = new0(char, n); + if (!s) + return -ENOMEM; + + r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n); + if (r < 0) { + free(s); + return -errno; + } + } + + *ret = s; + return 0; +} diff --git a/src/shared/util.h b/src/shared/util.h index 338d79c7ac5..57667ef895a 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -40,6 +40,7 @@ #include #include #include +#include #include "macro.h" #include "time-util.h" @@ -811,3 +812,6 @@ int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *root_fd); int namespace_enter(int pidns_fd, int mntns_fd, int root_fd); bool pid_valid(pid_t pid); + +int getpeercred(int fd, struct ucred *ucred); +int getpeersec(int fd, char **ret); diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c index 4bb0ceafe71..39d777ec738 100644 --- a/src/udev/udev-ctrl.c +++ b/src/udev/udev-ctrl.c @@ -181,10 +181,10 @@ struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl) { struct udev_ctrl_connection *conn; struct ucred ucred; - socklen_t slen; const int on = 1; + int r; - conn = calloc(1, sizeof(struct udev_ctrl_connection)); + conn = new(struct udev_ctrl_connection, 1); if (conn == NULL) return NULL; conn->refcount = 1; @@ -198,9 +198,9 @@ struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl) } /* check peer credential of connection */ - slen = sizeof(ucred); - if (getsockopt(conn->sock, SOL_SOCKET, SO_PEERCRED, &ucred, &slen) < 0) { - log_error("unable to receive credentials of ctrl connection: %m\n"); + r = getpeercred(conn->sock, &ucred); + if (r < 0) { + log_error("unable to receive credentials of ctrl connection: %s", strerror(-r)); goto err; } if (ucred.uid > 0) {