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.
This commit is contained in:
Lennart Poettering 2013-12-24 15:53:04 +01:00
parent 96415cad2f
commit eff0527098
9 changed files with 81 additions and 67 deletions

View File

@ -358,40 +358,6 @@ static int process_hello(sd_bus *a, sd_bus *b, sd_bus_message *m, bool *got_hell
return 1; 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[]) { int main(int argc, char *argv[]) {
_cleanup_bus_unref_ sd_bus *a = NULL, *b = NULL; _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; sd_is_socket(out_fd, AF_UNIX, 0, 0) > 0;
if (is_unix) { if (is_unix) {
socklen_t l = sizeof(ucred); getpeercred(in_fd, &ucred);
r = getsockopt(in_fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l);
if (r < 0) {
r = -errno;
goto finish;
}
assert(l == sizeof(ucred));
getpeersec(in_fd, &peersec); getpeersec(in_fd, &peersec);
} }

View File

@ -671,10 +671,11 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
case AF_UNIX: { case AF_UNIX: {
struct ucred ucred; struct ucred ucred;
int k;
l = sizeof(ucred); k = getpeercred(fd, &ucred);
if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0) if (k < 0)
return -errno; return k;
if (asprintf(&r, if (asprintf(&r,
"%u-%lu-%lu", "%u-%lu-%lu",

View File

@ -354,7 +354,6 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent
Server *s = userdata; Server *s = userdata;
StdoutStream *stream; StdoutStream *stream;
int fd, r; int fd, r;
socklen_t len;
assert(s); assert(s);
@ -386,8 +385,8 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent
stream->fd = fd; stream->fd = fd;
len = sizeof(stream->ucred); r = getpeercred(fd, &stream->ucred);
if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &stream->ucred, &len) < 0) { if (r < 0) {
log_error("Failed to determine peer credentials: %m"); log_error("Failed to determine peer credentials: %m");
goto fail; goto fail;
} }

View File

@ -625,14 +625,10 @@ void bus_socket_setup(sd_bus *b) {
} }
static void bus_get_peercred(sd_bus *b) { static void bus_get_peercred(sd_bus *b) {
socklen_t l;
assert(b); assert(b);
/* Get the peer for socketpair() sockets */ /* Get the peer for socketpair() sockets */
l = sizeof(b->ucred); b->ucred_valid = getpeercred(b->input_fd, &b->ucred) >= 0;
if (getsockopt(b->input_fd, SOL_SOCKET, SO_PEERCRED, &b->ucred, &l) >= 0 && l >= sizeof(b->ucred))
b->ucred_valid = b->ucred.pid > 0;
} }
static int bus_socket_start_auth_client(sd_bus *b) { static int bus_socket_start_auth_client(sd_bus *b) {

View File

@ -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_free_ char *p = NULL, *tty = NULL;
_cleanup_close_ int fd = -1; _cleanup_close_ int fd = -1;
struct ucred ucred; struct ucred ucred;
socklen_t l;
int v, r; int v, r;
assert(display); 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) if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0)
return -errno; return -errno;
l = sizeof(ucred); r = getpeercred(fd, &ucred);
r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l);
if (r < 0) if (r < 0)
return -errno; return r;
r = get_ctty(ucred.pid, NULL, &tty); r = get_ctty(ucred.pid, NULL, &tty);
if (r < 0) if (r < 0)

View File

@ -579,6 +579,7 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_
int getpeername_pretty(int fd, char **ret) { int getpeername_pretty(int fd, char **ret) {
union sockaddr_union sa; union sockaddr_union sa;
socklen_t salen; socklen_t salen;
int r;
assert(fd >= 0); assert(fd >= 0);
assert(ret); assert(ret);
@ -593,9 +594,9 @@ int getpeername_pretty(int fd, char **ret) {
/* UNIX connection sockets are anonymous, so let's use /* UNIX connection sockets are anonymous, so let's use
* PID/UID as pretty credentials instead */ * PID/UID as pretty credentials instead */
salen = sizeof(ucred); r = getpeercred(fd, &ucred);
if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &salen) < 0) if (r < 0)
return -errno; return r;
if (asprintf(ret, "PID %lu/UID %lu", (unsigned long) ucred.pid, (unsigned long) ucred.pid) < 0) if (asprintf(ret, "PID %lu/UID %lu", (unsigned long) ucred.pid, (unsigned long) ucred.pid) < 0)
return -ENOMEM; return -ENOMEM;

View File

@ -6117,3 +6117,61 @@ bool pid_valid(pid_t pid) {
return errno != ESRCH; 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;
}

View File

@ -40,6 +40,7 @@
#include <unistd.h> #include <unistd.h>
#include <locale.h> #include <locale.h>
#include <mntent.h> #include <mntent.h>
#include <sys/socket.h>
#include "macro.h" #include "macro.h"
#include "time-util.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); int namespace_enter(int pidns_fd, int mntns_fd, int root_fd);
bool pid_valid(pid_t pid); bool pid_valid(pid_t pid);
int getpeercred(int fd, struct ucred *ucred);
int getpeersec(int fd, char **ret);

View File

@ -181,10 +181,10 @@ struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl)
{ {
struct udev_ctrl_connection *conn; struct udev_ctrl_connection *conn;
struct ucred ucred; struct ucred ucred;
socklen_t slen;
const int on = 1; const int on = 1;
int r;
conn = calloc(1, sizeof(struct udev_ctrl_connection)); conn = new(struct udev_ctrl_connection, 1);
if (conn == NULL) if (conn == NULL)
return NULL; return NULL;
conn->refcount = 1; conn->refcount = 1;
@ -198,9 +198,9 @@ struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl)
} }
/* check peer credential of connection */ /* check peer credential of connection */
slen = sizeof(ucred); r = getpeercred(conn->sock, &ucred);
if (getsockopt(conn->sock, SOL_SOCKET, SO_PEERCRED, &ucred, &slen) < 0) { if (r < 0) {
log_error("unable to receive credentials of ctrl connection: %m\n"); log_error("unable to receive credentials of ctrl connection: %s", strerror(-r));
goto err; goto err;
} }
if (ucred.uid > 0) { if (ucred.uid > 0) {