]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #30550 from yuwata/network-nexthop-cleanups-3
authorLuca Boccassi <bluca@debian.org>
Fri, 22 Dec 2023 09:44:39 +0000 (10:44 +0100)
committerGitHub <noreply@github.com>
Fri, 22 Dec 2023 09:44:39 +0000 (10:44 +0100)
network: several cleanups for nexthop (part3)

134 files changed:
TODO
man/crypttab.xml
man/rules/meson.build
man/systemd-cryptenroll.xml
man/systemd-run.xml
man/systemd.exec.xml
man/systemd.mount.xml
man/systemd.network.xml
man/uid0.xml [new file with mode: 0644]
mkosi.images/base/mkosi.build.chroot
shell-completion/bash/systemd-cat
shell-completion/bash/systemd-cgls
shell-completion/bash/systemd-confext
src/basic/build.c
src/basic/env-util.c
src/basic/env-util.h
src/basic/socket-util.c
src/basic/terminal-util.c
src/basic/terminal-util.h
src/core/dbus-execute.c
src/core/dbus-service.c
src/core/dbus-unit.c
src/core/dbus-util.c
src/core/dbus-util.h
src/core/dbus.c
src/core/exec-invoke.c
src/core/execute.c
src/core/execute.h
src/core/manager.c
src/core/service.c
src/core/service.h
src/core/socket.c
src/creds/creds.c
src/cryptenroll/cryptenroll-pkcs11.c
src/fstab-generator/fstab-generator.c
src/home/homectl-pkcs11.c
src/home/homed-home-bus.c
src/home/homed-manager-bus.c
src/hostname/hostnamed.c
src/import/importd.c
src/journal/journald-context.c
src/journal/journald-server.c
src/libsystemd/sd-bus/bus-track.c
src/libsystemd/sd-journal/catalog.c
src/libsystemd/sd-journal/sd-journal.c
src/libsystemd/sd-netlink/sd-netlink.c
src/locale/localed.c
src/login/logind-dbus.c
src/login/logind-polkit.c
src/login/logind-seat-dbus.c
src/login/logind-session-dbus.c
src/login/logind-user-dbus.c
src/machine/image-dbus.c
src/machine/machine-dbus.c
src/machine/machined-dbus.c
src/network/meson.build
src/network/networkd-address.c
src/network/networkd-link-bus.c
src/network/networkd-manager-bus.c
src/network/networkd-manager-varlink.c [new file with mode: 0644]
src/network/networkd-manager-varlink.h [new file with mode: 0644]
src/network/networkd-manager.c
src/network/networkd-manager.h
src/network/networkd-queue.c
src/network/networkd-queue.h
src/network/networkd-state-file.c
src/network/networkd-state-file.h
src/nspawn/nspawn.c
src/oom/oomd-util.c
src/pcrextend/pcrextend.c
src/pcrlock/pcrlock.c
src/portable/portabled-bus.c
src/portable/portabled-image-bus.c
src/resolve/resolved-bus.c
src/resolve/resolved-dns-cache.c
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-transaction.c
src/resolve/resolved-dns-trust-anchor.c
src/resolve/resolved-dns-zone.c
src/resolve/resolved-dnssd-bus.c
src/resolve/resolved-link-bus.c
src/run/meson.build
src/run/run.c
src/run/systemd-uid0.in [new file with mode: 0644]
src/shared/ask-password-api.c
src/shared/bus-polkit.c
src/shared/bus-polkit.h
src/shared/color-util.c [new file with mode: 0644]
src/shared/color-util.h [new file with mode: 0644]
src/shared/install.c
src/shared/meson.build
src/shared/openssl-util.c
src/shared/openssl-util.h
src/shared/pkcs11-util.c
src/shared/pkcs11-util.h
src/shared/ptyfwd.c
src/shared/ptyfwd.h
src/shared/varlink-io.systemd.Credentials.c [new file with mode: 0644]
src/shared/varlink-io.systemd.Credentials.h [new file with mode: 0644]
src/shared/varlink-io.systemd.Network.c [new file with mode: 0644]
src/shared/varlink-io.systemd.Network.h [new file with mode: 0644]
src/shared/varlink.c
src/shared/varlink.h
src/sysext/sysext.c
src/systemctl/systemctl-edit.c
src/test/meson.build
src/test/test-color-util.c [new file with mode: 0644]
src/test/test-env-util.c
src/test/test-mountpoint-util.c
src/test/test-terminal-util.c
src/test/test-varlink-idl.c
src/timedate/timedated.c
src/timesync/timesyncd-bus.c
test/TEST-24-CRYPTSETUP/template.cfg [new file with mode: 0644]
test/TEST-24-CRYPTSETUP/test.sh
test/test-fstab-generator/test-18-options.fstab.expected.sysroot/foo.service.wants/mnt-wantedby-automount.automount [new symlink]
test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-requiredby.mount
test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.automount [new file with mode: 0644]
test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.mount [new file with mode: 0644]
test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby.mount
test/test-fstab-generator/test-18-options.fstab.expected/foo.service.wants/mnt-wantedby-automount.automount [new symlink]
test/test-fstab-generator/test-18-options.fstab.expected/mnt-requiredby.mount
test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.automount [new file with mode: 0644]
test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.mount [new file with mode: 0644]
test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby.mount
test/test-fstab-generator/test-18-options.fstab.input
test/test-network/conf/25-address-static.network.d/20-clear-addresses.conf [new file with mode: 0644]
test/test-network/systemd-networkd-tests.py
test/units/testsuite-24.sh
test/units/testsuite-54.sh
test/units/testsuite-74.run.sh
units/meson.build
units/systemd-creds.socket [new file with mode: 0644]
units/systemd-creds@.service [new file with mode: 0644]

diff --git a/TODO b/TODO
index e480f70ef7823839458013446e1dfd6d08596fbc..4d2fd411dca34fd33b1515fa102b521ce01d039b 100644 (file)
--- a/TODO
+++ b/TODO
@@ -132,6 +132,10 @@ Deprecations and removals:
 
 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
@@ -140,14 +144,6 @@ Features:
 * 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
 
index eb742be058221f5e438f047208ace545ea1d6904..c35d782c8dd4106ec196c164be9f6b450e0909b7 100644 (file)
       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 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>
@@ -969,8 +973,8 @@ external   /dev/sda3       keyfile:LABEL=keydev keyfile-timeout=10s,cipher=xchac
       <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>
index 3d63cf1131ac9a0beca92699b39dbbdd633a160a..622921f8d63db890dbe71f48c03e7ea8aadfae68 100644 (file)
@@ -1257,6 +1257,7 @@ manpages = [
   ''],
  ['udev_new', '3', ['udev_ref', 'udev_unref'], ''],
  ['udevadm', '8', [], ''],
+ ['uid0', '1', [], ''],
  ['ukify', '1', [], 'ENABLE_UKIFY'],
  ['user@.service',
   '5',
index 8fd885cb26427721ee8446d8e11b370e4d38f014..40e07ce24d9007db5bf82e59581cbc786023031a 100644 (file)
@@ -36,8 +36,8 @@
     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>
index 5be9823c373de5143472db3bbd15ac5e9880f190..e6cf2a3bcb0bca82a1ae07b412f5c6a6093a8b77 100644 (file)
         <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" />
@@ -677,7 +705,8 @@ $ systemd-run --user --wait -p SuccessExitStatus=SIGUSR1 --expand-environment=no
       <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>
 
index 525303c6ebfbde904332ea6009464c8c254f60a5..37fa6e4f3c10d262cb41777fd6fa05f347475454 100644 (file)
       <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>
index e11b9ffed42f4e92dffb341c09860154ff1db568..5573f8b2c4f7d3f0898da654ad015686da148c91 100644 (file)
         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>
 
index 7dc447d0a7f84b690bf4a3044edb7bac0679e7a7..b587573a5f6b007b7c07633de8a4800170df1e51 100644 (file)
@@ -662,6 +662,9 @@ Table=1234</programlisting></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>
diff --git a/man/uid0.xml b/man/uid0.xml
new file mode 100644 (file)
index 0000000..0d36ca7
--- /dev/null
@@ -0,0 +1,232 @@
+<?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>
index b60ed8d73129c4be9b371fcd1aeed549433462be..9524fdb103a6bc4f940e1671990e5b3fadcb4d96 100755 (executable)
@@ -70,7 +70,7 @@ if [ ! -f "$BUILDDIR"/build.ninja ]; then
         -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
index b209140804d135d1272932b046dbd49bee32a6e8..e1e600217289877c5c87550f8002b690793b3135 100644 (file)
@@ -30,7 +30,7 @@ _systemd_cat() {
 
     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
@@ -40,7 +40,7 @@ _systemd_cat() {
             --identifier|-t)
                 comps=''
                 ;;
-            --priority|-p)
+            --priority|-p|--stderr-priority)
                 comps='emerg alert crit err warning notice info debug'
                 ;;
             --level-prefix)
index 8dda5a82dd134fc9780b51f85cbc75b39535f8b9..f80bea73532fb5fed50ddf31690fb5ee5944413a 100644 (file)
@@ -44,7 +44,7 @@ _systemd_cgls() {
 
     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
@@ -57,6 +57,9 @@ _systemd_cgls() {
             --unit|-u)
                 comps=$( __get_units_have_cgroup --system )
                 ;;
+            -c|--cgroup-id|-x|--xattr)
+                comps='yes no'
+                ;;
             --user-unit)
                 comps=$( __get_units_have_cgroup --user )
                 ;;
index c8eca3b2cbe30ae95182463951d9ed343498f4f9..09d114611c52bb55ece785d8f0090258a22283eb 100644 (file)
@@ -30,9 +30,12 @@ _systemd-confext() {
         [STANDALONE]='-h --help --version
                      --no-pager
                      --no-legend
+                     --no-reload
                      --force'
         [ARG]='--root
-               --json'
+               --json
+               --noexec
+               --image-policy'
     )
 
     local -A VERBS=(
@@ -54,6 +57,12 @@ _systemd-confext() {
             --json)
                 comps='pretty short off'
                 ;;
+            --noexec)
+                comps='false true'
+                ;;
+            --image-policy)
+                comps=''
+                ;;
         esac
         COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
         return 0
index c587adad7b063d824c543fe78f17ca44456867e4..8fb32ab9b6eed7842e5d9e3d4d852729228e233e 100644 (file)
@@ -138,6 +138,12 @@ const char* const systemd_features =
         " -LIBCRYPTSETUP"
 #endif
 
+#if HAVE_LIBCRYPTSETUP_PLUGINS
+        " +LIBCRYPTSETUP_PLUGINS"
+#else
+        " -LIBCRYPTSETUP_PLUGINS"
+#endif
+
 #if HAVE_LIBFDISK
         " +LIBFDISK"
 #else
index d3bf73385fbcf64ccc35263a105c43b0d2ddc476..7ac47732ba823e825493df44f58edf93fc051fcc 100644 (file)
@@ -458,6 +458,35 @@ int strv_env_assign(char ***l, const char *key, const char *value) {
         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;
index f7fb1e90823d9735c581325e30166274d95ce583..8e77cc71d6b8aac27ac256babc69977524d4ec92 100644 (file)
@@ -49,6 +49,7 @@ int strv_env_replace_consume(char ***l, char *p); /* In place ... */
 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)
 
index beb64d8e6c7bf2edce4e30e3d21e0277a1a5126a..4f28d16b5e4b275ae2e66775195e7cbe1f2d1d37 100644 (file)
@@ -872,13 +872,11 @@ bool address_label_valid(const char *p) {
 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))
@@ -907,8 +905,10 @@ int getpeersec(int fd, char **ret) {
                 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;
index 3355b749cc01c8af2b2bf3f557140570dc778a25..b740b49775f8789311ba72f3c0d81fd62246e4d1 100644 (file)
@@ -27,6 +27,7 @@
 #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"
@@ -1551,3 +1552,218 @@ int set_terminal_cursor_position(int fd, unsigned int row, unsigned int column)
 
         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;
+}
index cae42887c44a89659bde1d92c435f7cbe08813b0..1ec057c2dd5be75efb345df23968b04ca22a10f7 100644 (file)
@@ -6,6 +6,7 @@
 #include <stdio.h>
 #include <syslog.h>
 #include <sys/types.h>
+#include <termios.h>
 
 #include "macro.h"
 #include "time-util.h"
@@ -168,7 +169,6 @@ bool underline_enabled(void);
 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;
 }
@@ -286,3 +286,7 @@ static inline const char* ansi_highlight_green_red(bool b) {
 
 /* 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);
index 4daa1cefd334a5d9b0fc9c6da39a4e564c45f879..2c6dce0a0882b1c0a30706ed812be46b11ac495b 100644 (file)
@@ -67,6 +67,7 @@ static BUS_DEFINE_PROPERTY_GET(property_get_cpu_sched_policy, "i", ExecContext,
 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,
@@ -1038,7 +1039,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
         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),
index 41f4ee399ef748d29d74017f42c12a7abe026a04..77cf6f003d7ada1d92a53f8f4a319d5a5c4a7ce8 100644 (file)
@@ -166,9 +166,7 @@ static int bus_service_method_mount(sd_bus_message *message, void *userdata, sd_
         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)
index 48b7e10ea56cdf63ab06df32603bb72fed2b98d3..8b4983dcb5f9e874c9fd3a52c23b71eb3028e0e1 100644 (file)
@@ -408,9 +408,7 @@ int bus_unit_method_start_generic(
         r = bus_verify_manage_units_async_full(
                         u,
                         verb,
-                        CAP_SYS_ADMIN,
                         polkit_message_for_job[job_type],
-                        true,
                         message,
                         error);
         if (r < 0)
@@ -491,9 +489,7 @@ int bus_unit_method_enqueue_job(sd_bus_message *message, void *userdata, sd_bus_
         r = bus_verify_manage_units_async_full(
                         u,
                         jtype,
-                        CAP_SYS_ADMIN,
                         polkit_message_for_job[type],
-                        true,
                         message,
                         error);
         if (r < 0)
@@ -549,9 +545,7 @@ int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *
         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)
@@ -579,9 +573,7 @@ int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus
         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)
@@ -611,9 +603,7 @@ int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_b
         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)
@@ -641,9 +631,7 @@ int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *e
         r = bus_verify_manage_units_async_full(
                         u,
                         "ref",
-                        CAP_SYS_ADMIN,
-                        NULL,
-                        false,
+                        /* polkit_message= */ NULL,
                         message,
                         error);
         if (r < 0)
@@ -712,9 +700,7 @@ int bus_unit_method_clean(sd_bus_message *message, void *userdata, sd_bus_error
         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)
@@ -760,9 +746,7 @@ static int bus_unit_method_freezer_generic(sd_bus_message *message, void *userda
         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)
index d680a64268670c1108bd3505aee5f817751d065a..822a17e49ff28cbf8cd0a1085923ae9b8f029cbc 100644 (file)
@@ -151,9 +151,7 @@ int bus_set_transient_usec_internal(
 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) {
 
@@ -171,11 +169,8 @@ int bus_verify_manage_units_async_full(
 
         return bus_verify_polkit_async(
                         call,
-                        capability,
                         "org.freedesktop.systemd1.manage-units",
                         details,
-                        interactive,
-                        UID_INVALID,
                         &u->manager->polkit_registry,
                         error);
 }
index 9464b25516d96ed94e4eb39fd18b89a72261f4e0..ee944c166ce7226bf7637a8fdb8d45978a1b3563 100644 (file)
@@ -249,7 +249,7 @@ static inline int bus_set_transient_usec(Unit *u, const char *name, usec_t *p, s
 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);
 
index ba2cec4d7718ad3299108ab2d865f8d8f4665e51..f7d4a97096215dd4a8a4d27e836b7a4e0b73845a 100644 (file)
@@ -1189,22 +1189,46 @@ int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l) {
 }
 
 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) {
index 7ce26582969645ae7a3ab70d25b574b60133f124..61f66b6914a65b913ec9b3acc611cdb7fd7b7079 100644 (file)
@@ -1883,7 +1883,7 @@ static int build_environment(
                                                     "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);
index da55ef0564b77ed7d6ced15695f9c0583460f011..7b661d70c9f02894aacb449092a75fa1653b416e 100644 (file)
@@ -1661,6 +1661,15 @@ uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) {
         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;
 
index 5a6927aa02730a25a733a4ecb6608fd6004cb377..e3708e1b0145feb3c3fce96aa2ac8837e858b9e6 100644 (file)
@@ -527,6 +527,7 @@ int exec_context_get_nice(const ExecContext *c);
 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);
index 6ca643e6932b341239b688c7aa2800f13d7378c1..c07f537b9f2ca4223753816cf0bbed642e01cee0 100644 (file)
@@ -1804,7 +1804,7 @@ static void manager_distribute_fds(Manager *m, FDSet *fds) {
 
         HASHMAP_FOREACH(u, m->units) {
 
-                if (fdset_size(fds) <= 0)
+                if (fdset_isempty(fds))
                         break;
 
                 if (!UNIT_VTABLE(u)->distribute_fds)
@@ -2745,7 +2745,7 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
         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;
@@ -3810,7 +3810,7 @@ void manager_check_finished(Manager *m) {
 
         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,
@@ -4545,7 +4545,7 @@ ManagerState manager_state(Manager *m) {
         }
 
         /* 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;
index b9eb40c5559c1c17928bb50eb17207b8ddec1d63..7bd4e99b11fb64f97cb873debcb41204e09dc0d7 100644 (file)
@@ -34,6 +34,7 @@
 #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"
@@ -4975,6 +4976,57 @@ static void service_release_resources(Unit *u) {
                 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",
index e85302e616663cad2ef5c7a0ab12e5b41f2f0cba..364cd6885b8759b9f6d2fc0553fd5985ffdf2f33 100644 (file)
@@ -255,6 +255,8 @@ void service_release_socket_fd(Service *s);
 
 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_;
 
index c42a94d046dccccdd1b944a67d10cd63dedfa212..93faccf6ffca9c862df2ccd1b2daed01e0d73f5a 100644 (file)
@@ -1385,50 +1385,26 @@ int socket_load_service_unit(Socket *s, int cfd, Unit **ret) {
 }
 
 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(
index 10d117118fbca592f2ba761ed85462c108142da7..c9bf2e1e36cee4dba373f5db14977bea47a1a775 100644 (file)
@@ -24,6 +24,9 @@
 #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 {
@@ -54,6 +57,7 @@ static usec_t arg_timestamp = USEC_INFINITY;
 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);
@@ -933,6 +937,11 @@ static int parse_argv(int argc, char *argv[]) {
         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;
 }
 
@@ -952,6 +961,150 @@ static int creds_main(int argc, char *argv[]) {
         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;
 
@@ -961,6 +1114,33 @@ static int run(int argc, char *argv[]) {
         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);
 }
 
index 54b6b8624281db74177adfefde6f54d48d4a9f5b..7d6112e40271cb1d87ad7c334e2a1e88a53bbec8 100644 (file)
@@ -6,7 +6,6 @@
 #include "memory-util.h"
 #include "openssl-util.h"
 #include "pkcs11-util.h"
-#include "random-util.h"
 
 int enroll_pkcs11(
                 struct crypt_device *cd,
@@ -18,12 +17,11 @@ int enroll_pkcs11(
         _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);
@@ -37,27 +35,9 @@ int enroll_pkcs11(
         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.) */
@@ -87,7 +67,7 @@ int enroll_pkcs11(
                                        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");
 
index fd1a78aed2f37847cae190d3603228a78c6730be..3c0b597470a49efe89e216a40286c0a2ad927d14 100644 (file)
@@ -599,13 +599,25 @@ static int add_mount(
                 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) {
@@ -696,26 +708,7 @@ static int add_mount(
                 }
         }
 
-        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");
@@ -745,11 +738,37 @@ static int add_mount(
                 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;
index 2539af063118690de030ca398ad816fbfeb107a9..6ae291ed932df5431c941d9432264e8907e602d7 100644 (file)
@@ -8,7 +8,6 @@
 #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(
@@ -158,11 +157,10 @@ static int acquire_pkcs11_certificate(
 }
 
 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);
@@ -171,27 +169,9 @@ int identity_add_pkcs11_key_data(JsonVariant **v, const char *uri) {
         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);
@@ -202,7 +182,7 @@ int identity_add_pkcs11_key_data(JsonVariant **v, const char *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;
index a47f4d8a844483f134a3b58ac215ee767d56f9ce..413a706f4c363367b586fc2c7c22c422760f23ca 100644 (file)
@@ -203,11 +203,8 @@ int bus_home_method_unregister(
 
         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)
@@ -243,11 +240,8 @@ int bus_home_method_realize(
 
         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)
@@ -283,11 +277,8 @@ int bus_home_method_remove(
 
         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)
@@ -354,12 +345,11 @@ int bus_home_method_authenticate(
         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);
@@ -395,11 +385,8 @@ int bus_home_method_update_record(Home *h, sd_bus_message *message, UserRecord *
 
         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)
@@ -461,11 +448,8 @@ int bus_home_method_resize(
 
         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)
@@ -506,12 +490,11 @@ int bus_home_method_change_password(
         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);
index 7cf543922f9ae299c580628791de5833a7ba9c4d..b5dffb2c695af826743c5892f6b8101ee9490e98 100644 (file)
@@ -396,11 +396,8 @@ static int method_register_home(
 
         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)
@@ -443,11 +440,8 @@ static int method_create_home(
 
         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)
index fc7a97fb99b9e5a0c1bffbdceaa7045111ad589b..893eb4cc0f15be02265cdc8f037419fdea805d62 100644 (file)
@@ -1054,13 +1054,12 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *
 
         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)
@@ -1101,13 +1100,12 @@ static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_
         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)
@@ -1177,17 +1175,15 @@ static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_mess
                         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)
@@ -1259,13 +1255,12 @@ static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_err
         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)
@@ -1306,11 +1301,8 @@ static int method_get_hardware_serial(sd_bus_message *m, void *userdata, sd_bus_
 
         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)
@@ -1350,11 +1342,8 @@ static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *erro
 
         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)
index e1a1ddc2ee186bf3b716dfda28fccaa7ac83f798..e9bbbb628dae84697b66a2bfc72d7287a3d07558 100644 (file)
@@ -704,11 +704,8 @@ static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
 
         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)
@@ -775,11 +772,8 @@ static int method_import_fs(sd_bus_message *msg, void *userdata, sd_bus_error *e
 
         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)
@@ -843,11 +837,8 @@ static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
 
         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)
@@ -916,11 +907,8 @@ static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_er
 
         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)
@@ -1036,11 +1024,8 @@ static int method_cancel(sd_bus_message *msg, void *userdata, sd_bus_error *erro
 
         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)
@@ -1065,11 +1050,8 @@ static int method_cancel_transfer(sd_bus_message *msg, void *userdata, sd_bus_er
 
         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)
index f5f6ec590ff02b4cc04a9456154601ace6433fd8..78401f9857c0e174527b5652f83fe07c29cebfc5 100644 (file)
@@ -650,8 +650,8 @@ void client_context_flush_all(Server *s) {
 
         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);
index d1f8485661e4b78b3bb9418e8e2fa071f1f0e868..476da317fe764e8a976b17020bd3ccda7f0e490f 100644 (file)
@@ -2633,7 +2633,7 @@ int server_init(Server *s, const char *namespace) {
         /* 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);
         }
index f9c59a1007c729d17a44ef68141bba131b358041..6f6fa2d943aa621c29192ac962e66a4eb076f3ce 100644 (file)
@@ -69,7 +69,7 @@ static void bus_track_add_to_queue(sd_bus_track *track) {
                 return;
 
         /* still referenced? */
-        if (hashmap_size(track->names) > 0)
+        if (!hashmap_isempty(track->names))
                 return;
 
         /* Nothing to call? */
index 4c5562ba488182fa35503bb79dc6f9e0020379c4..832b678bf33423d214c17437b841f795bb0f3cb7 100644 (file)
@@ -452,11 +452,12 @@ int catalog_update(const char* database, const char* root, const char* const* di
                         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)
index 6b9ff0a4ed880557b940a9d37b79ca88119c6b6b..f2a0c666703be94be7657459414e99ad453c0a69 100644 (file)
@@ -932,8 +932,8 @@ static int real_journal_next(sd_journal *j, direction_t direction) {
         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);
index b6730b71dc15a907f01ecaa67405e2b81afab047..b347ee6339530bf7b9de9738eccb9b289439f3f5 100644 (file)
@@ -176,7 +176,7 @@ static int dispatch_rqueue(sd_netlink *nl, sd_netlink_message **ret) {
         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 */
@@ -443,7 +443,7 @@ int sd_netlink_wait(sd_netlink *nl, uint64_t timeout_usec) {
         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);
@@ -623,7 +623,7 @@ int sd_netlink_get_events(sd_netlink *nl) {
         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) {
@@ -633,7 +633,7 @@ 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;
         }
index 5d96237fae6290b4b9f0abc028211781ae22f294..8ce8c0d08fc5eff6003fcbe78555ee870c6d0125 100644 (file)
@@ -281,13 +281,12 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
                 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)
@@ -386,13 +385,12 @@ static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_erro
         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)
@@ -506,13 +504,12 @@ static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_err
         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)
index 34992b5681059a00215754bd9aa1a589e61a444a..898c6f752b6e70434f8e6c8244d8c7b15b8fb670 100644 (file)
@@ -236,7 +236,6 @@ int manager_get_seat_from_creds(
 
 static int return_test_polkit(
                 sd_bus_message *message,
-                int capability,
                 const char *action,
                 const char **details,
                 uid_t good_user,
@@ -246,7 +245,7 @@ static int return_test_polkit(
         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;
 
@@ -1245,11 +1244,8 @@ static int method_lock_sessions(sd_bus_message *message, void *userdata, sd_bus_
 
         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)
@@ -1397,14 +1393,13 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu
         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)
@@ -1565,13 +1560,12 @@ static int method_attach_device(sd_bus_message *message, void *userdata, sd_bus_
         } 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)
@@ -1596,13 +1590,12 @@ static int method_flush_devices(sd_bus_message *message, void *userdata, sd_bus_
         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)
@@ -1938,13 +1931,12 @@ static int verify_shutdown_creds(
         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)
@@ -1959,12 +1951,12 @@ static int verify_shutdown_creds(
                         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)
@@ -1974,12 +1966,12 @@ static int verify_shutdown_creds(
         }
 
         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)
@@ -2529,11 +2521,8 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd
 
         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)
@@ -2640,7 +2629,13 @@ static int method_can_shutdown_or_sleep(
         }
 
         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;
 
@@ -2653,7 +2648,13 @@ static int method_can_shutdown_or_sleep(
         }
 
         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;
 
@@ -2671,7 +2672,13 @@ static int method_can_shutdown_or_sleep(
                 /* 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;
 
@@ -2779,14 +2786,12 @@ static int method_set_reboot_parameter(
                 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)
@@ -2817,10 +2822,9 @@ static int method_can_reboot_parameter(
 
         return return_test_polkit(
                         message,
-                        CAP_SYS_ADMIN,
                         "org.freedesktop.login1.set-reboot-parameter",
-                        NULL,
-                        UID_INVALID,
+                        /* details= */ NULL,
+                        /* good_user= */ UID_INVALID,
                         error);
 }
 
@@ -2898,14 +2902,12 @@ static int method_set_reboot_to_firmware_setup(
                 /* 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)
@@ -2962,10 +2964,9 @@ static int method_can_reboot_to_firmware_setup(
 
         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);
 }
 
@@ -3062,14 +3063,12 @@ static int method_set_reboot_to_boot_loader_menu(
                 /* 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)
@@ -3137,10 +3136,9 @@ static int method_can_reboot_to_boot_loader_menu(
 
         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);
 }
 
@@ -3261,14 +3259,12 @@ static int method_set_reboot_to_boot_loader_entry(
                 /* 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)
@@ -3329,10 +3325,9 @@ static int method_can_reboot_to_boot_loader_entry(
 
         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);
 }
 
@@ -3403,14 +3398,12 @@ static int method_set_wall_message(
             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)
@@ -3470,7 +3463,6 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error
 
         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" :
@@ -3479,9 +3471,7 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error
                         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)
index e4efd6461673f1cfd1f631d7c85ccecb472464cd..2f1523ae04b70e5439a039707c8669e19048aaa7 100644 (file)
@@ -9,11 +9,8 @@ int check_polkit_chvt(sd_bus_message *message, Manager *manager, sd_bus_error *e
 #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
index 877b9c1af14faf12468f1de36c9a75b4427f81d3..0a395c65095fad2967df01072685a244e92b48bd 100644 (file)
@@ -134,11 +134,8 @@ int bus_seat_method_terminate(sd_bus_message *message, void *userdata, sd_bus_er
 
         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)
index a136ae418cee126d453a84f0f11897a87720b631..9348ccc4dd0e3f7a65306c12fa6a9febbf66f3ca 100644 (file)
@@ -158,12 +158,11 @@ int bus_session_method_terminate(sd_bus_message *message, void *userdata, sd_bus
 
         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);
@@ -204,12 +203,11 @@ int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_erro
 
         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);
@@ -309,12 +307,11 @@ int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro
         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);
index 88649b2f4be36b6f0b4576a2676461f3ed2c2ff1..0763a5b03f3acc1e10aa6d15c47f465bbbf7fa47 100644 (file)
@@ -192,12 +192,11 @@ int bus_user_method_terminate(sd_bus_message *message, void *userdata, sd_bus_er
 
         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);
@@ -220,12 +219,11 @@ int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_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);
index aa4525ddbdaeeeb5e718c0beecea212ecc29b063..69039de2e6c3fe645d7a60dd65687c5ee0a34474 100644 (file)
@@ -50,11 +50,8 @@ int bus_image_method_remove(
 
         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)
@@ -121,11 +118,8 @@ int bus_image_method_rename(
 
         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)
@@ -173,11 +167,8 @@ int bus_image_method_clone(
 
         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)
@@ -240,11 +231,8 @@ int bus_image_method_mark_read_only(
 
         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)
@@ -285,11 +273,8 @@ int bus_image_method_set_limit(
 
         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)
index 4620f32d627e351582f9b71a0f1519959221cf6a..6c2c2232fe3a6b46a908f52439409323f5a552ff 100644 (file)
@@ -73,11 +73,8 @@ int bus_machine_method_unregister(sd_bus_message *message, void *userdata, sd_bu
 
         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)
@@ -106,11 +103,8 @@ int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus
 
         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)
@@ -157,11 +151,8 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro
 
         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)
@@ -449,11 +440,8 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_
 
         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)
@@ -541,11 +529,8 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu
 
         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)
@@ -656,11 +641,8 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
 
         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)
@@ -861,11 +843,8 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
 
         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)
@@ -949,11 +928,8 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
 
         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)
@@ -1070,11 +1046,8 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
 
         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)
index 9fec047385b509dcabaea02c5790f5ea902ad36c..6b108dc0094071e4cc06e8bc37dc7899905faa67 100644 (file)
@@ -720,11 +720,8 @@ static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_err
 
         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)
@@ -855,11 +852,8 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus
 
         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)
index 3d692abf443da67c8e68f345060c62a00f6406c1..5f06948752beb292f70a7f7bb2eddb695dc98b6d 100644 (file)
@@ -56,8 +56,9 @@ sources = files(
         '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',
index e3bdc66c587efe5731f460864b2030513f647a17..ad49793a1689dede01b33a86c6acd6d6745524fd 100644 (file)
@@ -1994,10 +1994,16 @@ int config_parse_address(
         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();
index 58d487570a7c13609f44e3c9ffa59932ef61274b..743957d27c67e26a5ca3e73b64e9d116a277ecc9 100644 (file)
@@ -100,10 +100,12 @@ int bus_link_method_set_ntp_servers(sd_bus_message *message, void *userdata, sd_
                         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)
@@ -134,10 +136,12 @@ static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, voi
         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) {
@@ -231,10 +235,12 @@ int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_
         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)
@@ -266,10 +272,12 @@ int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, s
         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)
@@ -310,10 +318,12 @@ int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_er
                         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)
@@ -354,10 +364,12 @@ int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_err
                         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)
@@ -398,10 +410,12 @@ int bus_link_method_set_dns_over_tls(sd_bus_message *message, void *userdata, sd
                         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)
@@ -442,10 +456,12 @@ int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_e
                         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)
@@ -496,10 +512,12 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v
                         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)
@@ -525,10 +543,11 @@ int bus_link_method_revert_ntp(sd_bus_message *message, void *userdata, sd_bus_e
         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)
@@ -553,10 +572,12 @@ int bus_link_method_revert_dns(sd_bus_message *message, void *userdata, sd_bus_e
         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)
@@ -580,10 +601,12 @@ int bus_link_method_force_renew(sd_bus_message *message, void *userdata, sd_bus_
                                          "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)
@@ -607,10 +630,12 @@ int bus_link_method_renew(sd_bus_message *message, void *userdata, sd_bus_error
                                          "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)
@@ -629,10 +654,12 @@ int bus_link_method_reconfigure(sd_bus_message *message, void *userdata, sd_bus_
 
         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)
index aecbc1d67c69ef378e25ea108689819126fffd2d..a8906f81c1ba606f0326a73979b31e6a5438d8cd 100644 (file)
@@ -201,10 +201,12 @@ static int bus_method_reload(sd_bus_message *message, void *userdata, sd_bus_err
         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)
diff --git a/src/network/networkd-manager-varlink.c b/src/network/networkd-manager-varlink.c
new file mode 100644 (file)
index 0000000..d9750d9
--- /dev/null
@@ -0,0 +1,90 @@
+/* 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");
+}
diff --git a/src/network/networkd-manager-varlink.h b/src/network/networkd-manager-varlink.h
new file mode 100644 (file)
index 0000000..46078a8
--- /dev/null
@@ -0,0 +1,7 @@
+/* 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);
index d5ee841b1b106871916f5d5a79686a7648e0c57b..c8df73d444249904f54bb0f32f916c1988a72dc8 100644 (file)
@@ -36,8 +36,9 @@
 #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"
@@ -429,24 +430,12 @@ static int manager_connect_rtnl(Manager *m, int fd) {
         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) {
@@ -522,11 +511,7 @@ int manager_setup(Manager *m) {
         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;
 
@@ -545,6 +530,10 @@ int manager_setup(Manager *m) {
         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;
@@ -657,6 +646,8 @@ Manager* manager_free(Manager *m) {
 
         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);
 
index c20e7ff767b5cf0f9182983a9f70dbbfbf75d07a..2554e0e0310f888ee23a49250d577ee96a6f30b4 100644 (file)
@@ -17,6 +17,7 @@
 #include "ordered-set.h"
 #include "set.h"
 #include "time-util.h"
+#include "varlink.h"
 
 struct Manager {
         sd_netlink *rtnl;
@@ -25,6 +26,7 @@ struct Manager {
         sd_event *event;
         sd_resolve *resolve;
         sd_bus *bus;
+        VarlinkServer *varlink_server;
         sd_device_monitor *device_monitor;
         Hashmap *polkit_registry;
         int ethtool_fd;
index a88418e28e17a8f43c9d98484e82307570466f57..91e874c6fad2ede436f7a055c2e2ddaf6a3de9c5 100644 (file)
@@ -210,10 +210,11 @@ int link_queue_request_full(
                            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;
index d6f5de421e2a125d50611383d6a7ac224e3b24e2..21fa7d9453475ae1182b4f31b20254e9c4ab0a0c 100644 (file)
@@ -1,7 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
-#include "sd-event.h"
 #include "sd-netlink.h"
 
 #include "alloc-util.h"
@@ -136,7 +135,7 @@ static inline int link_queue_request(
                                         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_;
index 3a95ba8d8bf8d9f785cbf11b1fd0e500028ace8b..9f0e365c22b6ce1d3dbfa7e52f386b0ce73042e6 100644 (file)
@@ -861,3 +861,26 @@ int link_save_and_clean_full(Link *link, bool also_save_manager) {
         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;
+}
index 684f0d154edfa3cdfa98e971b470af1ef81eebae..7efd1578da09cea1d1f1946f7fbb84fe71bec87f 100644 (file)
@@ -12,3 +12,4 @@ static inline int link_save_and_clean(Link *link) {
 }
 
 int manager_save(Manager *m);
+int manager_clean_all(Manager *manager);
index 1902ee8aed5dad658dcb89c900682c3e4c6ea146..b4a74a1e4375052f8f8e9ec59583ec001315a4b6 100644 (file)
@@ -3394,7 +3394,7 @@ static int inner_child(
         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.");
index f9f0af2d0499b790fd1bc04d3803cfe98087991b..6e6678c33d990b8113bda61299848eb8436ec8cd 100644 (file)
@@ -276,7 +276,7 @@ int oomd_cgroup_kill(const char *path, bool recurse, bool dry_run) {
         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);
index 12959496ded7d690c367339fdc7070ccf44d6684..ead353f5a69704e22f0d312574480fe566991816 100644 (file)
@@ -276,18 +276,18 @@ static int vl_method_extend(Varlink *link, JsonVariant *parameters, VarlinkMetho
                 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;
 
index 5bac04519bed1fac77002d2a4807f83b36aa0dcd..23bdab0befe66e331e857a0a7424d4c999452b8e 100644 (file)
@@ -11,6 +11,7 @@
 #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"
@@ -1932,40 +1933,6 @@ static int event_log_map_components(EventLog *el) {
         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]) {
index 0d5518060eb9a9276ac2243b247d06cf63bc9216..4f239e2b125eb85bfd8c84f67c2bd5b9bdcda009 100644 (file)
@@ -320,11 +320,8 @@ static int method_detach_image(sd_bus_message *message, void *userdata, sd_bus_e
 
         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)
@@ -377,11 +374,8 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus
 
         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)
index 1f61c3b8c42c6c654dc1738c6667d8a074c40a28..63f177eb74b28f58685a6c7103c75e0e81a0f616 100644 (file)
@@ -451,11 +451,8 @@ static int bus_image_method_detach(
 
         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)
@@ -1010,11 +1007,8 @@ int bus_image_acquire(
         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)
@@ -1064,11 +1058,8 @@ int bus_image_acquire(
                 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)
index d0304318c4ca7abc02ffa164ec3811b2a81daccb..ef3f5237a9ea131aa2a2aed458db83611048458b 100644 (file)
@@ -1988,10 +1988,12 @@ static int bus_method_register_service(sd_bus_message *message, void *userdata,
         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)
index 78665bc93be6ad50651184e77d2e761b3f3d56f4..c5c5037cec6193296ad53c9bab568cf81c16125d 100644 (file)
@@ -164,8 +164,8 @@ void dns_cache_flush(DnsCache *c) {
         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);
@@ -186,7 +186,7 @@ static void dns_cache_make_space(DnsCache *c, unsigned add) {
                 _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)
index 2e8b3e55801723ecce469c435965674d005c5d4a..40615ff605139f349ba3f1275f353ee60017c056 100644 (file)
@@ -1557,7 +1557,7 @@ int dns_scope_add_dnssd_services(DnsScope *scope) {
 
         assert(scope);
 
-        if (hashmap_size(scope->manager->dnssd_services) == 0)
+        if (hashmap_isempty(scope->manager->dnssd_services))
                 return 0;
 
         scope->announced = false;
index 696fce532a41f98fb36c679b62f5576b58fe626f..fe88e502e7c11c3ad24264ff47ace13279113a36 100644 (file)
@@ -2808,7 +2808,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
                         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;
@@ -2835,7 +2835,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
                         /* 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;
index 1703c43d4b0ab0d906b0a4011108886fbdbfa9ad..2156f4f685d27955cb3d24ffd7d1463d48a084f5 100644 (file)
@@ -181,7 +181,7 @@ static int dns_trust_anchor_add_builtin_negative(DnsTrustAnchor *d) {
          * 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);
index f533f972fc955418177718f2c01264bd62638b7e..d4ede46459c6eb6c0b61c10f22cee8568244f479 100644 (file)
@@ -70,8 +70,8 @@ void dns_zone_flush(DnsZone *z) {
         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);
index 0f0d4786ef12b89946ce80581532cf361c6d6a7e..0ae24fbf02827e7c912f865a2b5e22a5d9195504 100644 (file)
@@ -20,10 +20,14 @@ int bus_dnssd_method_unregister(sd_bus_message *message, void *userdata, sd_bus_
 
         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)
index 4f8f591306cbefeceba4bd1851030a7786d3a443..7ca3214b0649db304f9fe7547a65e4608c3faee2 100644 (file)
@@ -236,10 +236,11 @@ static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, voi
         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) {
@@ -368,10 +369,12 @@ int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_
         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)
@@ -446,10 +449,12 @@ int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, s
         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)
@@ -493,10 +498,12 @@ int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_er
                         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)
@@ -541,10 +548,12 @@ int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_err
                         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)
@@ -589,10 +598,12 @@ int bus_link_method_set_dns_over_tls(sd_bus_message *message, void *userdata, sd
                         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)
@@ -637,10 +648,12 @@ int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_e
                         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)
@@ -698,10 +711,12 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v
                         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)
@@ -734,10 +749,12 @@ int bus_link_method_revert(sd_bus_message *message, void *userdata, sd_bus_error
         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)
index 597a25abeb634a9cae463fbb9cdc6633c10e9d1e..95336b86fe5a7143ec1ead09b56317f853e313a9 100644 (file)
@@ -7,3 +7,17 @@ executables += [
                 '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)
index 06c00cbf56af3f129c0c2d6898bf7bcc4567a5ec..45da75f8897bb55bf540156ce7132a74ec621f6e 100644 (file)
@@ -17,6 +17,7 @@
 #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"
@@ -73,6 +74,9 @@ static bool arg_aggressive_gc = false;
 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);
@@ -82,6 +86,8 @@ STATIC_DESTRUCTOR_REGISTER(arg_socket_property, 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;
@@ -104,7 +110,7 @@ static int help(void) {
                "  -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"
@@ -123,6 +129,8 @@ static int help(void) {
                "  -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"
@@ -146,6 +154,40 @@ static int help(void) {
         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;
 
@@ -162,6 +204,18 @@ static int add_timer_property(const char *name, const char *val) {
         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 {
@@ -194,6 +248,8 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_WAIT,
                 ARG_WORKING_DIRECTORY,
                 ARG_SHELL,
+                ARG_IGNORE_FAILURE,
+                ARG_BACKGROUND,
         };
 
         static const struct option options[] = {
@@ -239,6 +295,8 @@ static int parse_argv(int argc, char *argv[]) {
                 { "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         },
                 {},
         };
 
@@ -282,7 +340,7 @@ static int parse_argv(int argc, char *argv[]) {
                         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;
@@ -524,6 +582,16 @@ static int parse_argv(int argc, char *argv[]) {
                         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;
 
@@ -651,6 +719,259 @@ static int parse_argv(int argc, char *argv[]) {
         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;
 
@@ -899,7 +1220,7 @@ static int transient_service_set_properties(sd_bus_message *m, const char *pty_p
                 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);
 
@@ -910,9 +1231,10 @@ static int transient_service_set_properties(sd_bus_message *m, const char *pty_p
                 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);
 
@@ -1432,6 +1754,9 @@ static int start_transient_service(sd_bus *bus) {
 
                         /* 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);
@@ -1899,6 +2224,8 @@ static int start_transient_trigger(sd_bus *bus, const char *suffix) {
 }
 
 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)
@@ -1919,7 +2246,10 @@ static int run(int argc, char* argv[]) {
         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;
 
diff --git a/src/run/systemd-uid0.in b/src/run/systemd-uid0.in
new file mode 100644 (file)
index 0000000..57bd5e3
--- /dev/null
@@ -0,0 +1,23 @@
+# 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
index 0e323f4644e576274a1840896264147b5a81df23..6d71245549d66f3ee84969918d2fa2161061b7b2 100644 (file)
@@ -443,9 +443,7 @@ int ask_password_tty(
                         (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)
index 904b897984a52d7aa449bbec3ff7456c96c75d79..9f923372a4b3e3ecdddf523a1c8c57cb5ae89821 100644 (file)
@@ -102,7 +102,6 @@ static int bus_message_new_polkit_auth_call(
 
 int bus_test_polkit(
                 sd_bus_message *call,
-                int capability,
                 const char *action,
                 const char **details,
                 uid_t good_user,
@@ -120,7 +119,7 @@ int bus_test_polkit(
         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)
@@ -465,12 +464,11 @@ static int async_polkit_query_check_action(
  * <- 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) {
@@ -499,7 +497,7 @@ int bus_verify_polkit_async(
         }
 #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)
index e2a3b7eef6659aa0bd1beb131649a432e462e63e..d82ac4679c617ee70cb024aadb9b0c1d77b74871 100644 (file)
@@ -4,8 +4,13 @@
 #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);
diff --git a/src/shared/color-util.c b/src/shared/color-util.c
new file mode 100644 (file)
index 0000000..776445e
--- /dev/null
@@ -0,0 +1,77 @@
+/* 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);
+}
diff --git a/src/shared/color-util.h b/src/shared/color-util.h
new file mode 100644 (file)
index 0000000..a4ae9eb
--- /dev/null
@@ -0,0 +1,11 @@
+/* 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);
index b5f386b5a15297ace5de0f101d59f24350ee4a54..265407f080e1e158ed9318c419b584081c42d12c 100644 (file)
@@ -747,7 +747,7 @@ static int remove_marked_symlinks(
         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);
index b24a541de5d0bf64b1d6e09dc9b06549746e5411..b2cee6fa2c869ac6d9f949e79fe09b5887e7fdd0 100644 (file)
@@ -39,6 +39,7 @@ shared_sources = files(
         'chown-recursive.c',
         'clean-ipc.c',
         'clock-util.c',
+        'color-util.c',
         'common-signal.c',
         'compare-operator.c',
         'condition.c',
@@ -172,11 +173,13 @@ shared_sources = files(
         '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',
index b0a5563395a041f73bc43a59a71f691f74551372..d4a689cf852fc0e924b2ebd98be09bcfcf285453 100644 (file)
@@ -4,11 +4,12 @@
 #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, ...)                                \
@@ -523,7 +524,6 @@ int rsa_encrypt_bytes(
 
         *ret_encrypt_key = TAKE_PTR(b);
         *ret_encrypt_key_size = l;
-
         return 0;
 }
 
@@ -989,7 +989,7 @@ int ecc_ecdh(const EVP_PKEY *private_pkey,
         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();
 
@@ -1128,6 +1128,170 @@ int string_hashsum(
         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]) {
index e3f34a8576e0d794b187a61e94f2acdbb4baa1fd..2ca3e8c1ce0904679217ff2516d8c7233fcbeda9 100644 (file)
@@ -108,6 +108,8 @@ int ecc_pkey_new(int curve_id, EVP_PKEY **ret);
 
 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);
index 6e88dc3803860e8c2ef2a57b39ef173d24cc49fe..c3d97b80f9e4c30e870d53a0056a56f83eb14732 100644 (file)
@@ -586,143 +586,419 @@ int pkcs11_token_find_private_key(
                 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 = &params,
+                .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,
@@ -737,17 +1013,6 @@ int pkcs11_token_decrypt_data(
         _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)
@@ -780,6 +1045,42 @@ int pkcs11_token_decrypt_data(
         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) {
index 5bc23c14c4c977d6700d4869e6b67535c67fac4f..2ff6997823e1b4a486fbac75ab5eebcb3bc885f9 100644 (file)
@@ -1,6 +1,9 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+#if HAVE_OPENSSL
+#  include <openssl/x509.h>
+#endif
 #include <stdbool.h>
 
 #if HAVE_P11KIT
@@ -10,7 +13,6 @@
 
 #include "ask-password-api.h"
 #include "macro.h"
-#include "openssl-util.h"
 #include "time-util.h"
 
 bool pkcs11_uri_valid(const char *uri);
@@ -50,6 +52,7 @@ char *pkcs11_token_model(const CK_TOKEN_INFO *token_info);
 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);
index 1a92d7ec266fc9d2774c1e539944683cda6570e0..03660ad4178fc0d3bc5aa4ed0cca43d5397c6bb8 100644 (file)
 
 #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;
 
@@ -67,7 +78,8 @@ struct PTYForward {
         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;
@@ -75,6 +87,10 @@ struct PTYForward {
 
         PTYForwardHandler handler;
         void *userdata;
+
+        char *background_color;
+        AnsiColorState ansi_color_state;
+        char *csi_sequence;
 };
 
 #define ESCAPE_USEC (1*USEC_PER_SEC)
@@ -115,6 +131,14 @@ static void pty_forward_disconnect(PTYForward *f) {
         }
 
         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) {
@@ -202,11 +226,244 @@ static bool drained(PTYForward *f) {
         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) ||
@@ -267,9 +524,9 @@ static int shovel(PTYForward *f) {
                         }
                 }
 
-                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
@@ -289,7 +546,12 @@ static int shovel(PTYForward *f) {
                                 }
                         } 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"));
                         }
                 }
 
@@ -555,6 +817,7 @@ PTYForward *pty_forward_free(PTYForward *f) {
         if (!f)
                 return NULL;
         pty_forward_disconnect(f);
+        free(f->background_color);
         return mfree(f);
 }
 
@@ -689,3 +952,9 @@ int pty_forward_set_width_height(PTYForward *f, unsigned width, unsigned height)
 
         return 0;
 }
+
+int pty_forward_set_background_color(PTYForward *f, const char *color) {
+        assert(f);
+
+        return free_and_strdup(&f->background_color, color);
+}
index f0ae6e949d04b76d8b5f83c73e94d73b67ecad9f..bae8d3591e44834b59259b568178291f90418cc4 100644 (file)
@@ -39,4 +39,6 @@ int pty_forward_set_priority(PTYForward *f, int64_t priority);
 
 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);
diff --git a/src/shared/varlink-io.systemd.Credentials.c b/src/shared/varlink-io.systemd.Credentials.c
new file mode 100644 (file)
index 0000000..b827337
--- /dev/null
@@ -0,0 +1,34 @@
+/* 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);
diff --git a/src/shared/varlink-io.systemd.Credentials.h b/src/shared/varlink-io.systemd.Credentials.h
new file mode 100644 (file)
index 0000000..c0ecc3d
--- /dev/null
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "varlink-idl.h"
+
+extern const VarlinkInterface vl_interface_io_systemd_Credentials;
diff --git a/src/shared/varlink-io.systemd.Network.c b/src/shared/varlink-io.systemd.Network.c
new file mode 100644 (file)
index 0000000..ec25e26
--- /dev/null
@@ -0,0 +1,20 @@
+/* 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);
diff --git a/src/shared/varlink-io.systemd.Network.h b/src/shared/varlink-io.systemd.Network.h
new file mode 100644 (file)
index 0000000..12d532a
--- /dev/null
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "varlink-idl.h"
+
+extern const VarlinkInterface vl_interface_io_systemd_Network;
index 749b644a56c672a193c51dfb4469ade02cb0fa14..12a27fcb8dfc43061af25d12cc50e24dd1cbf7e9 100644 (file)
@@ -1006,7 +1006,7 @@ static int varlink_dispatch_local_error(Varlink *v, const char *error) {
 
         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;
 }
@@ -1132,7 +1132,7 @@ static int varlink_dispatch_reply(Varlink *v) {
                 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);
@@ -1176,9 +1176,7 @@ static int generic_method_get_info(
         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)
@@ -1327,16 +1325,16 @@ static int varlink_dispatch_method(Varlink *v) {
 
                 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;
                                 }
@@ -1347,24 +1345,21 @@ static int varlink_dispatch_method(Varlink *v) {
                 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) {
 
@@ -1386,7 +1381,7 @@ static int varlink_dispatch_method(Varlink *v) {
                 assert_not_reached();
         }
 
-        return r;
+        return 1;
 
 invalid:
         r = -EINVAL;
@@ -2272,7 +2267,7 @@ int varlink_reply(Varlink *v, JsonVariant *parameters) {
 
                 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);
@@ -2343,13 +2338,13 @@ int varlink_error(Varlink *v, const char *error_id, JsonVariant *parameters) {
 
         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);
@@ -2425,6 +2420,13 @@ int varlink_error_invalid_parameter(Varlink *v, JsonVariant *parameters) {
         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,
@@ -2464,7 +2466,7 @@ int varlink_notify(Varlink *v, JsonVariant *parameters) {
 
                 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);
@@ -2504,8 +2506,7 @@ int varlink_dispatch(Varlink *v, JsonVariant *parameters, const JsonDispatch tab
         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;
         }
 
@@ -3021,9 +3022,11 @@ static int count_connection(VarlinkServer *server, const struct ucred *ucred) {
         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)));
 
@@ -3032,7 +3035,7 @@ static int count_connection(VarlinkServer *server, const struct ucred *ucred) {
 
                 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;
@@ -3472,7 +3475,7 @@ int varlink_server_bind_method(VarlinkServer *s, const char *method, VarlinkMeth
 
         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)
@@ -3482,7 +3485,7 @@ int varlink_server_bind_method(VarlinkServer *s, const char *method, VarlinkMeth
         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);
 
@@ -3519,7 +3522,7 @@ int varlink_server_bind_connect(VarlinkServer *s, VarlinkConnect callback) {
         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;
@@ -3529,7 +3532,7 @@ int varlink_server_bind_disconnect(VarlinkServer *s, VarlinkDisconnect callback)
         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;
@@ -3543,7 +3546,7 @@ int varlink_server_add_interface(VarlinkServer *s, const VarlinkInterface *inter
         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)
@@ -3696,11 +3699,11 @@ int varlink_server_deserialize_one(VarlinkServer *s, const char *value, FDSet *f
                 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, " ");
@@ -3708,9 +3711,9 @@ int varlink_server_deserialize_one(VarlinkServer *s, const char *value, FDSet *f
 
         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);
@@ -3725,7 +3728,7 @@ int varlink_server_deserialize_one(VarlinkServer *s, const char *value, FDSet *f
 
         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;
index f8e7f5d554fdbda63d7c4278edfabdce10cab8ab..c60f695be78ec77acd062b76fefa985355680a9c 100644 (file)
@@ -109,6 +109,7 @@ int varlink_replyb(Varlink *v, ...);
 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 */
index 8dc515e4d5241cbe1ecfa87f814e3ecba538bd04..33cb8c19635239839c6dad12e704c1ecf6b0b091 100644 (file)
@@ -342,7 +342,7 @@ static int parse_image_class_parameter(Varlink *link, const char *value, ImageCl
 
         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);
index d967b1649ef83c9fb73411da23b80980e47d001f..a6bb5e52892252b28fb561b7e2cbb7d18ef5cfbc 100644 (file)
@@ -14,7 +14,7 @@
 #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;
@@ -50,7 +50,7 @@ int verb_cat(int argc, char *argv[], void *userdata) {
                 _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(),
@@ -197,7 +197,7 @@ static int find_paths_to_edit(
                 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;
@@ -233,13 +233,13 @@ static int find_paths_to_edit(
                 _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);
index b0a92bb43b9aa1a475f60b255387a83b121d03b0..aec125d483ac9d68fb4170f24c0bd341c1b6c0bb 100644 (file)
@@ -57,6 +57,7 @@ simple_tests += files(
         'test-cgroup.c',
         'test-chase.c',
         'test-clock.c',
+        'test-color-util.c',
         'test-compare-operator.c',
         'test-condition.c',
         'test-conf-files.c',
diff --git a/src/test/test-color-util.c b/src/test/test-color-util.c
new file mode 100644 (file)
index 0000000..3d00d87
--- /dev/null
@@ -0,0 +1,63 @@
+/* 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);
index dffbad6327ba4e03decb3e9313683dd71ae9a76d..c267c2e34189116acae7c18b1d179e2eaf377151 100644 (file)
@@ -133,6 +133,27 @@ TEST(strv_env_assign) {
         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;
 
index 5dcec9658d53aa7e92b371eea6edb3b6d2120409..40b12dd0880724cb4f0faf7481be391f9d6e8461 100644 (file)
@@ -138,11 +138,6 @@ TEST(path_is_mount_point) {
         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:
          *
index a2b7101573404f490c0e9eea325e24d876cdbcd7..0b46cad9827a40b81adf46964c01b8d719d1d6e0 100644 (file)
@@ -165,4 +165,15 @@ TEST(get_ctty) {
                 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);
index cbdb9c64fbb35cc764e815a7f85bdf49b3df9684..1770cb3c03859775223e1a949576d53cba02f131 100644 (file)
@@ -8,8 +8,10 @@
 #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"
@@ -133,6 +135,8 @@ TEST(parse_format) {
         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);
@@ -143,6 +147,8 @@ TEST(parse_format) {
         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);
 }
 
index c7be30f563f9c1f98924cc2438a44d7c66bfeda5..53c2a6fb710cd4afb1f1c19f51e9087d58537166 100644 (file)
@@ -665,13 +665,12 @@ static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error *
         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)
@@ -740,13 +739,12 @@ static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error
         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)
@@ -860,13 +858,12 @@ static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *erro
         } 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)
@@ -924,13 +921,12 @@ static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error
         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)
index 7237080f325be0c62d8eed4c7f03ca76bbc54b89..d1d2a14c0f577dc1d8f58f37e7f552fc2eb40a92 100644 (file)
@@ -67,10 +67,12 @@ static int method_set_runtime_servers(sd_bus_message *message, void *userdata, s
                         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)
diff --git a/test/TEST-24-CRYPTSETUP/template.cfg b/test/TEST-24-CRYPTSETUP/template.cfg
new file mode 100644 (file)
index 0000000..42f8b9d
--- /dev/null
@@ -0,0 +1,8 @@
+dn = "cn = systemd"
+expiration_days = 30
+
+signing_key
+encryption_key
+
+tls_www_client
+email_protection_key
index eace3f23c03e5623d5a84c2c4b4f81c9ebc265c1..a6739eee1cd104824df627c83f2e13bbd9bdecdb 100755 (executable)
@@ -39,6 +39,117 @@ check_result_qemu() {
     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
 
@@ -57,6 +168,10 @@ test_create_image() {
     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"
@@ -83,7 +198,7 @@ EOF
         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
diff --git a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/foo.service.wants/mnt-wantedby-automount.automount b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/foo.service.wants/mnt-wantedby-automount.automount
new file mode 120000 (symlink)
index 0000000..14fd395
--- /dev/null
@@ -0,0 +1 @@
+../mnt-wantedby-automount.automount
\ No newline at end of file
index 5edc4ddf22388269bfa3e0b6ecb3eee4626f1119..96fd67261f54af27831c2687cb4eefd0f24d2dec 100644 (file)
@@ -3,7 +3,9 @@
 [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]
diff --git a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.automount b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.automount
new file mode 100644 (file)
index 0000000..b9f2d28
--- /dev/null
@@ -0,0 +1,8 @@
+# Automatically generated by systemd-fstab-generator
+
+[Unit]
+SourcePath=/etc/fstab
+Documentation=man:fstab(5) man:systemd-fstab-generator(8)
+
+[Automount]
+Where=/mnt/wantedby-automount
diff --git a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.mount b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.mount
new file mode 100644 (file)
index 0000000..d909476
--- /dev/null
@@ -0,0 +1,14 @@
+# 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
index e12df820d45db488a41cf2f5b8af7e2d1e07180c..ff76a6f9b49ecd187853c92b72a5a21c327d3f6a 100644 (file)
@@ -3,7 +3,9 @@
 [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]
diff --git a/test/test-fstab-generator/test-18-options.fstab.expected/foo.service.wants/mnt-wantedby-automount.automount b/test/test-fstab-generator/test-18-options.fstab.expected/foo.service.wants/mnt-wantedby-automount.automount
new file mode 120000 (symlink)
index 0000000..14fd395
--- /dev/null
@@ -0,0 +1 @@
+../mnt-wantedby-automount.automount
\ No newline at end of file
index 5edc4ddf22388269bfa3e0b6ecb3eee4626f1119..96fd67261f54af27831c2687cb4eefd0f24d2dec 100644 (file)
@@ -3,7 +3,9 @@
 [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]
diff --git a/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.automount b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.automount
new file mode 100644 (file)
index 0000000..b9f2d28
--- /dev/null
@@ -0,0 +1,8 @@
+# Automatically generated by systemd-fstab-generator
+
+[Unit]
+SourcePath=/etc/fstab
+Documentation=man:fstab(5) man:systemd-fstab-generator(8)
+
+[Automount]
+Where=/mnt/wantedby-automount
diff --git a/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.mount b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.mount
new file mode 100644 (file)
index 0000000..d909476
--- /dev/null
@@ -0,0 +1,14 @@
+# 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
index e12df820d45db488a41cf2f5b8af7e2d1e07180c..ff76a6f9b49ecd187853c92b72a5a21c327d3f6a 100644 (file)
@@ -3,7 +3,9 @@
 [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]
index 98ef0b9fba6deb32600e7d04118a0ee54ae64a8b..2112d68828b02add5300f44f9102b094e0c6670c 100644 (file)
@@ -1,16 +1,17 @@
-/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
diff --git a/test/test-network/conf/25-address-static.network.d/20-clear-addresses.conf b/test/test-network/conf/25-address-static.network.d/20-clear-addresses.conf
new file mode 100644 (file)
index 0000000..a38b07c
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+[Network]
+# An empty string clears previously configured addresses.
+Address=
+Address=10.4.0.1/16
index adb60cd48939b93a32ca9c9beb3d014538055009..6af6e6dc10f1745e17ebd2c057563817cfb3dfe9 100755 (executable)
@@ -1087,7 +1087,7 @@ class NetworkctlTests(unittest.TestCase, Utilities):
         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'])
 
@@ -1121,7 +1121,7 @@ class NetworkctlTests(unittest.TestCase, Utilities):
         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'])
 
@@ -1154,7 +1154,7 @@ class NetworkctlTests(unittest.TestCase, Utilities):
             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'])
 
@@ -2756,6 +2756,15 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         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')
@@ -3186,7 +3195,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         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'])
 
index 0a8a920eeb430f099aab5498c0a5c563cb33ce42..eeec411e9c8f4fb49af5da00ac9045ade1b92c85 100755 (executable)
@@ -5,8 +5,6 @@ set -o pipefail
 
 # 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
@@ -185,6 +183,7 @@ empty0               $IMAGE_EMPTY    -                               headless=1,
 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
@@ -229,6 +228,19 @@ mkdir -p /run/cryptsetup-keys.d
 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}
index bcbe7a1e6a90c60889670167c96ee8073286fd8c..c7d11cffe6ecefc41088cf6852f434a5b0dc6f9d 100755 (executable)
@@ -314,6 +314,16 @@ if ! systemd-detect-virt -q -c ; then
     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
index 2caca7a5a2ac00cfbe72491433a757bb33b4a706..d43d17ca6124a05a77414da48db8ac691d61721d 100755 (executable)
@@ -227,5 +227,9 @@ B=$(systemd-run -q --wait --pipe -p ProtectProc=invisible cat /proc/version)
 
 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"
index 8542245239ac8bd42d9ed2bdb89e09591d8eeafd..9d3604951d56bf6a1b6d1e450520bdb25759ae6c 100644 (file)
@@ -280,6 +280,11 @@ units = [
           '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',
diff --git a/units/systemd-creds.socket b/units/systemd-creds.socket
new file mode 100644 (file)
index 0000000..794fac1
--- /dev/null
@@ -0,0 +1,23 @@
+#  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
diff --git a/units/systemd-creds@.service b/units/systemd-creds@.service
new file mode 100644 (file)
index 0000000..37cdd31
--- /dev/null
@@ -0,0 +1,19 @@
+#  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