sd-varlink: mark varlink sockets via xattrs (#42454)
Linux 7.0 added the ability to mark socket inodes with xattrs. Let's use that to clearly mark all our Varlink sockets as being varlink related. This is then used to implement a very useful new command "varlinkctl list-sockets" which lists all varlink entrypoint sockets marked this way. By marking not just the entrypoint inodes but also the connection sockets properly, we can one day add an ebpf based "varlinkctl trace" command that watches varlink sockets for traffic. but that's material for a later PR.
This commit is contained in:
@@ -126,6 +126,8 @@ SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
- remove any trace of "cpuacct" cgroup controller, it's a cgroupv1 thing.
|
||||
similar "devices"
|
||||
|
||||
- drop socket_xattr_supported() once our baseline is kernel 7.0
|
||||
|
||||
## Features
|
||||
|
||||
- **report:**
|
||||
@@ -229,12 +231,6 @@ SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
sequences of this type, so that every step of the way we get the right
|
||||
behaviour.
|
||||
|
||||
- now that the kernel supports xattrs on sockets: mark varlink entrypoint
|
||||
sockets, server side of varlink sockets, and client sides of valrink sockets
|
||||
with distinct xattrs to make them recognizable (similar maybe for our other
|
||||
protocols, such as syslog, journal native entry point). For entrypoints might
|
||||
require new .socket unit setting.
|
||||
|
||||
- implement "varlinkctl trace" or so, that watches socket traffic on a group of
|
||||
processes (select by pid, select by cgroup, select by all machine), and shows
|
||||
traffic of all sockets marked via the new varlink socket xattrs. Use BPF for
|
||||
|
||||
@@ -5245,6 +5245,12 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as Symlinks = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly a(ss) XAttrEntryPoint = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly a(ss) XAttrListen = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly a(ss) XAttrAccept = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly i Mark = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly u MaxConnections = ...;
|
||||
@@ -6616,6 +6622,12 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="Symlinks"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="XAttrEntryPoint"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="XAttrListen"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="XAttrAccept"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="Mark"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MaxConnections"/>
|
||||
@@ -7299,6 +7311,17 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
||||
<para><varname>FlushPending</varname> specifies whether to flush the socket
|
||||
just before entering the listening state. This setting only applies to sockets with
|
||||
<varname>Accept=</varname> set to <literal>no</literal>.</para>
|
||||
|
||||
<para><varname>XAttrEntryPoint</varname>, <varname>XAttrListen</varname> and
|
||||
<varname>XAttrAccept</varname> each contain a list of extended attributes (as
|
||||
<literal><replaceable>NAME</replaceable></literal>/<literal><replaceable>VALUE</replaceable></literal>
|
||||
pairs) to set on socket inodes. <varname>XAttrEntryPoint</varname> applies to the file-system inode an
|
||||
<constant>AF_UNIX</constant> socket is bound to, <varname>XAttrListen</varname> applies to the listening
|
||||
socket, and <varname>XAttrAccept</varname> applies to connection sockets accepted off the listening
|
||||
socket. See the <varname>XAttrEntryPoint=</varname>, <varname>XAttrListen=</varname> and
|
||||
<varname>XAttrAccept=</varname> settings in
|
||||
<citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
|
||||
details.</para>
|
||||
</refsect2>
|
||||
</refsect1>
|
||||
|
||||
@@ -12977,6 +13000,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
|
||||
<varname>IOPressureWatch</varname>,
|
||||
<varname>CPUSetPartition</varname>, and
|
||||
<varname>OOMRules</varname> were added in version 261.</para>
|
||||
<para><varname>XAttrEntryPoint</varname>,
|
||||
<varname>XAttrListen</varname>, and
|
||||
<varname>XAttrAccept</varname> were added in version 262.</para>
|
||||
</refsect2>
|
||||
<refsect2>
|
||||
<title>Mount Unit Objects</title>
|
||||
|
||||
@@ -977,6 +977,12 @@ manpages = [
|
||||
['sd_varlink_is_connected', '3', ['sd_varlink_is_idle'], ''],
|
||||
['sd_varlink_push_fd', '3', ['sd_varlink_push_dup_fd'], ''],
|
||||
['sd_varlink_send', '3', ['sd_varlink_sendb', 'sd_varlink_sendbo'], ''],
|
||||
['sd_varlink_server_listen_address',
|
||||
'3',
|
||||
['sd_varlink_server_listen_auto',
|
||||
'sd_varlink_server_listen_fd',
|
||||
'sd_varlink_server_listen_name'],
|
||||
''],
|
||||
['sd_varlink_server_new', '3', [], ''],
|
||||
['sd_varlink_set_description', '3', ['sd_varlink_get_description'], ''],
|
||||
['sd_varlink_set_relative_timeout', '3', [], ''],
|
||||
|
||||
@@ -55,6 +55,84 @@
|
||||
makes the functionality implemented by sd-varlink available from the command line.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Recognizing Varlink Socket Inodes</title>
|
||||
|
||||
<para>On kernels that support extended attributes on socket inodes (available since Linux 7.0),
|
||||
sd-varlink automatically tags the <constant>AF_UNIX</constant> socket inodes it creates and manages with
|
||||
the <varname>user.varlink</varname> extended attribute. The attribute records the role the socket plays
|
||||
in the Varlink communication, allowing other processes to discover and classify Varlink sockets, for
|
||||
example via <command>varlinkctl list-sockets</command> (see
|
||||
<citerefentry><refentrytitle>varlinkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>).</para>
|
||||
|
||||
<para>Four distinct roles are defined:</para>
|
||||
|
||||
<table>
|
||||
<title><varname>user.varlink</varname> extended attribute roles</title>
|
||||
|
||||
<tgroup cols='2'>
|
||||
<colspec colname='value' />
|
||||
<colspec colname='meaning' />
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Attribute value</entry>
|
||||
<entry>Meaning</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry><literal>client</literal></entry>
|
||||
<entry>Set on the connecting end of a connection, i.e. a socket created via <function>socket()</function> + <function>connect()</function>. Only for inodes on the anonymous socket file system (<literal>sockfs</literal>).</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>server</literal></entry>
|
||||
<entry>Set on the serving end of a connection, i.e. a socket obtained via <function>accept()</function> on a listening socket. Only for inodes on the anonymous socket file system (<literal>sockfs</literal>).</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>listen</literal></entry>
|
||||
<entry>Set on a listening socket, i.e. a socket created via <function>socket()</function> + <function>listen()</function>. Only for inodes on the anonymous socket file system (<literal>sockfs</literal>).</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>entrypoint</literal></entry>
|
||||
<entry>Set on the entrypoint socket inode, i.e. the socket node bound into the regular file system that clients connect to. Unlike the other three, this attribute lives on a real file-system inode (not on <literal>sockfs</literal>) and is hence visible to any process that can resolve the socket path.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<para>Note that for socket activated Varlink services it is recommended to set these extended attributes
|
||||
via <varname>XAttrEntryPoint=</varname>, <varname>XAttrListen=</varname> and
|
||||
<varname>XAttrAccept=</varname> settings on the socket units, see
|
||||
<citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
|
||||
details. Specifically, for socket units for which <varname>Accept=no</varname> is set:</para>
|
||||
|
||||
<programlisting>…
|
||||
[Socket]
|
||||
SocketMode=0644
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
…</programlisting>
|
||||
|
||||
<para>For socket units which have <varname>Accept=yes</varname>:</para>
|
||||
|
||||
<programlisting>…
|
||||
[Socket]
|
||||
Accept=yes
|
||||
SocketMode=0644
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
…</programlisting>
|
||||
|
||||
<para>Note that for Varlink sockets that shall not be world-accessible it is recommended to unset the
|
||||
relevant "w" bits in the socket access mode, but to keep the relevant "r" bits set. As the access check
|
||||
done by <function>connect()</function> looks for the "w" bits, this does not make the service
|
||||
unnecessarily accessible, however it has the benefit that the extended attributes that identify the
|
||||
socket as Varlink-related may still be read by other users (as that's restricted by the "r" bit). Or in
|
||||
other words, it's recommended to set <literal>SocketMode=0644</literal> for services that shall not be
|
||||
accessible to arbitrary users, rather than <literal>SocketMode=0600</literal>.</para>
|
||||
</refsect1>
|
||||
|
||||
<xi:include href="libsystemd-pkgconfig.xml" />
|
||||
|
||||
<refsect1>
|
||||
@@ -87,6 +165,7 @@
|
||||
<member><citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>sd-json</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>varlinkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
|
||||
<member><citerefentry project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
</simplelist></para>
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
<?xml version='1.0'?>
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
||||
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
|
||||
|
||||
<refentry id="sd_varlink_server_listen_address" xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<refentryinfo>
|
||||
<title>sd_varlink_server_listen_address</title>
|
||||
<productname>systemd</productname>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>sd_varlink_server_listen_address</refentrytitle>
|
||||
<manvolnum>3</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>sd_varlink_server_listen_address</refname>
|
||||
<refname>sd_varlink_server_listen_fd</refname>
|
||||
<refname>sd_varlink_server_listen_name</refname>
|
||||
<refname>sd_varlink_server_listen_auto</refname>
|
||||
|
||||
<refpurpose>Bind a Varlink server object to listening sockets</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<funcsynopsis>
|
||||
<funcsynopsisinfo>#include <systemd/sd-varlink.h></funcsynopsisinfo>
|
||||
|
||||
<funcsynopsisinfo>#define SD_VARLINK_SERVER_MODE_MKDIR_0755 …</funcsynopsisinfo>
|
||||
|
||||
<funcprototype>
|
||||
<funcdef>int <function>sd_varlink_server_listen_address</function></funcdef>
|
||||
<paramdef>sd_varlink_server* <parameter>server</parameter></paramdef>
|
||||
<paramdef>const char* <parameter>address</parameter></paramdef>
|
||||
<paramdef>mode_t <parameter>mode</parameter></paramdef>
|
||||
</funcprototype>
|
||||
|
||||
<funcprototype>
|
||||
<funcdef>int <function>sd_varlink_server_listen_fd</function></funcdef>
|
||||
<paramdef>sd_varlink_server* <parameter>server</parameter></paramdef>
|
||||
<paramdef>int <parameter>fd</parameter></paramdef>
|
||||
</funcprototype>
|
||||
|
||||
<funcprototype>
|
||||
<funcdef>int <function>sd_varlink_server_listen_name</function></funcdef>
|
||||
<paramdef>sd_varlink_server* <parameter>server</parameter></paramdef>
|
||||
<paramdef>const char* <parameter>name</parameter></paramdef>
|
||||
</funcprototype>
|
||||
|
||||
<funcprototype>
|
||||
<funcdef>int <function>sd_varlink_server_listen_auto</function></funcdef>
|
||||
<paramdef>sd_varlink_server* <parameter>server</parameter></paramdef>
|
||||
</funcprototype>
|
||||
|
||||
</funcsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para>These functions add listening sockets to a Varlink server object, as previously allocated with
|
||||
<citerefentry><refentrytitle>sd_varlink_server_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
|
||||
Once the server object is attached to an event loop (see
|
||||
<function>sd_varlink_server_attach_event()</function>) it will accept incoming connections on the
|
||||
configured sockets and dispatch the registered method calls.</para>
|
||||
|
||||
<para><function>sd_varlink_server_listen_address()</function> allocates a new
|
||||
<constant>AF_UNIX</constant> socket, binds it to the specified <parameter>address</parameter>, and starts
|
||||
listening on it. The <parameter>address</parameter> is a file system path of the socket to bind to. (Use
|
||||
<function>sd_varlink_server_listen_fd()</function> below for sockets in the abstract namespace, in foreign
|
||||
address families, or that are otherwise already allocated.)</para>
|
||||
|
||||
<para>The <parameter>mode</parameter> parameter specifies the file system access mode to apply to the
|
||||
socket inode, i.e. a bit mask of permission bits as in
|
||||
<citerefentry project='man-pages'><refentrytitle>chmod</refentrytitle><manvolnum>2</manvolnum></citerefentry>.
|
||||
Only the lower nine bits (<constant>0777</constant>) are permitted. The special value
|
||||
<constant>(mode_t) -1</constant> may be specified to pick a suitable default: this is
|
||||
<constant>0666</constant> in the general case, but <constant>0644</constant> if the server was allocated
|
||||
with the <constant>SD_VARLINK_SERVER_ROOT_ONLY</constant> or
|
||||
<constant>SD_VARLINK_SERVER_MYSELF_ONLY</constant> flags (the reason being that it is the
|
||||
<literal>w</literal> bit that controls
|
||||
<citerefentry project='man-pages'><refentrytitle>connect</refentrytitle><manvolnum>2</manvolnum></citerefentry>
|
||||
access, while leaving the <literal>r</literal> bit on permits other users to read the socket's extended
|
||||
attributes, which makes the socket recognizable as a Varlink entrypoint, see
|
||||
<citerefentry><refentrytitle>sd-varlink</refentrytitle><manvolnum>3</manvolnum></citerefentry>).</para>
|
||||
|
||||
<para>The constant <constant>SD_VARLINK_SERVER_MODE_MKDIR_0755</constant> may be OR-ed into the
|
||||
<parameter>mode</parameter> parameter (this is not supported if the mode is specified as
|
||||
<constant>(mode_t) -1</constant>). If specified, and <parameter>address</parameter> is an absolute path,
|
||||
the leading directories of the socket path are automatically created (with access mode
|
||||
<constant>0755</constant>) if they are missing.</para>
|
||||
|
||||
<para><function>sd_varlink_server_listen_fd()</function> adds an already allocated and listening socket to
|
||||
the server object, referenced by the file descriptor <parameter>fd</parameter>. This is typically used in
|
||||
conjunction with socket activation, to make use of a listening socket passed in from the service manager,
|
||||
see
|
||||
<citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>. The
|
||||
file descriptor is automatically switched to non-blocking mode and the close-on-exec flag is set on
|
||||
it.</para>
|
||||
|
||||
<para><function>sd_varlink_server_listen_name()</function> picks up sockets passed in via the socket
|
||||
activation protocol whose name (as set via <varname>$LISTEN_FDNAMES</varname>, i.e. via the
|
||||
<varname>FileDescriptorName=</varname> setting of the
|
||||
<citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry> unit)
|
||||
matches the specified <parameter>name</parameter>. For each matching file descriptor that refers to a
|
||||
listening socket <function>sd_varlink_server_listen_fd()</function> is invoked; file descriptors that
|
||||
refer to an already accepted connection are added as connections via
|
||||
<function>sd_varlink_server_add_connection()</function> instead.</para>
|
||||
|
||||
<para><function>sd_varlink_server_listen_auto()</function> is a convenience wrapper that automatically
|
||||
determines the sockets to listen on. It first picks up all socket activation file descriptors named
|
||||
<literal>varlink</literal> (by calling <function>sd_varlink_server_listen_name()</function> with that
|
||||
name). In addition, if the <varname>$SYSTEMD_VARLINK_LISTEN</varname> environment variable is set, it is
|
||||
used as an additional address to listen on: if set to <literal>-</literal> the Varlink protocol is spoken
|
||||
on standard input and output (see <function>sd_varlink_server_add_connection_stdio()</function>),
|
||||
otherwise the value is passed to <function>sd_varlink_server_listen_address()</function>. This is the
|
||||
recommended way for a Varlink service to set up its listening sockets, as it transparently supports both
|
||||
socket activation and direct invocation (for testing and debugging).</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Return Value</title>
|
||||
|
||||
<para>On success, <function>sd_varlink_server_listen_address()</function> and
|
||||
<function>sd_varlink_server_listen_fd()</function> return a non-negative integer.
|
||||
<function>sd_varlink_server_listen_name()</function> and
|
||||
<function>sd_varlink_server_listen_auto()</function> return the number of sockets and file descriptors
|
||||
they added to the server object (which may be zero). On failure, these calls return a negative errno-style
|
||||
error code.</para>
|
||||
|
||||
<refsect2>
|
||||
<title>Errors</title>
|
||||
|
||||
<para>Returned errors may indicate the following problems:</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><constant>-EINVAL</constant></term>
|
||||
|
||||
<listitem><para>An argument is invalid. This is also returned by
|
||||
<function>sd_varlink_server_listen_address()</function> if the <parameter>mode</parameter> parameter
|
||||
contains bits other than the permission bits and
|
||||
<constant>SD_VARLINK_SERVER_MODE_MKDIR_0755</constant>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><constant>-EBADF</constant></term>
|
||||
|
||||
<listitem><para>The file descriptor passed to <function>sd_varlink_server_listen_fd()</function> is
|
||||
negative.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect2>
|
||||
</refsect1>
|
||||
|
||||
<xi:include href="libsystemd-pkgconfig.xml" />
|
||||
|
||||
<refsect1>
|
||||
<title>History</title>
|
||||
<para><function>sd_varlink_server_listen_address()</function>,
|
||||
<function>sd_varlink_server_listen_fd()</function>, and
|
||||
<function>sd_varlink_server_listen_auto()</function> were added in version 257.</para>
|
||||
<para><function>sd_varlink_server_listen_name()</function> was added in version 258.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
|
||||
<para><simplelist type="inline">
|
||||
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>sd-varlink</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>sd_varlink_server_new</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>varlinkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
</simplelist></para>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
||||
@@ -675,6 +675,40 @@
|
||||
<xi:include href="version-info.xml" xpointer="v196"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>XAttrEntryPoint=</varname></term>
|
||||
<term><varname>XAttrListen=</varname></term>
|
||||
<term><varname>XAttrAccept=</varname></term>
|
||||
<listitem><para>Set an extended attribute on the socket, in the form
|
||||
<literal><replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></literal>. The extended
|
||||
attribute's name must be in the <literal>user.</literal> namespace, i.e. begin with the four
|
||||
characters <literal>user.</literal>. Specifiers are expanded in both the name and the value, see
|
||||
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
|
||||
details on the available specifiers. These settings may be used more than once to set multiple
|
||||
extended attributes; assigning the empty string resets the list.</para>
|
||||
|
||||
<para>The three variables control where the extended attributes are applied:
|
||||
<varname>XAttrEntryPoint=</varname> sets them on the file-system inode an
|
||||
<constant>AF_UNIX</constant> socket is bound to (i.e. the socket node visible in the file system at
|
||||
the specified listening path; this only applies to <constant>AF_UNIX</constant> sockets),
|
||||
<varname>XAttrListen=</varname> sets them on the listening socket, and
|
||||
<varname>XAttrAccept=</varname> sets them on the connection sockets accepted off the listening socket
|
||||
(and is thus only relevant in combination with
|
||||
<varname>Accept=</varname><literal>yes</literal>).</para>
|
||||
|
||||
<para>This is primarily useful to tag sockets so that they can be discovered and classified by other
|
||||
tools. For example, Varlink entrypoint sockets are supposed to be tagged with
|
||||
<literal>user.varlink=entrypoint</literal> via <varname>XAttrEntryPoint=</varname>, which makes them
|
||||
discoverable via <command>varlinkctl list-sockets</command>, see
|
||||
<citerefentry><refentrytitle>varlinkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
|
||||
|
||||
<para>These settings require kernel support for extended attributes on socket inodes (available since
|
||||
Linux 7.0). Extended attributes that cannot be applied are logged at debug level and otherwise
|
||||
ignored.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v262"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>SELinuxContextFromNet=</varname></term>
|
||||
<listitem><para>Takes a boolean argument. When true, systemd
|
||||
|
||||
@@ -81,6 +81,18 @@
|
||||
<arg choice="req" rep="repeat"><replaceable>CMDLINE</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
|
||||
<cmdsynopsis>
|
||||
<command>varlinkctl</command>
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
<arg choice="plain">list-registry</arg>
|
||||
</cmdsynopsis>
|
||||
|
||||
<cmdsynopsis>
|
||||
<command>varlinkctl</command>
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
<arg choice="plain">list-sockets</arg>
|
||||
</cmdsynopsis>
|
||||
|
||||
<cmdsynopsis>
|
||||
<command>varlinkctl</command>
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
@@ -221,6 +233,23 @@
|
||||
<xi:include href="version-info.xml" xpointer="v260"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><command>list-sockets</command></term>
|
||||
|
||||
<listitem><para>Shows a list of listening <constant>AF_UNIX</constant> stream sockets on the local
|
||||
system that are marked as Varlink entrypoints, along with whether the calling user has write (i.e.
|
||||
connect) access to each of them. A socket is considered a Varlink entrypoint if its inode carries the
|
||||
<varname>user.varlink</varname> extended attribute set to <literal>entrypoint</literal>.</para>
|
||||
|
||||
<para>Sockets are enumerated via the kernel's <constant>sock_diag</constant> netlink interface, and
|
||||
hence this is not restricted to sockets in <filename>/run/varlink/registry/</filename> (as
|
||||
<command>list-registry</command> is), but covers all file-system bound listening Varlink sockets,
|
||||
regardless of their location. This requires kernel support for extended attributes on socket inodes,
|
||||
which is available since Linux 7.0.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v262"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><command>validate-idl</command> [<replaceable>FILE</replaceable>]</term>
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ _varlinkctl() {
|
||||
fi
|
||||
|
||||
local -A VERBS=(
|
||||
[STANDALONE]='help list-registry'
|
||||
[STANDALONE]='help list-registry list-sockets'
|
||||
[CALL]='call'
|
||||
[FILE]='info list-interfaces validate-idl'
|
||||
[ADDRESS_INTERFACES]='list-methods introspect'
|
||||
|
||||
@@ -34,6 +34,7 @@ _regex_words varlink-commands 'varlink command' \
|
||||
'introspect:show an interface definition:$varlink_interface' \
|
||||
'call:invoke a method:$varlink_call' \
|
||||
'validate-idl:validate an interface description:$varlink_idl' \
|
||||
'list-sockets:List listening Varlink entrypoint sockets' \
|
||||
'help:show a help message'
|
||||
|
||||
local -a varlinkcmd=( /$'[^\0]#\0'/ "$reply[@]" )
|
||||
|
||||
@@ -56,6 +56,7 @@ struct statx;
|
||||
struct termios;
|
||||
struct tm;
|
||||
struct ucred;
|
||||
struct unix_diag_msg;
|
||||
|
||||
/* To forward declare FILE and DIR, we have to declare the internal struct names for them. Since these are
|
||||
* used for C++ symbol name mangling, they're effectively part of the ABI and won't actually change. */
|
||||
|
||||
@@ -56,3 +56,9 @@ static inline bool devnum_is_zero(dev_t d) {
|
||||
|
||||
#define DEVNUM_TO_PTR(u) ((void*) (uintptr_t) (u))
|
||||
#define PTR_TO_DEVNUM(p) ((dev_t) ((uintptr_t) (p)))
|
||||
|
||||
/* Convert a userspace dev_t (as returned by stat()) to the kernel's internal dev_t encoding. stat() returns
|
||||
* new_encode_dev(s_dev), while various kernel interfaces (e.g. the BPF sb helpers, or unix_diag's
|
||||
* udiag_vfs_dev) report s_dev directly, which uses MKDEV(major, minor) = (major << 20) | minor. */
|
||||
#define STAT_DEV_TO_KERNEL(dev) \
|
||||
((uint32_t) major(dev) << 20 | (uint32_t) minor(dev))
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
@@ -21,6 +22,7 @@
|
||||
#include "fd-util.h"
|
||||
#include "format-ifname.h"
|
||||
#include "format-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "io-util.h"
|
||||
#include "log.h"
|
||||
@@ -36,6 +38,8 @@
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "sysctl-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "xattr-util.h"
|
||||
|
||||
#if ENABLE_IDN
|
||||
# define IDN_FLAGS NI_IDN
|
||||
@@ -1891,3 +1895,41 @@ int tos_to_priority(uint8_t tos) {
|
||||
return TC_PRIO_BESTEFFORT;
|
||||
}
|
||||
}
|
||||
|
||||
int socket_xattr_supported(void) {
|
||||
int r;
|
||||
|
||||
// FIXME: Drop this check once Linux 7.0 becomes our baseline
|
||||
|
||||
/* Checks if socket inodes may have xattrs on this kernel. This should pass on kernel 7.0, fail on
|
||||
* older kernels */
|
||||
|
||||
static int cached = -1;
|
||||
if (cached >= 0)
|
||||
return cached;
|
||||
|
||||
const char *t;
|
||||
r = tmp_dir(&t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_free_ char *sp = NULL;
|
||||
r = tempfn_random_child(t, "sockxattrtest", &sp);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (mknod(sp, S_IFSOCK | 0600, /* dev= */ 0) < 0)
|
||||
return -errno;
|
||||
|
||||
_cleanup_(unlink_and_freep) char *sp_destroy = TAKE_PTR(sp);
|
||||
|
||||
/* Old kernels return EPERM. But let's also check for more appropriate error codes, to be friendly to
|
||||
* seccomp policies */
|
||||
r = xsetxattr(AT_FDCWD, sp_destroy, /* at_flags= */ 0, "user.testxxx", "1");
|
||||
if (ERRNO_IS_NEG_NOT_SUPPORTED(r) || r == -EPERM)
|
||||
return (cached = false);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to set test xattr on socket inode '%s': %m", sp_destroy);
|
||||
|
||||
return (cached = true);
|
||||
}
|
||||
|
||||
@@ -268,3 +268,5 @@ int socket_get_cookie(int fd, uint64_t *ret);
|
||||
void cmsg_close_all(struct msghdr *mh);
|
||||
|
||||
int tos_to_priority(uint8_t tos);
|
||||
|
||||
int socket_xattr_supported(void);
|
||||
|
||||
@@ -36,8 +36,7 @@ static int normalize_and_maybe_pin_inode(
|
||||
assert(ret_tfd);
|
||||
assert(ret_opath);
|
||||
|
||||
if (isempty(*path))
|
||||
*path = NULL; /* Normalize "" to NULL */
|
||||
*path = empty_to_null(*path); /* Normalize "" to NULL */
|
||||
|
||||
if (*fd == AT_FDCWD) {
|
||||
if (!*path) /* Both unspecified? Then operate on current working directory */
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "bpf-restrict-fsaccess.h"
|
||||
#include "devnum-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "initrd-util.h"
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <sys/sysmacros.h>
|
||||
|
||||
#include "core-forward.h"
|
||||
#include "macro.h"
|
||||
#include "shared-forward.h"
|
||||
@@ -33,12 +31,6 @@ enum {
|
||||
/* Maximum number of dm-verity devices tracked in the BPF hash map. */
|
||||
#define DMVERITY_DEVICES_MAX (16U*1024U)
|
||||
|
||||
/* Convert userspace dev_t (from stat()) to kernel dev_t encoding (MKDEV).
|
||||
* stat() returns new_encode_dev(s_dev); the BPF program reads s_dev directly
|
||||
* which uses MKDEV(major, minor) = (major << 20) | minor. */
|
||||
#define STAT_DEV_TO_KERNEL(dev) \
|
||||
((uint32_t)major(dev) << 20 | (uint32_t)minor(dev))
|
||||
|
||||
/* Mirrors the BPF program's .bss section layout for read-modify-write via
|
||||
* bpf_map_lookup_elem/bpf_map_update_elem on the serialized .bss map FD. */
|
||||
struct restrict_fsaccess_bss {
|
||||
|
||||
@@ -58,6 +58,33 @@ static int property_get_listen(
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static int property_get_xattr(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *reterr_error) {
|
||||
|
||||
char ***xattr = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(reply);
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "(ss)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
STRV_FOREACH_PAIR(name, value, *xattr) {
|
||||
r = sd_bus_message_append(reply, "(ss)", *name, *value);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
const sd_bus_vtable bus_socket_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_PROPERTY("BindIPv6Only", "s", property_get_bind_ipv6_only, offsetof(Socket, bind_ipv6_only), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
@@ -95,6 +122,9 @@ const sd_bus_vtable bus_socket_vtable[] = {
|
||||
SD_BUS_PROPERTY("RemoveOnStop", "b", bus_property_get_bool, offsetof(Socket, remove_on_stop), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Listen", "a(ss)", property_get_listen, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Symlinks", "as", NULL, offsetof(Socket, symlinks), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("XAttrEntryPoint", "a(ss)", property_get_xattr, offsetof(Socket, xattr_entrypoint), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("XAttrListen", "a(ss)", property_get_xattr, offsetof(Socket, xattr_listen), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("XAttrAccept", "a(ss)", property_get_xattr, offsetof(Socket, xattr_accept), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Mark", "i", bus_property_get_int, offsetof(Socket, mark), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("MaxConnections", "u", bus_property_get_unsigned, offsetof(Socket, max_connections), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("MaxConnectionsPerSource", "u", bus_property_get_unsigned, offsetof(Socket, max_connections_per_source), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
@@ -153,6 +183,63 @@ static BUS_DEFINE_SET_TRANSIENT_TO_STRING(socket_protocol, "i", int32_t, int, "%
|
||||
static BUS_DEFINE_SET_TRANSIENT_PARSE(socket_timestamping, SocketTimestamping, socket_timestamping_from_string_harder);
|
||||
static BUS_DEFINE_SET_TRANSIENT_PARSE(socket_defer_trigger, SocketDeferTrigger, socket_defer_trigger_from_string);
|
||||
|
||||
static int bus_socket_set_transient_xattr(
|
||||
Unit *u,
|
||||
const char *name,
|
||||
char ***p,
|
||||
sd_bus_message *message,
|
||||
UnitWriteFlags flags,
|
||||
sd_bus_error *reterr_error) {
|
||||
|
||||
_cleanup_strv_free_ char **pairs = NULL;
|
||||
const char *xname, *xvalue;
|
||||
bool empty = true;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(name);
|
||||
assert(p);
|
||||
assert(message);
|
||||
|
||||
r = sd_bus_message_enter_container(message, 'a', "(ss)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
while ((r = sd_bus_message_read(message, "(ss)", &xname, &xvalue)) > 0) {
|
||||
if (!startswith(xname, "user."))
|
||||
return sd_bus_error_setf(reterr_error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Extended attribute name does not begin with 'user.': %s", xname);
|
||||
|
||||
r = strv_extend_many(&pairs, xname, xvalue);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
empty = false;
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_exit_container(message);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
||||
if (empty) {
|
||||
*p = strv_free(*p);
|
||||
unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=", name);
|
||||
} else {
|
||||
r = strv_extend_strv(p, pairs, /* filter_duplicates= */ false);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
STRV_FOREACH_PAIR(n, v, pairs)
|
||||
unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s=%s", name, *n, *v);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int bus_socket_set_transient_property(
|
||||
Socket *s,
|
||||
const char *name,
|
||||
@@ -335,6 +422,15 @@ static int bus_socket_set_transient_property(
|
||||
&s->exec_command[ci],
|
||||
message, flags, reterr_error);
|
||||
|
||||
if (streq(name, "XAttrEntryPoint"))
|
||||
return bus_socket_set_transient_xattr(u, name, &s->xattr_entrypoint, message, flags, reterr_error);
|
||||
|
||||
if (streq(name, "XAttrListen"))
|
||||
return bus_socket_set_transient_xattr(u, name, &s->xattr_listen, message, flags, reterr_error);
|
||||
|
||||
if (streq(name, "XAttrAccept"))
|
||||
return bus_socket_set_transient_xattr(u, name, &s->xattr_accept, message, flags, reterr_error);
|
||||
|
||||
if (streq(name, "Symlinks")) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
|
||||
|
||||
@@ -561,6 +561,9 @@ Socket.PollLimitIntervalSec, config_parse_sec,
|
||||
Socket.PollLimitBurst, config_parse_unsigned, 0, offsetof(Socket, poll_limit.burst)
|
||||
Socket.DeferTrigger, config_parse_socket_defer_trigger, 0, offsetof(Socket, defer_trigger)
|
||||
Socket.DeferTriggerMaxSec, config_parse_sec_fix_0, 0, offsetof(Socket, defer_trigger_max_usec)
|
||||
Socket.XAttrEntryPoint, config_parse_xattr, 0, offsetof(Socket, xattr_entrypoint)
|
||||
Socket.XAttrListen, config_parse_xattr, 0, offsetof(Socket, xattr_listen)
|
||||
Socket.XAttrAccept, config_parse_xattr, 0, offsetof(Socket, xattr_accept)
|
||||
{% if ENABLE_SMACK %}
|
||||
Socket.SmackLabel, config_parse_unit_string_printf, 0, offsetof(Socket, smack)
|
||||
Socket.SmackLabelIPIn, config_parse_unit_string_printf, 0, offsetof(Socket, smack_ip_in)
|
||||
|
||||
@@ -2247,6 +2247,66 @@ int config_parse_socket_service(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_xattr(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
char ***lp = ASSERT_PTR(data);
|
||||
Unit *u = userdata;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
*lp = strv_free(*lp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_cleanup_free_ char *name = NULL, *value = NULL;
|
||||
r = split_pair(rvalue, "=", &name, &value);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse extended attribute expression, ignoring: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_cleanup_free_ char *expanded_name = NULL;
|
||||
r = unit_full_printf(u, name, &expanded_name);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to expand specifiers in extended attribute expression, ignoring: %s", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!startswith(expanded_name, "user.")) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, 0, "Extended attribute name does not begin with 'user.', ignoring: %s", expanded_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_cleanup_free_ char *expanded_value = NULL;
|
||||
r = unit_full_printf(u, value, &expanded_value);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to expand specifiers in extended attribute expression, ignoring: %s", value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strv_push_pair(lp, expanded_name, expanded_value) < 0)
|
||||
return log_oom();
|
||||
|
||||
TAKE_PTR(expanded_name);
|
||||
TAKE_PTR(expanded_value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_fdname(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
|
||||
@@ -173,6 +173,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_bind_network_interface);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_exec_memory_thp);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_cpuset_partition);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_luo_sessions);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_xattr);
|
||||
|
||||
/* gperf prototypes */
|
||||
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
|
||||
|
||||
+8
-1
@@ -210,6 +210,10 @@ static void socket_done(Unit *u) {
|
||||
|
||||
strv_free(s->symlinks);
|
||||
|
||||
strv_free(s->xattr_entrypoint);
|
||||
strv_free(s->xattr_listen);
|
||||
strv_free(s->xattr_accept);
|
||||
|
||||
s->user = mfree(s->user);
|
||||
s->group = mfree(s->group);
|
||||
|
||||
@@ -1510,7 +1514,9 @@ static int socket_address_listen_do(
|
||||
s->directory_mode,
|
||||
s->socket_mode,
|
||||
selinux_label,
|
||||
s->smack);
|
||||
s->smack,
|
||||
s->xattr_entrypoint,
|
||||
s->xattr_listen);
|
||||
}
|
||||
|
||||
#define log_address_error_errno(u, address, error, fmt) \
|
||||
@@ -3208,6 +3214,7 @@ static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
|
||||
if (cfd < 0)
|
||||
goto fail;
|
||||
|
||||
(void) socket_set_xattrs(cfd, /* path= */ NULL, p->socket->xattr_accept);
|
||||
socket_apply_socket_options(p->socket, p, cfd);
|
||||
}
|
||||
|
||||
|
||||
@@ -126,6 +126,10 @@ typedef struct Socket {
|
||||
|
||||
char **symlinks;
|
||||
|
||||
char **xattr_entrypoint;
|
||||
char **xattr_listen;
|
||||
char **xattr_accept;
|
||||
|
||||
bool accept;
|
||||
bool remove_on_stop;
|
||||
bool writable;
|
||||
|
||||
@@ -40,3 +40,51 @@ int sd_sock_diag_message_new_unix(
|
||||
*ret = TAKE_PTR(m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_sock_diag_message_new_unix_dump(
|
||||
sd_netlink *sdnl,
|
||||
sd_netlink_message **ret,
|
||||
uint32_t states,
|
||||
uint32_t show) {
|
||||
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
int r;
|
||||
|
||||
assert_return(sdnl, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = message_new(sdnl, &m, SOCK_DIAG_BY_FAMILY, NLM_F_REQUEST | NLM_F_DUMP);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Unlike sd_sock_diag_message_new_unix() this requests a dump of all AF_UNIX sockets matching the
|
||||
* specified state mask, rather than looking up a single socket by inode/cookie. The kernel's dump
|
||||
* handler ignores udiag_ino/udiag_cookie, hence we leave them zeroed. */
|
||||
|
||||
*(struct unix_diag_req*) NLMSG_DATA(m->hdr) = (struct unix_diag_req) {
|
||||
.sdiag_family = AF_UNIX,
|
||||
.udiag_states = states,
|
||||
.udiag_show = show,
|
||||
};
|
||||
|
||||
*ret = TAKE_PTR(m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_sock_diag_message_get_unix(sd_netlink_message *m, struct unix_diag_msg *ret) {
|
||||
assert_return(m, -EINVAL);
|
||||
assert_return(m->hdr, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (m->hdr->nlmsg_type != SOCK_DIAG_BY_FAMILY)
|
||||
return -EINVAL;
|
||||
|
||||
if (NLMSG_PAYLOAD(m->hdr, 0) < sizeof(struct unix_diag_msg))
|
||||
return -EBADMSG;
|
||||
|
||||
/* Reads out the fixed-size unix_diag_msg header that precedes the attributes in a reply. There's no
|
||||
* sd-netlink attribute for this leading family header, hence we read it directly. */
|
||||
|
||||
*ret = *(struct unix_diag_msg*) NLMSG_DATA(m->hdr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -7,3 +7,5 @@
|
||||
|
||||
int sd_sock_diag_socket_open(sd_netlink **ret);
|
||||
int sd_sock_diag_message_new_unix(sd_netlink *sdnl, sd_netlink_message **ret, ino_t inode, uint64_t cookie, uint32_t show);
|
||||
int sd_sock_diag_message_new_unix_dump(sd_netlink *sdnl, sd_netlink_message **ret, uint32_t states, uint32_t show);
|
||||
int sd_sock_diag_message_get_unix(sd_netlink_message *m, struct unix_diag_msg *ret);
|
||||
|
||||
@@ -18,6 +18,8 @@ static const NLAPolicy sdnl_req_policies[] = {
|
||||
DEFINE_POLICY_SET(sdnl_req);
|
||||
|
||||
static const NLAPolicy unix_diag_msg_policies[] = {
|
||||
[UNIX_DIAG_NAME] = BUILD_POLICY(STRING), /* Note: not NUL-terminated on the wire, read via sd_netlink_message_read_data() */
|
||||
[UNIX_DIAG_VFS] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct unix_diag_vfs)),
|
||||
[UNIX_DIAG_RQLEN] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct unix_diag_rqlen)),
|
||||
};
|
||||
DEFINE_POLICY_SET(unix_diag_msg);
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "varlink-io.systemd.h"
|
||||
#include "varlink-org.varlink.service.h"
|
||||
#include "varlink-util.h"
|
||||
#include "xattr-util.h"
|
||||
|
||||
#define VARLINK_DEFAULT_CONNECTIONS_MAX 4096U
|
||||
#define VARLINK_DEFAULT_CONNECTIONS_PER_UID_MAX 128U
|
||||
@@ -154,6 +155,33 @@ static int varlink_new(sd_varlink **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mark_varlink_socket(int fd, const char *path, const char *role) {
|
||||
int r;
|
||||
|
||||
assert(wildcard_fd_is_valid(fd));
|
||||
assert(role);
|
||||
|
||||
/* We define four roles:
|
||||
*
|
||||
* 1. "client" → this socket was created via socket()+connect() [sockfs]
|
||||
* 2. "server" → this socket was created via accept() [sockfs]
|
||||
* 3. "listen" → this socket was created via socket()+listen() [sockfs]
|
||||
* 4. "entrypoint" → this is the entrypoint socket inode [not sockfs]
|
||||
*/
|
||||
|
||||
r = socket_xattr_supported(); /* Let's check for the feature directly, to make use of the cache */
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
r = xsetxattr(fd, path, AT_EMPTY_PATH, "user.varlink", role);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to set 'user.varlink' to '%s': %m", role);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_varlink_connect_address(sd_varlink **ret, const char *address) {
|
||||
_cleanup_(sd_varlink_unrefp) sd_varlink *v = NULL;
|
||||
int r;
|
||||
@@ -169,14 +197,31 @@ _public_ int sd_varlink_connect_address(sd_varlink **ret, const char *address) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) mark_varlink_socket(v->stream.input_fd, /* path= */ NULL, "client");
|
||||
|
||||
varlink_set_state(v, VARLINK_IDLE_CLIENT);
|
||||
|
||||
*ret = TAKE_PTR(v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_varlink_connect_exec(sd_varlink **ret, const char *_command, char **_argv) {
|
||||
static int varlink_socketpair(int *ret_client_fd, int *ret_server_fd) {
|
||||
assert(ret_client_fd);
|
||||
assert(ret_server_fd);
|
||||
|
||||
_cleanup_close_pair_ int pair[2] = EBADF_PAIR;
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0, pair) < 0)
|
||||
return log_debug_errno(errno, "Failed to allocate AF_UNIX socket pair: %m");
|
||||
|
||||
(void) mark_varlink_socket(pair[0], /* path= */ NULL, "client");
|
||||
(void) mark_varlink_socket(pair[1], /* path= */ NULL, "server");
|
||||
|
||||
*ret_client_fd = TAKE_FD(pair[0]);
|
||||
*ret_server_fd = TAKE_FD(pair[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_varlink_connect_exec(sd_varlink **ret, const char *_command, char **_argv) {
|
||||
_cleanup_(pidref_done_sigkill_wait) PidRef pidref = PIDREF_NULL;
|
||||
_cleanup_free_ char *command = NULL;
|
||||
_cleanup_strv_free_ char **argv = NULL;
|
||||
@@ -200,17 +245,19 @@ _public_ int sd_varlink_connect_exec(sd_varlink **ret, const char *_command, cha
|
||||
|
||||
log_debug("Forking off Varlink child process '%s'.", command);
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0, pair) < 0)
|
||||
return log_debug_errno(errno, "Failed to allocate AF_UNIX socket pair: %m");
|
||||
_cleanup_close_ int client_fd = -EBADF, server_fd = -EBADF;
|
||||
r = varlink_socketpair(&client_fd, &server_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = fd_nonblock(pair[1], false);
|
||||
r = fd_nonblock(server_fd, false);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to disable O_NONBLOCK for varlink socket: %m");
|
||||
|
||||
r = pidref_safe_fork_full(
|
||||
"(sd-vlexec)",
|
||||
/* stdio_fds= */ NULL,
|
||||
/* except_fds= */ (int[]) { pair[1] },
|
||||
/* except_fds= */ (int[]) { server_fd },
|
||||
/* n_except_fds= */ 1,
|
||||
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_PACK_FDS|FORK_CLOEXEC_OFF|FORK_REOPEN_LOG|FORK_DEATHSIG_SIGTERM|FORK_RLIMIT_NOFILE_SAFE,
|
||||
&pidref);
|
||||
@@ -249,14 +296,14 @@ _public_ int sd_varlink_connect_exec(sd_varlink **ret, const char *_command, cha
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
pair[1] = safe_close(pair[1]);
|
||||
server_fd = safe_close(server_fd);
|
||||
|
||||
sd_varlink *v;
|
||||
r = varlink_new(&v);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to create varlink object: %m");
|
||||
|
||||
int conn_fd = TAKE_FD(pair[0]);
|
||||
int conn_fd = TAKE_FD(client_fd);
|
||||
r = json_stream_attach_fds(&v->stream, conn_fd, conn_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -280,7 +327,6 @@ static int ssh_path(const char **ret) {
|
||||
}
|
||||
|
||||
static int varlink_connect_ssh_unix(sd_varlink **ret, const char *where) {
|
||||
_cleanup_close_pair_ int pair[2] = EBADF_PAIR;
|
||||
_cleanup_(pidref_done_sigkill_wait) PidRef pidref = PIDREF_NULL;
|
||||
int r;
|
||||
|
||||
@@ -316,12 +362,14 @@ static int varlink_connect_ssh_unix(sd_varlink **ret, const char *where) {
|
||||
|
||||
log_debug("Forking off SSH child process '%s -W %s %s'.", ssh, p, h);
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0, pair) < 0)
|
||||
return log_debug_errno(errno, "Failed to allocate AF_UNIX socket pair: %m");
|
||||
_cleanup_close_ int client_fd = -EBADF, server_fd = -EBADF;
|
||||
r = varlink_socketpair(&client_fd, &server_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = pidref_safe_fork_full(
|
||||
"(sd-vlssh)",
|
||||
/* stdio_fds= */ (int[]) { pair[1], pair[1], STDERR_FILENO },
|
||||
/* stdio_fds= */ (int[]) { server_fd, server_fd, STDERR_FILENO },
|
||||
/* except_fds= */ NULL,
|
||||
/* n_except_fds= */ 0,
|
||||
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REOPEN_LOG|FORK_RLIMIT_NOFILE_SAFE|FORK_REARRANGE_STDIO,
|
||||
@@ -336,14 +384,14 @@ static int varlink_connect_ssh_unix(sd_varlink **ret, const char *where) {
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
pair[1] = safe_close(pair[1]);
|
||||
server_fd = safe_close(server_fd);
|
||||
|
||||
sd_varlink *v;
|
||||
r = varlink_new(&v);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to create varlink object: %m");
|
||||
|
||||
int conn_fd = TAKE_FD(pair[0]);
|
||||
int conn_fd = TAKE_FD(client_fd);
|
||||
r = json_stream_attach_fds(&v->stream, conn_fd, conn_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -562,6 +610,8 @@ _public_ int sd_varlink_connect_fd_pair(sd_varlink **ret, int input_fd, int outp
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) mark_varlink_socket(v->stream.input_fd, /* path= */ NULL, "client");
|
||||
|
||||
if (override_ucred)
|
||||
json_stream_set_peer_ucred(&v->stream, override_ucred);
|
||||
|
||||
@@ -3283,6 +3333,8 @@ _public_ int sd_varlink_server_add_connection_pair(
|
||||
}
|
||||
}
|
||||
|
||||
(void) mark_varlink_socket(input_fd, /* path= */ NULL, "server");
|
||||
|
||||
if (ret)
|
||||
*ret = v;
|
||||
|
||||
@@ -3394,10 +3446,19 @@ _public_ int sd_varlink_server_listen_fd(sd_varlink_server *s, int fd) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) mark_varlink_socket(ss->fd, /* path= */ NULL, "listen");
|
||||
|
||||
LIST_PREPEND(sockets, s->sockets, TAKE_PTR(ss));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static mode_t default_listen_mode(sd_varlink_server_flags_t flags) {
|
||||
/* NB: we use 0644 rather than 0600 here, because it's the "w" flag that controls connect()
|
||||
* privileges, but leaving the "r" flag on allows others to read our xattrs, which is good because it
|
||||
* makes our sockets recognizable as varlink, even if not connectible. */
|
||||
return (flags & (SD_VARLINK_SERVER_ROOT_ONLY|SD_VARLINK_SERVER_MYSELF_ONLY)) != 0 ? 0644 : 0666;
|
||||
}
|
||||
|
||||
_public_ int sd_varlink_server_listen_address(sd_varlink_server *s, const char *address, mode_t m) {
|
||||
_cleanup_(varlink_server_socket_freep) VarlinkServerSocket *ss = NULL;
|
||||
union sockaddr_union sockaddr;
|
||||
@@ -3407,6 +3468,12 @@ _public_ int sd_varlink_server_listen_address(sd_varlink_server *s, const char *
|
||||
|
||||
assert_return(s, -EINVAL);
|
||||
assert_return(address, -EINVAL);
|
||||
|
||||
/* NB: we resolve m being MODE_INVALID before checking SD_VARLINK_SERVER_MODE_MKDIR_0755, since that
|
||||
* flag is not defined for MODE_INVALID (if we'd check we'd see it always set...) */
|
||||
if (m == MODE_INVALID)
|
||||
m = default_listen_mode(s->flags);
|
||||
|
||||
assert_return((m & ~(0777|SD_VARLINK_SERVER_MODE_MKDIR_0755)) == 0, -EINVAL);
|
||||
|
||||
/* Validate that the definition of our flag doesn't collide with the official mode_t bits. Thankfully
|
||||
@@ -3431,6 +3498,8 @@ _public_ int sd_varlink_server_listen_address(sd_varlink_server *s, const char *
|
||||
|
||||
fd = fd_move_above_stdio(fd);
|
||||
|
||||
(void) mark_varlink_socket(fd, /* path= */ NULL, "listen");
|
||||
|
||||
/* See the comment in sd_varlink_server_listen_fd() */
|
||||
if (FLAGS_SET(s->flags, SD_VARLINK_SERVER_FD_PASSING_INPUT_STRICT))
|
||||
(void) setsockopt_int(fd, SOL_SOCKET, SO_PASSRIGHTS, FLAGS_SET(s->flags, SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT));
|
||||
@@ -3442,6 +3511,9 @@ _public_ int sd_varlink_server_listen_address(sd_varlink_server *s, const char *
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (path_is_absolute(address))
|
||||
(void) mark_varlink_socket(AT_FDCWD, address, "entrypoint");
|
||||
|
||||
if (listen(fd, SOMAXCONN_DELUXE) < 0)
|
||||
return -errno;
|
||||
|
||||
@@ -3577,7 +3649,7 @@ _public_ int sd_varlink_server_listen_auto(sd_varlink_server *s) {
|
||||
if (streq(e, "-"))
|
||||
r = sd_varlink_server_add_connection_stdio(s, /* ret= */ NULL);
|
||||
else
|
||||
r = sd_varlink_server_listen_address(s, e, FLAGS_SET(s->flags, SD_VARLINK_SERVER_ROOT_ONLY) ? 0600 : 0666);
|
||||
r = sd_varlink_server_listen_address(s, e, default_listen_mode(s->flags));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -224,6 +224,58 @@ static int bus_append_strv_colon(sd_bus_message *m, const char *field, const cha
|
||||
return bus_append_strv_full(m, field, eq, ":" WHITESPACE, EXTRACT_UNQUOTE);
|
||||
}
|
||||
|
||||
static int bus_append_xattr(sd_bus_message *m, const char *field, const char *eq) {
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(field);
|
||||
|
||||
/* Sends an extended attribute assignment of the form "name=value" as an a(ss) array of one
|
||||
* name/value pair (or as an empty array if the value is empty, which resets the list). */
|
||||
|
||||
r = sd_bus_message_open_container(m, 'r', "sv");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_append_basic(m, 's', field);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_open_container(m, 'v', "a(ss)");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_open_container(m, 'a', "(ss)");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
if (!isempty(eq)) {
|
||||
_cleanup_free_ char *name = NULL, *value = NULL;
|
||||
|
||||
r = split_pair(eq, "=", &name, &value);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse extended attribute expression '%s': %m", eq);
|
||||
|
||||
r = sd_bus_message_append(m, "(ss)", name, value);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
}
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int bus_append_byte_array(sd_bus_message *m, const char *field, const void *buf, size_t n) {
|
||||
int r;
|
||||
|
||||
@@ -2786,6 +2838,9 @@ static const BusProperty socket_properties[] = {
|
||||
{ "Timestamping", bus_append_string },
|
||||
{ "DeferTrigger", bus_append_string },
|
||||
{ "Symlinks", bus_append_strv },
|
||||
{ "XAttrEntryPoint", bus_append_xattr },
|
||||
{ "XAttrListen", bus_append_xattr },
|
||||
{ "XAttrAccept", bus_append_xattr },
|
||||
{ "SocketProtocol", bus_append_parse_ip_protocol },
|
||||
{ "ListenStream", bus_append_listen },
|
||||
{ "ListenDatagram", bus_append_listen },
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
#include "socket-label.h"
|
||||
#include "socket-util.h"
|
||||
#include "string-table.h"
|
||||
#include "strv.h"
|
||||
#include "umask-util.h"
|
||||
#include "xattr-util.h"
|
||||
|
||||
static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = {
|
||||
[SOCKET_ADDRESS_DEFAULT] = "default",
|
||||
@@ -36,6 +38,32 @@ SocketAddressBindIPv6Only socket_address_bind_ipv6_only_or_bool_from_string(cons
|
||||
return socket_address_bind_ipv6_only_from_string(s);
|
||||
}
|
||||
|
||||
int socket_set_xattrs(int fd, const char *path, char **xattrs) {
|
||||
int r;
|
||||
|
||||
assert(wildcard_fd_is_valid(fd));
|
||||
|
||||
if (strv_isempty(xattrs))
|
||||
return 0;
|
||||
|
||||
r = socket_xattr_supported();
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
int c = 0;
|
||||
STRV_FOREACH_PAIR(name, value, xattrs) {
|
||||
r = xsetxattr(fd, path, AT_EMPTY_PATH, *name, *value);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to set extended attribute '%s' on socket, ignoring: %m", *name);
|
||||
continue;
|
||||
}
|
||||
|
||||
c++;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
int socket_address_listen(
|
||||
const SocketAddress *a,
|
||||
int flags,
|
||||
@@ -48,7 +76,9 @@ int socket_address_listen(
|
||||
mode_t directory_mode,
|
||||
mode_t socket_mode,
|
||||
const char *selinux_label,
|
||||
const char *smack_label) {
|
||||
const char *smack_label,
|
||||
char **xattr_entrypoint,
|
||||
char **xattr_listen) {
|
||||
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
const char *p;
|
||||
@@ -119,6 +149,8 @@ int socket_address_listen(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) socket_set_xattrs(fd, /* path= */ NULL, xattr_listen);
|
||||
|
||||
p = socket_address_get_path(a);
|
||||
if (p) {
|
||||
/* Create parents */
|
||||
@@ -138,11 +170,14 @@ int socket_address_listen(
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (smack_label) {
|
||||
r = mac_smack_apply(p, SMACK_ATTR_ACCESS, smack_label);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to apply SMACK label for socket path, ignoring: %m");
|
||||
}
|
||||
|
||||
(void) socket_set_xattrs(AT_FDCWD, p, xattr_entrypoint);
|
||||
} else {
|
||||
if (bind(fd, &a->sockaddr.sa, a->size) < 0)
|
||||
return -errno;
|
||||
|
||||
@@ -26,4 +26,8 @@ int socket_address_listen(
|
||||
mode_t directory_mode,
|
||||
mode_t socket_mode,
|
||||
const char *selinux_label,
|
||||
const char *smack_label);
|
||||
const char *smack_label,
|
||||
char **xattr_entrypoint,
|
||||
char **xattr_listen);
|
||||
|
||||
int socket_set_xattrs(int fd, const char *path, char **xattrs);
|
||||
|
||||
@@ -198,7 +198,9 @@ int make_socket_fd(int log_level, const char* address, int type, int flags) {
|
||||
0755,
|
||||
0644,
|
||||
/* selinux_label= */ NULL,
|
||||
/* smack_label= */ NULL);
|
||||
/* smack_label= */ NULL,
|
||||
/* xattr_entrypoint= */ NULL,
|
||||
/* xattr_listen= */ NULL);
|
||||
if (fd < 0 || log_get_max_level() >= log_level) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
|
||||
|
||||
@@ -1467,6 +1467,25 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
|
||||
|
||||
return 1;
|
||||
|
||||
} else if (contents[0] == SD_BUS_TYPE_STRUCT_BEGIN &&
|
||||
STR_IN_SET(name, "XAttrEntryPoint", "XAttrListen", "XAttrAccept")) {
|
||||
const char *xname, *xvalue;
|
||||
|
||||
r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)");
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
while ((r = sd_bus_message_read(m, "(ss)", &xname, &xvalue)) > 0)
|
||||
bus_print_property_valuef(name, expected_value, flags, "%s=%s", xname, xvalue);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
r = sd_bus_message_exit_container(m);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
return 1;
|
||||
|
||||
} else if (contents[0] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "TimersMonotonic")) {
|
||||
const char *base;
|
||||
uint64_t v, next_elapse;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "bpf-restrict-fsaccess.h"
|
||||
#include "devnum-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "log.h"
|
||||
#include "string-util.h"
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <grp.h>
|
||||
#include <linux/pkt_sched.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
@@ -550,4 +551,16 @@ TEST(tos_to_priority) {
|
||||
ASSERT_EQ(tos_to_priority(0xff), TC_PRIO_CONTROL);
|
||||
}
|
||||
|
||||
TEST(socket_xattr_supported) {
|
||||
int r;
|
||||
|
||||
r = socket_xattr_supported();
|
||||
ASSERT_OK(r);
|
||||
|
||||
log_info("Extended attributes on socket inodes supported: %s", yes_no(r));
|
||||
|
||||
/* A second call must agree with the first. */
|
||||
ASSERT_EQ(socket_xattr_supported(), r);
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
||||
|
||||
@@ -189,7 +189,7 @@ int manager_start_varlink_server(Manager *manager, int fd) {
|
||||
return log_error_errno(r, "Failed to attach Varlink connection to event loop: %m");
|
||||
|
||||
if (fd < 0)
|
||||
r = sd_varlink_server_listen_address(v, UDEV_VARLINK_ADDRESS, 0600);
|
||||
r = sd_varlink_server_listen_address(v, UDEV_VARLINK_ADDRESS, 0644);
|
||||
else
|
||||
r = sd_varlink_server_listen_fd(v, fd);
|
||||
if (r < 0)
|
||||
|
||||
+193
-13
@@ -1,24 +1,33 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <linux/unix_diag.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sd-daemon.h"
|
||||
#include "sd-netlink.h"
|
||||
#include "sd-varlink.h"
|
||||
|
||||
#include "build.h"
|
||||
#include "bus-util.h"
|
||||
#include "chase.h"
|
||||
#include "devnum-util.h"
|
||||
#include "env-util.h"
|
||||
#include "errno-list.h"
|
||||
#include "errno-util.h"
|
||||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "format-table.h"
|
||||
#include "format-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "help-util.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
#include "memfd-util.h"
|
||||
#include "netlink-sock-diag.h"
|
||||
#include "options.h"
|
||||
#include "pager.h"
|
||||
#include "parse-argument.h"
|
||||
@@ -32,6 +41,7 @@
|
||||
#include "recurse-dir.h"
|
||||
#include "runtime-scope.h"
|
||||
#include "socket-forward.h"
|
||||
#include "socket-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "terminal-util.h"
|
||||
@@ -40,6 +50,7 @@
|
||||
#include "varlink-util.h"
|
||||
#include "verbs.h"
|
||||
#include "version.h"
|
||||
#include "xattr-util.h"
|
||||
|
||||
typedef struct PushFds {
|
||||
int *fds;
|
||||
@@ -71,14 +82,9 @@ STATIC_DESTRUCTOR_REGISTER(arg_graceful, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_push_fds, push_fds_done);
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
_cleanup_(table_unrefp) Table *options = NULL, *verbs = NULL;
|
||||
int r;
|
||||
|
||||
r = terminal_urlify_man("varlinkctl", "1", &link);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = option_parser_get_help_table(&options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -91,18 +97,16 @@ static int help(void) {
|
||||
|
||||
pager_open(arg_pager_flags);
|
||||
|
||||
printf("%s [OPTIONS...] COMMAND ...\n\n"
|
||||
"%sIntrospect Varlink Services.%s\n"
|
||||
"\nCommands:\n",
|
||||
program_invocation_short_name,
|
||||
ansi_highlight(),
|
||||
ansi_normal());
|
||||
help_cmdline("[OPTIONS...] COMMAND ...");
|
||||
help_abstract("Introspect Varlink Services.");
|
||||
|
||||
help_section("Commands");
|
||||
|
||||
r = table_print_or_warn(verbs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
printf("\nOptions:\n");
|
||||
help_section("Options");
|
||||
|
||||
r = table_print_or_warn(options);
|
||||
if (r < 0)
|
||||
@@ -112,7 +116,7 @@ static int help(void) {
|
||||
" %s --exec call ADDRESS METHOD PARAMS -- CMDLINE…\n",
|
||||
program_invocation_short_name);
|
||||
|
||||
printf("\nSee the %s for details.\n", link);
|
||||
help_man_page_reference("varlinkctl", "1");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1159,6 +1163,182 @@ static int verb_list_registry(int argc, char *argv[], uintptr_t _data, void *use
|
||||
return 0;
|
||||
}
|
||||
|
||||
VERB_NOARG(verb_list_sockets, "list-sockets", "List listening Varlink entrypoint sockets");
|
||||
static int verb_list_sockets(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
int r;
|
||||
|
||||
assert(argc <= 1);
|
||||
|
||||
/* Enumerates listening, file-system bound AF_UNIX SOCK_STREAM sockets via the sock_diag netlink API,
|
||||
* and lists those that are marked as Varlink entrypoints (i.e. carry the "user.varlink" xattr set to
|
||||
* "entrypoint"). */
|
||||
|
||||
r = socket_xattr_supported();
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to check if S_IFSOCK inodes support xattrs: %m");
|
||||
if (r == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "This kernel does not support extended attributes on socket inodes, cannot enumerate Varlink sockets.");
|
||||
|
||||
_cleanup_(sd_netlink_unrefp) sd_netlink *nl = NULL;
|
||||
r = sd_sock_diag_socket_open(&nl);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open sock_diag netlink socket: %m");
|
||||
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
|
||||
r = sd_sock_diag_message_new_unix_dump(nl, &req, 1U << TCP_LISTEN, UDIAG_SHOW_NAME|UDIAG_SHOW_VFS);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to allocate AF_UNIX socket dump request: %m");
|
||||
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *reply = NULL;
|
||||
r = sd_netlink_call(nl, req, /* timeout= */ 0, &reply);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to issue AF_UNIX socket dump: %m");
|
||||
|
||||
_cleanup_(table_unrefp) Table *table = table_new("path", "access");
|
||||
if (!table)
|
||||
return log_oom();
|
||||
|
||||
(void) table_set_sort(table, (size_t) 0);
|
||||
|
||||
for (sd_netlink_message *m = reply; m; m = sd_netlink_message_next(m)) {
|
||||
|
||||
r = sd_netlink_message_get_errno(m);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Error in AF_UNIX socket dump entry, ignoring: %m");
|
||||
continue;
|
||||
}
|
||||
|
||||
struct unix_diag_msg udm;
|
||||
r = sd_sock_diag_message_get_unix(m, &udm);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Failed to read AF_UNIX socket dump header, ignoring: %m");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We only care about listening stream sockets. The kernel already filtered by state, but
|
||||
* there's no way to filter by type in the request, so we do that here (and double check the
|
||||
* state for good measure). */
|
||||
if (udm.udiag_type != SOCK_STREAM)
|
||||
continue;
|
||||
if (udm.udiag_state != TCP_LISTEN)
|
||||
continue;
|
||||
|
||||
/* Read the bound name. This is not NUL terminated on the wire, hence read it as raw data. */
|
||||
_cleanup_free_ void *name = NULL;
|
||||
size_t name_size = 0;
|
||||
r = sd_netlink_message_read_data(m, UNIX_DIAG_NAME, &name_size, &name);
|
||||
if (r == -ENODATA) /* unnamed socket */
|
||||
continue;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read AF_UNIX socket name: %m");
|
||||
|
||||
/* Safely turn the raw, not necessarily NUL-terminated, name into a C string. This also
|
||||
* rejects any name with embedded NUL bytes. */
|
||||
_cleanup_free_ char *path = NULL;
|
||||
r = make_cstring(name, name_size, MAKE_CSTRING_ALLOW_TRAILING_NUL, &path);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to convert AF_UNIX socket name to string, skipping: %m");
|
||||
continue;
|
||||
}
|
||||
if (!path_is_absolute(path)) {
|
||||
log_debug("Got non-absolute AF_UNIX socket path '%s', skipping.", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* The kernel also reports the backing VFS inode/device, but only for file-system bound
|
||||
* sockets. We require it, both as a filter and to validate the path below. */
|
||||
_cleanup_free_ void *vfs = NULL;
|
||||
size_t vfs_size = 0;
|
||||
r = sd_netlink_message_read_data(m, UNIX_DIAG_VFS, &vfs_size, &vfs);
|
||||
if (r == -ENODATA)
|
||||
continue; /* not fs bound */
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read AF_UNIX socket VFS data: %m");
|
||||
if (vfs_size != sizeof(struct unix_diag_vfs)) {
|
||||
log_warning("Got AF_UNIX socket VFS data of unexpected size, skipping.");
|
||||
continue;
|
||||
}
|
||||
const struct unix_diag_vfs *uv = vfs;
|
||||
|
||||
/* Validate the path the kernel reported: open it (without following a final-component
|
||||
* symlink), and verify it really is a socket whose inode and backing device match what
|
||||
* netlink told us. This guards against the path having been unlinked/replaced in the
|
||||
* meantime, so that we only ever read xattrs off the right inode. */
|
||||
_cleanup_close_ int fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
|
||||
if (fd < 0) {
|
||||
log_debug_errno(errno, "Failed to open reported AF_UNIX socket path '%s', skipping: %m", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) < 0) {
|
||||
log_debug_errno(errno, "Failed to stat reported AF_UNIX socket path '%s', skipping: %m", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!S_ISSOCK(st.st_mode)) {
|
||||
log_debug("Reported AF_UNIX socket path '%s' is not a socket, skipping.", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* the unix_diag_vfs structure only gives us 32bit inode numbers, which it truncates. hence lets truncate the value before comparison */
|
||||
if (((st.st_ino ^ uv->udiag_vfs_ino) & UINT32_MAX) != 0) {
|
||||
log_debug("Inode of reported AF_UNIX socket path '%s' does not match netlink data, skipping.", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* udiag_vfs_dev carries the kernel-internal dev_t encoding, which differs from the userspace
|
||||
* dev_t in st_dev — hence translate before comparing. */
|
||||
if (STAT_DEV_TO_KERNEL(st.st_dev) != uv->udiag_vfs_dev) {
|
||||
log_debug("Backing device of reported AF_UNIX socket path '%s' does not match netlink data, skipping.", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* The path is validated now, hence we may safely read the Varlink role xattr off the fd. We
|
||||
* only list sockets that are marked as Varlink entrypoints. */
|
||||
_cleanup_free_ char *role = NULL;
|
||||
r = fgetxattr_malloc(fd, "user.varlink", &role, /* ret_size= */ NULL);
|
||||
if (r < 0) {
|
||||
if (!ERRNO_IS_NEG_XATTR_ABSENT(r))
|
||||
log_debug_errno(r, "Failed to read 'user.varlink' xattr of '%s', skipping: %m", path);
|
||||
continue;
|
||||
}
|
||||
if (!streq(role, "entrypoint"))
|
||||
continue;
|
||||
|
||||
_cleanup_free_ char *no = NULL;
|
||||
r = access_fd(fd, W_OK);
|
||||
if (r < 0) {
|
||||
no = strjoin("no (", ERRNO_NAME(r), ")");
|
||||
if (!no)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
r = table_add_many(
|
||||
table,
|
||||
TABLE_PATH, path,
|
||||
TABLE_STRING, no ?: "yes",
|
||||
TABLE_SET_COLOR, ansi_highlight_green_red(!no));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!table_isempty(table) || sd_json_format_enabled(arg_json_format_flags)) {
|
||||
r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, /* show_header= */ true);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to output table: %m");
|
||||
}
|
||||
|
||||
if (arg_legend && !sd_json_format_enabled(arg_json_format_flags)) {
|
||||
if (table_isempty(table))
|
||||
printf("No sockets found.\n");
|
||||
else
|
||||
printf("\n%zu entrypoint sockets listed.\n", table_get_rows(table) - 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Build a minimal IDL from a qualified method name so that introspection works. The parsed interface is
|
||||
* returned to the caller who must keep it alive for the lifetime of the server
|
||||
* (sd_varlink_server_add_interface() borrows the pointer). */
|
||||
|
||||
@@ -551,7 +551,7 @@ int vmspawn_varlink_setup(
|
||||
if (!listen_address)
|
||||
return log_oom();
|
||||
|
||||
r = sd_varlink_server_listen_address(ctx->varlink_server, listen_address, 0600);
|
||||
r = sd_varlink_server_listen_address(ctx->varlink_server, listen_address, 0644);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to listen on %s: %m", listen_address);
|
||||
|
||||
|
||||
Executable
+108
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
# shellcheck source=test/units/util.sh
|
||||
. "$(dirname "$0")"/util.sh
|
||||
|
||||
# Extended attributes on socket inodes (and hence the XAttr*= socket settings and
|
||||
# "varlinkctl list-sockets") require a sufficiently new kernel. Probe for actual
|
||||
# support and skip if unavailable, since the kernel version alone is not a reliable
|
||||
# indicator.
|
||||
if ! socket_inode_supports_user_xattrs; then
|
||||
echo "Socket inode extended attributes unsupported on this kernel, skipping." >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
UNIT_SOCKET_PATH="/run/test-socket-xattr.sock"
|
||||
RUN_SOCKET_PATH="/run/test-socket-xattr-run.sock"
|
||||
|
||||
at_exit() {
|
||||
set +e
|
||||
systemctl stop test-socket-xattr.socket test-socket-xattr-run.socket
|
||||
systemctl reset-failed test-socket-xattr-run.socket test-socket-xattr-run.service
|
||||
rm -f /run/systemd/system/test-socket-xattr.socket
|
||||
rm -f /run/systemd/system/test-socket-xattr.service
|
||||
systemctl daemon-reload
|
||||
}
|
||||
trap at_exit EXIT
|
||||
|
||||
# Read the single user.varlink extended attribute value off the given path.
|
||||
read_role() {
|
||||
getfattr --absolute-names --only-values --name=user.varlink "$1"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 1) XAttr*= configured in a unit file on disk
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# A matching .service unit is required to be able to start the .socket unit. It is
|
||||
# never actually triggered here (we only check the listening socket), so a trivial
|
||||
# service is sufficient.
|
||||
cat >/run/systemd/system/test-socket-xattr.service <<EOF
|
||||
[Service]
|
||||
ExecStart=/bin/true
|
||||
EOF
|
||||
|
||||
cat >/run/systemd/system/test-socket-xattr.socket <<EOF
|
||||
[Unit]
|
||||
Description=Socket xattr test (on-disk unit)
|
||||
|
||||
[Socket]
|
||||
ListenStream=$UNIT_SOCKET_PATH
|
||||
SocketMode=0666
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
RemoveOnStop=true
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl start test-socket-xattr.socket
|
||||
|
||||
# The socket node bound into the file system must carry the entrypoint xattr.
|
||||
test -S "$UNIT_SOCKET_PATH"
|
||||
[[ "$(read_role "$UNIT_SOCKET_PATH")" == "entrypoint" ]]
|
||||
|
||||
# The configured settings must be exposed again via the manager's D-Bus properties.
|
||||
systemctl show -P XAttrEntryPoint test-socket-xattr.socket | grep "user.varlink=entrypoint" >/dev/null
|
||||
systemctl show -P XAttrListen test-socket-xattr.socket | grep "user.varlink=listen" >/dev/null
|
||||
systemctl show -P XAttrAccept test-socket-xattr.socket | grep "user.varlink=server" >/dev/null
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 2) XAttr*= set via "systemd-run --socket-property="
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
rm -f "$RUN_SOCKET_PATH"
|
||||
|
||||
# systemd-run synthesizes the matching test-socket-xattr-run.service for us.
|
||||
systemd-run \
|
||||
--unit=test-socket-xattr-run \
|
||||
--service-type=oneshot \
|
||||
--remain-after-exit \
|
||||
--socket-property=ListenStream="$RUN_SOCKET_PATH" \
|
||||
--socket-property=SocketMode=0666 \
|
||||
--socket-property=XAttrEntryPoint=user.varlink=entrypoint \
|
||||
--socket-property=XAttrListen=user.varlink=listen \
|
||||
--socket-property=XAttrAccept=user.varlink=server \
|
||||
--socket-property=RemoveOnStop=true \
|
||||
true
|
||||
|
||||
systemctl cat test-socket-xattr-run.socket
|
||||
|
||||
# The XAttr*= settings must have been serialized into the transient socket unit.
|
||||
grep "^XAttrEntryPoint=user.varlink=entrypoint$" /run/systemd/transient/test-socket-xattr-run.socket >/dev/null
|
||||
grep "^XAttrListen=user.varlink=listen$" /run/systemd/transient/test-socket-xattr-run.socket >/dev/null
|
||||
grep "^XAttrAccept=user.varlink=server$" /run/systemd/transient/test-socket-xattr-run.socket >/dev/null
|
||||
|
||||
# And the transient socket must validate cleanly.
|
||||
systemd-analyze verify --recursive-errors=no "/run/systemd/transient/test-socket-xattr-run.socket"
|
||||
|
||||
# Wait for the transient socket to be bound, then check the entrypoint xattr.
|
||||
test -S "$RUN_SOCKET_PATH"
|
||||
[[ "$(read_role "$RUN_SOCKET_PATH")" == "entrypoint" ]]
|
||||
|
||||
systemctl show -P XAttrEntryPoint test-socket-xattr-run.socket | grep "user.varlink=entrypoint" >/dev/null
|
||||
systemctl show -P XAttrListen test-socket-xattr-run.socket | grep "user.varlink=listen" >/dev/null
|
||||
systemctl show -P XAttrAccept test-socket-xattr-run.socket | grep "user.varlink=server" >/dev/null
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
# shellcheck source=test/units/util.sh
|
||||
. "$(dirname "$0")"/util.sh
|
||||
|
||||
# "varlinkctl list-sockets" relies on extended attributes on socket inodes, which
|
||||
# require a sufficiently new kernel. Probe for actual support and skip if
|
||||
# unavailable, since the kernel version alone is not a reliable indicator.
|
||||
if ! socket_inode_supports_user_xattrs; then
|
||||
echo "Socket inode extended attributes unsupported on this kernel, skipping." >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ENTRYPOINT_PATH="/run/test-list-sockets-entrypoint.sock"
|
||||
PLAIN_PATH="/run/test-list-sockets-plain.sock"
|
||||
|
||||
at_exit() {
|
||||
set +e
|
||||
systemctl stop test-list-sockets-entrypoint.socket
|
||||
systemctl reset-failed test-list-sockets-entrypoint.socket test-list-sockets-entrypoint.service
|
||||
systemctl stop test-list-sockets-plain.socket
|
||||
systemctl reset-failed test-list-sockets-plain.socket test-list-sockets-plain.service
|
||||
rm -f "$ENTRYPOINT_PATH" "$PLAIN_PATH"
|
||||
}
|
||||
trap at_exit EXIT
|
||||
|
||||
rm -f "$ENTRYPOINT_PATH" "$PLAIN_PATH"
|
||||
|
||||
# A listening socket tagged as a Varlink entrypoint.
|
||||
systemd-run \
|
||||
--unit=test-list-sockets-entrypoint \
|
||||
--service-type=oneshot \
|
||||
--remain-after-exit \
|
||||
--socket-property=ListenStream="$ENTRYPOINT_PATH" \
|
||||
--socket-property=SocketMode=0666 \
|
||||
--socket-property=XAttrEntryPoint=user.varlink=entrypoint \
|
||||
--socket-property=RemoveOnStop=true \
|
||||
true
|
||||
|
||||
# A listening socket *without* the entrypoint marker, which must be ignored.
|
||||
systemd-run \
|
||||
--unit=test-list-sockets-plain \
|
||||
--service-type=oneshot \
|
||||
--remain-after-exit \
|
||||
--socket-property=ListenStream="$PLAIN_PATH" \
|
||||
--socket-property=SocketMode=0666 \
|
||||
--socket-property=RemoveOnStop=true \
|
||||
true
|
||||
|
||||
test -S "$ENTRYPOINT_PATH"
|
||||
test -S "$PLAIN_PATH"
|
||||
|
||||
# Plain text output should run cleanly and mention the entrypoint socket.
|
||||
varlinkctl list-sockets
|
||||
varlinkctl list-sockets | grep "$ENTRYPOINT_PATH" >/dev/null
|
||||
|
||||
# JSON output is an array of {path, access} objects. The entrypoint socket must be
|
||||
# present...
|
||||
json="$(varlinkctl --json=short list-sockets)"
|
||||
echo "$json" | jq -e --arg p "$ENTRYPOINT_PATH" 'any(.[]; .path == $p)' >/dev/null
|
||||
# ...and carry an "access" field (either "yes" or "No (…)").
|
||||
echo "$json" | jq -e --arg p "$ENTRYPOINT_PATH" 'any(.[]; .path == $p and (.access | type == "string"))' >/dev/null
|
||||
|
||||
# The socket without the entrypoint xattr must NOT be listed.
|
||||
(! echo "$json" | jq -e --arg p "$PLAIN_PATH" 'any(.[]; .path == $p)' >/dev/null)
|
||||
|
||||
# Stopping the socket unit must make the entrypoint disappear from the listing again.
|
||||
systemctl stop test-list-sockets-entrypoint.socket
|
||||
test ! -S "$ENTRYPOINT_PATH"
|
||||
(! varlinkctl --json=short list-sockets | jq -e --arg p "$ENTRYPOINT_PATH" 'any(.[]; .path == $p)' >/dev/null)
|
||||
|
||||
systemctl stop test-list-sockets-plain.socket
|
||||
test ! -S "$PLAIN_PATH"
|
||||
@@ -251,6 +251,24 @@ cgroupfs_supports_user_xattrs() {
|
||||
[[ "$(getfattr --name="$xattr" --absolute-names --only-values /sys/fs/cgroup)" -eq 254 ]]
|
||||
}
|
||||
|
||||
socket_inode_supports_user_xattrs() {
|
||||
local socket xattr
|
||||
|
||||
# The XAttr*= socket settings and "varlinkctl list-sockets" rely on extended
|
||||
# attributes on socket inodes. This needs a sufficiently new kernel, but the
|
||||
# kernel version alone is not a reliable indicator: some kernels that report
|
||||
# >= 7.0 still reject user.* xattrs on socket inodes (with EPERM/EOPNOTSUPP).
|
||||
# Hence probe for actual support, mirroring socket_xattr_supported() in the
|
||||
# C code, by binding a throwaway socket and trying to tag it.
|
||||
socket="$(mktemp -u /run/socket-xattr-probe.XXXXXX.sock)"
|
||||
xattr="user.probe_$RANDOM"
|
||||
# shellcheck disable=SC2064
|
||||
trap "rm -f '$socket'" RETURN
|
||||
|
||||
python3 -c 'import socket, sys; socket.socket(socket.AF_UNIX).bind(sys.argv[1])' "$socket" || return 1
|
||||
setfattr --name="$xattr" --value=1 "$socket" 2>/dev/null
|
||||
}
|
||||
|
||||
tpm_has_pcr() {
|
||||
local algorithm="${1:?}"
|
||||
local pcr="${2:?}"
|
||||
|
||||
@@ -20,3 +20,6 @@ FileDescriptorName=varlink
|
||||
SocketMode=0666
|
||||
Accept=yes
|
||||
MaxConnectionsPerSource=16
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
|
||||
@@ -18,6 +18,9 @@ Before=sockets.target
|
||||
ListenStream=/run/systemd/io.systemd.BootControl
|
||||
Symlinks=/run/varlink/registry/io.systemd.BootControl
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0600
|
||||
SocketMode=0644
|
||||
Accept=yes
|
||||
MaxConnectionsPerSource=16
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
|
||||
@@ -20,3 +20,6 @@ FileDescriptorName=varlink
|
||||
SocketMode=0666
|
||||
Accept=yes
|
||||
MaxConnectionsPerSource=16
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
|
||||
@@ -20,6 +20,9 @@ FileDescriptorName=varlink
|
||||
SocketMode=0666
|
||||
Accept=yes
|
||||
MaxConnectionsPerSource=16
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
|
||||
@@ -21,3 +21,5 @@ ListenStream=/run/systemd/io.systemd.Hostname
|
||||
Symlinks=/run/varlink/registry/io.systemd.Hostname
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0666
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
|
||||
@@ -20,3 +20,6 @@ SocketMode=0666
|
||||
Accept=yes
|
||||
MaxConnectionsPerSource=16
|
||||
RemoveOnStop=yes
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
|
||||
@@ -21,6 +21,9 @@ SocketMode=0666
|
||||
Accept=yes
|
||||
MaxConnectionsPerSource=16
|
||||
RemoveOnStop=yes
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
|
||||
# Note that this is typically pulled in automatically by
|
||||
# systemd-imds-generator, but you can also enable it manually if you like.
|
||||
|
||||
@@ -23,3 +23,5 @@ ListenStream=/run/systemd/io.systemd.Import
|
||||
Symlinks=/run/varlink/registry/io.systemd.Import
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0666
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
|
||||
@@ -19,6 +19,9 @@ ListenStream=/run/systemd/io.systemd.JournalAccess
|
||||
Symlinks=/run/varlink/registry/io.systemd.JournalAccess
|
||||
FileDescriptorName=varlink
|
||||
SocketGroup=systemd-journal
|
||||
SocketMode=0660
|
||||
SocketMode=0664
|
||||
Accept=yes
|
||||
MaxConnectionsPerSource=16
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
|
||||
@@ -16,3 +16,5 @@ StopWhenUnneeded=yes
|
||||
Service=systemd-journald@%i.service
|
||||
ListenStream=/run/systemd/journal.%i/io.systemd.journal
|
||||
SocketMode=0666
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
|
||||
@@ -17,3 +17,5 @@ Symlinks=/run/varlink/registry/io.systemd.Login /run/varlink/registry/io.systemd
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0666
|
||||
Service=systemd-logind.service
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
|
||||
@@ -16,3 +16,5 @@ ListenStream=/run/systemd/machine/io.systemd.Machine
|
||||
Symlinks=/run/systemd/machine/io.systemd.MachineImage /run/varlink/registry/io.systemd.Machine /run/varlink/registry/io.systemd.MachineImage
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0666
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
|
||||
@@ -20,6 +20,8 @@ ListenStream=/run/systemd/io.systemd.MountFileSystem
|
||||
Symlinks=/run/varlink/registry/io.systemd.MountFileSystem
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0666
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
|
||||
@@ -19,6 +19,9 @@ Before=shutdown.target
|
||||
ListenStream=/run/systemd/io.systemd.MuteConsole
|
||||
Symlinks=/run/varlink/registry/io.systemd.MuteConsole
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0600
|
||||
SocketMode=0644
|
||||
Accept=yes
|
||||
MaxConnectionsPerSource=16
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
|
||||
@@ -22,6 +22,8 @@ FileDescriptorName=resolve-hook
|
||||
SocketMode=0666
|
||||
Service=systemd-networkd.service
|
||||
RemoveOnStop=yes
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
|
||||
@@ -20,6 +20,8 @@ ListenStream=/run/systemd/report/io.systemd.Network
|
||||
FileDescriptorName=varlink-metrics
|
||||
SocketMode=0666
|
||||
Service=systemd-networkd.service
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
|
||||
@@ -21,6 +21,8 @@ Symlinks=/run/varlink/registry/io.systemd.Network
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0666
|
||||
Service=systemd-networkd.service
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
|
||||
@@ -20,6 +20,8 @@ ListenStream=/run/systemd/io.systemd.NamespaceResource
|
||||
Symlinks=/run/systemd/userdb/io.systemd.NamespaceResource /run/varlink/registry/io.systemd.NamespaceResource
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0666
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
|
||||
@@ -22,6 +22,8 @@ ConditionPathExists=/proc/pressure/memory
|
||||
ListenStream=/run/systemd/oom/io.systemd.ManagedOOM
|
||||
SocketMode=0666
|
||||
RemoveOnStop=yes
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
|
||||
@@ -19,9 +19,12 @@ ConditionSecurity=measured-os
|
||||
ListenStream=/run/systemd/io.systemd.PCRExtend
|
||||
Symlinks=/run/varlink/registry/io.systemd.PCRExtend
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0600
|
||||
SocketMode=0644
|
||||
Accept=yes
|
||||
MaxConnectionsPerSource=16
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
|
||||
@@ -19,6 +19,9 @@ ConditionSecurity=measured-uki
|
||||
ListenStream=/run/systemd/io.systemd.PCRLock
|
||||
Symlinks=/run/varlink/registry/io.systemd.PCRLock
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0600
|
||||
SocketMode=0644
|
||||
Accept=yes
|
||||
MaxConnectionsPerSource=16
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
|
||||
@@ -19,6 +19,9 @@ Before=shutdown.target
|
||||
ListenStream=/run/systemd/io.systemd.Repart
|
||||
Symlinks=/run/varlink/registry/io.systemd.Repart
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0600
|
||||
SocketMode=0644
|
||||
Accept=yes
|
||||
MaxConnectionsPerSource=16
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
|
||||
@@ -18,6 +18,9 @@ SocketMode=0666
|
||||
Accept=yes
|
||||
MaxConnectionsPerSource=16
|
||||
RemoveOnStop=yes
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
|
||||
@@ -20,6 +20,9 @@ SocketMode=0666
|
||||
Accept=yes
|
||||
MaxConnectionsPerSource=16
|
||||
RemoveOnStop=yes
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
|
||||
@@ -21,6 +21,9 @@ SocketMode=0666
|
||||
Accept=yes
|
||||
MaxConnectionsPerSource=16
|
||||
RemoveOnStop=yes
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
|
||||
@@ -28,6 +28,9 @@ SocketMode=0666
|
||||
Accept=yes
|
||||
MaxConnectionsPerSource=16
|
||||
RemoveOnStop=yes
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
|
||||
@@ -22,6 +22,9 @@ SocketMode=0600
|
||||
Accept=yes
|
||||
MaxConnectionsPerSource=16
|
||||
RemoveOnStop=yes
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
|
||||
@@ -20,6 +20,8 @@ ListenStream=/run/systemd/resolve/io.systemd.Resolve.Monitor
|
||||
Symlinks=/run/varlink/registry/io.systemd.Resolve.Monitor
|
||||
FileDescriptorName=varlink-monitor
|
||||
SocketMode=0666
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
|
||||
@@ -20,6 +20,8 @@ ListenStream=/run/systemd/resolve/io.systemd.Resolve
|
||||
Symlinks=/run/varlink/registry/io.systemd.Resolve
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0666
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
|
||||
@@ -19,6 +19,9 @@ FileDescriptorName=varlink
|
||||
SocketMode=0666
|
||||
Accept=yes
|
||||
MaxConnectionsPerSource=16
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
|
||||
@@ -20,6 +20,9 @@ FileDescriptorName=varlink
|
||||
SocketMode=0666
|
||||
Accept=yes
|
||||
MaxConnectionsPerSource=16
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
|
||||
@@ -22,6 +22,9 @@ FileDescriptorName=varlink
|
||||
SocketMode=0666
|
||||
Accept=yes
|
||||
MaxConnectionsPerSource=16
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
|
||||
@@ -19,6 +19,8 @@ Service=systemd-udevd.service
|
||||
ListenStream=/run/udev/io.systemd.Udev
|
||||
Symlinks=/run/varlink/registry/io.systemd.Udev
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0600
|
||||
SocketMode=0644
|
||||
RemoveOnStop=yes
|
||||
DeferTrigger=yes
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
|
||||
@@ -19,6 +19,8 @@ Symlinks=/run/systemd/userdb/io.systemd.NameServiceSwitch /run/systemd/userdb/io
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0666
|
||||
RemoveOnStop=yes
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
|
||||
@@ -19,3 +19,6 @@ Symlinks=%t/varlink/registry/io.systemd.AskPassword
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0600
|
||||
Accept=yes
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
|
||||
@@ -17,3 +17,5 @@ ListenStream=%t/systemd/io.systemd.Import
|
||||
Symlinks=%t/varlink/registry/io.systemd.Import
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0600
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
|
||||
@@ -17,3 +17,6 @@ Symlinks=%t/varlink/registry/io.systemd.JournalAccess
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0600
|
||||
Accept=yes
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
|
||||
@@ -16,3 +16,5 @@ ListenStream=%t/systemd/machine/io.systemd.Machine
|
||||
Symlinks=%t/systemd/machine/io.systemd.MachineImage %t/varlink/registry/io.systemd.Machine %t/varlink/registry/io.systemd.MachineImage
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0600
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
|
||||
@@ -18,6 +18,9 @@ FileDescriptorName=varlink
|
||||
SocketMode=0600
|
||||
Accept=yes
|
||||
MaxConnectionsPerSource=16
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
XAttrAccept=user.varlink=server
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
|
||||
Reference in New Issue
Block a user