.ucred = UCRED_INVALID,
+ .peer_pidfd = -EBADF,
+
.timestamp = USEC_INFINITY,
.timeout = VARLINK_DEFAULT_TIMEOUT_USEC,
- .af = -1,
+ .allow_fd_passing_input = -1,
- .peer_pidfd = -EBADF,
+ .af = -1,
};
*ret = v;
p = v->input_buffer + v->input_buffer_index + v->input_buffer_size;
rs = MALLOC_SIZEOF_SAFE(v->input_buffer) - (v->input_buffer_index + v->input_buffer_size);
- if (v->allow_fd_passing_input) {
+ if (v->allow_fd_passing_input > 0) {
iov = IOVEC_MAKE(p, rs);
/* Allocate the fd buffer on the heap, since we need a lot of space potentially */
return n;
if (n == 0) { /* EOF */
- if (v->allow_fd_passing_input)
+ if (v->allow_fd_passing_input > 0)
cmsg_close_all(&mh);
v->read_disconnected = true;
return 1;
}
- if (v->allow_fd_passing_input) {
+ if (v->allow_fd_passing_input > 0) {
struct cmsghdr *cmsg;
cmsg = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, (socklen_t) -1);
/* Returns one of the file descriptors that were received along with the current message. This does
* not duplicate the fd nor invalidate it, it hence remains in our possession. */
- if (!v->allow_fd_passing_input)
+ if (v->allow_fd_passing_input <= 0)
return -EPERM;
if (i >= v->n_input_fds)
* we'll invalidate the reference to it under our possession. If called twice in a row will return
* -EBADF */
- if (!v->allow_fd_passing_input)
+ if (v->allow_fd_passing_input <= 0)
return -EPERM;
if (i >= v->n_input_fds)
_public_ int sd_varlink_get_n_fds(sd_varlink *v) {
assert_return(v, -EINVAL);
- if (!v->allow_fd_passing_input)
+ if (v->allow_fd_passing_input <= 0)
return -EPERM;
return (int) v->n_input_fds;
assert_return(v, -EINVAL);
- if (v->allow_fd_passing_input == !!b)
+ if (v->allow_fd_passing_input >= 0 && (v->allow_fd_passing_input > 0) == !!b)
return 0;
- if (!b) {
- v->allow_fd_passing_input = false;
- return 1;
- }
-
r = verify_unix_socket(v);
- if (r < 0)
+ if (r < 0) {
+ assert(v->allow_fd_passing_input <= 0);
+
+ if (!b) {
+ v->allow_fd_passing_input = false;
+ return 0;
+ }
+
return r;
+ }
- v->allow_fd_passing_input = true;
- return 0;
+ if (!v->server || FLAGS_SET(v->server->flags, SD_VARLINK_SERVER_FD_PASSING_INPUT_STRICT)) {
+ r = setsockopt_int(v->input_fd, SOL_SOCKET, SO_PASSRIGHTS, !!b);
+ if (r < 0 && !ERRNO_IS_NEG_NOT_SUPPORTED(r))
+ log_debug_errno(r, "Failed to set SO_PASSRIGHTS socket option: %m");
+ }
+
+ v->allow_fd_passing_input = !!b;
+ return 1;
}
_public_ int sd_varlink_set_allow_fd_passing_output(sd_varlink *v, int b) {
SD_VARLINK_SERVER_INHERIT_USERDATA|
SD_VARLINK_SERVER_INPUT_SENSITIVE|
SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT|
- SD_VARLINK_SERVER_ALLOW_FD_PASSING_OUTPUT)) == 0, -EINVAL);
+ SD_VARLINK_SERVER_ALLOW_FD_PASSING_OUTPUT|
+ SD_VARLINK_SERVER_FD_PASSING_INPUT_STRICT)) == 0, -EINVAL);
s = new(sd_varlink_server, 1);
if (!s)
if (r < 0)
return r;
+ /* Link up the server and the connection, and take reference in both directions. Note that the
+ * reference on the connection is left dangling. It will be dropped when the connection is closed,
+ * which happens in varlink_close(), including in the event loop quit callback. */
+ v->server = sd_varlink_server_ref(server);
+ sd_varlink_ref(v);
+
v->input_fd = input_fd;
v->output_fd = output_fd;
if (server->flags & SD_VARLINK_SERVER_INHERIT_USERDATA)
(void) sd_varlink_set_allow_fd_passing_input(v, FLAGS_SET(server->flags, SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT));
(void) sd_varlink_set_allow_fd_passing_output(v, FLAGS_SET(server->flags, SD_VARLINK_SERVER_ALLOW_FD_PASSING_OUTPUT));
- /* Link up the server and the connection, and take reference in both directions. Note that the
- * reference on the connection is left dangling. It will be dropped when the connection is closed,
- * which happens in varlink_close(), including in the event loop quit callback. */
- v->server = sd_varlink_server_ref(server);
- sd_varlink_ref(v);
-
varlink_set_state(v, VARLINK_IDLE_SERVER);
if (server->event) {
if (r < 0)
return r;
+ /* If fd passing is disabled on server, and SD_VARLINK_SERVER_FD_PASSING_INPUT_STRICT flag is set,
+ * turn off SO_PASSRIGHTS immediately on listening socket. The conditionalization behind a flag
+ * is needed to retain backwards compat, where implementations would register a connection callback
+ * to enable fd passing after accept(), which might race with clients wrt SO_PASSRIGHTS state. */
+ if (FLAGS_SET(s->flags, SD_VARLINK_SERVER_FD_PASSING_INPUT_STRICT))
+ (void) setsockopt_int(fd, SOL_SOCKET, SO_PASSRIGHTS, FLAGS_SET(s->flags, SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT));
+
r = varlink_server_create_listen_fd_socket(s, fd, &ss);
if (r < 0)
return r;
fd = fd_move_above_stdio(fd);
+ /* See the comment in sd_varlink_server_listen_fd() */
+ if (FLAGS_SET(s->flags, SD_VARLINK_SERVER_FD_PASSING_INPUT_STRICT))
+ (void) setsockopt_int(fd, SOL_SOCKET, SO_PASSRIGHTS, FLAGS_SET(s->flags, SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT));
+
(void) sockaddr_un_unlink(&sockaddr.un);
WITH_UMASK(~m & 0777)