New upstream version 251.2

This commit is contained in:
Michael Biebl 2022-06-03 18:12:00 +02:00
parent 5249429fa0
commit cfd0f7785c
44 changed files with 574 additions and 310 deletions

View File

@ -686,7 +686,7 @@
<varlistentry> <varlistentry>
<term><option>--luks-cipher=</option><replaceable>CIPHER</replaceable></term> <term><option>--luks-cipher=</option><replaceable>CIPHER</replaceable></term>
<term><option>--luks-cipher-mode=</option><replaceable>MODE</replaceable></term> <term><option>--luks-cipher-mode=</option><replaceable>MODE</replaceable></term>
<term><option>--luks-volume-key-size=</option><replaceable>BITS</replaceable></term> <term><option>--luks-volume-key-size=</option><replaceable>BYTES</replaceable></term>
<term><option>--luks-pbkdf-type=</option><replaceable>TYPE</replaceable></term> <term><option>--luks-pbkdf-type=</option><replaceable>TYPE</replaceable></term>
<term><option>--luks-pbkdf-hash-algorithm=</option><replaceable>ALGORITHM</replaceable></term> <term><option>--luks-pbkdf-hash-algorithm=</option><replaceable>ALGORITHM</replaceable></term>
<term><option>--luks-pbkdf-time-cost=</option><replaceable>SECONDS</replaceable></term> <term><option>--luks-pbkdf-time-cost=</option><replaceable>SECONDS</replaceable></term>
@ -696,7 +696,12 @@
<listitem><para>Configures various cryptographic parameters for the LUKS2 storage mechanism. See <listitem><para>Configures various cryptographic parameters for the LUKS2 storage mechanism. See
<citerefentry <citerefentry
project='man-pages'><refentrytitle>cryptsetup</refentrytitle><manvolnum>8</manvolnum></citerefentry> project='man-pages'><refentrytitle>cryptsetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>
for details on the specific attributes.</para></listitem> for details on the specific attributes.</para>
<para>Note that <command>homectl</command> uses bytes for key size, like
<filename>/proc/crypto</filename>, but <citerefentry
project='man-pages'><refentrytitle>cryptsetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>
uses bits.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>

View File

@ -121,7 +121,7 @@
will be stored as an EFI variable in that case, overriding this option. will be stored as an EFI variable in that case, overriding this option.
</para> </para>
<para>If set to <literal>menu-hidden</literal> or <literal>0</literal> no menu <para>If set to <literal>menu-hidden</literal> or <literal>0</literal> (the default) no menu
is shown and the default entry will be booted immediately. The menu can be shown is shown and the default entry will be booted immediately. The menu can be shown
by pressing and holding a key before systemd-boot is launched. Setting this to by pressing and holding a key before systemd-boot is launched. Setting this to
<literal>menu-force</literal> disables the timeout while always showing the menu.</para> <literal>menu-force</literal> disables the timeout while always showing the menu.</para>
@ -211,7 +211,7 @@
<varlistentry> <varlistentry>
<term>beep</term> <term>beep</term>
<listitem><para>Beep n times when the n-th entry in the boot menu is shown (default disabled). <listitem><para>Takes a boolean argument. If timeout enabled beep every second, otherwise beep n times when n-th entry in boot menu is selected (default disabled).
Currently, only x86 is supported, where it uses the PC speaker.</para></listitem> Currently, only x86 is supported, where it uses the PC speaker.</para></listitem>
</varlistentry> </varlistentry>

View File

@ -0,0 +1,18 @@
/* SPDX-License-Identifier: CC0-1.0 */
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sd-bus.h>
int writer_with_negative_errno_return(int fd, sd_bus_error *error) {
const char *message = "Hello, World!\n";
ssize_t n = write(fd, message, strlen(message));
if (n >= 0)
return n; /* On success, return the number of bytes written, possibly 0. */
/* On error, initialize the error structure, and also propagate the errno
* value that write(2) set for us. */
return sd_bus_error_set_errnof(error, errno, "Failed to write to fd %i: %m", fd);
}

View File

@ -246,10 +246,15 @@
values in <parameter>e</parameter>, if <parameter>e</parameter> has been set with an error value before. values in <parameter>e</parameter>, if <parameter>e</parameter> has been set with an error value before.
Otherwise, it will return immediately. If the strings in <parameter>e</parameter> were set using Otherwise, it will return immediately. If the strings in <parameter>e</parameter> were set using
<function>sd_bus_error_set_const()</function>, they will be shared. Otherwise, they will be <function>sd_bus_error_set_const()</function>, they will be shared. Otherwise, they will be
copied. Returns a converted <varname>errno</varname>-like, negative error code or <constant>0</constant>. copied. Before this call, <parameter>dst</parameter> must be unset, i.e. either freshly initialized with
Before this call, <parameter>dst</parameter> must be unset, i.e. either freshly initialized with
<constant>NULL</constant> or reset using <function>sd_bus_error_free()</function>.</para> <constant>NULL</constant> or reset using <function>sd_bus_error_free()</function>.</para>
<para><function>sd_bus_error_copy()</function> generally returns <constant>0</constant> or a negative
<varname>errno</varname>-like value based on the input parameter <parameter>e</parameter>:
<constant>0</constant> if it was unset and a negative integer if it was set to some error, similarly to
<function>sd_bus_error_set()</function>. It may however also return an error generated internally, for
example <constant>-ENOMEM</constant> if a memory allocation fails.</para>
<para><function>sd_bus_error_move()</function> is similar to <function>sd_bus_error_copy()</function>, <para><function>sd_bus_error_move()</function> is similar to <function>sd_bus_error_copy()</function>,
but will move any error information from <parameter>e</parameter> into <parameter>dst</parameter>, but will move any error information from <parameter>e</parameter> into <parameter>dst</parameter>,
resetting the former. This function cannot fail, as no new memory is allocated. Note that if resetting the former. This function cannot fail, as no new memory is allocated. Note that if
@ -286,6 +291,18 @@
to <constant>NULL</constant>. The structure may be reused afterwards.</para> to <constant>NULL</constant>. The structure may be reused afterwards.</para>
</refsect1> </refsect1>
<refsect1>
<title>Reference ownership</title>
<para><structname>sd_bus_error</structname> is not reference-counted. Users should destroy resources held
by it by calling <function>sd_bus_error_free()</function>. Usually, error structures are allocated on the
stack or passed in as function parameters, but they may also be allocated dynamically, in which case it
is the duty of the caller to <citerefentry
project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> the memory
held by the structure itself after freeing its contents with
<function>sd_bus_error_free()</function>.</para>
</refsect1>
<refsect1> <refsect1>
<title>Return Value</title> <title>Return Value</title>
@ -297,7 +314,8 @@
<function>sd_bus_error_set_errnofv()</function>, return <constant>0</constant> when the specified error <function>sd_bus_error_set_errnofv()</function>, return <constant>0</constant> when the specified error
value is <constant>0</constant>, and a negative errno-like value corresponding to the value is <constant>0</constant>, and a negative errno-like value corresponding to the
<parameter>error</parameter> parameter otherwise. If an error occurs internally, one of the negative <parameter>error</parameter> parameter otherwise. If an error occurs internally, one of the negative
error values listed below will be returned.</para> error values listed below will be returned. This allows those functions to be conveniently used in a
<constant>return</constant> statement, see the example below.</para>
<para><function>sd_bus_error_get_errno()</function> returns <para><function>sd_bus_error_get_errno()</function> returns
<constant>false</constant> when <parameter>e</parameter> is <constant>false</constant> when <parameter>e</parameter> is
@ -305,7 +323,9 @@
<parameter>e-&gt;name</parameter> otherwise.</para> <parameter>e-&gt;name</parameter> otherwise.</para>
<para><function>sd_bus_error_copy()</function> and <function>sd_bus_error_move()</function> return a <para><function>sd_bus_error_copy()</function> and <function>sd_bus_error_move()</function> return a
negative error value converted from the source error, and zero if the error has not been set.</para> negative error value converted from the source error, and zero if the error has not been set. This
allows those functions to be conveniently used in a <constant>return</constant> statement, see the
example below.</para>
<para><function>sd_bus_error_is_set()</function> returns a <para><function>sd_bus_error_is_set()</function> returns a
non-zero value when <parameter>e</parameter> and the non-zero value when <parameter>e</parameter> and the
@ -316,32 +336,18 @@
<function>sd_bus_error_has_names_sentinel()</function> return a non-zero value when <parameter>e</parameter> is <function>sd_bus_error_has_names_sentinel()</function> return a non-zero value when <parameter>e</parameter> is
non-<constant>NULL</constant> and the <structfield>name</structfield> field is equal to one of the given non-<constant>NULL</constant> and the <structfield>name</structfield> field is equal to one of the given
names, zero otherwise.</para> names, zero otherwise.</para>
</refsect1>
<refsect1>
<title>Reference ownership</title>
<para><structname>sd_bus_error</structname> is not reference
counted. Users should destroy resources held by it by calling
<function>sd_bus_error_free()</function>. Usually, error structures
are allocated on the stack or passed in as function parameters,
but they may also be allocated dynamically, in which case it is
the duty of the caller to <citerefentry
project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry>
the memory held by the structure itself after freeing its contents
with <function>sd_bus_error_free()</function>.</para>
<refsect2> <refsect2>
<title>Errors</title> <title>Errors</title>
<para>Returned errors may indicate the following problems:</para> <para>Return value may indicate the following problems in the invocation of the function itself:</para>
<variablelist> <variablelist>
<varlistentry> <varlistentry>
<term><constant>-EINVAL</constant></term> <term><constant>-EINVAL</constant></term>
<listitem><para>Error was already set in <structname>sd_bus_error</structname> structure when one <listitem><para>Error was already set in the <structname>sd_bus_error</structname> structure when
the error-setting functions was called.</para></listitem> one the error-setting functions was called.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
@ -350,9 +356,29 @@
<listitem><para>Memory allocation failed.</para></listitem> <listitem><para>Memory allocation failed.</para></listitem>
</varlistentry> </varlistentry>
</variablelist> </variablelist>
<para>On success, <function>sd_bus_error_set()</function>, <function>sd_bus_error_setf()</function>,
<function>sd_bus_error_set_const()</function>, <function>sd_bus_error_set_errno()</function>,
<function>sd_bus_error_set_errnof()</function>, <function>sd_bus_error_set_errnofv()</function>,
<function>sd_bus_error_copy()</function>, and <function>sd_bus_error_move()</function> will return a
negative converted <varname>errno</varname>-style value, or <constant>0</constant> if the error
parameter is <constant>NULL</constant> or unset. D-Bus errors are converted to the integral
<varname>errno</varname>-style value, and the mapping mechanism is extensible, see the discussion
above. This effectively means that almost any negative <varname>errno</varname>-style value can be
returned.</para>
</refsect2> </refsect2>
</refsect1> </refsect1>
<refsect1>
<title>Examples</title>
<example>
<title>Using the negative return value to propagate an error</title>
<programlisting><xi:include href="sd_bus_error-example.c" parse="text" /></programlisting>
</example>
</refsect1>
<xi:include href="libsystemd-pkgconfig.xml" /> <xi:include href="libsystemd-pkgconfig.xml" />
<refsect1> <refsect1>

View File

@ -18,7 +18,7 @@
<refnamediv> <refnamediv>
<refname>shutdown</refname> <refname>shutdown</refname>
<refpurpose>Halt, power-off or reboot the machine</refpurpose> <refpurpose>Halt, power off or reboot the machine</refpurpose>
</refnamediv> </refnamediv>
<refsynopsisdiv> <refsynopsisdiv>
@ -33,8 +33,7 @@
<refsect1> <refsect1>
<title>Description</title> <title>Description</title>
<para><command>shutdown</command> may be used to halt, power-off <para><command>shutdown</command> may be used to halt, power off, or reboot the machine.</para>
or reboot the machine.</para>
<para>The first argument may be a time string (which is usually <para>The first argument may be a time string (which is usually
<literal>now</literal>). Optionally, this may be followed by a <literal>now</literal>). Optionally, this may be followed by a
@ -81,47 +80,41 @@
<term><option>-P</option></term> <term><option>-P</option></term>
<term><option>--poweroff</option></term> <term><option>--poweroff</option></term>
<listitem><para>Power-off the machine (the <listitem><para>Power the machine off (the default).</para></listitem>
default).</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><option>-r</option></term> <term><option>-r</option></term>
<term><option>--reboot</option></term> <term><option>--reboot</option></term>
<listitem><para>Reboot the <listitem><para>Reboot the machine.</para></listitem>
machine.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><option>-h</option></term> <term><option>-h</option></term>
<listitem><para>Equivalent to <option>--poweroff</option>, <listitem><para>The same as <option>--poweroff</option>, but does not override the action to take if
unless <option>--halt</option> is specified.</para></listitem> it is "halt". E.g. <command>shutdown --reboot -h</command> means "poweroff", but <command>shutdown
--halt -h</command> means "halt".</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><option>-k</option></term> <term><option>-k</option></term>
<listitem><para>Do not halt, power-off, reboot, just write <listitem><para>Do not halt, power off, or reboot, but just write the wall message.</para></listitem>
wall message.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><option>--no-wall</option></term> <term><option>--no-wall</option></term>
<listitem><para>Do not send wall <listitem><para>Do not send wall message before halt, power off, or reboot.</para></listitem>
message before
halt, power-off, reboot.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><option>-c</option></term> <term><option>-c</option></term>
<listitem><para>Cancel a pending shutdown. This may be used <listitem><para>Cancel a pending shutdown. This may be used to cancel the effect of an invocation of
to cancel the effect of an invocation of <command>shutdown</command> with a time argument that is not <literal>+0</literal> or
<command>shutdown</command> with a time argument that is not
<literal>+0</literal> or
<literal>now</literal>.</para></listitem> <literal>now</literal>.</para></listitem>
</varlistentry> </varlistentry>

View File

@ -25,6 +25,8 @@
<cmdsynopsis> <cmdsynopsis>
<command>systemd-creds</command> <command>systemd-creds</command>
<arg choice="opt" rep="repeat">OPTIONS</arg> <arg choice="opt" rep="repeat">OPTIONS</arg>
<arg choice="plain">COMMAND</arg>
<arg choice="opt" rep="repeat">ARGS</arg>
</cmdsynopsis> </cmdsynopsis>
</refsynopsisdiv> </refsynopsisdiv>

View File

@ -26,10 +26,9 @@
<refsect1> <refsect1>
<title>Description</title> <title>Description</title>
<para>A unit configuration file whose name ends in <para>A unit configuration file whose name ends in <literal>.automount</literal> encodes information
<literal>.automount</literal> encodes information about a file about a file system automount point controlled and supervised by systemd. Automount units may be used to
system automount point controlled and supervised by implement on-demand mounting as well as parallelized mounting of file systems.</para>
systemd.</para>
<para>This man page lists the configuration options specific to <para>This man page lists the configuration options specific to
this unit type. See this unit type. See
@ -55,9 +54,6 @@
accesses <filename>/home/lennart</filename> the mount unit accesses <filename>/home/lennart</filename> the mount unit
<filename>home-lennart.mount</filename> will be activated.</para> <filename>home-lennart.mount</filename> will be activated.</para>
<para>Automount units may be used to implement on-demand mounting
as well as parallelized mounting of file systems.</para>
<para>Note that automount units are separate from the mount itself, so you <para>Note that automount units are separate from the mount itself, so you
should not set <varname>After=</varname> or <varname>Requires=</varname> should not set <varname>After=</varname> or <varname>Requires=</varname>
for mount dependencies here. For example, you should not set for mount dependencies here. For example, you should not set
@ -65,8 +61,11 @@
filesystems. Doing so may result in an ordering cycle.</para> filesystems. Doing so may result in an ordering cycle.</para>
<para>Note that automount support on Linux is privileged, automount units are hence only available in the <para>Note that automount support on Linux is privileged, automount units are hence only available in the
system service manager (and root's user service manager), but not in unprivileged user's service system service manager (and root's user service manager), but not in unprivileged users' service
manager.</para> managers.</para>
<para>Note that automount units should not be nested. (The establishment of the inner automount point
would unconditionally pin the outer mount point, defeating its purpose.)</para>
</refsect1> </refsect1>
<refsect1> <refsect1>
@ -78,12 +77,12 @@
<para>The following dependencies are implicitly added:</para> <para>The following dependencies are implicitly added:</para>
<itemizedlist> <itemizedlist>
<listitem><para>If an automount unit is beneath another mount unit in the <listitem><para>If an automount unit is beneath another mount unit in the file system hierarchy, a
file system hierarchy, both a requirement and an ordering requirement and ordering dependencies are created to the on the unit higher in the hierarchy.
dependency between both units are created automatically.</para></listitem> </para></listitem>
<listitem><para>An implicit <varname>Before=</varname> dependency is created <listitem><para>An implicit <varname>Before=</varname> dependency is created between an automount
between an automount unit and the mount unit it activates.</para></listitem> unit and the mount unit it activates.</para></listitem>
</itemizedlist> </itemizedlist>
</refsect2> </refsect2>
@ -161,6 +160,7 @@
creating these directories. Takes an access mode in octal creating these directories. Takes an access mode in octal
notation. Defaults to 0755.</para></listitem> notation. Defaults to 0755.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><varname>TimeoutIdleSec=</varname></term> <term><varname>TimeoutIdleSec=</varname></term>
<listitem><para>Configures an idle timeout. Once the mount has been <listitem><para>Configures an idle timeout. Once the mount has been

View File

@ -357,20 +357,29 @@ int cg_kill(
Set *s, Set *s,
cg_kill_log_func_t log_kill, cg_kill_log_func_t log_kill,
void *userdata) { void *userdata) {
int r;
int r, ret;
r = cg_kill_items(controller, path, sig, flags, s, log_kill, userdata, "cgroup.procs"); r = cg_kill_items(controller, path, sig, flags, s, log_kill, userdata, "cgroup.procs");
if (r < 0 || sig != SIGKILL) if (r < 0 || sig != SIGKILL)
return r; return r;
ret = r;
/* Only in case of killing with SIGKILL and when using cgroupsv2, kill remaining threads manually as /* Only in case of killing with SIGKILL and when using cgroupsv2, kill remaining threads manually as
a workaround for kernel bug. It was fixed in 5.2-rc5 (c03cd7738a83), backported to 4.19.66 a workaround for kernel bug. It was fixed in 5.2-rc5 (c03cd7738a83), backported to 4.19.66
(4340d175b898) and 4.14.138 (feb6b123b7dd). */ (4340d175b898) and 4.14.138 (feb6b123b7dd). */
r = cg_unified_controller(controller); r = cg_unified_controller(controller);
if (r <= 0) if (r < 0)
return r;
if (r == 0)
return ret;
r = cg_kill_items(controller, path, sig, flags, s, log_kill, userdata, "cgroup.threads");
if (r < 0)
return r; return r;
return cg_kill_items(controller, path, sig, flags, s, log_kill, userdata, "cgroup.threads"); return r > 0 || ret > 0;
} }
int cg_kill_kernel_sigkill(const char *controller, const char *path) { int cg_kill_kernel_sigkill(const char *controller, const char *path) {

View File

@ -543,7 +543,7 @@ int bpf_firewall_compile(Unit *u) {
return supported; return supported;
if (supported == BPF_FIREWALL_UNSUPPORTED) if (supported == BPF_FIREWALL_UNSUPPORTED)
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP), return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP),
"BPF firewalling not supported on this manager, proceeding without."); "bpf-firewall: BPF firewalling not supported, proceeding without.");
if (supported != BPF_FIREWALL_SUPPORTED_WITH_MULTI && u->type == UNIT_SLICE) if (supported != BPF_FIREWALL_SUPPORTED_WITH_MULTI && u->type == UNIT_SLICE)
/* If BPF_F_ALLOW_MULTI is not supported we don't support any BPF magic on inner nodes (i.e. on slice /* If BPF_F_ALLOW_MULTI is not supported we don't support any BPF magic on inner nodes (i.e. on slice
* units), since that would mean leaf nodes couldn't do any BPF anymore at all. Under the assumption * units), since that would mean leaf nodes couldn't do any BPF anymore at all. Under the assumption
@ -551,7 +551,7 @@ int bpf_firewall_compile(Unit *u) {
* consistent with old systemd behaviour from before v238, where BPF wasn't supported in inner nodes at * consistent with old systemd behaviour from before v238, where BPF wasn't supported in inner nodes at
* all, either. */ * all, either. */
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP), return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP),
"BPF_F_ALLOW_MULTI is not supported on this manager, not doing BPF firewall on slice units."); "bpf-firewall: BPF_F_ALLOW_MULTI is not supported, not doing BPF firewall on slice units.");
/* If BPF_F_ALLOW_MULTI flag is supported program name is also supported (both were added to v4.15 /* If BPF_F_ALLOW_MULTI flag is supported program name is also supported (both were added to v4.15
* kernel). */ * kernel). */
@ -582,24 +582,24 @@ int bpf_firewall_compile(Unit *u) {
r = bpf_firewall_prepare_access_maps(u, ACCESS_ALLOWED, &u->ipv4_allow_map_fd, &u->ipv6_allow_map_fd, &ip_allow_any); r = bpf_firewall_prepare_access_maps(u, ACCESS_ALLOWED, &u->ipv4_allow_map_fd, &u->ipv6_allow_map_fd, &ip_allow_any);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Preparation of eBPF allow maps failed: %m"); return log_unit_error_errno(u, r, "bpf-firewall: Preparation of BPF allow maps failed: %m");
r = bpf_firewall_prepare_access_maps(u, ACCESS_DENIED, &u->ipv4_deny_map_fd, &u->ipv6_deny_map_fd, &ip_deny_any); r = bpf_firewall_prepare_access_maps(u, ACCESS_DENIED, &u->ipv4_deny_map_fd, &u->ipv6_deny_map_fd, &ip_deny_any);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Preparation of eBPF deny maps failed: %m"); return log_unit_error_errno(u, r, "bpf-firewall: Preparation of BPF deny maps failed: %m");
} }
r = bpf_firewall_prepare_accounting_maps(u, cc->ip_accounting, &u->ip_accounting_ingress_map_fd, &u->ip_accounting_egress_map_fd); r = bpf_firewall_prepare_accounting_maps(u, cc->ip_accounting, &u->ip_accounting_ingress_map_fd, &u->ip_accounting_egress_map_fd);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Preparation of eBPF accounting maps failed: %m"); return log_unit_error_errno(u, r, "bpf-firewall: Preparation of BPF accounting maps failed: %m");
r = bpf_firewall_compile_bpf(u, ingress_name, true, &u->ip_bpf_ingress, ip_allow_any, ip_deny_any); r = bpf_firewall_compile_bpf(u, ingress_name, true, &u->ip_bpf_ingress, ip_allow_any, ip_deny_any);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Compilation for ingress BPF program failed: %m"); return log_unit_error_errno(u, r, "bpf-firewall: Compilation of ingress BPF program failed: %m");
r = bpf_firewall_compile_bpf(u, egress_name, false, &u->ip_bpf_egress, ip_allow_any, ip_deny_any); r = bpf_firewall_compile_bpf(u, egress_name, false, &u->ip_bpf_egress, ip_allow_any, ip_deny_any);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Compilation for egress BPF program failed: %m"); return log_unit_error_errno(u, r, "bpf-firewall: Compilation of egress BPF program failed: %m");
return 0; return 0;
} }
@ -613,15 +613,15 @@ static int load_bpf_progs_from_fs_to_set(Unit *u, char **filter_paths, Set **set
r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, NULL, &prog); r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, NULL, &prog);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Can't allocate CGROUP SKB BPF program: %m"); return log_unit_error_errno(u, r, "bpf-firewall: Allocation of SKB BPF program failed: %m");
r = bpf_program_load_from_bpf_fs(prog, *bpf_fs_path); r = bpf_program_load_from_bpf_fs(prog, *bpf_fs_path);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Loading of ingress BPF program %s failed: %m", *bpf_fs_path); return log_unit_error_errno(u, r, "bpf-firewall: Loading of ingress BPF program %s failed: %m", *bpf_fs_path);
r = set_ensure_consume(set, &bpf_program_hash_ops, TAKE_PTR(prog)); r = set_ensure_consume(set, &bpf_program_hash_ops, TAKE_PTR(prog));
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Can't add program to BPF program set: %m"); return log_oom();
} }
return 0; return 0;
@ -645,7 +645,8 @@ int bpf_firewall_load_custom(Unit *u) {
return supported; return supported;
if (supported != BPF_FIREWALL_SUPPORTED_WITH_MULTI) if (supported != BPF_FIREWALL_SUPPORTED_WITH_MULTI)
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP), "BPF_F_ALLOW_MULTI not supported on this manager, cannot attach custom BPF programs."); return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP),
"bpf-firewall: BPF_F_ALLOW_MULTI not supported, cannot attach custom BPF programs.");
r = load_bpf_progs_from_fs_to_set(u, cc->ip_filters_ingress, &u->ip_bpf_custom_ingress); r = load_bpf_progs_from_fs_to_set(u, cc->ip_filters_ingress, &u->ip_bpf_custom_ingress);
if (r < 0) if (r < 0)
@ -671,7 +672,7 @@ static int attach_custom_bpf_progs(Unit *u, const char *path, int attach_type, S
SET_FOREACH_MOVE(prog, *set_installed, *set) { SET_FOREACH_MOVE(prog, *set_installed, *set) {
r = bpf_program_cgroup_attach(prog, attach_type, path, BPF_F_ALLOW_MULTI); r = bpf_program_cgroup_attach(prog, attach_type, path, BPF_F_ALLOW_MULTI);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Attaching custom egress BPF program to cgroup %s failed: %m", path); return log_unit_error_errno(u, r, "bpf-firewall: Attaching custom egress BPF program to cgroup %s failed: %m", path);
} }
return 0; return 0;
} }
@ -697,16 +698,19 @@ int bpf_firewall_install(Unit *u) {
if (supported < 0) if (supported < 0)
return supported; return supported;
if (supported == BPF_FIREWALL_UNSUPPORTED) if (supported == BPF_FIREWALL_UNSUPPORTED)
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP), "BPF firewalling not supported on this manager, proceeding without."); return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP),
"bpf-firewall: BPF firewalling not supported, proceeding without.");
if (supported != BPF_FIREWALL_SUPPORTED_WITH_MULTI && u->type == UNIT_SLICE) if (supported != BPF_FIREWALL_SUPPORTED_WITH_MULTI && u->type == UNIT_SLICE)
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP), "BPF_F_ALLOW_MULTI is not supported on this manager, not doing BPF firewall on slice units."); return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP),
"bpf-firewall: BPF_F_ALLOW_MULTI not supported, not doing BPF firewall on slice units.");
if (supported != BPF_FIREWALL_SUPPORTED_WITH_MULTI && if (supported != BPF_FIREWALL_SUPPORTED_WITH_MULTI &&
(!set_isempty(u->ip_bpf_custom_ingress) || !set_isempty(u->ip_bpf_custom_egress))) (!set_isempty(u->ip_bpf_custom_ingress) || !set_isempty(u->ip_bpf_custom_egress)))
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP), "BPF_F_ALLOW_MULTI not supported on this manager, cannot attach custom BPF programs."); return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP),
"bpf-firewall: BPF_F_ALLOW_MULTI not supported, cannot attach custom BPF programs.");
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &path); r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &path);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Failed to determine cgroup path: %m"); return log_unit_error_errno(u, r, "bpf-firewall: Failed to determine cgroup path: %m");
flags = supported == BPF_FIREWALL_SUPPORTED_WITH_MULTI ? BPF_F_ALLOW_MULTI : 0; flags = supported == BPF_FIREWALL_SUPPORTED_WITH_MULTI ? BPF_F_ALLOW_MULTI : 0;
@ -728,7 +732,8 @@ int bpf_firewall_install(Unit *u) {
if (u->ip_bpf_egress) { if (u->ip_bpf_egress) {
r = bpf_program_cgroup_attach(u->ip_bpf_egress, BPF_CGROUP_INET_EGRESS, path, flags); r = bpf_program_cgroup_attach(u->ip_bpf_egress, BPF_CGROUP_INET_EGRESS, path, flags);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Attaching egress BPF program to cgroup %s failed: %m", path); return log_unit_error_errno(u, r,
"bpf-firewall: Attaching egress BPF program to cgroup %s failed: %m", path);
/* Remember that this BPF program is installed now. */ /* Remember that this BPF program is installed now. */
u->ip_bpf_egress_installed = TAKE_PTR(u->ip_bpf_egress); u->ip_bpf_egress_installed = TAKE_PTR(u->ip_bpf_egress);
@ -737,7 +742,8 @@ int bpf_firewall_install(Unit *u) {
if (u->ip_bpf_ingress) { if (u->ip_bpf_ingress) {
r = bpf_program_cgroup_attach(u->ip_bpf_ingress, BPF_CGROUP_INET_INGRESS, path, flags); r = bpf_program_cgroup_attach(u->ip_bpf_ingress, BPF_CGROUP_INET_INGRESS, path, flags);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Attaching ingress BPF program to cgroup %s failed: %m", path); return log_unit_error_errno(u, r,
"bpf-firewall: Attaching ingress BPF program to cgroup %s failed: %m", path);
u->ip_bpf_ingress_installed = TAKE_PTR(u->ip_bpf_ingress); u->ip_bpf_ingress_installed = TAKE_PTR(u->ip_bpf_ingress);
} }
@ -824,11 +830,11 @@ int bpf_firewall_supported(void) {
r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER); r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
if (r < 0) if (r < 0)
return log_error_errno(r, "Can't determine whether the unified hierarchy is used: %m"); return log_error_errno(r, "bpf-firewall: Can't determine whether the unified hierarchy is used: %m");
if (r == 0) { if (r == 0) {
bpf_firewall_unsupported_reason = bpf_firewall_unsupported_reason =
log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN), log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN),
"Not running with unified cgroups, BPF firewalling is not supported."); "bpf-firewall: Not running with unified cgroup hierarchy, BPF firewalling is not supported.");
return supported = BPF_FIREWALL_UNSUPPORTED; return supported = BPF_FIREWALL_UNSUPPORTED;
} }
@ -836,21 +842,21 @@ int bpf_firewall_supported(void) {
r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, NULL, &program); r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, NULL, &program);
if (r < 0) { if (r < 0) {
bpf_firewall_unsupported_reason = bpf_firewall_unsupported_reason =
log_debug_errno(r, "Can't allocate CGROUP SKB BPF program, BPF firewalling is not supported: %m"); log_debug_errno(r, "bpf-firewall: Can't allocate CGROUP SKB BPF program, BPF firewalling is not supported: %m");
return supported = BPF_FIREWALL_UNSUPPORTED; return supported = BPF_FIREWALL_UNSUPPORTED;
} }
r = bpf_program_add_instructions(program, trivial, ELEMENTSOF(trivial)); r = bpf_program_add_instructions(program, trivial, ELEMENTSOF(trivial));
if (r < 0) { if (r < 0) {
bpf_firewall_unsupported_reason = bpf_firewall_unsupported_reason =
log_debug_errno(r, "Can't add trivial instructions to CGROUP SKB BPF program, BPF firewalling is not supported: %m"); log_debug_errno(r, "bpf-firewall: Can't add trivial instructions to CGROUP SKB BPF program, BPF firewalling is not supported: %m");
return supported = BPF_FIREWALL_UNSUPPORTED; return supported = BPF_FIREWALL_UNSUPPORTED;
} }
r = bpf_program_load_kernel(program, NULL, 0); r = bpf_program_load_kernel(program, NULL, 0);
if (r < 0) { if (r < 0) {
bpf_firewall_unsupported_reason = bpf_firewall_unsupported_reason =
log_debug_errno(r, "Can't load kernel CGROUP SKB BPF program, BPF firewalling is not supported: %m"); log_debug_errno(r, "bpf-firewall: Can't load kernel CGROUP SKB BPF program, BPF firewalling is not supported: %m");
return supported = BPF_FIREWALL_UNSUPPORTED; return supported = BPF_FIREWALL_UNSUPPORTED;
} }
@ -874,7 +880,7 @@ int bpf_firewall_supported(void) {
if (bpf(BPF_PROG_DETACH, &attr, sizeof(attr)) < 0) { if (bpf(BPF_PROG_DETACH, &attr, sizeof(attr)) < 0) {
if (errno != EBADF) { if (errno != EBADF) {
bpf_firewall_unsupported_reason = bpf_firewall_unsupported_reason =
log_debug_errno(errno, "Didn't get EBADF from BPF_PROG_DETACH, BPF firewalling is not supported: %m"); log_debug_errno(errno, "bpf-firewall: Didn't get EBADF from BPF_PROG_DETACH, BPF firewalling is not supported: %m");
return supported = BPF_FIREWALL_UNSUPPORTED; return supported = BPF_FIREWALL_UNSUPPORTED;
} }
@ -882,7 +888,7 @@ int bpf_firewall_supported(void) {
} else { } else {
bpf_firewall_unsupported_reason = bpf_firewall_unsupported_reason =
log_debug_errno(SYNTHETIC_ERRNO(EBADE), log_debug_errno(SYNTHETIC_ERRNO(EBADE),
"Wut? Kernel accepted our invalid BPF_PROG_DETACH call? " "bpf-firewall: Wut? Kernel accepted our invalid BPF_PROG_DETACH call? "
"Something is weird, assuming BPF firewalling is broken and hence not supported."); "Something is weird, assuming BPF firewalling is broken and hence not supported.");
return supported = BPF_FIREWALL_UNSUPPORTED; return supported = BPF_FIREWALL_UNSUPPORTED;
} }
@ -902,20 +908,20 @@ int bpf_firewall_supported(void) {
if (bpf(BPF_PROG_ATTACH, &attr, sizeof(attr)) < 0) { if (bpf(BPF_PROG_ATTACH, &attr, sizeof(attr)) < 0) {
if (errno == EBADF) { if (errno == EBADF) {
log_debug_errno(errno, "Got EBADF when using BPF_F_ALLOW_MULTI, which indicates it is supported. Yay!"); log_debug_errno(errno, "bpf-firewall: Got EBADF when using BPF_F_ALLOW_MULTI, which indicates it is supported. Yay!");
return supported = BPF_FIREWALL_SUPPORTED_WITH_MULTI; return supported = BPF_FIREWALL_SUPPORTED_WITH_MULTI;
} }
if (errno == EINVAL) if (errno == EINVAL)
log_debug_errno(errno, "Got EINVAL error when using BPF_F_ALLOW_MULTI, which indicates it's not supported."); log_debug_errno(errno, "bpf-firewall: Got EINVAL error when using BPF_F_ALLOW_MULTI, which indicates it's not supported.");
else else
log_debug_errno(errno, "Got unexpected error when using BPF_F_ALLOW_MULTI, assuming it's not supported: %m"); log_debug_errno(errno, "bpf-firewall: Got unexpected error when using BPF_F_ALLOW_MULTI, assuming it's not supported: %m");
return supported = BPF_FIREWALL_SUPPORTED; return supported = BPF_FIREWALL_SUPPORTED;
} else { } else {
bpf_firewall_unsupported_reason = bpf_firewall_unsupported_reason =
log_debug_errno(SYNTHETIC_ERRNO(EBADE), log_debug_errno(SYNTHETIC_ERRNO(EBADE),
"Wut? Kernel accepted our invalid BPF_PROG_ATTACH+BPF_F_ALLOW_MULTI call? " "bpf-firewall: Wut? Kernel accepted our invalid BPF_PROG_ATTACH+BPF_F_ALLOW_MULTI call? "
"Something is weird, assuming BPF firewalling is broken and hence not supported."); "Something is weird, assuming BPF firewalling is broken and hence not supported.");
return supported = BPF_FIREWALL_UNSUPPORTED; return supported = BPF_FIREWALL_UNSUPPORTED;
} }

View File

@ -63,7 +63,7 @@ static int attach_programs(Unit *u, const char *path, Hashmap* foreign_by_key, u
HASHMAP_FOREACH_KEY(prog, key, foreign_by_key) { HASHMAP_FOREACH_KEY(prog, key, foreign_by_key) {
r = bpf_program_cgroup_attach(prog, key->attach_type, path, attach_flags); r = bpf_program_cgroup_attach(prog, key->attach_type, path, attach_flags);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Attaching foreign BPF program to cgroup %s failed: %m", path); return log_unit_error_errno(u, r, "bpf-foreign: Attaching foreign BPF program to cgroup %s failed: %m", path);
} }
return 0; return 0;
@ -89,31 +89,31 @@ static int bpf_foreign_prepare(
r = path_is_fs_type(bpffs_path, BPF_FS_MAGIC); r = path_is_fs_type(bpffs_path, BPF_FS_MAGIC);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, return log_unit_error_errno(u, r,
"Failed to determine filesystem type of %s: %m", bpffs_path); "bpf-foreign: Failed to determine filesystem type of %s: %m", bpffs_path);
if (r == 0) if (r == 0)
return log_unit_error_errno(u, SYNTHETIC_ERRNO(EINVAL), return log_unit_error_errno(u, SYNTHETIC_ERRNO(EINVAL),
"Path in BPF filesystem is expected."); "bpf-foreign: Path in BPF filesystem is expected.");
r = bpf_program_new_from_bpffs_path(bpffs_path, &prog); r = bpf_program_new_from_bpffs_path(bpffs_path, &prog);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Failed to create foreign BPFProgram: %m"); return log_unit_error_errno(u, r, "bpf-foreign: Failed to create foreign BPF program: %m");
r = bpf_program_get_id_by_fd(prog->kernel_fd, &prog_id); r = bpf_program_get_id_by_fd(prog->kernel_fd, &prog_id);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Failed to get BPF program id by fd: %m"); return log_unit_error_errno(u, r, "bpf-foreign: Failed to get BPF program id from fd: %m");
r = bpf_foreign_key_new(prog_id, attach_type, &key); r = bpf_foreign_key_new(prog_id, attach_type, &key);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, return log_unit_error_errno(u, r,
"Failed to create foreign BPF program key from path '%s': %m", bpffs_path); "bpf-foreign: Failed to create foreign BPF program key from path '%s': %m", bpffs_path);
r = hashmap_ensure_put(&u->bpf_foreign_by_key, &bpf_foreign_by_key_hash_ops, key, prog); r = hashmap_ensure_put(&u->bpf_foreign_by_key, &bpf_foreign_by_key_hash_ops, key, prog);
if (r == -EEXIST) { if (r == -EEXIST) {
log_unit_warning_errno(u, r, "Foreign BPF program already exists, ignoring: %m"); log_unit_warning_errno(u, r, "bpf-foreign: Foreign BPF program already exists, ignoring: %m");
return 0; return 0;
} }
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Failed to put foreign BPFProgram into map: %m"); return log_unit_error_errno(u, r, "bpf-foreign: Failed to put foreign BPF program into map: %m");
TAKE_PTR(key); TAKE_PTR(key);
TAKE_PTR(prog); TAKE_PTR(prog);
@ -134,17 +134,17 @@ int bpf_foreign_install(Unit *u) {
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &cgroup_path); r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &cgroup_path);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Failed to get cgroup path: %m"); return log_unit_error_errno(u, r, "bpf-foreign: Failed to get cgroup path: %m");
LIST_FOREACH(programs, p, cc->bpf_foreign_programs) { LIST_FOREACH(programs, p, cc->bpf_foreign_programs) {
r = bpf_foreign_prepare(u, p->attach_type, p->bpffs_path); r = bpf_foreign_prepare(u, p->attach_type, p->bpffs_path);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Failed to prepare foreign BPF hashmap: %m"); return log_unit_error_errno(u, r, "bpf-foreign: Failed to prepare foreign BPF hashmap: %m");
} }
r = attach_programs(u, cgroup_path, u->bpf_foreign_by_key, BPF_F_ALLOW_MULTI); r = attach_programs(u, cgroup_path, u->bpf_foreign_by_key, BPF_F_ALLOW_MULTI);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Failed to install foreign BPF programs: %m"); return log_unit_error_errno(u, r, "bpf-foreign: Failed to install foreign BPF programs: %m");
return 0; return 0;
} }

View File

@ -26,6 +26,7 @@
/* libbpf, clang and llc compile time dependencies are satisfied */ /* libbpf, clang and llc compile time dependencies are satisfied */
#include "bpf-dlopen.h" #include "bpf-dlopen.h"
#include "bpf-link.h" #include "bpf-link.h"
#include "bpf-util.h"
#include "bpf/restrict_fs/restrict-fs-skel.h" #include "bpf/restrict_fs/restrict-fs-skel.h"
#define CGROUP_HASH_SIZE_MAX 2048 #define CGROUP_HASH_SIZE_MAX 2048
@ -61,29 +62,29 @@ static int prepare_restrict_fs_bpf(struct restrict_fs_bpf **ret_obj) {
obj = restrict_fs_bpf__open(); obj = restrict_fs_bpf__open();
if (!obj) if (!obj)
return log_error_errno(errno, "Failed to open BPF object: %m"); return log_error_errno(errno, "bpf-lsm: Failed to open BPF object: %m");
/* TODO Maybe choose a number based on runtime information? */ /* TODO Maybe choose a number based on runtime information? */
r = sym_bpf_map__resize(obj->maps.cgroup_hash, CGROUP_HASH_SIZE_MAX); r = sym_bpf_map__resize(obj->maps.cgroup_hash, CGROUP_HASH_SIZE_MAX);
assert(r <= 0); assert(r <= 0);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to resize BPF map '%s': %m", return log_error_errno(r, "bpf-lsm: Failed to resize BPF map '%s': %m",
sym_bpf_map__name(obj->maps.cgroup_hash)); sym_bpf_map__name(obj->maps.cgroup_hash));
/* Dummy map to satisfy the verifier */ /* Dummy map to satisfy the verifier */
inner_map_fd = sym_bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(uint32_t), 128, 0); inner_map_fd = sym_bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(uint32_t), 128, 0);
if (inner_map_fd < 0) if (inner_map_fd < 0)
return log_error_errno(errno, "Failed to create BPF map: %m"); return log_error_errno(errno, "bpf-lsm: Failed to create BPF map: %m");
r = sym_bpf_map__set_inner_map_fd(obj->maps.cgroup_hash, inner_map_fd); r = sym_bpf_map__set_inner_map_fd(obj->maps.cgroup_hash, inner_map_fd);
assert(r <= 0); assert(r <= 0);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to set inner map fd: %m"); return log_error_errno(r, "bpf-lsm: Failed to set inner map fd: %m");
r = restrict_fs_bpf__load(obj); r = restrict_fs_bpf__load(obj);
assert(r <= 0); assert(r <= 0);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to load BPF object"); return log_error_errno(r, "bpf-lsm: Failed to load BPF object: %m");
*ret_obj = TAKE_PTR(obj); *ret_obj = TAKE_PTR(obj);
@ -103,7 +104,7 @@ static int mac_bpf_use(void) {
r = read_one_line_file("/sys/kernel/security/lsm", &lsm_list); r = read_one_line_file("/sys/kernel/security/lsm", &lsm_list);
if (r < 0) { if (r < 0) {
if (r != -ENOENT) if (r != -ENOENT)
log_notice_errno(r, "Failed to read /sys/kernel/security/lsm, assuming bpf is unavailable: %m"); log_notice_errno(r, "bpf-lsm: Failed to read /sys/kernel/security/lsm, assuming bpf is unavailable: %m");
return 0; return 0;
} }
@ -116,7 +117,7 @@ static int mac_bpf_use(void) {
if (r == -ENOMEM) if (r == -ENOMEM)
return log_oom(); return log_oom();
if (r < 0) { if (r < 0) {
log_notice_errno(r, "Failed to parse /sys/kernel/security/lsm, assuming bpf is unavailable: %m"); log_notice_errno(r, "bpf-lsm: Failed to parse /sys/kernel/security/lsm, assuming bpf is unavailable: %m");
return 0; return 0;
} }
@ -135,33 +136,18 @@ bool lsm_bpf_supported(bool initialize) {
if (!initialize) if (!initialize)
return false; return false;
r = dlopen_bpf(); if (!cgroup_bpf_supported())
if (r < 0) {
log_info_errno(r, "Failed to open libbpf, LSM BPF is not supported: %m");
return (supported = false); return (supported = false);
}
r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
if (r < 0) {
log_warning_errno(r, "Can't determine whether the unified hierarchy is used: %m");
return (supported = false);
}
if (r == 0) {
log_info_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Not running with unified cgroup hierarchy, LSM BPF is not supported");
return (supported = false);
}
r = mac_bpf_use(); r = mac_bpf_use();
if (r < 0) { if (r < 0) {
log_warning_errno(r, "Can't determine whether the BPF LSM module is used: %m"); log_warning_errno(r, "bpf-lsm: Can't determine whether the BPF LSM module is used: %m");
return (supported = false); return (supported = false);
} }
if (r == 0) { if (r == 0) {
log_info_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), log_info_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"BPF LSM hook not enabled in the kernel, LSM BPF not supported"); "bpf-lsm: BPF LSM hook not enabled in the kernel, BPF LSM not supported");
return (supported = false); return (supported = false);
} }
@ -171,7 +157,7 @@ bool lsm_bpf_supported(bool initialize) {
if (!bpf_can_link_lsm_program(obj->progs.restrict_filesystems)) { if (!bpf_can_link_lsm_program(obj->progs.restrict_filesystems)) {
log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Failed to link BPF program. Assuming BPF is not available"); "bpf-lsm: Failed to link program; assuming BPF LSM is not available");
return (supported = false); return (supported = false);
} }
@ -192,10 +178,10 @@ int lsm_bpf_setup(Manager *m) {
link = sym_bpf_program__attach_lsm(obj->progs.restrict_filesystems); link = sym_bpf_program__attach_lsm(obj->progs.restrict_filesystems);
r = sym_libbpf_get_error(link); r = sym_libbpf_get_error(link);
if (r != 0) if (r != 0)
return log_error_errno(r, "Failed to link '%s' LSM BPF program: %m", return log_error_errno(r, "bpf-lsm: Failed to link '%s' LSM BPF program: %m",
sym_bpf_program__name(obj->progs.restrict_filesystems)); sym_bpf_program__name(obj->progs.restrict_filesystems));
log_info("LSM BPF program attached"); log_info("bpf-lsm: LSM BPF program attached");
obj->links.restrict_filesystems = TAKE_PTR(link); obj->links.restrict_filesystems = TAKE_PTR(link);
m->restrict_fs = TAKE_PTR(obj); m->restrict_fs = TAKE_PTR(obj);
@ -214,7 +200,7 @@ int lsm_bpf_unit_restrict_filesystems(Unit *u, const Set *filesystems, bool allo
if (!u->manager->restrict_fs) if (!u->manager->restrict_fs)
return log_unit_error_errno(u, SYNTHETIC_ERRNO(EINVAL), return log_unit_error_errno(u, SYNTHETIC_ERRNO(EINVAL),
"Restrict filesystems BPF object is not set, BPF LSM setup has failed?"); "bpf-lsm: BPF LSM object is not installed, has setup failed?");
int inner_map_fd = sym_bpf_create_map( int inner_map_fd = sym_bpf_create_map(
BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_HASH,
@ -223,39 +209,39 @@ int lsm_bpf_unit_restrict_filesystems(Unit *u, const Set *filesystems, bool allo
128, /* Should be enough for all filesystem types */ 128, /* Should be enough for all filesystem types */
0); 0);
if (inner_map_fd < 0) if (inner_map_fd < 0)
return log_unit_error_errno(u, errno, "Failed to create inner LSM map: %m"); return log_unit_error_errno(u, errno, "bpf-lsm: Failed to create inner BPF map: %m");
int outer_map_fd = sym_bpf_map__fd(u->manager->restrict_fs->maps.cgroup_hash); int outer_map_fd = sym_bpf_map__fd(u->manager->restrict_fs->maps.cgroup_hash);
if (outer_map_fd < 0) if (outer_map_fd < 0)
return log_unit_error_errno(u, errno, "Failed to get BPF map fd: %m"); return log_unit_error_errno(u, errno, "bpf-lsm: Failed to get BPF map fd: %m");
if (sym_bpf_map_update_elem(outer_map_fd, &u->cgroup_id, &inner_map_fd, BPF_ANY) != 0) if (sym_bpf_map_update_elem(outer_map_fd, &u->cgroup_id, &inner_map_fd, BPF_ANY) != 0)
return log_unit_error_errno(u, errno, "Error populating LSM BPF map: %m"); return log_unit_error_errno(u, errno, "bpf-lsm: Error populating BPF map: %m");
uint32_t allow = allow_list; uint32_t allow = allow_list;
/* Use key 0 to store whether this is an allow list or a deny list */ /* Use key 0 to store whether this is an allow list or a deny list */
if (sym_bpf_map_update_elem(inner_map_fd, &zero, &allow, BPF_ANY) != 0) if (sym_bpf_map_update_elem(inner_map_fd, &zero, &allow, BPF_ANY) != 0)
return log_unit_error_errno(u, errno, "Error initializing BPF map: %m"); return log_unit_error_errno(u, errno, "bpf-lsm: Error initializing map: %m");
SET_FOREACH(fs, filesystems) { SET_FOREACH(fs, filesystems) {
r = fs_type_from_string(fs, &magic); r = fs_type_from_string(fs, &magic);
if (r < 0) { if (r < 0) {
log_unit_warning(u, "Invalid filesystem name '%s', ignoring.", fs); log_unit_warning(u, "bpf-lsm: Invalid filesystem name '%s', ignoring.", fs);
continue; continue;
} }
log_unit_debug(u, "Restricting filesystem access to '%s'", fs); log_unit_debug(u, "bpf-lsm: Restricting filesystem access to '%s'", fs);
for (int i = 0; i < FILESYSTEM_MAGIC_MAX; i++) { for (int i = 0; i < FILESYSTEM_MAGIC_MAX; i++) {
if (magic[i] == 0) if (magic[i] == 0)
break; break;
if (sym_bpf_map_update_elem(inner_map_fd, &magic[i], &dummy_value, BPF_ANY) != 0) { if (sym_bpf_map_update_elem(inner_map_fd, &magic[i], &dummy_value, BPF_ANY) != 0) {
r = log_unit_error_errno(u, errno, "Failed to update BPF map: %m"); r = log_unit_error_errno(u, errno, "bpf-lsm: Failed to update BPF map: %m");
if (sym_bpf_map_delete_elem(outer_map_fd, &u->cgroup_id) != 0) if (sym_bpf_map_delete_elem(outer_map_fd, &u->cgroup_id) != 0)
log_unit_debug_errno(u, errno, "Failed to delete cgroup entry from LSM BPF map: %m"); log_unit_debug_errno(u, errno, "bpf-lsm: Failed to delete cgroup entry from BPF map: %m");
return r; return r;
} }
@ -278,10 +264,10 @@ int lsm_bpf_cleanup(const Unit *u) {
int fd = sym_bpf_map__fd(u->manager->restrict_fs->maps.cgroup_hash); int fd = sym_bpf_map__fd(u->manager->restrict_fs->maps.cgroup_hash);
if (fd < 0) if (fd < 0)
return log_unit_error_errno(u, errno, "Failed to get BPF map fd: %m"); return log_unit_error_errno(u, errno, "bpf-lsm: Failed to get BPF map fd: %m");
if (sym_bpf_map_delete_elem(fd, &u->cgroup_id) != 0) if (sym_bpf_map_delete_elem(fd, &u->cgroup_id) != 0)
return log_unit_debug_errno(u, errno, "Failed to delete cgroup entry from LSM BPF map: %m"); return log_unit_debug_errno(u, errno, "bpf-lsm: Failed to delete cgroup entry from LSM BPF map: %m");
return 0; return 0;
} }
@ -305,11 +291,11 @@ bool lsm_bpf_supported(bool initialize) {
} }
int lsm_bpf_setup(Manager *m) { int lsm_bpf_setup(Manager *m) {
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set up LSM BPF: %m"); return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "bpf-lsm: Failed to set up LSM BPF: %m");
} }
int lsm_bpf_unit_restrict_filesystems(Unit *u, const Set *filesystems, const bool allow_list) { int lsm_bpf_unit_restrict_filesystems(Unit *u, const Set *filesystems, const bool allow_list) {
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to restrict filesystems using LSM BPF: %m"); return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP), "bpf-lsm: Failed to restrict filesystems using LSM BPF: %m");
} }
int lsm_bpf_cleanup(const Unit *u) { int lsm_bpf_cleanup(const Unit *u) {
@ -344,7 +330,7 @@ int lsm_bpf_parse_filesystem(
set = filesystem_set_find(name); set = filesystem_set_find(name);
if (!set) { if (!set) {
log_syntax(unit, flags & FILESYSTEM_PARSE_LOG ? LOG_WARNING : LOG_DEBUG, filename, line, 0, log_syntax(unit, flags & FILESYSTEM_PARSE_LOG ? LOG_WARNING : LOG_DEBUG, filename, line, 0,
"Unknown filesystem group, ignoring: %s", name); "bpf-lsm: Unknown filesystem group, ignoring: %s", name);
return 0; return 0;
} }

View File

@ -11,8 +11,9 @@
/* libbpf, clang, llvm and bpftool compile time dependencies are satisfied */ /* libbpf, clang, llvm and bpftool compile time dependencies are satisfied */
#include "bpf-dlopen.h" #include "bpf-dlopen.h"
#include "bpf-link.h" #include "bpf-link.h"
#include "bpf/socket_bind/socket-bind-skel.h" #include "bpf-util.h"
#include "bpf/socket_bind/socket-bind-api.bpf.h" #include "bpf/socket_bind/socket-bind-api.bpf.h"
#include "bpf/socket_bind/socket-bind-skel.h"
static struct socket_bind_bpf *socket_bind_bpf_free(struct socket_bind_bpf *obj) { static struct socket_bind_bpf *socket_bind_bpf_free(struct socket_bind_bpf *obj) {
/* socket_bind_bpf__destroy handles object == NULL case */ /* socket_bind_bpf__destroy handles object == NULL case */
@ -68,27 +69,27 @@ static int prepare_socket_bind_bpf(
if (allow_count > SOCKET_BIND_MAX_RULES) if (allow_count > SOCKET_BIND_MAX_RULES)
return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, SYNTHETIC_ERRNO(EINVAL), return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, SYNTHETIC_ERRNO(EINVAL),
"Maximum number of socket bind rules=%u is exceeded", SOCKET_BIND_MAX_RULES); "bpf-socket-bind: Maximum number of socket bind rules=%u is exceeded", SOCKET_BIND_MAX_RULES);
if (deny_count > SOCKET_BIND_MAX_RULES) if (deny_count > SOCKET_BIND_MAX_RULES)
return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, SYNTHETIC_ERRNO(EINVAL), return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, SYNTHETIC_ERRNO(EINVAL),
"Maximum number of socket bind rules=%u is exceeded", SOCKET_BIND_MAX_RULES); "bpf-socket-bind: Maximum number of socket bind rules=%u is exceeded", SOCKET_BIND_MAX_RULES);
obj = socket_bind_bpf__open(); obj = socket_bind_bpf__open();
if (!obj) if (!obj)
return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, errno, "Failed to open BPF object: %m"); return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, errno, "bpf-socket-bind: Failed to open BPF object: %m");
if (sym_bpf_map__resize(obj->maps.sd_bind_allow, MAX(allow_count, 1u)) != 0) if (sym_bpf_map__resize(obj->maps.sd_bind_allow, MAX(allow_count, 1u)) != 0)
return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, errno, return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, errno,
"Failed to resize BPF map '%s': %m", sym_bpf_map__name(obj->maps.sd_bind_allow)); "bpf-socket-bind: Failed to resize BPF map '%s': %m", sym_bpf_map__name(obj->maps.sd_bind_allow));
if (sym_bpf_map__resize(obj->maps.sd_bind_deny, MAX(deny_count, 1u)) != 0) if (sym_bpf_map__resize(obj->maps.sd_bind_deny, MAX(deny_count, 1u)) != 0)
return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, errno, return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, errno,
"Failed to resize BPF map '%s': %m", sym_bpf_map__name(obj->maps.sd_bind_deny)); "bpf-socket-bind: Failed to resize BPF map '%s': %m", sym_bpf_map__name(obj->maps.sd_bind_deny));
if (socket_bind_bpf__load(obj) != 0) if (socket_bind_bpf__load(obj) != 0)
return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, errno, return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, errno,
"Failed to load BPF object: %m"); "bpf-socket-bind: Failed to load BPF object: %m");
allow_map_fd = sym_bpf_map__fd(obj->maps.sd_bind_allow); allow_map_fd = sym_bpf_map__fd(obj->maps.sd_bind_allow);
assert(allow_map_fd >= 0); assert(allow_map_fd >= 0);
@ -96,7 +97,7 @@ static int prepare_socket_bind_bpf(
r = update_rules_map(allow_map_fd, allow); r = update_rules_map(allow_map_fd, allow);
if (r < 0) if (r < 0)
return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, r, return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, r,
"Failed to put socket bind allow rules into BPF map '%s'", "bpf-socket-bind: Failed to put socket bind allow rules into BPF map '%s'",
sym_bpf_map__name(obj->maps.sd_bind_allow)); sym_bpf_map__name(obj->maps.sd_bind_allow));
deny_map_fd = sym_bpf_map__fd(obj->maps.sd_bind_deny); deny_map_fd = sym_bpf_map__fd(obj->maps.sd_bind_deny);
@ -105,7 +106,7 @@ static int prepare_socket_bind_bpf(
r = update_rules_map(deny_map_fd, deny); r = update_rules_map(deny_map_fd, deny);
if (r < 0) if (r < 0)
return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, r, return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, r,
"Failed to put socket bind deny rules into BPF map '%s'", "bpf-socket-bind: Failed to put socket bind deny rules into BPF map '%s'",
sym_bpf_map__name(obj->maps.sd_bind_deny)); sym_bpf_map__name(obj->maps.sd_bind_deny));
*ret_obj = TAKE_PTR(obj); *ret_obj = TAKE_PTR(obj);
@ -116,25 +117,17 @@ int bpf_socket_bind_supported(void) {
_cleanup_(socket_bind_bpf_freep) struct socket_bind_bpf *obj = NULL; _cleanup_(socket_bind_bpf_freep) struct socket_bind_bpf *obj = NULL;
int r; int r;
r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER); if (!cgroup_bpf_supported())
if (r < 0)
return log_debug_errno(r, "Can't determine whether the unified hierarchy is used: %m");
if (r == 0) {
log_debug("Not running with unified cgroup hierarchy, BPF is not supported");
return false;
}
if (dlopen_bpf() < 0)
return false; return false;
if (!sym_bpf_probe_prog_type(BPF_PROG_TYPE_CGROUP_SOCK_ADDR, /*ifindex=*/0)) { if (!sym_bpf_probe_prog_type(BPF_PROG_TYPE_CGROUP_SOCK_ADDR, /*ifindex=*/0)) {
log_debug("BPF program type cgroup_sock_addr is not supported"); log_debug("bpf-socket-bind: BPF program type cgroup_sock_addr is not supported");
return false; return false;
} }
r = prepare_socket_bind_bpf(/*unit=*/NULL, /*allow_rules=*/NULL, /*deny_rules=*/NULL, &obj); r = prepare_socket_bind_bpf(/*unit=*/NULL, /*allow_rules=*/NULL, /*deny_rules=*/NULL, &obj);
if (r < 0) { if (r < 0) {
log_debug_errno(r, "BPF based socket_bind is not supported: %m"); log_debug_errno(r, "bpf-socket-bind: socket bind filtering is not supported: %m");
return false; return false;
} }
@ -154,7 +147,7 @@ int bpf_socket_bind_add_initial_link_fd(Unit *u, int fd) {
r = fdset_put(u->initial_socket_bind_link_fds, fd); r = fdset_put(u->initial_socket_bind_link_fds, fd);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Failed to put socket-bind BPF link fd %d to initial fdset", fd); return log_unit_error_errno(u, r, "bpf-socket-bind: Failed to put BPF fd %d to initial fdset", fd);
return 0; return 0;
} }
@ -175,29 +168,29 @@ static int socket_bind_install_impl(Unit *u) {
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &cgroup_path); r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &cgroup_path);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Failed to get cgroup path: %m"); return log_unit_error_errno(u, r, "bpf-socket-bind: Failed to get cgroup path: %m");
if (!cc->socket_bind_allow && !cc->socket_bind_deny) if (!cc->socket_bind_allow && !cc->socket_bind_deny)
return 0; return 0;
r = prepare_socket_bind_bpf(u, cc->socket_bind_allow, cc->socket_bind_deny, &obj); r = prepare_socket_bind_bpf(u, cc->socket_bind_allow, cc->socket_bind_deny, &obj);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Failed to load BPF object: %m"); return log_unit_error_errno(u, r, "bpf-socket-bind: Failed to load BPF object: %m");
cgroup_fd = open(cgroup_path, O_RDONLY | O_CLOEXEC, 0); cgroup_fd = open(cgroup_path, O_RDONLY | O_CLOEXEC, 0);
if (cgroup_fd < 0) if (cgroup_fd < 0)
return log_unit_error_errno(u, errno, "Failed to open cgroup=%s for reading: %m", cgroup_path); return log_unit_error_errno(u, errno, "bpf-socket-bind: Failed to open cgroup %s for reading: %m", cgroup_path);
ipv4 = sym_bpf_program__attach_cgroup(obj->progs.sd_bind4, cgroup_fd); ipv4 = sym_bpf_program__attach_cgroup(obj->progs.sd_bind4, cgroup_fd);
r = sym_libbpf_get_error(ipv4); r = sym_libbpf_get_error(ipv4);
if (r != 0) if (r != 0)
return log_unit_error_errno(u, r, "Failed to link '%s' cgroup-bpf program: %m", return log_unit_error_errno(u, r, "bpf-socket-bind: Failed to link '%s' cgroup-bpf program: %m",
sym_bpf_program__name(obj->progs.sd_bind4)); sym_bpf_program__name(obj->progs.sd_bind4));
ipv6 = sym_bpf_program__attach_cgroup(obj->progs.sd_bind6, cgroup_fd); ipv6 = sym_bpf_program__attach_cgroup(obj->progs.sd_bind6, cgroup_fd);
r = sym_libbpf_get_error(ipv6); r = sym_libbpf_get_error(ipv6);
if (r != 0) if (r != 0)
return log_unit_error_errno(u, r, "Failed to link '%s' cgroup-bpf program: %m", return log_unit_error_errno(u, r, "bpf-socket-bind: Failed to link '%s' cgroup-bpf program: %m",
sym_bpf_program__name(obj->progs.sd_bind6)); sym_bpf_program__name(obj->progs.sd_bind6));
u->ipv4_socket_bind_link = TAKE_PTR(ipv4); u->ipv4_socket_bind_link = TAKE_PTR(ipv4);
@ -241,7 +234,8 @@ int bpf_socket_bind_add_initial_link_fd(Unit *u, int fd) {
} }
int bpf_socket_bind_install(Unit *u) { int bpf_socket_bind_install(Unit *u) {
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to install socket bind: BPF framework is not supported"); return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP),
"bpf-socket-bind: Failed to install; BPF framework is not supported");
} }
int bpf_serialize_socket_bind(Unit *u, FILE *f, FDSet *fds) { int bpf_serialize_socket_bind(Unit *u, FILE *f, FDSet *fds) {

34
src/core/bpf-util.c Normal file
View File

@ -0,0 +1,34 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "bpf-dlopen.h"
#include "bpf-util.h"
#include "cgroup-util.h"
#include "log.h"
bool cgroup_bpf_supported(void) {
static int supported = -1;
int r;
if (supported >= 0)
return supported;
r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
if (r < 0) {
log_warning_errno(r, "Can't determine whether the unified hierarchy is used: %m");
return (supported = false);
}
if (r == 0) {
log_info_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Not running with unified cgroup hierarchy, disabling cgroup BPF features.");
return (supported = false);
}
r = dlopen_bpf();
if (r < 0) {
log_info_errno(r, "Failed to open libbpf, cgroup BPF features disabled: %m");
return (supported = false);
}
return (supported = true);
}

5
src/core/bpf-util.h Normal file
View File

@ -0,0 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <stdbool.h>
bool cgroup_bpf_supported(void);

View File

@ -163,14 +163,56 @@ static int device_coldplug(Unit *u) {
assert(d->state == DEVICE_DEAD); assert(d->state == DEVICE_DEAD);
/* First, let's put the deserialized state and found mask into effect, if we have it. */ /* First, let's put the deserialized state and found mask into effect, if we have it. */
if (d->deserialized_state < 0)
if (d->deserialized_state < 0 ||
(d->deserialized_state == d->state &&
d->deserialized_found == d->found))
return 0; return 0;
d->found = d->deserialized_found; Manager *m = u->manager;
device_set_state(d, d->deserialized_state); DeviceFound found = d->deserialized_found;
DeviceState state = d->deserialized_state;
/* On initial boot, switch-root, reload, reexecute, the following happen:
* 1. MANAGER_IS_RUNNING() == false
* 2. enumerate devices: manager_enumerate() -> device_enumerate()
* Device.enumerated_found is set.
* 3. deserialize devices: manager_deserialize() -> device_deserialize()
* Device.deserialize_state and Device.deserialized_found are set.
* 4. coldplug devices: manager_coldplug() -> device_coldplug()
* deserialized properties are copied to the main properties.
* 5. MANAGER_IS_RUNNING() == true: manager_ready()
* 6. catchup devices: manager_catchup() -> device_catchup()
* Device.enumerated_found is applied to Device.found, and state is updated based on that.
*
* Notes:
* - On initial boot, no udev database exists. Hence, no devices are enumerated in the step 2.
* Also, there is no deserialized device. Device units are (a) generated based on dependencies of
* other units, or (b) generated when uevents are received.
*
* - On switch-root, the udev database may be cleared, except for devices with sticky bit, i.e.
* OPTIONS="db_persist". Hence, almost no devices are enumerated in the step 2. However, in general,
* we have several serialized devices. So, DEVICE_FOUND_UDEV bit in the deserialized_found must be
* ignored, as udev rules in initramfs and the main system are often different. If the deserialized
* state is DEVICE_PLUGGED, we need to downgrade it to DEVICE_TENTATIVE (or DEVICE_DEAD if nobody
* sees the device). Unlike the other starting mode, Manager.honor_device_enumeration == false
* (maybe, it is better to rename the flag) when device_coldplug() and device_catchup() are called.
* Hence, let's conditionalize the operations by using the flag. After switch-root, systemd-udevd
* will (re-)process all devices, and the Device.found and Device.state will be adjusted.
*
* - On reload or reexecute, we can trust enumerated_found, deserialized_found, and deserialized_state.
* Of course, deserialized parameters may be outdated, but the unit state can be adjusted later by
* device_catchup() or uevents. */
if (!m->honor_device_enumeration && !MANAGER_IS_USER(m) &&
!FLAGS_SET(d->enumerated_found, DEVICE_FOUND_UDEV)) {
found &= ~DEVICE_FOUND_UDEV; /* ignore DEVICE_FOUND_UDEV bit */
if (state == DEVICE_PLUGGED)
state = DEVICE_TENTATIVE; /* downgrade state */
}
if (d->found == found && d->state == state)
return 0;
d->found = found;
device_set_state(d, state);
return 0; return 0;
} }
@ -644,13 +686,9 @@ static void device_found_changed(Device *d, DeviceFound previous, DeviceFound no
} }
static void device_update_found_one(Device *d, DeviceFound found, DeviceFound mask) { static void device_update_found_one(Device *d, DeviceFound found, DeviceFound mask) {
Manager *m;
assert(d); assert(d);
m = UNIT(d)->manager; if (MANAGER_IS_RUNNING(UNIT(d)->manager)) {
if (MANAGER_IS_RUNNING(m) && (m->honor_device_enumeration || MANAGER_IS_USER(m))) {
DeviceFound n, previous; DeviceFound n, previous;
/* When we are already running, then apply the new mask right-away, and trigger state changes /* When we are already running, then apply the new mask right-away, and trigger state changes

View File

@ -135,6 +135,13 @@ libcore_sources = '''
unit.h unit.h
'''.split() '''.split()
if conf.get('BPF_FRAMEWORK') == 1
libcore_sources += files(
'bpf-util.c',
'bpf-util.h',
)
endif
subdir('bpf') subdir('bpf')
subdir('bpf/socket_bind') subdir('bpf/socket_bind')

View File

@ -9,7 +9,7 @@
#include "bpf-dlopen.h" #include "bpf-dlopen.h"
#include "bpf-link.h" #include "bpf-link.h"
#include "bpf-util.h"
#include "bpf/restrict_ifaces/restrict-ifaces-skel.h" #include "bpf/restrict_ifaces/restrict-ifaces-skel.h"
static struct restrict_ifaces_bpf *restrict_ifaces_bpf_free(struct restrict_ifaces_bpf *obj) { static struct restrict_ifaces_bpf *restrict_ifaces_bpf_free(struct restrict_ifaces_bpf *obj) {
@ -34,19 +34,19 @@ static int prepare_restrict_ifaces_bpf(
obj = restrict_ifaces_bpf__open(); obj = restrict_ifaces_bpf__open();
if (!obj) if (!obj)
return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, errno, "Failed to open BPF object: %m"); return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, errno, "restrict-interfaces: Failed to open BPF object: %m");
r = sym_bpf_map__resize(obj->maps.sd_restrictif, MAX(set_size(restrict_network_interfaces), 1u)); r = sym_bpf_map__resize(obj->maps.sd_restrictif, MAX(set_size(restrict_network_interfaces), 1u));
if (r != 0) if (r != 0)
return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, r, return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, r,
"Failed to resize BPF map '%s': %m", "restrict-interfaces: Failed to resize BPF map '%s': %m",
sym_bpf_map__name(obj->maps.sd_restrictif)); sym_bpf_map__name(obj->maps.sd_restrictif));
obj->rodata->is_allow_list = is_allow_list; obj->rodata->is_allow_list = is_allow_list;
r = restrict_ifaces_bpf__load(obj); r = restrict_ifaces_bpf__load(obj);
if (r != 0) if (r != 0)
return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, r, "Failed to load BPF object: %m"); return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, r, "restrict-interfaces: Failed to load BPF object: %m");
map_fd = sym_bpf_map__fd(obj->maps.sd_restrictif); map_fd = sym_bpf_map__fd(obj->maps.sd_restrictif);
@ -56,13 +56,15 @@ static int prepare_restrict_ifaces_bpf(
ifindex = rtnl_resolve_interface(&rtnl, iface); ifindex = rtnl_resolve_interface(&rtnl, iface);
if (ifindex < 0) { if (ifindex < 0) {
log_unit_warning_errno(u, ifindex, "Couldn't find index of network interface '%s', ignoring: %m", iface); log_unit_warning_errno(u, ifindex,
"restrict-interfaces: Couldn't find index of network interface '%s', ignoring: %m",
iface);
continue; continue;
} }
if (sym_bpf_map_update_elem(map_fd, &ifindex, &dummy, BPF_ANY)) if (sym_bpf_map_update_elem(map_fd, &ifindex, &dummy, BPF_ANY))
return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, errno, return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, errno,
"Failed to update BPF map '%s' fd: %m", "restrict-interfaces: Failed to update BPF map '%s' fd: %m",
sym_bpf_map__name(obj->maps.sd_restrictif)); sym_bpf_map__name(obj->maps.sd_restrictif));
} }
@ -78,29 +80,21 @@ int restrict_network_interfaces_supported(void) {
if (supported >= 0) if (supported >= 0)
return supported; return supported;
r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER); if (!cgroup_bpf_supported())
if (r < 0) return (supported = false);
return log_error_errno(r, "Can't determine whether the unified hierarchy is used: %m");
if (r == 0) {
log_debug("Not running with unified cgroup hierarchy, BPF is not supported");
return supported = 0;
}
if (dlopen_bpf() < 0)
return false;
if (!sym_bpf_probe_prog_type(BPF_PROG_TYPE_CGROUP_SKB, /*ifindex=*/0)) { if (!sym_bpf_probe_prog_type(BPF_PROG_TYPE_CGROUP_SKB, /*ifindex=*/0)) {
log_debug("BPF program type cgroup_skb is not supported"); log_debug("restrict-interfaces: BPF program type cgroup_skb is not supported");
return supported = 0; return (supported = false);
} }
r = prepare_restrict_ifaces_bpf(NULL, true, NULL, &obj); r = prepare_restrict_ifaces_bpf(NULL, true, NULL, &obj);
if (r < 0) { if (r < 0) {
log_debug_errno(r, "Failed to load BPF object: %m"); log_debug_errno(r, "restrict-interfaces: Failed to load BPF object: %m");
return supported = 0; return (supported = false);
} }
return supported = bpf_can_link_program(obj->progs.sd_restrictif_i); return (supported = bpf_can_link_program(obj->progs.sd_restrictif_i));
} }
static int restrict_network_interfaces_install_impl(Unit *u) { static int restrict_network_interfaces_install_impl(Unit *u) {
@ -117,7 +111,7 @@ static int restrict_network_interfaces_install_impl(Unit *u) {
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &cgroup_path); r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &cgroup_path);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Failed to get cgroup path: %m"); return log_unit_error_errno(u, r, "restrict-interfaces: Failed to get cgroup path: %m");
if (!cc->restrict_network_interfaces) if (!cc->restrict_network_interfaces)
return 0; return 0;
@ -136,12 +130,12 @@ static int restrict_network_interfaces_install_impl(Unit *u) {
ingress_link = sym_bpf_program__attach_cgroup(obj->progs.sd_restrictif_i, cgroup_fd); ingress_link = sym_bpf_program__attach_cgroup(obj->progs.sd_restrictif_i, cgroup_fd);
r = sym_libbpf_get_error(ingress_link); r = sym_libbpf_get_error(ingress_link);
if (r != 0) if (r != 0)
return log_unit_error_errno(u, r, "Failed to create ingress cgroup link: %m"); return log_unit_error_errno(u, r, "restrict-interfaces: Failed to create ingress cgroup link: %m");
egress_link = sym_bpf_program__attach_cgroup(obj->progs.sd_restrictif_e, cgroup_fd); egress_link = sym_bpf_program__attach_cgroup(obj->progs.sd_restrictif_e, cgroup_fd);
r = sym_libbpf_get_error(egress_link); r = sym_libbpf_get_error(egress_link);
if (r != 0) if (r != 0)
return log_unit_error_errno(u, r, "Failed to create egress cgroup link: %m"); return log_unit_error_errno(u, r, "restrict-interfaces: Failed to create egress cgroup link: %m");
u->restrict_ifaces_ingress_bpf_link = TAKE_PTR(ingress_link); u->restrict_ifaces_ingress_bpf_link = TAKE_PTR(ingress_link);
u->restrict_ifaces_egress_bpf_link = TAKE_PTR(egress_link); u->restrict_ifaces_egress_bpf_link = TAKE_PTR(egress_link);
@ -180,7 +174,8 @@ int restrict_network_interfaces_add_initial_link_fd(Unit *u, int fd) {
r = fdset_put(u->initial_restric_ifaces_link_fds, fd); r = fdset_put(u->initial_restric_ifaces_link_fds, fd);
if (r < 0) if (r < 0)
return log_unit_error_errno(u, r, "Failed to put restrict-ifaces-bpf-fd %d to restored fdset: %m", fd); return log_unit_error_errno(u, r,
"restrict-interfaces: Failed to put restrict-ifaces-bpf-fd %d to restored fdset: %m", fd);
return 0; return 0;
} }
@ -192,7 +187,7 @@ int restrict_network_interfaces_supported(void) {
int restrict_network_interfaces_install(Unit *u) { int restrict_network_interfaces_install(Unit *u) {
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP), return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP),
"Failed to install RestrictInterfaces: BPF programs built from source code are not supported: %m"); "restrict-interfaces: Failed to install; BPF programs built from source code are not supported: %m");
} }
int serialize_restrict_network_interfaces(Unit *u, FILE *f, FDSet *fds) { int serialize_restrict_network_interfaces(Unit *u, FILE *f, FDSet *fds) {

View File

@ -47,6 +47,20 @@
# define SWAP64(n) (n) # define SWAP64(n) (n)
#endif #endif
/* The condition below is from glibc's string/string-inline.c.
* See definition of _STRING_INLINE_unaligned. */
#if !defined(__mc68020__) && !defined(__s390__) && !defined(__i386__)
/* To check alignment gcc has an appropriate operator. Other compilers don't. */
# if __GNUC__ >= 2
# define UNALIGNED_P(p) (((size_t) p) % __alignof__(uint32_t) != 0)
# else
# define UNALIGNED_P(p) (((size_t) p) % sizeof(uint32_t) != 0)
# endif
#else
# define UNALIGNED_P(p) false
#endif
/* This array contains the bytes used to pad the buffer to the next /* This array contains the bytes used to pad the buffer to the next
64-byte boundary. (FIPS 180-2:5.1.1) */ 64-byte boundary. (FIPS 180-2:5.1.1) */
static const uint8_t fillbuf[64] = { static const uint8_t fillbuf[64] = {
@ -94,10 +108,7 @@ void sha256_init_ctx(struct sha256_ctx *ctx) {
} }
/* Process the remaining bytes in the internal buffer and the usual /* Process the remaining bytes in the internal buffer and the usual
prolog according to the standard and write the result to RESBUF. prolog according to the standard and write the result to RESBUF. */
IMPORTANT: On some systems it is required that RESBUF is correctly
aligned for a 32 bits value. */
void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf) { void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf) {
/* Take yet unprocessed bytes into account. */ /* Take yet unprocessed bytes into account. */
uint32_t bytes = ctx->buflen; uint32_t bytes = ctx->buflen;
@ -122,7 +133,10 @@ void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf) {
/* Put result from CTX in first 32 bytes following RESBUF. */ /* Put result from CTX in first 32 bytes following RESBUF. */
for (size_t i = 0; i < 8; ++i) for (size_t i = 0; i < 8; ++i)
((uint32_t *) resbuf)[i] = SWAP(ctx->H[i]); if (UNALIGNED_P(resbuf))
memcpy((uint8_t*) resbuf + i * sizeof(uint32_t), (uint32_t[]) { SWAP(ctx->H[i]) }, sizeof(uint32_t));
else
((uint32_t *) resbuf)[i] = SWAP(ctx->H[i]);
return resbuf; return resbuf;
} }
@ -156,17 +170,6 @@ void sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx
/* Process available complete blocks. */ /* Process available complete blocks. */
if (len >= 64) { if (len >= 64) {
/* The condition below is from glibc's string/string-inline.c.
* See definition of _STRING_INLINE_unaligned. */
#if !defined(__mc68020__) && !defined(__s390__) && !defined(__i386__)
/* To check alignment gcc has an appropriate operator. Other compilers don't. */
# if __GNUC__ >= 2
# define UNALIGNED_P(p) (((size_t) p) % __alignof__(uint32_t) != 0)
# else
# define UNALIGNED_P(p) (((size_t) p) % sizeof(uint32_t) != 0)
# endif
if (UNALIGNED_P(buffer)) if (UNALIGNED_P(buffer))
while (len > 64) { while (len > 64) {
memcpy(ctx->buffer, buffer, 64); memcpy(ctx->buffer, buffer, 64);
@ -174,9 +177,7 @@ void sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx
buffer = (const char *) buffer + 64; buffer = (const char *) buffer + 64;
len -= 64; len -= 64;
} }
else else {
#endif
{
sha256_process_block(buffer, len & ~63, ctx); sha256_process_block(buffer, len & ~63, ctx);
buffer = (const char *) buffer + (len & ~63); buffer = (const char *) buffer + (len & ~63);
len &= 63; len &= 63;

View File

@ -80,12 +80,13 @@ fi
if [ "${0##*/}" = "installkernel" ]; then if [ "${0##*/}" = "installkernel" ]; then
COMMAND=add COMMAND=add
# make install doesn't pass any initrds # kernel's install.sh invokes us as
no_initrds=1 # /sbin/installkernel <version> <vmlinuz> <map> <installation-dir>
# We ignore the last two arguments.
set -- "$1"
else else
COMMAND="$1" COMMAND="$1"
[ $# -ge 1 ] && shift [ $# -ge 1 ] && shift
no_initrds=0
fi fi
if [ "$COMMAND" = "inspect" ]; then if [ "$COMMAND" = "inspect" ]; then
@ -320,16 +321,8 @@ case "$COMMAND" in
fi fi
for f in $PLUGINS; do for f in $PLUGINS; do
if [ "$no_initrds" = 1 ]; then [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "+$f add $KERNEL_VERSION $ENTRY_DIR_ABS" "$@"
# kernel's install.sh invokes us as "$f" add "$KERNEL_VERSION" "$ENTRY_DIR_ABS" "$@"
# /sbin/installkernel <version> <vmlinuz> <map> <installation-dir>
# We ignore the last two arguments.
[ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "+$f add $KERNEL_VERSION $ENTRY_DIR_ABS $1"
"$f" add "$KERNEL_VERSION" "$ENTRY_DIR_ABS" "$1"
else
[ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "+$f add $KERNEL_VERSION $ENTRY_DIR_ABS $*"
"$f" add "$KERNEL_VERSION" "$ENTRY_DIR_ABS" "$@"
fi
err=$? err=$?
[ $err -eq $skip_remaining ] && break [ $err -eq $skip_remaining ] && break
@ -362,8 +355,8 @@ case "$COMMAND" in
# Assert that ENTRY_DIR_ABS actually matches what we are printing here # Assert that ENTRY_DIR_ABS actually matches what we are printing here
[ "${ENTRY_DIR_ABS%/*}" = "$KERNEL_INSTALL_BOOT_ROOT/$ENTRY_TOKEN" ] || { echo "Assertion didn't pass." >&2; exit 1; } [ "${ENTRY_DIR_ABS%/*}" = "$KERNEL_INSTALL_BOOT_ROOT/$ENTRY_TOKEN" ] || { echo "Assertion didn't pass." >&2; exit 1; }
;; ;;
*) *)
echo "Error: unknown command '$COMMAND'" >&2 echo "Error: unknown command '$COMMAND'" >&2
exit 1 exit 1

View File

@ -222,7 +222,8 @@ tests += [
libglib, libglib,
libgobject, libgobject,
libgio, libgio,
libdbus]], libdbus,
libm]],
[files('sd-bus/test-bus-signature.c'), [files('sd-bus/test-bus-signature.c'),
[], [],

View File

@ -428,7 +428,7 @@ int bus_message_from_header(
_cleanup_free_ sd_bus_message *m = NULL; _cleanup_free_ sd_bus_message *m = NULL;
struct bus_header *h; struct bus_header *h;
size_t a, label_sz; size_t a, label_sz = 0; /* avoid false maybe-uninitialized warning */
assert(bus); assert(bus);
assert(header || header_accessible <= 0); assert(header || header_accessible <= 0);
@ -506,7 +506,10 @@ int bus_message_from_header(
m->fields_size = BUS_MESSAGE_BSWAP32(m, h->dbus1.fields_size); m->fields_size = BUS_MESSAGE_BSWAP32(m, h->dbus1.fields_size);
m->body_size = BUS_MESSAGE_BSWAP32(m, h->dbus1.body_size); m->body_size = BUS_MESSAGE_BSWAP32(m, h->dbus1.body_size);
if (sizeof(struct bus_header) + ALIGN8(m->fields_size) + m->body_size != message_size) assert(message_size >= sizeof(struct bus_header));
if (m->fields_size > message_size - sizeof(struct bus_header) ||
ALIGN8(m->fields_size) > message_size - sizeof(struct bus_header) ||
m->body_size != message_size - sizeof(struct bus_header) - ALIGN8(m->fields_size))
return -EBADMSG; return -EBADMSG;
} }
@ -3061,15 +3064,21 @@ void bus_body_part_unmap(struct bus_body_part *part) {
return; return;
} }
static int buffer_peek(const void *p, uint32_t sz, size_t *rindex, size_t align, size_t nbytes, void **r) { static int buffer_peek(const void *p, size_t sz, size_t *rindex, size_t align, size_t nbytes, void **r) {
size_t k, start, end; size_t k, start, end;
assert(rindex); assert(rindex);
assert(align > 0); assert(align > 0);
start = ALIGN_TO((size_t) *rindex, align); start = ALIGN_TO(*rindex, align);
end = start + nbytes; if (start > sz)
return -EBADMSG;
/* Avoid overflow below */
if (nbytes > SIZE_MAX - start)
return -EBADMSG;
end = start + nbytes;
if (end > sz) if (end > sz)
return -EBADMSG; return -EBADMSG;
@ -3272,10 +3281,17 @@ static int message_peek_body(
assert(rindex); assert(rindex);
assert(align > 0); assert(align > 0);
start = ALIGN_TO((size_t) *rindex, align); start = ALIGN_TO(*rindex, align);
padding = start - *rindex; if (start > m->user_body_size)
end = start + nbytes; return -EBADMSG;
padding = start - *rindex;
/* Avoid overflow below */
if (nbytes > SIZE_MAX - start)
return -EBADMSG;
end = start + nbytes;
if (end > m->user_body_size) if (end > m->user_body_size)
return -EBADMSG; return -EBADMSG;

View File

@ -2,6 +2,7 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include <sys/stat.h>
#include "def.h" #include "def.h"
#include "hashmap.h" #include "hashmap.h"

View File

@ -2342,8 +2342,6 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd
if (r == 0) if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
reset_scheduled_shutdown(m);
if (m->enable_wall_messages) { if (m->enable_wall_messages) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
_cleanup_free_ char *username = NULL; _cleanup_free_ char *username = NULL;
@ -2361,6 +2359,8 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd
username, tty, logind_wall_tty_filter, m); username, tty, logind_wall_tty_filter, m);
} }
reset_scheduled_shutdown(m);
return sd_bus_reply_method_return(message, "b", true); return sd_bus_reply_method_return(message, "b", true);
} }

View File

@ -42,20 +42,28 @@ _const_ static usec_t when_wall(usec_t n, usec_t elapse) {
return left % USEC_PER_HOUR; return left % USEC_PER_HOUR;
} }
bool logind_wall_tty_filter(const char *tty, void *userdata) { bool logind_wall_tty_filter(const char *tty, bool is_local, void *userdata) {
Manager *m = userdata; Manager *m = ASSERT_PTR(userdata);
const char *p;
assert(m); assert(m->scheduled_shutdown_action);
if (!m->scheduled_shutdown_tty) const char *p = path_startswith(tty, "/dev/");
return true;
p = path_startswith(tty, "/dev/");
if (!p) if (!p)
return true; return true;
return !streq(p, m->scheduled_shutdown_tty); /* Do not send information about events which do not destroy local sessions to local terminals. We
* can assume that if the system enters sleep or hibernation, this will be visible in an obvious way
* for any local user. And once the systems exits sleep or hibernation, the notication would be just
* noise, in particular for auto-suspend. */
if (is_local &&
IN_SET(m->scheduled_shutdown_action->handle,
HANDLE_SUSPEND,
HANDLE_HIBERNATE,
HANDLE_HYBRID_SLEEP,
HANDLE_SUSPEND_THEN_HIBERNATE))
return false;
return !streq_ptr(p, m->scheduled_shutdown_tty);
} }
static int warn_wall(Manager *m, usec_t n) { static int warn_wall(Manager *m, usec_t n) {

View File

@ -180,6 +180,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_n_autovts);
CONFIG_PARSER_PROTOTYPE(config_parse_tmpfs_size); CONFIG_PARSER_PROTOTYPE(config_parse_tmpfs_size);
int manager_setup_wall_message_timer(Manager *m); int manager_setup_wall_message_timer(Manager *m);
bool logind_wall_tty_filter(const char *tty, void *userdata); bool logind_wall_tty_filter(const char *tty, bool is_local, void *userdata);
int manager_read_efi_boot_loader_entries(Manager *m); int manager_read_efi_boot_loader_entries(Manager *m);

View File

@ -528,18 +528,26 @@ static int dns_stub_send(
if (s) if (s)
r = dns_stream_write_packet(s, reply); r = dns_stream_write_packet(s, reply);
else { else {
int fd; int fd, ifindex;
fd = find_socket_fd(m, l, p->family, &p->sender, SOCK_DGRAM); fd = find_socket_fd(m, l, p->family, &p->sender, SOCK_DGRAM);
if (fd < 0) if (fd < 0)
return fd; return fd;
if (address_is_proxy(p->family, &p->destination))
/* Force loopback iface if this is the loopback proxy stub
* and ifindex was normalized to 0 by manager_recv(). */
ifindex = p->ifindex ?: LOOPBACK_IFINDEX;
else
/* Force loopback iface if this is the main listener stub. */
ifindex = l ? p->ifindex : LOOPBACK_IFINDEX;
/* Note that it is essential here that we explicitly choose the source IP address for this /* Note that it is essential here that we explicitly choose the source IP address for this
* packet. This is because otherwise the kernel will choose it automatically based on the * packet. This is because otherwise the kernel will choose it automatically based on the
* routing table and will thus pick 127.0.0.1 rather than 127.0.0.53. */ * routing table and will thus pick 127.0.0.1 rather than 127.0.0.53/54. */
r = manager_send(m, r = manager_send(m,
fd, fd,
l || address_is_proxy(p->family, &p->destination) ? p->ifindex : LOOPBACK_IFINDEX, /* force loopback iface if this is the main listener stub */ ifindex,
p->family, &p->sender, p->sender_port, &p->destination, p->family, &p->sender, p->sender_port, &p->destination,
reply); reply);
} }

View File

@ -9,7 +9,6 @@ static void *bpf_dl = NULL;
struct bpf_link* (*sym_bpf_program__attach_cgroup)(struct bpf_program *, int); struct bpf_link* (*sym_bpf_program__attach_cgroup)(struct bpf_program *, int);
struct bpf_link* (*sym_bpf_program__attach_lsm)(struct bpf_program *); struct bpf_link* (*sym_bpf_program__attach_lsm)(struct bpf_program *);
long (*sym_libbpf_get_error)(const void *);
int (*sym_bpf_link__fd)(const struct bpf_link *); int (*sym_bpf_link__fd)(const struct bpf_link *);
int (*sym_bpf_link__destroy)(struct bpf_link *); int (*sym_bpf_link__destroy)(struct bpf_link *);
int (*sym_bpf_map__fd)(const struct bpf_map *); int (*sym_bpf_map__fd)(const struct bpf_map *);
@ -26,9 +25,27 @@ void (*sym_bpf_object__detach_skeleton)(struct bpf_object_skeleton *);
void (*sym_bpf_object__destroy_skeleton)(struct bpf_object_skeleton *); void (*sym_bpf_object__destroy_skeleton)(struct bpf_object_skeleton *);
bool (*sym_bpf_probe_prog_type)(enum bpf_prog_type, __u32); bool (*sym_bpf_probe_prog_type)(enum bpf_prog_type, __u32);
const char* (*sym_bpf_program__name)(const struct bpf_program *); const char* (*sym_bpf_program__name)(const struct bpf_program *);
libbpf_print_fn_t (*sym_libbpf_set_print)(libbpf_print_fn_t);
long (*sym_libbpf_get_error)(const void *);
_printf_(2,0)
static int bpf_print_func(enum libbpf_print_level level, const char *fmt, va_list ap) {
#if !LOG_TRACE
/* libbpf logs a lot of details at its debug level, which we don't need to see. */
if (level == LIBBPF_DEBUG)
return 0;
#endif
/* All other levels are downgraded to LOG_DEBUG */
/* errno is used here, on the assumption that if the log message uses %m, errno will be set to
* something useful. Otherwise, it shouldn't matter, we may pass 0 or some bogus value. */
return log_internalv(LOG_DEBUG, errno, NULL, 0, NULL, fmt, ap);
}
int dlopen_bpf(void) { int dlopen_bpf(void) {
return dlopen_many_sym_or_warn( int r;
r = dlopen_many_sym_or_warn(
&bpf_dl, "libbpf.so.0", LOG_DEBUG, &bpf_dl, "libbpf.so.0", LOG_DEBUG,
DLSYM_ARG(bpf_link__destroy), DLSYM_ARG(bpf_link__destroy),
DLSYM_ARG(bpf_link__fd), DLSYM_ARG(bpf_link__fd),
@ -48,7 +65,14 @@ int dlopen_bpf(void) {
DLSYM_ARG(bpf_program__attach_cgroup), DLSYM_ARG(bpf_program__attach_cgroup),
DLSYM_ARG(bpf_program__attach_lsm), DLSYM_ARG(bpf_program__attach_lsm),
DLSYM_ARG(bpf_program__name), DLSYM_ARG(bpf_program__name),
DLSYM_ARG(libbpf_set_print),
DLSYM_ARG(libbpf_get_error)); DLSYM_ARG(libbpf_get_error));
if (r < 0)
return r;
/* We set the print helper unconditionally. Otherwise libbpf will emit not useful log messages. */
(void) sym_libbpf_set_print(bpf_print_func);
return r;
} }
#else #else

View File

@ -8,7 +8,6 @@
extern struct bpf_link* (*sym_bpf_program__attach_cgroup)(struct bpf_program *, int); extern struct bpf_link* (*sym_bpf_program__attach_cgroup)(struct bpf_program *, int);
extern struct bpf_link* (*sym_bpf_program__attach_lsm)(struct bpf_program *); extern struct bpf_link* (*sym_bpf_program__attach_lsm)(struct bpf_program *);
extern long (*sym_libbpf_get_error)(const void *);
extern int (*sym_bpf_link__fd)(const struct bpf_link *); extern int (*sym_bpf_link__fd)(const struct bpf_link *);
extern int (*sym_bpf_link__destroy)(struct bpf_link *); extern int (*sym_bpf_link__destroy)(struct bpf_link *);
extern int (*sym_bpf_map__fd)(const struct bpf_map *); extern int (*sym_bpf_map__fd)(const struct bpf_map *);
@ -27,6 +26,8 @@ extern void (*sym_bpf_object__detach_skeleton)(struct bpf_object_skeleton *);
extern void (*sym_bpf_object__destroy_skeleton)(struct bpf_object_skeleton *); extern void (*sym_bpf_object__destroy_skeleton)(struct bpf_object_skeleton *);
extern bool (*sym_bpf_probe_prog_type)(enum bpf_prog_type, __u32); extern bool (*sym_bpf_probe_prog_type)(enum bpf_prog_type, __u32);
extern const char* (*sym_bpf_program__name)(const struct bpf_program *); extern const char* (*sym_bpf_program__name)(const struct bpf_program *);
extern libbpf_print_fn_t (*sym_libbpf_set_print)(libbpf_print_fn_t);
extern long (*sym_libbpf_get_error)(const void *);
#endif #endif

View File

@ -571,7 +571,11 @@ static int verify_xbootldr_udev(
r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v); r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to get device property: %m"); return log_error_errno(r, "Failed to get device property: %m");
if (id128_equal_string(v, GPT_XBOOTLDR))
r = id128_equal_string(v, GPT_XBOOTLDR);
if (r < 0)
return log_error_errno(r, "Failed to parse ID_PART_ENTRY_TYPE=%s: %m", v);
if (r == 0)
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
"File system \"%s\" has wrong type for extended boot loader partition.", node); "File system \"%s\" has wrong type for extended boot loader partition.", node);

View File

@ -1123,7 +1123,7 @@ int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* filter
if (ERRNO_IS_SECCOMP_FATAL(r)) if (ERRNO_IS_SECCOMP_FATAL(r))
return r; return r;
if (r < 0) if (r < 0)
log_debug_errno(r, "Failed to install systemc call filter for architecture %s, skipping: %m", log_debug_errno(r, "Failed to install system call filter for architecture %s, skipping: %m",
seccomp_arch_to_string(arch)); seccomp_arch_to_string(arch));
} }

View File

@ -337,7 +337,7 @@ int utmp_wall(
const char *message, const char *message,
const char *username, const char *username,
const char *origin_tty, const char *origin_tty,
bool (*match_tty)(const char *tty, void *userdata), bool (*match_tty)(const char *tty, bool is_local, void *userdata),
void *userdata) { void *userdata) {
_unused_ _cleanup_(utxent_cleanup) bool utmpx = false; _unused_ _cleanup_(utxent_cleanup) bool utmpx = false;
@ -381,17 +381,20 @@ int utmp_wall(
if (u->ut_type != USER_PROCESS || u->ut_user[0] == 0) if (u->ut_type != USER_PROCESS || u->ut_user[0] == 0)
continue; continue;
/* this access is fine, because STRLEN("/dev/") << 32 (UT_LINESIZE) */ /* This access is fine, because strlen("/dev/") < 32 (UT_LINESIZE) */
if (path_startswith(u->ut_line, "/dev/")) if (path_startswith(u->ut_line, "/dev/"))
path = u->ut_line; path = u->ut_line;
else { else {
if (asprintf(&buf, "/dev/%.*s", (int) sizeof(u->ut_line), u->ut_line) < 0) if (asprintf(&buf, "/dev/%.*s", (int) sizeof(u->ut_line), u->ut_line) < 0)
return -ENOMEM; return -ENOMEM;
path = buf; path = buf;
} }
if (!match_tty || match_tty(path, userdata)) { /* It seems that the address field is always set for remote logins.
* For local logins and other local entries, we get [0,0,0,0]. */
bool is_local = memeqzero(u->ut_addr_v6, sizeof(u->ut_addr_v6));
if (!match_tty || match_tty(path, is_local, userdata)) {
q = write_to_terminal(path, text); q = write_to_terminal(path, text);
if (q < 0) if (q < 0)
r = q; r = q;

View File

@ -23,7 +23,7 @@ int utmp_wall(
const char *message, const char *message,
const char *username, const char *username,
const char *origin_tty, const char *origin_tty,
bool (*match_tty)(const char *tty, void *userdata), bool (*match_tty)(const char *tty, bool is_local, void *userdata),
void *userdata); void *userdata);
static inline bool utxent_start(void) { static inline bool utxent_start(void) {

View File

@ -211,7 +211,9 @@ tests += [
[files('test-mkdir.c')], [files('test-mkdir.c')],
[files('test-json.c')], [files('test-json.c'),
[],
[libm]],
[files('test-modhex.c')], [files('test-modhex.c')],
@ -275,7 +277,9 @@ tests += [
[files('test-parse-helpers.c')], [files('test-parse-helpers.c')],
[files('test-parse-util.c')], [files('test-parse-util.c'),
[],
[libm]],
[files('test-sysctl-util.c')], [files('test-sysctl-util.c')],
@ -664,6 +668,8 @@ tests += [
[], [], [], 'ENABLE_NSCD', 'manual'], [], [], [], 'ENABLE_NSCD', 'manual'],
[files('test-hmac.c')], [files('test-hmac.c')],
[files('test-sha256.c')],
] ]
############################################################ ############################################################

View File

@ -655,7 +655,7 @@ TEST(memory_deny_write_execute_shmat) {
log_notice("Seccomp not available, skipping %s", __func__); log_notice("Seccomp not available, skipping %s", __func__);
return; return;
} }
if (!have_seccomp_privs()) { if (!have_seccomp_privs() || have_effective_cap(CAP_IPC_OWNER) <= 0) {
log_notice("Not privileged, skipping %s", __func__); log_notice("Not privileged, skipping %s", __func__);
return; return;
} }

50
src/test/test-sha256.c Normal file
View File

@ -0,0 +1,50 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "hexdecoct.h"
#include "sha256.h"
#include "string-util.h"
#include "tests.h"
static void sha256_process_string(const char *key, struct sha256_ctx *ctx) {
sha256_process_bytes(key, strlen(key), ctx);
}
static void test_sha256_one(const char *key, const char *expect) {
uint8_t result[SHA256_DIGEST_SIZE + 3];
_cleanup_free_ char *str = NULL;
struct sha256_ctx ctx;
log_debug("\"%s\" → %s", key, expect);
assert_se(str = new(char, strlen(key) + 4));
/* This tests unaligned buffers. */
for (size_t i = 0; i < 4; i++) {
strcpy(str + i, key);
for (size_t j = 0; j < 4; j++) {
_cleanup_free_ char *hex_result = NULL;
sha256_init_ctx(&ctx);
sha256_process_string(str + i, &ctx);
sha256_finish_ctx(&ctx, result + j);
hex_result = hexmem(result + j, SHA256_DIGEST_SIZE);
assert_se(streq_ptr(hex_result, expect));
}
}
}
TEST(sha256) {
/* Results compared with output of 'echo -n "<input>" | sha256sum -' */
test_sha256_one("abcdefghijklmnopqrstuvwxyz",
"71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73");
test_sha256_one("ほげほげあっちょんぶりけ",
"ce7225683653be3b74861c5a4323b6baf3c3ceb361413ca99e3a5b52c04411bd");
test_sha256_one("0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789",
"9cfe7faff7054298ca87557e15a10262de8d3eee77827417fbdfea1c41b9ec23");
}
DEFINE_TEST_MAIN(LOG_INFO);

View File

@ -94,7 +94,7 @@ static int send_passwords(const char *socket_name, char **passwords) {
return (int) n; return (int) n;
} }
static bool wall_tty_match(const char *path, void *userdata) { static bool wall_tty_match(const char *path, bool is_local, void *userdata) {
_cleanup_free_ char *p = NULL; _cleanup_free_ char *p = NULL;
_cleanup_close_ int fd = -1; _cleanup_close_ int fd = -1;
struct stat st; struct stat st;

View File

@ -556,7 +556,7 @@ static int run(int argc, char *argv[]) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Parent already died?"); return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Parent already died?");
if (kill(parent, SIGUSR2) < 0) if (kill(parent, SIGUSR2) < 0)
return log_error_errno(errno, "Failed to kill our own parent."); return log_error_errno(errno, "Failed to kill our own parent: %m");
} }
} }

View File

@ -127,7 +127,7 @@ static int run(int argc, char *argv[]) {
r = query_volatile_mode(&m); r = query_volatile_mode(&m);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to determine volatile mode from kernel command line."); return log_error_errno(r, "Failed to determine volatile mode from kernel command line: %m");
if (r == 0 && argc >= 2) { if (r == 0 && argc >= 2) {
/* The kernel command line always wins. However if nothing was set there, the argument passed here wins instead. */ /* The kernel command line always wins. However if nothing was set there, the argument passed here wins instead. */
m = volatile_mode_from_string(argv[1]); m = volatile_mode_from_string(argv[1]);

View File

@ -10,6 +10,11 @@ TEST_FORCE_NEWIMAGE=1
# shellcheck source=test/test-functions # shellcheck source=test/test-functions
. "${TEST_BASE_DIR:?}/test-functions" . "${TEST_BASE_DIR:?}/test-functions"
PART_UUID="deadbeef-dead-dead-beef-000000000000"
DM_NAME="test24_varcrypt"
KERNEL_APPEND+=" rd.luks=1 luks.name=$PART_UUID=$DM_NAME luks.key=$PART_UUID=/keyfile:LABEL=varcrypt_keydev"
QEMU_OPTIONS+=" -drive format=raw,cache=unsafe,file=${STATEDIR:?}/keydev.img"
check_result_qemu() { check_result_qemu() {
local ret=1 local ret=1
@ -17,13 +22,13 @@ check_result_qemu() {
[[ -e "${initdir:?}/testok" ]] && ret=0 [[ -e "${initdir:?}/testok" ]] && ret=0
[[ -f "$initdir/failed" ]] && cp -a "$initdir/failed" "${TESTDIR:?}" [[ -f "$initdir/failed" ]] && cp -a "$initdir/failed" "${TESTDIR:?}"
cryptsetup luksOpen "${LOOPDEV:?}p2" varcrypt <"$TESTDIR/keyfile" cryptsetup luksOpen "${LOOPDEV:?}p2" "${DM_NAME:?}" <"$TESTDIR/keyfile"
mount /dev/mapper/varcrypt "$initdir/var" mount "/dev/mapper/$DM_NAME" "$initdir/var"
save_journal "$initdir/var/log/journal" save_journal "$initdir/var/log/journal"
check_coverage_reports "${initdir:?}" || ret=5 check_coverage_reports "${initdir:?}" || ret=5
_umount_dir "$initdir/var" _umount_dir "$initdir/var"
_umount_dir "$initdir" _umount_dir "$initdir"
cryptsetup luksClose /dev/mapper/varcrypt cryptsetup luksClose "/dev/mapper/$DM_NAME"
[[ -f "$TESTDIR/failed" ]] && cat "$TESTDIR/failed" [[ -f "$TESTDIR/failed" ]] && cat "$TESTDIR/failed"
echo "${JOURNAL_LIST:-No journals were saved}" echo "${JOURNAL_LIST:-No journals were saved}"
@ -36,45 +41,65 @@ test_create_image() {
create_empty_image_rootdir create_empty_image_rootdir
echo -n test >"${TESTDIR:?}/keyfile" echo -n test >"${TESTDIR:?}/keyfile"
cryptsetup -q luksFormat --pbkdf pbkdf2 --pbkdf-force-iterations 1000 "${LOOPDEV:?}p2" "$TESTDIR/keyfile" cryptsetup -q luksFormat --uuid="$PART_UUID" --pbkdf pbkdf2 --pbkdf-force-iterations 1000 "${LOOPDEV:?}p2" "$TESTDIR/keyfile"
cryptsetup luksOpen "${LOOPDEV}p2" varcrypt <"$TESTDIR/keyfile" cryptsetup luksOpen "${LOOPDEV}p2" "${DM_NAME:?}" <"$TESTDIR/keyfile"
mkfs.ext4 -L var /dev/mapper/varcrypt mkfs.ext4 -L var "/dev/mapper/$DM_NAME"
mkdir -p "${initdir:?}/var" mkdir -p "${initdir:?}/var"
mount /dev/mapper/varcrypt "$initdir/var" mount "/dev/mapper/$DM_NAME" "$initdir/var"
# Create what will eventually be our root filesystem onto an overlay LOG_LEVEL=5
(
LOG_LEVEL=5
# shellcheck source=/dev/null
source <(udevadm info --export --query=env --name=/dev/mapper/varcrypt)
# shellcheck source=/dev/null
source <(udevadm info --export --query=env --name="${LOOPDEV}p2")
setup_basic_environment setup_basic_environment
mask_supporting_services mask_supporting_services
install_dmevent install_dmevent
generate_module_dependencies generate_module_dependencies
cat >"$initdir/etc/crypttab" <<EOF
$DM_NAME UUID=$ID_FS_UUID /etc/varkey
EOF
echo -n test >"$initdir/etc/varkey"
ddebug <"$initdir/etc/crypttab"
cat >>"$initdir/etc/fstab" <<EOF # Create a keydev
/dev/mapper/varcrypt /var ext4 defaults 0 1 dd if=/dev/zero of="${STATEDIR:?}/keydev.img" bs=1M count=16
mkfs.ext4 -L varcrypt_keydev "$STATEDIR/keydev.img"
mkdir -p "$STATEDIR/keydev"
mount "$STATEDIR/keydev.img" "$STATEDIR/keydev"
echo -n test >"$STATEDIR/keydev/keyfile"
umount "$STATEDIR/keydev"
cat >>"$initdir/etc/fstab" <<EOF
/dev/mapper/$DM_NAME /var ext4 defaults 0 1
EOF EOF
# Forward journal messages to the console, so we have something # Forward journal messages to the console, so we have something
# to investigate even if we fail to mount the encrypted /var # to investigate even if we fail to mount the encrypted /var
echo ForwardToConsole=yes >> "$initdir/etc/systemd/journald.conf" echo ForwardToConsole=yes >> "$initdir/etc/systemd/journald.conf"
)
# If $INITRD wasn't provided explicitly, generate a custom one with dm-crypt
# support
if [[ -z "$INITRD" ]]; then
INITRD="${TESTDIR:?}/initrd.img"
dinfo "Generating a custom initrd with dm-crypt support in '${INITRD:?}'"
if command -v dracut >/dev/null; then
dracut --force --verbose --add crypt "$INITRD"
elif command -v mkinitcpio >/dev/null; then
mkinitcpio --addhooks sd-encrypt --generate "$INITRD"
elif command -v mkinitramfs >/dev/null; then
# The cryptroot hook is provided by the cryptsetup-initramfs package
if ! dpkg-query -s cryptsetup-initramfs; then
derror "Missing 'cryptsetup-initramfs' package for dm-crypt support in initrd"
return 1
fi
mkinitramfs -o "$INITRD"
else
dfatal "Unrecognized initrd generator, can't continue"
return 1
fi
fi
} }
cleanup_root_var() { cleanup_root_var() {
ddebug "umount ${initdir:?}/var" ddebug "umount ${initdir:?}/var"
mountpoint "$initdir/var" && umount "$initdir/var" mountpoint "$initdir/var" && umount "$initdir/var"
[[ -b /dev/mapper/varcrypt ]] && cryptsetup luksClose /dev/mapper/varcrypt [[ -b "/dev/mapper/${DM_NAME:?}" ]] && cryptsetup luksClose "/dev/mapper/$DM_NAME"
} }
test_cleanup() { test_cleanup() {

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -337,6 +337,11 @@ qemu_min_version() {
# Return 0 if qemu did run (then you must check the result state/logs for actual # Return 0 if qemu did run (then you must check the result state/logs for actual
# success), or 1 if qemu is not available. # success), or 1 if qemu is not available.
run_qemu() { run_qemu() {
# If the test provided its own initrd, use it (e.g. TEST-24)
if [[ -z "$INITRD" && -f "${TESTDIR:?}/initrd.img" ]]; then
INITRD="$TESTDIR/initrd.img"
fi
if [ -f /etc/machine-id ]; then if [ -f /etc/machine-id ]; then
read -r MACHINE_ID </etc/machine-id read -r MACHINE_ID </etc/machine-id
[ -z "$INITRD" ] && [ -e "$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/initrd" ] \ [ -z "$INITRD" ] && [ -e "$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/initrd" ] \

View File

@ -73,7 +73,7 @@ def run(args):
logger.info("waiting for reboot") logger.info("waiting for reboot")
console.expect('H login: ', 10) console.expect('H login: ', 30)
console.sendline('root') console.sendline('root')
console.expect('bash.*# ', 10) console.expect('bash.*# ', 10)