diff --git a/src/home/homed-manager.c b/src/home/homed-manager.c index 71218336601..93846875878 100644 --- a/src/home/homed-manager.c +++ b/src/home/homed-manager.c @@ -37,6 +37,7 @@ #include "homed-varlink.h" #include "io-util.h" #include "mkdir.h" +#include "notify-recv.h" #include "openssl-util.h" #include "process-util.h" #include "quota-util.h" @@ -1068,123 +1069,33 @@ static int manager_bind_varlink(Manager *m) { return 0; } -static ssize_t read_datagram( - int fd, - struct ucred *ret_sender, - void **ret, - int *ret_passed_fd) { - - CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred)) + CMSG_SPACE(sizeof(int))) control; - _cleanup_free_ void *buffer = NULL; - _cleanup_close_ int passed_fd = -EBADF; - struct ucred *sender = NULL; - struct cmsghdr *cmsg; - struct msghdr mh; - struct iovec iov; - ssize_t n, m; - - assert(fd >= 0); - assert(ret_sender); - assert(ret); - assert(ret_passed_fd); - - n = next_datagram_size_fd(fd); - if (n < 0) - return n; - - buffer = malloc(n + 2); - if (!buffer) - return -ENOMEM; - - /* Pass one extra byte, as a size check */ - iov = IOVEC_MAKE(buffer, n + 1); - - mh = (struct msghdr) { - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = &control, - .msg_controllen = sizeof(control), - }; - - m = recvmsg_safe(fd, &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); - if (m < 0) - return m; - - /* Ensure the size matches what we determined before */ - if (m != n) { - cmsg_close_all(&mh); - return -EMSGSIZE; - } - - CMSG_FOREACH(cmsg, &mh) { - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_CREDENTIALS && - cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) { - assert(!sender); - sender = CMSG_TYPED_DATA(cmsg, struct ucred); - } - - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_RIGHTS) { - - if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) { - cmsg_close_all(&mh); - return -EMSGSIZE; - } - - assert(passed_fd < 0); - passed_fd = *CMSG_TYPED_DATA(cmsg, int); - } - } - - if (sender) - *ret_sender = *sender; - else - *ret_sender = (struct ucred) UCRED_INVALID; - - *ret_passed_fd = TAKE_FD(passed_fd); - - /* For safety reasons: let's always NUL terminate. */ - ((char*) buffer)[n] = 0; - *ret = TAKE_PTR(buffer); - - return 0; -} - static int on_notify_socket(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - _cleanup_strv_free_ char **l = NULL; - _cleanup_free_ void *datagram = NULL; - _cleanup_close_ int passed_fd = -EBADF; - struct ucred sender = UCRED_INVALID; Manager *m = ASSERT_PTR(userdata); - ssize_t n; - Home *h; + int r; assert(s); - n = read_datagram(fd, &sender, &datagram, &passed_fd); - if (n < 0) { - if (ERRNO_IS_TRANSIENT(n)) - return 0; - return log_error_errno(n, "Failed to read notify datagram: %m"); - } + _cleanup_(fdset_free_asyncp) FDSet *passed_fds = NULL; + _cleanup_(pidref_done) PidRef sender = PIDREF_NULL; + _cleanup_strv_free_ char **l = NULL; + r = notify_recv_with_fds_strv(fd, &l, /* ret_ucred= */ NULL, &sender, &passed_fds); + if (r == -EAGAIN) + return 0; + if (r < 0) + return r; - if (sender.pid <= 0) { - log_warning("Received notify datagram without valid sender PID, ignoring."); + if (fdset_size(passed_fds) > 1) { + log_warning("Received notify datagram with multiple fds, ignoring."); return 0; } - h = hashmap_get(m->homes_by_worker_pid, PID_TO_PTR(sender.pid)); + Home *h = hashmap_get(m->homes_by_worker_pid, PID_TO_PTR(sender.pid)); if (!h) { log_warning("Received notify datagram of unknown process, ignoring."); return 0; } - l = strv_split(datagram, "\n"); - if (!l) - return log_oom(); - - home_process_notify(h, l, TAKE_FD(passed_fd)); + home_process_notify(h, l, fdset_steal_first(passed_fds)); return 0; } @@ -1224,7 +1135,11 @@ static int manager_listen_notify(Manager *m) { r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true); if (r < 0) - return r; + return log_error_errno(r, "Failed to enable SO_PASSCRED on notify socket: %m"); + + r = setsockopt_int(fd, SOL_SOCKET, SO_PASSPIDFD, true); + if (r < 0) + log_warning_errno(r, "Failed to enable SO_PASSPIDFD on notify socket, ignoring: %m"); r = sd_event_add_io(m->event, &m->notify_socket_event_source, fd, EPOLLIN, on_notify_socket, m); if (r < 0)