![Lennart Poettering](/assets/img/avatar_default.png)
This adds some paranoia code that moves some of the fds we allocate for longer periods of times to fds > 2 if they are allocated below this boundary. This is a paranoid safety thing, in order to avoid that external code might end up erroneously use our fds under the assumption they were valid stdin/stdout/stderr. Think: some app closes stdin/stdout/stderr and then invokes 'fprintf(stderr, …' which causes writes on our fds. This both adds the helper to do the moving as well as ports over a number of users to this new logic. Since we don't want to litter all our code with invocations of this I tried to strictly focus on fds we keep open for long periods of times only and only in code that is frequently loaded into foreign programs (under the assumptions that in our own codebase we are smart enough to always keep stdin/stdout/stderr allocated to avoid this pitfall). Specifically this means all code used by NSS and our sd-xyz API: 1. our logging APIs 2. sd-event 3. sd-bus 4. sd-resolve 5. sd-netlink This changed was inspired by this: https://github.com/systemd/systemd/issues/8075#issuecomment-363689755 This shows that apparently IRL there are programs that do close stdin/stdout/stderr, and we should accomodate for that. Note that this won't fix any bugs, this just makes sure that buggy programs are less likely to interfere with out own code.
136 lines
4.2 KiB
C
136 lines
4.2 KiB
C
/* SPDX-License-Identifier: LGPL-2.1+ */
|
|
/***
|
|
This file is part of systemd.
|
|
|
|
Copyright 2013 Lennart Poettering
|
|
|
|
systemd is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
|
(at your option) any later version.
|
|
|
|
systemd is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
|
***/
|
|
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
|
|
#include "bus-container.h"
|
|
#include "bus-internal.h"
|
|
#include "bus-socket.h"
|
|
#include "fd-util.h"
|
|
#include "process-util.h"
|
|
#include "util.h"
|
|
|
|
int bus_container_connect_socket(sd_bus *b) {
|
|
_cleanup_close_pair_ int pair[2] = { -1, -1 };
|
|
_cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
|
|
int r, error_buf = 0;
|
|
pid_t child;
|
|
ssize_t n;
|
|
|
|
assert(b);
|
|
assert(b->input_fd < 0);
|
|
assert(b->output_fd < 0);
|
|
assert(b->nspid > 0 || b->machine);
|
|
|
|
if (b->nspid <= 0) {
|
|
r = container_get_leader(b->machine, &b->nspid);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
|
|
if (b->input_fd < 0)
|
|
return -errno;
|
|
|
|
b->input_fd = fd_move_above_stdio(b->input_fd);
|
|
|
|
b->output_fd = b->input_fd;
|
|
|
|
bus_socket_setup(b);
|
|
|
|
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
|
|
return -errno;
|
|
|
|
r = safe_fork("(sd-buscntr)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
|
|
if (r < 0)
|
|
return r;
|
|
if (r == 0) {
|
|
pid_t grandchild;
|
|
|
|
pair[0] = safe_close(pair[0]);
|
|
|
|
r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
|
|
if (r < 0)
|
|
_exit(EXIT_FAILURE);
|
|
|
|
/* We just changed PID namespace, however it will only
|
|
* take effect on the children we now fork. Hence,
|
|
* let's fork another time, and connect from this
|
|
* grandchild, so that SO_PEERCRED of our connection
|
|
* comes from a process from within the container, and
|
|
* not outside of it */
|
|
|
|
r = safe_fork("(sd-buscntr2)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &grandchild);
|
|
if (r < 0)
|
|
_exit(EXIT_FAILURE);
|
|
if (r == 0) {
|
|
|
|
r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
|
|
if (r < 0) {
|
|
/* Try to send error up */
|
|
error_buf = errno;
|
|
(void) write(pair[1], &error_buf, sizeof(error_buf));
|
|
_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
_exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
r = wait_for_terminate_and_check("(sd-buscntr2)", grandchild, 0);
|
|
if (r < 0)
|
|
_exit(EXIT_FAILURE);
|
|
|
|
_exit(r);
|
|
}
|
|
|
|
pair[1] = safe_close(pair[1]);
|
|
|
|
r = wait_for_terminate_and_check("(sd-buscntr)", child, 0);
|
|
if (r < 0)
|
|
return r;
|
|
if (r != EXIT_SUCCESS)
|
|
return -EPROTO;
|
|
|
|
n = read(pair[0], &error_buf, sizeof(error_buf));
|
|
if (n < 0)
|
|
return -errno;
|
|
|
|
if (n > 0) {
|
|
if (n != sizeof(error_buf))
|
|
return -EIO;
|
|
|
|
if (error_buf < 0)
|
|
return -EIO;
|
|
|
|
if (error_buf == EINPROGRESS)
|
|
return 1;
|
|
|
|
if (error_buf > 0)
|
|
return -error_buf;
|
|
}
|
|
|
|
return bus_socket_start_auth(b);
|
|
}
|