Merge pull request #17967 from poettering/connect-user-bus

add support for "systemctl --user --machine=foobar@.host" for connecting to user bus of user "foobar"
This commit is contained in:
Lennart Poettering 2020-12-15 21:14:01 +01:00 committed by GitHub
commit 94b78105c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 409 additions and 160 deletions

View File

@ -24,6 +24,7 @@
<refname>sd_bus_open_with_description</refname> <refname>sd_bus_open_with_description</refname>
<refname>sd_bus_open_user</refname> <refname>sd_bus_open_user</refname>
<refname>sd_bus_open_user_with_description</refname> <refname>sd_bus_open_user_with_description</refname>
<refname>sd_bus_open_user_machine</refname>
<refname>sd_bus_open_system</refname> <refname>sd_bus_open_system</refname>
<refname>sd_bus_open_system_with_description</refname> <refname>sd_bus_open_system_with_description</refname>
<refname>sd_bus_open_system_remote</refname> <refname>sd_bus_open_system_remote</refname>
@ -73,6 +74,12 @@
<paramdef>const char *<parameter>description</parameter></paramdef> <paramdef>const char *<parameter>description</parameter></paramdef>
</funcprototype> </funcprototype>
<funcprototype>
<funcdef>int <function>sd_bus_open_user_machine</function></funcdef>
<paramdef>sd_bus **<parameter>bus</parameter></paramdef>
<paramdef>const char *<parameter>machine</parameter></paramdef>
</funcprototype>
<funcprototype> <funcprototype>
<funcdef>int <function>sd_bus_open_system</function></funcdef> <funcdef>int <function>sd_bus_open_system</function></funcdef>
<paramdef>sd_bus **<parameter>bus</parameter></paramdef> <paramdef>sd_bus **<parameter>bus</parameter></paramdef>
@ -187,14 +194,24 @@
work for the root user on the remote machine.</para> work for the root user on the remote machine.</para>
<para><function>sd_bus_open_system_machine()</function> connects to the system bus in the specified <para><function>sd_bus_open_system_machine()</function> connects to the system bus in the specified
<parameter>machine</parameter>, where <parameter>machine</parameter> is the name of a local <parameter>machine</parameter>, where <parameter>machine</parameter> is the name of a local container,
container. See possibly prefixed by a user name and a separating <literal>@</literal>. If the container name is
specified as the special string <literal>.host</literal> the connection is made to the local system. This
is useful to connect to the local system bus as specific user, e.g. <literal>foobar@.host</literal> to
connect to the local system bus as local user <literal>foobar</literal>. If the <literal>@</literal>
syntax is used either the left-hand side or the right-hand side may be ommited (but not both) in which
case the local user name or <literal>.host</literal> is implied. If the <literal>@</literal> syntax is
not used the connection is always made as root user. See
<citerefentry><refentrytitle>sd_bus_set_address</refentrytitle><manvolnum>3</manvolnum></citerefentry> <citerefentry><refentrytitle>sd_bus_set_address</refentrytitle><manvolnum>3</manvolnum></citerefentry>
for a description of the address syntax, and for a description of the address syntax, and
<citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for more <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for more
information about the "machine" concept. Note that connections into local containers are only available information about the "machine" concept. Note that connections into local containers are only available
to privileged processes at this time.</para> to privileged processes at this time.</para>
<para><function>sd_bus_open_user_machine()</function> is similar to
<function>sd_bus_open_system_machine()</function>, but connects to the user bus of the root user, or if
the <literal>@</literal> syntax is used, of the specified user.</para>
<para>These calls allocate a bus connection object and initiate <para>These calls allocate a bus connection object and initiate
the connection to a well-known bus of some form. An alternative to the connection to a well-known bus of some form. An alternative to
using these high-level calls is to create an unconnected bus using these high-level calls is to create an unconnected bus
@ -210,6 +227,7 @@
<title>Reference ownership</title> <title>Reference ownership</title>
<para>The functions <function>sd_bus_open()</function>, <para>The functions <function>sd_bus_open()</function>,
<function>sd_bus_open_user()</function>, <function>sd_bus_open_user()</function>,
<function>sd_bus_open_user_machine()</function>,
<function>sd_bus_open_system()</function>, <function>sd_bus_open_system()</function>,
<function>sd_bus_open_system_remote()</function>, and <function>sd_bus_open_system_remote()</function>, and
<function>sd_bus_open_system_machine()</function> return a new <function>sd_bus_open_system_machine()</function> return a new

View File

@ -45,8 +45,14 @@
<term><option>--machine=</option></term> <term><option>--machine=</option></term>
<listitem id='machine-text'> <listitem id='machine-text'>
<para>Execute operation on a local container. Specify a <para>Execute operation on a local container. Specify a container name to connect to, optionally
container name to connect to.</para> prefixed by a user name to connect as and a separating <literal>@</literal> character. If the special
string <literal>.host</literal> is used in place of the container name, a connection to the local
system is made (which is useful to connect to a specific user's user bus: <literal>--user
--machine=lennart@.host</literal>). If the <literal>@</literal> syntax is not used, the connection is
made as root user. If the <literal>@</literal> syntax is used either the left hand side or the right hand
side may be ommitted (but not both) in which case the local user name and <literal>.host</literal> are
implied.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
</variablelist> </variablelist>

View File

@ -89,6 +89,8 @@ int gethostname_strict(char **ret) {
} }
bool valid_ldh_char(char c) { bool valid_ldh_char(char c) {
/* "LDH" → "Letters, digits, hyphens", as per RFC 5890, Section 2.3.1 */
return return
(c >= 'a' && c <= 'z') || (c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') || (c >= 'A' && c <= 'Z') ||
@ -96,28 +98,24 @@ bool valid_ldh_char(char c) {
c == '-'; c == '-';
} }
/** bool hostname_is_valid(const char *s, ValidHostnameFlags flags) {
* Check if s looks like a valid hostname or FQDN. This does not do
* full DNS validation, but only checks if the name is composed of
* allowed characters and the length is not above the maximum allowed
* by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if
* allow_trailing_dot is true and at least two components are present
* in the name. Note that due to the restricted charset and length
* this call is substantially more conservative than
* dns_name_is_valid().
*/
bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
unsigned n_dots = 0; unsigned n_dots = 0;
const char *p; const char *p;
bool dot, hyphen; bool dot, hyphen;
/* Check if s looks like a valid hostname or FQDN. This does not do full DNS validation, but only
* checks if the name is composed of allowed characters and the length is not above the maximum
* allowed by Linux (c.f. dns_name_is_valid()). A trailing dot is allowed if
* VALID_HOSTNAME_TRAILING_DOT flag is set and at least two components are present in the name. Note
* that due to the restricted charset and length this call is substantially more conservative than
* dns_name_is_valid(). Doesn't accept empty hostnames, hostnames with leading dots, and hostnames
* with multiple dots in a sequence. Doesn't allow hyphens at the beginning or end of label. */
if (isempty(s)) if (isempty(s))
return false; return false;
/* Doesn't accept empty hostnames, hostnames with if (streq(s, ".host")) /* Used by the container logic to denote the "root container" */
* leading dots, and hostnames with multiple dots in a return FLAGS_SET(flags, VALID_HOSTNAME_DOT_HOST);
* sequence. Also ensures that the length stays below
* HOST_NAME_MAX. */
for (p = s, dot = hyphen = true; *p; p++) for (p = s, dot = hyphen = true; *p; p++)
if (*p == '.') { if (*p == '.') {
@ -143,14 +141,13 @@ bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
hyphen = false; hyphen = false;
} }
if (dot && (n_dots < 2 || !allow_trailing_dot)) if (dot && (n_dots < 2 || !FLAGS_SET(flags, VALID_HOSTNAME_TRAILING_DOT)))
return false; return false;
if (hyphen) if (hyphen)
return false; return false;
if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on Linux, but DNS allows domain names up to
* Linux, but DNS allows domain names * 255 characters */
* up to 255 characters */
return false; return false;
return true; return true;
@ -241,7 +238,7 @@ int shorten_overlong(const char *s, char **ret) {
if (!h) if (!h)
return -ENOMEM; return -ENOMEM;
if (hostname_is_valid(h, false)) { if (hostname_is_valid(h, 0)) {
*ret = h; *ret = h;
return 0; return 0;
} }
@ -252,7 +249,7 @@ int shorten_overlong(const char *s, char **ret) {
strshorten(h, HOST_NAME_MAX); strshorten(h, HOST_NAME_MAX);
if (!hostname_is_valid(h, false)) { if (!hostname_is_valid(h, 0)) {
free(h); free(h);
return -EDOM; return -EDOM;
} }
@ -285,7 +282,7 @@ int read_etc_hostname_stream(FILE *f, char **ret) {
hostname_cleanup(p); /* normalize the hostname */ hostname_cleanup(p); /* normalize the hostname */
if (!hostname_is_valid(p, true)) /* check that the hostname we return is valid */ if (!hostname_is_valid(p, VALID_HOSTNAME_TRAILING_DOT)) /* check that the hostname we return is valid */
return -EBADMSG; return -EBADMSG;
copy = strdup(p); copy = strdup(p);

View File

@ -14,10 +14,14 @@ char* gethostname_short_malloc(void);
int gethostname_strict(char **ret); int gethostname_strict(char **ret);
bool valid_ldh_char(char c) _const_; bool valid_ldh_char(char c) _const_;
bool hostname_is_valid(const char *s, bool allow_trailing_dot) _pure_;
char* hostname_cleanup(char *s);
#define machine_name_is_valid(s) hostname_is_valid(s, false) typedef enum ValidHostnameFlags {
VALID_HOSTNAME_TRAILING_DOT = 1 << 0, /* Accept trailing dot on multi-label names */
VALID_HOSTNAME_DOT_HOST = 1 << 1, /* Accept ".host" as valid hostname */
} ValidHostnameFlags;
bool hostname_is_valid(const char *s, ValidHostnameFlags flags) _pure_;
char* hostname_cleanup(char *s);
bool is_localhost(const char *hostname); bool is_localhost(const char *hostname);

View File

@ -165,7 +165,7 @@ int container_get_leader(const char *machine, pid_t *pid) {
return 0; return 0;
} }
if (!machine_name_is_valid(machine)) if (!hostname_is_valid(machine, 0))
return -EINVAL; return -EINVAL;
p = strjoina("/run/systemd/machines/", machine); p = strjoina("/run/systemd/machines/", machine);

View File

@ -121,7 +121,7 @@ static int acquire_bus(bool set_monitor, sd_bus **ret) {
break; break;
case BUS_TRANSPORT_MACHINE: case BUS_TRANSPORT_MACHINE:
r = bus_set_address_system_machine(bus, arg_host); r = bus_set_address_machine(bus, arg_user, arg_host);
break; break;
default: default:

View File

@ -24,7 +24,7 @@ int hostname_setup(void) {
if (r < 0) if (r < 0)
log_warning_errno(r, "Failed to retrieve system hostname from kernel command line, ignoring: %m"); log_warning_errno(r, "Failed to retrieve system hostname from kernel command line, ignoring: %m");
else if (r > 0) { else if (r > 0) {
if (hostname_is_valid(b, true)) if (hostname_is_valid(b, VALID_HOSTNAME_TRAILING_DOT))
hn = b; hn = b;
else { else {
log_warning("Hostname specified on kernel command line is invalid, ignoring: %s", b); log_warning("Hostname specified on kernel command line is invalid, ignoring: %s", b);
@ -34,12 +34,11 @@ int hostname_setup(void) {
if (!hn) { if (!hn) {
r = read_etc_hostname(NULL, &b); r = read_etc_hostname(NULL, &b);
if (r < 0) { if (r == -ENOENT)
if (r == -ENOENT) enoent = true;
enoent = true; else if (r < 0)
else log_warning_errno(r, "Failed to read configured hostname, ignoring: %m");
log_warning_errno(r, "Failed to read configured hostname: %m"); else
} else
hn = b; hn = b;
} }

View File

@ -493,7 +493,7 @@ static int prompt_hostname(void) {
break; break;
} }
if (!hostname_is_valid(h, true)) { if (!hostname_is_valid(h, VALID_HOSTNAME_TRAILING_DOT)) {
log_error("Specified hostname invalid."); log_error("Specified hostname invalid.");
continue; continue;
} }
@ -1135,15 +1135,15 @@ static int parse_argv(int argc, char *argv[]) {
break; break;
case ARG_HOSTNAME: case ARG_HOSTNAME:
if (!hostname_is_valid(optarg, true)) if (!hostname_is_valid(optarg, VALID_HOSTNAME_TRAILING_DOT))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Host name %s is not valid.", optarg); "Host name %s is not valid.", optarg);
hostname_cleanup(optarg);
r = free_and_strdup(&arg_hostname, optarg); r = free_and_strdup(&arg_hostname, optarg);
if (r < 0) if (r < 0)
return log_oom(); return log_oom();
hostname_cleanup(arg_hostname);
break; break;
case ARG_MACHINE_ID: case ARG_MACHINE_ID:

View File

@ -248,7 +248,7 @@ static int set_hostname(int argc, char **argv, void *userdata) {
/* If the passed hostname is already valid, then assume the user doesn't know anything about pretty /* If the passed hostname is already valid, then assume the user doesn't know anything about pretty
* hostnames, so let's unset the pretty hostname, and just set the passed hostname as static/dynamic * hostnames, so let's unset the pretty hostname, and just set the passed hostname as static/dynamic
* hostname. */ * hostname. */
if (arg_static && hostname_is_valid(hostname, true)) if (arg_static && hostname_is_valid(hostname, VALID_HOSTNAME_TRAILING_DOT))
p = ""; /* No pretty hostname (as it is redundant), just a static one */ p = ""; /* No pretty hostname (as it is redundant), just a static one */
else else
p = hostname; /* Use the passed name as pretty hostname */ p = hostname; /* Use the passed name as pretty hostname */

View File

@ -596,7 +596,7 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *
if (isempty(name)) if (isempty(name))
name = FALLBACK_HOSTNAME; name = FALLBACK_HOSTNAME;
if (!hostname_is_valid(name, false)) if (!hostname_is_valid(name, 0))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name); return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name);
assert_se(uname(&u) >= 0); assert_se(uname(&u) >= 0);
@ -650,7 +650,7 @@ static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_
if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME])) if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME]))
return sd_bus_reply_method_return(m, NULL); return sd_bus_reply_method_return(m, NULL);
if (!isempty(name) && !hostname_is_valid(name, false)) if (!isempty(name) && !hostname_is_valid(name, 0))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name); return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name);
r = bus_verify_polkit_async( r = bus_verify_polkit_async(

View File

@ -65,7 +65,7 @@ static int export_tar(int argc, char *argv[], void *userdata) {
_cleanup_close_ int open_fd = -1; _cleanup_close_ int open_fd = -1;
int r, fd; int r, fd;
if (machine_name_is_valid(argv[1])) { if (hostname_is_valid(argv[1], 0)) {
r = image_find(IMAGE_MACHINE, argv[1], &image); r = image_find(IMAGE_MACHINE, argv[1], &image);
if (r == -ENOENT) if (r == -ENOENT)
return log_error_errno(r, "Machine image %s not found.", argv[1]); return log_error_errno(r, "Machine image %s not found.", argv[1]);
@ -141,7 +141,7 @@ static int export_raw(int argc, char *argv[], void *userdata) {
_cleanup_close_ int open_fd = -1; _cleanup_close_ int open_fd = -1;
int r, fd; int r, fd;
if (machine_name_is_valid(argv[1])) { if (hostname_is_valid(argv[1], 0)) {
r = image_find(IMAGE_MACHINE, argv[1], &image); r = image_find(IMAGE_MACHINE, argv[1], &image);
if (r == -ENOENT) if (r == -ENOENT)
return log_error_errno(r, "Machine image %s not found.", argv[1]); return log_error_errno(r, "Machine image %s not found.", argv[1]);

View File

@ -126,7 +126,7 @@ static int import_fs(int argc, char *argv[], void *userdata) {
local = empty_or_dash_to_null(local); local = empty_or_dash_to_null(local);
if (local) { if (local) {
if (!machine_name_is_valid(local)) if (!hostname_is_valid(local, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local image name '%s' is not valid.", "Local image name '%s' is not valid.",
local); local);

View File

@ -393,7 +393,7 @@ int raw_import_start(RawImport *i, int fd, const char *local, bool force_local,
assert(fd >= 0); assert(fd >= 0);
assert(local); assert(local);
if (!machine_name_is_valid(local)) if (!hostname_is_valid(local, 0))
return -EINVAL; return -EINVAL;
if (i->input_fd >= 0) if (i->input_fd >= 0)

View File

@ -329,7 +329,7 @@ int tar_import_start(TarImport *i, int fd, const char *local, bool force_local,
assert(fd >= 0); assert(fd >= 0);
assert(local); assert(local);
if (!machine_name_is_valid(local)) if (!hostname_is_valid(local, 0))
return -EINVAL; return -EINVAL;
if (i->input_fd >= 0) if (i->input_fd >= 0)

View File

@ -64,7 +64,7 @@ static int import_tar(int argc, char *argv[], void *userdata) {
local = ll; local = ll;
if (!machine_name_is_valid(local)) if (!hostname_is_valid(local, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local image name '%s' is not valid.", "Local image name '%s' is not valid.",
local); local);
@ -159,7 +159,7 @@ static int import_raw(int argc, char *argv[], void *userdata) {
local = ll; local = ll;
if (!machine_name_is_valid(local)) if (!hostname_is_valid(local, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local image name '%s' is not valid.", "Local image name '%s' is not valid.",
local); local);

View File

@ -717,7 +717,7 @@ static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
if (!S_ISREG(st.st_mode) && !S_ISFIFO(st.st_mode)) if (!S_ISREG(st.st_mode) && !S_ISFIFO(st.st_mode))
return -EINVAL; return -EINVAL;
if (!machine_name_is_valid(local)) if (!hostname_is_valid(local, 0))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Local name %s is invalid", local); "Local name %s is invalid", local);
@ -787,7 +787,7 @@ static int method_import_fs(sd_bus_message *msg, void *userdata, sd_bus_error *e
if (r < 0) if (r < 0)
return r; return r;
if (!machine_name_is_valid(local)) if (!hostname_is_valid(local, 0))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Local name %s is invalid", local); "Local name %s is invalid", local);
@ -852,7 +852,7 @@ static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
if (r < 0) if (r < 0)
return r; return r;
if (!machine_name_is_valid(local)) if (!hostname_is_valid(local, 0))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Local name %s is invalid", local); "Local name %s is invalid", local);
@ -932,7 +932,7 @@ static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_er
if (isempty(local)) if (isempty(local))
local = NULL; local = NULL;
else if (!machine_name_is_valid(local)) else if (!hostname_is_valid(local, 0))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Local name %s is invalid", local); "Local name %s is invalid", local);

View File

@ -651,7 +651,7 @@ int raw_pull_start(
if (!http_url_is_valid(url)) if (!http_url_is_valid(url))
return -EINVAL; return -EINVAL;
if (local && !machine_name_is_valid(local)) if (local && !hostname_is_valid(local, 0))
return -EINVAL; return -EINVAL;
if (i->raw_job) if (i->raw_job)

View File

@ -481,7 +481,7 @@ int tar_pull_start(
if (!http_url_is_valid(url)) if (!http_url_is_valid(url))
return -EINVAL; return -EINVAL;
if (local && !machine_name_is_valid(local)) if (local && !hostname_is_valid(local, 0))
return -EINVAL; return -EINVAL;
if (i->tar_job) if (i->tar_job)

View File

@ -72,7 +72,7 @@ static int pull_tar(int argc, char *argv[], void *userdata) {
local = ll; local = ll;
if (!machine_name_is_valid(local)) if (!hostname_is_valid(local, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local image name '%s' is not valid.", "Local image name '%s' is not valid.",
local); local);
@ -158,7 +158,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
local = ll; local = ll;
if (!machine_name_is_valid(local)) if (!hostname_is_valid(local, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local image name '%s' is not valid.", "Local image name '%s' is not valid.",
local); local);

View File

@ -1984,7 +1984,7 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in
assert_return(machine, -EINVAL); assert_return(machine, -EINVAL);
assert_return(ret, -EINVAL); assert_return(ret, -EINVAL);
assert_return((flags & ~OPEN_CONTAINER_ALLOWED_FLAGS) == 0, -EINVAL); assert_return((flags & ~OPEN_CONTAINER_ALLOWED_FLAGS) == 0, -EINVAL);
assert_return(machine_name_is_valid(machine), -EINVAL); assert_return(hostname_is_valid(machine, 0), -EINVAL);
p = strjoina("/run/systemd/machines/", machine); p = strjoina("/run/systemd/machines/", machine);
r = parse_env_file(NULL, p, r = parse_env_file(NULL, p,

View File

@ -544,7 +544,7 @@ int sd_dhcp_client_set_hostname(
/* Make sure hostnames qualify as DNS and as Linux hostnames */ /* Make sure hostnames qualify as DNS and as Linux hostnames */
if (hostname && if (hostname &&
!(hostname_is_valid(hostname, false) && dns_name_is_valid(hostname) > 0)) !(hostname_is_valid(hostname, 0) && dns_name_is_valid(hostname) > 0))
return -EINVAL; return -EINVAL;
return free_and_strdup(&client->hostname, hostname); return free_and_strdup(&client->hostname, hostname);

View File

@ -407,7 +407,7 @@ int sd_dhcp6_client_set_fqdn(
/* Make sure FQDN qualifies as DNS and as Linux hostname */ /* Make sure FQDN qualifies as DNS and as Linux hostname */
if (fqdn && if (fqdn &&
!(hostname_is_valid(fqdn, false) && dns_name_is_valid(fqdn) > 0)) !(hostname_is_valid(fqdn, 0) && dns_name_is_valid(fqdn) > 0))
return -EINVAL; return -EINVAL;
return free_and_strdup(&client->fqdn, fqdn); return free_and_strdup(&client->fqdn, fqdn);

View File

@ -739,6 +739,8 @@ global:
LIBSYSTEMD_248 { LIBSYSTEMD_248 {
global: global:
sd_bus_open_user_machine;
sd_event_source_set_ratelimit; sd_event_source_set_ratelimit;
sd_event_source_get_ratelimit; sd_event_source_get_ratelimit;
sd_event_source_is_ratelimited; sd_event_source_is_ratelimited;

View File

@ -49,7 +49,7 @@ int bus_container_connect_socket(sd_bus *b) {
bus_socket_setup(b); bus_socket_setup(b);
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pair) < 0)
return -errno; return -errno;
r = namespace_fork("(sd-buscntrns)", "(sd-buscntr)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG, r = namespace_fork("(sd-buscntrns)", "(sd-buscntr)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG,

View File

@ -713,7 +713,7 @@ _public_ int sd_bus_get_name_creds(
} }
r = bus_creds_add_more(c, mask, pid, 0); r = bus_creds_add_more(c, mask, pid, 0);
if (r < 0) if (r < 0 && r != -ESRCH) /* Return the error, but ignore ESRCH which just means the process is already gone */
return r; return r;
} }
@ -788,7 +788,7 @@ _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **r
} }
r = bus_creds_add_more(c, mask, pid, 0); r = bus_creds_add_more(c, mask, pid, 0);
if (r < 0) if (r < 0 && r != -ESRCH) /* If the process vanished, then don't complain, just return what we got */
return r; return r;
*ret = TAKE_PTR(c); *ret = TAKE_PTR(c);

View File

@ -603,13 +603,15 @@ _public_ int sd_bus_set_property(
return r; return r;
} }
_public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **creds) { _public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **ret) {
sd_bus_creds *c; sd_bus_creds *c;
int r;
assert_return(call, -EINVAL); assert_return(call, -EINVAL);
assert_return(call->sealed, -EPERM); assert_return(call->sealed, -EPERM);
assert_return(call->bus, -EINVAL); assert_return(call->bus, -EINVAL);
assert_return(!bus_pid_changed(call->bus), -ECHILD); assert_return(!bus_pid_changed(call->bus), -ECHILD);
assert_return(ret, -EINVAL);
if (!BUS_IS_OPEN(call->bus->state)) if (!BUS_IS_OPEN(call->bus->state))
return -ENOTCONN; return -ENOTCONN;
@ -618,7 +620,7 @@ _public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_b
/* All data we need? */ /* All data we need? */
if (c && (mask & ~c->mask) == 0) { if (c && (mask & ~c->mask) == 0) {
*creds = sd_bus_creds_ref(c); *ret = sd_bus_creds_ref(c);
return 0; return 0;
} }
@ -629,15 +631,22 @@ _public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_b
if (call->sender) if (call->sender)
/* There's a sender, but the creds are missing. */ /* There's a sender, but the creds are missing. */
return sd_bus_get_name_creds(call->bus, call->sender, mask, creds); return sd_bus_get_name_creds(call->bus, call->sender, mask, ret);
else else
/* There's no sender. For direct connections /* There's no sender. For direct connections
* the credentials of the AF_UNIX peer matter, * the credentials of the AF_UNIX peer matter,
* which may be queried via sd_bus_get_owner_creds(). */ * which may be queried via sd_bus_get_owner_creds(). */
return sd_bus_get_owner_creds(call->bus, mask, creds); return sd_bus_get_owner_creds(call->bus, mask, ret);
} }
return bus_creds_extend_by_pid(c, mask, creds); r = bus_creds_extend_by_pid(c, mask, ret);
if (r == -ESRCH) {
/* Process doesn't exist anymore? propagate the few things we have */
*ret = sd_bus_creds_ref(c);
return 0;
}
return r;
} }
_public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) { _public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) {

View File

@ -401,7 +401,7 @@ void bus_close_io_fds(sd_bus *b);
int bus_set_address_system(sd_bus *bus); int bus_set_address_system(sd_bus *bus);
int bus_set_address_user(sd_bus *bus); int bus_set_address_user(sd_bus *bus);
int bus_set_address_system_remote(sd_bus *b, const char *host); int bus_set_address_system_remote(sd_bus *b, const char *host);
int bus_set_address_system_machine(sd_bus *b, const char *machine); int bus_set_address_machine(sd_bus *b, bool user, const char *machine);
int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error); int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error);

View File

@ -41,6 +41,7 @@
#include "process-util.h" #include "process-util.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
#include "user-util.h"
#define log_debug_bus_message(m) \ #define log_debug_bus_message(m) \
do { \ do { \
@ -973,7 +974,7 @@ static int parse_container_unix_address(sd_bus *b, const char **p, char **guid)
return -EINVAL; return -EINVAL;
if (machine) { if (machine) {
if (!streq(machine, ".host") && !machine_name_is_valid(machine)) if (!hostname_is_valid(machine, VALID_HOSTNAME_DOT_HOST))
return -EINVAL; return -EINVAL;
free_and_replace(b->machine, machine); free_and_replace(b->machine, machine);
@ -1448,7 +1449,7 @@ int bus_set_address_system_remote(sd_bus *b, const char *host) {
} }
if (!in_charset(p, "0123456789") || *p == '\0') { if (!in_charset(p, "0123456789") || *p == '\0') {
if (!machine_name_is_valid(p) || got_forward_slash) if (!hostname_is_valid(p, 0) || got_forward_slash)
return -EINVAL; return -EINVAL;
m = TAKE_PTR(p); m = TAKE_PTR(p);
@ -1463,7 +1464,7 @@ int bus_set_address_system_remote(sd_bus *b, const char *host) {
interpret_port_as_machine_old_syntax: interpret_port_as_machine_old_syntax:
/* Let's make sure this is not a port of some kind, /* Let's make sure this is not a port of some kind,
* and is a valid machine name. */ * and is a valid machine name. */
if (!in_charset(m, "0123456789") && machine_name_is_valid(m)) if (!in_charset(m, "0123456789") && hostname_is_valid(m, 0))
c = strjoina(",argv", p ? "7" : "5", "=--machine=", m); c = strjoina(",argv", p ? "7" : "5", "=--machine=", m);
} }
@ -1514,44 +1515,228 @@ _public_ int sd_bus_open_system_remote(sd_bus **ret, const char *host) {
return 0; return 0;
} }
int bus_set_address_system_machine(sd_bus *b, const char *machine) { int bus_set_address_machine(sd_bus *b, bool user, const char *machine) {
_cleanup_free_ char *e = NULL; const char *rhs;
char *a; char *a;
assert(b); assert(b);
assert(machine); assert(machine);
e = bus_address_escape(machine); rhs = strchr(machine, '@');
if (!e) if (rhs || user) {
return -ENOMEM; _cleanup_free_ char *u = NULL, *eu = NULL, *erhs = NULL;
a = strjoin("x-machine-unix:machine=", e); /* If there's an "@" in the container specification, we'll connect as a user specified at its
if (!a) * left hand side, which is useful in combination with user=true. This isn't as trivial as it
return -ENOMEM; * might sound: it's not sufficient to enter the container and connect to some socket there,
* since the --user socket path depends on $XDG_RUNTIME_DIR which is set via PAM. Thus, to be
* able to connect, we need to have a PAM session. Our way out? We use systemd-run to get
* into the container and acquire a PAM session there, and then invoke systemd-stdio-bridge
* in it, which propagates the bus transport to us.*/
if (rhs) {
if (rhs > machine)
u = strndup(machine, rhs - machine);
else
u = getusername_malloc(); /* Empty user name, let's use the local one */
if (!u)
return -ENOMEM;
eu = bus_address_escape(u);
if (!eu)
return -ENOMEM;
rhs++;
} else {
/* No "@" specified but we shall connect to the user instance? Then assume root (and
* not a user named identically to the calling one). This means:
*
* --machine=foobar --user connect to user bus of root user in container "foobar"
* --machine=@foobar --user connect to user bus of user named like the calling user in container "foobar"
*
* Why? so that behaviour for "--machine=foobar --system" is roughly similar to
* "--machine=foobar --user": both times we unconditionally connect as root user
* regardless what the calling user is. */
rhs = machine;
}
if (!isempty(rhs)) {
erhs = bus_address_escape(rhs);
if (!erhs)
return -ENOMEM;
}
/* systemd-run -M… -PGq --wait -pUser=… -pPAMName=login systemd-stdio-bridge */
a = strjoin("unixexec:path=systemd-run,"
"argv1=-M", erhs ?: ".host", ","
"argv2=-PGq,"
"argv3=--wait,"
"argv4=-pUser%3d", eu ?: "root", ",",
"argv5=-pPAMName%3dlogin,"
"argv6=systemd-stdio-bridge");
if (!a)
return -ENOMEM;
if (user) {
char *k;
/* Ideally we'd use the "--user" switch to systemd-stdio-bridge here, but it's only
* available in recent systemd versions. Using the "-p" switch with the explicit path
* is a working alternative, and is compatible with older versions, hence that's what
* we use here. */
k = strjoin(a, ",argv7=-punix:path%3d%24%7bXDG_RUNTIME_DIR%7d/bus");
if (!k)
return -ENOMEM;
free_and_replace(a, k);
}
} else {
_cleanup_free_ char *e = NULL;
/* Just a container name, we can go the simple way, and just join the container, and connect
* to the well-known path of the system bus there. */
e = bus_address_escape(machine);
if (!e)
return -ENOMEM;
a = strjoin("x-machine-unix:machine=", e);
if (!a)
return -ENOMEM;
}
return free_and_replace(b->address, a); return free_and_replace(b->address, a);
} }
_public_ int sd_bus_open_system_machine(sd_bus **ret, const char *machine) { static int user_and_machine_valid(const char *user_and_machine) {
const char *h;
/* Checks if a container specification in the form "user@container" or just "container" is valid.
*
* If the "@" syntax is used we'll allow either the "user" or the "container" part to be omitted, but
* not both. */
h = strchr(user_and_machine, '@');
if (!h)
h = user_and_machine;
else {
_cleanup_free_ char *user = NULL;
user = strndup(user_and_machine, h - user_and_machine);
if (!user)
return -ENOMEM;
if (!isempty(user) && !valid_user_group_name(user, VALID_USER_RELAX))
return false;
h++;
if (isempty(h))
return !isempty(user);
}
return hostname_is_valid(h, VALID_HOSTNAME_DOT_HOST);
}
static int user_and_machine_equivalent(const char *user_and_machine) {
_cleanup_free_ char *un = NULL;
const char *f;
/* Returns true if the specified user+machine name are actually equivalent to our own identity and
* our own host. If so we can shortcut things. Why bother? Because that way we don't have to fork
* off short-lived worker processes that are then unavailable for authentication and logging in the
* peer. Moreover joining a namespace requires privileges. If we are in the right namespace anyway,
* we can avoid permission problems thus. */
assert(user_and_machine);
/* Omitting the user name means that we shall use the same user name as we run as locally, which
* means we'll end up on the same host, let's shortcut */
if (streq(user_and_machine, "@.host"))
return true;
/* Otherwise, if we are root, then we can also allow the ".host" syntax, as that's the user this
* would connect to. */
if (geteuid() == 0 && STR_IN_SET(user_and_machine, ".host", "root@.host"))
return true;
/* Otherwise, we have to figure our user name, and compare things with that. */
un = getusername_malloc();
if (!un)
return -ENOMEM;
f = startswith(user_and_machine, un);
if (!f)
return false;
return STR_IN_SET(f, "@", "@.host");
}
_public_ int sd_bus_open_system_machine(sd_bus **ret, const char *user_and_machine) {
_cleanup_(bus_freep) sd_bus *b = NULL; _cleanup_(bus_freep) sd_bus *b = NULL;
int r; int r;
assert_return(machine, -EINVAL); assert_return(user_and_machine, -EINVAL);
assert_return(ret, -EINVAL); assert_return(ret, -EINVAL);
assert_return(streq(machine, ".host") || machine_name_is_valid(machine), -EINVAL);
if (user_and_machine_equivalent(user_and_machine))
return sd_bus_open_system(ret);
r = user_and_machine_valid(user_and_machine);
if (r < 0)
return r;
assert_return(r > 0, -EINVAL);
r = sd_bus_new(&b); r = sd_bus_new(&b);
if (r < 0) if (r < 0)
return r; return r;
r = bus_set_address_system_machine(b, machine); r = bus_set_address_machine(b, false, user_and_machine);
if (r < 0) if (r < 0)
return r; return r;
b->bus_client = true; b->bus_client = true;
b->trusted = false;
b->is_system = true; b->is_system = true;
b->is_local = false;
r = sd_bus_start(b);
if (r < 0)
return r;
*ret = TAKE_PTR(b);
return 0;
}
_public_ int sd_bus_open_user_machine(sd_bus **ret, const char *user_and_machine) {
_cleanup_(bus_freep) sd_bus *b = NULL;
int r;
assert_return(user_and_machine, -EINVAL);
assert_return(ret, -EINVAL);
/* Shortcut things if we'd end up on this host and as the same user. */
if (user_and_machine_equivalent(user_and_machine))
return sd_bus_open_user(ret);
r = user_and_machine_valid(user_and_machine);
if (r < 0)
return r;
assert_return(r > 0, -EINVAL);
r = sd_bus_new(&b);
if (r < 0)
return r;
r = bus_set_address_machine(b, true, user_and_machine);
if (r < 0)
return r;
b->bus_client = true;
b->trusted = true;
r = sd_bus_start(b); r = sd_bus_start(b);
if (r < 0) if (r < 0)

View File

@ -847,7 +847,7 @@ _public_ int sd_get_machine_names(char ***machines) {
/* Filter out the unit: symlinks */ /* Filter out the unit: symlinks */
for (a = b = l; *a; a++) { for (a = b = l; *a; a++) {
if (startswith(*a, "unit:") || !machine_name_is_valid(*a)) if (startswith(*a, "unit:") || !hostname_is_valid(*a, 0))
free(*a); free(*a);
else { else {
*b = *a; *b = *a;
@ -877,7 +877,7 @@ _public_ int sd_machine_get_class(const char *machine, char **class) {
if (!c) if (!c)
return -ENOMEM; return -ENOMEM;
} else { } else {
if (!machine_name_is_valid(machine)) if (!hostname_is_valid(machine, 0))
return -EINVAL; return -EINVAL;
p = strjoina("/run/systemd/machines/", machine); p = strjoina("/run/systemd/machines/", machine);
@ -899,7 +899,7 @@ _public_ int sd_machine_get_ifindices(const char *machine, int **ret_ifindices)
const char *p; const char *p;
int r; int r;
assert_return(machine_name_is_valid(machine), -EINVAL); assert_return(hostname_is_valid(machine, 0), -EINVAL);
p = strjoina("/run/systemd/machines/", machine); p = strjoina("/run/systemd/machines/", machine);
r = parse_env_file(NULL, p, "NETIF", &netif_line); r = parse_env_file(NULL, p, "NETIF", &netif_line);

View File

@ -487,7 +487,7 @@ static int container_bus_new(Machine *m, sd_bus_error *error, sd_bus **ret) {
if (r < 0) if (r < 0)
return r; return r;
if (asprintf(&address, "x-machine-kernel:pid=%1$" PID_PRI ";x-machine-unix:pid=%1$" PID_PRI, m->leader) < 0) if (asprintf(&address, "x-machine-unix:pid=%" PID_PRI, m->leader) < 0)
return -ENOMEM; return -ENOMEM;
bus->address = address; bus->address = address;

View File

@ -1561,7 +1561,7 @@ static int make_service_name(const char *name, char **ret) {
assert(name); assert(name);
assert(ret); assert(ret);
if (!machine_name_is_valid(name)) if (!hostname_is_valid(name, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Invalid machine name %s.", name); "Invalid machine name %s.", name);
@ -1881,7 +1881,7 @@ static int import_tar(int argc, char *argv[], void *userdata) {
local = ll; local = ll;
if (!machine_name_is_valid(local)) if (!hostname_is_valid(local, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local name %s is not a suitable machine name.", "Local name %s is not a suitable machine name.",
local); local);
@ -1941,7 +1941,7 @@ static int import_raw(int argc, char *argv[], void *userdata) {
local = ll; local = ll;
if (!machine_name_is_valid(local)) if (!hostname_is_valid(local, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local name %s is not a suitable machine name.", "Local name %s is not a suitable machine name.",
local); local);
@ -1995,7 +1995,7 @@ static int import_fs(int argc, char *argv[], void *userdata) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Need either path or local name."); "Need either path or local name.");
if (!machine_name_is_valid(local)) if (!hostname_is_valid(local, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local name %s is not a suitable machine name.", "Local name %s is not a suitable machine name.",
local); local);
@ -2048,7 +2048,7 @@ static int export_tar(int argc, char *argv[], void *userdata) {
assert(bus); assert(bus);
local = argv[1]; local = argv[1];
if (!machine_name_is_valid(local)) if (!hostname_is_valid(local, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Machine name %s is not valid.", local); "Machine name %s is not valid.", local);
@ -2090,7 +2090,7 @@ static int export_raw(int argc, char *argv[], void *userdata) {
assert(bus); assert(bus);
local = argv[1]; local = argv[1];
if (!machine_name_is_valid(local)) if (!hostname_is_valid(local, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Machine name %s is not valid.", local); "Machine name %s is not valid.", local);
@ -2155,7 +2155,7 @@ static int pull_tar(int argc, char *argv[], void *userdata) {
local = ll; local = ll;
if (!machine_name_is_valid(local)) if (!hostname_is_valid(local, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local name %s is not a suitable machine name.", "Local name %s is not a suitable machine name.",
local); local);
@ -2211,7 +2211,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
local = ll; local = ll;
if (!machine_name_is_valid(local)) if (!hostname_is_valid(local, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local name %s is not a suitable machine name.", "Local name %s is not a suitable machine name.",
local); local);

View File

@ -239,7 +239,7 @@ static int method_create_or_register_machine(Manager *manager, sd_bus_message *m
r = sd_bus_message_read(message, "s", &name); r = sd_bus_message_read(message, "s", &name);
if (r < 0) if (r < 0)
return r; return r;
if (!machine_name_is_valid(name)) if (!hostname_is_valid(name, 0))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine name"); return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine name");
r = sd_bus_message_read_array(message, 'y', &v, &n); r = sd_bus_message_read_array(message, 'y', &v, &n);

View File

@ -166,7 +166,7 @@ static int manager_enumerate_machines(Manager *m) {
if (startswith(de->d_name, "unit:")) if (startswith(de->d_name, "unit:"))
continue; continue;
if (!machine_name_is_valid(de->d_name)) if (!hostname_is_valid(de->d_name, 0))
continue; continue;
k = manager_add_machine(m, de->d_name, &machine); k = manager_add_machine(m, de->d_name, &machine);

View File

@ -601,7 +601,7 @@ static int parse_cmdline_ip_address(Context *context, int family, const char *va
if (p != value) { if (p != value) {
hostname = strndupa(value, p - value); hostname = strndupa(value, p - value);
if (!hostname_is_valid(hostname, false)) if (!hostname_is_valid(hostname, 0))
return -EINVAL; return -EINVAL;
} }

View File

@ -922,7 +922,7 @@ int config_parse_hostname(
if (r < 0) if (r < 0)
return r; return r;
if (!hostname_is_valid(hn, false)) { if (!hostname_is_valid(hn, 0)) {
log_syntax(unit, LOG_WARNING, filename, line, 0, log_syntax(unit, LOG_WARNING, filename, line, 0,
"Hostname is not valid, ignoring assignment: %s", rvalue); "Hostname is not valid, ignoring assignment: %s", rvalue);
return 0; return 0;

View File

@ -462,7 +462,7 @@ static int oci_hostname(const char *name, JsonVariant *v, JsonDispatchFlags flag
assert_se(n = json_variant_string(v)); assert_se(n = json_variant_string(v));
if (!hostname_is_valid(n, false)) if (!hostname_is_valid(n, 0))
return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
"Hostname string is not a valid hostname: %s", n); "Hostname string is not a valid hostname: %s", n);

View File

@ -701,7 +701,7 @@ int config_parse_hostname(
assert(rvalue); assert(rvalue);
assert(s); assert(s);
if (!hostname_is_valid(rvalue, false)) { if (!hostname_is_valid(rvalue, 0)) {
log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid hostname, ignoring: %s", rvalue); log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid hostname, ignoring: %s", rvalue);
return 0; return 0;
} }

View File

@ -985,7 +985,7 @@ static int parse_argv(int argc, char *argv[]) {
if (isempty(optarg)) if (isempty(optarg))
arg_machine = mfree(arg_machine); arg_machine = mfree(arg_machine);
else { else {
if (!machine_name_is_valid(optarg)) if (!hostname_is_valid(optarg, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Invalid machine name: %s", optarg); "Invalid machine name: %s", optarg);
@ -999,7 +999,7 @@ static int parse_argv(int argc, char *argv[]) {
if (isempty(optarg)) if (isempty(optarg))
arg_hostname = mfree(arg_hostname); arg_hostname = mfree(arg_hostname);
else { else {
if (!hostname_is_valid(optarg, false)) if (!hostname_is_valid(optarg, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Invalid hostname: %s", optarg); "Invalid hostname: %s", optarg);
@ -2984,7 +2984,7 @@ static int determine_names(void) {
return log_oom(); return log_oom();
hostname_cleanup(arg_machine); hostname_cleanup(arg_machine);
if (!machine_name_is_valid(arg_machine)) if (!hostname_is_valid(arg_machine, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine machine name automatically, please use -M."); return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine machine name automatically, please use -M.");
if (arg_ephemeral) { if (arg_ephemeral) {

View File

@ -249,7 +249,12 @@ int bus_connect_user_systemd(sd_bus **_bus) {
return 0; return 0;
} }
int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **ret) { int bus_connect_transport(
BusTransport transport,
const char *host,
bool user,
sd_bus **ret) {
_cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
int r; int r;
@ -258,7 +263,7 @@ int bus_connect_transport(BusTransport transport, const char *host, bool user, s
assert(ret); assert(ret);
assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL); assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP); assert_return(transport != BUS_TRANSPORT_REMOTE || !user, -EOPNOTSUPP);
switch (transport) { switch (transport) {
@ -279,7 +284,10 @@ int bus_connect_transport(BusTransport transport, const char *host, bool user, s
break; break;
case BUS_TRANSPORT_MACHINE: case BUS_TRANSPORT_MACHINE:
r = sd_bus_open_system_machine(&bus, host); if (user)
r = sd_bus_open_user_machine(&bus, host);
else
r = sd_bus_open_system_machine(&bus, host);
break; break;
default: default:
@ -293,7 +301,6 @@ int bus_connect_transport(BusTransport transport, const char *host, bool user, s
return r; return r;
*ret = TAKE_PTR(bus); *ret = TAKE_PTR(bus);
return 0; return 0;
} }

View File

@ -9,6 +9,7 @@
#include "sd-bus.h" #include "sd-bus.h"
#include "sd-event.h" #include "sd-event.h"
#include "errno-util.h"
#include "macro.h" #include "macro.h"
#include "string-util.h" #include "string-util.h"
#include "time-util.h" #include "time-util.h"
@ -39,13 +40,21 @@ int bus_connect_transport(BusTransport transport, const char *host, bool user, s
int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus); int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus);
#define bus_log_address_error(r) \ #define bus_log_address_error(r) \
log_error_errno(r, \ ({ \
r == -ENOMEDIUM ? "Failed to set bus address: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined" : \ int _k = (r); \
"Failed to set bus address: %m") log_error_errno(_k, \
_k == -ENOMEDIUM ? "Failed to set bus address: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined (consider using --machine=<user>@.host --user to connect to bus of other user)" : \
"Failed to set bus address: %m"); \
})
#define bus_log_connect_error(r) \ #define bus_log_connect_error(r) \
log_error_errno(r, \ ({ \
r == -ENOMEDIUM ? "Failed to connect to bus: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined" : \ int _k = (r); \
"Failed to connect to bus: %m") log_error_errno(_k, \
_k == -ENOMEDIUM ? "Failed to connect to bus: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined (consider using --machine=<user>@.host --user to connect to bus of other user)" : \
ERRNO_IS_PRIVILEGE(_k) ? "Failed to connect to bus: Operation not permitted (consider using --machine=<user>@.host --user to connect to bus of other user)" : \
"Failed to connect to bus: %m"); \
})
#define bus_log_parse_error(r) \ #define bus_log_parse_error(r) \
log_error_errno(r, "Failed to parse bus message: %m") log_error_errno(r, "Failed to parse bus message: %m")

View File

@ -1523,9 +1523,6 @@ static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) {
assert(machine); assert(machine);
assert(boot_id); assert(boot_id);
if (!machine_name_is_valid(machine))
return -EINVAL;
r = container_get_leader(machine, &pid); r = container_get_leader(machine, &pid);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -23,6 +23,7 @@
static const char *arg_bus_path = DEFAULT_BUS_PATH; static const char *arg_bus_path = DEFAULT_BUS_PATH;
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
static bool arg_user = false;
static int help(void) { static int help(void) {
@ -30,8 +31,10 @@ static int help(void) {
"STDIO or socket-activatable proxy to a given DBus endpoint.\n\n" "STDIO or socket-activatable proxy to a given DBus endpoint.\n\n"
" -h --help Show this help\n" " -h --help Show this help\n"
" --version Show package version\n" " --version Show package version\n"
" -p --bus-path=PATH Path to the kernel bus (default: %s)\n" " -p --bus-path=PATH Path to the bus address (default: %s)\n"
" -M --machine=MACHINE Name of machine to connect to\n", " --system Connect to system bus\n"
" --user Connect to user bus\n"
" -M --machine=CONTAINER Name of local container to connect to\n",
program_invocation_short_name, DEFAULT_BUS_PATH); program_invocation_short_name, DEFAULT_BUS_PATH);
return 0; return 0;
@ -42,12 +45,16 @@ static int parse_argv(int argc, char *argv[]) {
enum { enum {
ARG_VERSION = 0x100, ARG_VERSION = 0x100,
ARG_MACHINE, ARG_MACHINE,
ARG_USER,
ARG_SYSTEM,
}; };
static const struct option options[] = { static const struct option options[] = {
{ "help", no_argument, NULL, 'h' }, { "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION }, { "version", no_argument, NULL, ARG_VERSION },
{ "bus-path", required_argument, NULL, 'p' }, { "bus-path", required_argument, NULL, 'p' },
{ "user", no_argument, NULL, ARG_USER },
{ "system", no_argument, NULL, ARG_SYSTEM },
{ "machine", required_argument, NULL, 'M' }, { "machine", required_argument, NULL, 'M' },
{}, {},
}; };
@ -67,6 +74,14 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_VERSION: case ARG_VERSION:
return version(); return version();
case ARG_USER:
arg_user = true;
break;
case ARG_SYSTEM:
arg_user = false;
break;
case 'p': case 'p':
arg_bus_path = optarg; arg_bus_path = optarg;
break; break;
@ -121,7 +136,7 @@ static int run(int argc, char *argv[]) {
return log_error_errno(r, "Failed to allocate bus: %m"); return log_error_errno(r, "Failed to allocate bus: %m");
if (arg_transport == BUS_TRANSPORT_MACHINE) if (arg_transport == BUS_TRANSPORT_MACHINE)
r = bus_set_address_system_machine(a, arg_bus_path); r = bus_set_address_machine(a, arg_user, arg_bus_path);
else else
r = sd_bus_set_address(a, arg_bus_path); r = sd_bus_set_address(a, arg_bus_path);
if (r < 0) if (r < 0)

View File

@ -877,7 +877,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option"); assert_not_reached("Unhandled option");
} }
if (arg_transport != BUS_TRANSPORT_LOCAL && arg_scope != UNIT_FILE_SYSTEM) if (arg_transport == BUS_TRANSPORT_REMOTE && arg_scope != UNIT_FILE_SYSTEM)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Cannot access user instance remotely."); "Cannot access user instance remotely.");

View File

@ -135,6 +135,7 @@ int sd_bus_open(sd_bus **ret);
int sd_bus_open_with_description(sd_bus **ret, const char *description); int sd_bus_open_with_description(sd_bus **ret, const char *description);
int sd_bus_open_user(sd_bus **ret); int sd_bus_open_user(sd_bus **ret);
int sd_bus_open_user_with_description(sd_bus **ret, const char *description); int sd_bus_open_user_with_description(sd_bus **ret, const char *description);
int sd_bus_open_user_machine(sd_bus **ret, const char *machine);
int sd_bus_open_system(sd_bus **ret); int sd_bus_open_system(sd_bus **ret);
int sd_bus_open_system_with_description(sd_bus **ret, const char *description); int sd_bus_open_system_with_description(sd_bus **ret, const char *description);
int sd_bus_open_system_remote(sd_bus **ret, const char *host); int sd_bus_open_system_remote(sd_bus **ret, const char *host);

View File

@ -10,40 +10,40 @@
#include "util.h" #include "util.h"
static void test_hostname_is_valid(void) { static void test_hostname_is_valid(void) {
assert_se(hostname_is_valid("foobar", false)); assert_se(hostname_is_valid("foobar", 0));
assert_se(hostname_is_valid("foobar.com", false)); assert_se(hostname_is_valid("foobar.com", 0));
assert_se(!hostname_is_valid("foobar.com.", false)); assert_se(!hostname_is_valid("foobar.com.", 0));
assert_se(hostname_is_valid("fooBAR", false)); assert_se(hostname_is_valid("fooBAR", 0));
assert_se(hostname_is_valid("fooBAR.com", false)); assert_se(hostname_is_valid("fooBAR.com", 0));
assert_se(!hostname_is_valid("fooBAR.", false)); assert_se(!hostname_is_valid("fooBAR.", 0));
assert_se(!hostname_is_valid("fooBAR.com.", false)); assert_se(!hostname_is_valid("fooBAR.com.", 0));
assert_se(!hostname_is_valid("fööbar", false)); assert_se(!hostname_is_valid("fööbar", 0));
assert_se(!hostname_is_valid("", false)); assert_se(!hostname_is_valid("", 0));
assert_se(!hostname_is_valid(".", false)); assert_se(!hostname_is_valid(".", 0));
assert_se(!hostname_is_valid("..", false)); assert_se(!hostname_is_valid("..", 0));
assert_se(!hostname_is_valid("foobar.", false)); assert_se(!hostname_is_valid("foobar.", 0));
assert_se(!hostname_is_valid(".foobar", false)); assert_se(!hostname_is_valid(".foobar", 0));
assert_se(!hostname_is_valid("foo..bar", false)); assert_se(!hostname_is_valid("foo..bar", 0));
assert_se(!hostname_is_valid("foo.bar..", false)); assert_se(!hostname_is_valid("foo.bar..", 0));
assert_se(!hostname_is_valid("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", false)); assert_se(!hostname_is_valid("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 0));
assert_se(!hostname_is_valid("au-xph5-rvgrdsb5hcxc-47et3a5vvkrc-server-wyoz4elpdpe3.openstack.local", false)); assert_se(!hostname_is_valid("au-xph5-rvgrdsb5hcxc-47et3a5vvkrc-server-wyoz4elpdpe3.openstack.local", 0));
assert_se(hostname_is_valid("foobar", true)); assert_se(hostname_is_valid("foobar", VALID_HOSTNAME_TRAILING_DOT));
assert_se(hostname_is_valid("foobar.com", true)); assert_se(hostname_is_valid("foobar.com", VALID_HOSTNAME_TRAILING_DOT));
assert_se(hostname_is_valid("foobar.com.", true)); assert_se(hostname_is_valid("foobar.com.", VALID_HOSTNAME_TRAILING_DOT));
assert_se(hostname_is_valid("fooBAR", true)); assert_se(hostname_is_valid("fooBAR", VALID_HOSTNAME_TRAILING_DOT));
assert_se(hostname_is_valid("fooBAR.com", true)); assert_se(hostname_is_valid("fooBAR.com", VALID_HOSTNAME_TRAILING_DOT));
assert_se(!hostname_is_valid("fooBAR.", true)); assert_se(!hostname_is_valid("fooBAR.", VALID_HOSTNAME_TRAILING_DOT));
assert_se(hostname_is_valid("fooBAR.com.", true)); assert_se(hostname_is_valid("fooBAR.com.", VALID_HOSTNAME_TRAILING_DOT));
assert_se(!hostname_is_valid("fööbar", true)); assert_se(!hostname_is_valid("fööbar", VALID_HOSTNAME_TRAILING_DOT));
assert_se(!hostname_is_valid("", true)); assert_se(!hostname_is_valid("", VALID_HOSTNAME_TRAILING_DOT));
assert_se(!hostname_is_valid(".", true)); assert_se(!hostname_is_valid(".", VALID_HOSTNAME_TRAILING_DOT));
assert_se(!hostname_is_valid("..", true)); assert_se(!hostname_is_valid("..", VALID_HOSTNAME_TRAILING_DOT));
assert_se(!hostname_is_valid("foobar.", true)); assert_se(!hostname_is_valid("foobar.", VALID_HOSTNAME_TRAILING_DOT));
assert_se(!hostname_is_valid(".foobar", true)); assert_se(!hostname_is_valid(".foobar", VALID_HOSTNAME_TRAILING_DOT));
assert_se(!hostname_is_valid("foo..bar", true)); assert_se(!hostname_is_valid("foo..bar", VALID_HOSTNAME_TRAILING_DOT));
assert_se(!hostname_is_valid("foo.bar..", true)); assert_se(!hostname_is_valid("foo.bar..", VALID_HOSTNAME_TRAILING_DOT));
assert_se(!hostname_is_valid("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", true)); assert_se(!hostname_is_valid("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", VALID_HOSTNAME_TRAILING_DOT));
} }
static void test_hostname_cleanup(void) { static void test_hostname_cleanup(void) {
@ -151,7 +151,7 @@ static void test_hostname_malloc(void) {
} }
static void test_fallback_hostname(void) { static void test_fallback_hostname(void) {
if (!hostname_is_valid(FALLBACK_HOSTNAME, false)) { if (!hostname_is_valid(FALLBACK_HOSTNAME, 0)) {
log_error("Configured fallback hostname \"%s\" is not valid.", FALLBACK_HOSTNAME); log_error("Configured fallback hostname \"%s\" is not valid.", FALLBACK_HOSTNAME);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }