Features:
+* add a new ExecStart= flag that inserts the configured user's shell as first
+ word in the comand line. (maybe use character '.'). Usecase: tool such as
+ uid0 can use that to spawn the target user's default shell.
+
* varlink: figure out how to do docs for our varlink interfaces. Idea: install
interface files augmented with docs in /usr/share/ somewhere. And have
functionality in varlinkctl to merge interface info extracted from binaries
* introduce mntid_t, and make it 64bit, as apparently the kernel switched to
64bit mount ids
-* Add an alias to systemd-run maybe called "uid0" or so, which tries to mimic
- the sudo/su command lines to some level, but is backed by transient services,
- and proper security isolate/tty forwarding. This would then allow us to run
- systems with NNP turned on reasonably nicely. To make this extra nice and
- pretty, in ptyfwd rewrite every NL we pass through so that it resets the bg
- color to some reddish tone, and erase the whole coming line first, so that
- the background color indicates when operating with privileges and when not.
-
* use udev rule networkd ownership property to take ownership of network
interfaces nspawn creates
see above and below.</para></listitem>
<listitem><para>The key may be acquired via a PKCS#11 compatible hardware security token or
- smartcard. In this case an encrypted key is stored on disk/removable media, acquired via
- <constant>AF_UNIX</constant>, or stored in the LUKS2 JSON token metadata header. The encrypted key is
- then decrypted by the PKCS#11 token with an RSA key stored on it, and then used to unlock the encrypted
- volume. Use the <option>pkcs11-uri=</option> option described below to use this mechanism.</para></listitem>
+ smartcard. In this case a saved key used in unlock process is stored on disk/removable media, acquired via
+ <constant>AF_UNIX</constant>, or stored in the LUKS2 JSON token metadata header. For RSA, the saved key
+ is an encrypted volume key. The encrypted volume key is then decrypted by the PKCS#11 token with an RSA
+ private key stored on it, and used to unlock the encrypted volume. For elliptic-curve (EC) cryptography,
+ the saved key is the public key generated in enrollment process. The public key is then used to derive
+ a shared secret with a private key stored in the PKCS#11 token. The derived shared secret is then used
+ to unlock the volume. Use the <option>pkcs11-uri=</option> option described below to use this mechanism.
+ </para></listitem>
<listitem><para>Similarly, the key may be acquired via a FIDO2 compatible hardware security token
(which must implement the "hmac-secret" extension). In this case a key generated randomly during
<term><option>pkcs11-uri=</option></term>
<listitem><para>Takes either the special value <literal>auto</literal> or an <ulink
- url="https://tools.ietf.org/html/rfc7512">RFC7512 PKCS#11 URI</ulink> pointing to a private RSA key
+ url="https://tools.ietf.org/html/rfc7512">RFC7512 PKCS#11 URI</ulink> pointing to a private key
which is used to decrypt the encrypted key specified in the third column of the line. This is useful
for unlocking encrypted volumes through PKCS#11 compatible security tokens or smartcards. See below
for an example how to set up this mechanism for unlocking a LUKS2 volume with a YubiKey security
security token metadata in its LUKS2 JSON token section. In this mode the URI and the encrypted key
are automatically read from the LUKS2 JSON token header. Use
<citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
- as simple tool for enrolling PKCS#11 security tokens or smartcards in a way compatible with
+ as a simple tool for enrolling PKCS#11 security tokens or smartcards in a way compatible with
<literal>auto</literal>. In this mode the third column of the line should remain empty (that is,
specified as <literal>-</literal>).</para>
- <para>The specified URI can refer directly to a private RSA key stored on a token or alternatively
- just to a slot or token, in which case a search for a suitable private RSA key will be performed. In
- this case if multiple suitable objects are found the token is refused. The encrypted key configured
- in the third column of the line is passed as is (i.e. in binary form, unprocessed) to RSA
- decryption. The resulting decrypted key is then Base64 encoded before it is used to unlock the LUKS
- volume.</para>
+ <para>The specified URI can refer directly to a private key stored on a token or alternatively
+ just to a slot or token, in which case a search for a suitable private key will be performed. In
+ this case if multiple suitable objects are found the token is refused. The keyfile configured
+ in the third column of the line is used as is (i.e. in binary form, unprocessed). The resulting
+ decrypted key (for RSA) or derived shared secret (for ECC) is then Base64 encoded before it is used
+ to unlock the LUKS volume.</para>
<para>Use <command>systemd-cryptenroll --pkcs11-token-uri=list</command> to list all suitable PKCS#11
security tokens currently plugged in, along with their URIs.</para>
<title>Yubikey-based PKCS#11 Volume Unlocking Example</title>
<para>The PKCS#11 logic allows hooking up any compatible security token that is capable of storing RSA
- decryption keys for unlocking an encrypted volume. Here's an example how to set up a Yubikey security
- token for this purpose on a LUKS2 volume, using <citerefentry
+ or EC cryptographic keys for unlocking an encrypted volume. Here's an example how to set up a Yubikey
+ security token for this purpose on a LUKS2 volume, using <citerefentry
project='debian'><refentrytitle>ykmap</refentrytitle><manvolnum>1</manvolnum></citerefentry> from the
yubikey-manager project to initialize the token and
<citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
''],
['udev_new', '3', ['udev_ref', 'udev_unref'], ''],
['udevadm', '8', [], ''],
+ ['uid0', '1', [], ''],
['ukify', '1', [], 'ENABLE_UKIFY'],
['user@.service',
'5',
supports tokens and credentials of the following kind to be enrolled:</para>
<orderedlist>
- <listitem><para>PKCS#11 security tokens and smartcards that may carry an RSA key pair (e.g. various
- YubiKeys)</para></listitem>
+ <listitem><para>PKCS#11 security tokens and smartcards that may carry an RSA or EC key pair (e.g.
+ various YubiKeys)</para></listitem>
<listitem><para>FIDO2 security tokens that implement the <literal>hmac-secret</literal> extension (most
FIDO2 keys, including YubiKeys)</para></listitem>
smartcard URI referring to the token. Alternatively the special value <literal>auto</literal> may
be specified, in order to automatically determine the URI of a currently plugged in security token
(of which there must be exactly one). The special value <literal>list</literal> may be used to
- enumerate all suitable PKCS#11 tokens currently plugged in. The security token must contain an RSA
- key pair which is used to encrypt the randomly generated key that is used to unlock the LUKS2
- volume. The encrypted key is then stored in the LUKS2 JSON token header area.</para>
+ enumerate all suitable PKCS#11 tokens currently plugged in.</para>
+
+ <para>The PKCS#11 token must contain an RSA or EC key pair which will be used to unlock a LUKS2 volume.
+ For RSA, a randomly generated volume key is encrypted with a public key in the token, and stored in
+ the LUKS2 JSON token header area. To unlock a volume, the stored encrypted volume key will be decrypted
+ with a private key in the token. For ECC, ECDH algorithm is used: we generate a pair of EC keys in
+ the same EC group, then derive a shared secret using the generated private key and the public key
+ in the token. The derived shared secret is used as a volume key. The generated public key is
+ stored in the LUKS2 JSON token header area. The generated private key is erased. To unlock a volume,
+ we derive the shared secret with the stored public key and a private key in the token.</para>
<para>In order to unlock a LUKS2 volume with an enrolled PKCS#11 security token, specify the
<option>pkcs11-uri=</option> option in the respective <filename>/etc/crypttab</filename> line:</para>
<term><option>--slice-inherit</option></term>
<listitem><para>Make the new <filename>.service</filename> or <filename>.scope</filename> unit part
- of the inherited slice. This option can be combined with <option>--slice=</option>.</para>
-
- <para>An inherited slice is located within <command>systemd-run</command> slice. Example: if
- <command>systemd-run</command> slice is <filename>foo.slice</filename>, and the
- <option>--slice=</option> argument is <filename>bar</filename>, the unit will be placed under the
+ of the slice the <command>systemd-run</command> itself has been invoked in. This option may be
+ combined with <option>--slice=</option>, in which case the slice specified via
+ <option>--slice=</option> is placed within the slice the <command>systemd-run</command> command is
+ invoked in.</para>
+
+ <para>Example: consider <command>systemd-run</command> being invoked in the slice
+ <filename>foo.slice</filename>, and the <option>--slice=</option> argument is
+ <filename>bar</filename>. The unit will then be placed under
<filename>foo-bar.slice</filename>.</para>
<xi:include href="version-info.xml" xpointer="v246"/>
<xi:include href="version-info.xml" xpointer="v236"/></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--ignore-failure</option></term>
+
+ <listitem><para>By default, if the specified command fails the invoked unit will be marked failed
+ (though possibly still unloaded, see <option>--collect=</option>, above), and this is reported in the
+ logs. If this switch is specified this is suppressed and any non-success exit status/code of the
+ command is treated as success.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--background=<replaceable>COLOR</replaceable></option></term>
+
+ <listitem><para>Change the terminal background color to the specified ANSI color as long as the
+ session lasts. The color specified should be an ANSI X3.64 SGR background color, i.e. strings such as
+ <literal>40</literal>, <literal>41</literal>, …, <literal>47</literal>, <literal>48;2;…</literal>,
+ <literal>48;5;…</literal>. See <ulink
+ url="https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters">ANSI
+ Escape Code (Wikipedia)</ulink> for details.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+
<xi:include href="user-system-options.xml" xpointer="user" />
<xi:include href="user-system-options.xml" xpointer="system" />
<xi:include href="user-system-options.xml" xpointer="host" />
<citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-mount</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>uid0</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</para>
</refsect1>
<varlistentry>
<term><varname>SetLoginEnvironment=</varname></term>
- <listitem><para>Takes a boolean parameter that controls whether to set <varname>$HOME</varname>,
- <varname>$LOGNAME</varname>, and <varname>$SHELL</varname> environment variables. If unset, this is
- controlled by whether <varname>User=</varname> is set. If true, they will always be set for system services,
- i.e. even when the default user <literal>root</literal> is used. If false, the mentioned variables are not set
- by systemd, no matter whether <varname>User=</varname> is used or not. This option normally has no effect
- on user services, since these variables are typically inherited from user manager's own environment anyway.</para>
+ <listitem><para>Takes a boolean parameter that controls whether to set the <varname>$HOME</varname>,
+ <varname>$LOGNAME</varname>, and <varname>$SHELL</varname> environment variables. If not set, this
+ defaults to true if <varname>User=</varname>, <varname>DynamicUser=</varname> or
+ <varname>PAMName=</varname> are set, false otherwise. If set to true, the variables will always be
+ set for system services, i.e. even when the default user <literal>root</literal> is used. If set to
+ false, the mentioned variables are not set by the service manager, no matter whether
+ <varname>User=</varname>, <varname>DynamicUser=</varname>, or <varname>PAMName=</varname> are used or
+ not. This option normally has no effect on services of the per-user service manager, since in that
+ case these variables are typically inherited from user manager's own environment anyway.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
system hierarchy, both a requirement dependency and an ordering
dependency between both units are created automatically.</para></listitem>
- <listitem><para>Block device backed file systems automatically gain
- <varname>BindsTo=</varname> and <varname>After=</varname> type
- dependencies on the device unit encapsulating the block
- device (see below).</para></listitem>
-
- <listitem><para>If traditional file system quota is enabled for a mount
- unit, automatic <varname>Wants=</varname> and
- <varname>Before=</varname> dependencies on
- <filename>systemd-quotacheck.service</filename> and
- <filename>quotaon.service</filename> are added.</para></listitem>
-
- <listitem><para>Additional implicit dependencies may be added as result of
- execution and resource control parameters as documented in
+ <listitem><para>Block device backed file systems automatically gain <varname>Requires=</varname>,
+ <varname>StopPropagatedFrom=</varname>, and <varname>After=</varname> type dependencies on the
+ device unit encapsulating the block device (see <varname>x-systemd.device-bound=</varname> for details).
+ </para></listitem>
+
+ <listitem><para>If traditional file system quota is enabled for a mount unit, automatic
+ <varname>Wants=</varname> and <varname>Before=</varname> dependencies on
+ <filename>systemd-quotacheck.service</filename> and <filename>quotaon.service</filename>
+ are added.</para></listitem>
+
+ <listitem><para>Additional implicit dependencies may be added as result of execution and
+ resource control parameters as documented in
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
and
<citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
<listitem><para>Mount units referring to local file systems automatically gain
an <varname>After=</varname> dependency on <filename>local-fs-pre.target</filename>, and a
- <varname>Before=</varname> dependency on <filename>local-fs.target</filename> unless
- <option>nofail</option> mount option is set.</para></listitem>
-
- <listitem><para>Network mount units
- automatically acquire <varname>After=</varname> dependencies on <filename>remote-fs-pre.target</filename>,
- <filename>network.target</filename> and <filename>network-online.target</filename>, and gain a
- <varname>Before=</varname> dependency on <filename>remote-fs.target</filename> unless
- <option>nofail</option> mount option is set. Towards the latter a
- <varname>Wants=</varname> unit is added as well.</para></listitem>
+ <varname>Before=</varname> dependency on <filename>local-fs.target</filename> unless one or more
+ mount options among <option>nofail</option>, <option>x-systemd.wanted-by=</option>,
+ and <option>x-systemd.required-by=</option> is set. See below for detailed information.</para>
+
+ <para>Additionally, an <varname>After=</varname> dependency on <filename>swap.target</filename>
+ is added when the file system type is <literal>tmpfs</literal>.</para>
+ </listitem>
+
+ <listitem><para>Network mount units automatically acquire <varname>After=</varname> dependencies on
+ <filename>remote-fs-pre.target</filename>, <filename>network.target</filename>,
+ plus <varname>After=</varname> and <varname>Wants=</varname> dependencies on <filename>network-online.target</filename>,
+ and a <varname>Before=</varname> dependency on <filename>remote-fs.target</filename>, unless
+ one or more mount options among <option>nofail</option>, <option>x-systemd.wanted-by=</option>,
+ and <option>x-systemd.required-by=</option> is set.</para></listitem>
</itemizedlist>
<para>Mount units referring to local and network file systems are distinguished by their file system type
<term><option>x-systemd.wanted-by=</option></term>
<term><option>x-systemd.required-by=</option></term>
- <listitem><para>In the created mount unit, configures a
- <varname>WantedBy=</varname> or <varname>RequiredBy=</varname>
- dependency on another unit. This option may be
- specified more than once. If this is specified, the normal
- automatic dependencies on the created mount unit, e.g.,
- <filename>local-fs.target</filename>, are not automatically
- created. See <varname>WantedBy=</varname> and <varname>RequiredBy=</varname> in
+ <listitem><para>In the created mount unit, configures a <varname>WantedBy=</varname> or
+ <varname>RequiredBy=</varname> dependency on another unit. This option may be specified more than once.
+ If this is specified, the default dependencies (see above) other than <filename>umount.target</filename>
+ on the created mount unit, e.g. <filename>local-fs.target</filename>, are not automatically created.
+ Hence it is likely that some ordering dependencies need to be set up manually through
+ <option>x-systemd.before=</option> and <option>x-systemd.after=</option>. See <varname>WantedBy=</varname>
+ and <varname>RequiredBy=</varname> in
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details.</para>
number of dynamically created network interfaces with the same network configuration and
automatic address range assignment.</para>
+ <para>If an empty string is specified, then the all previous assignments in both [Network] and
+ [Address] sections are cleared.</para>
+
<xi:include href="version-info.xml" xpointer="v211"/>
</listitem>
</varlistentry>
--- /dev/null
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
+<refentry id="uid0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>uid0</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>uid0</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>uid0</refname>
+ <refpurpose>Elevate privileges</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>uid0</command>
+ <arg choice="opt" rep="repeat">OPTIONS</arg>
+ <arg choice="opt" rep="repeat">COMMAND</arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><command>uid0</command> may be used to temporarily and interactively acquire elavated or different
+ privileges. It serves a similar purpose as <citerefentry
+ project='man-pages'><refentrytitle>sudo</refentrytitle><manvolnum>8</manvolnum></citerefentry>, but
+ operates differently in a couple of key areas:</para>
+
+ <itemizedlist>
+ <listitem><para>No execution or security context credentials are inherited from the caller into the
+ invoked commands, as they are invoked from a fresh, isolated service forked off the service
+ manager.</para></listitem>
+
+ <listitem><para>Authentication takes place via <ulink
+ url="https://www.freedesktop.org/wiki/Software/polkit">polkit</ulink>, thus isolating the
+ authentication prompt from the terminal (if possible).</para></listitem>
+
+ <listitem><para>An independent pseudo-tty is allocated for the invoked command, detaching its lifecycle and
+ isolating it for security.</para></listitem>
+
+ <listitem><para>No SetUID/SetGID file access bit functionality is used for the implementation.</para></listitem>
+ </itemizedlist>
+
+ <para>Altogether this should provide a safer and more robust alternative to the <command>sudo</command>
+ mechanism, in particular in OS environments where SetUID/SetGID support is not available (for example by
+ setting the <varname>NoNewPrivileges=</varname> variable in
+ <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>).</para>
+
+ <para>Any session invoked via <command>uid0</command> will run through the
+ <literal>systemd-uid0</literal> PAM stack.</para>
+
+ <para>Note that <command>uid0</command> is implemented as an alternative multi-call invocation of
+ <citerefentry><refentrytitle>systemd-run</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Options</title>
+
+ <para>The following options are understood:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><option>--no-ask-password</option></term>
+
+ <listitem><para>Do not query the user for authentication for privileged operations.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--unit=</option></term>
+
+ <listitem><para>Use this unit name instead of an automatically generated one.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--property=</option></term>
+
+ <listitem><para>Sets a property on the service unit that is created. This option takes an assignment
+ in the same format as
+ <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
+ <command>set-property</command> command.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--description=</option></term>
+
+ <listitem><para>Provide a description for the service unit that is invoked. If not specified,
+ the command itself will be used as a description. See <varname>Description=</varname> in
+ <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ </para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--slice=</option></term>
+
+ <listitem><para>Make the new <filename>.service</filename> unit part of the specified slice, instead
+ of <filename>user.slice</filename>.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--slice-inherit</option></term>
+
+ <listitem><para>Make the new <filename>.service</filename> unit part of the slice the
+ <command>uid0</command> itself has been invoked in. This option may be combined with
+ <option>--slice=</option>, in which case the slice specified via <option>--slice=</option> is placed
+ within the slice the <command>uid0</command> command is invoked in.</para>
+
+ <para>Example: consider <command>uid0</command> being invoked in the slice
+ <filename>foo.slice</filename>, and the <option>--slice=</option> argument is
+ <filename>bar</filename>. The unit will then be placed under
+ <filename>foo-bar.slice</filename>.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--user=</option></term>
+ <term><option>-u</option></term>
+ <term><option>--group=</option></term>
+ <term><option>-g</option></term>
+
+ <listitem><para>Switches to the specified user/group instead of root.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--nice=</option></term>
+
+ <listitem><para>Runs the invoked session with the specified nice level.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--chdir=</option></term>
+ <term><option>-D</option></term>
+
+ <listitem><para>Runs the invoked session with the specified working directory. If not specified
+ defaults to the client's current working directory if switching to the root user, or the target
+ user's home directory otherwise.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--setenv=<replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term>
+
+ <listitem><para>Runs the invoked session with the specified environment variable set. This parameter
+ may be used more than once to set multiple variables. When <literal>=</literal> and
+ <replaceable>VALUE</replaceable> are omitted, the value of the variable with the same name in the
+ invoking environment will be used.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--background=<replaceable>COLOR</replaceable></option></term>
+
+ <listitem><para>Change the terminal background color to the specified ANSI color as long as the
+ session lasts. If not specified, the background will be tinted in a reddish tone when operating as
+ root, and in a yellowish tone when operating under another UID, as reminder of the changed
+ privileges. The color specified should be an ANSI X3.64 SGR background color, i.e. strings such as
+ <literal>40</literal>, <literal>41</literal>, …, <literal>47</literal>, <literal>48;2;…</literal>,
+ <literal>48;5;…</literal>. See <ulink
+ url="https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters">ANSI
+ Escape Code (Wikipedia)</ulink> for details. Set to an empty string to disable.</para>
+
+ <para>Example: <literal>--background=44</literal> for a blue background.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+
+ <xi:include href="user-system-options.xml" xpointer="machine" />
+ <xi:include href="standard-options.xml" xpointer="help" />
+ <xi:include href="standard-options.xml" xpointer="version" />
+ </variablelist>
+
+ <para>All command line arguments after the first non-option argument become part of the command line of
+ the launched process. If no command line is specified an interactive shell is invoked. The shell to
+ invoke may be controlled via <option>--setenv=SHELL=…</option> and currently defaults to the
+ <emphasis>originating user's</emphasis> shell (i.e. not the target user's!) if operating locally, or
+ <filename>/bin/sh</filename> when operating with <option>--machine=</option>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Exit status</title>
+
+ <para>On success, 0 is returned. If <command>uid0</command> failed to start the session or the specified command fails, a
+ non-zero return value will be returned.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-run</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry project='man-pages'><refentrytitle>sudo</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
-D tests=unsafe
-D slow-tests="${SLOW_TESTS:-false}"
-D create-log-dirs=false
- -D pamconfdir=no
+ -D pamconfdir=/usr/lib/pam.d/
-D utmp=true
-D hibernate=true
-D ldconfig=true
local -A OPTS=(
[STANDALONE]='-h --help --version'
- [ARG]='-t --identifier -p --priority --level-prefix'
+ [ARG]='-t --identifier -p --priority --stderr-priority --level-prefix'
)
_init_completion || return
--identifier|-t)
comps=''
;;
- --priority|-p)
+ --priority|-p|--stderr-priority)
comps='emerg alert crit err warning notice info debug'
;;
--level-prefix)
local -A OPTS=(
[STANDALONE]='-h --help --version --all -l --full -k --no-pager --xattr=no --cgroup-id=no'
- [ARG]='-M --machine -u --unit --user-unit'
+ [ARG]='-c --cgroup-id -M --machine -u --unit --user-unit -x --xattr'
)
_init_completion || return
--unit|-u)
comps=$( __get_units_have_cgroup --system )
;;
+ -c|--cgroup-id|-x|--xattr)
+ comps='yes no'
+ ;;
--user-unit)
comps=$( __get_units_have_cgroup --user )
;;
[STANDALONE]='-h --help --version
--no-pager
--no-legend
+ --no-reload
--force'
[ARG]='--root
- --json'
+ --json
+ --noexec
+ --image-policy'
)
local -A VERBS=(
--json)
comps='pretty short off'
;;
+ --noexec)
+ comps='false true'
+ ;;
+ --image-policy)
+ comps=''
+ ;;
esac
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
return 0
" -LIBCRYPTSETUP"
#endif
+#if HAVE_LIBCRYPTSETUP_PLUGINS
+ " +LIBCRYPTSETUP_PLUGINS"
+#else
+ " -LIBCRYPTSETUP_PLUGINS"
+#endif
+
#if HAVE_LIBFDISK
" +LIBFDISK"
#else
return strv_env_replace_consume(l, p);
}
+int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) {
+ int r;
+
+ assert(l);
+ assert(key);
+
+ if (!env_name_is_valid(key))
+ return -EINVAL;
+
+ if (!valuef) {
+ strv_env_unset(*l, key);
+ return 0;
+ }
+
+ _cleanup_free_ char *value = NULL;
+ va_list ap;
+ va_start(ap, valuef);
+ r = vasprintf(&value, valuef, ap);
+ va_end(ap);
+ if (r < 0)
+ return -ENOMEM;
+
+ char *p = strjoin(key, "=", value);
+ if (!p)
+ return -ENOMEM;
+
+ return strv_env_replace_consume(l, p);
+}
+
int _strv_env_assign_many(char ***l, ...) {
va_list ap;
int r;
int strv_env_replace_strdup(char ***l, const char *assignment);
int strv_env_replace_strdup_passthrough(char ***l, const char *assignment);
int strv_env_assign(char ***l, const char *key, const char *value);
+int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) _printf_(3, 4);
int _strv_env_assign_many(char ***l, ...) _sentinel_;
#define strv_env_assign_many(l, ...) _strv_env_assign_many(l, __VA_ARGS__, NULL)
int getpeercred(int fd, struct ucred *ucred) {
socklen_t n = sizeof(struct ucred);
struct ucred u;
- int r;
assert(fd >= 0);
assert(ucred);
- r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n);
- if (r < 0)
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n) < 0)
return -errno;
if (n != sizeof(struct ucred))
if (!s)
return -ENOMEM;
- if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0)
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0) {
+ s[n] = 0;
break;
+ }
if (errno != ERANGE)
return -errno;
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
+#include "hexdecoct.h"
#include "inotify-util.h"
#include "io-util.h"
#include "log.h"
return 0;
}
+
+void termios_disable_echo(struct termios *termios) {
+ assert(termios);
+
+ termios->c_lflag &= ~(ICANON|ECHO);
+ termios->c_cc[VMIN] = 1;
+ termios->c_cc[VTIME] = 0;
+}
+
+typedef enum BackgroundColorState {
+ BACKGROUND_TEXT,
+ BACKGROUND_ESCAPE,
+ BACKGROUND_BRACKET,
+ BACKGROUND_FIRST_ONE,
+ BACKGROUND_SECOND_ONE,
+ BACKGROUND_SEMICOLON,
+ BACKGROUND_R,
+ BACKGROUND_G,
+ BACKGROUND_B,
+ BACKGROUND_RED,
+ BACKGROUND_GREEN,
+ BACKGROUND_BLUE,
+} BackgroundColorState;
+
+typedef struct BackgroundColorContext {
+ BackgroundColorState state;
+ uint32_t red, green, blue;
+ unsigned red_bits, green_bits, blue_bits;
+} BackgroundColorContext;
+
+static int scan_background_color_response(
+ BackgroundColorContext *context,
+ const char *buf,
+ size_t size) {
+
+ assert(context);
+ assert(buf || size == 0);
+
+ for (size_t i = 0; i < size; i++) {
+ char c = buf[i];
+
+ switch (context->state) {
+
+ case BACKGROUND_TEXT:
+ context->state = c == '\x1B' ? BACKGROUND_ESCAPE : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_ESCAPE:
+ context->state = c == ']' ? BACKGROUND_BRACKET : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_BRACKET:
+ context->state = c == '1' ? BACKGROUND_FIRST_ONE : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_FIRST_ONE:
+ context->state = c == '1' ? BACKGROUND_SECOND_ONE : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_SECOND_ONE:
+ context->state = c == ';' ? BACKGROUND_SEMICOLON : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_SEMICOLON:
+ context->state = c == 'r' ? BACKGROUND_R : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_R:
+ context->state = c == 'g' ? BACKGROUND_G : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_G:
+ context->state = c == 'b' ? BACKGROUND_B : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_B:
+ context->state = c == ':' ? BACKGROUND_RED : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_RED:
+ if (c == '/')
+ context->state = context->red_bits > 0 ? BACKGROUND_GREEN : BACKGROUND_TEXT;
+ else {
+ int d = unhexchar(c);
+ if (d < 0 || context->red_bits >= sizeof(context->red)*8)
+ context->state = BACKGROUND_TEXT;
+ else {
+ context->red = (context->red << 4) | d;
+ context->red_bits += 4;
+ }
+ }
+ break;
+
+ case BACKGROUND_GREEN:
+ if (c == '/')
+ context->state = context->green_bits > 0 ? BACKGROUND_BLUE : BACKGROUND_TEXT;
+ else {
+ int d = unhexchar(c);
+ if (d < 0 || context->green_bits >= sizeof(context->green)*8)
+ context->state = BACKGROUND_TEXT;
+ else {
+ context->green = (context->green << 4) | d;
+ context->green_bits += 4;
+ }
+ }
+ break;
+
+ case BACKGROUND_BLUE:
+ if (c == '\x07') {
+ if (context->blue_bits > 0)
+ return 1; /* success! */
+
+ context->state = BACKGROUND_TEXT;
+ } else {
+ int d = unhexchar(c);
+ if (d < 0 || context->blue_bits >= sizeof(context->blue)*8)
+ context->state = BACKGROUND_TEXT;
+ else {
+ context->blue = (context->blue << 4) | d;
+ context->blue_bits += 4;
+ }
+ }
+ break;
+ }
+
+ /* Reset any colors we might have picked up */
+ if (context->state == BACKGROUND_TEXT) {
+ /* reset color */
+ context->red = context->green = context->blue = 0;
+ context->red_bits = context->green_bits = context->blue_bits = 0;
+ }
+ }
+
+ return 0; /* all good, but not enough data yet */
+}
+
+int get_default_background_color(double *ret_red, double *ret_green, double *ret_blue) {
+ int r;
+
+ assert(ret_red);
+ assert(ret_green);
+ assert(ret_blue);
+
+ if (!colors_enabled())
+ return -EOPNOTSUPP;
+
+ if (isatty(STDOUT_FILENO) < 1 || isatty(STDIN_FILENO) < 1)
+ return -EOPNOTSUPP;
+
+ if (streq_ptr(getenv("TERM"), "linux")) {
+ /* Linux console is black */
+ *ret_red = *ret_green = *ret_blue = 0.0;
+ return 0;
+ }
+
+ struct termios old_termios;
+ if (tcgetattr(STDIN_FILENO, &old_termios) < 0)
+ return -errno;
+
+ struct termios new_termios = old_termios;
+ termios_disable_echo(&new_termios);
+
+ if (tcsetattr(STDOUT_FILENO, TCSADRAIN, &new_termios) < 0)
+ return -errno;
+
+ r = loop_write(STDOUT_FILENO, "\x1B]11;?\x07", SIZE_MAX);
+ if (r < 0)
+ goto finish;
+
+ usec_t end = usec_add(now(CLOCK_MONOTONIC), 100 * USEC_PER_MSEC);
+ char buf[256];
+ size_t buf_full = 0;
+ BackgroundColorContext context = {};
+
+ for (;;) {
+ usec_t n = now(CLOCK_MONOTONIC);
+
+ if (n >= end) {
+ r = -EOPNOTSUPP;
+ goto finish;
+ }
+
+ r = fd_wait_for_event(STDIN_FILENO, POLLIN, usec_sub_unsigned(end, n));
+ if (r < 0)
+ goto finish;
+
+ ssize_t l;
+ l = read(STDIN_FILENO, buf, sizeof(buf) - buf_full);
+ if (l < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ buf_full += l;
+ assert(buf_full <= sizeof(buf));
+
+ r = scan_background_color_response(&context, buf, buf_full);
+ if (r < 0)
+ goto finish;
+ if (r > 0) {
+ assert(context.red_bits > 0);
+ *ret_red = (double) context.red / ((UINT64_C(1) << context.red_bits) - 1);
+ assert(context.green_bits > 0);
+ *ret_green = (double) context.green / ((UINT64_C(1) << context.green_bits) - 1);
+ assert(context.blue_bits > 0);
+ *ret_blue = (double) context.blue / ((UINT64_C(1) << context.blue_bits) - 1);
+ r = 0;
+ goto finish;
+ }
+ }
+
+finish:
+ (void) tcsetattr(STDOUT_FILENO, TCSADRAIN, &old_termios);
+ return r;
+}
#include <stdio.h>
#include <syslog.h>
#include <sys/types.h>
+#include <termios.h>
#include "macro.h"
#include "time-util.h"
bool dev_console_colors_enabled(void);
static inline bool colors_enabled(void) {
-
/* Returns true if colors are considered supported on our stdout. */
return get_color_mode() != COLOR_OFF;
}
/* This assumes there is a 'tty' group */
#define TTY_MODE 0620
+
+void termios_disable_echo(struct termios *termios);
+
+int get_default_background_color(double *ret_red, double *ret_green, double *ret_blue);
static BUS_DEFINE_PROPERTY_GET(property_get_cpu_sched_priority, "i", ExecContext, exec_context_get_cpu_sched_priority);
static BUS_DEFINE_PROPERTY_GET(property_get_coredump_filter, "t", ExecContext, exec_context_get_coredump_filter);
static BUS_DEFINE_PROPERTY_GET(property_get_timer_slack_nsec, "t", ExecContext, exec_context_get_timer_slack_nsec);
+static BUS_DEFINE_PROPERTY_GET(property_get_set_login_environment, "b", ExecContext, exec_context_get_set_login_environment);
static int property_get_environment_files(
sd_bus *bus,
SD_BUS_PROPERTY("User", "s", NULL, offsetof(ExecContext, user), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Group", "s", NULL, offsetof(ExecContext, group), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DynamicUser", "b", bus_property_get_bool, offsetof(ExecContext, dynamic_user), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("SetLoginEnvironment", "b", bus_property_get_tristate, offsetof(ExecContext, set_login_environment), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("SetLoginEnvironment", "b", property_get_set_login_environment, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(ExecContext, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SetCredential", "a(say)", property_get_set_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SetCredentialEncrypted", "a(say)", property_get_set_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST),
r = bus_verify_manage_units_async_full(
u,
is_image ? "mount-image" : "bind-mount",
- CAP_SYS_ADMIN,
N_("Authentication is required to mount on '$(unit)'."),
- true,
message,
error);
if (r < 0)
r = bus_verify_manage_units_async_full(
u,
verb,
- CAP_SYS_ADMIN,
polkit_message_for_job[job_type],
- true,
message,
error);
if (r < 0)
r = bus_verify_manage_units_async_full(
u,
jtype,
- CAP_SYS_ADMIN,
polkit_message_for_job[type],
- true,
message,
error);
if (r < 0)
r = bus_verify_manage_units_async_full(
u,
"kill",
- CAP_KILL,
N_("Authentication is required to send a UNIX signal to the processes of '$(unit)'."),
- true,
message,
error);
if (r < 0)
r = bus_verify_manage_units_async_full(
u,
"reset-failed",
- CAP_SYS_ADMIN,
N_("Authentication is required to reset the \"failed\" state of '$(unit)'."),
- true,
message,
error);
if (r < 0)
r = bus_verify_manage_units_async_full(
u,
"set-property",
- CAP_SYS_ADMIN,
N_("Authentication is required to set properties on '$(unit)'."),
- true,
message,
error);
if (r < 0)
r = bus_verify_manage_units_async_full(
u,
"ref",
- CAP_SYS_ADMIN,
- NULL,
- false,
+ /* polkit_message= */ NULL,
message,
error);
if (r < 0)
r = bus_verify_manage_units_async_full(
u,
"clean",
- CAP_DAC_OVERRIDE,
N_("Authentication is required to delete files and directories associated with '$(unit)'."),
- true,
message,
error);
if (r < 0)
r = bus_verify_manage_units_async_full(
u,
perm,
- CAP_SYS_ADMIN,
N_("Authentication is required to freeze or thaw the processes of '$(unit)' unit."),
- true,
message,
error);
if (r < 0)
int bus_verify_manage_units_async_full(
Unit *u,
const char *verb,
- int capability,
const char *polkit_message,
- bool interactive,
sd_bus_message *call,
sd_bus_error *error) {
return bus_verify_polkit_async(
call,
- capability,
"org.freedesktop.systemd1.manage-units",
details,
- interactive,
- UID_INVALID,
&u->manager->polkit_registry,
error);
}
static inline int bus_set_transient_usec_fix_0(Unit *u, const char *name, usec_t *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error) {
return bus_set_transient_usec_internal(u, name, p, true, message, flags, error);
}
-int bus_verify_manage_units_async_full(Unit *u, const char *verb, int capability, const char *polkit_message, bool interactive, sd_bus_message *call, sd_bus_error *error);
+int bus_verify_manage_units_async_full(Unit *u, const char *verb, const char *polkit_message, sd_bus_message *call, sd_bus_error *error);
int bus_read_mount_options(sd_bus_message *message, sd_bus_error *error, MountOptions **ret_options, char **ret_format_str, const char *separator);
}
int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-units", NULL, false, UID_INVALID, &m->polkit_registry, error);
+ return bus_verify_polkit_async(
+ call,
+ "org.freedesktop.systemd1.manage-units",
+ /* details= */ NULL,
+ &m->polkit_registry,
+ error);
}
int bus_verify_manage_unit_files_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-unit-files", NULL, false, UID_INVALID, &m->polkit_registry, error);
+ return bus_verify_polkit_async(
+ call,
+ "org.freedesktop.systemd1.manage-unit-files",
+ /* details= */ NULL,
+ &m->polkit_registry,
+ error);
}
int bus_verify_reload_daemon_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.reload-daemon", NULL, false, UID_INVALID, &m->polkit_registry, error);
+ return bus_verify_polkit_async(
+ call,
+ "org.freedesktop.systemd1.reload-daemon",
+ /* details= */ NULL,
+ &m->polkit_registry, error);
}
int bus_verify_set_environment_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.set-environment", NULL, false, UID_INVALID, &m->polkit_registry, error);
+ return bus_verify_polkit_async(
+ call,
+ "org.freedesktop.systemd1.set-environment",
+ /* details= */ NULL,
+ &m->polkit_registry,
+ error);
}
int bus_verify_bypass_dump_ratelimit_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.bypass-dump-ratelimit", NULL, false, UID_INVALID, &m->polkit_registry, error);
+ return bus_verify_polkit_async(
+ call,
+ "org.freedesktop.systemd1.bypass-dump-ratelimit",
+ /* details= */ NULL,
+ &m->polkit_registry,
+ error);
}
uint64_t manager_bus_n_queued_write(Manager *m) {
"Failed to determine user credentials for root: %m");
}
- bool set_user_login_env = c->set_login_environment >= 0 ? c->set_login_environment : (c->user || c->dynamic_user);
+ bool set_user_login_env = exec_context_get_set_login_environment(c);
if (username) {
x = strjoin("USER=", username);
return (uint64_t) MAX(r, 0);
}
+bool exec_context_get_set_login_environment(const ExecContext *c) {
+ assert(c);
+
+ if (c->set_login_environment >= 0)
+ return c->set_login_environment;
+
+ return c->user || c->dynamic_user || c->pam_name;
+}
+
char** exec_context_get_syscall_filter(const ExecContext *c) {
_cleanup_strv_free_ char **l = NULL;
int exec_context_get_cpu_sched_policy(const ExecContext *c);
int exec_context_get_cpu_sched_priority(const ExecContext *c);
uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c);
+bool exec_context_get_set_login_environment(const ExecContext *c);
char** exec_context_get_syscall_filter(const ExecContext *c);
char** exec_context_get_syscall_archs(const ExecContext *c);
char** exec_context_get_syscall_log(const ExecContext *c);
HASHMAP_FOREACH(u, m->units) {
- if (fdset_size(fds) <= 0)
+ if (fdset_isempty(fds))
break;
if (!UNIT_VTABLE(u)->distribute_fds)
if (!found)
log_warning("Cannot find unit for notify message of PID "PID_FMT", ignoring.", ucred->pid);
- if (fdset_size(fds) > 0)
+ if (!fdset_isempty(fds))
log_warning("Got extra auxiliary fds with notification message, closing them.");
return 0;
manager_check_basic_target(m);
- if (hashmap_size(m->jobs) > 0) {
+ if (!hashmap_isempty(m->jobs)) {
if (m->jobs_in_progress_event_source)
/* Ignore any failure, this is only for feedback */
(void) sd_event_source_set_time(m->jobs_in_progress_event_source,
}
/* Are there any failed units? If so, we are in degraded mode */
- if (set_size(m->failed_units) > 0)
+ if (!set_isempty(m->failed_units))
return MANAGER_DEGRADED;
return MANAGER_RUNNING;
#include "path-util.h"
#include "process-util.h"
#include "random-util.h"
+#include "selinux-util.h"
#include "serialize.h"
#include "service.h"
#include "signal-util.h"
service_set_state(s, SERVICE_DEAD);
}
+int service_determine_exec_selinux_label(Service *s, char **ret) {
+ int r;
+
+ assert(s);
+ assert(ret);
+
+ if (!mac_selinux_use())
+ return -ENODATA;
+
+ /* Returns the SELinux label used for execution of the main service binary */
+
+ if (s->exec_context.selinux_context) { /* Prefer the explicitly configured label if there is one */
+ char *con = strdup(s->exec_context.selinux_context);
+ if (!con)
+ return -ENOMEM;
+
+ *ret = con;
+ return 0;
+ }
+
+ if (s->exec_context.root_image ||
+ s->exec_context.n_extension_images > 0 ||
+ !strv_isempty(s->exec_context.extension_directories)) /* We cannot chase paths through images */
+ return log_unit_debug_errno(UNIT(s), SYNTHETIC_ERRNO(ENODATA), "Service with RootImage=, ExtensionImages= or ExtensionDirectories= set, cannot determine socket SELinux label before activation, ignoring.");
+
+ ExecCommand *c = s->exec_command[SERVICE_EXEC_START];
+ if (!c)
+ return -ENODATA;
+
+ _cleanup_free_ char *path = NULL;
+ r = chase(c->path, s->exec_context.root_directory, CHASE_PREFIX_ROOT, &path, NULL);
+ if (r < 0) {
+ log_unit_debug_errno(UNIT(s), r, "Failed to resolve service binary '%s', ignoring.", c->path);
+ return -ENODATA;
+ }
+
+ r = mac_selinux_get_create_label_from_exe(path, ret);
+ if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) {
+ log_unit_debug_errno(UNIT(s), r, "Reading SELinux label off binary '%s' is not supported, ignoring.", path);
+ return -ENODATA;
+ }
+ if (ERRNO_IS_NEG_PRIVILEGE(r)) {
+ log_unit_debug_errno(UNIT(s), r, "Can't read SELinux label off binary '%s', due to privileges, ignoring.", path);
+ return -ENODATA;
+ }
+ if (r < 0)
+ return log_unit_debug_errno(UNIT(s), r, "Failed to read SELinux label off binary '%s': %m", path);
+
+ return 0;
+}
+
static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
[SERVICE_RESTART_NO] = "no",
[SERVICE_RESTART_ON_SUCCESS] = "on-success",
usec_t service_restart_usec_next(Service *s);
+int service_determine_exec_selinux_label(Service *s, char **ret);
+
const char* service_restart_to_string(ServiceRestart i) _const_;
ServiceRestart service_restart_from_string(const char *s) _pure_;
}
static int socket_determine_selinux_label(Socket *s, char **ret) {
+ Unit *service;
int r;
assert(s);
assert(ret);
- Unit *service;
- ExecCommand *c;
- const char *exec_context;
- _cleanup_free_ char *path = NULL;
-
- r = socket_load_service_unit(s, -1, &service);
- if (r == -ENODATA)
- goto no_label;
+ r = socket_load_service_unit(s, /* cfd= */ -EBADF, &service);
+ if (r == -ENODATA) {
+ *ret = NULL;
+ return 0;
+ }
if (r < 0)
return r;
- exec_context = SERVICE(service)->exec_context.selinux_context;
- if (exec_context) {
- char *con;
-
- con = strdup(exec_context);
- if (!con)
- return -ENOMEM;
-
- *ret = TAKE_PTR(con);
+ r = service_determine_exec_selinux_label(SERVICE(service), ret);
+ if (r == -ENODATA) {
+ *ret = NULL;
return 0;
}
-
- c = SERVICE(service)->exec_command[SERVICE_EXEC_START];
- if (!c)
- goto no_label;
-
- r = chase(c->path, SERVICE(service)->exec_context.root_directory, CHASE_PREFIX_ROOT, &path, NULL);
- if (r < 0)
- goto no_label;
-
- r = mac_selinux_get_create_label_from_exe(path, ret);
- if (IN_SET(r, -EPERM, -EOPNOTSUPP))
- goto no_label;
return r;
-
-no_label:
- *ret = NULL;
- return 0;
}
static int socket_address_listen_do(
#include "terminal-util.h"
#include "tpm2-pcr.h"
#include "tpm2-util.h"
+#include "user-util.h"
+#include "varlink.h"
+#include "varlink-io.systemd.Credentials.h"
#include "verbs.h"
typedef enum TranscodeMode {
static usec_t arg_not_after = USEC_INFINITY;
static bool arg_pretty = false;
static bool arg_quiet = false;
+static bool arg_varlink = false;
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_signature, freep);
if (arg_tpm2_public_key_pcr_mask == UINT32_MAX)
arg_tpm2_public_key_pcr_mask = UINT32_C(1) << TPM2_PCR_KERNEL_BOOT;
+ r = varlink_invocation(VARLINK_ALLOW_ACCEPT);
+ if (r < 0)
+ return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
+ arg_varlink = r;
+
return 1;
}
return dispatch_verb(argc, argv, verbs, NULL);
}
+typedef struct MethodEncryptParameters {
+ const char *name;
+ const char *text;
+ struct iovec data;
+ uint64_t timestamp;
+ uint64_t not_after;
+} MethodEncryptParameters;
+
+static void method_encrypt_parameters_done(MethodEncryptParameters *p) {
+ assert(p);
+
+ iovec_done_erase(&p->data);
+}
+
+static int vl_method_encrypt(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "name", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(MethodEncryptParameters, name), 0 },
+ { "text", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(MethodEncryptParameters, text), 0 },
+ { "data", JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, offsetof(MethodEncryptParameters, data), 0 },
+ { "timestamp", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(MethodEncryptParameters, timestamp), 0 },
+ { "notAfter", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(MethodEncryptParameters, not_after), 0 },
+ {}
+ };
+ _cleanup_(method_encrypt_parameters_done) MethodEncryptParameters p = {
+ .timestamp = UINT64_MAX,
+ .not_after = UINT64_MAX,
+ };
+ _cleanup_(iovec_done) struct iovec output = {};
+ int r;
+
+ assert(link);
+
+ json_variant_sensitive(parameters);
+
+ r = varlink_dispatch(link, parameters, dispatch_table, &p);
+ if (r != 0)
+ return r;
+
+ if (p.name && !credential_name_valid(p.name))
+ return varlink_error_invalid_parameter_name(link, "name");
+ /* Specifying both or neither the text string and the binary data is not allowed */
+ if (!!p.text == !!p.data.iov_base)
+ return varlink_error_invalid_parameter_name(link, "data");
+ if (p.timestamp == UINT64_MAX)
+ p.timestamp = now(CLOCK_REALTIME);
+ if (p.not_after != UINT64_MAX && p.not_after < p.timestamp)
+ return varlink_error_invalid_parameter_name(link, "notAfter");
+
+ r = encrypt_credential_and_warn(
+ arg_with_key,
+ p.name,
+ p.timestamp,
+ p.not_after,
+ arg_tpm2_device,
+ arg_tpm2_pcr_mask,
+ arg_tpm2_public_key,
+ arg_tpm2_public_key_pcr_mask,
+ p.text ?: p.data.iov_base, p.text ? strlen(p.text) : p.data.iov_len,
+ &output.iov_base, &output.iov_len);
+ if (r < 0)
+ return r;
+
+ _cleanup_(json_variant_unrefp) JsonVariant *reply = NULL;
+
+ r = json_build(&reply, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_IOVEC_BASE64("blob", &output)));
+ if (r < 0)
+ return r;
+
+ /* Let's also mark the (theoretically encrypted) reply as sensitive, in case the NULL encryption scheme was used. */
+ json_variant_sensitive(reply);
+
+ return varlink_reply(link, reply);
+}
+
+typedef struct MethodDecryptParameters {
+ const char *name;
+ struct iovec blob;
+ uint64_t timestamp;
+} MethodDecryptParameters;
+
+static void method_decrypt_parameters_done(MethodDecryptParameters *p) {
+ assert(p);
+
+ iovec_done_erase(&p->blob);
+}
+
+static int vl_method_decrypt(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "name", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(MethodDecryptParameters, name), 0 },
+ { "blob", JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, offsetof(MethodDecryptParameters, blob), 0 },
+ { "timestamp", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(MethodDecryptParameters, timestamp), 0 },
+ {}
+ };
+ _cleanup_(method_decrypt_parameters_done) MethodDecryptParameters p = {
+ .timestamp = UINT64_MAX,
+ };
+ _cleanup_(iovec_done_erase) struct iovec output = {};
+ int r;
+
+ assert(link);
+
+ /* Let's also mark the (theoretically encrypted) input as sensitive, in case the NULL encryption scheme was used. */
+ json_variant_sensitive(parameters);
+
+ r = varlink_dispatch(link, parameters, dispatch_table, &p);
+ if (r != 0)
+ return r;
+
+ if (p.name && !credential_name_valid(p.name))
+ return varlink_error_invalid_parameter_name(link, "name");
+ if (!p.blob.iov_base)
+ return varlink_error_invalid_parameter_name(link, "blob");
+ if (p.timestamp == UINT64_MAX)
+ p.timestamp = now(CLOCK_REALTIME);
+
+ r = decrypt_credential_and_warn(
+ p.name,
+ p.timestamp,
+ arg_tpm2_device,
+ arg_tpm2_signature,
+ p.blob.iov_base, p.blob.iov_len,
+ &output.iov_base, &output.iov_len);
+ if (r == -EBADMSG)
+ return varlink_error(link, "io.systemd.Credentials.BadFormat", NULL);
+ if (r == -EREMOTE)
+ return varlink_error(link, "io.systemd.Credentials.NameMismatch", NULL);
+ if (r == -ESTALE)
+ return varlink_error(link, "io.systemd.Credentials.TimeMismatch", NULL);
+ if (r < 0)
+ return r;
+
+ _cleanup_(json_variant_unrefp) JsonVariant *reply = NULL;
+
+ r = json_build(&reply, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_IOVEC_BASE64("data", &output)));
+ if (r < 0)
+ return r;
+
+ json_variant_sensitive(reply);
+
+ return varlink_reply(link, reply);
+}
+
static int run(int argc, char *argv[]) {
int r;
if (r <= 0)
return r;
+ if (arg_varlink) {
+ _cleanup_(varlink_server_unrefp) VarlinkServer *varlink_server = NULL;
+
+ /* Invocation as Varlink service */
+
+ r = varlink_server_new(&varlink_server, VARLINK_SERVER_ROOT_ONLY|VARLINK_SERVER_INHERIT_USERDATA);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate Varlink server: %m");
+
+ r = varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_Credentials);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add Varlink interface: %m");
+
+ r = varlink_server_bind_method_many(
+ varlink_server,
+ "io.systemd.Credentials.Encrypt", vl_method_encrypt,
+ "io.systemd.Credentials.Decrypt", vl_method_decrypt);
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind Varlink methods: %m");
+
+ r = varlink_server_loop_auto(varlink_server);
+ if (r < 0)
+ return log_error_errno(r, "Failed to run Varlink event loop: %m");
+
+ return 0;
+ }
+
return creds_main(argc, argv);
}
#include "memory-util.h"
#include "openssl-util.h"
#include "pkcs11-util.h"
-#include "random-util.h"
int enroll_pkcs11(
struct crypt_device *cd,
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
_cleanup_free_ char *keyslot_as_string = NULL;
- size_t decrypted_key_size, encrypted_key_size;
- _cleanup_free_ void *encrypted_key = NULL;
+ size_t decrypted_key_size, saved_key_size;
+ _cleanup_free_ void *saved_key = NULL;
_cleanup_(X509_freep) X509 *cert = NULL;
ssize_t base64_encoded_size;
const char *node;
- EVP_PKEY *pkey;
int keyslot, r;
assert_se(cd);
if (r < 0)
return r;
- pkey = X509_get0_pubkey(cert);
- if (!pkey)
- return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to extract public key from X.509 certificate.");
-
- r = rsa_pkey_to_suitable_key_size(pkey, &decrypted_key_size);
- if (r < 0)
- return log_error_errno(r, "Failed to determine RSA public key size.");
-
- log_debug("Generating %zu bytes random key.", decrypted_key_size);
-
- decrypted_key = malloc(decrypted_key_size);
- if (!decrypted_key)
- return log_oom();
-
- r = crypto_random_bytes(decrypted_key, decrypted_key_size);
- if (r < 0)
- return log_error_errno(r, "Failed to generate random key: %m");
-
- r = rsa_encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &encrypted_key, &encrypted_key_size);
+ r = x509_generate_volume_keys(cert, &decrypted_key, &decrypted_key_size, &saved_key, &saved_key_size);
if (r < 0)
- return log_error_errno(r, "Failed to encrypt key: %m");
+ return log_error_errno(r, "Failed to generate volume keys: %m");
/* Let's base64 encode the key to use, for compat with homed (and it's easier to type it in by
* keyboard, if that might ever end up being necessary.) */
JSON_BUILD_PAIR("type", JSON_BUILD_CONST_STRING("systemd-pkcs11")),
JSON_BUILD_PAIR("keyslots", JSON_BUILD_ARRAY(JSON_BUILD_STRING(keyslot_as_string))),
JSON_BUILD_PAIR("pkcs11-uri", JSON_BUILD_STRING(uri)),
- JSON_BUILD_PAIR("pkcs11-key", JSON_BUILD_BASE64(encrypted_key, encrypted_key_size))));
+ JSON_BUILD_PAIR("pkcs11-key", JSON_BUILD_BASE64(saved_key, saved_key_size))));
if (r < 0)
return log_error_errno(r, "Failed to prepare PKCS#11 JSON token object: %m");
SET_FLAG(flags, MOUNT_NOFAIL, true);
}
+ if (!strv_isempty(wanted_by) || !strv_isempty(required_by)) {
+ /* If x-systemd.{wanted,required}-by= is specified, target_unit is not used */
+ target_unit = NULL;
+
+ /* Don't set default ordering dependencies on local-fs.target or remote-fs.target, but we
+ * still need to conflict with umount.target. */
+ fputs("DefaultDependencies=no\n"
+ "Conflicts=umount.target\n"
+ "Before=umount.target\n",
+ f);
+ }
+
r = write_extra_dependencies(f, opts);
if (r < 0)
return r;
/* Order the mount unit we generate relative to target_unit, so that DefaultDependencies= on the
* target unit won't affect us. */
- if (!FLAGS_SET(flags, MOUNT_NOFAIL))
+ if (target_unit && !FLAGS_SET(flags, MOUNT_NOFAIL))
fprintf(f, "Before=%s\n", target_unit);
if (passno != 0) {
}
}
- if (!FLAGS_SET(flags, MOUNT_AUTOMOUNT)) {
- if (!FLAGS_SET(flags, MOUNT_NOAUTO) && strv_isempty(wanted_by) && strv_isempty(required_by)) {
- r = generator_add_symlink(dest, target_unit,
- (flags & MOUNT_NOFAIL) ? "wants" : "requires", name);
- if (r < 0)
- return r;
- } else {
- STRV_FOREACH(s, wanted_by) {
- r = generator_add_symlink(dest, *s, "wants", name);
- if (r < 0)
- return r;
- }
-
- STRV_FOREACH(s, required_by) {
- r = generator_add_symlink(dest, *s, "requires", name);
- if (r < 0)
- return r;
- }
- }
- } else {
+ if (FLAGS_SET(flags, MOUNT_AUTOMOUNT)) {
r = unit_name_from_path(where, ".automount", &automount_name);
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
r = fflush_and_check(f);
if (r < 0)
return log_error_errno(r, "Failed to write unit file %s: %m", automount_name);
+ }
- r = generator_add_symlink(dest, target_unit,
- (flags & MOUNT_NOFAIL) ? "wants" : "requires", automount_name);
- if (r < 0)
- return r;
+ if (target_unit) {
+ assert(strv_isempty(wanted_by));
+ assert(strv_isempty(required_by));
+
+ /* noauto has no effect if x-systemd.automount is used */
+ if (!FLAGS_SET(flags, MOUNT_NOAUTO) || automount_name) {
+ r = generator_add_symlink(dest, target_unit,
+ FLAGS_SET(flags, MOUNT_NOFAIL) ? "wants" : "requires",
+ automount_name ?: name);
+ if (r < 0)
+ return r;
+ }
+ } else {
+ const char *unit_name = automount_name ?: name;
+
+ STRV_FOREACH(s, wanted_by) {
+ r = generator_add_symlink(dest, *s, "wants", unit_name);
+ if (r < 0)
+ return r;
+ }
+
+ STRV_FOREACH(s, required_by) {
+ r = generator_add_symlink(dest, *s, "requires", unit_name);
+ if (r < 0)
+ return r;
+ }
+
+ if ((flags & (MOUNT_NOAUTO|MOUNT_NOFAIL)) != 0)
+ log_warning("x-systemd.wanted-by= and/or x-systemd.required-by= specified, 'noauto' and 'nofail' have no effect.");
}
return true;
#include "memory-util.h"
#include "openssl-util.h"
#include "pkcs11-util.h"
-#include "random-util.h"
#include "strv.h"
static int add_pkcs11_encrypted_key(
}
int identity_add_pkcs11_key_data(JsonVariant **v, const char *uri) {
- _cleanup_(erase_and_freep) void *decrypted_key = NULL, *encrypted_key = NULL;
+ _cleanup_(erase_and_freep) void *decrypted_key = NULL, *saved_key = NULL;
_cleanup_(erase_and_freep) char *pin = NULL;
- size_t decrypted_key_size, encrypted_key_size;
+ size_t decrypted_key_size, saved_key_size;
_cleanup_(X509_freep) X509 *cert = NULL;
- EVP_PKEY *pkey;
int r;
assert(v);
if (r < 0)
return r;
- pkey = X509_get0_pubkey(cert);
- if (!pkey)
- return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to extract public key from X.509 certificate.");
-
- r = rsa_pkey_to_suitable_key_size(pkey, &decrypted_key_size);
- if (r < 0)
- return log_error_errno(r, "Failed to extract RSA key size from X509 certificate.");
-
- log_debug("Generating %zu bytes random key.", decrypted_key_size);
-
- decrypted_key = malloc(decrypted_key_size);
- if (!decrypted_key)
- return log_oom();
-
- r = crypto_random_bytes(decrypted_key, decrypted_key_size);
- if (r < 0)
- return log_error_errno(r, "Failed to generate random key: %m");
-
- r = rsa_encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &encrypted_key, &encrypted_key_size);
+ r = x509_generate_volume_keys(cert, &decrypted_key, &decrypted_key_size, &saved_key, &saved_key_size);
if (r < 0)
- return log_error_errno(r, "Failed to encrypt key: %m");
+ return log_error_errno(r, "Failed to generate volume keys: %m");
/* Add the token URI to the public part of the record. */
r = add_pkcs11_token_uri(v, uri);
r = add_pkcs11_encrypted_key(
v,
uri,
- encrypted_key, encrypted_key_size,
+ saved_key, saved_key_size,
decrypted_key, decrypted_key_size);
if (r < 0)
return r;
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.home1.remove-home",
- NULL,
- true,
- UID_INVALID,
+ /* details= */ NULL,
&h->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.home1.create-home",
- NULL,
- true,
- UID_INVALID,
+ /* details= */ NULL,
&h->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.home1.remove-home",
- NULL,
- true,
- UID_INVALID,
+ /* details= */ NULL,
&h->manager->polkit_registry,
error);
if (r < 0)
if (r < 0)
return r;
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.home1.authenticate-home",
- NULL,
- true,
+ /* details= */ NULL,
+ /* interactive= */ false,
h->uid,
&h->manager->polkit_registry,
error);
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.home1.update-home",
- NULL,
- true,
- UID_INVALID,
+ /* details= */ NULL,
&h->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.home1.resize-home",
- NULL,
- true,
- UID_INVALID,
+ /* details= */ NULL,
&h->manager->polkit_registry,
error);
if (r < 0)
if (r < 0)
return r;
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.home1.passwd-home",
- NULL,
- true,
+ /* details= */ NULL,
+ /* interactive= */ false,
h->uid,
&h->manager->polkit_registry,
error);
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.home1.create-home",
- NULL,
- true,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.home1.create-home",
- NULL,
- true,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
context_read_etc_hostname(c);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
m,
- CAP_SYS_ADMIN,
"org.freedesktop.hostname1.set-hostname",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&c->polkit_registry,
error);
if (r < 0)
if (name && !hostname_is_valid(name, 0))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
m,
- CAP_SYS_ADMIN,
"org.freedesktop.hostname1.set-static-hostname",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&c->polkit_registry,
error);
if (r < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid location '%s'", name);
}
- /* Since the pretty hostname should always be changed at the
- * same time as the static one, use the same policy action for
- * both... */
+ /* Since the pretty hostname should always be changed at the same time as the static one, use the
+ * same policy action for both... */
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
m,
- CAP_SYS_ADMIN,
prop == PROP_PRETTY_HOSTNAME ? "org.freedesktop.hostname1.set-static-hostname" : "org.freedesktop.hostname1.set-machine-info",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&c->polkit_registry,
error);
if (r < 0)
if (r < 0)
return r;
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
m,
- CAP_SYS_ADMIN,
"org.freedesktop.hostname1.get-product-uuid",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&c->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
m,
- CAP_SYS_ADMIN,
"org.freedesktop.hostname1.get-hardware-serial",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&c->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
m,
- CAP_SYS_ADMIN,
"org.freedesktop.hostname1.get-description",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&c->polkit_registry,
error);
if (r == 0)
r = bus_verify_polkit_async(
msg,
- CAP_SYS_ADMIN,
"org.freedesktop.import1.import",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
msg,
- CAP_SYS_ADMIN,
"org.freedesktop.import1.import",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
msg,
- CAP_SYS_ADMIN,
"org.freedesktop.import1.export",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
msg,
- CAP_SYS_ADMIN,
"org.freedesktop.import1.pull",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
msg,
- CAP_SYS_ADMIN,
"org.freedesktop.import1.pull",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&t->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
msg,
- CAP_SYS_ADMIN,
"org.freedesktop.import1.pull",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
client_context_flush_regular(s);
- assert(prioq_size(s->client_contexts_lru) == 0);
- assert(hashmap_size(s->client_contexts) == 0);
+ assert(prioq_isempty(s->client_contexts_lru));
+ assert(hashmap_isempty(s->client_contexts));
s->client_contexts_lru = prioq_free(s->client_contexts_lru);
s->client_contexts = hashmap_free(s->client_contexts);
/* Try to restore streams, but don't bother if this fails */
(void) server_restore_streams(s, fds);
- if (fdset_size(fds) > 0) {
+ if (!fdset_isempty(fds)) {
log_warning("%u unknown file descriptors passed, closing.", fdset_size(fds));
fds = fdset_free(fds);
}
return;
/* still referenced? */
- if (hashmap_size(track->names) > 0)
+ if (!hashmap_isempty(track->names))
return;
/* Nothing to call? */
return log_error_errno(r, "Failed to import file '%s': %m", *f);
}
- if (ordered_hashmap_size(h) <= 0) {
+ if (ordered_hashmap_isempty(h)) {
log_info("No items in catalog.");
return 0;
- } else
- log_debug("Found %u items in catalog.", ordered_hashmap_size(h));
+ }
+
+ log_debug("Found %u items in catalog.", ordered_hashmap_size(h));
items = new(CatalogItem, ordered_hashmap_size(h));
if (!items)
if (r < 0)
return r;
- for (unsigned i = 0; i < n_files; i++) {
- JournalFile *f = (JournalFile *)files[i];
+ FOREACH_ARRAY(_f, files, n_files) {
+ JournalFile *f = (JournalFile*) *_f;
bool found;
r = next_beyond_location(j, f, direction);
assert(nl);
assert(ret);
- if (ordered_set_size(nl->rqueue) <= 0) {
+ if (ordered_set_isempty(nl->rqueue)) {
/* Try to read a new message */
r = socket_read_message(nl);
if (r == -ENOBUFS) /* FIXME: ignore buffer overruns for now */
assert_return(nl, -EINVAL);
assert_return(!netlink_pid_changed(nl), -ECHILD);
- if (ordered_set_size(nl->rqueue) > 0)
+ if (!ordered_set_isempty(nl->rqueue))
return 0;
r = netlink_poll(nl, false, timeout_usec);
assert_return(nl, -EINVAL);
assert_return(!netlink_pid_changed(nl), -ECHILD);
- return ordered_set_size(nl->rqueue) == 0 ? POLLIN : 0;
+ return ordered_set_isempty(nl->rqueue) ? POLLIN : 0;
}
int sd_netlink_get_timeout(sd_netlink *nl, uint64_t *timeout_usec) {
assert_return(timeout_usec, -EINVAL);
assert_return(!netlink_pid_changed(nl), -ECHILD);
- if (ordered_set_size(nl->rqueue) > 0) {
+ if (!ordered_set_isempty(nl->rqueue)) {
*timeout_usec = 0;
return 1;
}
return sd_bus_reply_method_return(m, NULL);
}
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
m,
- CAP_SYS_ADMIN,
"org.freedesktop.locale1.set-locale",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&c->polkit_registry,
error);
if (r < 0)
if (vc_context_equal(&c->vc, &in) && !x_needs_update)
return sd_bus_reply_method_return(m, NULL);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
m,
- CAP_SYS_ADMIN,
"org.freedesktop.locale1.set-keyboard",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&c->polkit_registry,
error);
if (r < 0)
if (x11_context_equal(&c->x11_from_vc, &in) && x11_context_equal(&c->x11_from_xorg, &in) && !convert)
return sd_bus_reply_method_return(m, NULL);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
m,
- CAP_SYS_ADMIN,
"org.freedesktop.locale1.set-keyboard",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&c->polkit_registry,
error);
if (r < 0)
static int return_test_polkit(
sd_bus_message *message,
- int capability,
const char *action,
const char **details,
uid_t good_user,
bool challenge;
int r;
- r = bus_test_polkit(message, capability, action, details, good_user, &challenge, e);
+ r = bus_test_polkit(message, action, details, good_user, &challenge, e);
if (r < 0)
return r;
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.login1.lock-sessions",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
if (!pw)
return errno_or_else(ENOENT);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_SYS_ADMIN,
uid == auth_uid ? "org.freedesktop.login1.set-self-linger" :
"org.freedesktop.login1.set-user-linger",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
} else if (!seat_name_is_valid(seat)) /* Note that a seat does not have to exist yet for this operation to succeed */
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Seat name %s is not valid", seat);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.login1.attach-device",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
if (r < 0)
return r;
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.login1.flush-devices",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
interactive = flags & SD_LOGIND_INTERACTIVE;
if (multiple_sessions) {
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_SYS_BOOT,
a->polkit_action_multiple_sessions,
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED,
"Access denied to root due to active block inhibitor");
- r = bus_verify_polkit_async(message,
- CAP_SYS_BOOT,
+ r = bus_verify_polkit_async_full(
+ message,
a->polkit_action_ignore_inhibit,
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
}
if (!multiple_sessions && !blocked) {
- r = bus_verify_polkit_async(message,
- CAP_SYS_BOOT,
+ r = bus_verify_polkit_async_full(
+ message,
a->polkit_action,
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_BOOT,
a->polkit_action,
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
}
if (multiple_sessions) {
- r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action_multiple_sessions, NULL, UID_INVALID, &challenge, error);
+ r = bus_test_polkit(
+ message,
+ a->polkit_action_multiple_sessions,
+ /* details= */ NULL,
+ /* good_user= */ UID_INVALID,
+ &challenge,
+ error);
if (r < 0)
return r;
}
if (blocked) {
- r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action_ignore_inhibit, NULL, UID_INVALID, &challenge, error);
+ r = bus_test_polkit(
+ message,
+ a->polkit_action_ignore_inhibit,
+ /* details= */ NULL,
+ /* good_user= */ UID_INVALID,
+ &challenge,
+ error);
if (r < 0)
return r;
/* If neither inhibit nor multiple sessions
* apply then just check the normal policy */
- r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action, NULL, UID_INVALID, &challenge, error);
+ r = bus_test_polkit(
+ message,
+ a->polkit_action,
+ /* details= */ NULL,
+ /* good_user= */ UID_INVALID,
+ &challenge,
+ error);
if (r < 0)
return r;
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED,
"Reboot parameter not supported in containers, refusing.");
- r = bus_verify_polkit_async(message,
- CAP_SYS_ADMIN,
- "org.freedesktop.login1.set-reboot-parameter",
- NULL,
- false,
- UID_INVALID,
- &m->polkit_registry,
- error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.login1.set-reboot-parameter",
+ /* details= */ NULL,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return return_test_polkit(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.login1.set-reboot-parameter",
- NULL,
- UID_INVALID,
+ /* details= */ NULL,
+ /* good_user= */ UID_INVALID,
error);
}
/* non-EFI case: $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP is set to on */
use_efi = false;
- r = bus_verify_polkit_async(message,
- CAP_SYS_ADMIN,
- "org.freedesktop.login1.set-reboot-to-firmware-setup",
- NULL,
- false,
- UID_INVALID,
- &m->polkit_registry,
- error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.login1.set-reboot-to-firmware-setup",
+ /* details= */ NULL,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return return_test_polkit(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.login1.set-reboot-to-firmware-setup",
- NULL,
- UID_INVALID,
+ /* details= */ NULL,
+ /* good_user= */ UID_INVALID,
error);
}
/* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU is set to on */
use_efi = false;
- r = bus_verify_polkit_async(message,
- CAP_SYS_ADMIN,
- "org.freedesktop.login1.set-reboot-to-boot-loader-menu",
- NULL,
- false,
- UID_INVALID,
- &m->polkit_registry,
- error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.login1.set-reboot-to-boot-loader-menu",
+ /* details= */ NULL,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return return_test_polkit(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.login1.set-reboot-to-boot-loader-menu",
- NULL,
- UID_INVALID,
+ /* details= */ NULL,
+ /* good_user= */ UID_INVALID,
error);
}
/* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY is set to on */
use_efi = false;
- r = bus_verify_polkit_async(message,
- CAP_SYS_ADMIN,
- "org.freedesktop.login1.set-reboot-to-boot-loader-entry",
- NULL,
- false,
- UID_INVALID,
- &m->polkit_registry,
- error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.login1.set-reboot-to-boot-loader-entry",
+ /* details= */ NULL,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return return_test_polkit(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.login1.set-reboot-to-boot-loader-entry",
- NULL,
- UID_INVALID,
+ /* details= */ NULL,
+ /* good_user= */ UID_INVALID,
error);
}
m->enable_wall_messages == enable_wall_messages)
goto done;
- r = bus_verify_polkit_async(message,
- CAP_SYS_ADMIN,
- "org.freedesktop.login1.set-wall-message",
- NULL,
- false,
- UID_INVALID,
- &m->polkit_registry,
- error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.login1.set-wall-message",
+ /* details= */ NULL,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_BOOT,
w == INHIBIT_SHUTDOWN ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-shutdown" : "org.freedesktop.login1.inhibit-delay-shutdown") :
w == INHIBIT_SLEEP ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-sleep" : "org.freedesktop.login1.inhibit-delay-sleep") :
w == INHIBIT_IDLE ? "org.freedesktop.login1.inhibit-block-idle" :
w == INHIBIT_HANDLE_REBOOT_KEY ? "org.freedesktop.login1.inhibit-handle-reboot-key" :
w == INHIBIT_HANDLE_HIBERNATE_KEY ? "org.freedesktop.login1.inhibit-handle-hibernate-key" :
"org.freedesktop.login1.inhibit-handle-lid-switch",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
#if ENABLE_POLKIT
return bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.login1.chvt",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&manager->polkit_registry,
error);
#else
r = bus_verify_polkit_async(
message,
- CAP_KILL,
"org.freedesktop.login1.manage",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&s->manager->polkit_registry,
error);
if (r < 0)
assert(message);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_KILL,
"org.freedesktop.login1.manage",
- NULL,
- false,
+ /* details= */ NULL,
+ /* interactive= */ false,
s->user->user_record->uid,
&s->manager->polkit_registry,
error);
assert(message);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.login1.lock-sessions",
- NULL,
- false,
+ /* details= */ NULL,
+ /* interactive= */ false,
s->user->user_record->uid,
&s->manager->polkit_registry,
error);
if (!SIGNAL_VALID(signo))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_KILL,
"org.freedesktop.login1.manage",
- NULL,
- false,
+ /* details= */ NULL,
+ /* interactive= */ false,
s->user->user_record->uid,
&s->manager->polkit_registry,
error);
assert(message);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_KILL,
"org.freedesktop.login1.manage",
- NULL,
- false,
+ /* details= */ NULL,
+ /* interactive= */ false,
u->user_record->uid,
&u->manager->polkit_registry,
error);
assert(message);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_KILL,
"org.freedesktop.login1.manage",
- NULL,
- false,
+ /* details= */ NULL,
+ /* interactive= */ false,
u->user_record->uid,
&u->manager->polkit_registry,
error);
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
details,
- false,
- UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
details,
- false,
- UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
details,
- false,
- UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
details,
- false,
- UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
details,
- false,
- UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_KILL,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_KILL,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_KILL,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
'networkd-link.c',
'networkd-lldp-rx.c',
'networkd-lldp-tx.c',
- 'networkd-manager-bus.c',
'networkd-manager.c',
+ 'networkd-manager-bus.c',
+ 'networkd-manager-varlink.c',
'networkd-ndisc.c',
'networkd-neighbor.c',
'networkd-netlabel.c',
assert(rvalue);
assert(data);
- if (streq(section, "Network"))
+ if (streq(section, "Network")) {
+ if (isempty(rvalue)) {
+ /* If an empty string specified in [Network] section, clear previously assigned addresses. */
+ network->addresses_by_section = ordered_hashmap_free_with_destructor(network->addresses_by_section, address_free);
+ return 0;
+ }
+
/* we are not in an Address section, so use line number instead. */
r = address_new_static(network, filename, line, &n);
- else
+ } else
r = address_new_static(network, filename, section_line, &n);
if (r == -ENOMEM)
return log_oom();
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid NTP server: %s", *i);
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.set-ntp-servers",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.set-ntp-servers",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
if (r < 0)
return r;
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.set-dns-servers",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.set-dns-servers",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
goto finalize;
if (r == 0) {
if (r < 0)
return r;
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.set-domains",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.set-domains",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
if (r < 0)
return r;
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.set-default-route",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.set-default-route",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid LLMNR setting: %s", llmnr);
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.set-llmnr",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.set-llmnr",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid MulticastDNS setting: %s", mdns);
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.set-mdns",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.set-mdns",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSOverTLS setting: %s", dns_over_tls);
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.set-dns-over-tls",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.set-dns-over-tls",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSSEC setting: %s", dnssec);
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.set-dnssec",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.set-dnssec",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return r;
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.set-dnssec-negative-trust-anchors",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.set-dnssec-negative-trust-anchors",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
if (r < 0)
return r;
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.revert-ntp",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.revert-ntp",
+ /* details= */ NULL,
+ &l->manager->polkit_registry, error);
if (r < 0)
return r;
if (r == 0)
if (r < 0)
return r;
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.revert-dns",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.revert-dns",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
"Interface %s is not managed by systemd-networkd",
l->ifname);
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.forcerenew",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.forcerenew",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
"Interface %s is not managed by systemd-networkd",
l->ifname);
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.renew",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.renew",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
assert(message);
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.reconfigure",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.reconfigure",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
Manager *manager = userdata;
int r;
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.reload",
- NULL, true, UID_INVALID,
- &manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.reload",
+ /* details= */ NULL,
+ &manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <unistd.h>
+
+#include "networkd-manager-varlink.h"
+#include "varlink.h"
+#include "varlink-io.systemd.Network.h"
+
+static int vl_method_get_states(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+ Manager *m = ASSERT_PTR(userdata);
+
+ assert(link);
+
+ if (json_variant_elements(parameters) > 0)
+ return varlink_error_invalid_parameter(link, parameters);
+
+ return varlink_replyb(link,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_STRING("AddressState", link_address_state_to_string(m->address_state)),
+ JSON_BUILD_PAIR_STRING("IPv4AddressState", link_address_state_to_string(m->ipv4_address_state)),
+ JSON_BUILD_PAIR_STRING("IPv6AddressState", link_address_state_to_string(m->ipv6_address_state)),
+ JSON_BUILD_PAIR_STRING("CarrierState", link_carrier_state_to_string(m->carrier_state)),
+ JSON_BUILD_PAIR_CONDITION(m->online_state >= 0, "OnlineState", JSON_BUILD_STRING(link_online_state_to_string(m->online_state))),
+ JSON_BUILD_PAIR_STRING("OperationalState", link_operstate_to_string(m->operational_state))));
+}
+
+static int vl_method_get_namespace_id(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+ uint64_t id;
+
+ assert(link);
+
+ if (json_variant_elements(parameters) > 0)
+ return varlink_error_invalid_parameter(link, parameters);
+
+ struct stat st;
+ if (stat("/proc/self/ns/net", &st) < 0) {
+ log_warning_errno(errno, "Failed to stat network namespace, ignoring: %m");
+ id = 0;
+ } else
+ id = st.st_ino;
+
+ return varlink_replyb(link,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_UNSIGNED("NamespaceId", id)));
+}
+
+int manager_connect_varlink(Manager *m) {
+ _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL;
+ int r;
+
+ assert(m);
+
+ if (m->varlink_server)
+ return 0;
+
+ r = varlink_server_new(&s, VARLINK_SERVER_ACCOUNT_UID|VARLINK_SERVER_INHERIT_USERDATA);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate varlink server object: %m");
+
+ varlink_server_set_userdata(s, m);
+
+ r = varlink_server_add_interface(s, &vl_interface_io_systemd_Network);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add Network interface to varlink server: %m");
+
+ r = varlink_server_bind_method_many(
+ s,
+ "io.systemd.Network.GetStates", vl_method_get_states,
+ "io.systemd.Network.GetNamespaceId", vl_method_get_namespace_id);
+ if (r < 0)
+ return log_error_errno(r, "Failed to register varlink methods: %m");
+
+ r = varlink_server_listen_address(s, "/run/systemd/netif/io.systemd.Network", 0666);
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind to varlink socket: %m");
+
+ r = varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
+
+ m->varlink_server = TAKE_PTR(s);
+ return 0;
+}
+
+void manager_varlink_done(Manager *m) {
+ assert(m);
+
+ m->varlink_server = varlink_server_unref(m->varlink_server);
+ (void) unlink("/run/systemd/netif/io.systemd.Network");
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "networkd-manager.h"
+
+int manager_connect_varlink(Manager *m);
+void manager_varlink_done(Manager *m);
#include "networkd-dhcp-server-bus.h"
#include "networkd-dhcp6.h"
#include "networkd-link-bus.h"
-#include "networkd-manager-bus.h"
#include "networkd-manager.h"
+#include "networkd-manager-bus.h"
+#include "networkd-manager-varlink.h"
#include "networkd-neighbor.h"
#include "networkd-network-bus.h"
#include "networkd-nexthop.h"
return manager_setup_rtnl_filter(m);
}
-static int manager_dirty_handler(sd_event_source *s, void *userdata) {
- Manager *m = ASSERT_PTR(userdata);
- Link *link;
- int r;
-
- if (m->dirty) {
- r = manager_save(m);
- if (r < 0)
- log_warning_errno(r, "Failed to update state file %s, ignoring: %m", m->state_file);
- }
-
- SET_FOREACH(link, m->dirty_links) {
- r = link_save_and_clean(link);
- if (r < 0)
- log_link_warning_errno(link, r, "Failed to update link state file %s, ignoring: %m", link->state_file);
- }
+static int manager_post_handler(sd_event_source *s, void *userdata) {
+ Manager *manager = ASSERT_PTR(userdata);
- return 1;
+ (void) manager_process_requests(manager);
+ (void) manager_clean_all(manager);
+ return 0;
}
static int signal_terminate_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
if (r < 0)
log_debug_errno(r, "Failed allocate memory pressure event source, ignoring: %m");
- r = sd_event_add_post(m->event, NULL, manager_dirty_handler, m);
- if (r < 0)
- return r;
-
- r = sd_event_add_post(m->event, NULL, manager_process_requests, m);
+ r = sd_event_add_post(m->event, NULL, manager_post_handler, m);
if (r < 0)
return r;
if (m->test_mode)
return 0;
+ r = manager_connect_varlink(m);
+ if (r < 0)
+ return r;
+
r = manager_connect_bus(m);
if (r < 0)
return r;
sd_device_monitor_unref(m->device_monitor);
+ manager_varlink_done(m);
+
bus_verify_polkit_async_registry_free(m->polkit_registry);
sd_bus_flush_close_unref(m->bus);
#include "ordered-set.h"
#include "set.h"
#include "time-util.h"
+#include "varlink.h"
struct Manager {
sd_netlink *rtnl;
sd_event *event;
sd_resolve *resolve;
sd_bus *bus;
+ VarlinkServer *varlink_server;
sd_device_monitor *device_monitor;
Hashmap *polkit_registry;
int ethtool_fd;
process, counter, netlink_handler, ret);
}
-int manager_process_requests(sd_event_source *s, void *userdata) {
- Manager *manager = ASSERT_PTR(userdata);
+int manager_process_requests(Manager *manager) {
int r;
+ assert(manager);
+
for (;;) {
bool processed = false;
Request *req;
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-#include "sd-event.h"
#include "sd-netlink.h"
#include "alloc-util.h"
ret); \
})
-int manager_process_requests(sd_event_source *s, void *userdata);
+int manager_process_requests(Manager *manager);
int request_call_netlink_async(sd_netlink *nl, sd_netlink_message *m, Request *req);
const char* request_type_to_string(RequestType t) _const_;
link_clean(link);
return k;
}
+
+int manager_clean_all(Manager *manager) {
+ int r, ret = 0;
+
+ assert(manager);
+
+ if (manager->dirty) {
+ r = manager_save(manager);
+ if (r < 0)
+ log_warning_errno(r, "Failed to update state file %s, ignoring: %m", manager->state_file);
+ RET_GATHER(ret, r);
+ }
+
+ Link *link;
+ SET_FOREACH(link, manager->dirty_links) {
+ r = link_save_and_clean(link);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Failed to update link state file %s, ignoring: %m", link->state_file);
+ RET_GATHER(ret, r);
+ }
+
+ return ret;
+}
}
int manager_save(Manager *m);
+int manager_clean_all(Manager *manager);
if (asprintf(envp + n_env++, "container_uuid=%s", SD_ID128_TO_UUID_STRING(arg_uuid)) < 0)
return log_oom();
- if (fdset_size(fds) > 0) {
+ if (!fdset_isempty(fds)) {
r = fdset_cloexec(fds, false);
if (r < 0)
return log_error_errno(r, "Failed to unset O_CLOEXEC for file descriptors.");
if (r < 0)
log_debug_errno(r, "Failed to set user.oomd_kill on kill: %m");
- return set_size(pids_killed) != 0;
+ return !set_isempty(pids_killed);
}
typedef void (*dump_candidate_func)(const OomdCGroupContext *ctx, FILE *f, const char *prefix);
return r;
if (!TPM2_PCR_INDEX_VALID(p.pcr))
- return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", "pcr")));
+ return varlink_error_invalid_parameter_name(link, "pcr");
if (p.text) {
/* Specifying both the text string and the binary data is not allowed */
if (p.data.iov_base)
- return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", "data")));
+ return varlink_error_invalid_parameter_name(link, "data");
r = extend_now(p.pcr, p.text, strlen(p.text), _TPM2_USERSPACE_EVENT_TYPE_INVALID);
} else if (p.data.iov_base)
r = extend_now(p.pcr, p.data.iov_base, p.data.iov_len, _TPM2_USERSPACE_EVENT_TYPE_INVALID);
else
- return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", "text")));
+ return varlink_error_invalid_parameter_name(link, "text");
if (r < 0)
return r;
#include "blockdev-util.h"
#include "build.h"
#include "chase.h"
+#include "color-util.h"
#include "conf-files.h"
#include "efi-api.h"
#include "env-util.h"
return event_log_validate_fully_recognized(el);
}
-static void hsv_to_rgb(
- double h, double s, double v,
- uint8_t* ret_r, uint8_t *ret_g, uint8_t *ret_b) {
-
- double c, x, m, r, g, b;
-
- assert(s >= 0 && s <= 100);
- assert(v >= 0 && v <= 100);
- assert(ret_r);
- assert(ret_g);
- assert(ret_b);
-
- c = (s / 100.0) * (v / 100.0);
- x = c * (1 - fabs(fmod(h / 60.0, 2) - 1));
- m = (v / 100) - c;
-
- if (h >= 0 && h < 60)
- r = c, g = x, b = 0.0;
- else if (h >= 60 && h < 120)
- r = x, g = c, b = 0.0;
- else if (h >= 120 && h < 180)
- r = 0.0, g = c, b = x;
- else if (h >= 180 && h < 240)
- r = 0.0, g = x, b = c;
- else if (h >= 240 && h < 300)
- r = x, g = 0.0, b = c;
- else
- r = c, g = 0.0, b = x;
-
- *ret_r = (uint8_t) ((r + m) * 255);
- *ret_g = (uint8_t) ((g + m) * 255);
- *ret_b = (uint8_t) ((b + m) * 255);
-}
-
#define ANSI_TRUE_COLOR_MAX (7U + 3U + 1U + 3U + 1U + 3U + 2U)
static const char *ansi_true_color(uint8_t r, uint8_t g, uint8_t b, char ret[static ANSI_TRUE_COLOR_MAX]) {
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.portable1.attach-images",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.portable1.manage-images",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.portable1.attach-images",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
if (mode == BUS_IMAGE_AUTHENTICATE_ALL) {
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
polkit_action,
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
if (mode == BUS_IMAGE_AUTHENTICATE_BY_PATH) {
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
polkit_action,
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
if (r < 0)
return r;
- r = bus_verify_polkit_async(message, CAP_SYS_ADMIN,
- "org.freedesktop.resolve1.register-service",
- NULL, false, UID_INVALID,
- &m->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.resolve1.register-service",
+ /* details= */ NULL,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
while ((key = hashmap_first_key(c->by_key)))
dns_cache_remove_by_key(c, key);
- assert(hashmap_size(c->by_key) == 0);
- assert(prioq_size(c->by_expiry) == 0);
+ assert(hashmap_isempty(c->by_key));
+ assert(prioq_isempty(c->by_expiry));
c->by_key = hashmap_free(c->by_key);
c->by_expiry = prioq_free(c->by_expiry);
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
DnsCacheItem *i;
- if (prioq_size(c->by_expiry) <= 0)
+ if (prioq_isempty(c->by_expiry))
break;
if (prioq_size(c->by_expiry) + add < CACHE_MAX)
assert(scope);
- if (hashmap_size(scope->manager->dnssd_services) == 0)
+ if (hashmap_isempty(scope->manager->dnssd_services))
return 0;
scope->announced = false;
if (r == 0)
continue;
- return FLAGS_SET(t->answer_query_flags, SD_RESOLVED_AUTHENTICATED);
+ return FLAGS_SET(dt->answer_query_flags, SD_RESOLVED_AUTHENTICATED);
}
return true;
/* We found the transaction that was supposed to find the SOA RR for us. It was
* successful, but found no RR for us. This means we are not at a zone cut. In this
* case, we require authentication if the SOA lookup was authenticated too. */
- return FLAGS_SET(t->answer_query_flags, SD_RESOLVED_AUTHENTICATED);
+ return FLAGS_SET(dt->answer_query_flags, SD_RESOLVED_AUTHENTICATED);
}
return true;
* trust anchor defined at all. This enables easy overriding
* of negative trust anchors. */
- if (set_size(d->negative_by_name) > 0)
+ if (!set_isempty(d->negative_by_name))
return 0;
r = set_ensure_allocated(&d->negative_by_name, &dns_name_hash_ops);
while ((i = hashmap_first(z->by_key)))
dns_zone_item_remove_and_free(z, i);
- assert(hashmap_size(z->by_key) == 0);
- assert(hashmap_size(z->by_name) == 0);
+ assert(hashmap_isempty(z->by_key));
+ assert(hashmap_isempty(z->by_name));
z->by_key = hashmap_free(z->by_key);
z->by_name = hashmap_free(z->by_name);
m = s->manager;
- r = bus_verify_polkit_async(message, CAP_SYS_ADMIN,
- "org.freedesktop.resolve1.unregister-service",
- NULL, false, s->originator,
- &m->polkit_registry, error);
+ r = bus_verify_polkit_async_full(
+ message,
+ "org.freedesktop.resolve1.unregister-service",
+ /* details= */ NULL,
+ /* interactive= */ false,
+ /* good_user= */ s->originator,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
if (r < 0)
return r;
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.resolve1.set-dns-servers",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.resolve1.set-dns-servers",
+ /* details= */ NULL,
+ &l->manager->polkit_registry, error);
if (r < 0)
goto finalize;
if (r == 0) {
if (r < 0)
return r;
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.resolve1.set-domains",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.resolve1.set-domains",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
if (r < 0)
return r;
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.resolve1.set-default-route",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.resolve1.set-default-route",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid LLMNR setting: %s", llmnr);
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.resolve1.set-llmnr",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.resolve1.set-llmnr",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid MulticastDNS setting: %s", mdns);
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.resolve1.set-mdns",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.resolve1.set-mdns",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSOverTLS setting: %s", dns_over_tls);
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.resolve1.set-dns-over-tls",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.resolve1.set-dns-over-tls",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSSEC setting: %s", dnssec);
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.resolve1.set-dnssec",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.resolve1.set-dnssec",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return -ENOMEM;
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.resolve1.set-dnssec-negative-trust-anchors",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.resolve1.set-dnssec-negative-trust-anchors",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
if (r < 0)
return r;
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.resolve1.revert",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.resolve1.revert",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
'sources' : files('run.c'),
},
]
+
+install_emptydir(bindir)
+
+meson.add_install_script(sh, '-c',
+ ln_s.format(bindir / 'systemd-run',
+ bindir / 'uid0'))
+
+custom_target(
+ 'systemd-uid0',
+ input : 'systemd-uid0.in',
+ output : 'systemd-uid0',
+ command : [jinja2_cmdline, '@INPUT@', '@OUTPUT@'],
+ install : pamconfdir != 'no',
+ install_dir : pamconfdir)
#include "bus-unit-util.h"
#include "bus-wait-for-jobs.h"
#include "calendarspec.h"
+#include "color-util.h"
#include "env-util.h"
#include "escape.h"
#include "exit-status.h"
static char *arg_working_directory = NULL;
static bool arg_shell = false;
static char **arg_cmdline = NULL;
+static char *arg_exec_path = NULL;
+static bool arg_ignore_failure = false;
+static char *arg_background = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_description, freep);
STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_timer_property, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_working_directory, freep);
STATIC_DESTRUCTOR_REGISTER(arg_cmdline, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_exec_path, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_background, freep);
static int help(void) {
_cleanup_free_ char *link = NULL;
" -p --property=NAME=VALUE Set service or scope unit property\n"
" --description=TEXT Description for unit\n"
" --slice=SLICE Run in the specified slice\n"
- " --slice-inherit Inherit the slice\n"
+ " --slice-inherit Inherit the slice from the caller\n"
" --expand-environment=BOOL Control expansion of environment variables\n"
" --no-block Do not wait until operation finished\n"
" -r --remain-after-exit Leave service around until explicitly stopped\n"
" -q --quiet Suppress information messages during runtime\n"
" -G --collect Unload unit after it ran, even when failed\n"
" -S --shell Invoke a $SHELL interactively\n"
+ " --ignore-failure Ignore the exit status of the invoked process\n"
+ " --background=COLOR Set ANSI color for background\n"
"\n%3$sPath options:%4$s\n"
" --path-property=NAME=VALUE Set path unit property\n"
"\n%3$sSocket options:%4$s\n"
return 0;
}
+static int help_sudo_mode(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("uid0", "1", &link);
+ if (r < 0)
+ return log_oom();
+
+ printf("%s [OPTIONS...] COMMAND [ARGUMENTS...]\n"
+ "\n%sElevate privileges interactively.%s\n\n"
+ " -h --help Show this help\n"
+ " -V --version Show package version\n"
+ " --no-ask-password Do not prompt for password\n"
+ " --machine=CONTAINER Operate on local container\n"
+ " --unit=UNIT Run under the specified unit name\n"
+ " --property=NAME=VALUE Set service or scope unit property\n"
+ " --description=TEXT Description for unit\n"
+ " --slice=SLICE Run in the specified slice\n"
+ " --slice-inherit Inherit the slice\n"
+ " -u --user=USER Run as system user\n"
+ " -g --group=GROUP Run as system group\n"
+ " --nice=NICE Nice level\n"
+ " -D --chdir=PATH Set working directory\n"
+ " --setenv=NAME[=VALUE] Set environment variable\n"
+ " --background=COLOR Set ANSI color for background\n"
+ "\nSee the %s for details.\n",
+ program_invocation_short_name,
+ ansi_highlight(),
+ ansi_normal(),
+ link);
+
+ return 0;
+}
+
static int add_timer_property(const char *name, const char *val) {
char *p;
return 0;
}
+static char **make_login_shell_cmdline(const char *shell) {
+ _cleanup_free_ char *argv0 = NULL;
+
+ assert(shell);
+
+ argv0 = strjoin("-", shell); /* The - is how shells determine if they shall be consider login shells */
+ if (!argv0)
+ return NULL;
+
+ return strv_new(argv0);
+}
+
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_WAIT,
ARG_WORKING_DIRECTORY,
ARG_SHELL,
+ ARG_IGNORE_FAILURE,
+ ARG_BACKGROUND,
};
static const struct option options[] = {
{ "working-directory", required_argument, NULL, ARG_WORKING_DIRECTORY },
{ "same-dir", no_argument, NULL, 'd' },
{ "shell", no_argument, NULL, 'S' },
+ { "ignore-failure", no_argument, NULL, ARG_IGNORE_FAILURE },
+ { "background", no_argument, NULL, ARG_BACKGROUND },
{},
};
break;
case ARG_DESCRIPTION:
- r = free_and_strdup(&arg_description, optarg);
+ r = free_and_strdup_warn(&arg_description, optarg);
if (r < 0)
return r;
break;
arg_shell = true;
break;
+ case ARG_IGNORE_FAILURE:
+ arg_ignore_failure = true;
+ break;
+
+ case ARG_BACKGROUND:
+ r = free_and_strdup_warn(&arg_background, optarg);
+ if (r < 0)
+ return r;
+ break;
+
case '?':
return -EINVAL;
return 1;
}
+static int parse_argv_sudo_mode(int argc, char *argv[]) {
+
+ enum {
+ ARG_NO_ASK_PASSWORD = 0x100,
+ ARG_HOST,
+ ARG_MACHINE,
+ ARG_UNIT,
+ ARG_PROPERTY,
+ ARG_DESCRIPTION,
+ ARG_SLICE,
+ ARG_SLICE_INHERIT,
+ ARG_NICE,
+ ARG_SETENV,
+ ARG_BACKGROUND,
+ };
+
+ /* If invoked as "uid0" binary, let's expose a more sudo-like interface. We add various extensions
+ * though (but limit the extension to long options). */
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
+ { "machine", required_argument, NULL, ARG_MACHINE },
+ { "unit", required_argument, NULL, ARG_UNIT },
+ { "property", required_argument, NULL, ARG_PROPERTY },
+ { "description", required_argument, NULL, ARG_DESCRIPTION },
+ { "slice", required_argument, NULL, ARG_SLICE },
+ { "slice-inherit", no_argument, NULL, ARG_SLICE_INHERIT },
+ { "user", required_argument, NULL, 'u' },
+ { "group", required_argument, NULL, 'g' },
+ { "nice", required_argument, NULL, ARG_NICE },
+ { "chdir", required_argument, NULL, 'D' },
+ { "setenv", required_argument, NULL, ARG_SETENV },
+ { "background", required_argument, NULL, ARG_BACKGROUND },
+ {},
+ };
+
+ int r, c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
+ * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
+ optind = 0;
+ while ((c = getopt_long(argc, argv, "+hVu:g:D:", options, NULL)) >= 0)
+
+ switch (c) {
+
+ case 'h':
+ return help_sudo_mode();
+
+ case 'V':
+ return version();
+
+ case ARG_NO_ASK_PASSWORD:
+ arg_ask_password = false;
+ break;
+
+ case ARG_MACHINE:
+ arg_transport = BUS_TRANSPORT_MACHINE;
+ arg_host = optarg;
+ break;
+
+ case ARG_UNIT:
+ arg_unit = optarg;
+ break;
+
+ case ARG_PROPERTY:
+ if (strv_extend(&arg_property, optarg) < 0)
+ return log_oom();
+
+ break;
+
+ case ARG_DESCRIPTION:
+ r = free_and_strdup_warn(&arg_description, optarg);
+ if (r < 0)
+ return r;
+ break;
+
+ case ARG_SLICE:
+ arg_slice = optarg;
+ break;
+
+ case ARG_SLICE_INHERIT:
+ arg_slice_inherit = true;
+ break;
+
+ case 'u':
+ arg_exec_user = optarg;
+ break;
+
+ case 'g':
+ arg_exec_group = optarg;
+ break;
+
+ case ARG_NICE:
+ r = parse_nice(optarg, &arg_nice);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse nice value: %s", optarg);
+
+ arg_nice_set = true;
+ break;
+
+ case 'D':
+ r = parse_path_argument(optarg, true, &arg_working_directory);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case ARG_SETENV:
+ r = strv_env_replace_strdup_passthrough(&arg_environment, optarg);
+ if (r < 0)
+ return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
+
+ break;
+
+ case ARG_BACKGROUND:
+ r = free_and_strdup_warn(&arg_background, optarg);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached();
+ }
+
+ if (!arg_working_directory) {
+ if (arg_exec_user) {
+ /* When switching to a specific user, also switch to its home directory. */
+ arg_working_directory = strdup("~");
+ if (!arg_working_directory)
+ return log_oom();
+ } else {
+ /* When switching to root without this being specified, then stay in the current directory */
+ r = safe_getcwd(&arg_working_directory);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get current working directory: %m");
+ }
+ }
+
+ arg_service_type = "exec";
+ arg_quiet = true;
+ arg_wait = true;
+ arg_aggressive_gc = true;
+
+ arg_stdio = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO) ? ARG_STDIO_PTY : ARG_STDIO_DIRECT;
+ arg_expand_environment = false;
+ arg_send_sighup = true;
+
+ _cleanup_strv_free_ char **l = NULL;
+ if (argc > optind)
+ l = strv_copy(argv + optind);
+ else {
+ const char *e;
+
+ e = strv_env_get(arg_environment, "SHELL");
+ if (e)
+ arg_exec_path = strdup(e);
+ else {
+ if (arg_transport == BUS_TRANSPORT_LOCAL) {
+ r = get_shell(&arg_exec_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine shell: %m");
+ } else
+ arg_exec_path = strdup("/bin/sh");
+ }
+ if (!arg_exec_path)
+ return log_oom();
+
+ l = make_login_shell_cmdline(arg_exec_path);
+ }
+ if (!l)
+ return log_oom();
+
+ strv_free_and_replace(arg_cmdline, l);
+
+ if (!arg_slice) {
+ arg_slice = strdup("user.slice");
+ if (!arg_slice)
+ return log_oom();
+ }
+
+ _cleanup_free_ char *un = NULL;
+ un = getusername_malloc();
+ if (!un)
+ return log_oom();
+
+ /* Set a bunch of environment variables in a roughly sudo-compatible way */
+ r = strv_env_assign(&arg_environment, "SUDO_USER", un);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set $SUDO_USER environment variable: %m");
+
+ r = strv_env_assignf(&arg_environment, "SUDO_UID", UID_FMT, getuid());
+ if (r < 0)
+ return log_error_errno(r, "Failed to set $SUDO_UID environment variable: %m");
+
+ r = strv_env_assignf(&arg_environment, "SUDO_GID", GID_FMT, getgid());
+ if (r < 0)
+ return log_error_errno(r, "Failed to set $SUDO_GID environment variable: %m");
+
+ if (strv_extendf(&arg_property, "LogExtraFields=ELEVATED_UID=" UID_FMT, getuid()) < 0)
+ return log_oom();
+
+ if (strv_extendf(&arg_property, "LogExtraFields=ELEVATED_GID=" GID_FMT, getgid()) < 0)
+ return log_oom();
+
+ if (strv_extendf(&arg_property, "LogExtraFields=ELEVATED_USER=%s", un) < 0)
+ return log_oom();
+
+ if (strv_extend(&arg_property, "PAMName=systemd-uid0") < 0)
+ return log_oom();
+
+ if (!arg_background && arg_stdio == ARG_STDIO_PTY) {
+ double red, green, blue;
+
+ r = get_default_background_color(&red, &green, &blue);
+ if (r < 0)
+ log_debug_errno(r, "Unable to get terminal background color, not tinting background: %m");
+ else {
+ double h, s, v;
+
+ rgb_to_hsv(red, green, blue, &h, &s, &v);
+
+ if (!arg_exec_user || STR_IN_SET(arg_exec_user, "root", "0"))
+ h = 0; /* red */
+ else
+ h = 60 /* yellow */;
+
+ if (v > 50) /* If the background is bright, then pull down saturation */
+ s = 25;
+ else /* otherwise pump it up */
+ s = 75;
+
+ v = MAX(30, v); /* Make sure we don't hide the color in black */
+
+ uint8_t r8, g8, b8;
+ hsv_to_rgb(h, s, v, &r8, &g8, &b8);
+
+ if (asprintf(&arg_background, "48;2;%u;%u;%u", r8, g8, b8) < 0)
+ return log_oom();
+ }
+ }
+
+ return 1;
+}
+
static int transient_unit_set_properties(sd_bus_message *m, UnitType t, char **properties) {
int r;
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_append(m, "s", arg_cmdline[0]);
+ r = sd_bus_message_append(m, "s", arg_exec_path ?: arg_cmdline[0]);
if (r < 0)
return bus_log_create_error(r);
if (use_ex_prop)
r = sd_bus_message_append_strv(
m,
- STRV_MAKE(arg_expand_environment > 0 ? NULL : "no-env-expand"));
+ STRV_MAKE(arg_expand_environment > 0 ? NULL : "no-env-expand",
+ arg_ignore_failure ? "ignore-failure" : NULL));
else
- r = sd_bus_message_append(m, "b", false);
+ r = sd_bus_message_append(m, "b", arg_ignore_failure);
if (r < 0)
return bus_log_create_error(r);
/* Make sure to process any TTY events before we process bus events */
(void) pty_forward_set_priority(c.forward, SD_EVENT_PRIORITY_IMPORTANT);
+
+ if (!isempty(arg_background))
+ (void) pty_forward_set_background_color(c.forward, arg_background);
}
path = unit_dbus_path_from_name(service);
}
static bool shall_make_executable_absolute(void) {
+ if (arg_exec_path)
+ return false;
if (strv_isempty(arg_cmdline))
return false;
if (arg_transport != BUS_TRANSPORT_LOCAL)
log_parse_environment();
log_open();
- r = parse_argv(argc, argv);
+ if (invoked_as(argv, "uid0"))
+ r = parse_argv_sudo_mode(argc, argv);
+ else
+ r = parse_argv(argc, argv);
if (r <= 0)
return r;
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# This file is part of systemd.
+#
+# Used by uid0 sessions
+
+{% if ENABLE_HOMED %}
+-account sufficient pam_systemd_home.so
+{% endif %}
+account required pam_unix.so
+
+{% if HAVE_SELINUX %}
+session required pam_selinux.so close
+session required pam_selinux.so open
+{% endif %}
+session required pam_loginuid.so
+session optional pam_keyinit.so force revoke
+session required pam_namespace.so
+{% if ENABLE_HOMED %}
+-session optional pam_systemd_home.so
+{% endif %}
+session optional pam_umask.so silent
+session optional pam_systemd.so
+session required pam_unix.so
(void) loop_write(ttyfd, ANSI_NORMAL, SIZE_MAX);
new_termios = old_termios;
- new_termios.c_lflag &= ~(ICANON|ECHO);
- new_termios.c_cc[VMIN] = 1;
- new_termios.c_cc[VTIME] = 0;
+ termios_disable_echo(&new_termios);
r = RET_NERRNO(tcsetattr(ttyfd, TCSADRAIN, &new_termios));
if (r < 0)
int bus_test_polkit(
sd_bus_message *call,
- int capability,
const char *action,
const char **details,
uid_t good_user,
if (r != 0)
return r;
- r = sd_bus_query_sender_privilege(call, capability);
+ r = sd_bus_query_sender_privilege(call, -1);
if (r < 0)
return r;
if (r > 0)
* <- async_polkit_defer(q)
*/
-int bus_verify_polkit_async(
+int bus_verify_polkit_async_full(
sd_bus_message *call,
- int capability,
const char *action,
const char **details,
- bool interactive,
+ bool interactive, /* Use only for legacy method calls that have a separate "allow_interactive_authentication" field */
uid_t good_user,
Hashmap **registry,
sd_bus_error *ret_error) {
}
#endif
- r = sd_bus_query_sender_privilege(call, capability);
+ r = sd_bus_query_sender_privilege(call, -1);
if (r < 0)
return r;
if (r > 0)
#include "sd-bus.h"
#include "hashmap.h"
+#include "user-util.h"
-int bus_test_polkit(sd_bus_message *call, int capability, const char *action, const char **details, uid_t good_user, bool *_challenge, sd_bus_error *e);
+int bus_test_polkit(sd_bus_message *call, const char *action, const char **details, uid_t good_user, bool *_challenge, sd_bus_error *e);
+
+int bus_verify_polkit_async_full(sd_bus_message *call, const char *action, const char **details, bool interactive, uid_t good_user, Hashmap **registry, sd_bus_error *error);
+static inline int bus_verify_polkit_async(sd_bus_message *call, const char *action, const char **details, Hashmap **registry, sd_bus_error *ret_error) {
+ return bus_verify_polkit_async_full(call, action, details, false, UID_INVALID, registry, ret_error);
+}
-int bus_verify_polkit_async(sd_bus_message *call, int capability, const char *action, const char **details, bool interactive, uid_t good_user, Hashmap **registry, sd_bus_error *error);
Hashmap *bus_verify_polkit_async_registry_free(Hashmap *registry);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <math.h>
+
+#include "color-util.h"
+#include "macro.h"
+
+void rgb_to_hsv(double r, double g, double b,
+ double *ret_h, double *ret_s, double *ret_v) {
+
+ assert(r >= 0 && r <= 1);
+ assert(g >= 0 && g <= 1);
+ assert(b >= 0 && b <= 1);
+ assert(ret_h);
+ assert(ret_s);
+ assert(ret_v);
+
+ double max_color = fmax(r, fmax(g, b));
+ double min_color = fmin(r, fmin(g, b));
+ double delta = max_color - min_color;
+
+ *ret_v = max_color * 100.0;
+
+ if (max_color > 0)
+ *ret_s = delta / max_color * 100.0;
+ else {
+ *ret_s = 0;
+ *ret_h = NAN;
+ return;
+ }
+
+ if (delta > 0) {
+ if (r >= max_color)
+ *ret_h = 60 * fmod((g - b) / delta, 6);
+ else if (g >= max_color)
+ *ret_h = 60 * (((b - r) / delta) + 2);
+ else if (b >= max_color)
+ *ret_h = 60 * (((r - g) / delta) + 4);
+
+ *ret_h = fmod(*ret_h, 360);
+ } else
+ *ret_h = NAN;
+}
+
+void hsv_to_rgb(double h, double s, double v,
+ uint8_t* ret_r, uint8_t *ret_g, uint8_t *ret_b) {
+
+ double c, x, m, r, g, b;
+
+ assert(s >= 0 && s <= 100);
+ assert(v >= 0 && v <= 100);
+ assert(ret_r);
+ assert(ret_g);
+ assert(ret_b);
+
+ h = fmod(h, 360);
+ c = (s / 100.0) * (v / 100.0);
+ x = c * (1 - fabs(fmod(h / 60.0, 2) - 1));
+ m = (v / 100) - c;
+
+ if (h >= 0 && h < 60)
+ r = c, g = x, b = 0.0;
+ else if (h >= 60 && h < 120)
+ r = x, g = c, b = 0.0;
+ else if (h >= 120 && h < 180)
+ r = 0.0, g = c, b = x;
+ else if (h >= 180 && h < 240)
+ r = 0.0, g = x, b = c;
+ else if (h >= 240 && h < 300)
+ r = x, g = 0.0, b = c;
+ else
+ r = c, g = 0.0, b = x;
+
+ *ret_r = (uint8_t) ((r + m) * 255);
+ *ret_g = (uint8_t) ((g + m) * 255);
+ *ret_b = (uint8_t) ((b + m) * 255);
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+
+void rgb_to_hsv(double r, double g, double b,
+ double *ret_h, double *ret_s, double *ret_v);
+
+void hsv_to_rgb(
+ double h, double s, double v,
+ uint8_t* ret_r, uint8_t *ret_g, uint8_t *ret_b);
assert(config_path);
assert(lp);
- if (set_size(remove_symlinks_to) <= 0)
+ if (set_isempty(remove_symlinks_to))
return 0;
fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC);
'chown-recursive.c',
'clean-ipc.c',
'clock-util.c',
+ 'color-util.c',
'common-signal.c',
'compare-operator.c',
'condition.c',
'varlink.c',
'varlink-idl.c',
'varlink-io.systemd.c',
+ 'varlink-io.systemd.Credentials.c',
'varlink-io.systemd.Journal.c',
'varlink-io.systemd.ManagedOOM.c',
+ 'varlink-io.systemd.Network.c',
'varlink-io.systemd.PCRExtend.c',
- 'varlink-io.systemd.Resolve.Monitor.c',
'varlink-io.systemd.Resolve.c',
+ 'varlink-io.systemd.Resolve.Monitor.c',
'varlink-io.systemd.UserDatabase.c',
'varlink-io.systemd.oom.c',
'varlink-io.systemd.service.c',
#include "fd-util.h"
#include "hexdecoct.h"
#include "openssl-util.h"
+#include "random-util.h"
#include "string-util.h"
#if HAVE_OPENSSL
-/* For each error in the the OpenSSL thread error queue, log the provided message and the OpenSSL error
- * string. If there are no errors in the OpenSSL thread queue, this logs the message with "No openssl
+/* For each error in the OpenSSL thread error queue, log the provided message and the OpenSSL error
+ * string. If there are no errors in the OpenSSL thread queue, this logs the message with "No OpenSSL
* errors." This logs at level debug. Returns -EIO (or -ENOMEM). */
#define log_openssl_errors(fmt, ...) _log_openssl_errors(UNIQ, fmt, ##__VA_ARGS__)
#define _log_openssl_errors(u, fmt, ...) \
*ret_encrypt_key = TAKE_PTR(b);
*ret_encrypt_key_size = l;
-
return 0;
}
if (EVP_PKEY_derive(ctx, NULL, &shared_secret_size) <= 0)
return log_openssl_errors("Failed to get ECC shared secret size");
- _cleanup_free_ void *shared_secret = malloc(shared_secret_size);
+ _cleanup_(erase_and_freep) void *shared_secret = malloc(shared_secret_size);
if (!shared_secret)
return log_oom_debug();
return 0;
}
# endif
+
+static int ecc_pkey_generate_volume_keys(
+ EVP_PKEY *pkey,
+ void **ret_decrypted_key,
+ size_t *ret_decrypted_key_size,
+ void **ret_saved_key,
+ size_t *ret_saved_key_size) {
+
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey_new = NULL;
+ _cleanup_(erase_and_freep) void *decrypted_key = NULL;
+ _cleanup_free_ unsigned char *saved_key = NULL;
+ size_t decrypted_key_size, saved_key_size;
+ int nid = NID_undef;
+ int r;
+
+#if OPENSSL_VERSION_MAJOR >= 3
+ _cleanup_free_ char *curve_name = NULL;
+ size_t len = 0;
+
+ if (EVP_PKEY_get_group_name(pkey, NULL, 0, &len) != 1 || len == 0)
+ return log_openssl_errors("Failed to determine PKEY group name length");
+
+ len++;
+ curve_name = new(char, len);
+ if (!curve_name)
+ return log_oom_debug();
+
+ if (EVP_PKEY_get_group_name(pkey, curve_name, len, &len) != 1)
+ return log_openssl_errors("Failed to get PKEY group name");
+
+ nid = OBJ_sn2nid(curve_name);
+#else
+ EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey);
+ if (!ec_key)
+ return log_openssl_errors("PKEY doesn't have EC_KEY associated");
+
+ if (EC_KEY_check_key(ec_key) != 1)
+ return log_openssl_errors("EC_KEY associated with PKEY is not valid");
+
+ nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key));
+#endif
+
+ r = ecc_pkey_new(nid, &pkey_new);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to generate a new EC keypair: %m");
+
+ r = ecc_ecdh(pkey_new, pkey, &decrypted_key, &decrypted_key_size);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to derive shared secret: %m");
+
+#if OPENSSL_VERSION_MAJOR >= 3
+ /* EVP_PKEY_get1_encoded_public_key() always returns uncompressed format of EC points.
+ See https://github.com/openssl/openssl/discussions/22835 */
+ saved_key_size = EVP_PKEY_get1_encoded_public_key(pkey_new, &saved_key);
+ if (saved_key_size == 0)
+ return log_openssl_errors("Failed to convert the generated public key to SEC1 format");
+#else
+ EC_KEY *ec_key_new = EVP_PKEY_get0_EC_KEY(pkey_new);
+ if (!ec_key_new)
+ return log_openssl_errors("The generated key doesn't have associated EC_KEY");
+
+ if (EC_KEY_check_key(ec_key_new) != 1)
+ return log_openssl_errors("EC_KEY associated with the generated key is not valid");
+
+ saved_key_size = EC_POINT_point2oct(EC_KEY_get0_group(ec_key_new),
+ EC_KEY_get0_public_key(ec_key_new),
+ POINT_CONVERSION_UNCOMPRESSED,
+ NULL, 0, NULL);
+ if (saved_key_size == 0)
+ return log_openssl_errors("Failed to determine size of the generated public key");
+
+ saved_key = malloc(saved_key_size);
+ if (!saved_key)
+ return log_oom_debug();
+
+ saved_key_size = EC_POINT_point2oct(EC_KEY_get0_group(ec_key_new),
+ EC_KEY_get0_public_key(ec_key_new),
+ POINT_CONVERSION_UNCOMPRESSED,
+ saved_key, saved_key_size, NULL);
+ if (saved_key_size == 0)
+ return log_openssl_errors("Failed to convert the generated public key to SEC1 format");
+#endif
+
+ *ret_decrypted_key = TAKE_PTR(decrypted_key);
+ *ret_decrypted_key_size = decrypted_key_size;
+ *ret_saved_key = TAKE_PTR(saved_key);
+ *ret_saved_key_size = saved_key_size;
+ return 0;
+}
+
+static int rsa_pkey_generate_volume_keys(
+ EVP_PKEY *pkey,
+ void **ret_decrypted_key,
+ size_t *ret_decrypted_key_size,
+ void **ret_saved_key,
+ size_t *ret_saved_key_size) {
+
+ _cleanup_(erase_and_freep) void *decrypted_key = NULL;
+ _cleanup_free_ void *saved_key = NULL;
+ size_t decrypted_key_size, saved_key_size;
+ int r;
+
+ r = rsa_pkey_to_suitable_key_size(pkey, &decrypted_key_size);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to determine RSA public key size.");
+
+ log_debug("Generating %zu bytes random key.", decrypted_key_size);
+
+ decrypted_key = malloc(decrypted_key_size);
+ if (!decrypted_key)
+ return log_oom_debug();
+
+ r = crypto_random_bytes(decrypted_key, decrypted_key_size);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to generate random key: %m");
+
+ r = rsa_encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &saved_key, &saved_key_size);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to encrypt random key: %m");
+
+ *ret_decrypted_key = TAKE_PTR(decrypted_key);
+ *ret_decrypted_key_size = decrypted_key_size;
+ *ret_saved_key = TAKE_PTR(saved_key);
+ *ret_saved_key_size = saved_key_size;
+ return 0;
+}
+
+int x509_generate_volume_keys(
+ X509 *cert,
+ void **ret_decrypted_key,
+ size_t *ret_decrypted_key_size,
+ void **ret_saved_key,
+ size_t *ret_saved_key_size) {
+
+ assert(cert);
+ assert(ret_decrypted_key);
+ assert(ret_decrypted_key_size);
+ assert(ret_saved_key);
+ assert(ret_saved_key_size);
+
+ EVP_PKEY *pkey = X509_get0_pubkey(cert);
+ if (!pkey)
+ return log_openssl_errors("Failed to extract public key from X.509 certificate.");
+
+#if OPENSSL_VERSION_MAJOR >= 3
+ int type = EVP_PKEY_get_base_id(pkey);
+#else
+ int type = EVP_PKEY_base_id(pkey);
+#endif
+ switch (type) {
+
+ case EVP_PKEY_RSA:
+ return rsa_pkey_generate_volume_keys(pkey, ret_decrypted_key, ret_decrypted_key_size, ret_saved_key, ret_saved_key_size);
+
+ case EVP_PKEY_EC:
+ return ecc_pkey_generate_volume_keys(pkey, ret_decrypted_key, ret_decrypted_key_size, ret_saved_key, ret_saved_key_size);
+
+ case NID_undef:
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine a type of public key");
+
+ default:
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unsupported public key type: %s", OBJ_nid2sn(type));
+ }
+}
#endif
int x509_fingerprint(X509 *cert, uint8_t buffer[static SHA256_DIGEST_SIZE]) {
int ecc_ecdh(const EVP_PKEY *private_pkey, const EVP_PKEY *peer_pkey, void **ret_shared_secret, size_t *ret_shared_secret_size);
+int x509_generate_volume_keys(X509 *cert, void **ret_decrypted_key, size_t *ret_decrypted_key_size, void **ret_saved_key, size_t *ret_saved_key_size);
+
int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size);
int digest_and_sign(const EVP_MD *md, EVP_PKEY *privkey, const void *data, size_t size, void **ret, size_t *ret_size);
P11KitUri *search_uri,
CK_OBJECT_HANDLE *ret_object) {
- bool found_decrypt = false, found_class = false, found_key_type = false;
+ uint_fast8_t n_objects = 0;
+ bool found_class = false;
_cleanup_free_ CK_ATTRIBUTE *attributes_buffer = NULL;
- CK_ULONG n_attributes, a, n_objects;
- CK_ATTRIBUTE *attributes = NULL;
- CK_OBJECT_HANDLE objects[2];
- CK_RV rv, rv2;
- int r;
+ CK_OBJECT_HANDLE object, candidate;
+ static const CK_OBJECT_CLASS class = CKO_PRIVATE_KEY;
+ CK_BBOOL decrypt_value, derive_value;
+ CK_ATTRIBUTE optional_attributes[] = {
+ { CKA_DECRYPT, &decrypt_value, sizeof(decrypt_value) },
+ { CKA_DERIVE, &derive_value, sizeof(derive_value) }
+ };
+ CK_RV rv;
assert(m);
assert(search_uri);
assert(ret_object);
- r = dlopen_p11kit();
- if (r < 0)
- return r;
-
- attributes = sym_p11_kit_uri_get_attributes(search_uri, &n_attributes);
- for (a = 0; a < n_attributes; a++) {
+ CK_ULONG n_attributes;
+ CK_ATTRIBUTE *attributes = sym_p11_kit_uri_get_attributes(search_uri, &n_attributes);
+ for (CK_ULONG i = 0; i < n_attributes; i++) {
/* We use the URI's included match attributes, but make them more strict. This allows users
* to specify a token URL instead of an object URL and the right thing should happen if
* there's only one suitable key on the token. */
- switch (attributes[a].type) {
-
+ switch (attributes[i].type) {
case CKA_CLASS: {
CK_OBJECT_CLASS c;
- if (attributes[a].ulValueLen != sizeof(c))
+ if (attributes[i].ulValueLen != sizeof(c))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_CLASS attribute size.");
- memcpy(&c, attributes[a].pValue, sizeof(c));
+ memcpy(&c, attributes[i].pValue, sizeof(c));
if (c != CKO_PRIVATE_KEY)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Selected PKCS#11 object is not a private key, refusing.");
found_class = true;
break;
- }
+ }}
+ }
+
+ if (!found_class) {
+ /* Hmm, let's slightly extend the attribute list we search for */
- case CKA_DECRYPT: {
- CK_BBOOL b;
+ attributes_buffer = new(CK_ATTRIBUTE, n_attributes + 1);
+ if (!attributes_buffer)
+ return log_oom();
- if (attributes[a].ulValueLen != sizeof(b))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_DECRYPT attribute size.");
+ memcpy(attributes_buffer, attributes, sizeof(CK_ATTRIBUTE) * n_attributes);
- memcpy(&b, attributes[a].pValue, sizeof(b));
- if (!b)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Selected PKCS#11 object is not suitable for decryption, refusing.");
+ attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
+ .type = CKA_CLASS,
+ .pValue = (CK_OBJECT_CLASS*) &class,
+ .ulValueLen = sizeof(class),
+ };
+
+ attributes = attributes_buffer;
+ }
+
+ rv = m->C_FindObjectsInit(session, attributes, n_attributes);
+ if (rv != CKR_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to initialize object find call: %s", sym_p11_kit_strerror(rv));
+
+ for (;;) {
+ CK_ULONG b;
+ rv = m->C_FindObjects(session, &candidate, 1, &b);
+ if (rv != CKR_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to find objects: %s", sym_p11_kit_strerror(rv));
- found_decrypt = true;
+ if (b == 0)
break;
+
+ bool can_decrypt = false, can_derive = false;
+ optional_attributes[0].ulValueLen = sizeof(decrypt_value);
+ optional_attributes[1].ulValueLen = sizeof(derive_value);
+
+ rv = m->C_GetAttributeValue(session, candidate, optional_attributes, ELEMENTSOF(optional_attributes));
+ if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to get attributes of a selected private key: %s", sym_p11_kit_strerror(rv));
+
+ if (optional_attributes[0].ulValueLen != CK_UNAVAILABLE_INFORMATION && decrypt_value == CK_TRUE)
+ can_decrypt = true;
+
+ if (optional_attributes[1].ulValueLen != CK_UNAVAILABLE_INFORMATION && derive_value == CK_TRUE)
+ can_derive = true;
+
+ if (can_decrypt || can_derive) {
+ n_objects++;
+ if (n_objects > 1)
+ break;
+ object = candidate;
}
+ }
- case CKA_KEY_TYPE: {
- CK_KEY_TYPE t;
+ rv = m->C_FindObjectsFinal(session);
+ if (rv != CKR_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to finalize object find call: %s", sym_p11_kit_strerror(rv));
- if (attributes[a].ulValueLen != sizeof(t))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_KEY_TYPE attribute size.");
+ if (n_objects == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
+ "Failed to find selected private key suitable for decryption or derivation on token.");
- memcpy(&t, attributes[a].pValue, sizeof(t));
- if (t != CKK_RSA)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected PKCS#11 object is not an RSA key, refusing.");
+ if (n_objects > 1)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
+ "Configured private key URI matches multiple keys, refusing.");
- found_key_type = true;
- break;
- }}
+ *ret_object = object;
+ return 0;
+}
+
+static const char* object_class_to_string(CK_OBJECT_CLASS class) {
+ switch (class) {
+ case CKO_CERTIFICATE:
+ return "CKO_CERTIFICATE";
+ case CKO_PUBLIC_KEY:
+ return "CKO_PUBLIC_KEY";
+ case CKO_PRIVATE_KEY:
+ return "CKO_PRIVATE_KEY";
+ case CKO_SECRET_KEY:
+ return "CKO_SECRET_KEY";
+ default:
+ return NULL;
}
+}
- if (!found_decrypt || !found_class || !found_key_type) {
- /* Hmm, let's slightly extend the attribute list we search for */
+/* Returns an object with the given class and the same CKA_ID or CKA_LABEL as prototype */
+int pkcs11_token_find_related_object(
+ CK_FUNCTION_LIST *m,
+ CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE prototype,
+ CK_OBJECT_CLASS class,
+ CK_OBJECT_HANDLE *ret_object ) {
- attributes_buffer = new(CK_ATTRIBUTE, n_attributes + !found_decrypt + !found_class + !found_key_type);
- if (!attributes_buffer)
+ _cleanup_free_ void *buffer = NULL;
+ CK_ATTRIBUTE attributes[] = {
+ { CKA_ID, NULL_PTR, 0 },
+ { CKA_LABEL, NULL_PTR, 0 }
+ };
+ CK_OBJECT_CLASS search_class = class;
+ CK_ATTRIBUTE search_attributes[2] = {
+ { CKA_CLASS, &search_class, sizeof(search_class) }
+ };
+ CK_ULONG n_objects;
+ CK_OBJECT_HANDLE objects[2];
+ CK_RV rv;
+
+ rv = m->C_GetAttributeValue(session, prototype, attributes, ELEMENTSOF(attributes));
+ if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve length of attributes: %s", sym_p11_kit_strerror(rv));
+
+ if (attributes[0].ulValueLen != CK_UNAVAILABLE_INFORMATION) {
+ buffer = malloc(attributes[0].ulValueLen);
+ if (!buffer)
return log_oom();
- memcpy(attributes_buffer, attributes, sizeof(CK_ATTRIBUTE) * n_attributes);
+ attributes[0].pValue = buffer;
+ rv = m->C_GetAttributeValue(session, prototype, &attributes[0], 1);
+ if (rv != CKR_OK)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to retrieve CKA_ID: %s", sym_p11_kit_strerror(rv));
- if (!found_decrypt) {
- static const CK_BBOOL yes = true;
+ search_attributes[1] = attributes[0];
- attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
- .type = CKA_DECRYPT,
- .pValue = (CK_BBOOL*) &yes,
- .ulValueLen = sizeof(yes),
- };
- }
+ } else if (attributes[1].ulValueLen != CK_UNAVAILABLE_INFORMATION) {
+ buffer = malloc(attributes[1].ulValueLen);
+ if (!buffer)
+ return log_oom();
- if (!found_class) {
- static const CK_OBJECT_CLASS class = CKO_PRIVATE_KEY;
+ attributes[1].pValue = buffer;
+ rv = m->C_GetAttributeValue(session, prototype, &attributes[1], 1);
+ if (rv != CKR_OK)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to retrieve CKA_LABEL: %s", sym_p11_kit_strerror(rv));
- attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
- .type = CKA_CLASS,
- .pValue = (CK_OBJECT_CLASS*) &class,
- .ulValueLen = sizeof(class),
- };
- }
+ search_attributes[1] = attributes[1];
- if (!found_key_type) {
- static const CK_KEY_TYPE type = CKK_RSA;
+ } else
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "The prototype does not have CKA_ID or CKA_LABEL");
- attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
- .type = CKA_KEY_TYPE,
- .pValue = (CK_KEY_TYPE*) &type,
- .ulValueLen = sizeof(type),
- };
- }
+ rv = m->C_FindObjectsInit(session, search_attributes, 2);
+ if (rv != CKR_OK)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to initialize object find call: %s", sym_p11_kit_strerror(rv));
- attributes = attributes_buffer;
+ rv = m->C_FindObjects(session, objects, 2, &n_objects);
+ if (rv != CKR_OK)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to find objects: %s", sym_p11_kit_strerror(rv));
+
+ rv = m->C_FindObjectsFinal(session);
+ if (rv != CKR_OK)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to finalize object find call: %s", sym_p11_kit_strerror(rv));
+
+ if (n_objects == 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
+ "Failed to find a related object with class %s", object_class_to_string(class));
+
+ if (n_objects > 1)
+ log_warning("Found multiple related objects with class %s, using the first object.",
+ object_class_to_string(class));
+
+ *ret_object = objects[0];
+ return 0;
+}
+
+#if HAVE_OPENSSL
+static int ecc_convert_to_compressed(
+ CK_FUNCTION_LIST *m,
+ CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object,
+ const void *uncompressed_point,
+ size_t uncompressed_point_size,
+ void **ret_compressed_point,
+ size_t *ret_compressed_point_size) {
+
+ _cleanup_free_ void *ec_params_buffer = NULL;
+ CK_ATTRIBUTE ec_params_attr = { CKA_EC_PARAMS, NULL_PTR, 0 };
+ CK_RV rv;
+ int r;
+
+ rv = m->C_GetAttributeValue(session, object, &ec_params_attr, 1);
+ if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to retrieve length of CKA_EC_PARAMS: %s", sym_p11_kit_strerror(rv));
+
+ if (ec_params_attr.ulValueLen != CK_UNAVAILABLE_INFORMATION) {
+ ec_params_buffer = malloc(ec_params_attr.ulValueLen);
+ if (!ec_params_buffer)
+ return log_oom();
+
+ ec_params_attr.pValue = ec_params_buffer;
+ rv = m->C_GetAttributeValue(session, object, &ec_params_attr, 1);
+ if (rv != CKR_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to retrieve CKA_EC_PARAMS from a private key: %s", sym_p11_kit_strerror(rv));
+ } else {
+ CK_OBJECT_HANDLE public_key;
+ r = pkcs11_token_find_related_object(m, session, object, CKO_PUBLIC_KEY, &public_key);
+ if (r < 0)
+ return log_error_errno(r, "Failed to find a public key for compressing a EC point");
+
+ ec_params_attr.ulValueLen = 0;
+ rv = m->C_GetAttributeValue(session, public_key, &ec_params_attr, 1);
+ if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to retrieve length of CKA_EC_PARAMS: %s", sym_p11_kit_strerror(rv));
+
+ if (ec_params_attr.ulValueLen == CK_UNAVAILABLE_INFORMATION)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "The public key does not have CKA_EC_PARAMS");
+
+ ec_params_buffer = malloc(ec_params_attr.ulValueLen);
+ if (!ec_params_buffer)
+ return log_oom();
+
+ ec_params_attr.pValue = ec_params_buffer;
+ rv = m->C_GetAttributeValue(session, public_key, &ec_params_attr, 1);
+ if (rv != CKR_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to retrieve CKA_EC_PARAMS from a public key: %s", sym_p11_kit_strerror(rv));
}
- rv = m->C_FindObjectsInit(session, attributes, n_attributes);
+ _cleanup_(EC_GROUP_freep) EC_GROUP *group = NULL;
+ _cleanup_(EC_POINT_freep) EC_POINT *point = NULL;
+ _cleanup_(BN_CTX_freep) BN_CTX *bnctx = NULL;
+ _cleanup_free_ void *compressed_point = NULL;
+ size_t compressed_point_size;
+
+ const unsigned char *ec_params_value = ec_params_attr.pValue;
+ group = d2i_ECPKParameters(NULL, &ec_params_value, ec_params_attr.ulValueLen);
+ if (!group)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unable to decode CKA_EC_PARAMS");
+
+ point = EC_POINT_new(group);
+ if (!point)
+ return log_oom();
+
+ bnctx = BN_CTX_new();
+ if (!bnctx)
+ return log_oom();
+
+ if (EC_POINT_oct2point(group, point, uncompressed_point, uncompressed_point_size, bnctx) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unable to decode an uncompressed EC point");
+
+ compressed_point_size = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, NULL, 0, bnctx);
+ if (compressed_point_size == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to determine size of a compressed EC point");
+
+ compressed_point = malloc(compressed_point_size);
+ if (!compressed_point)
+ return log_oom();
+
+ compressed_point_size = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, compressed_point, compressed_point_size, bnctx);
+ if (compressed_point_size == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert a EC point to compressed format");
+
+ *ret_compressed_point = TAKE_PTR(compressed_point);
+ *ret_compressed_point_size = compressed_point_size;
+ return 0;
+}
+#endif
+
+/* Since EC keys doesn't support encryption directly, we use ECDH protocol to derive shared secret here.
+ * We use PKCS#11 C_DeriveKey function to derive a shared secret with a private key stored in the token and
+ * a public key saved on enrollment. */
+static int pkcs11_token_decrypt_data_ecc(
+ CK_FUNCTION_LIST *m,
+ CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object,
+ const void *encrypted_data,
+ size_t encrypted_data_size,
+ void **ret_decrypted_data,
+ size_t *ret_decrypted_data_size) {
+
+ static const CK_BBOOL yes = CK_TRUE, no = CK_FALSE;
+ static const CK_OBJECT_CLASS shared_secret_class = CKO_SECRET_KEY;
+ static const CK_KEY_TYPE shared_secret_type = CKK_GENERIC_SECRET;
+ static const CK_ATTRIBUTE shared_secret_template[] = {
+ { CKA_TOKEN, (void*) &no, sizeof(no) },
+ { CKA_CLASS, (void*) &shared_secret_class, sizeof(shared_secret_class) },
+ { CKA_KEY_TYPE, (void*) &shared_secret_type, sizeof(shared_secret_type) },
+ { CKA_SENSITIVE, (void*) &no, sizeof(no) },
+ { CKA_EXTRACTABLE, (void*) &yes, sizeof(yes) }
+ };
+ CK_ECDH1_DERIVE_PARAMS params = {
+ .kdf = CKD_NULL,
+ .pPublicData = (void*) encrypted_data,
+ .ulPublicDataLen = encrypted_data_size
+ };
+ CK_MECHANISM mechanism = {
+ .mechanism = CKM_ECDH1_DERIVE,
+ .pParameter = ¶ms,
+ .ulParameterLen = sizeof(params)
+ };
+ CK_OBJECT_HANDLE shared_secret_handle;
+ CK_SESSION_INFO session_info;
+ CK_MECHANISM_INFO mechanism_info;
+ CK_RV rv, rv2;
+#if HAVE_OPENSSL
+ _cleanup_free_ void *compressed_point = NULL;
+ int r;
+#endif
+
+ rv = m->C_GetSessionInfo(session, &session_info);
if (rv != CKR_OK)
return log_error_errno(SYNTHETIC_ERRNO(EIO),
- "Failed to initialize object find call: %s", sym_p11_kit_strerror(rv));
+ "Failed to get information about the PKCS#11 session: %s", sym_p11_kit_strerror(rv));
- rv = m->C_FindObjects(session, objects, ELEMENTSOF(objects), &n_objects);
- rv2 = m->C_FindObjectsFinal(session);
+ rv = m->C_GetMechanismInfo(session_info.slotID, CKM_ECDH1_DERIVE, &mechanism_info);
if (rv != CKR_OK)
return log_error_errno(SYNTHETIC_ERRNO(EIO),
- "Failed to find objects: %s", sym_p11_kit_strerror(rv));
+ "Failed to get information about CKM_ECDH1_DERIVE: %s", sym_p11_kit_strerror(rv));
+
+ if (!(mechanism_info.flags & CKF_EC_UNCOMPRESS)) {
+ if (mechanism_info.flags & CKF_EC_COMPRESS) {
+#if HAVE_OPENSSL
+ log_debug("CKM_ECDH1_DERIVE accepts compressed EC points only, trying to convert.");
+ size_t compressed_point_size;
+ r = ecc_convert_to_compressed(m, session, object, encrypted_data, encrypted_data_size, &compressed_point, &compressed_point_size);
+ if (r < 0)
+ return r;
+
+ params.pPublicData = compressed_point;
+ params.ulPublicDataLen = compressed_point_size;
+#else
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "CKM_ECDH1_DERIVE does not support uncompressed format of EC points");
+#endif
+ } else
+ log_debug("Both CKF_EC_UNCOMPRESS and CKF_EC_COMPRESS are false for CKM_ECDH1_DERIVE, ignoring.");
+ }
+
+ rv = m->C_DeriveKey(session, &mechanism, object, (CK_ATTRIBUTE*) shared_secret_template, ELEMENTSOF(shared_secret_template), &shared_secret_handle);
+ if (rv != CKR_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to derive a shared secret: %s", sym_p11_kit_strerror(rv));
+
+ CK_ATTRIBUTE shared_secret_attr = { CKA_VALUE, NULL_PTR, 0};
+
+ rv = m->C_GetAttributeValue(session, shared_secret_handle, &shared_secret_attr, 1);
+ if (rv != CKR_OK) {
+ rv2 = m->C_DestroyObject(session, shared_secret_handle);
+ if (rv2 != CKR_OK)
+ log_warning("Failed to destroy a shared secret, ignoring: %s", sym_p11_kit_strerror(rv2));
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve shared secret length: %s", sym_p11_kit_strerror(rv));
+ }
+
+ shared_secret_attr.pValue = malloc(shared_secret_attr.ulValueLen);
+ if (!shared_secret_attr.pValue)
+ return log_oom();
+
+ rv = m->C_GetAttributeValue(session, shared_secret_handle, &shared_secret_attr, 1);
+ rv2 = m->C_DestroyObject(session, shared_secret_handle);
if (rv2 != CKR_OK)
- return log_error_errno(SYNTHETIC_ERRNO(EIO),
- "Failed to finalize object find call: %s", sym_p11_kit_strerror(rv));
- if (n_objects == 0)
- return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
- "Failed to find selected private key suitable for decryption on token.");
- if (n_objects > 1)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
- "Configured private key URI matches multiple keys, refusing.");
+ log_warning("Failed to destroy a shared secret, ignoring: %s", sym_p11_kit_strerror(rv2));
- *ret_object = objects[0];
+ if (rv != CKR_OK) {
+ erase_and_free(shared_secret_attr.pValue);
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve a shared secret: %s", sym_p11_kit_strerror(rv));
+ }
+
+ log_info("Successfully derived key with security token.");
+
+ *ret_decrypted_data = shared_secret_attr.pValue;
+ *ret_decrypted_data_size = shared_secret_attr.ulValueLen;
return 0;
}
-int pkcs11_token_decrypt_data(
+static int pkcs11_token_decrypt_data_rsa(
CK_FUNCTION_LIST *m,
CK_SESSION_HANDLE session,
CK_OBJECT_HANDLE object,
_cleanup_(erase_and_freep) CK_BYTE *dbuffer = NULL;
CK_ULONG dbuffer_size = 0;
CK_RV rv;
- int r;
-
- assert(m);
- assert(encrypted_data);
- assert(encrypted_data_size > 0);
- assert(ret_decrypted_data);
- assert(ret_decrypted_data_size);
-
- r = dlopen_p11kit();
- if (r < 0)
- return r;
rv = m->C_DecryptInit(session, (CK_MECHANISM*) &mechanism, object);
if (rv != CKR_OK)
return 0;
}
+int pkcs11_token_decrypt_data(
+ CK_FUNCTION_LIST *m,
+ CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object,
+ const void *encrypted_data,
+ size_t encrypted_data_size,
+ void **ret_decrypted_data,
+ size_t *ret_decrypted_data_size) {
+
+ CK_KEY_TYPE key_type;
+ CK_ATTRIBUTE key_type_template = { CKA_KEY_TYPE, &key_type, sizeof(key_type) };
+ CK_RV rv;
+
+ assert(m);
+ assert(encrypted_data);
+ assert(encrypted_data_size > 0);
+ assert(ret_decrypted_data);
+ assert(ret_decrypted_data_size);
+
+ rv = m->C_GetAttributeValue(session, object, &key_type_template, 1);
+ if (rv != CKR_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve private key type");
+
+ switch (key_type) {
+
+ case CKK_RSA:
+ return pkcs11_token_decrypt_data_rsa(m, session, object, encrypted_data, encrypted_data_size, ret_decrypted_data, ret_decrypted_data_size);
+
+ case CKK_EC:
+ return pkcs11_token_decrypt_data_ecc(m, session, object, encrypted_data, encrypted_data_size, ret_decrypted_data, ret_decrypted_data_size);
+
+ default:
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unsupported private key type: %lu", key_type);
+ }
+}
+
int pkcs11_token_acquire_rng(
CK_FUNCTION_LIST *m,
CK_SESSION_HANDLE session) {
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#if HAVE_OPENSSL
+# include <openssl/x509.h>
+#endif
#include <stdbool.h>
#if HAVE_P11KIT
#include "ask-password-api.h"
#include "macro.h"
-#include "openssl-util.h"
#include "time-util.h"
bool pkcs11_uri_valid(const char *uri);
int pkcs11_token_login_by_pin(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, const CK_TOKEN_INFO *token_info, const char *token_label, const void *pin, size_t pin_size);
int pkcs11_token_login(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_SLOT_ID slotid, const CK_TOKEN_INFO *token_info, const char *friendly_name, const char *icon_name, const char *key_name, const char *credential_name, usec_t until, AskPasswordFlags ask_password_flags, bool headless, char **ret_used_pin);
+int pkcs11_token_find_related_object(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE prototype, CK_OBJECT_CLASS class, CK_OBJECT_HANDLE *ret_object);
int pkcs11_token_find_x509_certificate(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, P11KitUri *search_uri, CK_OBJECT_HANDLE *ret_object);
#if HAVE_OPENSSL
int pkcs11_token_read_x509_certificate(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, X509 **ret_cert);
#include "alloc-util.h"
#include "errno-util.h"
+#include "extract-word.h"
#include "fd-util.h"
#include "io-util.h"
#include "log.h"
#include "macro.h"
#include "ptyfwd.h"
#include "stat-util.h"
+#include "strv.h"
#include "terminal-util.h"
#include "time-util.h"
+typedef enum AnsiColorState {
+ ANSI_COLOR_STATE_TEXT,
+ ANSI_COLOR_STATE_ESC,
+ ANSI_COLOR_STATE_CSI_SEQUENCE,
+ ANSI_COLOR_STATE_NEWLINE,
+ _ANSI_COLOR_STATE_MAX,
+ _ANSI_COLOR_STATE_INVALID = -EINVAL,
+} AnsiColorState;
+
struct PTYForward {
sd_event *event;
bool last_char_set:1;
char last_char;
- char in_buffer[LINE_MAX], out_buffer[LINE_MAX];
+ char in_buffer[LINE_MAX], *out_buffer;
+ size_t out_buffer_size;
size_t in_buffer_full, out_buffer_full;
usec_t escape_timestamp;
PTYForwardHandler handler;
void *userdata;
+
+ char *background_color;
+ AnsiColorState ansi_color_state;
+ char *csi_sequence;
};
#define ESCAPE_USEC (1*USEC_PER_SEC)
}
f->saved_stdout = f->saved_stdin = false;
+
+ f->out_buffer = mfree(f->out_buffer);
+ f->out_buffer_size = 0;
+ f->out_buffer_full = 0;
+ f->in_buffer_full = 0;
+
+ f->csi_sequence = mfree(f->csi_sequence);
+ f->ansi_color_state = _ANSI_COLOR_STATE_INVALID;
}
static int pty_forward_done(PTYForward *f, int rcode) {
return true;
}
+static char *background_color_sequence(PTYForward *f) {
+ assert(f);
+ assert(f->background_color);
+
+ /* This sets the background color to the desired one, and erase the rest of the line with it */
+
+ return strjoin("\x1B[", f->background_color, "m", ANSI_ERASE_TO_END_OF_LINE);
+}
+
+static int insert_string(PTYForward *f, size_t offset, const char *s) {
+ assert(f);
+ assert(offset <= f->out_buffer_full);
+ assert(s);
+
+ size_t l = strlen(s);
+ assert(l <= INT_MAX); /* Make sure we can still return this */
+
+ void *p = realloc(f->out_buffer, MAX(f->out_buffer_full + l, (size_t) LINE_MAX));
+ if (!p)
+ return -ENOMEM;
+
+ f->out_buffer = p;
+ f->out_buffer_size = MALLOC_SIZEOF_SAFE(f->out_buffer);
+
+ memmove(f->out_buffer + offset + l, f->out_buffer + offset, f->out_buffer_full - offset);
+ memcpy(f->out_buffer + offset, s, l);
+ f->out_buffer_full += l;
+
+ return (int) l;
+}
+
+static int insert_erase_newline(PTYForward *f, size_t offset) {
+ _cleanup_free_ char *s = NULL;
+
+ assert(f);
+ assert(f->background_color);
+
+ s = background_color_sequence(f);
+ if (!s)
+ return -ENOMEM;
+
+ return insert_string(f, offset, s);
+}
+
+static int is_csi_background_reset_sequence(const char *seq) {
+ enum {
+ COLOR_TOKEN_NO,
+ COLOR_TOKEN_START,
+ COLOR_TOKEN_8BIT,
+ COLOR_TOKEN_24BIT_R,
+ COLOR_TOKEN_24BIT_G,
+ COLOR_TOKEN_24BIT_B,
+ } token_state = COLOR_TOKEN_NO;
+
+ bool b = false;
+ int r;
+
+ assert(seq);
+
+ /* This parses CSI "m" sequences, and determines if they reset the background color. If so returns
+ * 1. This can then be used to insert another sequence that sets the color to the desired
+ * replacement. */
+
+ for (;;) {
+ _cleanup_free_ char *token = NULL;
+
+ r = extract_first_word(&seq, &token, ";", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_RETAIN_ESCAPE);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ switch (token_state) {
+
+ case COLOR_TOKEN_NO:
+
+ if (STR_IN_SET(token, "", "0", "00", "49"))
+ b = true; /* These tokens set the background back to normal */
+ else if (STR_IN_SET(token, "40", "41", "42", "43", "44", "45", "46", "47", "48"))
+ b = false; /* And these tokens set them to something other than normal */
+
+ if (STR_IN_SET(token, "38", "48", "58"))
+ token_state = COLOR_TOKEN_START; /* These tokens mean an 8bit or 24bit color will follow */
+ break;
+
+ case COLOR_TOKEN_START:
+
+ if (STR_IN_SET(token, "5", "05"))
+ token_state = COLOR_TOKEN_8BIT; /* 8bit color */
+ else if (STR_IN_SET(token, "2", "02"))
+ token_state = COLOR_TOKEN_24BIT_R; /* 24bit color */
+ else
+ token_state = COLOR_TOKEN_NO; /* something weird? */
+ break;
+
+ case COLOR_TOKEN_24BIT_R:
+ token_state = COLOR_TOKEN_24BIT_G;
+ break;
+
+ case COLOR_TOKEN_24BIT_G:
+ token_state = COLOR_TOKEN_24BIT_B;
+ break;
+
+ case COLOR_TOKEN_8BIT:
+ case COLOR_TOKEN_24BIT_B:
+ token_state = COLOR_TOKEN_NO;
+ break;
+ }
+ }
+
+ return b;
+}
+
+static int insert_background_fix(PTYForward *f, size_t offset) {
+ assert(f);
+ assert(f->background_color);
+
+ if (!is_csi_background_reset_sequence(strempty(f->csi_sequence)))
+ return 0;
+
+ _cleanup_free_ char *s = NULL;
+ s = strjoin(";", f->background_color);
+ if (!s)
+ return -ENOMEM;
+
+ return insert_string(f, offset, s);
+}
+
+static int pty_forward_ansi_process(PTYForward *f, size_t offset) {
+ int r;
+
+ assert(f);
+ assert(offset <= f->out_buffer_full);
+
+ if (!f->background_color)
+ return 0;
+
+ for (size_t i = offset; i < f->out_buffer_full; i++) {
+ char c = f->out_buffer[i];
+
+ switch (f->ansi_color_state) {
+
+ case ANSI_COLOR_STATE_TEXT:
+ if (c == '\n')
+ f->ansi_color_state = ANSI_COLOR_STATE_NEWLINE;
+ if (c == 0x1B) /* ESC */
+ f->ansi_color_state = ANSI_COLOR_STATE_ESC;
+ break;
+
+ case ANSI_COLOR_STATE_NEWLINE: {
+ /* Immediately after a newline insert an ANSI sequence to erase the line with a background color */
+
+ r = insert_erase_newline(f, i);
+ if (r < 0)
+ return r;
+
+ i += r;
+
+ f->ansi_color_state = ANSI_COLOR_STATE_TEXT;
+ break;
+ }
+
+ case ANSI_COLOR_STATE_ESC: {
+
+ if (c == '[')
+ f->ansi_color_state = ANSI_COLOR_STATE_CSI_SEQUENCE;
+ else
+ f->ansi_color_state = ANSI_COLOR_STATE_TEXT;
+
+ break;
+ }
+
+ case ANSI_COLOR_STATE_CSI_SEQUENCE: {
+
+ if (c >= 0x20 && c <= 0x3F) {
+ /* If this is a "parameter" or "intermediary" byte (i.e. ranges 0x20…0x2F and
+ * 0x30…0x3F) then we are still in the CSI sequence */
+
+ if (strlen_ptr(f->csi_sequence) >= 64) {
+ /* Safety check: lets not accept unbounded CSI sequences */
+
+ f->csi_sequence = mfree(f->csi_sequence);
+ f->ansi_color_state = ANSI_COLOR_STATE_TEXT;
+ } else if (!strextend(&f->csi_sequence, CHAR_TO_STR(c)))
+ return -ENOMEM;
+ } else {
+ /* Otherwise, the CSI sequence is over */
+
+ if (c == 'm') {
+ /* This is an "SGR" (Select Graphic Rendition) sequence. Patch in our background color. */
+ r = insert_background_fix(f, i);
+ if (r < 0)
+ return r;
+
+ i += r;
+ }
+
+ f->csi_sequence = mfree(f->csi_sequence);
+ f->ansi_color_state = ANSI_COLOR_STATE_TEXT;
+ }
+
+ break;
+ }
+
+ default:
+ assert_not_reached();
+ }
+ }
+
+ return 0;
+}
+
static int shovel(PTYForward *f) {
ssize_t k;
+ int r;
assert(f);
+ if (f->out_buffer_size == 0 && f->background_color) {
+ /* Erase the first line when we start */
+ f->out_buffer = background_color_sequence(f);
+ if (!f->out_buffer)
+ return pty_forward_done(f, log_oom());
+
+ f->out_buffer_full = strlen(f->out_buffer);
+ f->out_buffer_size = MALLOC_SIZEOF_SAFE(f->out_buffer);
+ }
+
+ if (f->out_buffer_size < LINE_MAX) {
+ /* Make sure we always have room for at least one "line" */
+ void *p = realloc(f->out_buffer, LINE_MAX);
+ if (!p)
+ return pty_forward_done(f, log_oom());
+
+ f->out_buffer = p;
+ f->out_buffer_size = MALLOC_SIZEOF_SAFE(p);
+ }
+
while ((f->stdin_readable && f->in_buffer_full <= 0) ||
(f->master_writable && f->in_buffer_full > 0) ||
(f->master_readable && f->out_buffer_full <= 0) ||
}
}
- if (f->master_readable && f->out_buffer_full < LINE_MAX) {
+ if (f->master_readable && f->out_buffer_full < MIN(f->out_buffer_size, (size_t) LINE_MAX)) {
- k = read(f->master, f->out_buffer + f->out_buffer_full, LINE_MAX - f->out_buffer_full);
+ k = read(f->master, f->out_buffer + f->out_buffer_full, f->out_buffer_size - f->out_buffer_full);
if (k < 0) {
/* Note that EIO on the master device might be caused by vhangup() or
}
} else {
f->read_from_master = true;
+ size_t scan_index = f->out_buffer_full;
f->out_buffer_full += (size_t) k;
+
+ r = pty_forward_ansi_process(f, scan_index);
+ if (r < 0)
+ return pty_forward_done(f, log_error_errno(r, "Failed to scan for ANSI sequences: %m"));
}
}
if (!f)
return NULL;
pty_forward_disconnect(f);
+ free(f->background_color);
return mfree(f);
}
return 0;
}
+
+int pty_forward_set_background_color(PTYForward *f, const char *color) {
+ assert(f);
+
+ return free_and_strdup(&f->background_color, color);
+}
int pty_forward_set_width_height(PTYForward *f, unsigned width, unsigned height);
+int pty_forward_set_background_color(PTYForward *f, const char *color);
+
DEFINE_TRIVIAL_CLEANUP_FUNC(PTYForward*, pty_forward_free);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "varlink-io.systemd.Credentials.h"
+
+static VARLINK_DEFINE_METHOD(
+ Encrypt,
+ VARLINK_DEFINE_INPUT(name, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(text, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(data, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(timestamp, VARLINK_INT, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(notAfter, VARLINK_INT, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(allowInteractiveAuthentication, VARLINK_BOOL, VARLINK_NULLABLE),
+ VARLINK_DEFINE_OUTPUT(blob, VARLINK_STRING, 0));
+
+static VARLINK_DEFINE_METHOD(
+ Decrypt,
+ VARLINK_DEFINE_INPUT(name, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(blob, VARLINK_STRING, 0),
+ VARLINK_DEFINE_INPUT(timestamp, VARLINK_INT, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(allowInteractiveAuthentication, VARLINK_BOOL, VARLINK_NULLABLE),
+ VARLINK_DEFINE_OUTPUT(data, VARLINK_STRING, 0));
+
+static VARLINK_DEFINE_ERROR(BadFormat);
+static VARLINK_DEFINE_ERROR(NameMismatch);
+static VARLINK_DEFINE_ERROR(TimeMismatch);
+
+VARLINK_DEFINE_INTERFACE(
+ io_systemd_Credentials,
+ "io.systemd.Credentials",
+ &vl_method_Encrypt,
+ &vl_method_Decrypt,
+ &vl_error_BadFormat,
+ &vl_error_NameMismatch,
+ &vl_error_TimeMismatch);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "varlink-idl.h"
+
+extern const VarlinkInterface vl_interface_io_systemd_Credentials;
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "varlink-io.systemd.Network.h"
+
+static VARLINK_DEFINE_METHOD(GetStates,
+ VARLINK_DEFINE_OUTPUT(AddressState, VARLINK_STRING, 0),
+ VARLINK_DEFINE_OUTPUT(IPv4AddressState, VARLINK_STRING, 0),
+ VARLINK_DEFINE_OUTPUT(IPv6AddressState, VARLINK_STRING, 0),
+ VARLINK_DEFINE_OUTPUT(CarrierState, VARLINK_STRING, 0),
+ VARLINK_DEFINE_OUTPUT(OnlineState, VARLINK_STRING, 0),
+ VARLINK_DEFINE_OUTPUT(OperationalState, VARLINK_STRING, 0));
+
+static VARLINK_DEFINE_METHOD(GetNamespaceId,
+ VARLINK_DEFINE_OUTPUT(NamespaceId, VARLINK_INT, 0));
+
+VARLINK_DEFINE_INTERFACE(
+ io_systemd_Network,
+ "io.systemd.Network",
+ &vl_method_GetStates,
+ &vl_method_GetNamespaceId);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "varlink-idl.h"
+
+extern const VarlinkInterface vl_interface_io_systemd_Network;
r = v->reply_callback(v, NULL, error, VARLINK_REPLY_ERROR|VARLINK_REPLY_LOCAL, v->userdata);
if (r < 0)
- log_debug_errno(r, "Reply callback returned error, ignoring: %m");
+ varlink_log_errno(v, r, "Reply callback returned error, ignoring: %m");
return 1;
}
if (v->reply_callback) {
r = v->reply_callback(v, parameters, error, flags, v->userdata);
if (r < 0)
- log_debug_errno(r, "Reply callback returned error, ignoring: %m");
+ varlink_log_errno(v, r, "Reply callback returned error, ignoring: %m");
}
varlink_clear_current(v);
assert(link);
if (json_variant_elements(parameters) != 0)
- return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER,
- JSON_BUILD_OBJECT(
- JSON_BUILD_PAIR_VARIANT("parameter", json_variant_by_index(parameters, 0))));
+ return varlink_error_invalid_parameter(link, parameters);
product = strjoin("systemd (", program_invocation_short_name, ")");
if (!product)
v->current_method = hashmap_get(v->server->symbols, method);
if (!v->current_method)
- log_debug("No interface description defined for method '%s', not validating.", method);
+ varlink_log(v, "No interface description defined for method '%s', not validating.", method);
else {
const char *bad_field;
r = varlink_idl_validate_method_call(v->current_method, parameters, &bad_field);
if (r < 0) {
- log_debug_errno(r, "Parameters for method %s() didn't pass validation on field '%s': %m", method, strna(bad_field));
+ varlink_log_errno(v, r, "Parameters for method %s() didn't pass validation on field '%s': %m", method, strna(bad_field));
- if (!FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) {
- r = varlink_errorb(v, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", bad_field)));
+ if (IN_SET(v->state, VARLINK_PROCESSING_METHOD, VARLINK_PROCESSING_METHOD_MORE)) {
+ r = varlink_error_invalid_parameter_name(v, bad_field);
if (r < 0)
return r;
}
if (!invalid) {
r = callback(v, parameters, flags, v->userdata);
if (r < 0) {
- log_debug_errno(r, "Callback for %s returned error: %m", method);
+ varlink_log_errno(v, r, "Callback for %s returned error: %m", method);
/* We got an error back from the callback. Propagate it to the client if the method call remains unanswered. */
- if (v->state == VARLINK_PROCESSED_METHOD)
- r = 0; /* already processed */
- else if (!FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) {
+ if (IN_SET(v->state, VARLINK_PROCESSING_METHOD, VARLINK_PROCESSING_METHOD_MORE)) {
r = varlink_error_errno(v, r);
if (r < 0)
return r;
}
}
}
- } else if (!FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) {
+ } else if (IN_SET(v->state, VARLINK_PROCESSING_METHOD, VARLINK_PROCESSING_METHOD_MORE)) {
r = varlink_errorb(v, VARLINK_ERROR_METHOD_NOT_FOUND, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("method", JSON_BUILD_STRING(method))));
if (r < 0)
return r;
- } else
- r = 0;
+ }
switch (v->state) {
assert_not_reached();
}
- return r;
+ return 1;
invalid:
r = -EINVAL;
r = varlink_idl_validate_method_reply(v->current_method, parameters, &bad_field);
if (r < 0)
- log_debug_errno(r, "Return parameters for method reply %s() didn't pass validation on field '%s', ignoring: %m", v->current_method->name, strna(bad_field));
+ varlink_log_errno(v, r, "Return parameters for method reply %s() didn't pass validation on field '%s', ignoring: %m", v->current_method->name, strna(bad_field));
}
r = varlink_enqueue_json(v, m);
VarlinkSymbol *symbol = hashmap_get(v->server->symbols, error_id);
if (!symbol)
- log_debug("No interface description defined for error '%s', not validating.", error_id);
+ varlink_log(v, "No interface description defined for error '%s', not validating.", error_id);
else {
const char *bad_field = NULL;
r = varlink_idl_validate_error(symbol, parameters, &bad_field);
if (r < 0)
- log_debug_errno(r, "Parameters for error %s didn't pass validation on field '%s', ignoring: %m", error_id, strna(bad_field));
+ varlink_log_errno(v, r, "Parameters for error %s didn't pass validation on field '%s', ignoring: %m", error_id, strna(bad_field));
}
r = varlink_enqueue_json(v, m);
return -EINVAL;
}
+int varlink_error_invalid_parameter_name(Varlink *v, const char *name) {
+ return varlink_errorb(
+ v,
+ VARLINK_ERROR_INVALID_PARAMETER,
+ JSON_BUILD_OBJECT(JSON_BUILD_PAIR("parameter", JSON_BUILD_STRING(name))));
+}
+
int varlink_error_errno(Varlink *v, int error) {
return varlink_errorb(
v,
r = varlink_idl_validate_method_reply(v->current_method, parameters, &bad_field);
if (r < 0)
- log_debug_errno(r, "Return parameters for method reply %s() didn't pass validation on field '%s', ignoring: %m", v->current_method->name, strna(bad_field));
+ varlink_log_errno(v, r, "Return parameters for method reply %s() didn't pass validation on field '%s', ignoring: %m", v->current_method->name, strna(bad_field));
}
r = varlink_enqueue_json(v, m);
r = json_dispatch_full(parameters, table, /* bad= */ NULL, /* flags= */ 0, userdata, &bad_field);
if (r < 0) {
if (bad_field)
- return varlink_errorb(v, VARLINK_ERROR_INVALID_PARAMETER,
- JSON_BUILD_OBJECT(JSON_BUILD_PAIR("parameter", JSON_BUILD_STRING(bad_field))));
+ return varlink_error_invalid_parameter_name(v, bad_field);
return r;
}
server->n_connections++;
if (FLAGS_SET(server->flags, VARLINK_SERVER_ACCOUNT_UID)) {
+ assert(uid_is_valid(ucred->uid));
+
r = hashmap_ensure_allocated(&server->by_uid, NULL);
if (r < 0)
- return log_debug_errno(r, "Failed to allocate UID hash table: %m");
+ return varlink_server_log_errno(server, r, "Failed to allocate UID hash table: %m");
c = PTR_TO_UINT(hashmap_get(server->by_uid, UID_TO_PTR(ucred->uid)));
r = hashmap_replace(server->by_uid, UID_TO_PTR(ucred->uid), UINT_TO_PTR(c + 1));
if (r < 0)
- return log_debug_errno(r, "Failed to increment counter in UID hash table: %m");
+ return varlink_server_log_errno(server, r, "Failed to increment counter in UID hash table: %m");
}
return 0;
if (varlink_symbol_in_interface(method, "org.varlink.service") ||
varlink_symbol_in_interface(method, "io.systemd"))
- return log_debug_errno(SYNTHETIC_ERRNO(EEXIST), "Cannot bind server to '%s'.", method);
+ return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EEXIST), "Cannot bind server to '%s'.", method);
m = strdup(method);
if (!m)
if (r == -ENOMEM)
return log_oom_debug();
if (r < 0)
- return log_debug_errno(r, "Failed to register callback: %m");
+ return varlink_server_log_errno(s, r, "Failed to register callback: %m");
if (r > 0)
TAKE_PTR(m);
assert_return(s, -EINVAL);
if (callback && s->connect_callback && callback != s->connect_callback)
- return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), "A different callback was already set.");
+ return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EBUSY), "A different callback was already set.");
s->connect_callback = callback;
return 0;
assert_return(s, -EINVAL);
if (callback && s->disconnect_callback && callback != s->disconnect_callback)
- return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), "A different callback was already set.");
+ return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EBUSY), "A different callback was already set.");
s->disconnect_callback = callback;
return 0;
assert_return(interface->name, -EINVAL);
if (hashmap_contains(s->interfaces, interface->name))
- return log_debug_errno(SYNTHETIC_ERRNO(EEXIST), "Duplicate registration of interface '%s'.", interface->name);
+ return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EEXIST), "Duplicate registration of interface '%s'.", interface->name);
r = hashmap_ensure_put(&s->interfaces, &string_hash_ops, interface->name, (void*) interface);
if (r < 0)
return log_oom_debug();
if (v[n] != ' ')
- return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EINVAL),
"Failed to deserialize VarlinkServerSocket: %s: %m", value);
v = startswith(v + n + 1, "varlink-server-socket-fd=");
if (!v)
- return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EINVAL),
"Failed to deserialize VarlinkServerSocket fd %s: %m", value);
n = strcspn(v, " ");
fd = parse_fd(buf);
if (fd < 0)
- return log_debug_errno(fd, "Unable to parse VarlinkServerSocket varlink-server-socket-fd=%s: %m", buf);
+ return varlink_server_log_errno(s, fd, "Unable to parse VarlinkServerSocket varlink-server-socket-fd=%s: %m", buf);
if (!fdset_contains(fds, fd))
- return log_debug_errno(SYNTHETIC_ERRNO(EBADF),
+ return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EBADF),
"VarlinkServerSocket varlink-server-socket-fd= has unknown fd %d: %m", fd);
ss = new(VarlinkServerSocket, 1);
r = varlink_server_add_socket_event_source(s, ss, SD_EVENT_PRIORITY_NORMAL);
if (r < 0)
- return log_debug_errno(r, "Failed to add VarlinkServerSocket event source to the event loop: %m");
+ return varlink_server_log_errno(s, r, "Failed to add VarlinkServerSocket event source to the event loop: %m");
LIST_PREPEND(sockets, s->sockets, TAKE_PTR(ss));
return 0;
int varlink_error(Varlink *v, const char *error_id, JsonVariant *parameters);
int varlink_errorb(Varlink *v, const char *error_id, ...);
int varlink_error_invalid_parameter(Varlink *v, JsonVariant *parameters);
+int varlink_error_invalid_parameter_name(Varlink *v, const char *name);
int varlink_error_errno(Varlink *v, int error);
/* Enqueue a "more" reply */
c = image_class_from_string(value);
if (!IN_SET(c, IMAGE_SYSEXT, IMAGE_CONFEXT))
- return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", "class")));
+ return varlink_error_invalid_parameter_name(link, "class");
if (hierarchies) {
r = parse_env_extension_hierarchies(&h, image_class_info[c].name_env);
#include "terminal-util.h"
int verb_cat(int argc, char *argv[], void *userdata) {
- _cleanup_hashmap_free_ Hashmap *cached_name_map = NULL, *cached_id_map = NULL;
+ _cleanup_hashmap_free_ Hashmap *cached_id_map = NULL, *cached_name_map = NULL;
_cleanup_(lookup_paths_free) LookupPaths lp = {};
_cleanup_strv_free_ char **names = NULL;
sd_bus *bus;
_cleanup_free_ char *fragment_path = NULL;
_cleanup_strv_free_ char **dropin_paths = NULL;
- r = unit_find_paths(bus, *name, &lp, false, &cached_name_map, &cached_id_map, &fragment_path, &dropin_paths);
+ r = unit_find_paths(bus, *name, &lp, false, &cached_id_map, &cached_name_map, &fragment_path, &dropin_paths);
if (r == -ERFKILL) {
printf("%s# Unit %s is masked%s.\n",
ansi_highlight_magenta(),
EditFileContext *context,
char **names) {
- _cleanup_hashmap_free_ Hashmap *cached_name_map = NULL, *cached_id_map = NULL;
+ _cleanup_hashmap_free_ Hashmap *cached_id_map = NULL, *cached_name_map = NULL;
_cleanup_(lookup_paths_free) LookupPaths lp = {};
_cleanup_free_ char *drop_in_alloc = NULL, *suffix = NULL;
const char *drop_in;
_cleanup_free_ char *path = NULL;
_cleanup_strv_free_ char **unit_paths = NULL;
- r = unit_find_paths(bus, *name, &lp, /* force_client_side= */ false, &cached_name_map, &cached_id_map, &path, &unit_paths);
+ r = unit_find_paths(bus, *name, &lp, /* force_client_side= */ false, &cached_id_map, &cached_name_map, &path, &unit_paths);
if (r == -EKEYREJECTED) {
/* If loading of the unit failed server side complete, then the server won't tell us
* the unit file path. In that case, find the file client side. */
log_debug_errno(r, "Unit '%s' was not loaded correctly, retrying client-side.", *name);
- r = unit_find_paths(bus, *name, &lp, /* force_client_side= */ true, &cached_name_map, &cached_id_map, &path, &unit_paths);
+ r = unit_find_paths(bus, *name, &lp, /* force_client_side= */ true, &cached_id_map, &cached_name_map, &path, &unit_paths);
}
if (r == -ERFKILL)
return log_error_errno(r, "Unit '%s' masked, cannot edit.", *name);
'test-cgroup.c',
'test-chase.c',
'test-clock.c',
+ 'test-color-util.c',
'test-compare-operator.c',
'test-condition.c',
'test-conf-files.c',
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "color-util.h"
+#include "tests.h"
+
+TEST(hsv_to_rgb) {
+ uint8_t r, g, b;
+
+ hsv_to_rgb(0, 0, 0, &r, &g, &b);
+ assert(r == 0 && g == 0 && b == 0);
+
+ hsv_to_rgb(60, 0, 0, &r, &g, &b);
+ assert(r == 0 && g == 0 && b == 0);
+
+ hsv_to_rgb(0, 0, 100, &r, &g, &b);
+ assert(r == 255 && g == 255 && b == 255);
+
+ hsv_to_rgb(0, 100, 100, &r, &g, &b);
+ assert(r == 255 && g == 0 && b == 0);
+
+ hsv_to_rgb(120, 100, 100, &r, &g, &b);
+ assert(r == 0 && g == 255 && b == 0);
+
+ hsv_to_rgb(240, 100, 100, &r, &g, &b);
+ assert(r == 0 && g == 0 && b == 255);
+
+ hsv_to_rgb(311, 52, 62, &r, &g, &b);
+ assert(r == 158 && g == 75 && b == 143);
+}
+
+TEST(rgb_to_hsv) {
+
+ double h, s, v;
+ rgb_to_hsv(0, 0, 0, &h, &s, &v);
+ assert(s <= 0);
+ assert(v <= 0);
+
+ rgb_to_hsv(1, 1, 1, &h, &s, &v);
+ assert(s <= 0);
+ assert(v >= 100);
+
+ rgb_to_hsv(1, 0, 0, &h, &s, &v);
+ assert(h >= 359 || h <= 1);
+ assert(s >= 100);
+ assert(v >= 100);
+
+ rgb_to_hsv(0, 1, 0, &h, &s, &v);
+ assert(h >= 119 && h <= 121);
+ assert(s >= 100);
+ assert(v >= 100);
+
+ rgb_to_hsv(0, 0, 1, &h, &s, &v);
+ assert(h >= 239 && h <= 241);
+ assert(s >= 100);
+ assert(v >= 100);
+
+ rgb_to_hsv(0.5, 0.6, 0.7, &h, &s, &v);
+ assert(h >= 209 && h <= 211);
+ assert(s >= 28 && s <= 31);
+ assert(v >= 69 && v <= 71);
+}
+
+DEFINE_TEST_MAIN(LOG_DEBUG);
assert_se(streq(a[0], "a=A"));
}
+TEST(strv_env_assignf) {
+ _cleanup_strv_free_ char **a = NULL;
+
+ assert_se(strv_env_assignf(&a, "a", "a") > 0);
+ assert_se(strv_env_assignf(&a, "a", "%c", 'a') == 0);
+
+ assert_se(strv_env_assignf(&a, "c", "xxx%iyyy", 5) > 0);
+ assert_se(strv_length(a) == 2);
+ assert_se(strv_equal(a, STRV_MAKE("a=a", "c=xxx5yyy")));
+ assert_se(strv_env_assignf(&a, "c", NULL) == 0);
+
+ assert_se(strv_env_assignf(&a, "b", "b") > 0);
+ assert_se(strv_env_assignf(&a, "a", "A") == 0);
+ assert_se(strv_env_assignf(&a, "b", NULL) == 0);
+
+ assert_se(strv_env_assignf(&a, "a=", "B") == -EINVAL);
+
+ assert_se(strv_length(a) == 1);
+ assert_se(streq(a[0], "a=A"));
+}
+
TEST(strv_env_assign_many) {
_cleanup_strv_free_ char **a = NULL;
assert_se(path_is_mount_point("/proc/1/", NULL, AT_SYMLINK_FOLLOW) == 0);
assert_se(path_is_mount_point("/proc/1/", NULL, 0) == 0);
- assert_se(path_is_mount_point("/dev", NULL, AT_SYMLINK_FOLLOW) > 0);
- assert_se(path_is_mount_point("/dev", NULL, 0) > 0);
- assert_se(path_is_mount_point("/dev/", NULL, AT_SYMLINK_FOLLOW) > 0);
- assert_se(path_is_mount_point("/dev/", NULL, 0) > 0);
-
/* we'll create a hierarchy of different kinds of dir/file/link
* layouts:
*
log_notice("Not invoked with stdin == ctty, cutting get_ctty() test short");
}
+TEST(get_default_background_color) {
+ double red, green, blue;
+ int r;
+
+ r = get_default_background_color(&red, &green, &blue);
+ if (r < 0)
+ log_notice_errno(r, "Can't get terminal default background color: %m");
+ else
+ log_notice("R=%g G=%g B=%g", red, green, blue);
+}
+
DEFINE_TEST_MAIN(LOG_INFO);
#include "varlink.h"
#include "varlink-idl.h"
#include "varlink-io.systemd.h"
+#include "varlink-io.systemd.Credentials.h"
#include "varlink-io.systemd.Journal.h"
#include "varlink-io.systemd.ManagedOOM.h"
+#include "varlink-io.systemd.Network.h"
#include "varlink-io.systemd.PCRExtend.h"
#include "varlink-io.systemd.Resolve.Monitor.h"
#include "varlink-io.systemd.Resolve.h"
print_separator();
test_parse_format_one(&vl_interface_io_systemd_ManagedOOM);
print_separator();
+ test_parse_format_one(&vl_interface_io_systemd_Network);
+ print_separator();
test_parse_format_one(&vl_interface_io_systemd_oom);
print_separator();
test_parse_format_one(&vl_interface_io_systemd);
print_separator();
test_parse_format_one(&vl_interface_io_systemd_sysext);
print_separator();
+ test_parse_format_one(&vl_interface_io_systemd_Credentials);
+ print_separator();
test_parse_format_one(&vl_interface_xyz_test);
}
if (streq_ptr(z, c->zone))
return sd_bus_reply_method_return(m, NULL);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
m,
- CAP_SYS_TIME,
"org.freedesktop.timedate1.set-timezone",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&c->polkit_registry,
error);
if (r < 0)
if (lrtc == c->local_rtc && !fix_system)
return sd_bus_reply_method_return(m, NULL);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
m,
- CAP_SYS_TIME,
"org.freedesktop.timedate1.set-local-rtc",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&c->polkit_registry,
error);
if (r < 0)
} else
timespec_store(&ts, (usec_t) utc);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
m,
- CAP_SYS_TIME,
"org.freedesktop.timedate1.set-time",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&c->polkit_registry,
error);
if (r < 0)
if (context_ntp_service_exists(c) <= 0)
return sd_bus_error_set(error, BUS_ERROR_NO_NTP_SUPPORT, "NTP not supported");
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
m,
- CAP_SYS_TIME,
"org.freedesktop.timedate1.set-ntp",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&c->polkit_registry,
error);
if (r < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid NTP server name or address, refusing: %s", *name);
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.timesync1.set-runtime-servers",
- NULL, true, UID_INVALID,
- &m->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.timesync1.set-runtime-servers",
+ /* details= */ NULL,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
--- /dev/null
+dn = "cn = systemd"
+expiration_days = 30
+
+signing_key
+encryption_key
+
+tls_www_client
+email_protection_key
return $ret
}
+can_test_pkcs11() {
+ if ! command -v "softhsm2-util" >/dev/null; then
+ ddebug "softhsm2-util not available, skipping the PKCS#11 test"
+ return 1
+ fi
+ if ! command -v "pkcs11-tool" >/dev/null; then
+ ddebug "pkcs11-tool not available, skipping the PKCS#11 test"
+ return 1
+ fi
+ if ! command -v "certtool" >/dev/null; then
+ ddebug "certtool not available, skipping the PKCS#11 test"
+ return 1
+ fi
+ if ! "${SYSTEMCTL:?}" --version | grep -q "+P11KIT"; then
+ ddebug "Support for p11-kit is disabled, skipping the PKCS#11 test"
+ return 1
+ fi
+ if ! "${SYSTEMCTL:?}" --version | grep -q "+LIBCRYPTSETUP\b"; then
+ ddebug "Support for libcryptsetup is disabled, skipping the PKCS#11 test"
+ return 1
+ fi
+ if ! "${SYSTEMCTL:?}" --version | grep -q "+LIBCRYPTSETUP_PLUGINS"; then
+ ddebug "Support for libcryptsetup plugins is disabled, skipping the PKCS#11 test"
+ return 1
+ fi
+
+ return 0
+}
+
+setup_pkcs11_token() {
+ dinfo "Setup PKCS#11 token"
+ local P11_MODULE_CONFIGS_DIR P11_MODULE_DIR SOFTHSM_MODULE
+
+ export SOFTHSM2_CONF="/tmp/softhsm2.conf"
+ mkdir -p "$initdir/var/lib/softhsm/tokens/"
+ cat >${SOFTHSM2_CONF} <<EOF
+directories.tokendir = $initdir/var/lib/softhsm/tokens/
+objectstore.backend = file
+slots.removable = false
+slots.mechanisms = ALL
+EOF
+ export GNUTLS_PIN="1234"
+ export GNUTLS_SO_PIN="12345678"
+ softhsm2-util --init-token --free --label "TestToken" --pin ${GNUTLS_PIN} --so-pin ${GNUTLS_SO_PIN}
+
+ if ! P11_MODULE_CONFIGS_DIR=$(pkg-config --variable=p11_module_configs p11-kit-1); then
+ echo "WARNING! Cannot get p11_module_configs from p11-kit-1.pc, assuming /usr/share/p11-kit/modules" >&2
+ P11_MODULE_CONFIGS_DIR="/usr/share/p11-kit/modules"
+ fi
+
+ if ! P11_MODULE_DIR=$(pkg-config --variable=p11_module_path p11-kit-1); then
+ echo "WARNING! Cannot get p11_module_path from p11-kit-1.pc, assuming /usr/lib/pkcs11" >&2
+ P11_MODULE_DIR="/usr/lib/pkcs11"
+ fi
+
+ SOFTHSM_MODULE=$(grep -F 'module:' "$P11_MODULE_CONFIGS_DIR/softhsm2.module"| cut -d ':' -f 2| xargs)
+ if [[ "$SOFTHSM_MODULE" =~ ^[^/] ]]; then
+ SOFTHSM_MODULE="$P11_MODULE_DIR/$SOFTHSM_MODULE"
+ fi
+
+ # RSA #####################################################
+ pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --keypairgen --key-type "RSA:2048" --label "RSATestKey" --usage-decrypt
+
+ certtool --generate-self-signed \
+ --load-privkey="pkcs11:token=TestToken;object=RSATestKey;type=private" \
+ --load-pubkey="pkcs11:token=TestToken;object=RSATestKey;type=public" \
+ --template "$TEST_BASE_DIR/$TESTNAME/template.cfg" \
+ --outder --outfile "/tmp/rsa_test.crt"
+
+ pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --write-object "/tmp/rsa_test.crt" --type cert --label "RSATestKey"
+ rm "/tmp/rsa_test.crt"
+
+ # prime256v1 ##############################################
+ pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --keypairgen --key-type "EC:prime256v1" --label "ECTestKey" --usage-derive
+
+ certtool --generate-self-signed \
+ --load-privkey="pkcs11:token=TestToken;object=ECTestKey;type=private" \
+ --load-pubkey="pkcs11:token=TestToken;object=ECTestKey;type=public" \
+ --template "$TEST_BASE_DIR/$TESTNAME/template.cfg" \
+ --outder --outfile "/tmp/ec_test.crt"
+
+ pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --write-object "/tmp/ec_test.crt" --type cert --label "ECTestKey"
+ rm "/tmp/ec_test.crt"
+
+ ###########################################################
+ rm ${SOFTHSM2_CONF}
+ unset SOFTHSM2_CONF
+
+ inst_libs "$SOFTHSM_MODULE"
+ inst_library "$SOFTHSM_MODULE"
+ inst_simple "$P11_MODULE_CONFIGS_DIR/softhsm2.module"
+
+ cat >"$initdir/etc/softhsm2.conf" <<EOF
+directories.tokendir = /var/lib/softhsm/tokens/
+objectstore.backend = file
+slots.removable = false
+slots.mechanisms = ALL
+log.level = INFO
+EOF
+
+ mkdir -p "$initdir/etc/systemd/system/systemd-cryptsetup@.service.d"
+ cat >"$initdir/etc/systemd/system/systemd-cryptsetup@.service.d/PKCS11.conf" <<EOF
+[Service]
+Environment="SOFTHSM2_CONF=/etc/softhsm2.conf"
+Environment="PIN=$GNUTLS_PIN"
+EOF
+
+ unset GNUTLS_PIN
+ unset GNUTLS_SO_PIN
+}
+
test_create_image() {
create_empty_image_rootdir
install_dmevent
generate_module_dependencies
+ if can_test_pkcs11; then
+ setup_pkcs11_token
+ fi
+
# Create a keydev
dd if=/dev/zero of="${STATEDIR:?}/keydev.img" bs=1M count=16
mkfs.ext4 -L varcrypt_keydev "$STATEDIR/keydev.img"
if command -v dracut >/dev/null; then
dracut --force --verbose --add crypt "$INITRD"
elif command -v mkinitcpio >/dev/null; then
- mkinitcpio --addhooks sd-encrypt --generate "$INITRD"
+ mkinitcpio -S autodetect --addhooks sd-encrypt --generate "$INITRD"
elif command -v mkinitramfs >/dev/null; then
# The cryptroot hook is provided by the cryptsetup-initramfs package
if ! dpkg-query -s cryptsetup-initramfs; then
--- /dev/null
+../mnt-wantedby-automount.automount
\ No newline at end of file
[Unit]
Documentation=man:fstab(5) man:systemd-fstab-generator(8)
SourcePath=/etc/fstab
-Before=local-fs.target
+DefaultDependencies=no
+Conflicts=umount.target
+Before=umount.target
After=blockdev@dev-sdx8.target
[Mount]
--- /dev/null
+# Automatically generated by systemd-fstab-generator
+
+[Unit]
+SourcePath=/etc/fstab
+Documentation=man:fstab(5) man:systemd-fstab-generator(8)
+
+[Automount]
+Where=/mnt/wantedby-automount
--- /dev/null
+# Automatically generated by systemd-fstab-generator
+
+[Unit]
+Documentation=man:fstab(5) man:systemd-fstab-generator(8)
+SourcePath=/etc/fstab
+DefaultDependencies=no
+Conflicts=umount.target
+Before=umount.target
+After=blockdev@dev-sdx17.target
+
+[Mount]
+What=/dev/sdx17
+Where=/mnt/wantedby
+Options=x-systemd.wanted-by=foo.service,x-systemd.automount
[Unit]
Documentation=man:fstab(5) man:systemd-fstab-generator(8)
SourcePath=/etc/fstab
-Before=local-fs.target
+DefaultDependencies=no
+Conflicts=umount.target
+Before=umount.target
After=blockdev@dev-sdx7.target
[Mount]
--- /dev/null
+../mnt-wantedby-automount.automount
\ No newline at end of file
[Unit]
Documentation=man:fstab(5) man:systemd-fstab-generator(8)
SourcePath=/etc/fstab
-Before=local-fs.target
+DefaultDependencies=no
+Conflicts=umount.target
+Before=umount.target
After=blockdev@dev-sdx8.target
[Mount]
--- /dev/null
+# Automatically generated by systemd-fstab-generator
+
+[Unit]
+SourcePath=/etc/fstab
+Documentation=man:fstab(5) man:systemd-fstab-generator(8)
+
+[Automount]
+Where=/mnt/wantedby-automount
--- /dev/null
+# Automatically generated by systemd-fstab-generator
+
+[Unit]
+Documentation=man:fstab(5) man:systemd-fstab-generator(8)
+SourcePath=/etc/fstab
+DefaultDependencies=no
+Conflicts=umount.target
+Before=umount.target
+After=blockdev@dev-sdx17.target
+
+[Mount]
+What=/dev/sdx17
+Where=/mnt/wantedby
+Options=x-systemd.wanted-by=foo.service,x-systemd.automount
[Unit]
Documentation=man:fstab(5) man:systemd-fstab-generator(8)
SourcePath=/etc/fstab
-Before=local-fs.target
+DefaultDependencies=no
+Conflicts=umount.target
+Before=umount.target
After=blockdev@dev-sdx7.target
[Mount]
-/dev/sdx1 /sysroot auto defaults 0 1
-/dev/sdx2 /mnt/timeout auto x-systemd.mount-timeout=10m 0 0
-/dev/sdx3 /mnt/after auto x-systemd.after=foo.service 0 0
-/dev/sdx4 /mnt/before auto x-systemd.before=foo.service 0 0
-/dev/sdx5 /mnt/requires auto x-systemd.requires=foo.service 0 0
-/dev/sdx6 /mnt/reqmounts auto x-systemd.requires-mounts-for=/hoge 0 0
-/dev/sdx7 /mnt/wantedby auto x-systemd.wanted-by=foo.service 0 0
-/dev/sdx8 /mnt/requiredby auto x-systemd.required-by=foo.service 0 0
-/dev/sdx9 /mnt/automount1 auto x-systemd.automount,x-systemd.idle-timeout=30m 0 0
-/dev/sdx10 /mnt/automount2 auto x-systemd.automount,nofail 0 0
-/dev/sdx11 /mnt/rwonly auto x-systemd.rw-only 0 0
-/dev/sdx12 /mnt/mkfs ext4 x-systemd.makefs 0 0
-/dev/sdx13 /mnt/growfs auto x-systemd.growfs 0 0
-/dev/sdx14 /mnt/pcrfs auto x-systemd.pcrfs 0 0
-/dev/sdx15 /mnt/noauto auto noauto 0 0
-/dev/sdx16 /mnt/nofail auto nofail 0 0
+/dev/sdx1 /sysroot auto defaults 0 1
+/dev/sdx2 /mnt/timeout auto x-systemd.mount-timeout=10m 0 0
+/dev/sdx3 /mnt/after auto x-systemd.after=foo.service 0 0
+/dev/sdx4 /mnt/before auto x-systemd.before=foo.service 0 0
+/dev/sdx5 /mnt/requires auto x-systemd.requires=foo.service 0 0
+/dev/sdx6 /mnt/reqmounts auto x-systemd.requires-mounts-for=/hoge 0 0
+/dev/sdx7 /mnt/wantedby auto x-systemd.wanted-by=foo.service 0 0
+/dev/sdx8 /mnt/requiredby auto x-systemd.required-by=foo.service 0 0
+/dev/sdx9 /mnt/automount1 auto x-systemd.automount,x-systemd.idle-timeout=30m 0 0
+/dev/sdx10 /mnt/automount2 auto x-systemd.automount,nofail 0 0
+/dev/sdx11 /mnt/rwonly auto x-systemd.rw-only 0 0
+/dev/sdx12 /mnt/mkfs ext4 x-systemd.makefs 0 0
+/dev/sdx13 /mnt/growfs auto x-systemd.growfs 0 0
+/dev/sdx14 /mnt/pcrfs auto x-systemd.pcrfs 0 0
+/dev/sdx15 /mnt/noauto auto noauto 0 0
+/dev/sdx16 /mnt/nofail auto nofail 0 0
+/dev/sdx17 /mnt/wantedby-automount auto x-systemd.wanted-by=foo.service,x-systemd.automount 0 0
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+[Network]
+# An empty string clears previously configured addresses.
+Address=
+Address=10.4.0.1/16
self.assertNotIn('dummy98', output)
def test_reconfigure(self):
- copy_network_unit('25-address-static.network', '12-dummy.netdev')
+ copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False)
start_networkd()
self.wait_online(['dummy98:routable'])
self.assertNotIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
self.assertNotIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
- copy_network_unit('25-address-static.network')
+ copy_network_unit('25-address-static.network', copy_dropins=False)
networkctl_reload()
self.wait_online(['dummy98:routable'])
check()
def test_up_down(self):
- copy_network_unit('25-address-static.network', '12-dummy.netdev')
+ copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False)
start_networkd()
self.wait_online(['dummy98:routable'])
for i in range(1, 254):
self.assertIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output)
+ # test for an empty string assignment for Address= in [Network]
+ copy_network_unit('25-address-static.network.d/20-clear-addresses.conf')
+ networkctl_reload()
+ self.wait_online(['dummy98:routable'])
+ output = check_output('ip -4 address show dev dummy98')
+ for i in range(1, 254):
+ self.assertNotIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output)
+ self.assertIn('inet 10.4.0.1/16 brd 10.4.255.255', output)
+
def test_address_ipv4acd(self):
check_output('ip netns add ns99')
check_output('ip link add veth99 type veth peer veth-peer')
output = check_output(*networkctl_cmd, '--json=short', 'status', env=env)
check_json(output)
- copy_network_unit('25-address-static.network')
+ copy_network_unit('25-address-static.network', copy_dropins=False)
networkctl_reload()
self.wait_online(['dummy98:routable'])
# TODO:
# - /proc/cmdline parsing
-# - figure out token support (apart from TPM2, as that's covered by TEST-70-TPM2)
-# - this might help https://www.qemu.org/docs/master/system/devices/ccid.html
# - expect + interactive auth?
# We set up an encrypted /var partition which should get mounted automatically
empty1 $IMAGE_EMPTY - headless=1,try-empty-password=1
# This one expects the key to be under /{etc,run}/cryptsetup-keys.d/empty_nokey.key
empty_nokey $IMAGE_EMPTY - headless=1
+empty_pkcs11_auto $IMAGE_EMPTY - headless=1,pkcs11-uri=auto
detached $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,header=$IMAGE_DETACHED_HEADER,keyfile-offset=32,keyfile-size=16
detached_store0 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,header=/header:LABEL=header_store,keyfile-offset=32,keyfile-size=16
cp "$IMAGE_EMPTY_KEYFILE" /run/cryptsetup-keys.d/empty_nokey.key
cryptsetup_start_and_check empty_nokey
+if [[ -r /etc/softhsm2.conf ]]; then
+ # Test unlocking with a PKCS#11 token
+ export SOFTHSM2_CONF="/etc/softhsm2.conf"
+ PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=RSATestKey" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
+ cryptsetup_start_and_check empty_pkcs11_auto
+ cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
+ cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
+ PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=ECTestKey" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
+ cryptsetup_start_and_check empty_pkcs11_auto
+ cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
+ cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
+fi
+
cryptsetup_start_and_check detached
cryptsetup_start_and_check detached_store{0..2}
cryptsetup_start_and_check -f detached_fail{0..4}
systemctl -P Wants show getty.target | grep -q container-getty@idontexist.service
fi
+# Decrypt/encrypt via varlink
+
+echo -n '{"data":"Zm9vYmFyCg=="}' > /tmp/vlcredsdata
+
+varlinkctl call /run/systemd/io.systemd.Credentials io.systemd.Credentials.Encrypt "$(cat /tmp/vlcredsdata)" | \
+ varlinkctl call /run/systemd/io.systemd.Credentials io.systemd.Credentials.Decrypt > /tmp/vlcredsdata2
+
+cmp /tmp/vlcredsdata /tmp/vlcredsdata2
+rm /tmp/vlcredsdata /tmp/vlcredsdata2
+
systemd-analyze log-level info
touch /testok
assert_eq "$A" "$B"
+# Check that invoking the tool under the uid0 alias name works
+uid0 ls /
+echo "$(uid0 echo foo)" = "foo"
+
umount /proc/version
rm "$V"
'file' : 'systemd-coredump@.service.in',
'conditions' : ['ENABLE_COREDUMP'],
},
+ {
+ 'file' : 'systemd-creds.socket',
+ 'symlinks' : ['sockets.target.wants/'],
+ },
+ { 'file' : 'systemd-creds@.service' },
{ 'file' : 'systemd-exit.service' },
{
'file' : 'systemd-firstboot.service',
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Credential Encryption/Decryption (Varlink)
+Documentation=man:systemd-creds(1)
+DefaultDependencies=no
+Before=sockets.target
+
+[Socket]
+ListenStream=/run/systemd/io.systemd.Credentials
+FileDescriptorName=varlink
+SocketMode=0600
+Accept=yes
+
+[Install]
+WantedBy=sockets.target
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Credential Encryption/Decryption (Varlink)
+Documentation=man:systemd-creds(1)
+DefaultDependencies=no
+Conflicts=shutdown.target initrd-switch-root.target
+Before=shutdown.target initrd-switch-root.target
+
+[Service]
+Environment=LISTEN_FDNAMES=varlink
+ExecStart=-systemd-creds