Compare commits

...

53 Commits

Author SHA1 Message Date
Lennart Poettering 13b6c4c8de
Merge pull request #14267 from poettering/pkcs11-cryptsetup
just the pkcs11 hookup for classic cryptsetup (/etc/crypttab) split out of the homed PR
2019-12-17 15:30:32 +01:00
Yu Watanabe 6e5df4036f
Merge pull request #14337 from yuwata/network-tc-fq-more
network: tc: introduce more FQ settings
2019-12-17 23:30:10 +09:00
Anita Zhang 1209ef94bd [import] fix stdin/stdout pipe behavior in import/export tar/raw
The code existed in machinectl to use stdin/stdout if the path for
import/export tar/raw was empty or dash (-) but a check to
`fd_verify_regular` in importd prevented it from working.

Update the check instead to explicitly check for regular file or
pipe/fifo.

Fixes #14346
2019-12-17 23:14:53 +09:00
Yu Watanabe 4d92039fce
Merge pull request #14352 from yuwata/sd-netlink-tiny-fixes
sd-netlink: tiny fixes
2019-12-17 23:10:46 +09:00
Yu Watanabe 3267cb45e9
Merge pull request #14208 from poettering/json-homed-prepare
json bits from homed PR
2019-12-17 23:10:08 +09:00
Jan Alexander Steffens (heftig) 05de16766b hwdb: Add Bluetooth-attached Logitech MX Master
Except for the product IDs, the original MX Master appears identical to
the MX Master 2S.
2019-12-17 13:02:28 +01:00
Zbigniew Jędrzejewski-Szmek 0d9ac4ea57
Merge pull request #14360 from yuwata/udev-alternative-names-policy
udev: introduce AlternativeNamesPolicy= setting
2019-12-17 12:13:47 +01:00
Bart Willems 4afb4a9cc5 systemctl: show what verbs support --dry-run in the help page
Signed-off-by: Bart Willems <bwillems@protonmail.com>
2019-12-17 11:08:00 +01:00
Lennart Poettering c16782577b
Merge pull request #14241 from keszybz/resume-timeout
Bump resume timeout to infinity
2019-12-17 10:34:43 +01:00
Yu Watanabe 6d185cffb1 sd-netlink: add a whitespce between cast operator and variable 2019-12-17 17:36:08 +09:00
Yu Watanabe f501c25151 sd-netlink: make netlink_container_parse() takes size_t for rt_len
And use another unsigned short variable for RTA_OK() macro.
2019-12-17 17:35:25 +09:00
Yu Watanabe 49f5cbe924 network: set AlternativeNamesPolicy= in 99-default.link 2019-12-17 15:44:48 +09:00
Yu Watanabe ef1d2c07f9 udev: introduce AlternativeNamesPolicy= setting 2019-12-17 15:44:43 +09:00
Yu Watanabe bb181dd4a6 udev: do not fail if kernel does not support alternative names 2019-12-17 15:43:50 +09:00
Yu Watanabe 78f8849f84 udev: extend the length of ID_NET_NAME_XXX= to ALTIFNAMSIZ 2019-12-17 11:04:11 +09:00
Yu Watanabe 4ef289250f test-network: add a test case for new FQ settings 2019-12-14 12:33:37 +09:00
Yu Watanabe e83562e51e network: tc: add more settings for FQ 2019-12-13 23:36:17 +09:00
Lennart Poettering c2d54475c4 man: document pkcs#11 hookup in /etc/crypttab 2019-12-09 19:25:25 +01:00
Lennart Poettering 086697094e cryptsetup: add native pkcs#11 support to cryptsetup
This adds a new crypttab option for volumes "pkcs11-uri=" which takes a
PKCS#11 URI. When used the key stored in the line's key file is
decrypted with the private key the PKCS#11 URI indiciates.

This means any smartcard that can store private RSA keys is usable for
unlocking LUKS devices.
2019-12-09 19:25:25 +01:00
Lennart Poettering f573629c0b udev: mark all ccid/security devices with a special tag
This adds a udev tag that is supposed to be attached to all devices
that might potentially expose a PKCS#11 slot, i.e. CCID smartcards and
similar. We can then use the appearance of devices of this type as
trigger to rescan PKCS#11 slots.
2019-12-09 19:25:25 +01:00
Lennart Poettering 839fddbe50 shared: add pkcs11-util.[ch] 2019-12-09 19:25:25 +01:00
Lennart Poettering 3f63701983 shared: add openssl helpers 2019-12-09 18:38:59 +01:00
Zbigniew Jędrzejewski-Szmek ff757c9d29 hibernate-resume-generator: wait "infinitely" for the resume device
This makes changes similar to the parent commit, but for hibernate-resume-generator.
If resume= is specified on the kernel command line, we'll set JobRunningTimeoutSec=0
for the device. This matches what we do for the root device.

In practice, other timeouts will take effect. For example dracut tries (and
fails :[ ) to start dracut-emergency.service after some time.

Fixes #7242, https://bugzilla.redhat.com/show_bug.cgi?id=1705522.
2019-12-03 17:20:19 +01:00
Zbigniew Jędrzejewski-Szmek 7cecc56316 cryptsetup-generator: unconfuse writing of the device timeout
The code was using timeout=0 as the default option string. This option string
was ultimately passed to generator_write_timeouts(), which only looks for
comment=systemd.device-timeout= or x-systemd.device-timeout=, i.e. the whole
call path was bogus. Let's rework this: generator_write_timeouts() now writes
any timeouts if configured by the user. create_disk() writes out it's own
timeout, but with lower priority. Since the code path that was calling
timeout=0 was not effective, the only change is that we stop overwriting the
timeout if explicitly configured by the user.

In both code paths, ignore failure to write.
2019-12-03 17:20:19 +01:00
Lennart Poettering 2d8143048b json: add new output flag JSON_PRETTY_AUTO
This takes inspiration from JSON_COLOR_AUTO: it will automatically map
to JSON_PRETTY if connected to a TTY and JSON_NEWLINE otherwise.
2019-12-02 09:47:00 +01:00
Lennart Poettering 19a209cc71 json: add const string dispatcher
This adds json_dispatch_const_string() which is similar to
json_dispatch_string() but doesn't store a strdup()'ed copy of the
string, but a pointer directly into the JSON record.

This should simplify cases where the json variant sticks around long
enough anyway.
2019-12-02 09:47:00 +01:00
Lennart Poettering e4defdc4b0 json: teach json_build() to build arrays from C arrays of JsonVariant 2019-12-02 09:47:00 +01:00
Lennart Poettering a42ef715a2 json: add more dispatch helpers 2019-12-02 09:47:00 +01:00
Lennart Poettering a832b08e6e json: add json_variant_set_field_integer() and json_variant_set_field_boolean() helpers 2019-12-02 09:47:00 +01:00
Lennart Poettering faca141c5f json: add json_variant_unbase64() helper 2019-12-02 09:47:00 +01:00
Lennart Poettering 0b1f2e8a06 json: add new flag for forcing a flush after dumping json data to file
This is particularly useful when no trailing \n is generated, i.e. stdio
doesn't flush the output on its own.
2019-12-02 09:47:00 +01:00
Lennart Poettering 0ac0787e30 json: add explicit log call for ENOMEM 2019-12-02 09:47:00 +01:00
Lennart Poettering 3dd1b600b8 json: permit 'null' as a way to reset tri-states to default 2019-12-02 09:47:00 +01:00
Lennart Poettering aafa52ab83 json: add ability to generate empty arrays/objects in json builder 2019-12-02 09:47:00 +01:00
Lennart Poettering 886b0c93a8 json: allow putting together base64 fields with json_build() 2019-12-02 09:47:00 +01:00
Lennart Poettering 21e2151107 json: add new helper json_variant_append_array() 2019-12-02 09:47:00 +01:00
Lennart Poettering cc164891da json: add new helper json_variant_new_base64() 2019-12-02 09:47:00 +01:00
Lennart Poettering b7fc90a2e6 json: add concept of normalization
Let's add a concept of normalization: as preparation for signing json
records let's add a mechanism to bring JSON records into a well-defined
order so that we can safely validate JSON records.

This adds two booleans to each JsonVariant object: "sorted" and
"normalized". The latter indicates whether a variant is fully sorted
(i.e. all keys of objects listed in alphabetical order) recursively down
the tree. The former is a weaker property: it only checks whether the
keys of the object itself are sorted. All variants which are
"normalized" are also "sorted", but not vice versa.

The knowledge of the "sorted" property is then used to optimize
searching for keys in the variant by using bisection.

Both properties are determined at the moment the variants are allocated.
Since our objects are immutable this is safe.
2019-12-02 09:47:00 +01:00
Lennart Poettering ca409a59c8 json: add json_variant_merge() helper 2019-12-02 09:47:00 +01:00
Lennart Poettering 15f1fb3e3e json: add json_variant_set_field_string() and json_variant_set_field_unsigned() 2019-12-02 09:47:00 +01:00
Lennart Poettering a7f8c9ce60 nspawn-oci: use new json_variant_strv() helper 2019-12-02 09:47:00 +01:00
Lennart Poettering 22f14d6b02 json: add json_variant_strv() helper that converts a json variant to an strv
Only works for arrays of strings, of course.
2019-12-02 09:47:00 +01:00
Lennart Poettering ba23dbf1eb json: optionally, make string checks stricter when dispatching strings 2019-12-02 09:47:00 +01:00
Lennart Poettering d642f640bf json: add flags parameter to json_parse_file(), for parsing "sensitive" data
This will call json_variant_sensitive() internally while parsing for
each allocated sub-variant. This is better than calling it a posteriori
at the end, because partially parsed variants will always be properly
erased from memory this way.
2019-12-02 09:47:00 +01:00
Lennart Poettering f325aaf341 json: add json_parse_file_at() helper
This is an "at" function, similar to json_parse_file().
2019-12-02 09:47:00 +01:00
Lennart Poettering 83bc6cb792 json: add a new "sensitive" flags for JsonVariant objects
An object marked with this flag will be erased from memory when it is
freed. This is useful for dealing with sensitive data (key material,
passphrases) encoded in JSON objects.
2019-12-02 09:47:00 +01:00
Lennart Poettering 78a41236e4 json: add new json_variant_set_field() helper 2019-12-02 09:47:00 +01:00
Lennart Poettering f2ff34ff2a json: add new API json_variant_filter() for dropping fields from objects 2019-12-02 09:47:00 +01:00
Lennart Poettering e787b211a5 json: add new json_variant_is_blank_{object,array}() helpers 2019-12-02 09:47:00 +01:00
Lennart Poettering 07737617a1 json: beef up strv parser to also accept a single string instead of an array of strings
Let's be permissive in what we accept and take a single string instead
of an array of strings, when a string is requested, too.
2019-12-02 09:47:00 +01:00
Lennart Poettering 95244ceb9c fileio: add WRITE_STRING_FILE_MODE_0600 flag for writing files
usually we want to create new files with mode 0666 (modulated by the
umask). Sometimes we want more restrictive access though, let's add an
explicit flag support for that.

(Note that we don't bother with arbitrary access modes to keep things
simple: just "open as umask permits" and "private to me", nothing else)
2019-12-02 09:47:00 +01:00
Lennart Poettering 8241f785f4 fileio: add 'dir_fd' parameter to read_full_file_full()
Let's introduce an "at" version of read_full_file().
2019-12-02 09:47:00 +01:00
Lennart Poettering 0a38e6b9a3 fileio: add an openat() flavour for fopen()
This adds xfopenat() which is to fopen() what xopendirat() is to
opendir(), i.e. the "at" counterpart to fopen().

(Similar to the xopendir() case, we prefix this with "x", in case libc
gains this natively eventually.)
2019-12-02 09:47:00 +01:00
45 changed files with 3260 additions and 237 deletions

View File

@ -449,7 +449,7 @@ mouse:usb:v046dp406a:name:Logitech MX Anywhere 2S:
mouse:bluetooth:v046dpb01a:name:MX Anywhere 2S Mouse: mouse:bluetooth:v046dpb01a:name:MX Anywhere 2S Mouse:
MOUSE_WHEEL_CLICK_ANGLE=20 MOUSE_WHEEL_CLICK_ANGLE=20
# Logitech MX Master # Logitech MX Master (via Logitech Unifying Receiver)
# Horiz wheel has 14 stops, angle is rounded up # Horiz wheel has 14 stops, angle is rounded up
mouse:usb:v046dp4060:name:Logitech MX Master: mouse:usb:v046dp4060:name:Logitech MX Master:
mouse:usb:v046dp4041:name:Logitech MX Master: mouse:usb:v046dp4041:name:Logitech MX Master:
@ -459,6 +459,15 @@ mouse:usb:v046dp4041:name:Logitech MX Master:
MOUSE_WHEEL_CLICK_COUNT=24 MOUSE_WHEEL_CLICK_COUNT=24
MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL=14 MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL=14
# Logitech MX Master (via Bluetooth)
# Horiz wheel has 14 stops, angle is rounded up
mouse:bluetooth:v046dpb012:name:MX Master Mouse:
MOUSE_DPI=1000@2000
MOUSE_WHEEL_CLICK_ANGLE=15
MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL=26
MOUSE_WHEEL_CLICK_COUNT=24
MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL=14
# Logitech MX Master 2S (via Logitech Unifying Receiver) # Logitech MX Master 2S (via Logitech Unifying Receiver)
# Horiz wheel has 14 stops, angle is rounded up # Horiz wheel has 14 stops, angle is rounded up
mouse:usb:v046dp4069:name:Logitech MX Master 2s: mouse:usb:v046dp4069:name:Logitech MX Master 2s:

View File

@ -10,7 +10,7 @@
The Red Hat version has been written by Miloslav Trmac <mitr@redhat.com>. The Red Hat version has been written by Miloslav Trmac <mitr@redhat.com>.
--> -->
<refentry id="crypttab" conditional='HAVE_LIBCRYPTSETUP'> <refentry id="crypttab" conditional='HAVE_LIBCRYPTSETUP' xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo> <refentryinfo>
<title>crypttab</title> <title>crypttab</title>
@ -413,9 +413,22 @@
<varlistentry> <varlistentry>
<term><option>verify</option></term> <term><option>verify</option></term>
<listitem><para> If the encryption password is read from <listitem><para>If the encryption password is read from console, it has to be entered twice to
console, it has to be entered twice to prevent prevent typos.</para></listitem>
typos.</para></listitem> </varlistentry>
<varlistentry>
<term><option>pkcs11-uri=</option></term>
<listitem><para>Takes a <ulink url="https://tools.ietf.org/html/rfc7512">RFC7512 PKCS#11 URI</ulink>
pointing to a private RSA key which is used to decrypt the key specified in the third column of the
line. This is useful for unlocking encrypted volumes through security tokens or smartcards. See below
for an example how to set up this mechanism for unlocking a LUKS volume with a YubiKey security
token. 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 suitable private RSA key object is automatically searched on
it. In this case if multiple suitable objects are found the token is refused. The key configured in
the third column is passed as is to RSA decryption. The resulting decrypted key is then base64
encoded before it is used to unlock the LUKS volume.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
@ -458,7 +471,7 @@
</refsect1> </refsect1>
<refsect1> <refsect1>
<title>Example</title> <title>Examples</title>
<example> <example>
<title>/etc/crypttab example</title> <title>/etc/crypttab example</title>
<para>Set up four encrypted block devices. One using LUKS for <para>Set up four encrypted block devices. One using LUKS for
@ -471,6 +484,27 @@ truecrypt /dev/sda2 /etc/container_password tcrypt
hidden /mnt/tc_hidden /dev/null tcrypt-hidden,tcrypt-keyfile=/etc/keyfile hidden /mnt/tc_hidden /dev/null tcrypt-hidden,tcrypt-keyfile=/etc/keyfile
external /dev/sda3 keyfile:LABEL=keydev keyfile-timeout=10s</programlisting> external /dev/sda3 keyfile:LABEL=keydev keyfile-timeout=10s</programlisting>
</example> </example>
<example>
<title>Yubikey-based Volume Unlocking Example</title>
<para>The PKCS#11 logic allows hooking up any compatible security token that is capable of storing RSA
decryption keys. Here's an example how to set up a Yubikey security token for this purpose:</para>
<programlisting><xi:include href="yubikey-crypttab.sh" parse="text" /></programlisting>
<para>A few notes on the above:</para>
<itemizedlist>
<listitem><para>We use RSA (and not ECC), since Yubikeys support PKCS#11 Decrypt() only for RSA keys</para></listitem>
<listitem><para>We use RSA2048, which is the longest key size current Yubikeys support</para></listitem>
<listitem><para>LUKS key size must be shorter than 2048bit due to RSA padding, hence we use 128 bytes</para></listitem>
<listitem><para>We use Yubikey key slot 9d, since that's apparently the keyslot to use for decryption purposes,
<ulink url="https://developers.yubico.com/PIV/Introduction/Certificate_slots.html">see
documentation</ulink>.</para></listitem>
</itemizedlist>
</example>
</refsect1> </refsect1>
<refsect1> <refsect1>

View File

@ -356,12 +356,24 @@
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><varname>AlternativeNamesPolicy=</varname></term>
<listitem>
<para>A space-separated list of policies by which the interface's alternative names
should be set. Each of the policies may fail, and all successful policies are used. The
available policies are <literal>database</literal>, <literal>onboard</literal>,
<literal>slot</literal>, <literal>path</literal>, and <literal>mac</literal>. If the
kernel does not support the alternative names, then this setting will be ignored.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><varname>AlternativeName=</varname></term> <term><varname>AlternativeName=</varname></term>
<listitem> <listitem>
<para>The alternative interface name to use. This option can be specified multiple times. <para>The alternative interface name to use. This option can be specified multiple times.
If the empty string is assigned to this option, the list is reset, and all prior assignments If the empty string is assigned to this option, the list is reset, and all prior assignments
have no effect.</para> have no effect. If the kernel does not support the alternative names, then this setting will
be ignored.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>

View File

@ -2515,6 +2515,76 @@
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><varname>FairQueueTrafficPolicingFlowLimit=</varname></term>
<listitem>
<para>Specifies the hard limit on the maximum number of packets queued per flow. Defaults to
unset and kernel's default is used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>FairQueueTrafficPolicingQuantum=</varname></term>
<listitem>
<para>Specifies the credit per dequeue RR round, i.e. the amount of bytes a flow is allowed
to dequeue at once. When suffixed with K, M, or G, the specified size is parsed as Kilobytes,
Megabytes, or Gigabytes, respectively, to the base of 1024. Defaults to unset and kernel's
default is used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>FairQueueTrafficPolicingInitialQuantum=</varname></term>
<listitem>
<para>Specifies the initial sending rate credit, i.e. the amount of bytes a new flow is
allowed to dequeue initially. When suffixed with K, M, or G, the specified size is parsed as
Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1024. Defaults to unset and
kernel's default is used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>FairQueueTrafficPolicingMaximumRate=</varname></term>
<listitem>
<para>Specifies the maximum sending rate of a flow. When suffixed with K, M, or G, the
specified size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of
1000. Defaults to unset and kernel's default is used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>FairQueueTrafficPolicingBuckets=</varname></term>
<listitem>
<para>Specifies the size of the hash table used for flow lookups. Defaults to unset and
kernel's default is used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>FairQueueTrafficPolicingOrphanMask=</varname></term>
<listitem>
<para>Takes an unsigned integer. For packets not owned by a socket, fq is able to mask a part
of hash and reduce number of buckets associated with the traffic. Defaults to unset and
kernel's default is used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>FairQueueTrafficPolicingPacing=</varname></term>
<listitem>
<para>Takes a boolean, and enables or disables flow pacing. Defaults to unset and kernel's
default is used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>FairQueueTrafficPolicingCEThresholdSec=</varname></term>
<listitem>
<para>Takes a timespan. This sets a threshold above which all packets are marked with ECN
Congestion Experienced (CE). Defaults to unset and kernel's default is used.</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect1> </refsect1>

45
man/yubikey-crypttab.sh Normal file
View File

@ -0,0 +1,45 @@
# Make sure noone can read the files we generate but us
umask 077
# Destroy any old key on the Yubikey (careful!)
ykman piv reset
# Generate a new private/public key pair on the device, store the public key in 'pubkey.pem'.
ykman piv generate-key -a RSA2048 9d pubkey.pem
# Create a self-signed certificate from this public key, and store it on the device.
ykman piv generate-certificate --subject "Knobelei" 9d pubkey.pem
# Check if the newly create key on the Yubikey shows up as token in PKCS#11. Have a look at the output, and
# copy the resulting token URI to the clipboard.
p11tool --list-tokens
# Generate a (secret) random key to use as LUKS decryption key.
dd if=/dev/urandom of=plaintext.bin bs=128 count=1
# Encode the secret key also as base64 text (with all whitespace removed)
base64 &lt; plaintext.bin | tr -d '\n\r\t ' &gt; plaintext.base64
# Encrypt this newly generated (binary) LUKS decryption key using the public key whose private key is on the
# Yubikey, store the result in /etc/encrypted-luks-key.bin, where we'll look for it during boot.
openssl rsautl -encrypt -pubin -inkey pubkey.pem -in plaintext.bin -out /etc/encrypted-luks-key.bin
# Configure the LUKS decryption key on the LUKS device. We use very low pbkdf settings since the key already
# has quite a high quality (it comes directly from /dev/urandom after all), and thus we don't need to do much
# key derivation.
cryptsetup luksAddKey /dev/sda1 plaintext.base64 --pbkdf=pbkdf2 --pbkdf-force-iterations=1000
# Now securely delete the plain text LUKS key, we don't need it anymore, and since it contains secret key
# material it should be removed from disk thoroughly.
shred -u plaintext.bin plaintext.base64
# We don't need the public key anymore either, let's remove it too. Since this one is not security
# sensitive we just do a regular "rm" here.
rm pubkey.pem
# Test: Let's run systemd-cryptsetup to test if this all worked. The option string should contain the full
# PKCS#11 URI we have in the clipboard, it tells the tool how to decypher the encrypted LUKS key.
systemd-cryptsetup attach mytest /dev/sda1 /etc/encrypted-luks-key.bin 'pkcs11-uri=pkcs11:…'
# If that worked, let's now add the same line persistently to /etc/crypttab, for the future.
echo "mytest /dev/sda1 /etc/encrypted-luks-key 'pkcs11-uri=pkcs11:…' >> /etc/crypttab

View File

@ -1104,6 +1104,18 @@ else
endif endif
conf.set10('HAVE_OPENSSL', have) conf.set10('HAVE_OPENSSL', have)
want_p11kit = get_option('p11kit')
if want_p11kit != 'false' and not skip_deps
libp11kit = dependency('p11-kit-1',
version : '>= 0.23.3',
required : want_p11kit == 'true')
have = libp11kit.found()
else
have = false
libp11kit = []
endif
conf.set10('HAVE_P11KIT', have)
want_elfutils = get_option('elfutils') want_elfutils = get_option('elfutils')
if want_elfutils != 'false' and not skip_deps if want_elfutils != 'false' and not skip_deps
libdw = dependency('libdw', libdw = dependency('libdw',
@ -1997,11 +2009,21 @@ executable('systemd-system-update-generator',
install_dir : systemgeneratordir) install_dir : systemgeneratordir)
if conf.get('HAVE_LIBCRYPTSETUP') == 1 if conf.get('HAVE_LIBCRYPTSETUP') == 1
systemd_cryptsetup_sources = files('''
src/cryptsetup/cryptsetup.c
src/cryptsetup/cryptsetup-pkcs11.h
'''.split())
if conf.get('HAVE_P11KIT') == 1
systemd_cryptsetup_sources += files('src/cryptsetup/cryptsetup-pkcs11.c')
endif
executable('systemd-cryptsetup', executable('systemd-cryptsetup',
'src/cryptsetup/cryptsetup.c', systemd_cryptsetup_sources,
include_directories : includes, include_directories : includes,
link_with : [libshared], link_with : [libshared],
dependencies : [libcryptsetup], dependencies : [libcryptsetup,
libp11kit],
install_rpath : rootlibexecdir, install_rpath : rootlibexecdir,
install : true, install : true,
install_dir : rootlibexecdir) install_dir : rootlibexecdir)
@ -3194,6 +3216,7 @@ missing = []
foreach tuple : [ foreach tuple : [
['libcryptsetup'], ['libcryptsetup'],
['PAM'], ['PAM'],
['p11kit'],
['AUDIT'], ['AUDIT'],
['IMA'], ['IMA'],
['AppArmor'], ['AppArmor'],

View File

@ -282,6 +282,8 @@ option('gnutls', type : 'combo', choices : ['auto', 'true', 'false'],
description : 'gnutls support') description : 'gnutls support')
option('openssl', type : 'combo', choices : ['auto', 'true', 'false'], option('openssl', type : 'combo', choices : ['auto', 'true', 'false'],
description : 'openssl support') description : 'openssl support')
option('p11kit', type : 'combo', choices : ['auto', 'true', 'false'],
description : 'p11kit support')
option('elfutils', type : 'combo', choices : ['auto', 'true', 'false'], option('elfutils', type : 'combo', choices : ['auto', 'true', 'false'],
description : 'elfutils support') description : 'elfutils support')
option('zlib', type : 'combo', choices : ['auto', 'true', 'false'], option('zlib', type : 'combo', choices : ['auto', 'true', 'false'],

View File

@ -12,4 +12,5 @@ OriginalName=*
[Link] [Link]
NamePolicy=keep kernel database onboard slot path NamePolicy=keep kernel database onboard slot path
AlternativeNamesPolicy=database onboard slot path
MACAddressPolicy=persistent MACAddressPolicy=persistent

View File

@ -4,4 +4,10 @@ ACTION=="remove", GOTO="fido_id_end"
SUBSYSTEM=="hidraw", IMPORT{program}="fido_id" SUBSYSTEM=="hidraw", IMPORT{program}="fido_id"
# Tag any form of security token as such
ENV{ID_SECURITY_TOKEN}=="1", TAG+="security-device"
# Tag any CCID device (i.e. Smartcard Reader) as security token
SUBSYSTEM=="usb", ATTR{bInterfaceClass}=="0b", TAG+="security-device"
LABEL="fido_id_end" LABEL="fido_id_end"

View File

@ -136,16 +136,21 @@ static int write_string_file_atomic(
assert(fn); assert(fn);
assert(line); assert(line);
/* Note that we'd really like to use O_TMPFILE here, but can't really, since we want replacement
* semantics here, and O_TMPFILE can't offer that. i.e. rename() replaces but linkat() doesn't. */
r = fopen_temporary(fn, &f, &p); r = fopen_temporary(fn, &f, &p);
if (r < 0) if (r < 0)
return r; return r;
(void) fchmod_umask(fileno(f), 0644);
r = write_string_stream_ts(f, line, flags, ts); r = write_string_stream_ts(f, line, flags, ts);
if (r < 0) if (r < 0)
goto fail; goto fail;
r = fchmod_umask(fileno(f), FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0644);
if (r < 0)
goto fail;
if (rename(p, fn) < 0) { if (rename(p, fn) < 0) {
r = -errno; r = -errno;
goto fail; goto fail;
@ -165,7 +170,7 @@ int write_string_file_ts(
struct timespec *ts) { struct timespec *ts) {
_cleanup_fclose_ FILE *f = NULL; _cleanup_fclose_ FILE *f = NULL;
int q, r; int q, r, fd;
assert(fn); assert(fn);
assert(line); assert(line);
@ -190,16 +195,11 @@ int write_string_file_ts(
} else } else
assert(!ts); assert(!ts);
if (flags & WRITE_STRING_FILE_CREATE) { /* We manually build our own version of fopen(..., "we") that works without O_CREAT and with O_NOFOLLOW if needed. */
r = fopen_unlocked(fn, "we", &f); fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY |
if (r < 0) (FLAGS_SET(flags, WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0) |
goto fail; (FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0),
} else { (FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666));
int fd;
/* We manually build our own version of fopen(..., "we") that
* works without O_CREAT */
fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY | ((flags & WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0));
if (fd < 0) { if (fd < 0) {
r = -errno; r = -errno;
goto fail; goto fail;
@ -210,7 +210,6 @@ int write_string_file_ts(
safe_close(fd); safe_close(fd);
goto fail; goto fail;
} }
}
if (flags & WRITE_STRING_FILE_DISABLE_BUFFER) if (flags & WRITE_STRING_FILE_DISABLE_BUFFER)
setvbuf(f, NULL, _IONBF, 0); setvbuf(f, NULL, _IONBF, 0);
@ -543,17 +542,19 @@ finalize:
return r; return r;
} }
int read_full_file_full(const char *filename, ReadFullFileFlags flags, char **contents, size_t *size) { int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size) {
_cleanup_fclose_ FILE *f = NULL; _cleanup_fclose_ FILE *f = NULL;
int r; int r;
assert(filename); assert(filename);
assert(contents); assert(contents);
r = fopen_unlocked(filename, "re", &f); r = xfopenat(dir_fd, filename, "re", 0, &f);
if (r < 0) if (r < 0)
return r; return r;
(void) __fsetlocking(f, FSETLOCKING_BYCALLER);
return read_full_stream_full(f, filename, flags, contents, size); return read_full_stream_full(f, filename, flags, contents, size);
} }
@ -680,6 +681,81 @@ DIR *xopendirat(int fd, const char *name, int flags) {
return d; return d;
} }
static int mode_to_flags(const char *mode) {
const char *p;
int flags;
if ((p = startswith(mode, "r+")))
flags = O_RDWR;
else if ((p = startswith(mode, "r")))
flags = O_RDONLY;
else if ((p = startswith(mode, "w+")))
flags = O_RDWR|O_CREAT|O_TRUNC;
else if ((p = startswith(mode, "w")))
flags = O_WRONLY|O_CREAT|O_TRUNC;
else if ((p = startswith(mode, "a+")))
flags = O_RDWR|O_CREAT|O_APPEND;
else if ((p = startswith(mode, "a")))
flags = O_WRONLY|O_CREAT|O_APPEND;
else
return -EINVAL;
for (; *p != 0; p++) {
switch (*p) {
case 'e':
flags |= O_CLOEXEC;
break;
case 'x':
flags |= O_EXCL;
break;
case 'm':
/* ignore this here, fdopen() might care later though */
break;
case 'c': /* not sure what to do about this one */
default:
return -EINVAL;
}
}
return flags;
}
int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **ret) {
FILE *f;
/* A combination of fopen() with openat() */
if (dir_fd == AT_FDCWD && flags == 0) {
f = fopen(path, mode);
if (!f)
return -errno;
} else {
int fd, mode_flags;
mode_flags = mode_to_flags(mode);
if (mode_flags < 0)
return mode_flags;
fd = openat(dir_fd, path, mode_flags | flags);
if (fd < 0)
return -errno;
f = fdopen(fd, mode);
if (!f) {
safe_close(fd);
return -errno;
}
}
*ret = f;
return 0;
}
static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) { static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) {
char **i; char **i;

View File

@ -6,6 +6,7 @@
#include <stddef.h> #include <stddef.h>
#include <stdio.h> #include <stdio.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/fcntl.h>
#include <sys/types.h> #include <sys/types.h>
#include "macro.h" #include "macro.h"
@ -22,6 +23,7 @@ typedef enum {
WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 5, WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 5,
WRITE_STRING_FILE_NOFOLLOW = 1 << 6, WRITE_STRING_FILE_NOFOLLOW = 1 << 6,
WRITE_STRING_FILE_MKDIR_0755 = 1 << 7, WRITE_STRING_FILE_MKDIR_0755 = 1 << 7,
WRITE_STRING_FILE_MODE_0600 = 1 << 8,
/* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one /* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one
more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file() more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file()
@ -52,9 +54,9 @@ static inline int write_string_file(const char *fn, const char *line, WriteStrin
int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4); int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4);
int read_one_line_file(const char *filename, char **line); int read_one_line_file(const char *filename, char **line);
int read_full_file_full(const char *filename, ReadFullFileFlags flags, char **contents, size_t *size); int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size);
static inline int read_full_file(const char *filename, char **contents, size_t *size) { static inline int read_full_file(const char *filename, char **contents, size_t *size) {
return read_full_file_full(filename, 0, contents, size); return read_full_file_full(AT_FDCWD, filename, 0, contents, size);
} }
int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size); int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size);
int read_full_stream_full(FILE *f, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size); int read_full_stream_full(FILE *f, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size);
@ -69,6 +71,7 @@ int executable_is_script(const char *path, char **interpreter);
int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field); int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field);
DIR *xopendirat(int dirfd, const char *name, int flags); DIR *xopendirat(int dirfd, const char *name, int flags);
int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **ret);
int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f); int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f);
int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f); int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f);

View File

@ -366,7 +366,7 @@ static int create_disk(
r = generator_write_timeouts(arg_dest, device, name, options, &filtered); r = generator_write_timeouts(arg_dest, device, name, options, &filtered);
if (r < 0) if (r < 0)
return r; log_warning_errno(r, "Failed to write device timeout drop-in: %m");
if (filtered) { if (filtered) {
filtered_escaped = specifier_escape(filtered); filtered_escaped = specifier_escape(filtered);
@ -419,11 +419,11 @@ static int create_disk(
return r; return r;
if (!noauto && !nofail) { if (!noauto && !nofail) {
r = write_drop_in(arg_dest, dmname, 90, "device-timeout", r = write_drop_in(arg_dest, dmname, 40, "device-timeout",
"# Automatically generated by systemd-cryptsetup-generator\n\n" "# Automatically generated by systemd-cryptsetup-generator\n\n"
"[Unit]\nJobTimeoutSec=0"); "[Unit]\nJobTimeoutSec=0");
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to write device drop-in: %m"); log_warning_errno(r, "Failed to write device timeout drop-in: %m");
} }
return 0; return 0;
@ -650,7 +650,6 @@ static int add_proc_cmdline_devices(void) {
crypto_device *d; crypto_device *d;
HASHMAP_FOREACH(d, arg_disks, i) { HASHMAP_FOREACH(d, arg_disks, i) {
const char *options;
_cleanup_free_ char *device = NULL; _cleanup_free_ char *device = NULL;
if (!d->create) if (!d->create)
@ -666,14 +665,11 @@ static int add_proc_cmdline_devices(void) {
if (!device) if (!device)
return log_oom(); return log_oom();
if (d->options) r = create_disk(d->name,
options = d->options; device,
else if (arg_default_options) d->keyfile ?: arg_default_keyfile,
options = arg_default_options; d->keydev,
else d->options ?: arg_default_options);
options = "timeout=0";
r = create_disk(d->name, device, d->keyfile ?: arg_default_keyfile, d->keydev, options);
if (r < 0) if (r < 0)
return r; return r;
} }

View File

@ -0,0 +1,172 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <p11-kit/p11-kit.h>
#include <p11-kit/uri.h>
#include "alloc-util.h"
#include "ask-password-api.h"
#include "cryptsetup-pkcs11.h"
#include "escape.h"
#include "fd-util.h"
#include "macro.h"
#include "memory-util.h"
#include "pkcs11-util.h"
#include "stat-util.h"
#include "strv.h"
static int load_key_file(
const char *key_file,
size_t key_file_size,
uint64_t key_file_offset,
void **ret_encrypted_key,
size_t *ret_encrypted_key_size) {
_cleanup_(erase_and_freep) char *buffer = NULL;
_cleanup_close_ int fd = -1;
ssize_t n;
int r;
assert(key_file);
assert(ret_encrypted_key);
assert(ret_encrypted_key_size);
fd = open(key_file, O_RDONLY|O_CLOEXEC);
if (fd < 0)
return log_error_errno(errno, "Failed to load encrypted PKCS#11 key: %m");
if (key_file_size == 0) {
struct stat st;
if (fstat(fd, &st) < 0)
return log_error_errno(errno, "Failed to stat key file: %m");
r = stat_verify_regular(&st);
if (r < 0)
return log_error_errno(r, "Key file is not a regular file: %m");
if (st.st_size == 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file is empty, refusing.");
if ((uint64_t) st.st_size > SIZE_MAX)
return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Key file too large, refusing.");
if (key_file_offset >= (uint64_t) st.st_size)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file offset too large for file, refusing.");
key_file_size = st.st_size - key_file_offset;
}
buffer = malloc(key_file_size);
if (!buffer)
return log_oom();
if (key_file_offset > 0)
n = pread(fd, buffer, key_file_size, key_file_offset);
else
n = read(fd, buffer, key_file_size);
if (n < 0)
return log_error_errno(errno, "Failed to read PKCS#11 key file: %m");
if (n == 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Empty encrypted key found, refusing.");
*ret_encrypted_key = TAKE_PTR(buffer);
*ret_encrypted_key_size = (size_t) n;
return 0;
}
struct pkcs11_callback_data {
const char *friendly_name;
usec_t until;
void *encrypted_key;
size_t encrypted_key_size;
void *decrypted_key;
size_t decrypted_key_size;
};
static void pkcs11_callback_data_release(struct pkcs11_callback_data *data) {
free(data->decrypted_key);
free(data->encrypted_key);
}
static int pkcs11_callback(
CK_FUNCTION_LIST *m,
CK_SESSION_HANDLE session,
CK_SLOT_ID slot_id,
const CK_SLOT_INFO *slot_info,
const CK_TOKEN_INFO *token_info,
P11KitUri *uri,
void *userdata) {
struct pkcs11_callback_data *data = userdata;
CK_OBJECT_HANDLE object;
int r;
assert(m);
assert(slot_info);
assert(token_info);
assert(uri);
assert(data);
/* Called for every token matching our URI */
r = pkcs11_token_login(m, session, slot_id, token_info, data->friendly_name, "drive-harddisk", "pkcs11-pin", data->until, NULL);
if (r < 0)
return r;
/* We are likely called during early boot, where entropy is scarce. Mix some data from the PKCS#11
* token, if it supports that. It should be cheap, given that we already are talking to it anyway and
* shouldn't hurt. */
(void) pkcs11_token_acquire_rng(m, session);
r = pkcs11_token_find_private_key(m, session, uri, &object);
if (r < 0)
return r;
r = pkcs11_token_decrypt_data(m, session, object, data->encrypted_key, data->encrypted_key_size, &data->decrypted_key, &data->decrypted_key_size);
if (r < 0)
return r;
return 1;
}
int decrypt_pkcs11_key(
const char *friendly_name,
const char *pkcs11_uri,
const char *key_file,
size_t key_file_size,
uint64_t key_file_offset,
usec_t until,
void **ret_decrypted_key,
size_t *ret_decrypted_key_size) {
_cleanup_(pkcs11_callback_data_release) struct pkcs11_callback_data data = {
.friendly_name = friendly_name,
.until = until,
};
int r;
assert(friendly_name);
assert(pkcs11_uri);
assert(key_file);
assert(ret_decrypted_key);
assert(ret_decrypted_key_size);
/* The functions called here log about all errors, except for EAGAIN which means "token not found right now" */
r = load_key_file(key_file, key_file_size, key_file_offset, &data.encrypted_key, &data.encrypted_key_size);
if (r < 0)
return r;
r = pkcs11_find_token(pkcs11_uri, pkcs11_callback, &data);
if (r < 0)
return r;
*ret_decrypted_key = TAKE_PTR(data.decrypted_key);
*ret_decrypted_key_size = data.decrypted_key_size;
return 0;
}

View File

@ -0,0 +1,37 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <sys/types.h>
#include "log.h"
#include "time-util.h"
#if HAVE_P11KIT
int decrypt_pkcs11_key(
const char *friendly_name,
const char *pkcs11_uri,
const char *key_file,
size_t key_file_size,
uint64_t key_file_offset,
usec_t until,
void **ret_decrypted_key,
size_t *ret_decrypted_key_size);
#else
static inline int decrypt_pkcs11_key(
const char *friendly_name,
const char *pkcs11_uri,
const char *key_file,
size_t key_file_size,
uint64_t key_file_offset,
usec_t until,
void **ret_decrypted_key,
size_t *ret_decrypted_key_size) {
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"PKCS#11 Token support not available.");
}
#endif

View File

@ -12,16 +12,19 @@
#include "alloc-util.h" #include "alloc-util.h"
#include "ask-password-api.h" #include "ask-password-api.h"
#include "crypt-util.h" #include "crypt-util.h"
#include "cryptsetup-pkcs11.h"
#include "device-util.h" #include "device-util.h"
#include "escape.h" #include "escape.h"
#include "fileio.h" #include "fileio.h"
#include "fstab-util.h" #include "fstab-util.h"
#include "hexdecoct.h"
#include "log.h" #include "log.h"
#include "main-func.h" #include "main-func.h"
#include "mount-util.h" #include "mount-util.h"
#include "nulstr-util.h" #include "nulstr-util.h"
#include "parse-util.h" #include "parse-util.h"
#include "path-util.h" #include "path-util.h"
#include "pkcs11-util.h"
#include "pretty-print.h" #include "pretty-print.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
@ -54,11 +57,13 @@ static char **arg_tcrypt_keyfiles = NULL;
static uint64_t arg_offset = 0; static uint64_t arg_offset = 0;
static uint64_t arg_skip = 0; static uint64_t arg_skip = 0;
static usec_t arg_timeout = USEC_INFINITY; static usec_t arg_timeout = USEC_INFINITY;
static char *arg_pkcs11_uri = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_cipher, freep); STATIC_DESTRUCTOR_REGISTER(arg_cipher, freep);
STATIC_DESTRUCTOR_REGISTER(arg_hash, freep); STATIC_DESTRUCTOR_REGISTER(arg_hash, freep);
STATIC_DESTRUCTOR_REGISTER(arg_header, freep); STATIC_DESTRUCTOR_REGISTER(arg_header, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tcrypt_keyfiles, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_tcrypt_keyfiles, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_uri, freep);
/* Options Debian's crypttab knows we don't: /* Options Debian's crypttab knows we don't:
@ -228,6 +233,15 @@ static int parse_one_option(const char *option) {
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to parse %s: %m", option); return log_error_errno(r, "Failed to parse %s: %m", option);
} else if ((val = startswith(option, "pkcs11-uri="))) {
if (!pkcs11_uri_valid(val))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "pkcs11-uri= parameter expects a PKCS#11 URI, refusing");
r = free_and_strdup(&arg_pkcs11_uri, val);
if (r < 0)
return log_oom();
} else if (!streq(option, "x-initrd.attach")) } else if (!streq(option, "x-initrd.attach"))
log_warning("Encountered unknown /etc/crypttab option '%s', ignoring.", option); log_warning("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
@ -314,28 +328,19 @@ static char *disk_mount_point(const char *label) {
return NULL; return NULL;
} }
static int get_password(const char *vol, const char *src, usec_t until, bool accept_cached, char ***ret) { static char *friendly_disk_name(const char *src, const char *vol) {
_cleanup_free_ char *description = NULL, *name_buffer = NULL, *mount_point = NULL, *text = NULL, *disk_path = NULL; _cleanup_free_ char *description = NULL, *mount_point = NULL;
_cleanup_strv_free_erase_ char **passwords = NULL; char *name_buffer = NULL;
const char *name = NULL; int r;
char **p, *id;
int r = 0;
assert(vol);
assert(src); assert(src);
assert(ret); assert(vol);
description = disk_description(src); description = disk_description(src);
mount_point = disk_mount_point(vol); mount_point = disk_mount_point(vol);
disk_path = cescape(src); /* If the description string is simply the volume name, then let's not show this twice */
if (!disk_path)
return log_oom();
if (description && streq(vol, description)) if (description && streq(vol, description))
/* If the description string is simply the
* volume name, then let's not show this
* twice */
description = mfree(description); description = mfree(description);
if (mount_point && description) if (mount_point && description)
@ -344,13 +349,39 @@ static int get_password(const char *vol, const char *src, usec_t until, bool acc
r = asprintf(&name_buffer, "%s on %s", vol, mount_point); r = asprintf(&name_buffer, "%s on %s", vol, mount_point);
else if (description) else if (description)
r = asprintf(&name_buffer, "%s (%s)", description, vol); r = asprintf(&name_buffer, "%s (%s)", description, vol);
else
return strdup(vol);
if (r < 0) if (r < 0)
return NULL;
return name_buffer;
}
static int get_password(
const char *vol,
const char *src,
usec_t until,
bool accept_cached,
char ***ret) {
_cleanup_free_ char *friendly = NULL, *text = NULL, *disk_path = NULL;
_cleanup_strv_free_erase_ char **passwords = NULL;
char **p, *id;
int r = 0;
assert(vol);
assert(src);
assert(ret);
friendly = friendly_disk_name(src, vol);
if (!friendly)
return log_oom(); return log_oom();
name = name_buffer ? name_buffer : vol; if (asprintf(&text, "Please enter passphrase for disk %s:", friendly) < 0)
return log_oom();
if (asprintf(&text, "Please enter passphrase for disk %s:", name) < 0) disk_path = cescape(src);
if (!disk_path)
return log_oom(); return log_oom();
id = strjoina("cryptsetup:", disk_path); id = strjoina("cryptsetup:", disk_path);
@ -366,7 +397,7 @@ static int get_password(const char *vol, const char *src, usec_t until, bool acc
assert(strv_length(passwords) == 1); assert(strv_length(passwords) == 1);
if (asprintf(&text, "Please enter passphrase for disk %s (verification):", name) < 0) if (asprintf(&text, "Please enter passphrase for disk %s (verification):", friendly) < 0)
return log_oom(); return log_oom();
id = strjoina("cryptsetup-verification:", disk_path); id = strjoina("cryptsetup-verification:", disk_path);
@ -424,6 +455,11 @@ static int attach_tcrypt(
assert(name); assert(name);
assert(key_file || (passwords && passwords[0])); assert(key_file || (passwords && passwords[0]));
if (arg_pkcs11_uri) {
log_error("Sorry, but tcrypt devices are currently not supported in conjunction with pkcs11 support.");
return -EAGAIN; /* Ask for a regular password */
}
if (arg_tcrypt_hidden) if (arg_tcrypt_hidden)
params.flags |= CRYPT_TCRYPT_HIDDEN_HEADER; params.flags |= CRYPT_TCRYPT_HIDDEN_HEADER;
@ -467,14 +503,14 @@ static int attach_luks_or_plain(
const char *name, const char *name,
const char *key_file, const char *key_file,
char **passwords, char **passwords,
uint32_t flags) { uint32_t flags,
usec_t until) {
int r = 0; int r = 0;
bool pass_volume_key = false; bool pass_volume_key = false;
assert(cd); assert(cd);
assert(name); assert(name);
assert(key_file || passwords);
if ((!arg_type && !crypt_get_type(cd)) || streq_ptr(arg_type, CRYPT_PLAIN)) { if ((!arg_type && !crypt_get_type(cd)) || streq_ptr(arg_type, CRYPT_PLAIN)) {
struct crypt_params_plain params = { struct crypt_params_plain params = {
@ -528,7 +564,111 @@ static int attach_luks_or_plain(
crypt_get_volume_key_size(cd)*8, crypt_get_volume_key_size(cd)*8,
crypt_get_device_name(cd)); crypt_get_device_name(cd));
if (key_file) { if (arg_pkcs11_uri) {
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_free_ void *decrypted_key = NULL;
_cleanup_free_ char *friendly = NULL;
size_t decrypted_key_size = 0;
if (!key_file)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "PKCS#11 mode selected but no key file specified, refusing.");
friendly = friendly_disk_name(crypt_get_device_name(cd), name);
if (!friendly)
return log_oom();
for (;;) {
bool processed = false;
r = decrypt_pkcs11_key(
friendly,
arg_pkcs11_uri,
key_file,
arg_keyfile_size, arg_keyfile_offset,
until,
&decrypted_key, &decrypted_key_size);
if (r >= 0)
break;
if (r != -EAGAIN) /* EAGAIN means: token not found */
return r;
if (!monitor) {
/* We didn't find the token. In this case, watch for it via udev. Let's
* create an event loop and monitor first. */
assert(!event);
r = sd_event_default(&event);
if (r < 0)
return log_error_errno(r, "Failed to allocate event loop: %m");
r = sd_device_monitor_new(&monitor);
if (r < 0)
return log_error_errno(r, "Failed to allocate device monitor: %m");
r = sd_device_monitor_filter_add_match_tag(monitor, "security-device");
if (r < 0)
return log_error_errno(r, "Failed to configure device monitor: %m");
r = sd_device_monitor_attach_event(monitor, event);
if (r < 0)
return log_error_errno(r, "Failed to attach device monitor: %m");
r = sd_device_monitor_start(monitor, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Failed to start device monitor: %m");
log_notice("Security token %s not present for unlocking volume %s, please plug it in.",
arg_pkcs11_uri, friendly);
/* Let's immediately rescan in case the token appeared in the time we needed
* to create and configure the monitor */
continue;
}
for (;;) {
/* Wait for one event, and then eat all subsequent events until there are no
* further ones */
r = sd_event_run(event, processed ? 0 : UINT64_MAX);
if (r < 0)
return log_error_errno(r, "Failed to run event loop: %m");
if (r == 0)
break;
processed = true;
}
log_debug("Got one or more potentially relevant udev events, rescanning PKCS#11...");
}
if (pass_volume_key)
r = crypt_activate_by_volume_key(cd, name, decrypted_key, decrypted_key_size, flags);
else {
_cleanup_free_ char *base64_encoded = NULL;
/* Before using this key as passphrase we base64 encode it. Why? For compatibility
* with homed's PKCS#11 hookup: there we want to use the key we acquired through
* PKCS#11 for other authentication/decryption mechanisms too, and some of them do
* not not take arbitrary binary blobs, but require NUL-terminated strings most
* importantly UNIX password hashes. Hence, for compatibility we want to use a string
* without embedded NUL here too, and that's easiest to generate from a binary blob
* via base64 encoding. */
r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
if (r < 0)
return log_oom();
r = crypt_activate_by_passphrase(cd, name, arg_key_slot, base64_encoded, strlen(base64_encoded), flags);
}
if (r == -EPERM) {
log_error_errno(r, "Failed to activate with PKCS#11 decrypted key. (Key incorrect?)");
return -EAGAIN; /* log actual error, but return EAGAIN */
}
if (r < 0)
return log_error_errno(r, "Failed to activate with PKCS#11 acquired key: %m");
} else if (key_file) {
r = crypt_activate_by_keyfile_device_offset(cd, name, arg_key_slot, key_file, arg_keyfile_size, arg_keyfile_offset, flags); r = crypt_activate_by_keyfile_device_offset(cd, name, arg_key_slot, key_file, arg_keyfile_size, arg_keyfile_offset, flags);
if (r == -EPERM) { if (r == -EPERM) {
log_error_errno(r, "Failed to activate with key file '%s'. (Key data incorrect?)", key_file); log_error_errno(r, "Failed to activate with key file '%s'. (Key data incorrect?)", key_file);
@ -717,7 +857,7 @@ static int run(int argc, char *argv[]) {
for (tries = 0; arg_tries == 0 || tries < arg_tries; tries++) { for (tries = 0; arg_tries == 0 || tries < arg_tries; tries++) {
_cleanup_strv_free_erase_ char **passwords = NULL; _cleanup_strv_free_erase_ char **passwords = NULL;
if (!key_file) { if (!key_file && !arg_pkcs11_uri) {
r = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords); r = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords);
if (r == -EAGAIN) if (r == -EAGAIN)
continue; continue;
@ -728,7 +868,7 @@ static int run(int argc, char *argv[]) {
if (streq_ptr(arg_type, CRYPT_TCRYPT)) if (streq_ptr(arg_type, CRYPT_TCRYPT))
r = attach_tcrypt(cd, argv[2], key_file, passwords, flags); r = attach_tcrypt(cd, argv[2], key_file, passwords, flags);
else else
r = attach_luks_or_plain(cd, argv[2], key_file, passwords, flags); r = attach_luks_or_plain(cd, argv[2], key_file, passwords, flags, until);
if (r >= 0) if (r >= 0)
break; break;
if (r != -EAGAIN) if (r != -EAGAIN)
@ -736,6 +876,7 @@ static int run(int argc, char *argv[]) {
/* Passphrase not correct? Let's try again! */ /* Passphrase not correct? Let's try again! */
key_file = NULL; key_file = NULL;
arg_pkcs11_uri = NULL;
} }
if (arg_tries != 0 && tries >= arg_tries) if (arg_tries != 0 && tries >= arg_tries)

View File

@ -18,7 +18,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
f = fmemopen_unlocked((char*) data, size, "re"); f = fmemopen_unlocked((char*) data, size, "re");
assert_se(f); assert_se(f);
if (json_parse_file(f, NULL, &v, NULL, NULL) < 0) if (json_parse_file(f, NULL, 0, &v, NULL, NULL) < 0)
return 0; return 0;
g = open_memstream_unlocked(&out, &out_size); g = open_memstream_unlocked(&out, &out_size);

View File

@ -5,6 +5,7 @@
#include <unistd.h> #include <unistd.h>
#include "alloc-util.h" #include "alloc-util.h"
#include "dropin.h"
#include "fstab-util.h" #include "fstab-util.h"
#include "generator.h" #include "generator.h"
#include "log.h" #include "log.h"
@ -68,18 +69,18 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
} }
static int process_resume(void) { static int process_resume(void) {
_cleanup_free_ char *name = NULL, *lnk = NULL; _cleanup_free_ char *service_unit = NULL, *device_unit = NULL, *lnk = NULL;
const char *opts;
int r; int r;
if (!arg_resume_device) if (!arg_resume_device)
return 0; return 0;
r = unit_name_from_path_instance("systemd-hibernate-resume", arg_resume_device, ".service", &name); r = unit_name_from_path_instance("systemd-hibernate-resume", arg_resume_device, ".service",
&service_unit);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m"); return log_error_errno(r, "Failed to generate unit name: %m");
lnk = strjoin(arg_dest, "/" SPECIAL_SYSINIT_TARGET ".wants/", name); lnk = strjoin(arg_dest, "/" SPECIAL_SYSINIT_TARGET ".wants/", service_unit);
if (!lnk) if (!lnk)
return log_oom(); return log_oom();
@ -87,12 +88,21 @@ static int process_resume(void) {
if (symlink(SYSTEM_DATA_UNIT_PATH "/systemd-hibernate-resume@.service", lnk) < 0) if (symlink(SYSTEM_DATA_UNIT_PATH "/systemd-hibernate-resume@.service", lnk) < 0)
return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
if (arg_resume_options) r = unit_name_from_path(arg_resume_device, ".device", &device_unit);
opts = arg_resume_options; if (r < 0)
else return log_error_errno(r, "Failed to generate unit name: %m");
opts = arg_root_options;
r = generator_write_timeouts(arg_dest, arg_resume_device, arg_resume_device, opts, NULL); r = write_drop_in(arg_dest, device_unit, 40, "device-timeout",
"# Automatically generated by systemd-cryptsetup-generator\n\n"
"[Unit]\nJobTimeoutSec=0");
if (r < 0)
log_warning_errno(r, "Failed to write device timeout drop-in: %m");
r = generator_write_timeouts(arg_dest,
arg_resume_device,
arg_resume_device,
arg_resume_options ?: arg_root_options,
NULL);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -694,6 +694,7 @@ static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
const char *local, *object; const char *local, *object;
Manager *m = userdata; Manager *m = userdata;
TransferType type; TransferType type;
struct stat st;
uint32_t id; uint32_t id;
assert(msg); assert(msg);
@ -717,9 +718,11 @@ static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
if (r < 0) if (r < 0)
return r; return r;
r = fd_verify_regular(fd); if (fstat(fd, &st) < 0)
if (r < 0) return -errno;
return r;
if (!S_ISREG(st.st_mode) && !S_ISFIFO(st.st_mode))
return -EINVAL;
if (!machine_name_is_valid(local)) if (!machine_name_is_valid(local))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local); return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
@ -829,6 +832,7 @@ static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
const char *local, *object, *format; const char *local, *object, *format;
Manager *m = userdata; Manager *m = userdata;
TransferType type; TransferType type;
struct stat st;
uint32_t id; uint32_t id;
assert(msg); assert(msg);
@ -855,9 +859,11 @@ static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
if (!machine_name_is_valid(local)) if (!machine_name_is_valid(local))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local); return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
r = fd_verify_regular(fd); if (fstat(fd, &st) < 0)
if (r < 0) return -errno;
return r;
if (!S_ISREG(st.st_mode) && !S_ISFIFO(st.st_mode))
return -EINVAL;
type = streq_ptr(sd_bus_message_get_member(msg), "ExportTar") ? TRANSFER_EXPORT_TAR : TRANSFER_EXPORT_RAW; type = streq_ptr(sd_bus_message_get_member(msg), "ExportTar") ? TRANSFER_EXPORT_TAR : TRANSFER_EXPORT_RAW;

View File

@ -864,7 +864,7 @@ int sd_netlink_message_read_strv(sd_netlink_message *m, unsigned short container
const NLType *nl_type; const NLType *nl_type;
struct rtattr *rta; struct rtattr *rta;
void *container; void *container;
unsigned short rt_len; size_t rt_len;
int r; int r;
assert_return(m, -EINVAL); assert_return(m, -EINVAL);
@ -896,10 +896,14 @@ int sd_netlink_message_read_strv(sd_netlink_message *m, unsigned short container
if (r < 0) if (r < 0)
return r; return r;
rt_len = (unsigned short) r; rt_len = (size_t) r;
rta = container; rta = container;
for (; RTA_OK(rta, rt_len); rta = RTA_NEXT(rta, rt_len)) { /* RTA_OK() macro compares with rta->rt_len, which is unsigned short, and
* LGTM.com analysis does not like the type difference. Hence, here we
* introduce an unsigned short variable as a workaround. */
unsigned short len = rt_len;
for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
unsigned short type; unsigned short type;
type = RTA_TYPE(rta); type = RTA_TYPE(rta);
@ -918,11 +922,15 @@ int sd_netlink_message_read_strv(sd_netlink_message *m, unsigned short container
static int netlink_container_parse(sd_netlink_message *m, static int netlink_container_parse(sd_netlink_message *m,
struct netlink_container *container, struct netlink_container *container,
struct rtattr *rta, struct rtattr *rta,
unsigned rt_len) { size_t rt_len) {
_cleanup_free_ struct netlink_attribute *attributes = NULL; _cleanup_free_ struct netlink_attribute *attributes = NULL;
size_t n_allocated = 0; size_t n_allocated = 0;
for (; RTA_OK(rta, rt_len); rta = RTA_NEXT(rta, rt_len)) { /* RTA_OK() macro compares with rta->rt_len, which is unsigned short, and
* LGTM.com analysis does not like the type difference. Hence, here we
* introduce an unsigned short variable as a workaround. */
unsigned short len = rt_len;
for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
unsigned short type; unsigned short type;
type = RTA_TYPE(rta); type = RTA_TYPE(rta);
@ -1022,7 +1030,6 @@ int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short typ
return r; return r;
size = (size_t) r; size = (size_t) r;
m->n_containers++; m->n_containers++;
r = netlink_container_parse(m, r = netlink_container_parse(m,
@ -1052,7 +1059,6 @@ int sd_netlink_message_enter_array(sd_netlink_message *m, unsigned short type_id
return r; return r;
size = (size_t) r; size = (size_t) r;
m->n_containers++; m->n_containers++;
r = netlink_container_parse(m, r = netlink_container_parse(m,

View File

@ -756,6 +756,7 @@ static const NLType rtnl_tca_option_data_fq_types[] = {
[TCA_FQ_FLOW_REFILL_DELAY] = { .type = NETLINK_TYPE_U32 }, [TCA_FQ_FLOW_REFILL_DELAY] = { .type = NETLINK_TYPE_U32 },
[TCA_FQ_LOW_RATE_THRESHOLD] = { .type = NETLINK_TYPE_U32 }, [TCA_FQ_LOW_RATE_THRESHOLD] = { .type = NETLINK_TYPE_U32 },
[TCA_FQ_CE_THRESHOLD] = { .type = NETLINK_TYPE_U32 }, [TCA_FQ_CE_THRESHOLD] = { .type = NETLINK_TYPE_U32 },
[TCA_FQ_ORPHAN_MASK] = { .type = NETLINK_TYPE_U32 },
}; };
static const NLType rtnl_tca_option_data_fq_codel_types[] = { static const NLType rtnl_tca_option_data_fq_codel_types[] = {

View File

@ -981,7 +981,7 @@ static int macsec_read_key_file(NetDev *netdev, SecurityAssociation *sa) {
(void) warn_file_is_world_accessible(sa->key_file, NULL, NULL, 0); (void) warn_file_is_world_accessible(sa->key_file, NULL, NULL, 0);
r = read_full_file_full(sa->key_file, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNHEX, (char **) &key, &key_len); r = read_full_file_full(AT_FDCWD, sa->key_file, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNHEX, (char **) &key, &key_len);
if (r < 0) if (r < 0)
return log_netdev_error_errno(netdev, r, return log_netdev_error_errno(netdev, r,
"Failed to read key from '%s', ignoring: %m", "Failed to read key from '%s', ignoring: %m",

View File

@ -902,7 +902,7 @@ static int wireguard_read_key_file(const char *filename, uint8_t dest[static WG_
(void) warn_file_is_world_accessible(filename, NULL, NULL, 0); (void) warn_file_is_world_accessible(filename, NULL, NULL, 0);
r = read_full_file_full(filename, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64, &key, &key_len); r = read_full_file_full(AT_FDCWD, filename, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64, &key, &key_len);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -266,7 +266,15 @@ TrafficControlQueueingDiscipline.FairQueuingControlledDelayTargetSec, con
TrafficControlQueueingDiscipline.FairQueuingControlledDelayIntervalSec, config_parse_tc_fair_queuing_controlled_delay_usec, 0, 0 TrafficControlQueueingDiscipline.FairQueuingControlledDelayIntervalSec, config_parse_tc_fair_queuing_controlled_delay_usec, 0, 0
TrafficControlQueueingDiscipline.FairQueuingControlledDelayCEThresholdSec, config_parse_tc_fair_queuing_controlled_delay_usec, 0, 0 TrafficControlQueueingDiscipline.FairQueuingControlledDelayCEThresholdSec, config_parse_tc_fair_queuing_controlled_delay_usec, 0, 0
TrafficControlQueueingDiscipline.FairQueuingControlledDelayECN, config_parse_tc_fair_queuing_controlled_delay_bool, 0, 0 TrafficControlQueueingDiscipline.FairQueuingControlledDelayECN, config_parse_tc_fair_queuing_controlled_delay_bool, 0, 0
TrafficControlQueueingDiscipline.FairQueueTrafficPolicingPacketLimit, config_parse_tc_fair_queue_traffic_policing_packet_limit, 0, 0 TrafficControlQueueingDiscipline.FairQueueTrafficPolicingPacketLimit, config_parse_tc_fair_queue_traffic_policing_u32, 0, 0
TrafficControlQueueingDiscipline.FairQueueTrafficPolicingFlowLimit, config_parse_tc_fair_queue_traffic_policing_u32, 0, 0
TrafficControlQueueingDiscipline.FairQueueTrafficPolicingQuantum, config_parse_tc_fair_queue_traffic_policing_size, 0, 0
TrafficControlQueueingDiscipline.FairQueueTrafficPolicingInitialQuantum, config_parse_tc_fair_queue_traffic_policing_size, 0, 0
TrafficControlQueueingDiscipline.FairQueueTrafficPolicingMaximumRate, config_parse_tc_fair_queue_traffic_policing_max_rate, 0, 0
TrafficControlQueueingDiscipline.FairQueueTrafficPolicingBuckets, config_parse_tc_fair_queue_traffic_policing_u32, 0, 0
TrafficControlQueueingDiscipline.FairQueueTrafficPolicingOrphanMask, config_parse_tc_fair_queue_traffic_policing_u32, 0, 0
TrafficControlQueueingDiscipline.FairQueueTrafficPolicingPacing, config_parse_tc_fair_queue_traffic_policing_bool, 0, 0
TrafficControlQueueingDiscipline.FairQueueTrafficPolicingCEThresholdSec, config_parse_tc_fair_queue_traffic_policing_usec, 0, 0
/* backwards compatibility: do not add new entries to this section */ /* backwards compatibility: do not add new entries to this section */
Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local) Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local)
DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier) DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)

View File

@ -9,6 +9,20 @@
#include "netlink-util.h" #include "netlink-util.h"
#include "parse-util.h" #include "parse-util.h"
#include "string-util.h" #include "string-util.h"
#include "util.h"
static int fair_queue_traffic_policing_init(QDisc *qdisc) {
FairQueueTrafficPolicing *fq;
assert(qdisc);
fq = FQ(qdisc);
fq->pacing = -1;
fq->ce_threshold_usec = USEC_INFINITY;
return 0;
}
static int fair_queue_traffic_policing_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) { static int fair_queue_traffic_policing_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
FairQueueTrafficPolicing *fq; FairQueueTrafficPolicing *fq;
@ -24,9 +38,62 @@ static int fair_queue_traffic_policing_fill_message(Link *link, QDisc *qdisc, sd
if (r < 0) if (r < 0)
return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m"); return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
r = sd_netlink_message_append_u32(req, TCA_FQ_PLIMIT, fq->limit); if (fq->packet_limit > 0) {
r = sd_netlink_message_append_u32(req, TCA_FQ_PLIMIT, fq->packet_limit);
if (r < 0) if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_FQ_PLIMIT attribute: %m"); return log_link_error_errno(link, r, "Could not append TCA_FQ_PLIMIT attribute: %m");
}
if (fq->flow_limit > 0) {
r = sd_netlink_message_append_u32(req, TCA_FQ_FLOW_PLIMIT, fq->flow_limit);
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_FQ_FLOW_PLIMIT attribute: %m");
}
if (fq->quantum > 0) {
r = sd_netlink_message_append_u32(req, TCA_FQ_QUANTUM, fq->quantum);
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_FQ_QUANTUM attribute: %m");
}
if (fq->initial_quantum > 0) {
r = sd_netlink_message_append_u32(req, TCA_FQ_INITIAL_QUANTUM, fq->initial_quantum);
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_FQ_INITIAL_QUANTUM attribute: %m");
}
if (fq->pacing >= 0) {
r = sd_netlink_message_append_u32(req, TCA_FQ_RATE_ENABLE, fq->pacing);
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_FQ_RATE_ENABLE attribute: %m");
}
if (fq->max_rate > 0) {
r = sd_netlink_message_append_u32(req, TCA_FQ_FLOW_MAX_RATE, fq->max_rate);
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_FQ_FLOW_MAX_RATE attribute: %m");
}
if (fq->buckets > 0) {
uint32_t l;
l = log2u(fq->buckets);
r = sd_netlink_message_append_u32(req, TCA_FQ_BUCKETS_LOG, l);
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_FQ_BUCKETS_LOG attribute: %m");
}
if (fq->orphan_mask > 0) {
r = sd_netlink_message_append_u32(req, TCA_FQ_ORPHAN_MASK, fq->orphan_mask);
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_FQ_ORPHAN_MASK attribute: %m");
}
if (fq->ce_threshold_usec != USEC_INFINITY) {
r = sd_netlink_message_append_u32(req, TCA_FQ_CE_THRESHOLD, fq->ce_threshold_usec);
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_FQ_CE_THRESHOLD attribute: %m");
}
r = sd_netlink_message_close_container(req); r = sd_netlink_message_close_container(req);
if (r < 0) if (r < 0)
@ -35,7 +102,137 @@ static int fair_queue_traffic_policing_fill_message(Link *link, QDisc *qdisc, sd
return 0; return 0;
} }
int config_parse_tc_fair_queue_traffic_policing_packet_limit( int config_parse_tc_fair_queue_traffic_policing_u32(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
FairQueueTrafficPolicing *fq;
Network *network = data;
uint32_t *p;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = qdisc_new_static(QDISC_KIND_FQ, network, filename, section_line, &qdisc);
if (r == -ENOMEM)
return log_oom();
if (r < 0)
return log_syntax(unit, LOG_ERR, filename, line, r,
"More than one kind of queueing discipline, ignoring assignment: %m");
fq = FQ(qdisc);
if (streq(lvalue, "FairQueueTrafficPolicingPacketLimit"))
p = &fq->packet_limit;
else if (streq(lvalue, "FairQueueTrafficPolicingFlowLimit"))
p = &fq->flow_limit;
else if (streq(lvalue, "FairQueueTrafficPolicingBuckets"))
p = &fq->buckets;
else if (streq(lvalue, "FairQueueTrafficPolicingOrphanMask"))
p = &fq->orphan_mask;
else
assert_not_reached("Invalid lvalue");
if (isempty(rvalue)) {
*p = 0;
qdisc = NULL;
return 0;
}
r = safe_atou32(rvalue, p);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
qdisc = NULL;
return 0;
}
int config_parse_tc_fair_queue_traffic_policing_size(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
FairQueueTrafficPolicing *fq;
Network *network = data;
uint64_t sz;
uint32_t *p;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = qdisc_new_static(QDISC_KIND_FQ, network, filename, section_line, &qdisc);
if (r == -ENOMEM)
return log_oom();
if (r < 0)
return log_syntax(unit, LOG_ERR, filename, line, r,
"More than one kind of queueing discipline, ignoring assignment: %m");
fq = FQ(qdisc);
if (streq(lvalue, "FairQueueTrafficPolicingQuantum"))
p = &fq->quantum;
else if (streq(lvalue, "FairQueueTrafficPolicingInitialQuantum"))
p = &fq->initial_quantum;
else
assert_not_reached("Invalid lvalue");
if (isempty(rvalue)) {
*p = 0;
qdisc = NULL;
return 0;
}
r = parse_size(rvalue, 1024, &sz);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
if (sz > UINT32_MAX) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Specified '%s=' is too large, ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
*p = sz;
qdisc = NULL;
return 0;
}
int config_parse_tc_fair_queue_traffic_policing_bool(
const char *unit, const char *unit,
const char *filename, const char *filename,
unsigned line, unsigned line,
@ -67,13 +264,13 @@ int config_parse_tc_fair_queue_traffic_policing_packet_limit(
fq = FQ(qdisc); fq = FQ(qdisc);
if (isempty(rvalue)) { if (isempty(rvalue)) {
fq->limit = 0; fq->pacing = -1;
qdisc = NULL; qdisc = NULL;
return 0; return 0;
} }
r = safe_atou32(rvalue, &fq->limit); r = parse_boolean(rvalue);
if (r < 0) { if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s", "Failed to parse '%s=', ignoring assignment: %s",
@ -81,12 +278,132 @@ int config_parse_tc_fair_queue_traffic_policing_packet_limit(
return 0; return 0;
} }
fq->pacing = r;
qdisc = NULL;
return 0;
}
int config_parse_tc_fair_queue_traffic_policing_usec(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
FairQueueTrafficPolicing *fq;
Network *network = data;
usec_t sec;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = qdisc_new_static(QDISC_KIND_FQ, network, filename, section_line, &qdisc);
if (r == -ENOMEM)
return log_oom();
if (r < 0)
return log_syntax(unit, LOG_ERR, filename, line, r,
"More than one kind of queueing discipline, ignoring assignment: %m");
fq = FQ(qdisc);
if (isempty(rvalue)) {
fq->ce_threshold_usec = USEC_INFINITY;
qdisc = NULL;
return 0;
}
r = parse_sec(rvalue, &sec);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
if (sec > UINT32_MAX) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"Specified '%s=' is too large, ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
fq->ce_threshold_usec = sec;
qdisc = NULL;
return 0;
}
int config_parse_tc_fair_queue_traffic_policing_max_rate(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
FairQueueTrafficPolicing *fq;
Network *network = data;
uint64_t sz;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = qdisc_new_static(QDISC_KIND_FQ, network, filename, section_line, &qdisc);
if (r == -ENOMEM)
return log_oom();
if (r < 0)
return log_syntax(unit, LOG_ERR, filename, line, r,
"More than one kind of queueing discipline, ignoring assignment: %m");
fq = FQ(qdisc);
if (isempty(rvalue)) {
fq->max_rate = 0;
qdisc = NULL;
return 0;
}
r = parse_size(rvalue, 1000, &sz);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
if (sz / 8 > UINT32_MAX) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"Specified '%s=' is too large, ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
fq->max_rate = sz / 8;
qdisc = NULL; qdisc = NULL;
return 0; return 0;
} }
const QDiscVTable fq_vtable = { const QDiscVTable fq_vtable = {
.init = fair_queue_traffic_policing_init,
.object_size = sizeof(FairQueueTrafficPolicing), .object_size = sizeof(FairQueueTrafficPolicing),
.tca_kind = "fq", .tca_kind = "fq",
.fill_message = fair_queue_traffic_policing_fill_message, .fill_message = fair_queue_traffic_policing_fill_message,

View File

@ -8,10 +8,22 @@
typedef struct FairQueueTrafficPolicing { typedef struct FairQueueTrafficPolicing {
QDisc meta; QDisc meta;
uint32_t limit; uint32_t packet_limit;
uint32_t flow_limit;
uint32_t quantum;
uint32_t initial_quantum;
uint32_t max_rate;
uint32_t buckets;
uint32_t orphan_mask;
int pacing;
usec_t ce_threshold_usec;
} FairQueueTrafficPolicing; } FairQueueTrafficPolicing;
DEFINE_QDISC_CAST(FQ, FairQueueTrafficPolicing); DEFINE_QDISC_CAST(FQ, FairQueueTrafficPolicing);
extern const QDiscVTable fq_vtable; extern const QDiscVTable fq_vtable;
CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queue_traffic_policing_packet_limit); CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queue_traffic_policing_u32);
CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queue_traffic_policing_size);
CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queue_traffic_policing_bool);
CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queue_traffic_policing_usec);
CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queue_traffic_policing_max_rate);

View File

@ -172,24 +172,13 @@ static int oci_env(const char *name, JsonVariant *v, JsonDispatchFlags flags, vo
static int oci_args(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { static int oci_args(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
_cleanup_strv_free_ char **l = NULL; _cleanup_strv_free_ char **l = NULL;
char ***value = userdata; char ***value = userdata;
JsonVariant *e;
int r; int r;
assert(value); assert(value);
JSON_VARIANT_ARRAY_FOREACH(e, v) { r = json_variant_strv(v, &l);
const char *n;
if (!json_variant_is_string(e))
return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
"Argument is not a string.");
assert_se(n = json_variant_string(e));
r = strv_extend(&l, n);
if (r < 0) if (r < 0)
return log_oom(); return json_log(v, flags, r, "Cannot parse arguments as list of strings: %m");
}
if (strv_isempty(l)) if (strv_isempty(l))
return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
@ -2214,7 +2203,7 @@ int oci_load(FILE *f, const char *bundle, Settings **ret) {
path = strjoina(bundle, "/config.json"); path = strjoina(bundle, "/config.json");
r = json_parse_file(f, path, &oci, &line, &column); r = json_parse_file(f, path, 0, &oci, &line, &column);
if (r < 0) { if (r < 0) {
if (line != 0 && column != 0) if (line != 0 && column != 0)
return log_error_errno(r, "Failed to parse '%s' at %u:%u: %m", path, line, column); return log_error_errno(r, "Failed to parse '%s' at %u:%u: %m", path, line, column);

View File

@ -237,10 +237,12 @@ int generator_write_timeouts(
return log_error_errno(r, "Failed to make unit name from path: %m"); return log_error_errno(r, "Failed to make unit name from path: %m");
return write_drop_in_format(dir, unit, 50, "device-timeout", return write_drop_in_format(dir, unit, 50, "device-timeout",
"# Automatically generated by %s\n\n" "# Automatically generated by %s\n"
"# from supplied options \"%s\"\n\n"
"[Unit]\n" "[Unit]\n"
"JobRunningTimeoutSec=%s", "JobRunningTimeoutSec=%s",
program_invocation_short_name, program_invocation_short_name,
opts,
timeout); timeout);
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */ /* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once #pragma once
#include <fcntl.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
@ -54,6 +55,7 @@ typedef enum JsonVariantType {
} JsonVariantType; } JsonVariantType;
int json_variant_new_stringn(JsonVariant **ret, const char *s, size_t n); int json_variant_new_stringn(JsonVariant **ret, const char *s, size_t n);
int json_variant_new_base64(JsonVariant **ret, const void *p, size_t n);
int json_variant_new_integer(JsonVariant **ret, intmax_t i); int json_variant_new_integer(JsonVariant **ret, intmax_t i);
int json_variant_new_unsigned(JsonVariant **ret, uintmax_t u); int json_variant_new_unsigned(JsonVariant **ret, uintmax_t u);
int json_variant_new_real(JsonVariant **ret, long double d); int json_variant_new_real(JsonVariant **ret, long double d);
@ -120,6 +122,10 @@ static inline bool json_variant_is_null(JsonVariant *v) {
} }
bool json_variant_is_negative(JsonVariant *v); bool json_variant_is_negative(JsonVariant *v);
bool json_variant_is_blank_object(JsonVariant *v);
bool json_variant_is_blank_array(JsonVariant *v);
bool json_variant_is_normalized(JsonVariant *v);
bool json_variant_is_sorted(JsonVariant *v);
size_t json_variant_elements(JsonVariant *v); size_t json_variant_elements(JsonVariant *v);
JsonVariant *json_variant_by_index(JsonVariant *v, size_t index); JsonVariant *json_variant_by_index(JsonVariant *v, size_t index);
@ -128,6 +134,8 @@ JsonVariant *json_variant_by_key_full(JsonVariant *v, const char *key, JsonVaria
bool json_variant_equal(JsonVariant *a, JsonVariant *b); bool json_variant_equal(JsonVariant *a, JsonVariant *b);
void json_variant_sensitive(JsonVariant *v);
struct json_variant_foreach_state { struct json_variant_foreach_state {
JsonVariant *variant; JsonVariant *variant;
size_t idx; size_t idx;
@ -155,19 +163,46 @@ int json_variant_get_source(JsonVariant *v, const char **ret_source, unsigned *r
typedef enum JsonFormatFlags { typedef enum JsonFormatFlags {
JSON_FORMAT_NEWLINE = 1 << 0, /* suffix with newline */ JSON_FORMAT_NEWLINE = 1 << 0, /* suffix with newline */
JSON_FORMAT_PRETTY = 1 << 1, /* add internal whitespace to appeal to human readers */ JSON_FORMAT_PRETTY = 1 << 1, /* add internal whitespace to appeal to human readers */
JSON_FORMAT_COLOR = 1 << 2, /* insert ANSI color sequences */ JSON_FORMAT_PRETTY_AUTO = 1 << 2, /* same, but only if connected to a tty (and JSON_FORMAT_NEWLINE otherwise) */
JSON_FORMAT_COLOR_AUTO = 1 << 3, /* insert ANSI color sequences if colors_enabled() says so */ JSON_FORMAT_COLOR = 1 << 3, /* insert ANSI color sequences */
JSON_FORMAT_SOURCE = 1 << 4, /* prefix with source filename/line/column */ JSON_FORMAT_COLOR_AUTO = 1 << 4, /* insert ANSI color sequences if colors_enabled() says so */
JSON_FORMAT_SSE = 1 << 5, /* prefix/suffix with W3C server-sent events */ JSON_FORMAT_SOURCE = 1 << 5, /* prefix with source filename/line/column */
JSON_FORMAT_SEQ = 1 << 6, /* prefix/suffix with RFC 7464 application/json-seq */ JSON_FORMAT_SSE = 1 << 6, /* prefix/suffix with W3C server-sent events */
JSON_FORMAT_SEQ = 1 << 7, /* prefix/suffix with RFC 7464 application/json-seq */
JSON_FORMAT_FLUSH = 1 << 8, /* call fflush() after dumping JSON */
} JsonFormatFlags; } JsonFormatFlags;
int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret); int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret);
void json_variant_dump(JsonVariant *v, JsonFormatFlags flags, FILE *f, const char *prefix); void json_variant_dump(JsonVariant *v, JsonFormatFlags flags, FILE *f, const char *prefix);
int json_parse(const char *string, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column); int json_variant_filter(JsonVariant **v, char **to_remove);
int json_parse_continue(const char **p, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column);
int json_parse_file(FILE *f, const char *path, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column); int json_variant_set_field(JsonVariant **v, const char *field, JsonVariant *value);
int json_variant_set_field_string(JsonVariant **v, const char *field, const char *value);
int json_variant_set_field_integer(JsonVariant **v, const char *field, intmax_t value);
int json_variant_set_field_unsigned(JsonVariant **v, const char *field, uintmax_t value);
int json_variant_set_field_boolean(JsonVariant **v, const char *field, bool b);
int json_variant_append_array(JsonVariant **v, JsonVariant *element);
int json_variant_merge(JsonVariant **v, JsonVariant *m);
int json_variant_strv(JsonVariant *v, char ***ret);
int json_variant_sort(JsonVariant **v);
int json_variant_normalize(JsonVariant **v);
typedef enum JsonParseFlags {
JSON_PARSE_SENSITIVE = 1 << 0, /* mark variant as "sensitive", i.e. something containing secret key material or such */
} JsonParseFlags;
int json_parse(const char *string, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column);
int json_parse_continue(const char **p, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column);
int json_parse_file_at(FILE *f, int dir_fd, const char *path, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column);
static inline int json_parse_file(FILE *f, const char *path, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) {
return json_parse_file_at(f, AT_FDCWD, path, flags, ret, ret_line, ret_column);
}
enum { enum {
_JSON_BUILD_STRING, _JSON_BUILD_STRING,
@ -183,8 +218,10 @@ enum {
_JSON_BUILD_PAIR_CONDITION, _JSON_BUILD_PAIR_CONDITION,
_JSON_BUILD_NULL, _JSON_BUILD_NULL,
_JSON_BUILD_VARIANT, _JSON_BUILD_VARIANT,
_JSON_BUILD_VARIANT_ARRAY,
_JSON_BUILD_LITERAL, _JSON_BUILD_LITERAL,
_JSON_BUILD_STRV, _JSON_BUILD_STRV,
_JSON_BUILD_BASE64,
_JSON_BUILD_MAX, _JSON_BUILD_MAX,
}; };
@ -194,13 +231,17 @@ enum {
#define JSON_BUILD_REAL(d) _JSON_BUILD_REAL, ({ long double _x = d; _x; }) #define JSON_BUILD_REAL(d) _JSON_BUILD_REAL, ({ long double _x = d; _x; })
#define JSON_BUILD_BOOLEAN(b) _JSON_BUILD_BOOLEAN, ({ bool _x = b; _x; }) #define JSON_BUILD_BOOLEAN(b) _JSON_BUILD_BOOLEAN, ({ bool _x = b; _x; })
#define JSON_BUILD_ARRAY(...) _JSON_BUILD_ARRAY_BEGIN, __VA_ARGS__, _JSON_BUILD_ARRAY_END #define JSON_BUILD_ARRAY(...) _JSON_BUILD_ARRAY_BEGIN, __VA_ARGS__, _JSON_BUILD_ARRAY_END
#define JSON_BUILD_EMPTY_ARRAY _JSON_BUILD_ARRAY_BEGIN, _JSON_BUILD_ARRAY_END
#define JSON_BUILD_OBJECT(...) _JSON_BUILD_OBJECT_BEGIN, __VA_ARGS__, _JSON_BUILD_OBJECT_END #define JSON_BUILD_OBJECT(...) _JSON_BUILD_OBJECT_BEGIN, __VA_ARGS__, _JSON_BUILD_OBJECT_END
#define JSON_BUILD_EMPTY_OBJECT _JSON_BUILD_OBJECT_BEGIN, _JSON_BUILD_OBJECT_END
#define JSON_BUILD_PAIR(n, ...) _JSON_BUILD_PAIR, ({ const char *_x = n; _x; }), __VA_ARGS__ #define JSON_BUILD_PAIR(n, ...) _JSON_BUILD_PAIR, ({ const char *_x = n; _x; }), __VA_ARGS__
#define JSON_BUILD_PAIR_CONDITION(c, n, ...) _JSON_BUILD_PAIR_CONDITION, ({ bool _x = c; _x; }), ({ const char *_x = n; _x; }), __VA_ARGS__ #define JSON_BUILD_PAIR_CONDITION(c, n, ...) _JSON_BUILD_PAIR_CONDITION, ({ bool _x = c; _x; }), ({ const char *_x = n; _x; }), __VA_ARGS__
#define JSON_BUILD_NULL _JSON_BUILD_NULL #define JSON_BUILD_NULL _JSON_BUILD_NULL
#define JSON_BUILD_VARIANT(v) _JSON_BUILD_VARIANT, ({ JsonVariant *_x = v; _x; }) #define JSON_BUILD_VARIANT(v) _JSON_BUILD_VARIANT, ({ JsonVariant *_x = v; _x; })
#define JSON_BUILD_VARIANT_ARRAY(v, n) _JSON_BUILD_VARIANT_ARRAY, ({ JsonVariant **_x = v; _x; }), ({ size_t _y = n; _y; })
#define JSON_BUILD_LITERAL(l) _JSON_BUILD_LITERAL, ({ const char *_x = l; _x; }) #define JSON_BUILD_LITERAL(l) _JSON_BUILD_LITERAL, ({ const char *_x = l; _x; })
#define JSON_BUILD_STRV(l) _JSON_BUILD_STRV, ({ char **_x = l; _x; }) #define JSON_BUILD_STRV(l) _JSON_BUILD_STRV, ({ char **_x = l; _x; })
#define JSON_BUILD_BASE64(p, n) _JSON_BUILD_BASE64, ({ const void *_x = p; _x; }), ({ size_t _y = n; _y; })
int json_build(JsonVariant **ret, ...); int json_build(JsonVariant **ret, ...);
int json_buildv(JsonVariant **ret, va_list ap); int json_buildv(JsonVariant **ret, va_list ap);
@ -213,10 +254,11 @@ typedef enum JsonDispatchFlags {
JSON_PERMISSIVE = 1 << 0, /* Shall parsing errors be considered fatal for this property? */ JSON_PERMISSIVE = 1 << 0, /* Shall parsing errors be considered fatal for this property? */
JSON_MANDATORY = 1 << 1, /* Should existence of this property be mandatory? */ JSON_MANDATORY = 1 << 1, /* Should existence of this property be mandatory? */
JSON_LOG = 1 << 2, /* Should the parser log about errors? */ JSON_LOG = 1 << 2, /* Should the parser log about errors? */
JSON_SAFE = 1 << 3, /* Don't accept "unsafe" strings in json_dispatch_string() + json_dispatch_string() */
/* The following two may be passed into log_json() in addition to the three above */ /* The following two may be passed into log_json() in addition to the three above */
JSON_DEBUG = 1 << 3, /* Indicates that this log message is a debug message */ JSON_DEBUG = 1 << 4, /* Indicates that this log message is a debug message */
JSON_WARNING = 1 << 4, /* Indicates that this log message is a warning message */ JSON_WARNING = 1 << 5, /* Indicates that this log message is a warning message */
} JsonDispatchFlags; } JsonDispatchFlags;
typedef int (*JsonDispatchCallback)(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); typedef int (*JsonDispatchCallback)(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
@ -232,6 +274,7 @@ typedef struct JsonDispatch {
int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallback bad, JsonDispatchFlags flags, void *userdata); int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallback bad, JsonDispatchFlags flags, void *userdata);
int json_dispatch_string(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); int json_dispatch_string(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_const_string(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_strv(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); int json_dispatch_strv(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_boolean(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); int json_dispatch_boolean(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_tristate(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); int json_dispatch_tristate(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
@ -240,6 +283,10 @@ int json_dispatch_integer(const char *name, JsonVariant *variant, JsonDispatchFl
int json_dispatch_unsigned(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); int json_dispatch_unsigned(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_uint32(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); int json_dispatch_uint32(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_int32(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); int json_dispatch_int32(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_uid_gid(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_user_group_name(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_id128(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_unsupported(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
assert_cc(sizeof(uintmax_t) == sizeof(uint64_t)); assert_cc(sizeof(uintmax_t) == sizeof(uint64_t));
#define json_dispatch_uint64 json_dispatch_unsigned #define json_dispatch_uint64 json_dispatch_unsigned
@ -275,6 +322,9 @@ int json_log_internal(JsonVariant *variant, int level, int error, const char *fi
: -ERRNO_VALUE(_e); \ : -ERRNO_VALUE(_e); \
}) })
#define json_log_oom(variant, flags) \
json_log(variant, flags, SYNTHETIC_ERRNO(ENOMEM), "Out of memory.")
#define JSON_VARIANT_STRING_CONST(x) _JSON_VARIANT_STRING_CONST(UNIQ, (x)) #define JSON_VARIANT_STRING_CONST(x) _JSON_VARIANT_STRING_CONST(UNIQ, (x))
#define _JSON_VARIANT_STRING_CONST(xq, x) \ #define _JSON_VARIANT_STRING_CONST(xq, x) \
@ -284,5 +334,7 @@ int json_log_internal(JsonVariant *variant, int level, int error, const char *fi
(JsonVariant*) ((uintptr_t) UNIQ_T(json_string_const, xq) + 1); \ (JsonVariant*) ((uintptr_t) UNIQ_T(json_string_const, xq) + 1); \
}) })
int json_variant_unbase64(JsonVariant *v, void **ret, size_t *ret_size);
const char *json_variant_type_to_string(JsonVariantType t); const char *json_variant_type_to_string(JsonVariantType t);
JsonVariantType json_variant_type_from_string(const char *s); JsonVariantType json_variant_type_from_string(const char *s);

View File

@ -132,6 +132,7 @@ shared_sources = files('''
nscd-flush.h nscd-flush.h
nsflags.c nsflags.c
nsflags.h nsflags.h
openssl-util.h
os-util.c os-util.c
os-util.h os-util.h
output-mode.c output-mode.c
@ -141,6 +142,8 @@ shared_sources = files('''
path-lookup.c path-lookup.c
path-lookup.h path-lookup.h
pe-header.h pe-header.h
pkcs11-util.c
pkcs11-util.h
pretty-print.c pretty-print.c
pretty-print.h pretty-print.h
ptyfwd.c ptyfwd.c
@ -277,6 +280,8 @@ libshared_deps = [threads,
libkmod, libkmod,
liblz4, liblz4,
libmount, libmount,
libopenssl,
libp11kit,
librt, librt,
libseccomp, libseccomp,
libselinux, libselinux,

View File

@ -0,0 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <openssl/pem.h>
DEFINE_TRIVIAL_CLEANUP_FUNC(X509*, X509_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(X509_NAME*, X509_NAME_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(EVP_PKEY_CTX*, EVP_PKEY_CTX_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(EVP_CIPHER_CTX*, EVP_CIPHER_CTX_free);

912
src/shared/pkcs11-util.c Normal file
View File

@ -0,0 +1,912 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <fcntl.h>
#include "ask-password-api.h"
#include "escape.h"
#include "fd-util.h"
#include "io-util.h"
#include "memory-util.h"
#if HAVE_OPENSSL
#include "openssl-util.h"
#endif
#include "pkcs11-util.h"
#include "random-util.h"
#include "string-util.h"
#include "strv.h"
bool pkcs11_uri_valid(const char *uri) {
const char *p;
/* A very superficial checker for RFC7512 PKCS#11 URI syntax */
if (isempty(uri))
return false;
p = startswith(uri, "pkcs11:");
if (!p)
return false;
if (isempty(p))
return false;
if (!in_charset(p, ALPHANUMERICAL "-_?;&%="))
return false;
return true;
}
#if HAVE_P11KIT
int uri_from_string(const char *p, P11KitUri **ret) {
_cleanup_(p11_kit_uri_freep) P11KitUri *uri = NULL;
assert(p);
assert(ret);
uri = p11_kit_uri_new();
if (!uri)
return -ENOMEM;
if (p11_kit_uri_parse(p, P11_KIT_URI_FOR_ANY, uri) != P11_KIT_URI_OK)
return -EINVAL;
*ret = TAKE_PTR(uri);
return 0;
}
P11KitUri *uri_from_module_info(const CK_INFO *info) {
P11KitUri *uri;
assert(info);
uri = p11_kit_uri_new();
if (!uri)
return NULL;
*p11_kit_uri_get_module_info(uri) = *info;
return uri;
}
P11KitUri *uri_from_slot_info(const CK_SLOT_INFO *slot_info) {
P11KitUri *uri;
assert(slot_info);
uri = p11_kit_uri_new();
if (!uri)
return NULL;
*p11_kit_uri_get_slot_info(uri) = *slot_info;
return uri;
}
P11KitUri *uri_from_token_info(const CK_TOKEN_INFO *token_info) {
P11KitUri *uri;
assert(token_info);
uri = p11_kit_uri_new();
if (!uri)
return NULL;
*p11_kit_uri_get_token_info(uri) = *token_info;
return uri;
}
CK_RV pkcs11_get_slot_list_malloc(
CK_FUNCTION_LIST *m,
CK_SLOT_ID **ret_slotids,
CK_ULONG *ret_n_slotids) {
CK_RV rv;
assert(m);
assert(ret_slotids);
assert(ret_n_slotids);
for (unsigned tries = 0; tries < 16; tries++) {
_cleanup_free_ CK_SLOT_ID *slotids = NULL;
CK_ULONG n_slotids = 0;
rv = m->C_GetSlotList(0, NULL, &n_slotids);
if (rv != CKR_OK)
return rv;
if (n_slotids == 0) {
*ret_slotids = NULL;
*ret_n_slotids = 0;
return CKR_OK;
}
slotids = new(CK_SLOT_ID, n_slotids);
if (!slotids)
return CKR_HOST_MEMORY;
rv = m->C_GetSlotList(0, slotids, &n_slotids);
if (rv == CKR_OK) {
*ret_slotids = TAKE_PTR(slotids);
*ret_n_slotids = n_slotids;
return CKR_OK;
}
if (rv != CKR_BUFFER_TOO_SMALL)
return rv;
/* Hu? Maybe somebody plugged something in and things changed? Let's try again */
}
return CKR_BUFFER_TOO_SMALL;
}
char *pkcs11_token_label(const CK_TOKEN_INFO *token_info) {
char *t;
/* The label is not NUL terminated and likely padded with spaces, let's make a copy here, so that we
* can strip that. */
t = strndup((char*) token_info->label, sizeof(token_info->label));
if (!t)
return NULL;
strstrip(t);
return t;
}
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 *keyname,
usec_t until,
char **ret_used_pin) {
_cleanup_free_ char *token_uri_string = NULL, *token_uri_escaped = NULL, *id = NULL, *token_label = NULL;
_cleanup_(p11_kit_uri_freep) P11KitUri *token_uri = NULL;
CK_TOKEN_INFO updated_token_info;
int uri_result;
CK_RV rv;
int r;
assert(m);
assert(token_info);
token_label = pkcs11_token_label(token_info);
if (!token_label)
return log_oom();
token_uri = uri_from_token_info(token_info);
if (!token_uri)
return log_oom();
uri_result = p11_kit_uri_format(token_uri, P11_KIT_URI_FOR_ANY, &token_uri_string);
if (uri_result != P11_KIT_URI_OK)
return log_warning_errno(SYNTHETIC_ERRNO(EAGAIN), "Failed to format slot URI: %s", p11_kit_uri_message(uri_result));
if (FLAGS_SET(token_info->flags, CKF_PROTECTED_AUTHENTICATION_PATH)) {
rv = m->C_Login(session, CKU_USER, NULL, 0);
if (rv != CKR_OK)
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to log into security token '%s': %s", token_label, p11_kit_strerror(rv));
log_info("Successully logged into security token '%s' via protected authentication path.", token_label);
*ret_used_pin = NULL;
return 0;
}
if (!FLAGS_SET(token_info->flags, CKF_LOGIN_REQUIRED)) {
log_info("No login into security token '%s' required.", token_label);
*ret_used_pin = NULL;
return 0;
}
token_uri_escaped = cescape(token_uri_string);
if (!token_uri_escaped)
return log_oom();
id = strjoin("pkcs11:", token_uri_escaped);
if (!id)
return log_oom();
for (unsigned tries = 0; tries < 3; tries++) {
_cleanup_strv_free_erase_ char **passwords = NULL;
_cleanup_free_ char *text = NULL;
char **i, *e;
if (FLAGS_SET(token_info->flags, CKF_USER_PIN_FINAL_TRY))
r = asprintf(&text,
"Please enter correct PIN for security token '%s' in order to unlock %s (final try):",
token_label, friendly_name);
if (FLAGS_SET(token_info->flags, CKF_USER_PIN_COUNT_LOW))
r = asprintf(&text,
"PIN has been entered incorrectly previously, please enter correct PIN for security token '%s' in order to unlock %s:",
token_label, friendly_name);
else if (tries == 0)
r = asprintf(&text,
"Please enter PIN for security token '%s' in order to unlock %s:",
token_label, friendly_name);
else
r = asprintf(&text,
"Please enter PIN for security token '%s' in order to unlock %s (try #%u):",
token_label, friendly_name, tries+1);
if (r < 0)
return log_oom();
e = getenv("PIN");
if (e) {
passwords = strv_new(e);
if (!passwords)
return log_oom();
string_erase(e);
if (unsetenv("PIN") < 0)
return log_error_errno(errno, "Failed to unset $PIN: %m");
} else {
/* We never cache PINs, simply because it's fatal if we use wrong PINs, since usually there are only 3 tries */
r = ask_password_auto(text, icon_name, id, keyname, until, 0, &passwords);
if (r < 0)
return log_error_errno(r, "Failed to query PIN for security token '%s': %m", token_label);
}
STRV_FOREACH(i, passwords) {
rv = m->C_Login(session, CKU_USER, (CK_UTF8CHAR*) *i, strlen(*i));
if (rv == CKR_OK) {
if (ret_used_pin) {
char *c;
c = strdup(*i);
if (!c)
return log_oom();
*ret_used_pin = c;
}
log_info("Successfully logged into security token '%s'.", token_label);
return 0;
}
if (rv == CKR_PIN_LOCKED)
return log_error_errno(SYNTHETIC_ERRNO(EPERM),
"PIN has been locked, please reset PIN of security token '%s'.", token_label);
if (!IN_SET(rv, CKR_PIN_INCORRECT, CKR_PIN_LEN_RANGE))
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to log into security token '%s': %s", token_label, p11_kit_strerror(rv));
/* Referesh the token info, so that we can prompt knowing the new flags if they changed. */
rv = m->C_GetTokenInfo(slotid, &updated_token_info);
if (rv != CKR_OK)
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to acquire updated security token information for slot %lu: %s",
slotid, p11_kit_strerror(rv));
token_info = &updated_token_info;
log_notice("PIN for token '%s' is incorrect, please try again.", token_label);
}
}
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Too many attempts to log into token '%s'.", token_label);
}
int pkcs11_token_find_x509_certificate(
CK_FUNCTION_LIST *m,
CK_SESSION_HANDLE session,
P11KitUri *search_uri,
CK_OBJECT_HANDLE *ret_object) {
bool found_class = false, found_certificate_type = 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;
assert(m);
assert(search_uri);
assert(ret_object);
attributes = p11_kit_uri_get_attributes(search_uri, &n_attributes);
for (a = 0; a < n_attributes; a++) {
/* 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) {
case CKA_CLASS: {
CK_OBJECT_CLASS c;
if (attributes[a].ulValueLen != sizeof(c))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_CLASS attribute size.");
memcpy(&c, attributes[a].pValue, sizeof(c));
if (c != CKO_CERTIFICATE)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected PKCS#11 object is not an X.509 certificate, refusing.");
found_class = true;
break;
}
case CKA_CERTIFICATE_TYPE: {
CK_CERTIFICATE_TYPE t;
if (attributes[a].ulValueLen != sizeof(t))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_CERTIFICATE_TYPE attribute size.");
memcpy(&t, attributes[a].pValue, sizeof(t));
if (t != CKC_X_509)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected PKCS#11 object is not an X.509 certificate, refusing.");
found_certificate_type = true;
break;
}}
}
if (!found_class || !found_certificate_type) {
/* Hmm, let's slightly extend the attribute list we search for */
attributes_buffer = new(CK_ATTRIBUTE, n_attributes + !found_class + !found_certificate_type);
if (!attributes_buffer)
return log_oom();
memcpy(attributes_buffer, attributes, sizeof(CK_ATTRIBUTE) * n_attributes);
if (!found_class) {
static const CK_OBJECT_CLASS class = CKO_CERTIFICATE;
attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
.type = CKA_CLASS,
.pValue = (CK_OBJECT_CLASS*) &class,
.ulValueLen = sizeof(class),
};
}
if (!found_certificate_type) {
static const CK_CERTIFICATE_TYPE type = CKC_X_509;
attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
.type = CKA_CERTIFICATE_TYPE,
.pValue = (CK_CERTIFICATE_TYPE*) &type,
.ulValueLen = sizeof(type),
};
}
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", p11_kit_strerror(rv));
rv = m->C_FindObjects(session, objects, ELEMENTSOF(objects), &n_objects);
rv2 = m->C_FindObjectsFinal(session);
if (rv != CKR_OK)
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to find objects: %s", p11_kit_strerror(rv));
if (rv2 != CKR_OK)
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to finalize object find call: %s", p11_kit_strerror(rv));
if (n_objects == 0)
return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
"Failed to find selected X509 certificate on token.");
if (n_objects > 1)
return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
"Configured URI matches multiple certificates, refusing.");
*ret_object = objects[0];
return 0;
}
#if HAVE_OPENSSL
int pkcs11_token_read_x509_certificate(
CK_FUNCTION_LIST *m,
CK_SESSION_HANDLE session,
CK_OBJECT_HANDLE object,
X509 **ret_cert) {
_cleanup_free_ void *buffer = NULL;
_cleanup_free_ char *t = NULL;
CK_ATTRIBUTE attribute = {
.type = CKA_VALUE
};
CK_RV rv;
_cleanup_(X509_freep) X509 *x509 = NULL;
X509_NAME *name = NULL;
const unsigned char *p;
rv = m->C_GetAttributeValue(session, object, &attribute, 1);
if (rv != CKR_OK)
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to read X.509 certificate size off token: %s", p11_kit_strerror(rv));
buffer = malloc(attribute.ulValueLen);
if (!buffer)
return log_oom();
attribute.pValue = buffer;
rv = m->C_GetAttributeValue(session, object, &attribute, 1);
if (rv != CKR_OK)
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to read X.509 certificate data off token: %s", p11_kit_strerror(rv));
p = attribute.pValue;
x509 = d2i_X509(NULL, &p, attribute.ulValueLen);
if (!x509)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed parse X.509 certificate.");
name = X509_get_subject_name(x509);
if (!name)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to acquire X.509 subject name.");
t = X509_NAME_oneline(name, NULL, 0);
if (!t)
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to format X.509 subject name as string.");
log_debug("Using X.509 certificate issued for '%s'.", t);
*ret_cert = TAKE_PTR(x509);
return 0;
}
#endif
int pkcs11_token_find_private_key(
CK_FUNCTION_LIST *m,
CK_SESSION_HANDLE session,
P11KitUri *search_uri,
CK_OBJECT_HANDLE *ret_object) {
bool found_decrypt = false, found_class = false, found_key_type = 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;
assert(m);
assert(search_uri);
assert(ret_object);
attributes = p11_kit_uri_get_attributes(search_uri, &n_attributes);
for (a = 0; a < n_attributes; a++) {
/* 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) {
case CKA_CLASS: {
CK_OBJECT_CLASS c;
if (attributes[a].ulValueLen != sizeof(c))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_CLASS attribute size.");
memcpy(&c, attributes[a].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;
}
case CKA_DECRYPT: {
CK_BBOOL b;
if (attributes[a].ulValueLen != sizeof(b))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_DECRYPT attribute size.");
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.");
found_decrypt = true;
break;
}
case CKA_KEY_TYPE: {
CK_KEY_TYPE t;
if (attributes[a].ulValueLen != sizeof(t))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_KEY_TYPE attribute size.");
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.");
found_key_type = true;
break;
}}
}
if (!found_decrypt || !found_class || !found_key_type) {
/* Hmm, let's slightly extend the attribute list we search for */
attributes_buffer = new(CK_ATTRIBUTE, n_attributes + !found_decrypt + !found_class + !found_key_type);
if (!attributes_buffer)
return log_oom();
memcpy(attributes_buffer, attributes, sizeof(CK_ATTRIBUTE) * n_attributes);
if (!found_decrypt) {
static const CK_BBOOL yes = true;
attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
.type = CKA_DECRYPT,
.pValue = (CK_BBOOL*) &yes,
.ulValueLen = sizeof(yes),
};
}
if (!found_class) {
static const CK_OBJECT_CLASS class = CKO_PRIVATE_KEY;
attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
.type = CKA_CLASS,
.pValue = (CK_OBJECT_CLASS*) &class,
.ulValueLen = sizeof(class),
};
}
if (!found_key_type) {
static const CK_KEY_TYPE type = CKK_RSA;
attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
.type = CKA_KEY_TYPE,
.pValue = (CK_KEY_TYPE*) &type,
.ulValueLen = sizeof(type),
};
}
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", p11_kit_strerror(rv));
rv = m->C_FindObjects(session, objects, ELEMENTSOF(objects), &n_objects);
rv2 = m->C_FindObjectsFinal(session);
if (rv != CKR_OK)
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to find objects: %s", p11_kit_strerror(rv));
if (rv2 != CKR_OK)
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to finalize object find call: %s", 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.");
*ret_object = objects[0];
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) {
static const CK_MECHANISM mechanism = {
.mechanism = CKM_RSA_PKCS
};
_cleanup_(erase_and_freep) CK_BYTE *dbuffer = NULL;
CK_ULONG dbuffer_size = 0;
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_DecryptInit(session, (CK_MECHANISM*) &mechanism, object);
if (rv != CKR_OK)
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to initialize decryption on security token: %s", p11_kit_strerror(rv));
dbuffer_size = encrypted_data_size; /* Start with something reasonable */
dbuffer = malloc(dbuffer_size);
if (!dbuffer)
return log_oom();
rv = m->C_Decrypt(session, (CK_BYTE*) encrypted_data, encrypted_data_size, dbuffer, &dbuffer_size);
if (rv == CKR_BUFFER_TOO_SMALL) {
erase_and_free(dbuffer);
dbuffer = malloc(dbuffer_size);
if (!dbuffer)
return log_oom();
rv = m->C_Decrypt(session, (CK_BYTE*) encrypted_data, encrypted_data_size, dbuffer, &dbuffer_size);
}
if (rv != CKR_OK)
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to decrypt key on security token: %s", p11_kit_strerror(rv));
log_info("Successfully decrypted key with security token.");
*ret_decrypted_data = TAKE_PTR(dbuffer);
*ret_decrypted_data_size = dbuffer_size;
return 0;
}
int pkcs11_token_acquire_rng(
CK_FUNCTION_LIST *m,
CK_SESSION_HANDLE session) {
_cleanup_free_ void *buffer = NULL;
_cleanup_close_ int fd = -1;
size_t rps;
CK_RV rv;
int r;
assert(m);
/* While we are at it, let's read some RNG data from the PKCS#11 token and pass it to the kernel
* random pool. This should be cheap if we are talking to the device already. Note that we don't
* credit any entropy, since we don't know about the quality of the pkcs#11 token's RNG. Why bother
* at all? There are two sides to the argument whether to generate private keys on tokens or on the
* host. By crediting some data from the token RNG to the host's pool we at least can say that any
* key generated from it is at least as good as both sources individually. */
rps = random_pool_size();
buffer = malloc(rps);
if (!buffer)
return log_oom();
rv = m->C_GenerateRandom(session, buffer, rps);
if (rv != CKR_OK)
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Failed to generate RNG data on security token: %s", p11_kit_strerror(rv));
fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return log_debug_errno(errno, "Failed to open /dev/urandom for writing: %m");
r = loop_write(fd, buffer, rps, false);
if (r < 0)
return log_debug_errno(r, "Failed to write PKCS#11 acquired random data to /dev/urandom: %m");
log_debug("Successfully written %zu bytes random data acquired via PKCS#11 to kernel random pool.", rps);
return 0;
}
static int token_process(
CK_FUNCTION_LIST *m,
CK_SLOT_ID slotid,
const CK_SLOT_INFO *slot_info,
const CK_TOKEN_INFO *token_info,
P11KitUri *search_uri,
pkcs11_find_token_callback_t callback,
void *userdata) {
_cleanup_free_ char *token_label = NULL;
CK_SESSION_HANDLE session;
CK_RV rv;
int r;
assert(m);
assert(slot_info);
assert(token_info);
assert(search_uri);
token_label = pkcs11_token_label(token_info);
if (!token_label)
return log_oom();
rv = m->C_OpenSession(slotid, CKF_SERIAL_SESSION, NULL, NULL, &session);
if (rv != CKR_OK)
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to create session for security token '%s': %s", token_label, p11_kit_strerror(rv));
if (callback)
r = callback(m, session, slotid, slot_info, token_info, search_uri, userdata);
else
r = 1; /* if not callback was specified, just say we found what we were looking for */
rv = m->C_CloseSession(session);
if (rv != CKR_OK)
log_warning("Failed to close session on PKCS#11 token, ignoring: %s", p11_kit_strerror(rv));
return r;
}
static int slot_process(
CK_FUNCTION_LIST *m,
CK_SLOT_ID slotid,
P11KitUri *search_uri,
pkcs11_find_token_callback_t callback,
void *userdata) {
_cleanup_(p11_kit_uri_freep) P11KitUri* slot_uri = NULL, *token_uri = NULL;
_cleanup_free_ char *token_uri_string = NULL;
CK_TOKEN_INFO token_info;
CK_SLOT_INFO slot_info;
int uri_result;
CK_RV rv;
assert(m);
assert(search_uri);
/* We return -EAGAIN for all failures we can attribute to a specific slot in some way, so that the
* caller might try other slots before giving up. */
rv = m->C_GetSlotInfo(slotid, &slot_info);
if (rv != CKR_OK) {
log_warning("Failed to acquire slot info for slot %lu, ignoring slot: %s", slotid, p11_kit_strerror(rv));
return -EAGAIN;
}
slot_uri = uri_from_slot_info(&slot_info);
if (!slot_uri)
return log_oom();
if (DEBUG_LOGGING) {
_cleanup_free_ char *slot_uri_string = NULL;
uri_result = p11_kit_uri_format(slot_uri, P11_KIT_URI_FOR_ANY, &slot_uri_string);
if (uri_result != P11_KIT_URI_OK) {
log_warning("Failed to format slot URI, ignoring slot: %s", p11_kit_uri_message(uri_result));
return -EAGAIN;
}
log_debug("Found slot with URI %s", slot_uri_string);
}
rv = m->C_GetTokenInfo(slotid, &token_info);
if (rv == CKR_TOKEN_NOT_PRESENT) {
log_debug("Token not present in slot, ignoring.");
return -EAGAIN;
} else if (rv != CKR_OK) {
log_warning("Failed to acquire token info for slot %lu, ignoring slot: %s", slotid, p11_kit_strerror(rv));
return -EAGAIN;
}
token_uri = uri_from_token_info(&token_info);
if (!token_uri)
return log_oom();
uri_result = p11_kit_uri_format(token_uri, P11_KIT_URI_FOR_ANY, &token_uri_string);
if (uri_result != P11_KIT_URI_OK) {
log_warning("Failed to format slot URI: %s", p11_kit_uri_message(uri_result));
return -EAGAIN;
}
if (!p11_kit_uri_match_token_info(search_uri, &token_info)) {
log_debug("Found non-matching token with URI %s.", token_uri_string);
return -EAGAIN;
}
log_debug("Found matching token with URI %s.", token_uri_string);
return token_process(
m,
slotid,
&slot_info,
&token_info,
search_uri,
callback,
userdata);
}
static int module_process(
CK_FUNCTION_LIST *m,
P11KitUri *search_uri,
pkcs11_find_token_callback_t callback,
void *userdata) {
_cleanup_free_ char *name = NULL, *module_uri_string = NULL;
_cleanup_(p11_kit_uri_freep) P11KitUri* module_uri = NULL;
_cleanup_free_ CK_SLOT_ID *slotids = NULL;
CK_ULONG n_slotids = 0;
int uri_result;
CK_INFO info;
size_t k;
CK_RV rv;
int r;
assert(m);
assert(search_uri);
/* We ignore most errors from modules here, in order to skip over faulty modules: one faulty module
* should not have the effect that we don't try the others anymore. We indicate such per-module
* failures with -EAGAIN, which let's the caller try the next module. */
name = p11_kit_module_get_name(m);
if (!name)
return log_oom();
log_debug("Trying PKCS#11 module %s.", name);
rv = m->C_GetInfo(&info);
if (rv != CKR_OK) {
log_warning("Failed to get info on PKCS#11 module, ignoring module: %s", p11_kit_strerror(rv));
return -EAGAIN;
}
module_uri = uri_from_module_info(&info);
if (!module_uri)
return log_oom();
uri_result = p11_kit_uri_format(module_uri, P11_KIT_URI_FOR_ANY, &module_uri_string);
if (uri_result != P11_KIT_URI_OK) {
log_warning("Failed to format module URI, ignoring module: %s", p11_kit_uri_message(uri_result));
return -EAGAIN;
}
log_debug("Found module with URI %s", module_uri_string);
rv = pkcs11_get_slot_list_malloc(m, &slotids, &n_slotids);
if (rv != CKR_OK) {
log_warning("Failed to get slot list, ignoring module: %s", p11_kit_strerror(rv));
return -EAGAIN;
}
if (n_slotids == 0) {
log_debug("This module has no slots? Ignoring module.");
return -EAGAIN;
}
for (k = 0; k < n_slotids; k++) {
r = slot_process(
m,
slotids[k],
search_uri,
callback,
userdata);
if (r != -EAGAIN)
return r;
}
return -EAGAIN;
}
int pkcs11_find_token(
const char *pkcs11_uri,
pkcs11_find_token_callback_t callback,
void *userdata) {
_cleanup_(p11_kit_modules_finalize_and_releasep) CK_FUNCTION_LIST **modules = NULL;
_cleanup_(p11_kit_uri_freep) P11KitUri *search_uri = NULL;
int r;
assert(pkcs11_uri);
/* Execute the specified callback for each matching token found. If nothing is found returns
* -EAGAIN. Logs about all errors, except for EAGAIN, which the caller has to log about. */
r = uri_from_string(pkcs11_uri, &search_uri);
if (r < 0)
return log_error_errno(r, "Failed to parse PKCS#11 URI '%s': %m", pkcs11_uri);
modules = p11_kit_modules_load_and_initialize(0);
if (!modules)
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to initialize pkcs11 modules");
for (CK_FUNCTION_LIST **i = modules; *i; i++) {
r = module_process(
*i,
search_uri,
callback,
userdata);
if (r != -EAGAIN)
return r;
}
return -EAGAIN;
}
#endif

48
src/shared/pkcs11-util.h Normal file
View File

@ -0,0 +1,48 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <stdbool.h>
#if HAVE_P11KIT
#include <p11-kit/p11-kit.h>
#include <p11-kit/uri.h>
#if HAVE_OPENSSL
#include <openssl/pem.h>
#endif
#endif
#include "macro.h"
#include "time-util.h"
bool pkcs11_uri_valid(const char *uri);
#if HAVE_P11KIT
int uri_from_string(const char *p, P11KitUri **ret);
P11KitUri *uri_from_module_info(const CK_INFO *info);
P11KitUri *uri_from_slot_info(const CK_SLOT_INFO *slot_info);
P11KitUri *uri_from_token_info(const CK_TOKEN_INFO *token_info);
DEFINE_TRIVIAL_CLEANUP_FUNC(P11KitUri*, p11_kit_uri_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(CK_FUNCTION_LIST**, p11_kit_modules_finalize_and_release);
CK_RV pkcs11_get_slot_list_malloc(CK_FUNCTION_LIST *m, CK_SLOT_ID **ret_slotids, CK_ULONG *ret_n_slotids);
char *pkcs11_token_label(const CK_TOKEN_INFO *token_info);
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 *keyname, usec_t until, char **ret_used_pin);
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);
#endif
int pkcs11_token_find_private_key(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, P11KitUri *search_uri, CK_OBJECT_HANDLE *ret_object);
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);
int pkcs11_token_acquire_rng(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session);
typedef int (*pkcs11_find_token_callback_t)(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_SLOT_ID slotid, const CK_SLOT_INFO *slot_info, const CK_TOKEN_INFO *token_info, P11KitUri *uri, void *userdata);
int pkcs11_find_token(const char *pkcs11_uri, pkcs11_find_token_callback_t callback, void *userdata);
#endif

View File

@ -577,7 +577,7 @@ static int varlink_parse_message(Varlink *v) {
varlink_log(v, "New incoming message: %s", begin); varlink_log(v, "New incoming message: %s", begin);
r = json_parse(begin, &v->current, NULL, NULL); r = json_parse(begin, 0, &v->current, NULL, NULL);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -7956,6 +7956,9 @@ static int systemctl_help(void) {
" --what=RESOURCES Which types of resources to remove\n" " --what=RESOURCES Which types of resources to remove\n"
" --now Start or stop unit after enabling or disabling it\n" " --now Start or stop unit after enabling or disabling it\n"
" --dry-run Only print what would be done\n" " --dry-run Only print what would be done\n"
" Currently supported by verbs: halt, poweroff, reboot,\n"
" kexec, suspend, hibernate, suspend-then-hibernate,\n"
" hybrid-sleep, default, rescue, emergency, and exit.\n"
" -q --quiet Suppress output\n" " -q --quiet Suppress output\n"
" --wait For (re)start, wait until service stopped again\n" " --wait For (re)start, wait until service stopped again\n"
" For is-system-running, wait until startup is completed\n" " For is-system-running, wait until startup is completed\n"

View File

@ -82,7 +82,7 @@ static void test_variant(const char *data, Test test) {
_cleanup_free_ char *s = NULL; _cleanup_free_ char *s = NULL;
int r; int r;
r = json_parse(data, &v, NULL, NULL); r = json_parse(data, 0, &v, NULL, NULL);
assert_se(r == 0); assert_se(r == 0);
assert_se(v); assert_se(v);
@ -93,7 +93,7 @@ static void test_variant(const char *data, Test test) {
log_info("formatted normally: %s\n", s); log_info("formatted normally: %s\n", s);
r = json_parse(data, &w, NULL, NULL); r = json_parse(data, JSON_PARSE_SENSITIVE, &w, NULL, NULL);
assert_se(r == 0); assert_se(r == 0);
assert_se(w); assert_se(w);
assert_se(json_variant_has_type(v, json_variant_type(w))); assert_se(json_variant_has_type(v, json_variant_type(w)));
@ -110,7 +110,7 @@ static void test_variant(const char *data, Test test) {
log_info("formatted prettily:\n%s", s); log_info("formatted prettily:\n%s", s);
r = json_parse(data, &w, NULL, NULL); r = json_parse(data, 0, &w, NULL, NULL);
assert_se(r == 0); assert_se(r == 0);
assert_se(w); assert_se(w);
@ -302,7 +302,7 @@ static void test_build(void) {
assert_se(json_variant_format(a, 0, &s) >= 0); assert_se(json_variant_format(a, 0, &s) >= 0);
log_info("GOT: %s\n", s); log_info("GOT: %s\n", s);
assert_se(json_parse(s, &b, NULL, NULL) >= 0); assert_se(json_parse(s, 0, &b, NULL, NULL) >= 0);
assert_se(json_variant_equal(a, b)); assert_se(json_variant_equal(a, b));
a = json_variant_unref(a); a = json_variant_unref(a);
@ -313,7 +313,7 @@ static void test_build(void) {
s = mfree(s); s = mfree(s);
assert_se(json_variant_format(a, 0, &s) >= 0); assert_se(json_variant_format(a, 0, &s) >= 0);
log_info("GOT: %s\n", s); log_info("GOT: %s\n", s);
assert_se(json_parse(s, &b, NULL, NULL) >= 0); assert_se(json_parse(s, 0, &b, NULL, NULL) >= 0);
assert_se(json_variant_format(b, 0, &t) >= 0); assert_se(json_variant_format(b, 0, &t) >= 0);
log_info("GOT: %s\n", t); log_info("GOT: %s\n", t);
@ -365,7 +365,7 @@ static void test_source(void) {
assert_se(f = fmemopen_unlocked((void*) data, strlen(data), "r")); assert_se(f = fmemopen_unlocked((void*) data, strlen(data), "r"));
assert_se(json_parse_file(f, "waldo", &v, NULL, NULL) >= 0); assert_se(json_parse_file(f, "waldo", 0, &v, NULL, NULL) >= 0);
printf("--- non-pretty begin ---\n"); printf("--- non-pretty begin ---\n");
json_variant_dump(v, 0, stdout, NULL); json_variant_dump(v, 0, stdout, NULL);
@ -415,6 +415,93 @@ static void test_depth(void) {
fputs("\n", stdout); fputs("\n", stdout);
} }
static void test_normalize(void) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL;
_cleanup_free_ char *t = NULL;
assert_se(json_build(&v, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("b", JSON_BUILD_STRING("x")),
JSON_BUILD_PAIR("c", JSON_BUILD_STRING("y")),
JSON_BUILD_PAIR("a", JSON_BUILD_STRING("z")))) >= 0);
assert_se(!json_variant_is_sorted(v));
assert_se(!json_variant_is_normalized(v));
assert_se(json_variant_format(v, 0, &t) >= 0);
assert_se(streq(t, "{\"b\":\"x\",\"c\":\"y\",\"a\":\"z\"}"));
t = mfree(t);
assert_se(json_build(&w, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("bar", JSON_BUILD_STRING("zzz")),
JSON_BUILD_PAIR("foo", JSON_BUILD_VARIANT(v)))) >= 0);
assert_se(json_variant_is_sorted(w));
assert_se(!json_variant_is_normalized(w));
assert_se(json_variant_format(w, 0, &t) >= 0);
assert_se(streq(t, "{\"bar\":\"zzz\",\"foo\":{\"b\":\"x\",\"c\":\"y\",\"a\":\"z\"}}"));
t = mfree(t);
assert_se(json_variant_sort(&v) >= 0);
assert_se(json_variant_is_sorted(v));
assert_se(json_variant_is_normalized(v));
assert_se(json_variant_format(v, 0, &t) >= 0);
assert_se(streq(t, "{\"a\":\"z\",\"b\":\"x\",\"c\":\"y\"}"));
t = mfree(t);
assert_se(json_variant_normalize(&w) >= 0);
assert_se(json_variant_is_sorted(w));
assert_se(json_variant_is_normalized(w));
assert_se(json_variant_format(w, 0, &t) >= 0);
assert_se(streq(t, "{\"bar\":\"zzz\",\"foo\":{\"a\":\"z\",\"b\":\"x\",\"c\":\"y\"}}"));
t = mfree(t);
}
static void test_bisect(void) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
char c;
/* Tests the bisection logic in json_variant_by_key() */
for (c = 'z'; c >= 'a'; c--) {
if ((c % 3) == 0)
continue;
_cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
assert_se(json_variant_new_stringn(&w, (char[4]) { '<', c, c, '>' }, 4) >= 0);
assert_se(json_variant_set_field(&v, (char[2]) { c, 0 }, w) >= 0);
}
json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
assert_se(!json_variant_is_sorted(v));
assert_se(!json_variant_is_normalized(v));
assert_se(json_variant_normalize(&v) >= 0);
assert_se(json_variant_is_sorted(v));
assert_se(json_variant_is_normalized(v));
json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
for (c = 'a'; c <= 'z'; c++) {
JsonVariant *k;
const char *z;
k = json_variant_by_key(v, (char[2]) { c, 0 });
assert_se(!k == ((c % 3) == 0));
if (!k)
continue;
assert_se(json_variant_is_string(k));
z = (char[5]){ '<', c, c, '>', 0};
assert_se(streq(json_variant_string(k), z));
}
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG); test_setup_logging(LOG_DEBUG);
@ -462,10 +549,11 @@ int main(int argc, char *argv[]) {
test_variant("[ 0, -0, 0.0, -0.0, 0.000, -0.000, 0e0, -0e0, 0e+0, -0e-0, 0e-0, -0e000, 0e+000 ]", test_zeroes); test_variant("[ 0, -0, 0.0, -0.0, 0.000, -0.000, 0e0, -0e0, 0e+0, -0e-0, 0e-0, -0e000, 0e+000 ]", test_zeroes);
test_build(); test_build();
test_source(); test_source();
test_depth(); test_depth();
test_normalize();
test_bisect();
return 0; return 0;
} }

View File

@ -36,6 +36,7 @@ Link.MACAddress, config_parse_hwaddr, 0,
Link.NamePolicy, config_parse_name_policy, 0, offsetof(link_config, name_policy) Link.NamePolicy, config_parse_name_policy, 0, offsetof(link_config, name_policy)
Link.Name, config_parse_ifname, 0, offsetof(link_config, name) Link.Name, config_parse_ifname, 0, offsetof(link_config, name)
Link.AlternativeName, config_parse_ifnames, 1, offsetof(link_config, alternative_names) Link.AlternativeName, config_parse_ifnames, 1, offsetof(link_config, alternative_names)
Link.AlternativeNamesPolicy, config_parse_alternative_names_policy, 0, offsetof(link_config, alternative_names_policy)
Link.Alias, config_parse_ifalias, 0, offsetof(link_config, alias) Link.Alias, config_parse_ifalias, 0, offsetof(link_config, alias)
Link.MTUBytes, config_parse_mtu, AF_UNSPEC, offsetof(link_config, mtu) Link.MTUBytes, config_parse_mtu, AF_UNSPEC, offsetof(link_config, mtu)
Link.BitsPerSecond, config_parse_si_size, 0, offsetof(link_config, speed) Link.BitsPerSecond, config_parse_si_size, 0, offsetof(link_config, speed)

View File

@ -59,6 +59,7 @@ static void link_config_free(link_config *link) {
free(link->name_policy); free(link->name_policy);
free(link->name); free(link->name);
strv_free(link->alternative_names); strv_free(link->alternative_names);
free(link->alternative_names_policy);
free(link->alias); free(link->alias);
free(link); free(link);
@ -326,6 +327,7 @@ static int get_mac(sd_device *device, MACAddressPolicy policy, struct ether_addr
int link_config_apply(link_config_ctx *ctx, link_config *config, int link_config_apply(link_config_ctx *ctx, link_config *config,
sd_device *device, const char **name) { sd_device *device, const char **name) {
_cleanup_strv_free_ char **altnames = NULL;
struct ether_addr generated_mac; struct ether_addr generated_mac;
struct ether_addr *mac = NULL; struct ether_addr *mac = NULL;
const char *new_name = NULL; const char *new_name = NULL;
@ -401,7 +403,7 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
} }
if (ctx->enable_name_policy && config->name_policy) if (ctx->enable_name_policy && config->name_policy)
for (NamePolicy *p = config->name_policy; !new_name && *p != _NAMEPOLICY_INVALID; p++) { for (NamePolicy *p = config->name_policy; *p != _NAMEPOLICY_INVALID; p++) {
policy = *p; policy = *p;
switch (policy) { switch (policy) {
@ -438,6 +440,8 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
default: default:
assert_not_reached("invalid policy"); assert_not_reached("invalid policy");
} }
if (ifname_valid(new_name))
break;
} }
if (new_name) if (new_name)
@ -459,9 +463,52 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
if (r < 0) if (r < 0)
return log_warning_errno(r, "Could not set Alias=, MACAddress= or MTU= on %s: %m", old_name); return log_warning_errno(r, "Could not set Alias=, MACAddress= or MTU= on %s: %m", old_name);
r = rtnl_set_link_alternative_names(&ctx->rtnl, ifindex, config->alternative_names); if (config->alternative_names) {
altnames = strv_copy(config->alternative_names);
if (!altnames)
return log_oom();
}
if (config->alternative_names_policy)
for (NamePolicy *p = config->alternative_names_policy; *p != _NAMEPOLICY_INVALID; p++) {
const char *n;
switch (*p) {
case NAMEPOLICY_DATABASE:
(void) sd_device_get_property_value(device, "ID_NET_NAME_FROM_DATABASE", &n);
break;
case NAMEPOLICY_ONBOARD:
(void) sd_device_get_property_value(device, "ID_NET_NAME_ONBOARD", &n);
break;
case NAMEPOLICY_SLOT:
(void) sd_device_get_property_value(device, "ID_NET_NAME_SLOT", &n);
break;
case NAMEPOLICY_PATH:
(void) sd_device_get_property_value(device, "ID_NET_NAME_PATH", &n);
break;
case NAMEPOLICY_MAC:
(void) sd_device_get_property_value(device, "ID_NET_NAME_MAC", &n);
break;
default:
assert_not_reached("invalid policy");
}
if (!isempty(n)) {
r = strv_extend(&altnames, n);
if (r < 0) if (r < 0)
return log_warning_errno(r, "Could not set AlternativeName= on %s: %m", old_name); return log_oom();
}
}
if (new_name)
strv_remove(altnames, new_name);
strv_remove(altnames, old_name);
strv_uniq(altnames);
r = rtnl_set_link_alternative_names(&ctx->rtnl, ifindex, altnames);
if (r == -EOPNOTSUPP)
log_debug_errno(r, "Could not set AlternativeName= or apply AlternativeNamesPolicy= on %s, ignoring: %m", old_name);
else if (r < 0)
return log_warning_errno(r, "Could not set AlternativeName= or apply AlternativeNamesPolicy= on %s: %m", old_name);
*name = new_name; *name = new_name;
@ -509,3 +556,16 @@ DEFINE_STRING_TABLE_LOOKUP(name_policy, NamePolicy);
DEFINE_CONFIG_PARSE_ENUMV(config_parse_name_policy, name_policy, NamePolicy, DEFINE_CONFIG_PARSE_ENUMV(config_parse_name_policy, name_policy, NamePolicy,
_NAMEPOLICY_INVALID, _NAMEPOLICY_INVALID,
"Failed to parse interface name policy"); "Failed to parse interface name policy");
static const char* const alternative_names_policy_table[_NAMEPOLICY_MAX] = {
[NAMEPOLICY_DATABASE] = "database",
[NAMEPOLICY_ONBOARD] = "onboard",
[NAMEPOLICY_SLOT] = "slot",
[NAMEPOLICY_PATH] = "path",
[NAMEPOLICY_MAC] = "mac",
};
DEFINE_STRING_TABLE_LOOKUP(alternative_names_policy, NamePolicy);
DEFINE_CONFIG_PARSE_ENUMV(config_parse_alternative_names_policy, alternative_names_policy, NamePolicy,
_NAMEPOLICY_INVALID,
"Failed to parse alternative names policy");

View File

@ -47,6 +47,7 @@ struct link_config {
struct ether_addr *mac; struct ether_addr *mac;
MACAddressPolicy mac_address_policy; MACAddressPolicy mac_address_policy;
NamePolicy *name_policy; NamePolicy *name_policy;
NamePolicy *alternative_names_policy;
char *name; char *name;
char **alternative_names; char **alternative_names;
char *alias; char *alias;
@ -79,6 +80,9 @@ int link_get_driver(link_config_ctx *ctx, sd_device *device, char **ret);
const char *name_policy_to_string(NamePolicy p) _const_; const char *name_policy_to_string(NamePolicy p) _const_;
NamePolicy name_policy_from_string(const char *p) _pure_; NamePolicy name_policy_from_string(const char *p) _pure_;
const char *alternative_names_policy_to_string(NamePolicy p) _const_;
NamePolicy alternative_names_policy_from_string(const char *p) _pure_;
const char *mac_address_policy_to_string(MACAddressPolicy p) _const_; const char *mac_address_policy_to_string(MACAddressPolicy p) _const_;
MACAddressPolicy mac_address_policy_from_string(const char *p) _pure_; MACAddressPolicy mac_address_policy_from_string(const char *p) _pure_;
@ -87,3 +91,4 @@ const struct ConfigPerfItem* link_config_gperf_lookup(const char *key, GPERF_LEN
CONFIG_PARSER_PROTOTYPE(config_parse_mac_address_policy); CONFIG_PARSER_PROTOTYPE(config_parse_mac_address_policy);
CONFIG_PARSER_PROTOTYPE(config_parse_name_policy); CONFIG_PARSER_PROTOTYPE(config_parse_name_policy);
CONFIG_PARSER_PROTOTYPE(config_parse_alternative_names_policy);

View File

@ -19,6 +19,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <linux/if.h>
#include <linux/pci_regs.h> #include <linux/pci_regs.h>
#include "alloc-util.h" #include "alloc-util.h"
@ -56,22 +57,22 @@ struct netnames {
bool mac_valid; bool mac_valid;
sd_device *pcidev; sd_device *pcidev;
char pci_slot[IFNAMSIZ]; char pci_slot[ALTIFNAMSIZ];
char pci_path[IFNAMSIZ]; char pci_path[ALTIFNAMSIZ];
char pci_onboard[IFNAMSIZ]; char pci_onboard[ALTIFNAMSIZ];
const char *pci_onboard_label; const char *pci_onboard_label;
char usb_ports[IFNAMSIZ]; char usb_ports[ALTIFNAMSIZ];
char bcma_core[IFNAMSIZ]; char bcma_core[ALTIFNAMSIZ];
char ccw_busid[IFNAMSIZ]; char ccw_busid[ALTIFNAMSIZ];
char vio_slot[IFNAMSIZ]; char vio_slot[ALTIFNAMSIZ];
char platform_path[IFNAMSIZ]; char platform_path[ALTIFNAMSIZ];
char netdevsim_path[IFNAMSIZ]; char netdevsim_path[ALTIFNAMSIZ];
}; };
struct virtfn_info { struct virtfn_info {
sd_device *physfn_pcidev; sd_device *physfn_pcidev;
char suffix[IFNAMSIZ]; char suffix[ALTIFNAMSIZ];
}; };
/* skip intermediate virtio devices */ /* skip intermediate virtio devices */
@ -104,7 +105,7 @@ static int get_virtfn_info(sd_device *dev, struct netnames *names, struct virtfn
_cleanup_free_ char *virtfn_pci_syspath = NULL; _cleanup_free_ char *virtfn_pci_syspath = NULL;
struct dirent *dent; struct dirent *dent;
_cleanup_closedir_ DIR *dir = NULL; _cleanup_closedir_ DIR *dir = NULL;
char suffix[IFNAMSIZ]; char suffix[ALTIFNAMSIZ];
int r; int r;
assert(dev); assert(dev);
@ -819,7 +820,7 @@ static int builtin_net_id(sd_device *dev, int argc, char *argv[], bool test) {
r = names_mac(dev, &names); r = names_mac(dev, &names);
if (r >= 0 && names.mac_valid) { if (r >= 0 && names.mac_valid) {
char str[IFNAMSIZ]; char str[ALTIFNAMSIZ];
xsprintf(str, "%sx%02x%02x%02x%02x%02x%02x", prefix, xsprintf(str, "%sx%02x%02x%02x%02x%02x%02x", prefix,
names.mac[0], names.mac[1], names.mac[2], names.mac[0], names.mac[1], names.mac[2],
@ -831,7 +832,7 @@ static int builtin_net_id(sd_device *dev, int argc, char *argv[], bool test) {
/* get path names for Linux on System z network devices */ /* get path names for Linux on System z network devices */
if (names_ccw(dev, &names) >= 0 && names.type == NET_CCW) { if (names_ccw(dev, &names) >= 0 && names.type == NET_CCW) {
char str[IFNAMSIZ]; char str[ALTIFNAMSIZ];
if (snprintf_ok(str, sizeof str, "%s%s", prefix, names.ccw_busid)) if (snprintf_ok(str, sizeof str, "%s%s", prefix, names.ccw_busid))
udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str); udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
@ -840,7 +841,7 @@ static int builtin_net_id(sd_device *dev, int argc, char *argv[], bool test) {
/* get ibmveth/ibmvnic slot-based names. */ /* get ibmveth/ibmvnic slot-based names. */
if (names_vio(dev, &names) >= 0 && names.type == NET_VIO) { if (names_vio(dev, &names) >= 0 && names.type == NET_VIO) {
char str[IFNAMSIZ]; char str[ALTIFNAMSIZ];
if (snprintf_ok(str, sizeof str, "%s%s", prefix, names.vio_slot)) if (snprintf_ok(str, sizeof str, "%s%s", prefix, names.vio_slot))
udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str); udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
@ -849,7 +850,7 @@ static int builtin_net_id(sd_device *dev, int argc, char *argv[], bool test) {
/* get ACPI path names for ARM64 platform devices */ /* get ACPI path names for ARM64 platform devices */
if (names_platform(dev, &names, test) >= 0 && names.type == NET_PLATFORM) { if (names_platform(dev, &names, test) >= 0 && names.type == NET_PLATFORM) {
char str[IFNAMSIZ]; char str[ALTIFNAMSIZ];
if (snprintf_ok(str, sizeof str, "%s%s", prefix, names.platform_path)) if (snprintf_ok(str, sizeof str, "%s%s", prefix, names.platform_path))
udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str); udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
@ -858,7 +859,7 @@ static int builtin_net_id(sd_device *dev, int argc, char *argv[], bool test) {
/* get netdevsim path names */ /* get netdevsim path names */
if (names_netdevsim(dev, &names) >= 0 && names.type == NET_NETDEVSIM) { if (names_netdevsim(dev, &names) >= 0 && names.type == NET_NETDEVSIM) {
char str[IFNAMSIZ]; char str[ALTIFNAMSIZ];
if (snprintf_ok(str, sizeof str, "%s%s", prefix, names.netdevsim_path)) if (snprintf_ok(str, sizeof str, "%s%s", prefix, names.netdevsim_path))
udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str); udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
@ -872,7 +873,7 @@ static int builtin_net_id(sd_device *dev, int argc, char *argv[], bool test) {
/* plain PCI device */ /* plain PCI device */
if (names.type == NET_PCI) { if (names.type == NET_PCI) {
char str[IFNAMSIZ]; char str[ALTIFNAMSIZ];
if (names.pci_onboard[0] && if (names.pci_onboard[0] &&
snprintf_ok(str, sizeof str, "%s%s", prefix, names.pci_onboard)) snprintf_ok(str, sizeof str, "%s%s", prefix, names.pci_onboard))
@ -896,7 +897,7 @@ static int builtin_net_id(sd_device *dev, int argc, char *argv[], bool test) {
/* USB device */ /* USB device */
if (names_usb(dev, &names) >= 0 && names.type == NET_USB) { if (names_usb(dev, &names) >= 0 && names.type == NET_USB) {
char str[IFNAMSIZ]; char str[ALTIFNAMSIZ];
if (names.pci_path[0] && if (names.pci_path[0] &&
snprintf_ok(str, sizeof str, "%s%s%s", prefix, names.pci_path, names.usb_ports)) snprintf_ok(str, sizeof str, "%s%s%s", prefix, names.pci_path, names.usb_ports))
@ -910,7 +911,7 @@ static int builtin_net_id(sd_device *dev, int argc, char *argv[], bool test) {
/* Broadcom bus */ /* Broadcom bus */
if (names_bcma(dev, &names) >= 0 && names.type == NET_BCMA) { if (names_bcma(dev, &names) >= 0 && names.type == NET_BCMA) {
char str[IFNAMSIZ]; char str[ALTIFNAMSIZ];
if (names.pci_path[0] && if (names.pci_path[0] &&
snprintf_ok(str, sizeof str, "%s%s%s", prefix, names.pci_path, names.bcma_core)) snprintf_ok(str, sizeof str, "%s%s%s", prefix, names.pci_path, names.bcma_core))

View File

@ -119,6 +119,18 @@ machinectl remove scratch4
! test -f /var/lib/machines/scratch4 ! test -f /var/lib/machines/scratch4
! machinectl image-status scratch4 ! machinectl image-status scratch4
# Test import-tar hypen/stdin pipe behavior
cat /var/tmp/scratch.tar.gz | machinectl import-tar - scratch5
test -d /var/lib/machines/scratch5
machinectl image-status scratch5
diff -r /var/tmp/scratch/ /var/lib/machines/scratch5
# Test export-tar hypen/stdout pipe behavior
mkdir -p /var/tmp/extract
machinectl export-tar scratch5 - | tar xvf - -C /var/tmp/extract/
diff -r /var/tmp/scratch/ /var/tmp/extract/
rm -rf /var/tmp/extract
rm -rf /var/tmp/scratch rm -rf /var/tmp/scratch
echo OK > /testok echo OK > /testok

View File

@ -16,6 +16,7 @@ MACAddressPolicy=
MACAddress= MACAddress=
NamePolicy= NamePolicy=
Name= Name=
AlternativeNamesPolicy=
AlternativeName= AlternativeName=
Alias= Alias=
MTUBytes= MTUBytes=

View File

@ -287,3 +287,11 @@ FairQueuingControlledDelayIntervalSec=
FairQueuingControlledDelayCEThresholdSec= FairQueuingControlledDelayCEThresholdSec=
FairQueuingControlledDelayECN= FairQueuingControlledDelayECN=
FairQueueTrafficPolicingPacketLimit= FairQueueTrafficPolicingPacketLimit=
FairQueueTrafficPolicingFlowLimit=
FairQueueTrafficPolicingQuantum=
FairQueueTrafficPolicingInitialQuantum=
FairQueueTrafficPolicingMaximumRate=
FairQueueTrafficPolicingBuckets=
FairQueueTrafficPolicingOrphanMask=
FairQueueTrafficPolicingPacing=
FairQueueTrafficPolicingCEThresholdSec=

View File

@ -8,3 +8,11 @@ Address=10.1.2.3/16
[TrafficControlQueueingDiscipline] [TrafficControlQueueingDiscipline]
Parent=root Parent=root
FairQueueTrafficPolicingPacketLimit=1000 FairQueueTrafficPolicingPacketLimit=1000
FairQueueTrafficPolicingFlowLimit=200
FairQueueTrafficPolicingQuantum=1500
FairQueueTrafficPolicingInitialQuantum=13000
FairQueueTrafficPolicingMaximumRate=1M
FairQueueTrafficPolicingBuckets=512
FairQueueTrafficPolicingOrphanMask=511
FairQueueTrafficPolicingPacing=yes
FairQueueTrafficPolicingCEThresholdSec=100ms

View File

@ -2165,7 +2165,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
output = check_output('tc qdisc show dev dummy98') output = check_output('tc qdisc show dev dummy98')
print(output) print(output)
self.assertRegex(output, 'qdisc fq') self.assertRegex(output, 'qdisc fq')
self.assertRegex(output, 'limit 1000p') self.assertRegex(output, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511 quantum 1500 initial_quantum 13000 maxrate 1Mbit')
class NetworkdStateFileTests(unittest.TestCase, Utilities): class NetworkdStateFileTests(unittest.TestCase, Utilities):
links = [ links = [