bus-proxyd.c revision bc75205c773313a38a6958a6905a99bd56029196
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
Copyright 2013 Daniel Mack
Copyright 2014 Kay Sievers
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 <string.h>
#include <errno.h>
#include <stddef.h>
#include <getopt.h>
#include "log.h"
#include "util.h"
#include "socket-util.h"
#include "sd-daemon.h"
#include "sd-bus.h"
#include "bus-internal.h"
#include "bus-message.h"
#include "bus-util.h"
#include "build.h"
#include "strv.h"
#include "def.h"
#include "capability.h"
#include "bus-policy.h"
static char *arg_address = NULL;
static char *arg_command_line_buffer = NULL;
static bool arg_drop_privileges = false;
static char **arg_configuration = NULL;
static int help(void) {
printf("%s [OPTIONS...]\n\n"
"Connect STDIO or a socket to a given bus address.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --drop-privileges Drop privileges\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"
return 0;
}
enum {
ARG_VERSION = 0x100,
};
{},
};
int c, r;
switch (c) {
case 'h':
help();
return 0;
case ARG_VERSION:
return 0;
case ARG_ADDRESS: {
char *a;
if (!a)
return log_oom();
arg_address = a;
break;
}
case ARG_DROP_PRIVILEGES:
arg_drop_privileges = true;
break;
case ARG_CONFIGURATION:
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();
#ifdef ENABLE_KDBUS
#else
#endif
if (!a)
return log_oom();
arg_address = a;
break;
}
case '?':
return -EINVAL;
default:
assert_not_reached("Unhandled option");
}
/* If the first command line argument is only "x" characters
* we'll write who we are talking to into it, so that "ps" is
* explanatory */
log_error("Too many arguments");
return -EINVAL;
}
if (!arg_address) {
if (!arg_address)
return log_oom();
}
return 1;
}
const char *comm;
char **cmdline;
int r;
assert(a);
assert(b);
r = sd_bus_get_peer_creds(b, SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_COMM, &creds);
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
if (!name)
return -ENOMEM;
if (!p)
return -ENOMEM;
/* The status string gets the full command line ... */
sd_notifyf(false,
pid, p,
/* ... and the argv line only the short comm */
if (arg_command_line_buffer) {
size_t m, w;
w = snprintf(arg_command_line_buffer, m,
if (m > w)
memzero(arg_command_line_buffer + w, m - w);
}
pid, p,
a->unique_name);
return 0;
}
int r;
assert(a);
assert(b);
assert(m);
/* If we get NameOwnerChanged for our own name, we need to
* synthesize NameLost/NameAcquired, since socket clients need
* that, even though it is obsoleted on kdbus */
if (!a->is_kernel)
return 0;
return 0;
if (r < 0)
return r;
r = sd_bus_message_rewind(m, true);
if (r < 0)
return r;
b,
&n,
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"NameLost");
b,
&n,
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"NameAcquired");
} else
return 0;
if (r < 0)
return r;
if (r < 0)
return r;
r = bus_message_append_sender(n, "org.freedesktop.DBus");
if (r < 0)
return r;
r = bus_seal_synthetic_message(b, n);
if (r < 0)
return r;
return sd_bus_send(b, n, NULL);
}
int r;
assert(a);
assert(b);
assert(m);
if (!a->is_kernel)
return 0;
return 0;
return 0;
if (r < 0)
return r;
r = bus_message_append_sender(n, "org.freedesktop.DBus");
if (r < 0) {
return r;
}
r = bus_seal_synthetic_message(b, n);
if (r < 0) {
return r;
}
r = sd_bus_send(b, n, NULL);
if (r < 0) {
return r;
}
return 1;
}
int r;
assert(b);
assert(m);
r = bus_message_append_sender(m, "org.freedesktop.DBus");
if (r < 0)
return r;
r = bus_seal_synthetic_message(b, m);
if (r < 0)
return r;
return sd_bus_send(b, m, NULL);
}
int r;
return 0;
r = sd_bus_message_new_method_error(call, &m, e);
if (r < 0)
return r;
}
return 0;
if (sd_bus_error_is_set(p))
return synthetic_reply_method_error(call, p);
}
int r;
return 0;
r = sd_bus_message_new_method_return(call, &m);
if (r < 0)
return r;
if (r < 0)
return r;
}
}
int r;
r = sd_bus_message_new_method_return(call, &m);
if (r < 0)
r = sd_bus_message_append_strv(m, l);
if (r < 0)
}
static int get_creds_by_name(sd_bus *bus, const char *name, uint64_t mask, sd_bus_creds **_creds, sd_bus_error *error) {
int r;
return sd_bus_error_setf(error, SD_BUS_ERROR_NAME_HAS_NO_OWNER, "Name %s is currently not owned by anyone.", name);
if (r < 0)
return r;
return -ENOTSUP;
*_creds = c;
c = NULL;
return 0;
}
static int get_creds_by_message(sd_bus *bus, sd_bus_message *m, uint64_t mask, sd_bus_creds **_creds, sd_bus_error *error) {
const char *name;
int r;
assert(m);
if (r < 0)
return r;
}
int r;
if (r < 0)
return r;
if (r < 0)
return r;
if (r > 0)
return true;
return true;
return false;
}
int r;
assert(a);
assert(b);
assert(m);
if (!a->is_kernel)
return 0;
return 0;
if (0 && !isempty(sd_bus_message_get_signature(m, true))) {
return synthetic_reply_method_errno(m, r, &error);
}
return synthetic_reply_method_return(m, "s",
"<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "
"<node>\n"
" <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
" <method name=\"Introspect\">\n"
" <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"
" </method>\n"
" </interface>\n"
" <interface name=\"org.freedesktop.DBus\">\n"
" <method name=\"AddMatch\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" </method>\n"
" <method name=\"RemoveMatch\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" </method>\n"
" <method name=\"GetConnectionSELinuxSecurityContext\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" <arg type=\"ay\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"GetConnectionUnixProcessID\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" <arg type=\"u\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"GetConnectionUnixUser\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" <arg type=\"u\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"GetId\">\n"
" <arg type=\"s\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"GetNameOwner\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" <arg type=\"s\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"Hello\">\n"
" <arg type=\"s\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"ListActivatableNames\">\n"
" <arg type=\"as\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"ListNames\">\n"
" <arg type=\"as\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"ListQueuedOwners\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" <arg type=\"as\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"NameHasOwner\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" <arg type=\"b\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"ReleaseName\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" <arg type=\"u\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"ReloadConfig\">\n"
" </method>\n"
" <method name=\"RequestName\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" <arg type=\"u\" direction=\"in\"/>\n"
" <arg type=\"u\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"StartServiceByName\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" <arg type=\"u\" direction=\"in\"/>\n"
" <arg type=\"u\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"UpdateActivationEnvironment\">\n"
" <arg type=\"a{ss}\" direction=\"in\"/>\n"
" </method>\n"
" <signal name=\"NameAcquired\">\n"
" <arg type=\"s\"/>\n"
" </signal>\n"
" <signal name=\"NameLost\">\n"
" <arg type=\"s\"/>\n"
" </signal>\n"
" <signal name=\"NameOwnerChanged\">\n"
" <arg type=\"s\"/>\n"
" <arg type=\"s\"/>\n"
" <arg type=\"s\"/>\n"
" </signal>\n"
" </interface>\n"
"</node>\n");
const char *match;
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
return synthetic_reply_method_return(m, NULL);
const char *match;
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (r == 0)
return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_MATCH_RULE_NOT_FOUND, "Match rule not found"));
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
return synthetic_reply_method_return(m, NULL);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetConnectionSELinuxSecurityContext")) {
if (r < 0)
return synthetic_reply_method_errno(m, r, &error);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetConnectionUnixProcessID")) {
if (r < 0)
return synthetic_reply_method_errno(m, r, &error);
if (r < 0)
return synthetic_reply_method_errno(m, r, &error);
char buf[SD_ID128_STRING_MAX];
r = sd_bus_get_server_id(a, &server_id);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
const char *name;
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (r < 0)
return synthetic_reply_method_errno(m, r, &error);
/* "Hello" is handled in process_hello() */
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
/* Let's sort the names list to make it stable */
return synthetic_reply_return_strv(m, names);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
/* Let's sort the names list to make it stable */
return synthetic_reply_return_strv(m, names);
struct kdbus_cmd_name_list cmd = {};
struct kdbus_name_list *name_list;
struct kdbus_cmd_free cmd_free;
struct kdbus_name_info *name;
char *arg0;
int err = 0;
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (!service_name_is_valid(arg0))
sd_bus_error_setf(&error, SD_BUS_ERROR_NAME_HAS_NO_OWNER, "Could not get owners of name '%s': no such name.", arg0);
return synthetic_reply_method_errno(m, r, &error);
}
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (r < 0)
const char *entry_name = NULL;
struct kdbus_item *item;
char *n;
continue;
break;
}
r = strv_consume(&owners, n);
if (r < 0) {
err = r;
break;
}
}
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (err < 0)
return synthetic_reply_return_strv(m, owners);
const char *name;
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (!service_name_is_valid(name))
return synthetic_reply_method_return(m, "b", true);
return synthetic_reply_method_errno(m, r, NULL);
return synthetic_reply_method_return(m, "b", r >= 0);
const char *name;
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (!service_name_is_valid(name))
r = sd_bus_release_name(a, name);
if (r < 0) {
if (r == -ESRCH)
if (r == -EADDRINUSE)
return synthetic_reply_method_errno(m, r, NULL);
}
r = sd_bus_error_setf(&error, SD_BUS_ERROR_NOT_SUPPORTED, "%s() is not supported", sd_bus_message_get_member(m));
return synthetic_reply_method_errno(m, r, &error);
const char *name;
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (!service_name_is_valid(name))
param = 0;
if (flags & BUS_NAME_ALLOW_REPLACEMENT)
if (flags & BUS_NAME_REPLACE_EXISTING)
if (!(flags & BUS_NAME_DO_NOT_QUEUE))
if (r < 0) {
if (r == -EEXIST)
if (r == -EALREADY)
return synthetic_reply_method_errno(m, r, NULL);
}
if (r == 0)
const char *name;
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (!service_name_is_valid(name))
if (flags != 0)
if (r != -ESRCH)
return synthetic_reply_method_errno(m, r, NULL);
a,
&msg,
name,
"/",
"org.freedesktop.DBus.Peer",
"Ping");
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "UpdateActivationEnvironment")) {
if (!peer_is_privileged(a, m))
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
_cleanup_free_ char *s = NULL;
const char *key;
const char *value;
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (!s)
r = strv_extend(&args, s);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
r = sd_bus_message_exit_container(m);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
}
r = sd_bus_message_exit_container(m);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (!args)
a,
&msg,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"SetEnvironment");
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
return synthetic_reply_method_return(m, NULL);
} else {
return synthetic_reply_method_errno(m, r, &error);
}
}
bool is_hello;
int r;
assert(a);
assert(b);
assert(m);
/* As reaction to hello we need to respond with two messages:
* the callback reply and the NameAcquired for the unique
* name, since hello is otherwise obsolete on kdbus. */
is_hello =
if (!is_hello) {
if (*got_hello)
return 0;
return -EIO;
}
if (*got_hello) {
log_error("Got duplicate hello, aborting.");
return -EIO;
}
*got_hello = true;
if (!a->is_kernel)
return 0;
r = sd_bus_message_new_method_return(m, &n);
if (r < 0) {
return r;
}
if (r < 0) {
return r;
}
r = bus_message_append_sender(n, "org.freedesktop.DBus");
if (r < 0) {
return r;
}
r = bus_seal_synthetic_message(b, n);
if (r < 0) {
return r;
}
r = sd_bus_send(b, n, NULL);
if (r < 0) {
return r;
}
n = sd_bus_message_unref(n);
b,
&n,
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"NameAcquired");
if (r < 0) {
return r;
}
if (r < 0) {
return r;
}
r = bus_message_append_sender(n, "org.freedesktop.DBus");
if (r < 0) {
return r;
}
r = bus_seal_synthetic_message(b, n);
if (r < 0) {
return r;
}
r = sd_bus_send(b, n, NULL);
if (r < 0) {
return r;
}
return 1;
}
char **well_known = NULL;
sd_bus_creds *c;
int r;
assert(a);
assert(m);
if (!a->is_kernel)
return 0;
/* We will change the sender of messages from the bus driver
* so that they originate from the bus driver. This is a
* speciality originating from dbus1, where the bus driver did
* not have a unique id, but only the well-known name. */
c = sd_bus_message_get_creds(m);
if (!c)
return 0;
r = sd_bus_creds_get_well_known_names(c, &well_known);
if (r < 0)
return r;
m->sender = "org.freedesktop.DBus";
return 0;
}
bool got_hello = false;
bool is_unix;
log_open();
if (r <= 0)
goto finish;
if (r < 0) {
goto finish;
}
/* policy_dump(&policy); */
r = sd_listen_fds(0);
if (r == 0) {
} else if (r == 1) {
} else {
log_error("Illegal number of file descriptors passed");
goto finish;
}
is_unix =
if (is_unix) {
if (r < 0) {
goto finish;
}
}
if (arg_drop_privileges) {
const char *user = "systemd-bus-proxy";
if (r < 0) {
goto finish;
}
if (r < 0)
goto finish;
}
r = sd_bus_new(&a);
if (r < 0) {
goto finish;
}
r = sd_bus_set_name(a, "sd-proxy");
if (r < 0) {
goto finish;
}
r = sd_bus_set_address(a, arg_address);
if (r < 0) {
goto finish;
}
r = sd_bus_negotiate_fds(a, is_unix);
if (r < 0) {
goto finish;
}
a->fake_creds_valid = true;
}
if (peersec) {
a->fake_label = peersec;
}
a->manual_peer_interface = true;
r = sd_bus_start(a);
if (r < 0) {
goto finish;
}
r = sd_bus_get_server_id(a, &server_id);
if (r < 0) {
goto finish;
}
r = sd_bus_new(&b);
if (r < 0) {
goto finish;
}
if (r < 0) {
goto finish;
}
if (r < 0) {
goto finish;
}
r = sd_bus_negotiate_fds(b, is_unix);
if (r < 0) {
goto finish;
}
r = sd_bus_set_anonymous(b, true);
if (r < 0) {
goto finish;
}
b->manual_peer_interface = true;
r = sd_bus_start(b);
if (r < 0) {
goto finish;
}
r = rename_service(a, b);
if (r < 0)
if (a->is_kernel) {
const char *unique;
r = sd_bus_get_unique_name(a, &unique);
if (r < 0) {
goto finish;
}
"sender='org.freedesktop.DBus',"
"path='/org/freedesktop/DBus',"
"interface='org.freedesktop.DBus',"
"member='NameOwnerChanged',"
"arg1='",
"'",
NULL);
if (!match) {
log_oom();
goto finish;
}
if (r < 0) {
goto finish;
}
"sender='org.freedesktop.DBus',"
"path='/org/freedesktop/DBus',"
"interface='org.freedesktop.DBus',"
"member='NameOwnerChanged',"
"arg2='",
"'",
NULL);
if (!match) {
log_oom();
goto finish;
}
if (r < 0) {
goto finish;
}
}
for (;;) {
int k;
if (got_hello) {
r = sd_bus_process(a, &m);
if (r < 0) {
/* treat 'connection reset by peer' as clean exit condition */
if (r == -ECONNRESET)
r = 0;
else
goto finish;
}
if (m) {
/* We officially got EOF, let's quit */
r = 0;
goto finish;
}
k = synthesize_name_acquired(a, b, m);
if (k < 0) {
r = k;
goto finish;
}
patch_sender(a, m);
k = sd_bus_send(b, m, NULL);
if (k < 0) {
if (k == -ECONNRESET)
r = 0;
else {
r = k;
}
goto finish;
}
}
if (r > 0)
continue;
}
r = sd_bus_process(b, &m);
if (r < 0) {
/* treat 'connection reset by peer' as clean exit condition */
if (r == -ECONNRESET)
r = 0;
else
goto finish;
}
if (m) {
/* We officially got EOF, let's quit */
r = 0;
goto finish;
}
k = process_hello(a, b, m, &got_hello);
if (k < 0) {
r = k;
goto finish;
}
if (k > 0)
r = k;
else {
k = process_policy(a, b, m);
if (k < 0) {
r = k;
goto finish;
}
k = process_driver(a, b, m);
if (k < 0) {
r = k;
goto finish;
}
if (k > 0)
r = k;
else {
k = sd_bus_send(a, m, NULL);
if (k < 0) {
if (k == -ECONNRESET)
r = 0;
else {
r = k;
}
goto finish;
}
}
}
}
if (r > 0)
continue;
fd = sd_bus_get_fd(a);
if (fd < 0) {
goto finish;
}
events_a = sd_bus_get_events(a);
if (events_a < 0) {
goto finish;
}
r = sd_bus_get_timeout(a, &timeout_a);
if (r < 0) {
goto finish;
}
events_b = sd_bus_get_events(b);
if (events_b < 0) {
goto finish;
}
r = sd_bus_get_timeout(b, &timeout_b);
if (r < 0) {
goto finish;
}
t = timeout_a;
t = timeout_b;
if (t == (uint64_t) -1)
else {
if (t > nw)
t -= nw;
else
t = 0;
}
};
if (r < 0) {
log_error("ppoll() failed: %m");
goto finish;
}
}
sd_notify(false,
"STOPPING=1\n"
"STATUS=Shutting down.");
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}