diff options
Diffstat (limited to 'src/systemd-bus-proxyd/bus-proxyd.c')
-rw-r--r-- | src/systemd-bus-proxyd/bus-proxyd.c | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/src/systemd-bus-proxyd/bus-proxyd.c b/src/systemd-bus-proxyd/bus-proxyd.c new file mode 100644 index 0000000000..17b80c3e9a --- /dev/null +++ b/src/systemd-bus-proxyd/bus-proxyd.c @@ -0,0 +1,328 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + Copyright 2013 Daniel Mack + Copyright 2014 Kay Sievers + Copyright 2015 David Herrmann + + 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 <errno.h> +#include <getopt.h> +#include <pthread.h> +#include <stddef.h> +#include <string.h> +#include <sys/prctl.h> +#include <sys/socket.h> +#include <unistd.h> + +#include <systemd/sd-daemon.h> + +#include "alloc-util.h" +#include "bus-internal.h" +#include "bus-xml-policy.h" +#include "capability-util.h" +#include "def.h" +#include "fd-util.h" +#include "formats-util.h" +#include "log.h" +#include "proxy.h" +#include "string-util.h" +#include "strv.h" +#include "user-util.h" +#include "util.h" + +static char *arg_address = NULL; +static char **arg_configuration = NULL; + +typedef struct { + int fd; + SharedPolicy *policy; + uid_t bus_uid; +} ClientContext; + +static ClientContext *client_context_free(ClientContext *c) { + if (!c) + return NULL; + + safe_close(c->fd); + free(c); + + return NULL; +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(ClientContext*, client_context_free); + +static int client_context_new(ClientContext **out) { + _cleanup_(client_context_freep) ClientContext *c = NULL; + + c = new0(ClientContext, 1); + if (!c) + return -ENOMEM; + + c->fd = -1; + + *out = c; + c = NULL; + return 0; +} + +static void *run_client(void *userdata) { + _cleanup_(client_context_freep) ClientContext *c = userdata; + _cleanup_(proxy_freep) Proxy *p = NULL; + char comm[16]; + int r; + + r = proxy_new(&p, c->fd, c->fd, arg_address); + c->fd = -1; + + if (r < 0) + goto exit; + + /* set comm to "p$PIDu$UID" and suffix with '*' if truncated */ + r = snprintf(comm, sizeof(comm), "p" PID_FMT "u" UID_FMT, p->local_creds.pid, p->local_creds.uid); + if (r >= (ssize_t)sizeof(comm)) + comm[sizeof(comm) - 2] = '*'; + (void) prctl(PR_SET_NAME, comm); + + r = proxy_set_policy(p, c->policy, arg_configuration); + if (r < 0) + goto exit; + + r = proxy_hello_policy(p, c->bus_uid); + if (r < 0) + goto exit; + + r = proxy_run(p); + +exit: + return NULL; +} + +static int loop_clients(int accept_fd, uid_t bus_uid) { + _cleanup_(shared_policy_freep) SharedPolicy *sp = NULL; + pthread_attr_t attr; + int r; + + r = pthread_attr_init(&attr); + if (r != 0) + return log_error_errno(r, "Cannot initialize pthread attributes: %m"); + + r = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + if (r != 0) { + r = log_error_errno(r, "Cannot mark pthread attributes as detached: %m"); + goto finish; + } + + r = shared_policy_new(&sp); + if (r < 0) + goto finish; + + for (;;) { + ClientContext *c; + pthread_t tid; + int fd; + + fd = accept4(accept_fd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC); + if (fd < 0) { + if (errno == EAGAIN || errno == EINTR) + continue; + + r = log_error_errno(errno, "accept4() failed: %m"); + goto finish; + } + + r = client_context_new(&c); + if (r < 0) { + log_oom(); + close(fd); + continue; + } + + c->fd = fd; + c->policy = sp; + c->bus_uid = bus_uid; + + r = pthread_create(&tid, &attr, run_client, c); + if (r != 0) { + log_warning_errno(r, "Cannot spawn thread, ignoring: %m"); + client_context_free(c); + continue; + } + } + +finish: + pthread_attr_destroy(&attr); + return r; +} + +static int help(void) { + + printf("%s [OPTIONS...]\n\n" + "DBus proxy server.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --configuration=PATH Configuration file or directory\n" + " --machine=MACHINE Connect to specified machine\n" + " --address=ADDRESS Connect to the bus specified by ADDRESS\n" + " (default: " DEFAULT_SYSTEM_BUS_ADDRESS ")\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_ADDRESS, + ARG_CONFIGURATION, + ARG_MACHINE, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "address", required_argument, NULL, ARG_ADDRESS }, + { "configuration", required_argument, NULL, ARG_CONFIGURATION }, + { "machine", required_argument, NULL, ARG_MACHINE }, + {}, + }; + + int c, r; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_ADDRESS: + r = free_and_strdup(&arg_address, optarg); + if (r < 0) + return log_oom(); + break; + + case ARG_CONFIGURATION: + r = strv_extend(&arg_configuration, optarg); + if (r < 0) + return log_oom(); + break; + + case ARG_MACHINE: { + _cleanup_free_ char *e = NULL; + char *a; + + e = bus_address_escape(optarg); + if (!e) + return log_oom(); + + a = strjoin("x-machine-kernel:machine=", e, ";x-machine-unix:machine=", e, NULL); + if (!a) + return log_oom(); + + free(arg_address); + arg_address = a; + + break; + } + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (argc > optind) { + log_error("Too many arguments"); + return -EINVAL; + } + + if (!arg_address) { + arg_address = strdup(DEFAULT_SYSTEM_BUS_ADDRESS); + if (!arg_address) + return log_oom(); + } + + return 1; +} + +int main(int argc, char *argv[]) { + int r, accept_fd; + uid_t uid, bus_uid; + gid_t gid; + + log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); + log_parse_environment(); + log_open(); + + bus_uid = getuid(); + + if (geteuid() == 0) { + const char *user = "systemd-bus-proxy"; + + r = get_user_creds(&user, &uid, &gid, NULL, NULL); + if (r < 0) { + log_error_errno(r, "Cannot resolve user name %s: %m", user); + goto finish; + } + + r = drop_privileges(uid, gid, 1ULL << CAP_IPC_OWNER); + if (r < 0) { + log_error_errno(r, "Cannot drop privileges: %m"); + goto finish; + } + } + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + r = sd_listen_fds(0); + if (r != 1) { + log_error("Illegal number of file descriptors passed"); + goto finish; + } + + accept_fd = SD_LISTEN_FDS_START; + + r = fd_nonblock(accept_fd, false); + if (r < 0) { + log_error_errno(r, "Cannot mark accept-fd non-blocking: %m"); + goto finish; + } + + r = loop_clients(accept_fd, bus_uid); + +finish: + sd_notify(false, + "STOPPING=1\n" + "STATUS=Shutting down."); + + strv_free(arg_configuration); + free(arg_address); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} |