1
0
mirror of https://github.com/systemd/systemd synced 2026-03-10 23:24:49 +01:00

Compare commits

...

20 Commits

Author SHA1 Message Date
Luca Boccassi
10a04f6fd2
test: do not fail when parsing PID that isn't thread-group leader (#40677)
```
TEST-02-UNITTESTS.sh[4382]: [  707.393188] test-cgroup-util[426]: Failed to open pidfd for pid 414: Invalid argument
TEST-02-UNITTESTS.sh[4382]: [  707.393193] test-cgroup-util[426]: src/test/test-cgroup-util.c:249: Assertion failed: Expected "r = proc_dir_read_pidref(d, &pid)" to succeed, but got error: -22/EINVAL
```

The kernel can return EINVAL on pidfd_open() when the selected PID is
not a thread group leader. Don't fail the test, as we are iterating on
everything, so this can seldomly happen.
2026-02-13 21:37:56 +00:00
Yu Watanabe
a1c8e5e338
Allow non-system users/groups in networkd and udevd again (#40612) 2026-02-14 05:41:01 +09:00
Yu Watanabe
da86da21e9
Fixes for option parsing (#40641)
Preparatory work split out of #40624.
2026-02-14 04:58:50 +09:00
Michael Vogt
b5f8725989 varlinkctl: add pluggable protocol support to sd-varlink
When sd_varlink_connect_url() gets an unknown URL we now
check if there is a `$LIBEXECDIR/varlink-bridges/$scheme`
binary and execute it (with the url as the first arguments).

This makes varlink more flexible as it provides a way to
dynamically add "bridges" in LIBEXECDIR/varlink-bridges/. This is
conceptually similar to the libvarlink `varlink --bridge` command
and allows to e.g. call varlink over http{,s} via e.g. the new
varlink-http-bridge.

With a running varlink-http-bridge [0] one can do:
```console
$ varlinkctl call http://localhost:8080/ws/sockets/io.systemd.Hostname \
    io.systemd.Hostname.Describe {}
{
        "Hostname" : "top",
...
```

Closes: https://github.com/systemd/systemd/issues/40640

[0] https://github.com/mvo5/varlink-http-bridge/pull/1
2026-02-13 19:07:20 +01:00
Zbigniew Jędrzejewski-Szmek
c46cc2630d man: make it clear that homectl -A/T/N do not take an argument
Just like in the --help strings, we need to avoid confusion between
short options which take an argument and those that don't.
2026-02-13 11:07:27 +01:00
Zbigniew Jędrzejewski-Szmek
952f3a5dbf homectl: drop --and-resize and --and-change-password
The two options are were not documented or ever used in the codebase.
Additionally, the parser expected an argumentless option, while the option
table declared a required argument. So I think this was added for debugging
and never excercised properly. Since there was no public documentation for
those, it's as if they never existed, so it should be fine to drop them.
2026-02-13 11:07:26 +01:00
Zbigniew Jędrzejewski-Szmek
097f130731 resolvectl: split out helper to parse stdin
No functional change, just splitting up of a lengthy function.
2026-02-13 11:07:25 +01:00
Zbigniew Jędrzejewski-Szmek
b4df0a9ee6 nspawn: split out helper to parse --private-users
This is just to make parse_argv() slightly more managable.
2026-02-13 11:07:22 +01:00
Zbigniew Jędrzejewski-Szmek
26de1563d0 nspawn: simplify parsing of --hostname/--machine 2026-02-11 12:50:51 +01:00
Zbigniew Jędrzejewski-Szmek
c1fcc80421 journal-upload: drop custom option error handling
The line to set opterr=0 was added in the initial commit in
3d090cc6f34e5970765dd1e7ee5e648a056d180d. But afaict, this never worked as
intended, because ':' must be the first char in optstring given to
getopt_long() for it to return ':' for a missing option value. Since
this wasn't set, getopt_long() would return '?', and the missing value
would be handled as an unknown option:
  $ build/systemd-journal-upload --key
  Unknown option --key.
  $ build/systemd-journal-upload --asdf
  Unknown option --asdf.

Let's just do the standard thing:
  $ build/systemd-journal-upload --key
  build/systemd-journal-upload: option '--key' requires an argument
  $ build/systemd-journal-upload --asdf
  build/systemd-journal-upload: unrecognized option '--asdf'
2026-02-11 11:48:29 +01:00
Zbigniew Jędrzejewski-Szmek
7b6f1ea371 cryptenroll: simplification 2026-02-11 11:47:59 +01:00
Zbigniew Jędrzejewski-Szmek
4a991af017 cryptenroll: split out parser for --wipe-slot= 2026-02-11 11:47:52 +01:00
Zbigniew Jędrzejewski-Szmek
ff12bcd4fb path: simplify run()
No functional change, just less indents.
2026-02-11 11:47:18 +01:00
Zbigniew Jędrzejewski-Szmek
e4c6ee2ce5 mount-tool: fix confusion in --help
See grandparent commit for explanation.
2026-02-11 11:46:44 +01:00
Zbigniew Jędrzejewski-Szmek
84f6967558 importctl: fix confusion in --help
See parent commit for explanation.
2026-02-11 11:46:38 +01:00
Zbigniew Jędrzejewski-Szmek
cb406988ac cgtop: fix confusion in --help
When we say '-n --iterations=N' in --help, this means that the program
can be invoked with '-n N' or '--iterations=N' or '--iterations N'.
(The short option is specified without the argument.)

Here we tried to use '-p --order=path' to mean that the program can be
invoked with '-p' or '--order=path', but that is incompatible with the
established convention.

Also, indicate that the arg to --cpu is optional.
2026-02-11 11:46:12 +01:00
Zbigniew Jędrzejewski-Szmek
efb7717224 NEWS: add entry about non-system accounts 2026-02-11 11:24:37 +01:00
Zbigniew Jędrzejewski-Szmek
f6c8c89ff7 networkd: downgrade error for non-system users/groups to a warning
This reverts (in spirit) "network/tuntap: deny from owning Tun/Tap interfaces",
commit 940441b44c7040d62ae58b66bf124e9a0dae578d.

Justification similar as in the previous commit. The check is only
partially connected to the intended purpose and breaks backwards compat
without a sufficient reason.

Alternative fix for #37279.
2026-02-11 11:24:37 +01:00
Zbigniew Jędrzejewski-Szmek
5c05a339c6 udev-rules: downgrade error about non-system user/group in OWNER=/GROUP=
This reverts (in sprit) commit f5cdf9515aceca2e91f9a33b74267e0cf5a5b7e8,
"udev-rules: ignore non-system user/group in OWNER=/GROUP=".

The original change was done to clean up a situation where we added a
new group, but the group could already have been used for some other
purposes, and now the some unexpected entity would own the device.
Unfortunately, this check doesn't really address the issue, since the
existing account might as well be a system account, which might be
equally bad. In addition, this change is a big compatiblity break,
causing existing rules to stop working. Since quite a lot of systems
have local configuration to assign devices to users for various
purposes, this is very noticable to users. In a way, the original change
to add a new group was the compat break, and follow-up patch to cahnge
the rule parsing evolved a small compat break into a much bigger one.
There is merit to the change though, since device nodes shouldn't be
owned by users and groups and different mechanisms should be used
instead. To avoid breaking users systems, and since the original goal
cannot be achieved by this patch, let's downgrade this to a warning
to guide users towards different solutions.
2026-02-11 11:24:37 +01:00
Zbigniew Jędrzejewski-Szmek
79cf06fae4 catalog: fix grammar 2026-02-09 22:22:05 +01:00
24 changed files with 479 additions and 291 deletions

14
NEWS
View File

@ -31,6 +31,12 @@ CHANGES WITH 260 in spe:
rather than the first, to keep these options coherent with other rather than the first, to keep these options coherent with other
unit settings. unit settings.
* Support for non-system users and groups in udev rules and
systemd-networkd configuration has been restored, but is deprecated
and discouraged. systemd-udevd and systemd-networkd will emit
warnings when non-system accounts are used. This support will be
removed in a future release.
Changes in other components: Changes in other components:
* New options SYSTEMD_COLORS=auto-16, SYSTEMD_COLORS=auto-256, and * New options SYSTEMD_COLORS=auto-16, SYSTEMD_COLORS=auto-256, and
@ -669,13 +675,13 @@ CHANGES WITH 258:
an incompatible change of sorts, since per-user services will an incompatible change of sorts, since per-user services will
typically not be available for such PAM sessions of system users. typically not be available for such PAM sessions of system users.
* systemd-udevd ignores OWNER=/GROUP= settings with a non-system * systemd-udevd warns about OWNER=/GROUP= settings with a non-system
user/group specified in udev rules files, to avoid device nodes being user/group specified in udev rules files. Device nodes should not be
owned by a non-system user/group. It is recommended to check udev owned by a non-system user/group. It is recommended to check udev
rules files with 'udevadm verify' and/or 'udevadm test' commands if rules files with 'udevadm verify' and/or 'udevadm test' commands if
the specified user/group in OWNER=/GROUP= are valid. the specified user/group in OWNER=/GROUP= are valid.
Similarly, systemd-networkd refuses User=/Group= settings with a Similarly, systemd-networkd will warn about User=/Group= settings
non-system user/group specified in .netdev files for Tun/Tap with a non-system user/group specified in .netdev files for Tun/Tap
interfaces. interfaces.
* systemd-cryptenroll, systemd-repart and systemd-creds no longer * systemd-cryptenroll, systemd-repart and systemd-creds no longer

View File

@ -981,6 +981,24 @@ Defined-By: systemd
Support: %SUPPORT_URL% Support: %SUPPORT_URL%
Documentation: man:systemd(1) Documentation: man:systemd(1)
It has been attempted to break an ordering cycle between units for which jobs An unsuccessful attempt has been made to break an ordering cycle between units
have been enqueued as part of a transaction, but this was not successful. The for which jobs have been enqueued as part of a transaction. The transaction
transaction will fail. will fail.
-- 3405205d368e49feb5ab3925fee13874
Subject: Non-system user or group used for device ownership
Defined-By: systemd
Support: %SUPPORT_URL%
Documentation: man:systemd(1) systemd-udevd(8) systemd-networkd(8)
The ownership of a device managed by systemd-udevd or systemd-networkd is
assigned to a "regular" (non-system) user or group. This is currently allowed
for compatibility, but is deprecated and discouraged. Ownership of a device
node grants the privileges to change ACLs, the group, access mode, or set
labels or extended attributes, which creates a conflict of management, because
both udev and the user are in power to change these attributes. In addition,
device nodes appear early in boot, while regular users may appear only later.
For devices managed by systemd-udevd, it is instead recommended to use the
"uaccess"/"xaccess" mechanisms to grant limited and temporary access to device
nodes, see sd-login(8).

View File

@ -760,6 +760,9 @@ Tools using the Varlink protocol (such as `varlinkctl`) or sd-bus (such as
would listen on. If set to "-" the tool will turn stdin/stdout into a Varlink would listen on. If set to "-" the tool will turn stdin/stdout into a Varlink
connection. connection.
* `$SYSTEMD_VARLINK_BRIDGES_DIR` overrides the default `$LIBEXEC/varlink-bridges/`
path when looking up custom scheme bridge helper binaries.
`systemd-mountfsd`: `systemd-mountfsd`:
* `$SYSTEMD_MOUNTFSD_TRUSTED_DIRECTORIES` takes a boolean argument. If true * `$SYSTEMD_MOUNTFSD_TRUSTED_DIRECTORIES` takes a boolean argument. If true

View File

@ -256,20 +256,21 @@
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><option>--match=</option></term> <term><option>--match=this|other|any|auto</option></term>
<term><option>-A</option></term> <term><option>-A</option></term>
<term><option>-N</option></term> <term><option>-N</option></term>
<term><option>-T</option></term> <term><option>-T</option></term>
<listitem><para>Takes one of <literal>this</literal>, <literal>other</literal>, <listitem><para>Option <option>--match=</option> takes one of <literal>this</literal>,
<literal>any</literal> or <literal>auto</literal>. Some user record settings can be defined to match <literal>other</literal>, <literal>any</literal> or <literal>auto</literal>. Some user record
only specific machines, or all machines but one, or all machines. With this switch it is possibly to settings can be defined to match only specific machines, or all machines but one, or all machines.
control to which machines to apply the settings appearing on the command line after it. If With this switch it is possibly to control to which machines to apply the settings appearing on the
<literal>this</literal> is specified the setting will only apply to the local system (positive command line after it. If <literal>this</literal> is specified the setting will only apply to the
match), if <literal>other</literal> it will apply to all but the local system (negative match), if local system (positive match), if <literal>other</literal> it will apply to all but the local system
<literal>any</literal> it will apply to all systems (unless there's a matching positive or negative (negative match), if <literal>any</literal> it will apply to all systems (unless there's a matching
per-machine setting). If <literal>auto</literal> returns to the default logic: whether a setting positive or negative per-machine setting). If <literal>auto</literal> returns to the default logic:
applies by default to the local system or all systems depends on the option in question.</para> whether a setting applies by default to the local system or all systems depends on the option in
question.</para>
<para>Note that only some user record settings can be conditioned like this. This option has no <para>Note that only some user record settings can be conditioned like this. This option has no
effect on the others and is ignored there. This option may appear multiple times in a single command effect on the others and is ignored there. This option may appear multiple times in a single command

View File

@ -57,6 +57,29 @@
<xi:include href="libsystemd-pkgconfig.xml" /> <xi:include href="libsystemd-pkgconfig.xml" />
<refsect1>
<title>Directories</title>
<variablelist>
<varlistentry>
<term><filename>/usr/lib/systemd/varlink-bridges/</filename></term>
<listitem>
<para>When <function>sd_varlink_connect_url()</function> encounters a URL with a scheme that is not
natively supported, it looks for a bridge helper binary named after the URL scheme in this
directory. The binary is invoked the same way as <literal>exec:</literal> binaries but with the full
URL passed as the first command line argument.</para>
<para>For example, if
<command>varlinkctl introspect https://example.com/ws/sockets/io.systemd.Hostname</command>
is called, <command>varlinkctl</command> will look for an executable
<filename>/usr/lib/systemd/varlink-bridges/https</filename> and invoke it with
<literal>https://example.com/ws/sockets/io.systemd.Hostname</literal> as its only
argument.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1> <refsect1>
<title>See Also</title> <title>See Also</title>
<para><simplelist type="inline"> <para><simplelist type="inline">

View File

@ -2019,16 +2019,18 @@ Ports=eth2</programlisting>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><varname>User=</varname></term> <term><varname>User=</varname></term>
<listitem><para>User to grant access to the <filename>/dev/net/tun</filename> device. The specified <listitem><para>User to grant access to the <filename>/dev/net/tun</filename> device.
user must be a system user.</para> Note that the user must be resolvable during early boot. Using non-system users
is deprecated.</para>
<xi:include href="version-info.xml" xpointer="v215"/> <xi:include href="version-info.xml" xpointer="v215"/>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><varname>Group=</varname></term> <term><varname>Group=</varname></term>
<listitem><para>Group to grant access to the <filename>/dev/net/tun</filename> device. The specified <listitem><para>Group to grant access to the <filename>/dev/net/tun</filename> device.
group must be a system group.</para> Note that the group must be resolvable during early boot. Using non-system groups
is deprecated.</para>
<xi:include href="version-info.xml" xpointer="v215"/> <xi:include href="version-info.xml" xpointer="v215"/>
</listitem> </listitem>

View File

@ -183,6 +183,7 @@ ntpservicelistdir = libexecdir / 'ntp-units.d'
credstoredir = prefixdir / 'lib/credstore' credstoredir = prefixdir / 'lib/credstore'
pcrlockdir = prefixdir / 'lib/pcrlock.d' pcrlockdir = prefixdir / 'lib/pcrlock.d'
mimepackagesdir = prefixdir / 'share/mime/packages' mimepackagesdir = prefixdir / 'share/mime/packages'
varlinkbridgesdir = libexecdir / 'varlink-bridges'
configfiledir = get_option('configfiledir') configfiledir = get_option('configfiledir')
if configfiledir == '' if configfiledir == ''
@ -311,6 +312,7 @@ conf.set_quoted('USER_GENERATOR_DIR', usergeneratordir)
conf.set_quoted('USER_KEYRING_PATH', pkgsysconfdir / 'import-pubring.pgp') conf.set_quoted('USER_KEYRING_PATH', pkgsysconfdir / 'import-pubring.pgp')
conf.set_quoted('USER_KEYRING_PATH_LEGACY', pkgsysconfdir / 'import-pubring.gpg') conf.set_quoted('USER_KEYRING_PATH_LEGACY', pkgsysconfdir / 'import-pubring.gpg')
conf.set_quoted('USER_PRESET_DIR', userpresetdir) conf.set_quoted('USER_PRESET_DIR', userpresetdir)
conf.set_quoted('VARLINK_BRIDGES_DIR', varlinkbridgesdir)
conf.set_quoted('VENDOR_KEYRING_PATH', libexecdir / 'import-pubring.pgp') conf.set_quoted('VENDOR_KEYRING_PATH', libexecdir / 'import-pubring.pgp')
conf.set('ANSI_OK_COLOR', 'ANSI_' + get_option('ok-color').underscorify().to_upper()) conf.set('ANSI_OK_COLOR', 'ANSI_' + get_option('ok-color').underscorify().to_upper())

View File

@ -702,13 +702,18 @@ static int help(void) {
"Show top control groups by their resource usage.\n\n" "Show top control groups by their resource usage.\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 --order=path Order by path\n"
" -t --order=tasks Order by number of tasks/processes\n" " --order=path|tasks|cpu|memory|io\n"
" -c --order=cpu Order by CPU load (default)\n" " Order by specified property\n"
" -m --order=memory Order by memory load\n" " -p Same as --order=path, order by path\n"
" -i --order=io Order by IO load\n" " -t Same as --order=tasks, order by number of\n"
" tasks/processes\n"
" -c Same as --order=cpu, order by CPU load\n"
" -m Same as --order=memory, order by memory load\n"
" -i Same as --order=io, order by IO load\n"
" -r --raw Provide raw (not human-readable) numbers\n" " -r --raw Provide raw (not human-readable) numbers\n"
" --cpu=percentage Show CPU usage as percentage (default)\n" " --cpu[=percentage]\n"
" Show CPU usage as percentage (default)\n"
" --cpu=time Show CPU usage as time\n" " --cpu=time Show CPU usage as time\n"
" -P Count userspace processes instead of tasks (excl. kernel)\n" " -P Count userspace processes instead of tasks (excl. kernel)\n"
" -k Count all processes instead of tasks (incl. kernel)\n" " -k Count all processes instead of tasks (incl. kernel)\n"

View File

@ -169,6 +169,58 @@ static int determine_default_node(void) {
return 0; return 0;
} }
static int parse_wipe_slot(const char *arg) {
int r;
assert(arg);
if (isempty(arg)) {
arg_wipe_slots_mask = 0;
arg_wipe_slots_scope = WIPE_EXPLICIT;
return 0;
}
for (const char *p = arg;;) {
_cleanup_free_ char *slot = NULL;
r = extract_first_word(&p, &slot, ",", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r == 0)
return 0;
if (r < 0)
return log_error_errno(r, "Failed to parse slot list: %s", arg);
if (streq(slot, "all"))
arg_wipe_slots_scope = WIPE_ALL;
else if (streq(slot, "empty")) {
if (arg_wipe_slots_scope != WIPE_ALL) /* if "all" was specified before, that wins */
arg_wipe_slots_scope = WIPE_EMPTY_PASSPHRASE;
} else if (streq(slot, "password"))
arg_wipe_slots_mask |= 1U << ENROLL_PASSWORD;
else if (streq(slot, "recovery"))
arg_wipe_slots_mask |= 1U << ENROLL_RECOVERY;
else if (streq(slot, "pkcs11"))
arg_wipe_slots_mask |= 1U << ENROLL_PKCS11;
else if (streq(slot, "fido2"))
arg_wipe_slots_mask |= 1U << ENROLL_FIDO2;
else if (streq(slot, "tpm2"))
arg_wipe_slots_mask |= 1U << ENROLL_TPM2;
else {
unsigned n;
r = safe_atou(slot, &n);
if (r < 0)
return log_error_errno(r, "Failed to parse slot index: %s", slot);
if (n > INT_MAX)
return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Slot index out of range: %u", n);
if (!GREEDY_REALLOC(arg_wipe_slots, arg_n_wipe_slots + 1))
return log_oom();
arg_wipe_slots[arg_n_wipe_slots++] = (int) n;
}
}
}
static int help(void) { static int help(void) {
_cleanup_free_ char *link = NULL; _cleanup_free_ char *link = NULL;
int r; int r;
@ -580,62 +632,16 @@ static int parse_argv(int argc, char *argv[]) {
break; break;
case ARG_WIPE_SLOT: { case ARG_WIPE_SLOT:
const char *p = optarg; r = parse_wipe_slot(optarg);
if (isempty(optarg)) {
arg_wipe_slots_mask = 0;
arg_wipe_slots_scope = WIPE_EXPLICIT;
break;
}
for (;;) {
_cleanup_free_ char *slot = NULL;
unsigned n;
r = extract_first_word(&p, &slot, ",", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r == 0)
break;
if (r < 0)
return log_error_errno(r, "Failed to parse slot list: %s", optarg);
if (streq(slot, "all"))
arg_wipe_slots_scope = WIPE_ALL;
else if (streq(slot, "empty")) {
if (arg_wipe_slots_scope != WIPE_ALL) /* if "all" was specified before, that wins */
arg_wipe_slots_scope = WIPE_EMPTY_PASSPHRASE;
} else if (streq(slot, "password"))
arg_wipe_slots_mask |= 1U << ENROLL_PASSWORD;
else if (streq(slot, "recovery"))
arg_wipe_slots_mask |= 1U << ENROLL_RECOVERY;
else if (streq(slot, "pkcs11"))
arg_wipe_slots_mask |= 1U << ENROLL_PKCS11;
else if (streq(slot, "fido2"))
arg_wipe_slots_mask |= 1U << ENROLL_FIDO2;
else if (streq(slot, "tpm2"))
arg_wipe_slots_mask |= 1U << ENROLL_TPM2;
else {
r = safe_atou(slot, &n);
if (r < 0)
return log_error_errno(r, "Failed to parse slot index: %s", slot);
if (n > INT_MAX)
return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Slot index out of range: %u", n);
if (!GREEDY_REALLOC(arg_wipe_slots, arg_n_wipe_slots + 1))
return log_oom();
arg_wipe_slots[arg_n_wipe_slots++] = (int) n;
}
}
break;
}
case ARG_LIST_DEVICES:
r = blockdev_list(BLOCKDEV_LIST_SHOW_SYMLINKS|BLOCKDEV_LIST_REQUIRE_LUKS, /* ret_devices= */ NULL, /* ret_n_devices= */ NULL);
if (r < 0) if (r < 0)
return r; return r;
break;
return 0; case ARG_LIST_DEVICES:
return blockdev_list(BLOCKDEV_LIST_SHOW_SYMLINKS|BLOCKDEV_LIST_REQUIRE_LUKS,
/* ret_devices= */ NULL,
/* ret_n_devices= */ NULL);
case '?': case '?':
return -EINVAL; return -EINVAL;

View File

@ -106,8 +106,6 @@ static int arg_fido2_cred_alg = 0;
#endif #endif
static bool arg_recovery_key = false; static bool arg_recovery_key = false;
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF; static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
static bool arg_and_resize = false;
static bool arg_and_change_password = false;
static ExportFormat arg_export_format = EXPORT_FORMAT_FULL; static ExportFormat arg_export_format = EXPORT_FORMAT_FULL;
static uint64_t arg_capability_bounding_set = CAP_MASK_UNSET; static uint64_t arg_capability_bounding_set = CAP_MASK_UNSET;
static uint64_t arg_capability_ambient_set = CAP_MASK_UNSET; static uint64_t arg_capability_ambient_set = CAP_MASK_UNSET;
@ -1922,6 +1920,9 @@ static int update_home(int argc, char *argv[], void *userdata) {
} else } else
username = NULL; username = NULL;
bool and_change_password = !strv_isempty(arg_pkcs11_token_uri) || !strv_isempty(arg_fido2_device);
bool and_resize = arg_disk_size != UINT64_MAX || arg_disk_size_relative != UINT64_MAX;
r = acquire_bus(&bus); r = acquire_bus(&bus);
if (r < 0) if (r < 0)
return r; return r;
@ -1953,7 +1954,7 @@ static int update_home(int argc, char *argv[], void *userdata) {
/* If we do multiple operations, let's output things more verbosely, since otherwise the repeated /* If we do multiple operations, let's output things more verbosely, since otherwise the repeated
* authentication might be confusing. */ * authentication might be confusing. */
if (arg_and_resize || arg_and_change_password) if (and_resize || and_change_password)
log_info("Updating home directory."); log_info("Updating home directory.");
if (arg_offline) if (arg_offline)
@ -1988,7 +1989,7 @@ static int update_home(int argc, char *argv[], void *userdata) {
r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL); r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
if (r < 0) { if (r < 0) {
if (arg_and_change_password && if (and_change_password &&
sd_bus_error_has_name(&error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN)) sd_bus_error_has_name(&error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN))
/* In the generic handler we'd ask for a password in this case, but when /* In the generic handler we'd ask for a password in this case, but when
* changing passwords that's not sufficient, as we need to acquire all keys * changing passwords that's not sufficient, as we need to acquire all keys
@ -2002,13 +2003,13 @@ static int update_home(int argc, char *argv[], void *userdata) {
break; break;
} }
if (arg_and_resize) if (and_resize)
log_info("Resizing home."); log_info("Resizing home.");
(void) home_record_reset_human_interaction_permission(hr); (void) home_record_reset_human_interaction_permission(hr);
/* Also sync down disk size to underlying LUKS/fscrypt/quota */ /* Also sync down disk size to underlying LUKS/fscrypt/quota */
while (arg_and_resize) { while (and_resize) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
@ -2027,7 +2028,7 @@ static int update_home(int argc, char *argv[], void *userdata) {
r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL); r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
if (r < 0) { if (r < 0) {
if (arg_and_change_password && if (and_change_password &&
sd_bus_error_has_name(&error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN)) sd_bus_error_has_name(&error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN))
return log_error_errno(r, "Security token not inserted, refusing."); return log_error_errno(r, "Security token not inserted, refusing.");
@ -2038,13 +2039,13 @@ static int update_home(int argc, char *argv[], void *userdata) {
break; break;
} }
if (arg_and_change_password) if (and_change_password)
log_info("Synchronizing passwords and encryption keys."); log_info("Synchronizing passwords and encryption keys.");
(void) home_record_reset_human_interaction_permission(hr); (void) home_record_reset_human_interaction_permission(hr);
/* Also sync down passwords to underlying LUKS/fscrypt */ /* Also sync down passwords to underlying LUKS/fscrypt */
while (arg_and_change_password) { while (and_change_password) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
@ -4156,8 +4157,6 @@ static int parse_argv(int argc, char *argv[]) {
ARG_FIDO2_WITH_UP, ARG_FIDO2_WITH_UP,
ARG_FIDO2_WITH_UV, ARG_FIDO2_WITH_UV,
ARG_RECOVERY_KEY, ARG_RECOVERY_KEY,
ARG_AND_RESIZE,
ARG_AND_CHANGE_PASSWORD,
ARG_DROP_CACHES, ARG_DROP_CACHES,
ARG_LUKS_EXTRA_MOUNT_OPTIONS, ARG_LUKS_EXTRA_MOUNT_OPTIONS,
ARG_AUTO_RESIZE_MODE, ARG_AUTO_RESIZE_MODE,
@ -4266,8 +4265,6 @@ static int parse_argv(int argc, char *argv[]) {
{ "fido2-with-user-presence", required_argument, NULL, ARG_FIDO2_WITH_UP }, { "fido2-with-user-presence", required_argument, NULL, ARG_FIDO2_WITH_UP },
{ "fido2-with-user-verification", required_argument, NULL, ARG_FIDO2_WITH_UV }, { "fido2-with-user-verification", required_argument, NULL, ARG_FIDO2_WITH_UV },
{ "recovery-key", required_argument, NULL, ARG_RECOVERY_KEY }, { "recovery-key", required_argument, NULL, ARG_RECOVERY_KEY },
{ "and-resize", required_argument, NULL, ARG_AND_RESIZE },
{ "and-change-password", required_argument, NULL, ARG_AND_CHANGE_PASSWORD },
{ "drop-caches", required_argument, NULL, ARG_DROP_CACHES }, { "drop-caches", required_argument, NULL, ARG_DROP_CACHES },
{ "luks-extra-mount-options", required_argument, NULL, ARG_LUKS_EXTRA_MOUNT_OPTIONS }, { "luks-extra-mount-options", required_argument, NULL, ARG_LUKS_EXTRA_MOUNT_OPTIONS },
{ "auto-resize-mode", required_argument, NULL, ARG_AUTO_RESIZE_MODE }, { "auto-resize-mode", required_argument, NULL, ARG_AUTO_RESIZE_MODE },
@ -4800,14 +4797,6 @@ static int parse_argv(int argc, char *argv[]) {
break; break;
case ARG_AND_RESIZE:
arg_and_resize = true;
break;
case ARG_AND_CHANGE_PASSWORD:
arg_and_change_password = true;
break;
case ARG_DROP_CACHES: case ARG_DROP_CACHES:
r = parse_boolean_field(match_identity ?: &arg_identity_extra, "dropCaches", optarg); r = parse_boolean_field(match_identity ?: &arg_identity_extra, "dropCaches", optarg);
if (r < 0) if (r < 0)
@ -5008,12 +4997,6 @@ static int parse_argv(int argc, char *argv[]) {
} }
} }
if (!strv_isempty(arg_pkcs11_token_uri) || !strv_isempty(arg_fido2_device))
arg_and_change_password = true;
if (arg_disk_size != UINT64_MAX || arg_disk_size_relative != UINT64_MAX)
arg_and_resize = true;
if (!strv_isempty(arg_languages)) { if (!strv_isempty(arg_languages)) {
char **additional; char **additional;

View File

@ -1035,12 +1035,15 @@ static int help(int argc, char *argv[], void *userdata) {
" --format=xz|gzip|bzip2|zstd\n" " --format=xz|gzip|bzip2|zstd\n"
" Desired output format for export\n" " Desired output format for export\n"
" --force Install image even if already exists\n" " --force Install image even if already exists\n"
" -m --class=machine Install as machine image\n" " --class=TYPE Install as the specified TYPE\n"
" -P --class=portable Install as portable service image\n" " -m Install as --class=machine, machine image\n"
" -S --class=sysext Install as system extension image\n" " -P Install as --class=portable,\n"
" -C --class=confext Install as configuration extension image\n" " portable service image\n"
" -S Install as --class=sysext, system extension image\n"
" -C Install as --class=confext,\n"
" configuration extension image\n"
" --keep-download=BOOL Control whether to keep pristine copy of download\n" " --keep-download=BOOL Control whether to keep pristine copy of download\n"
" -N Shortcut for --keep-download=no\n" " -N Same as --keep-download=no\n"
"\nSee the %2$s for details.\n", "\nSee the %2$s for details.\n",
program_invocation_short_name, program_invocation_short_name,
link, link,

View File

@ -776,8 +776,6 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0); assert(argc >= 0);
assert(argv); assert(argv);
opterr = 0;
while ((c = getopt_long(argc, argv, "hu:mM:D:", options, NULL)) >= 0) while ((c = getopt_long(argc, argv, "hu:mM:D:", options, NULL)) >= 0)
switch (c) { switch (c) {
case 'h': case 'h':
@ -882,14 +880,7 @@ static int parse_argv(int argc, char *argv[]) {
break; break;
case '?': case '?':
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return -EINVAL;
"Unknown option %s.",
argv[optind - 1]);
case ':':
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Missing argument to %s.",
argv[optind - 1]);
default: default:
assert_not_reached(); assert_not_reached();

View File

@ -485,6 +485,14 @@ static int varlink_connect_ssh_exec(sd_varlink **ret, const char *where) {
return 0; return 0;
} }
/* Do basic validation of the URL scheme (loosely following RFC 1738) */
static bool is_valid_url_scheme(const char *s) {
return !isempty(s) &&
strchr(LOWERCASE_LETTERS, s[0]) &&
in_charset(s, LOWERCASE_LETTERS DIGITS "+.-") &&
filename_is_valid(s);
}
_public_ int sd_varlink_connect_url(sd_varlink **ret, const char *url) { _public_ int sd_varlink_connect_url(sd_varlink **ret, const char *url) {
_cleanup_free_ char *c = NULL; _cleanup_free_ char *c = NULL;
const char *p; const char *p;
@ -499,7 +507,7 @@ _public_ int sd_varlink_connect_url(sd_varlink **ret, const char *url) {
assert_return(ret, -EINVAL); assert_return(ret, -EINVAL);
assert_return(url, -EINVAL); assert_return(url, -EINVAL);
// FIXME: Maybe add support for vsock: and ssh-exec: URL schemes here. // FIXME: Maybe add support for vsock: URL schemes here.
/* The Varlink URL scheme is a bit underdefined. We support only the spec-defined unix: transport for /* The Varlink URL scheme is a bit underdefined. We support only the spec-defined unix: transport for
* now, plus exec:, ssh: transports we made up ourselves. Strictly speaking this shouldn't even be * now, plus exec:, ssh: transports we made up ourselves. Strictly speaking this shouldn't even be
@ -514,11 +522,39 @@ _public_ int sd_varlink_connect_url(sd_varlink **ret, const char *url) {
scheme = SCHEME_SSH_UNIX; scheme = SCHEME_SSH_UNIX;
else if ((p = startswith(url, "ssh-exec:"))) else if ((p = startswith(url, "ssh-exec:")))
scheme = SCHEME_SSH_EXEC; scheme = SCHEME_SSH_EXEC;
else else {
return log_debug_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "URL scheme not supported."); /* scheme is not built-in: check if we have a bridge helper binary */
const char *colon = strchr(url, ':');
if (!colon)
return log_debug_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT),
"Invalid URL '%s': does not contain a ':'", url);
_cleanup_free_ char *scheme_name = strndup(url, colon - url);
if (!scheme_name)
return log_oom_debug();
if (!is_valid_url_scheme(scheme_name))
return log_debug_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT),
"URL scheme not valid as bridge name: %s", scheme_name);
const char *bridges_dir = secure_getenv("SYSTEMD_VARLINK_BRIDGES_DIR") ?: VARLINK_BRIDGES_DIR;
_cleanup_free_ char *bridge = path_join(bridges_dir, scheme_name);
if (!bridge)
return log_oom_debug();
if (access(bridge, X_OK) < 0) {
if (errno == ENOENT)
return log_debug_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "URL scheme '%s' not supported (and no auxiliary bridge binary is available).", scheme_name);
return log_debug_errno(errno, "Failed to look up varlink bridge binary '%s': %m", bridge);
}
return sd_varlink_connect_exec(ret, bridge, STRV_MAKE(bridge, url));
}
/* The varlink.org reference C library supports more than just file system paths. We might want to /* The varlink.org reference C library supports more than just file system paths. We might want to
* support that one day too. For now simply refuse that. */ * support that one day too. For now simply refuse that for our built-in schemes. It is fine for
* external scheme handled via plugins (see above). */
if (p[strcspn(p, ";?#")] != '\0') if (p[strcspn(p, ";?#")] != '\0')
return log_debug_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "URL parameterization with ';', '?', '#' not supported."); return log_debug_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "URL parameterization with ';', '?', '#' not supported.");

View File

@ -141,7 +141,8 @@ static int help(void) {
" --fsck=no Don't run file system check before mount\n" " --fsck=no Don't run file system check before mount\n"
" --description=TEXT Description for unit\n" " --description=TEXT Description for unit\n"
" -p --property=NAME=VALUE Set mount unit property\n" " -p --property=NAME=VALUE Set mount unit property\n"
" -A --automount=BOOL Create an auto-mount point\n" " --automount=BOOL Create an automount point\n"
" -A Same as --automount=yes\n"
" --timeout-idle-sec=SEC Specify automount idle timeout\n" " --timeout-idle-sec=SEC Specify automount idle timeout\n"
" --automount-property=NAME=VALUE\n" " --automount-property=NAME=VALUE\n"
" Set automount unit property\n" " Set automount unit property\n"

View File

@ -258,6 +258,16 @@ const struct ConfigPerfItem* network_netdev_gperf_lookup(const char *str, GPERF_
/* Macros which append INTERFACE= to the message */ /* Macros which append INTERFACE= to the message */
#define log_netdev_syntax(netdev, level, message_id, fmt, ...) \
({ \
const NetDev *_n = (netdev); \
const char *_ifname = _n ? _n->ifname : NULL; \
log_struct(level, \
LOG_MESSAGE(fmt, __VA_ARGS__), \
LOG_MESSAGE_ID(message_id), \
LOG_ITEM("INTERFACE=%s", strempty(_ifname))); \
})
#define log_netdev_full_errno_zerook(netdev, level, error, ...) \ #define log_netdev_full_errno_zerook(netdev, level, error, ...) \
({ \ ({ \
const NetDev *_n = (netdev); \ const NetDev *_n = (netdev); \

View File

@ -6,6 +6,8 @@
#include <net/if_arp.h> #include <net/if_arp.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include "sd-messages.h"
#include "alloc-util.h" #include "alloc-util.h"
#include "daemon-util.h" #include "daemon-util.h"
#include "fd-util.h" #include "fd-util.h"
@ -14,6 +16,7 @@
#include "socket-util.h" #include "socket-util.h"
#include "string-util.h" #include "string-util.h"
#include "tuntap.h" #include "tuntap.h"
#include "uid-classification.h"
#include "user-record.h" #include "user-record.h"
#include "user-util.h" #include "user-util.h"
#include "userdb.h" #include "userdb.h"
@ -236,27 +239,43 @@ static int tuntap_verify(NetDev *netdev, const char *filename) {
if (t->user_name) { if (t->user_name) {
_cleanup_(user_record_unrefp) UserRecord *ur = NULL; _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
r = userdb_by_name(t->user_name, &USERDB_MATCH_ROOT_AND_SYSTEM, r = userdb_by_name(t->user_name, /* match = */ NULL,
USERDB_SUPPRESS_SHADOW | USERDB_PARSE_NUMERIC, USERDB_SUPPRESS_SHADOW | USERDB_PARSE_NUMERIC,
&ur); &ur);
if (r < 0) if (r < 0)
log_netdev_warning_errno(netdev, r, "Cannot resolve user name '%s', ignoring: %s", log_netdev_warning_errno(netdev, r, "Cannot resolve user name '%s', ignoring: %s",
t->user_name, STRERROR_USER(r)); t->user_name, STRERROR_USER(r));
else else {
if (!uid_is_system(ur->uid))
log_netdev_syntax(netdev, LOG_WARNING,
SD_MESSAGE_SYSTEM_ACCOUNT_REQUIRED_STR,
"User '%s' configured as owner is not a system user. "
"Support for device node ownership by non-system accounts is deprecated and will be removed in the future.",
t->user_name);
t->uid = ur->uid; t->uid = ur->uid;
}
} }
if (t->group_name) { if (t->group_name) {
_cleanup_(group_record_unrefp) GroupRecord *gr = NULL; _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
r = groupdb_by_name(t->group_name, &USERDB_MATCH_ROOT_AND_SYSTEM, r = groupdb_by_name(t->group_name, /* match = */ NULL,
USERDB_SUPPRESS_SHADOW | USERDB_PARSE_NUMERIC, USERDB_SUPPRESS_SHADOW | USERDB_PARSE_NUMERIC,
&gr); &gr);
if (r < 0) if (r < 0)
log_netdev_warning_errno(netdev, r, "Cannot resolve group name '%s', ignoring: %s", log_netdev_warning_errno(netdev, r, "Cannot resolve group name '%s', ignoring: %s",
t->group_name, STRERROR_GROUP(r)); t->group_name, STRERROR_GROUP(r));
else else {
if (!gid_is_system(gr->gid))
log_netdev_syntax(netdev, LOG_WARNING,
SD_MESSAGE_SYSTEM_ACCOUNT_REQUIRED_STR,
"Group '%s' configured as owner is not a system group. "
"Support for device node ownership by non-system accounts is deprecated and will be removed in the future.",
t->group_name);
t->gid = gr->gid; t->gid = gr->gid;
}
} }
return 0; return 0;

View File

@ -292,6 +292,71 @@ STATIC_DESTRUCTOR_REGISTER(arg_settings_filename, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep); STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
STATIC_DESTRUCTOR_REGISTER(arg_background, freep); STATIC_DESTRUCTOR_REGISTER(arg_background, freep);
static int parse_private_users(
const char *s,
UserNamespaceMode *ret_userns_mode,
uid_t *ret_uid_shift,
uid_t *ret_uid_range) {
int boolean, r;
assert(ret_userns_mode);
assert(ret_uid_shift);
assert(ret_uid_range);
if (!s)
boolean = true;
else if (!in_charset(s, DIGITS))
/* do *not* parse numbers as booleans */
boolean = parse_boolean(s);
else
boolean = -1;
if (boolean == 0) {
/* no: User namespacing off */
*ret_userns_mode = USER_NAMESPACE_NO;
*ret_uid_shift = UID_INVALID;
*ret_uid_range = UINT32_C(0x10000);
} else if (boolean > 0) {
/* yes: User namespacing on, UID range is read from root dir */
*ret_userns_mode = USER_NAMESPACE_FIXED;
*ret_uid_shift = UID_INVALID;
*ret_uid_range = UINT32_C(0x10000);
} else if (streq(s, "pick")) {
/* pick: User namespacing on, UID range is picked randomly */
*ret_userns_mode = USER_NAMESPACE_PICK; /* Note that arg_userns_ownership is
* implied by USER_NAMESPACE_PICK
* further down. */
*ret_uid_shift = UID_INVALID;
*ret_uid_range = UINT32_C(0x10000);
} else if (streq(s, "identity")) {
/* identity: User namespaces on, UID range is map of the 0…0xFFFF range to
* itself, i.e. we don't actually map anything, but do take benefit of
* isolation of capability sets. */
*ret_userns_mode = USER_NAMESPACE_FIXED;
*ret_uid_shift = 0;
*ret_uid_range = UINT32_C(0x10000);
} else if (streq(optarg, "managed")) {
/* managed: User namespace on, and acquire it from systemd-nsresourced */
*ret_userns_mode = USER_NAMESPACE_MANAGED;
*ret_uid_shift = UID_INVALID;
*ret_uid_range = UINT32_C(0x10000);
} else {
/* anything else: User namespacing on, UID range is explicitly configured */
r = parse_userns_uid_range(optarg, ret_uid_shift, ret_uid_range);
if (r < 0)
return r;
*ret_userns_mode = USER_NAMESPACE_FIXED;
}
return 0;
}
static int help(void) { static int help(void) {
_cleanup_free_ char *link = NULL; _cleanup_free_ char *link = NULL;
int r; int r;
@ -942,31 +1007,23 @@ static int parse_argv(int argc, char *argv[]) {
} }
case 'M': case 'M':
if (isempty(optarg)) if (!isempty(optarg) && !hostname_is_valid(optarg, /* flags= */ 0))
arg_machine = mfree(arg_machine); return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
else { "Invalid machine name: %s", optarg);
if (!hostname_is_valid(optarg, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Invalid machine name: %s", optarg);
r = free_and_strdup(&arg_machine, optarg); r = free_and_strdup_warn(&arg_machine, optarg);
if (r < 0) if (r < 0)
return log_oom(); return r;
}
break; break;
case ARG_HOSTNAME: case ARG_HOSTNAME:
if (isempty(optarg)) if (!isempty(optarg) && !hostname_is_valid(optarg, /* flags= */ 0))
arg_hostname = mfree(arg_hostname); return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
else { "Invalid hostname: %s", optarg);
if (!hostname_is_valid(optarg, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Invalid hostname: %s", optarg);
r = free_and_strdup(&arg_hostname, optarg); r = free_and_strdup_warn(&arg_hostname, optarg);
if (r < 0) if (r < 0)
return log_oom(); return r;
}
arg_settings_mask |= SETTING_HOSTNAME; arg_settings_mask |= SETTING_HOSTNAME;
break; break;
@ -1141,58 +1198,13 @@ static int parse_argv(int argc, char *argv[]) {
break; break;
case ARG_PRIVATE_USERS: { case ARG_PRIVATE_USERS:
int boolean; r = parse_private_users(optarg, &arg_userns_mode, &arg_uid_shift, &arg_uid_range);
if (r < 0)
if (!optarg) return r;
boolean = true;
else if (!in_charset(optarg, DIGITS))
/* do *not* parse numbers as booleans */
boolean = parse_boolean(optarg);
else
boolean = -1;
if (boolean == 0) {
/* no: User namespacing off */
arg_userns_mode = USER_NAMESPACE_NO;
arg_uid_shift = UID_INVALID;
arg_uid_range = UINT32_C(0x10000);
} else if (boolean > 0) {
/* yes: User namespacing on, UID range is read from root dir */
arg_userns_mode = USER_NAMESPACE_FIXED;
arg_uid_shift = UID_INVALID;
arg_uid_range = UINT32_C(0x10000);
} else if (streq(optarg, "pick")) {
/* pick: User namespacing on, UID range is picked randomly */
arg_userns_mode = USER_NAMESPACE_PICK; /* Note that arg_userns_ownership is
* implied by USER_NAMESPACE_PICK
* further down. */
arg_uid_shift = UID_INVALID;
arg_uid_range = UINT32_C(0x10000);
} else if (streq(optarg, "identity")) {
/* identity: User namespaces on, UID range is map of the 0…0xFFFF range to
* itself, i.e. we don't actually map anything, but do take benefit of
* isolation of capability sets. */
arg_userns_mode = USER_NAMESPACE_FIXED;
arg_uid_shift = 0;
arg_uid_range = UINT32_C(0x10000);
} else if (streq(optarg, "managed")) {
/* managed: User namespace on, and acquire it from systemd-nsresourced */
arg_userns_mode = USER_NAMESPACE_MANAGED;
arg_uid_shift = UID_INVALID;
arg_uid_range = UINT32_C(0x10000);
} else {
/* anything else: User namespacing on, UID range is explicitly configured */
r = parse_userns_uid_range(optarg, &arg_uid_shift, &arg_uid_range);
if (r < 0)
return r;
arg_userns_mode = USER_NAMESPACE_FIXED;
}
arg_settings_mask |= SETTING_USERNS; arg_settings_mask |= SETTING_USERNS;
break; break;
}
case 'U': case 'U':
if (userns_supported()) { if (userns_supported()) {

View File

@ -250,13 +250,11 @@ static int run(int argc, char* argv[]) {
if (r <= 0) if (r <= 0)
return r; return r;
if (argc > optind) { if (optind >= argc)
r = 0; return list_paths();
for (int i = optind; i < argc; i++)
RET_GATHER(r, print_path(argv[i]));
} else
r = list_paths();
for (int i = optind; i < argc; i++)
RET_GATHER(r, print_path(argv[i]));
return r; return r;
} }

View File

@ -14,6 +14,12 @@
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
typedef enum LookupType {
LOOKUP_TYPE_REGULAR,
LOOKUP_TYPE_PRIVATE,
LOOKUP_TYPE_EXCLUSIVE, /* -x */
} LookupType;
static int resolvconf_help(void) { static int resolvconf_help(void) {
_cleanup_free_ char *link = NULL; _cleanup_free_ char *link = NULL;
int r; int r;
@ -94,6 +100,75 @@ static int parse_search_domain(const char *string) {
return 0; return 0;
} }
static int parse_stdin(LookupType lookup_type) {
int r;
for (unsigned n = 0;;) {
_cleanup_free_ char *line = NULL;
const char *a;
r = read_stripped_line(stdin, LONG_LINE_MAX, &line);
if (r < 0)
return log_error_errno(r, "Failed to read from stdin: %m");
if (r == 0)
break;
n++;
if (IN_SET(*line, '#', ';', 0))
continue;
a = first_word(line, "nameserver");
if (a) {
(void) parse_nameserver(a);
continue;
}
a = first_word(line, "domain");
if (!a)
a = first_word(line, "search");
if (a) {
(void) parse_search_domain(a);
continue;
}
log_syntax(NULL, LOG_DEBUG, "stdin", n, 0, "Ignoring resolv.conf line: %s", line);
}
switch (lookup_type) {
case LOOKUP_TYPE_REGULAR:
break;
case LOOKUP_TYPE_PRIVATE:
arg_disable_default_route = true;
break;
case LOOKUP_TYPE_EXCLUSIVE:
/* If -x mode is selected, let's preferably route non-suffixed lookups to this interface.
* This somewhat matches the original -x behaviour */
r = strv_extend(&arg_set_domain, "~.");
if (r < 0)
return log_oom();
break;
default:
assert_not_reached();
}
if (strv_isempty(arg_set_dns))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"No DNS servers specified, refusing operation.");
if (strv_isempty(arg_set_domain)) {
/* When no domain/search is set, clear the current domains. */
r = strv_extend(&arg_set_domain, "");
if (r < 0)
return log_oom();
}
return 0;
}
int resolvconf_parse_argv(int argc, char *argv[]) { int resolvconf_parse_argv(int argc, char *argv[]) {
enum { enum {
@ -114,22 +189,18 @@ int resolvconf_parse_argv(int argc, char *argv[]) {
{} {}
}; };
enum {
TYPE_REGULAR,
TYPE_PRIVATE,
TYPE_EXCLUSIVE, /* -x */
} type = TYPE_REGULAR;
int c, r; int c, r;
assert(argc >= 0); assert(argc >= 0);
assert(argv); assert(argv);
/* openresolv checks these environment variables */ /* openresolv checks these environment variables */
LookupType lookup_type = LOOKUP_TYPE_REGULAR;
if (getenv("IF_EXCLUSIVE")) if (getenv("IF_EXCLUSIVE"))
type = TYPE_EXCLUSIVE; lookup_type = LOOKUP_TYPE_EXCLUSIVE;
if (getenv("IF_PRIVATE")) if (getenv("IF_PRIVATE"))
type = TYPE_PRIVATE; lookup_type = LOOKUP_TYPE_PRIVATE;
arg_mode = _MODE_INVALID; arg_mode = _MODE_INVALID;
@ -153,11 +224,11 @@ int resolvconf_parse_argv(int argc, char *argv[]) {
/* The exclusive/private/force stuff is an openresolv invention, we support in some skewed way */ /* The exclusive/private/force stuff is an openresolv invention, we support in some skewed way */
case 'x': case 'x':
type = TYPE_EXCLUSIVE; lookup_type = LOOKUP_TYPE_EXCLUSIVE;
break; break;
case 'p': case 'p':
type = TYPE_PRIVATE; lookup_type = LOOKUP_TYPE_PRIVATE;
break; break;
case 'f': case 'f':
@ -215,75 +286,12 @@ int resolvconf_parse_argv(int argc, char *argv[]) {
r = ifname_resolvconf_mangle(argv[optind]); r = ifname_resolvconf_mangle(argv[optind]);
if (r <= 0) if (r <= 0)
return r; return r;
optind++; optind++;
if (arg_mode == MODE_SET_LINK) { if (arg_mode == MODE_SET_LINK) {
unsigned n = 0; r = parse_stdin(lookup_type);
if (r < 0)
for (;;) { return r;
_cleanup_free_ char *line = NULL;
const char *a;
r = read_stripped_line(stdin, LONG_LINE_MAX, &line);
if (r < 0)
return log_error_errno(r, "Failed to read from stdin: %m");
if (r == 0)
break;
n++;
if (IN_SET(*line, '#', ';', 0))
continue;
a = first_word(line, "nameserver");
if (a) {
(void) parse_nameserver(a);
continue;
}
a = first_word(line, "domain");
if (!a)
a = first_word(line, "search");
if (a) {
(void) parse_search_domain(a);
continue;
}
log_syntax(NULL, LOG_DEBUG, "stdin", n, 0, "Ignoring resolv.conf line: %s", line);
}
switch (type) {
case TYPE_REGULAR:
break;
case TYPE_PRIVATE:
arg_disable_default_route = true;
break;
case TYPE_EXCLUSIVE:
/* If -x mode is selected, let's preferably route non-suffixed lookups to this interface. This
* somewhat matches the original -x behaviour */
r = strv_extend(&arg_set_domain, "~.");
if (r < 0)
return log_oom();
break;
default:
assert_not_reached();
}
if (strv_isempty(arg_set_dns))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"No DNS servers specified, refusing operation.");
if (strv_isempty(arg_set_domain)) {
/* When no domain/search is set, clear the current domains. */
r = strv_extend(&arg_set_domain, "");
if (r < 0)
return log_oom();
}
} }
return 1; /* work to do */ return 1; /* work to do */

View File

@ -303,6 +303,9 @@ _SD_BEGIN_DECLARATIONS;
#define SD_MESSAGE_CANT_BREAK_ORDERING_CYCLE SD_ID128_MAKE(b3,11,2d,da,d1,90,45,53,8c,76,68,5b,a5,91,8a,80) #define SD_MESSAGE_CANT_BREAK_ORDERING_CYCLE SD_ID128_MAKE(b3,11,2d,da,d1,90,45,53,8c,76,68,5b,a5,91,8a,80)
#define SD_MESSAGE_CANT_BREAK_ORDERING_CYCLE_STR SD_ID128_MAKE_STR(b3,11,2d,da,d1,90,45,53,8c,76,68,5b,a5,91,8a,80) #define SD_MESSAGE_CANT_BREAK_ORDERING_CYCLE_STR SD_ID128_MAKE_STR(b3,11,2d,da,d1,90,45,53,8c,76,68,5b,a5,91,8a,80)
#define SD_MESSAGE_SYSTEM_ACCOUNT_REQUIRED SD_ID128_MAKE(34,05,20,5d,36,8e,49,fe,b5,ab,39,25,fe,e1,38,74)
#define SD_MESSAGE_SYSTEM_ACCOUNT_REQUIRED_STR SD_ID128_MAKE_STR(34,05,20,5d,36,8e,49,fe,b5,ab,39,25,fe,e1,38,74)
_SD_END_DECLARATIONS; _SD_END_DECLARATIONS;
#endif #endif

View File

@ -246,7 +246,10 @@ TEST(proc, .sd_booted = true) {
_cleanup_(pidref_done) PidRef pid = PIDREF_NULL; _cleanup_(pidref_done) PidRef pid = PIDREF_NULL;
uid_t uid = UID_INVALID; uid_t uid = UID_INVALID;
ASSERT_OK(r = proc_dir_read_pidref(d, &pid)); r = proc_dir_read_pidref(d, &pid);
if (r == -EINVAL) /* Can happen if we pick a non-thread-group leader */
continue;
ASSERT_OK(r);
if (r == 0) if (r == 0)
break; break;

View File

@ -5,6 +5,7 @@
#include <unistd.h> #include <unistd.h>
#include "sd-json.h" #include "sd-json.h"
#include "sd-messages.h"
#include "alloc-util.h" #include "alloc-util.h"
#include "architecture.h" #include "architecture.h"
@ -50,6 +51,7 @@
#include "udev-trace.h" #include "udev-trace.h"
#include "udev-util.h" #include "udev-util.h"
#include "udev-worker.h" #include "udev-worker.h"
#include "uid-classification.h"
#include "user-record.h" #include "user-record.h"
#include "user-util.h" #include "user-util.h"
#include "userdb.h" #include "userdb.h"
@ -226,6 +228,26 @@ static bool token_is_for_parents(UdevRuleToken *token) {
/*** Logging helpers ***/ /*** Logging helpers ***/
#define log_udev_event_syntax(event, token, level, message_id, fmt, ...) \
({ \
UdevEvent *_event = (event); \
UdevRuleToken *_token = ASSERT_PTR(token); \
int _level = (level); \
sd_device *_d = token_is_for_parents(_token) ? _event->dev_parent : _event->dev; \
const char *_sysname = NULL; \
\
if (_d && log_get_max_level() >= LOG_PRI(_level)) \
(void) sd_device_get_sysname(_d, &_sysname); \
log_struct(_level, \
LOG_MESSAGE("%s:%u %s:" fmt, \
strna(_token->rule_line->rule_file ? _token->rule_line->rule_file->filename : NULL), \
_token->rule_line->line_number, \
_token->token_str, \
__VA_ARGS__), \
LOG_MESSAGE_ID(message_id), \
LOG_ITEM("DEVICE=%s", strempty(_sysname))); \
})
#define _log_udev_rule_file_full(device, device_u, file, file_u, line_nr, level, level_u, error, fmt, ...) \ #define _log_udev_rule_file_full(device, device_u, file, file_u, line_nr, level, level_u, error, fmt, ...) \
({ \ ({ \
int level_u = (level); \ int level_u = (level); \
@ -509,13 +531,19 @@ static int rule_resolve_user(UdevRuleLine *rule_line, const char *name, uid_t *r
} }
_cleanup_(user_record_unrefp) UserRecord *ur = NULL; _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
r = userdb_by_name(name, &USERDB_MATCH_ROOT_AND_SYSTEM, r = userdb_by_name(name, /* match= */ NULL,
USERDB_SUPPRESS_SHADOW | USERDB_PARSE_NUMERIC | USERDB_SYNTHESIZE_NUMERIC, USERDB_SUPPRESS_SHADOW | USERDB_PARSE_NUMERIC | USERDB_SYNTHESIZE_NUMERIC,
&ur); &ur);
if (r < 0) if (r < 0)
return log_line_error_errno(rule_line, r, return log_line_error_errno(rule_line, r,
"Failed to resolve user '%s', ignoring: %s", "Failed to resolve user '%s', ignoring: %s",
name, STRERROR_USER(r)); name, STRERROR_USER(r));
if (!uid_is_system(ur->uid))
log_struct(LOG_WARNING,
LOG_MESSAGE("%s:%u User %s configured to own a device node is not a system user. "
"Support for device node ownership by non-system accounts is deprecated and will be removed in the future.",
rule_line->rule_file->filename, rule_line->line_number, name),
LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_ACCOUNT_REQUIRED_STR));
_cleanup_free_ char *n = strdup(name); _cleanup_free_ char *n = strdup(name);
if (!n) if (!n)
@ -544,13 +572,19 @@ static int rule_resolve_group(UdevRuleLine *rule_line, const char *name, gid_t *
} }
_cleanup_(group_record_unrefp) GroupRecord *gr = NULL; _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
r = groupdb_by_name(name, &USERDB_MATCH_ROOT_AND_SYSTEM, r = groupdb_by_name(name, /* match= */ NULL,
USERDB_SUPPRESS_SHADOW | USERDB_PARSE_NUMERIC | USERDB_SYNTHESIZE_NUMERIC, USERDB_SUPPRESS_SHADOW | USERDB_PARSE_NUMERIC | USERDB_SYNTHESIZE_NUMERIC,
&gr); &gr);
if (r < 0) if (r < 0)
return log_line_error_errno(rule_line, r, return log_line_error_errno(rule_line, r,
"Failed to resolve group '%s', ignoring: %s", "Failed to resolve group '%s', ignoring: %s",
name, STRERROR_GROUP(r)); name, STRERROR_GROUP(r));
if (!gid_is_system(gr->gid))
log_struct(LOG_WARNING,
LOG_MESSAGE("%s:%u Group %s configured to own a device node is not a system group. "
"Support for device node ownership by non-system accounts is deprecated and will be removed in the future.",
rule_line->rule_file->filename, rule_line->line_number, name),
LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_ACCOUNT_REQUIRED_STR));
_cleanup_free_ char *n = strdup(name); _cleanup_free_ char *n = strdup(name);
if (!n) if (!n)
@ -2674,7 +2708,7 @@ static int udev_rule_apply_token_to_event(
return true; return true;
_cleanup_(user_record_unrefp) UserRecord *ur = NULL; _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
r = userdb_by_name(owner, &USERDB_MATCH_ROOT_AND_SYSTEM, r = userdb_by_name(owner, /* match= */ NULL,
USERDB_SUPPRESS_SHADOW | USERDB_PARSE_NUMERIC | USERDB_SYNTHESIZE_NUMERIC, USERDB_SUPPRESS_SHADOW | USERDB_PARSE_NUMERIC | USERDB_SYNTHESIZE_NUMERIC,
&ur); &ur);
if (r < 0) if (r < 0)
@ -2682,6 +2716,13 @@ static int udev_rule_apply_token_to_event(
"Failed to resolve user \"%s\", ignoring: %s", "Failed to resolve user \"%s\", ignoring: %s",
owner, STRERROR_USER(r)); owner, STRERROR_USER(r));
else { else {
if (!uid_is_system(ur->uid))
log_udev_event_syntax(event, token, LOG_WARNING,
SD_MESSAGE_SYSTEM_ACCOUNT_REQUIRED_STR,
"User %s configured to own a device node is not a system user. "
"Support for device node ownership by non-system accounts is deprecated and will be removed in the future.",
owner);
event->uid = ur->uid; event->uid = ur->uid;
log_event_debug(event, token, "Set owner: %s("UID_FMT")", owner, event->uid); log_event_debug(event, token, "Set owner: %s("UID_FMT")", owner, event->uid);
} }
@ -2700,7 +2741,7 @@ static int udev_rule_apply_token_to_event(
return true; return true;
_cleanup_(group_record_unrefp) GroupRecord *gr = NULL; _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
r = groupdb_by_name(group, &USERDB_MATCH_ROOT_AND_SYSTEM, r = groupdb_by_name(group, /* match= */ NULL,
USERDB_SUPPRESS_SHADOW | USERDB_PARSE_NUMERIC | USERDB_SYNTHESIZE_NUMERIC, USERDB_SUPPRESS_SHADOW | USERDB_PARSE_NUMERIC | USERDB_SYNTHESIZE_NUMERIC,
&gr); &gr);
if (r < 0) if (r < 0)
@ -2708,6 +2749,13 @@ static int udev_rule_apply_token_to_event(
"Failed to resolve group \"%s\", ignoring: %s", "Failed to resolve group \"%s\", ignoring: %s",
group, STRERROR_GROUP(r)); group, STRERROR_GROUP(r));
else { else {
if (!gid_is_system(gr->gid))
log_udev_event_syntax(event, token, LOG_WARNING,
SD_MESSAGE_SYSTEM_ACCOUNT_REQUIRED_STR,
"Group %s configured to own a device node is not a system group. "
"Support for device node ownership by non-system accounts is deprecated and will be removed in the future.",
group);
event->gid = gr->gid; event->gid = gr->gid;
log_event_debug(event, token, "Set group: %s("GID_FMT")", group, event->gid); log_event_debug(event, token, "Set group: %s("GID_FMT")", group, event->gid);
} }

View File

@ -11,3 +11,5 @@ executables += [
'sources' : varlinkctl_sources, 'sources' : varlinkctl_sources,
}, },
] ]
install_emptydir(varlinkbridgesdir)

View File

@ -315,8 +315,10 @@ if ! getent passwd 12345 >/dev/null; then
fi fi
# regular user # regular user
if getent passwd testuser >/dev/null; then if getent passwd testuser >/dev/null; then
test_syntax_error 'OWNER="testuser"' "Failed to resolve user 'testuser', ignoring: Not a system user" echo 'OWNER="testuser"' >"${rules}"
test_syntax_error "OWNER=\"$(id -u testuser)\"" "Failed to resolve user '$(id -u testuser)', ignoring: Not a system user" udevadm verify "${rules}"
echo "OWNER=\"$(id -u testuser)\"" >"${rules}"
udevadm verify "${rules}"
fi fi
test_syntax_error 'GROUP{a}="b"' 'Invalid attribute for GROUP.' test_syntax_error 'GROUP{a}="b"' 'Invalid attribute for GROUP.'
test_syntax_error 'GROUP-="b"' 'Invalid operator for GROUP.' test_syntax_error 'GROUP-="b"' 'Invalid operator for GROUP.'
@ -347,8 +349,11 @@ if ! getent group 12345 >/dev/null; then
fi fi
# regular group # regular group
if getent group testuser >/dev/null; then if getent group testuser >/dev/null; then
test_syntax_error 'GROUP="testuser"' "Failed to resolve group 'testuser', ignoring: Not a system group" echo 'GROUP="testuser"' >"${rules}"
test_syntax_error "GROUP=\"$(id -g testuser)\"" "Failed to resolve group '$(id -g testuser)', ignoring: Not a system group" udevadm verify "${rules}"
echo "GROUP=\"$(id -g testuser)\"" >"${rules}"
udevadm verify "${rules}"
fi fi
test_syntax_error 'MODE{a}="b"' 'Invalid attribute for MODE.' test_syntax_error 'MODE{a}="b"' 'Invalid attribute for MODE.'
test_syntax_error 'MODE-="b"' 'Invalid operator for MODE.' test_syntax_error 'MODE-="b"' 'Invalid operator for MODE.'