]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #16344 from keszybz/update-utmp-erofs
authorLennart Poettering <lennart@poettering.net>
Mon, 6 Jul 2020 15:52:17 +0000 (17:52 +0200)
committerGitHub <noreply@github.com>
Mon, 6 Jul 2020 15:52:17 +0000 (17:52 +0200)
Make update-utmp not fail if it only fails to write wtmp records

196 files changed:
NEWS
TODO
catalog/systemd.pl.catalog.in
docs/BOOT_LOADER_SPECIFICATION.md
docs/HOME_DIRECTORY.md
docs/INITRD_INTERFACE.md
docs/RANDOM_SEEDS.md
docs/USER_RECORD.md
hwdb.d/60-keyboard.hwdb
hwdb.d/60-sensor.hwdb
hwdb.d/70-mouse.hwdb
man/coredump.conf.xml
man/coredumpctl.xml
man/crypttab.xml
man/daemon.xml
man/file-hierarchy.xml
man/homectl.xml
man/homed.conf.xml
man/journal-remote.conf.xml
man/journal-upload.conf.xml
man/journalctl.xml
man/journald.conf.xml
man/kernel-install.xml
man/logind.conf.xml
man/machine-id.xml
man/machinectl.xml
man/networkd.conf.xml
man/nss-myhostname.xml
man/nss-mymachines.xml
man/nss-resolve.xml
man/nss-systemd.xml
man/org.freedesktop.LogControl1.xml
man/pam_systemd.xml
man/portablectl.xml
man/pstore.conf.xml
man/resolved.conf.xml
man/sd_bus_enqueue_for_read.xml
man/sd_bus_is_open.xml
man/sd_bus_message_new_method_error.xml
man/sd_bus_set_connected_signal.xml
man/sd_bus_slot_set_destroy_callback.xml
man/sd_bus_slot_set_floating.xml
man/sd_event_source_set_destroy_callback.xml
man/sd_hwdb_get.xml
man/sd_journal_has_runtime_files.xml
man/sd_machine_get_class.xml
man/sd_pid_get_owner_uid.xml
man/standard-conf.xml
man/sysctl.d.xml
man/systemctl.xml
man/systemd-analyze.xml
man/systemd-bless-boot-generator.xml
man/systemd-boot.xml
man/systemd-gpt-auto-generator.xml
man/systemd-journal-gatewayd.service.xml
man/systemd-journal-upload.service.xml
man/systemd-logind.service.xml
man/systemd-mount.xml
man/systemd-nspawn.xml
man/systemd-random-seed.service.xml
man/systemd-repart.xml
man/systemd-sleep.conf.xml
man/systemd-socket-proxyd.xml
man/systemd-suspend.service.xml
man/systemd-system.conf.xml
man/systemd-time-wait-sync.service.xml
man/systemd.automount.xml
man/systemd.device.xml
man/systemd.dnssd.xml
man/systemd.exec.xml
man/systemd.journal-fields.xml
man/systemd.kill.xml
man/systemd.link.xml
man/systemd.mount.xml
man/systemd.net-naming-scheme.xml
man/systemd.netdev.xml
man/systemd.network.xml
man/systemd.nspawn.xml
man/systemd.offline-updates.xml
man/systemd.path.xml
man/systemd.scope.xml
man/systemd.service.xml
man/systemd.slice.xml
man/systemd.socket.xml
man/systemd.special.xml
man/systemd.swap.xml
man/systemd.syntax.xml
man/systemd.target.xml
man/systemd.time.xml
man/systemd.timer.xml
man/systemd.unit.xml
man/systemd.xml
man/tc.xml
man/timesyncd.conf.xml
man/userdbctl.xml
meson.build
meson_options.txt
po/cs.po
src/basic/locale-util.c
src/basic/locale-util.h
src/basic/macro.h
src/basic/socket-util.c
src/basic/unit-name.c
src/basic/user-util.c
src/core/automount.c
src/core/bpf-firewall.c
src/core/execute.c
src/core/load-fragment.c
src/core/main.c
src/core/namespace.c
src/home/homectl-fido2.c [new file with mode: 0644]
src/home/homectl-fido2.h [new file with mode: 0644]
src/home/homectl-pkcs11.c [new file with mode: 0644]
src/home/homectl-pkcs11.h [new file with mode: 0644]
src/home/homectl.c
src/home/homed-home.c
src/home/homework-cifs.c
src/home/homework-cifs.h
src/home/homework-directory.c
src/home/homework-directory.h
src/home/homework-fido2.c [new file with mode: 0644]
src/home/homework-fido2.h [new file with mode: 0644]
src/home/homework-fscrypt.c
src/home/homework-fscrypt.h
src/home/homework-luks.c
src/home/homework-luks.h
src/home/homework-pkcs11.c
src/home/homework.c
src/home/homework.h
src/home/meson.build
src/home/pam_systemd_home.c
src/home/user-record-util.c
src/home/user-record-util.h
src/journal/journal-file.c
src/libsystemd-network/dhcp6-internal.h
src/libsystemd-network/dhcp6-network.c
src/libsystemd-network/icmp6-util.c
src/libsystemd-network/icmp6-util.h
src/libsystemd-network/sd-dhcp6-client.c
src/libsystemd-network/sd-radv.c
src/libsystemd-network/test-dhcp-client.c
src/libsystemd-network/test-dhcp6-client.c
src/libsystemd-network/test-ipv4ll.c
src/libsystemd-network/test-ndisc-ra.c
src/libsystemd-network/test-ndisc-rs.c
src/libsystemd/sd-bus/bus-common-errors.c
src/libsystemd/sd-bus/bus-common-errors.h
src/libsystemd/sd-bus/bus-internal.h
src/libsystemd/sd-device/sd-device.c
src/libsystemd/sd-netlink/netlink-types.c
src/login/pam_systemd.c
src/network/meson.build
src/network/networkd-dhcp6.c
src/network/networkd-dhcp6.h
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/network/networkd-sriov.c [new file with mode: 0644]
src/network/networkd-sriov.h [new file with mode: 0644]
src/network/tc/cake.c
src/network/tc/drr.c
src/network/tc/ets.c
src/network/tc/fifo.c
src/network/tc/fq-codel.c
src/network/tc/fq.c
src/network/tc/qfq.c
src/network/tc/tbf.c
src/network/tc/tbf.h
src/partition/repart.c
src/resolve/resolved-dns-packet.c
src/resolve/resolved-dns-rr.c
src/resolve/resolved-dns-rr.h
src/resolve/resolved-dns-stream.c
src/shared/pkcs11-util.c
src/shared/pkcs11-util.h
src/shared/user-record-show.c
src/shared/user-record.c
src/shared/user-record.h
src/shared/varlink.c
src/test/test-locale-util.c
src/test/test-unit-name.c
src/test/test-util.c
src/udev/udevadm-info.c
src/userdb/userdbctl.c
test/fuzz/fuzz-network-parser/directives.network
test/fuzz/fuzz-network-parser/oss-fuzz-23895 [new file with mode: 0644]
test/test-network/conf/25-qdisc-cake.network
test/test-network/conf/25-qdisc-clsact-and-htb.network
test/test-network/conf/25-qdisc-drr.network
test/test-network/conf/25-qdisc-qfq.network
test/test-network/conf/25-sriov.network [new file with mode: 0644]
test/test-network/systemd-networkd-tests.py
test/units/test-honor-first-shutdown.service
travis-ci/managers/fedora.sh

diff --git a/NEWS b/NEWS
index 5d79200cf1109b3a04fc58e70ee0151fcea88d52..19b5240cefe2701a25d86d78bd8f6f9d2e055a76 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -147,6 +147,9 @@ CHANGES WITH 246 in spe:
         * networkd.conf gained a new boolean setting ManageForeignRoutes=. If
           enabled systemd-networkd manages all routes configured by other tools.
 
+        * .network files managed by systemd-networkd gained a new section
+          [SR-IOV], in order to configure SR-IOV capable network devices.
+
         * systemd-networkd's [IPv6Prefix] section in .network files gained a
           new boolean setting Assign=. If enabled an address from the prefix is
           automatically assigned to the interface.
@@ -178,10 +181,10 @@ CHANGES WITH 246 in spe:
           traffic). DataBitRate=, DataSamplePoint=, FDMode=, FDNonISO= have
           been added to configure various CAN-FD aspects.
 
-        * systemd-networkd's [DHCPv6] section gained a new WithoutRA= boolean
-          setting. If enabled, DHCPv6 will be attempted right-away without
-          requiring an Router Advertisement packet suggesting it
-          first. Conversely, the [IPv6AcceptRA] gained a boolean option
+        * systemd-networkd's [DHCPv6] section gained a new option WithoutRA=.
+          When enabled, DHCPv6 will be attempted right-away without requiring an
+          Router Advertisement packet suggesting it first (i.e. without the 'M'
+          or 'O' flags set). The [IPv6AcceptRA] section gained a boolean option
           DHCPv6Client= that may be used to turn off the DHCPv6 client even if
           the RA packets suggest it.
 
diff --git a/TODO b/TODO
index 6751bd96aa70d863bbb77b54f8039737e54d88d0..2ef803509977ae03f2373c6cd061c80ef9e0bb10 100644 (file)
--- a/TODO
+++ b/TODO
@@ -150,8 +150,8 @@ Features:
   thus allows defining OS images which can be A/B updated and we default to the
   newest version automatically, both in nspawn and in sd-boot
 
-* cryptsetup/homed: also support FIDO2 HMAC password logic for unlocking
-  devices. (see: https://github.com/mjec/fido2-hmac-secret)
+* cryptsetup: support FIDO2 tokens for deriving keys (i.e. do what homed can do
+  also in plain cryptsetup)
 
 * systemd-gpt-auto should probably set x-systemd.growfs on the mounts it
   creates
index 5f3d41632266fc4058119966be271db5407d7873..961d09a2f545a32110f7d661765081074a0e9197 100644 (file)
@@ -298,7 +298,8 @@ Maszyna wirtualna @NAME@ (PID prowadzący @LEADER@) została wyłączona.
 Subject: Wyłączono tryb DNSSEC, ponieważ serwer go nie obsługuje
 Defined-By: systemd
 Support: %SUPPORT_URL%
-Documentation: man:systemd-resolved.service(8) resolved.conf(5)
+Documentation: man:systemd-resolved.service(8)
+Documentation: man:resolved.conf(5)
 
 Usługa resolver (systemd-resolved.service) wykryła, że skonfigurowany serwer
 DNS nie obsługuje DNSSEC, w wyniku czego walidacja DNSSEC została wyłączona.
@@ -416,6 +417,7 @@ przez jednostkę @UNIT@.
 Subject: Przyjmowanie nazwy użytkownika/grupy @USER_GROUP_NAME@, która nie zgadza się ze ścisłymi regułami nazw użytkowników/grup.
 Defined-By: systemd
 Support: %SUPPORT_URL%
+Documentation: https://systemd.io/USER_NAMES
 
 Podano nazwę użytkownika/grupy @USER_GROUP_NAME@, co zostało przyjęte
 zgodnie z rozluźnionymi regułami nazw użytkowników/grup, ale nie spełnia
@@ -432,5 +434,57 @@ nazw z początkową lub końcową spacją, ciągów „.” lub „..”, ciąg
 zawierających tylko cyfry lub ciągów zaczynających się myślnikiem
 i zawierających oprócz niego tylko cyfry.
 
-https://systemd.io/USER_NAMES zawiera więcej informacji o ścisłych
-i rozluźnionych regułach nazw użytkowników/grup.
+-- 1b3bb94037f04bbf81028e135a12d293
+Subject: Utworzenie prawidłowej nazwy jednostki ze ścieżki „@MOUNT_POINT@” się nie powiodło.
+Defined-By: systemd
+Support: %SUPPORT_URL%
+
+Następująca ścieżka do punktu montowania nie może zostać przekonwertowana
+na prawidłową nazwę jednostki .mount:
+
+    @MOUNT_POINT@
+
+Zwykle oznacza to, że ścieżka do punktu montowania jest dłuższa niż dozwolona
+dla prawidłowych nazw jednostek.
+
+systemd dynamicznie syntetyzuje jednostki .mount dla wszystkich punktów
+montowania pojawiających się na komputerze. Stosowany do tego jest prosty
+algorytm: używana jest bezwzględna nazwa ścieżki ze wszystkimi znakami „/”
+zastąpionymi znakami „-” (początkowy jest usuwany). Co więcej, wszystkie
+niealfanumeryczne znaki (a także „:”, „-”, „_”, „.”, „\”) są zastępowane
+„\xNN”, gdzie „NN” jest szesnastkowym kodem znaki. Na końcu dołączany jest
+przyrostek „.mount”. Wynikowy ciąg musi mieć poniżej 256 znaków długości,
+aby był prawidłową nazwą jednostki. To ograniczenie obowiązuje, aby wszystkie
+nazwy jednostek mogły być używane także jako nazwy plików. Jeśli punkt
+montowania — po zastosowaniu algorytmu — jest dłuższy niż to ograniczenie,
+to nie może zostać zmapowany do jednostki. W takim przypadku systemd nie
+zsyntetyzuje jednostki i nie może być używany do zarządzania punktem
+montowania. Nie pojawi się w tabeli jednostek menedżera usług, przez co
+nie zostanie także bezpiecznie i automatycznie zdjęty podczas wyłączania
+komputera.
+
+Zasadniczo zalecane jest unikanie takich za długich ścieżek do punktów
+montowania, a jeśli są używane mimo to, zarządzanie nimi niezależnie
+od systemd, tzn. stawianie ich i automatyczne zdejmowanie podczas
+wyłączania komputera przez inne oprogramowanie.
+
+-- b480325f9c394a7b802c231e51a2752c
+Subject: Skonfigurowany jest szczególny użytkownik @OFFENDING_USER@, jest to niebezpieczne!
+Defined-By: systemd
+Support: %SUPPORT_URL%
+Documentation: https://systemd.io/UIDS-GIDS
+
+Jednostka @UNIT@ jest skonfigurowana do używania User=@OFFENDING_USER@.
+
+Nie jest to bezpieczne. Głównym zastosowaniem użytkownika @OFFENDING_USER@
+w linuksowych systemach operacyjnych jest bycie właścicielem plików, których
+nie można w inny sposób zmapować do żadnego lokalnego użytkownika. Jest
+używany między innymi przez klienta NFS i przestrzenie nazw użytkowników
+Linuksa. Wykonywanie procesów jednostki pod tożsamością tego użytkownika
+może spowodować, że będą one miały dostęp do odczytu, a może nawet do zapisu,
+plików, których nie można zmapować w inny sposób.
+
+Mocno zalecane jest unikanie wykonywania usług pod tą tożsamością użytkownika,
+zwłaszcza na komputerach używających NFS lub mających kontenery. Należy
+przydzielić identyfikator użytkownika dla tej konkretnej usługi, statycznie
+przez systemd-sysusers lub dynamicznie przez ustawienie usługi DynamicUser=.
index 6bb72e0faaa9608ab54ee310ad0b62e7f5b18a7b..803ba5440faffc6a3a76120fa9222dcd5e161505 100644 (file)
@@ -47,7 +47,7 @@ functionality. Here's why we think that it is not enough for our uses:
 
 * The various EFI implementations implement the boot order/boot item logic to different levels. Some firmware implementations do not offer a boot menu at all and instead unconditionally follow the EFI boot order, booting the first item that is working.
 * If the firmware setup is used to reset all data usually all EFI boot entries are lost, making the system entirely unbootable, as the firmware setups generally do not offer a UI to define additional boot items. By placing the menu item information on disk, it is always available, regardless if the BIOS setup data is lost.
-* Harddisk images should be moveable between machines and be bootable without requiring explicit EFI variables to be set. This also requires that the list of boot options is defined on disk, and not in EFI variables alone.
+* Harddisk images should be movable between machines and be bootable without requiring explicit EFI variables to be set. This also requires that the list of boot options is defined on disk, and not in EFI variables alone.
 * EFI is not universal yet (especially on non-x86 platforms), this specification is useful both for EFI and non-EFI boot loaders.
 * Many EFI systems disable USB support during early boot to optimize boot times, thus making keyboard input unavailable in the EFI menu. It is thus useful if the OS UI has a standardized way to discover available boot options which can be booted to.
 
index d82aca70653209531e0098c064aa7d0365980df8..a3eabb7e635f87c88faa9e44cbb09340939cfa1b 100644 (file)
@@ -125,7 +125,7 @@ medium. (Moreover it allows to embed additional partitions later on, for
 example for allowing a multi-purpose USB stick that contains both a home
 directory and a generic storage volume.)
 
-Rationale for including the encrypted user record in the the LUKS2 header:
+Rationale for including the encrypted user record in the LUKS2 header:
 Linux kernel file system implementations are generally not robust towards
 maliciously formatted file systems; there's a good chance that file system
 images can be used as attack vectors, exploiting the kernel. Thus it is
index 8985f2761c8faa8c06aba80ea14527f424a3c74e..e59bbcce15c24283855f54ede139c71fe599846e 100644 (file)
@@ -36,7 +36,7 @@ interfaces are currently used by dracut and the ArchLinux initrds.
   optionally followed (in `argv[2]`, `argv[3]`, … systemd's original command
   line options, for example `--log-level=` and similar.
 
-* Storage daemons run from the initrd should follow the the guide on [systemd
+* Storage daemons run from the initrd should follow the guide on [systemd
   and Storage Daemons for the Root File
   System](https://systemd.io/ROOT_STORAGE_DAEMONS) to survive properly from the
   boot initrd all the way to the point where systemd jumps back into the initrd
index e4b4a7a9cb6e1ec5cd9b375b5dd1d01105cf11a7..da3fe40baad1418683e64cab1c245d087738e31b 100644 (file)
@@ -212,10 +212,10 @@ boot, in order to ensure the entropy pool is filled up quickly.
    random-seed`](https://www.freedesktop.org/software/systemd/man/bootctl.html#random-seed))
    a seed file with an initial seed is placed in a file `/loader/random-seed`
    in the ESP. In addition, an identically sized randomized EFI variable called
-   the the 'system token' is set, which is written to the machine's firmware
-   NVRAM. During boot, when `systemd-boot` finds both the random seed file and
-   the system token they are combined and hashed with SHA256 (in counter mode,
-   to generate sufficient data), to generate a new random seed file to store in
+   the 'system token' is set, which is written to the machine's firmware NVRAM.
+   During boot, when `systemd-boot` finds both the random seed file and the
+   system token they are combined and hashed with SHA256 (in counter mode, to
+   generate sufficient data), to generate a new random seed file to store in
    the ESP as well as a random seed to pass to the OS kernel. The new random
    seed file for the ESP is then written to the ESP, ensuring this is completed
    before the OS is invoked. Very early during initialization PID 1 will read
index 7b6fe47665ae1825d9ed95f0c020795142d28b0b..885a94858d371f538293637dc063a44c22244ae5 100644 (file)
@@ -546,6 +546,11 @@ below). It's undefined how precise the URI is: during log-in it is tested
 against all plugged in security tokens and if there's exactly one matching
 private key found with it it is used.
 
+`fido2HmacCredential` → An array of strings, each with a Base64-encoded FIDO2
+credential ID that shell be used for authentication with FIDO2 devices that
+implement the `hmac-secret` extension. The salt to pass to the FIDO2 device is
+found in `fido2HmacSalt`.
+
 `privileged` → An object, which contains the fields of the `privileged` section
 of the user record, see below.
 
@@ -594,7 +599,7 @@ as the lines in the traditional `~/.ssh/authorized_key` file.
 
 `pkcs11EncryptedKey` → An array of objects. Each element of the array should be
 an object consisting of three string fields: `uri` shall contain a PKCS#11
-security token URI, `data` shall contain a Base64 encoded encrypted key and
+security token URI, `data` shall contain a Base64-encoded encrypted key and
 `hashedPassword` shall contain a UNIX password hash to test the key
 against. Authenticating with a security token against this account shall work
 as follows: the encrypted secret key is converted from its Base64
@@ -602,13 +607,29 @@ representation into binary, then decrypted with the PKCS#11 `C_Decrypt()`
 function of the PKCS#11 module referenced by the specified URI, using the
 private key found on the same token. The resulting decrypted key is then
 Base64-encoded and tested against the specified UNIX hashed password. The
-Base64-enceded decrypted key may also be used to unlock further resources
+Base64-encoded decrypted key may also be used to unlock further resources
 during log-in, for example the LUKS or `fscrypt` storage backend. It is
 generally recommended that for each entry in `pkcs11EncryptedKey` there's also
 a matching one in `pkcs11TokenUri` and vice versa, with the same URI, appearing
 in the same order, but this should not be required by applications processing
 user records.
 
+`fido2HmacSalt` → An array of objects, implementing authentication support with
+FIDO2 devices that implement the `hmac-secret` extension. Each element of the
+array should be an object consisting of three string fields: `credential`,
+`salt`, `hashedPassword`. The first two shall contain Base64-encoded binary
+data: the FIDO2 credential ID and the salt value to pass to the FIDO2
+device. During authentication this salt along with the credential ID is sent to
+the FIDO2 token, which will HMAC hash the salt with its internal secret key and
+return the result. This resulting binary key should then be Base64-encoded and
+used as string password for the further layers of the stack. The
+`hashedPassword` field of the `fido2HmacSalt` field shall be a UNIX password
+hash to test this derived secret key against for authentication. It is
+generally recommended that for each entry in `fido2HmacSalt` there's also a
+matching one in `fido2HmacCredential`, and vice versa, with the same credential
+ID, appearing in the same order, but this should not be required by
+applications processing user records.
+
 ## Fields in the `perMachine` section
 
 As mentioned, the `perMachine` section contains settings that shall apply to
@@ -652,13 +673,13 @@ that may be used in this section are identical to the equally named ones in the
 `mountNoDevices`, `mountNoSuid`, `mountNoExecute`, `cifsDomain`,
 `cifsUserName`, `cifsService`, `imagePath`, `uid`, `gid`, `memberOf`,
 `fileSystemType`, `partitionUuid`, `luksUuid`, `fileSystemUuid`, `luksDiscard`,
-`luksOfflineDiscard`, `luksOfflineDiscard`, `luksCipher`, `luksCipherMode`,
-`luksVolumeKeySize`, `luksPbkdfHashAlgorithm`, `luksPbkdfType`,
-`luksPbkdfTimeCostUSec`, `luksPbkdfMemoryCost`, `luksPbkdfParallelThreads`,
-`rateLimitIntervalUSec`, `rateLimitBurst`, `enforcePasswordPolicy`,
-`autoLogin`, `stopDelayUSec`, `killProcesses`, `passwordChangeMinUSec`,
-`passwordChangeMaxUSec`, `passwordChangeWarnUSec`,
-`passwordChangeInactiveUSec`, `passwordChangeNow`, `pkcs11TokenUri`.
+`luksOfflineDiscard`, `luksCipher`, `luksCipherMode`, `luksVolumeKeySize`,
+`luksPbkdfHashAlgorithm`, `luksPbkdfType`, `luksPbkdfTimeCostUSec`,
+`luksPbkdfMemoryCost`, `luksPbkdfParallelThreads`, `rateLimitIntervalUSec`,
+`rateLimitBurst`, `enforcePasswordPolicy`, `autoLogin`, `stopDelayUSec`,
+`killProcesses`, `passwordChangeMinUSec`, `passwordChangeMaxUSec`,
+`passwordChangeWarnUSec`, `passwordChangeInactiveUSec`, `passwordChangeNow`,
+`pkcs11TokenUri`, `fido2HmacCredential`.
 
 ## Fields in the `binding` section
 
@@ -810,7 +831,7 @@ public key.
 The `signature` field in the top-level user record object is an array of
 objects. Each object encapsulates one signature and has two fields: `data` and
 `key` (both are strings). The `data` field contains the actual signature,
-encoded in base64, the `key` field contains a copy of the public key whose
+encoded in Base64, the `key` field contains a copy of the public key whose
 private key was used to make the signature, in PEM format. Currently only
 signatures with Ed25519 keys are defined.
 
@@ -864,13 +885,20 @@ The `secret` field of the top-level user record contains the following fields:
 
 `password` → an array of strings, each containing a plain text password.
 
-`pkcs11Pin` → an array of strings, each containing a plain text PIN, suitable
-for unlocking PKCS#11 security tokens that require that.
+`tokenPin` → an array of strings, each containing a plain text PIN, suitable
+for unlocking security tokens that require that. (The field `pkcs11Pin` should
+be considered a compatibility alias for this field, and merged with `tokenPin`
+in case both are set.)
 
 `pkcs11ProtectedAuthenticationPathPermitted` → a boolean. If set to true allows
 the receiver to use the PKCS#11 "protected authentication path" (i.e. a
 physical button/touch element on the security token) for authenticating the
-user. If false or unset authentication this way shall not be attempted.
+user. If false or unset, authentication this way shall not be attempted.
+
+`fido2UserPresencePermitted` → a boolean. If set to true allows the receiver to
+use the FIDO2 "user presence" flag. This is similar to the concept of
+`pkcs11ProtectedAuthenticationPathPermitted`, but exposes the FIDO2 concept
+behind it. If false or unset authentication this way shall not be attempted.
 
 ## Mapping to `struct passwd` and `struct spwd`
 
index 5886ef2a6370f40f7bc8e82411f8d06a1bca174e..3284cced1a93880a163da99e9568eca0d18175c4 100644 (file)
@@ -526,10 +526,15 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP*Pavilion*dv7*Notebook*PC:
  KEYBOARD_KEY_c6=break
  KEYBOARD_KEY_94=reserved
 
-# Pavilion and Spectre x360 13 (Prevents random airplane mode activation)
+# Pavilion 13 x360 (Tablet mode and SYSRQ key)
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*[pP][aA][vV][iI][lL][iI][oO][nN]*13*x360*:pvr*
+ KEYBOARD_KEY_d7=!f22                                   # touchpad off
+ KEYBOARD_KEY_d9=unknown
+ KEYBOARD_KEY_d2=sysrq                                  # Fn+Print = SYSRQ
+
+# Spectre x360 13 (Prevents random airplane mode activation)
 evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*[sS][pP][eE][cC][tT][rR][eE]*x360*13*:pvr*
 evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pn*[sS][pP][eE][cC][tT][rR][eE]*x360Convertible*:pvr*
-evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*[pP][aA][vV][iI][lL][iI][oO][nN]*13*x360*:pvr*
  KEYBOARD_KEY_d7=unknown
 
 # Spectre x360 13
index b14b05fde622b48ea01106461ddbb9f4fa29a46e..a1e80ca6c1e5da0d7914dc066ff1bf7a922bdf02 100644 (file)
@@ -689,6 +689,9 @@ sensor:modalias:acpi:KIOX020A*:dmi:*:svnTREKSTOR:pnPRIMEBOOKC11B:*
 #########################################
 # Umax
 #########################################
+sensor:modalias:acpi:KIOX000A*:dmi:*:svnUMAX:pnVisionBook10WiPro:*
+ ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+
 sensor:modalias:acpi:SMO8500*:dmi:*:svnUMAX:pnVisionBook10WiPlus:*
  ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
 
index 63ef1fba1227d893bd1956d0916d0f96fdb5479a..d0a2a22709a063ad0142f4b3d9204b078a14f256 100644 (file)
@@ -381,6 +381,10 @@ mouse:usb:v046dp101b:name:Logitech M705:
 mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:101b:
  MOUSE_DPI=1000@125
 
+# Logitech M705 (newer version?)
+mouse:usb:v046dp406d:name:Logitech M705:
+ MOUSE_DPI=1000@167
+
 # Logitech M305 Wireless Optical Mouse
 mouse:usb:v046dpc52f:name:Logitech USB Receiver:
  MOUSE_DPI=1000@170
index 81cdc33898f7c31a0ac25378e96ec93ac6058139..46da7741c97e5cf7abcb63cf460f1d31199f31ee 100644 (file)
@@ -49,7 +49,7 @@
     <title>Options</title>
 
     <para>All options are configured in the
-    <literal>[Coredump]</literal> section:</para>
+    [Coredump] section:</para>
 
     <variablelist class='config-directives'>
 
index a3136113694ec80958808466396d1804173654d8..150d410c24cc5d4ad681627aeadfc1cf3cd4f78d 100644 (file)
@@ -52,8 +52,8 @@
         matching specified characteristics. If no command is
         specified, this is the implied default.</para>
 
-        <para>The output is designed to be human readable and contains list contains
-        a table with the following columns:</para>
+        <para>The output is designed to be human readable and contains a table with the following
+        columns:</para>
         <variablelist>
           <varlistentry>
             <term>TIME</term>
index 4cdc52dcb887afeb91fc46aeabf5d982466d7e3c..ee54499bfe7a13c86869b3c7c4e355a0e7d511a5 100644 (file)
 
         <listitem><para>Perform encryption using the same cpu that IO was submitted on. The default is to use
         an unbound workqueue so that encryption work is automatically balanced between available CPUs.</para>
+
         <para>This requires kernel 4.0 or newer.</para>
         </listitem>
       </varlistentry>
         <term><option>submit-from-crypt-cpus</option></term>
 
         <listitem><para>Disable offloading writes to a separate thread after encryption. There are some
-        situations where offloading write bios from the encryption threads to a single thread degrades
-        performance significantly. The default is to offload write bios to the same thread because it benefits
-        CFQ to have writes submitted using the same context.</para>
+        situations where offloading write requests from the encryption threads to a dedicated thread degrades
+        performance significantly. The default is to offload write requests to a dedicated thread because it
+        benefits the CFQ scheduler to have writes submitted using the same context.</para>
+
         <para>This requires kernel 4.0 or newer.</para>
         </listitem>
       </varlistentry>
@@ -512,7 +514,8 @@ external   /dev/sda3       keyfile:LABEL=keydev keyfile-timeout=10s</programlist
 
       <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, using
-      <command>ykman</command> from the yubikey-manager project:</para>
+      <citerefentry project='debian'><refentrytitle>ykmap</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+      from the yubikey-manager project:</para>
 
 <programlisting><xi:include href="yubikey-crypttab.sh" parse="text" /></programlisting>
 
index 52b3883607cf9c8ea49339512b24ba8d0a515bd6..072529eeecbce9422e0a95c907c3dcf094020b57 100644 (file)
       special target unit <filename>sockets.target</filename>. It is
       recommended to place a
       <varname>WantedBy=sockets.target</varname> directive in the
-      <literal>[Install]</literal> section to automatically add such a
+      [Install] section to automatically add such a
       dependency on installation of a socket unit. Unless
       <varname>DefaultDependencies=no</varname> is set, the necessary
       ordering dependencies are implicitly created for all socket
         operating system-independent.</para></listitem>
 
         <listitem><para>Make sure to include an
-        <literal>[Install]</literal> section including installation
+        [Install] section including installation
         information for the unit file. See
         <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
         for details. To activate your service on boot, make sure to
index 497cb580c73165e61048365fca7b76201429664f..d5899dc362183d90402d6b5c8354e8aedadaecbe 100644 (file)
     <filename>/usr/share/</filename> hierarchy to the locations
     defined by the various relevant specifications.</para>
 
-    <para>During runtime, and for local configuration and state,
+    <para>During runtime, and for local configuration and runtime state,
     additional directories are defined:</para>
 
     <table>
index 3a4c6e5f5b1de0bd6ac8df5b0603e03ddebfcfea..ad59c174e0f99080335fe69927acf9baa1778dfa 100644 (file)
         <term><option>--identity=</option><replaceable>FILE</replaceable></term>
 
         <listitem><para>Read the user's JSON record from the specified file. If passed as
-        <literal>-</literal> reads the user record from standard input. The supplied JSON object must follow
-        the structure documented on <ulink url="https://systemd.io/USER_RECORDS">JSON User
-        Records</ulink>. This option may be used in conjunction with the <command>create</command> and
+        <literal>-</literal> read the user record from standard input. The supplied JSON object must follow
+        the structure documented on <ulink url="https://systemd.io/USER_RECORD">JSON User Records</ulink>.
+        This option may be used in conjunction with the <command>create</command> and
         <command>update</command> commands (see below), where it allows configuring the user record in JSON
         as-is, instead of setting the individual user record properties (see below).</para></listitem>
       </varlistentry>
         different system and the configured UID is taken by another user there, then
         <command>systemd-homed</command> may assign the user a different UID on that system. The specified
         UID must be outside of the system user range. It is recommended to use the 60001…60513 UID range for
-        this purpose. If not specified the UID is automatically picked. When logging in and the home
-        directory is found to be owned by a UID not matching the user's assigned one the home directory and
-        all files and directories inside it will have their ownership changed automatically before login
-        completes.</para>
+        this purpose. If not specified, the UID is automatically picked. If the home directory is found to be
+        owned by a different UID when logging in, the home directory and everything underneath it will have
+        its ownership changed automatically before login completes.</para>
 
         <para>Note that users managed by <command>systemd-homed</command> always have a matching group
         associated with the same name as well as a GID matching the UID of the user. Thus, configuring the
         privileges. Note that <command>systemd-homed</command> does not manage any groups besides a group
         matching the user in name and numeric UID/GID. Thus any groups listed here must be registered
         independently, for example with <citerefentry
-        project='man-pages'><refentrytitle>groupadd</refentrytitle><manvolnum>8</manvolnum></citerefentry>. If
-        non-existent groups that are listed there are ignored. This option may be used more than once, in
-        which case all specified group lists are combined. If the user is currently a member of a group
-        which is not listed, the user will be removed from the group.</para></listitem>
+        project='man-pages'><refentrytitle>groupadd</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+        Any non-existent groups are ignored. This option may be used more than once, in which case all
+        specified group lists are combined. If the user is currently a member of a group which is not listed,
+        the user will be removed from the group.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>--skel=</option><replaceable>PATH</replaceable></term>
 
         <listitem><para>Takes a file system path to a directory. Specifies the skeleton directory to
-        initialize the home directory with. All files and directories in the specified are copied into any
-        newly create home directory. If not specified defaults to
-        <filename>/etc/skel/</filename>.</para></listitem>
+        initialize the home directory with. All files and directories in the specified path are copied into
+        any newly create home directory. If not specified defaults to <filename>/etc/skel/</filename>.
+        </para></listitem>
       </varlistentry>
 
       <varlistentry>
         <listitem><para>Takes a specifier indicating the preferred language of the user. The
         <varname>$LANG</varname> environment variable is initialized from this value on login, and thus a
         value suitable for this environment variable is accepted here, for example
-        <option>--language=de_DE.UTF8</option></para></listitem>
+        <option>--language=de_DE.UTF8</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         security token with exactly one pair of X.509 certificate and private key. A random secret key is
         then generated, encrypted with the public key of the X.509 certificate, and stored as part of the
         user record. At login time it is decrypted with the PKCS#11 module and then used to unlock the
-        account and associated resources. See below for an example how to set up authentication with security
-        token.</para></listitem>
+        account and associated resources. See below for an example how to set up authentication with a
+        security token.</para>
+
+        <para>Instead of a valid PKCS#11 URI, the special strings <literal>list</literal> and
+        <literal>auto</literal> may be specified. If <literal>list</literal> is passed, a brief table of
+        suitable, currently plugged in PKCS#11 hardware tokens is shown, along with their URIs. If
+        <literal>auto</literal> is passed, a suitable PKCS#11 hardware token is automatically selected (this
+        operation will fail if there isn't exactly one suitable token discovered). The latter is a useful
+        shortcut for the most common case where a single PKCS#11 hardware token is plugged in.</para>
+
+        <para>Note that many hardware security tokens implement both PKCS#11/PIV and FIDO2 with the
+        <literal>hmac-secret</literal> extension (for example: the YubiKey 5 series), as supported with the
+        <option>--fido2-device=</option> option below. Both mechanisms are similarly powerful, though FIDO2
+        is the more modern technology. PKCS#11/PIV tokens have the benefit of being recognizable before
+        authentication and hence can be used for implying the user identity to use for logging in, which
+        FIDO2 does not allow. PKCS#11/PIV devices generally require initialization (i.e. storing a
+        private/public key pair on them, see example below) before they can be used; FIDO2 security tokens
+        generally do not required that, and work out of the box.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--fido2-device=</option><replaceable>PATH</replaceable></term>
+
+        <listitem><para>Takes a path to a Linux <literal>hidraw</literal> device
+        (e.g. <filename>/dev/hidraw1</filename>), referring to a FIDO2 security token implementing the
+        <literal>hmac-secret</literal> extension, that shall be able to unlock the user account. If used, a
+        random salt value is generated on the host, which is passed to the FIDO2 device, which calculates a
+        HMAC hash of it, keyed by its internal secret key. The result is then used as key for unlocking the
+        user account. The random salt is included in the user record, so that whenever authentication is
+        needed it can be passed again to the FIDO2 token, to retrieve the actual key.</para>
+
+        <para>Instead of a valid path to a FIDO2 <literal>hidraw</literal> device the special strings
+        <literal>list</literal> and <literal>auto</literal> may be specified. If <literal>list</literal> is
+        passed, a brief table of suitable discovered FIDO2 devices is shown. If <literal>auto</literal> is
+        passed, a suitable FIDO2 token is automatically selected, if exactly one is discovered. The latter is
+        a useful shortcut for the most common case where a single FIDO2 hardware token is plugged in.</para>
+
+        <para>Note that FIDO2 devices suitable for this option must implement the
+        <literal>hmac-secret</literal> extension. Most current devices (such as the YubiKey 5 series) do. If
+        the extension is not implemented the device cannot be used for unlocking home directories.</para>
+
+        <para>Note that many hardware security tokens implement both FIDO2 and PKCS#11/PIV (and thus may be
+        used with either <option>--fido2-device=</option> or <option>--pkcs11-token-uri=</option>), for a
+        discussion see above.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <listitem><para>Each of these options takes a time span specification as argument (in the syntax
         documented in
         <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>5</manvolnum></citerefentry>) and
-        configure various aspects of the user's password expiration policy. Specifically,
+        configures various aspects of the user's password expiration policy. Specifically,
         <option>--password-change-min=</option> configures how much time has to pass after changing the
         password of the user until the password may be changed again. If the user tries to change their
         password before this time passes the attempt is refused. <option>--password-change-max=</option>
-        configures how much time has to pass after the the password is changed until the password expires and
-        needs to be changed again. After this time passes any attempts to log in may only proceed after the
-        password is changed. <option>--password-change-warn=</option> specifies how much earlier than then
-        the time configured with <option>--password-change-max=</option> the user is warned at login to
-        change their password as it will expire soon. Finally <option>--password-change-inactive=</option>
-        configures the time which has to pass after the password as expired until the user is not permitted
-        to log in or change the password anymore. Note that these options only apply to password
-        authentication, and do not apply to other forms of authentication, for example PKCS#11-based security
-        token authentication.</para></listitem>
+        configures how soon after it has been changed the password expires and needs to be changed again.
+        After this time passes logging in may only proceed after the password is changed.
+        <option>--password-change-warn=</option> specifies how much earlier than then the time configured
+        with <option>--password-change-max=</option> the user is warned at login to change their password as
+        it will expire soon. Finally <option>--password-change-inactive=</option> configures the time which
+        has to pass after the password as expired until the user is not permitted to log in or change the
+        password anymore. Note that these options only apply to password authentication, and do not apply to
+        other forms of authentication, for example PKCS#11-based security token
+        authentication.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <para>Activation of a home directory involves various operations that depend on the selected storage
         mechanism. If the LUKS2 mechanism is used, this generally involves: inquiring the user for a
         password, setting up a loopback device, validating and activating the LUKS2 volume, checking the file
-        system, mounting the file system, and potentiatlly changing the ownership of all included files to
-        the correct UID/GID.</para></listitem>
+        system, mounting the file system, and potentially changing the ownership of all included files to the
+        correct UID/GID.</para></listitem>
       </varlistentry>
 
       <varlistentry>
     </example>
 
     <example>
-      <title>Set up authentication with a YubiKey security token:</title>
+      <title>Set up authentication with a YubiKey security token using PKCS#11/PIV:</title>
 
       <programlisting># Clear the Yubikey from any old keys (careful!)
 ykman piv reset
@@ -821,16 +862,18 @@ 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
 
-# We don't need the publibc key on disk anymore
+# We don't need the public key on disk anymore
 rm 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
+# Allow the security token to unlock the account of user 'lafcadio'.
+homectl update lafcadio --pkcs11-token-uri=auto</programlisting>
+    </example>
+
+    <example>
+      <title>Set up authentication with a FIDO2 security token:</title>
 
-# Allow the security token referenced by the determined PKCS#11 URI to unlock the account of user
-# 'lafcadio'. (Replace the '…' by the URI from the clipboard.)
-homectl update lafcadio --pkcs11-token-uri=…</programlisting>
+      <programlisting># Allow a FIDO2 security token to unlock the account of user 'nihilbaxter'.
+homectl update nihilbaxter --fido2-device=auto</programlisting>
     </example>
   </refsect1>
 
index 9e321940b723e79b9e0f7d72c888988c32f08861..2c5f3355d319d7cef26d45420f653028a7d2a8b5 100644 (file)
@@ -42,7 +42,7 @@
   <refsect1>
     <title>Options</title>
 
-    <para>The following options are available in the <literal>[Home]</literal> section:</para>
+    <para>The following options are available in the [Home] section:</para>
 
     <variablelist class='home-directives'>
 
index ab3dfd0111fbe005c77e47208437135f72d16c3c..3db12dd0ce6321036dbc9a2ef4cef0ad41ffd11b 100644 (file)
@@ -49,7 +49,7 @@
     <title>Options</title>
 
     <para>All options are configured in the
-    <literal>[Remote]</literal> section:</para>
+    [Remote] section:</para>
 
     <variablelist class='config-directives'>
       <varlistentry>
index f2721a75e7d271669a3fcf90d870f516646f757f..6cb1aa474de77f6dcf75c8aea1dc7d51c4a9c00a 100644 (file)
@@ -43,7 +43,7 @@
   <refsect1>
     <title>Options</title>
 
-    <para>All options are configured in the <literal>[Upload]</literal> section:</para>
+    <para>All options are configured in the [Upload] section:</para>
 
     <variablelist class='config-directives'>
       <varlistentry>
index 5c32ce8b9b37a751533b70027db29404271685f0..ea60196e86c52e9777be29101e653f1f1e5f933c 100644 (file)
         is also added for <literal>_SYSTEMD_SLICE=<replaceable>UNIT</replaceable></literal>,
         such that if the provided <replaceable>UNIT</replaceable> is a
         <citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-        unit, all logs of the children of the slice will be logged.
+        unit, all logs of children of the slice will be shown.
        </para>
 
         <para>This parameter can be specified multiple times.</para>
         is also added for <literal>_SYSTEMD_USER_SLICE=<replaceable>UNIT</replaceable></literal>,
         such that if the provided <replaceable>UNIT</replaceable> is a
         <citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-        unit, all logs of the children of the unit will be logged.</para>
+        unit, all logs of children of the unit will be shown.</para>
 
         <para>This parameter can be specified multiple times.</para>
         </listitem>
         underneath the specified directory instead of the root
         directory (e.g. <option>--update-catalog</option> will create
         <filename><replaceable>ROOT</replaceable>/var/lib/systemd/catalog/database</filename>,
-        and journal files under <filename><replaceable>ROOT</replaceable>/run/journal</filename>
-        or <filename><replaceable>ROOT</replaceable>/var/log/journal</filename> will be displayed).
+        and journal files under <filename><replaceable>ROOT</replaceable>/run/journal/</filename>
+        or <filename><replaceable>ROOT</replaceable>/var/log/journal/</filename> will be displayed).
         </para></listitem>
       </varlistentry>
 
         <filename>/run/log/journal/</filename> into <filename>/var/log/journal/</filename>, if persistent
         storage is enabled. This call does not return until the operation is complete. Note that this call is
         idempotent: the data is only flushed from <filename>/run/log/journal/</filename> into
-        <filename>/var/log/journal</filename> once during system runtime (but see
+        <filename>/var/log/journal/</filename> once during system runtime (but see
         <option>--relinquish-var</option> below), and this command exits cleanly without executing any
         operation if this has already happened. This command effectively guarantees that all data is flushed
-        to <filename>/var/log/journal</filename> at the time it returns.</para></listitem>
+        to <filename>/var/log/journal/</filename> at the time it returns.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index 957ec36a68c7d5cb3f9c814b8287be28384a12a6..5c9419b012e3e1819ebcf35450452f9796d9c21f 100644 (file)
@@ -53,7 +53,7 @@
     <title>Options</title>
 
     <para>All options are configured in the
-    <literal>[Journal]</literal> section:</para>
+    [Journal] section:</para>
 
     <variablelist class='config-directives'>
 
index 3df80751dd263fd4a67df87ba536884963be79a1..273270f55013d55f9dd6895b24b11d8112d0676d 100644 (file)
@@ -36,7 +36,7 @@
     <title>Description</title>
     <para><command>kernel-install</command> is used to install and remove kernel and initramfs images to and
     from the boot loader partition, referred to as <varname>$BOOT</varname> here. It will usually be one of
-    <filename>/boot</filename>, <filename>/efi</filename>, or <filename>/boot/efi</filename>, see below.
+    <filename>/boot/</filename>, <filename>/efi/</filename>, or <filename>/boot/efi/</filename>, see below.
     </para>
 
     <para><command>kernel-install</command> will execute the files
     <para>The partition where the kernels and <ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot
     Loader Specification</ulink> snippets are located is called <varname>$BOOT</varname>.
     <command>kernel-install</command> determines the location of this partition by checking
-    <filename>/efi/</filename>, <filename>/boot/</filename>, and <filename>/boot/efi</filename>
+    <filename>/efi/</filename>, <filename>/boot/</filename>, and <filename>/boot/efi/</filename>
     in turn. The first location where <filename>$BOOT/loader/entries/</filename> or
     <filename>$BOOT/$MACHINE_ID/</filename> exists is used.</para>
   </refsect1>
index b00daf366da7971c538d541c9f210e1bba4673c2..22f21355b625eae5b8183d574f899ec89ad0a017 100644 (file)
@@ -46,7 +46,7 @@
     <title>Options</title>
 
     <para>All options are configured in the
-    <literal>[Login]</literal> section:</para>
+    [Login] section:</para>
 
     <variablelist class='config-directives'>
 
       <varlistentry>
         <term><varname>HoldoffTimeoutSec=</varname></term>
 
-        <listitem><para>Specifies the timeout after system startup or
+        <listitem><para>Specifies a period of time after system startup or
         system resume in which systemd will hold off on reacting to
         lid events. This is required for the system to properly
         detect any hotplugged devices so systemd can ignore lid events
index ebee065a614f901e1000a5ab723be5c1c8ad43db..bd55366ac8805a739ac2d7f4e76a387d1f2b5ec6 100644 (file)
@@ -39,7 +39,7 @@
 
     <para>The machine ID may be set, for example when network booting, with the
     <varname>systemd.machine_id=</varname> kernel command line parameter or by passing the
-    option <option>--machine-id=</option> to systemd. An ID is specified in this manner
+    option <option>--machine-id=</option> to systemd. An ID specified in this manner
     has higher priority and will be used instead of the ID stored in
     <filename>/etc/machine-id</filename>.</para>
 
index e903eca7214aea2a83af521ba390212e569b60e5..37e51f90cf2718bb6c4bf0eee79e706351d536e9 100644 (file)
 
         <listitem><para>Copies files or directories from a container
         into the host system. Takes a container name, followed by the
-        source path in the container the destination path on the host.
+        source path in the container and the destination path on the host.
         If the destination path is omitted, the same as the source path
         is used.</para>
 
index 6c60f6cf6b25f07b5a40d58329d1a250a097b66c..e8e41ebe920f73676562bef89707ad598e07ce73 100644 (file)
@@ -45,7 +45,7 @@
   <refsect1>
     <title>[Network] Section Options</title>
 
-    <para>The following options are available in the <literal>[Network]</literal> section:</para>
+    <para>The following options are available in the [Network] section:</para>
 
     <variablelist class='network-directives'>
       <varlistentry>
index e23b24483e4fae3e1834db34924f7c4b1706d240..8bb21f2621b5b2533b40427f4316fd0b33abb6a4 100644 (file)
@@ -18,8 +18,7 @@
   <refnamediv>
     <refname>nss-myhostname</refname>
     <refname>libnss_myhostname.so.2</refname>
-    <refpurpose>Provide hostname resolution for the locally
-    configured system hostname.</refpurpose>
+    <refpurpose>Hostname resolution for the locally configured system hostname</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index 1ff88aba2d1dedca9cd80b8c92c27fa69e04cf7b..6e4883555047ecd207a737ba2ece387860d8967f 100644 (file)
@@ -18,8 +18,7 @@
   <refnamediv>
     <refname>nss-mymachines</refname>
     <refname>libnss_mymachines.so.2</refname>
-    <refpurpose>Provide hostname resolution for local
-    container instances.</refpurpose>
+    <refpurpose>Hostname resolution for local container instances</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index 8bc75b1316038901311fc7ab86b14561e66e5d47..332cf0b06d3019b7e24e762c61369fde7ceda009 100644 (file)
@@ -18,7 +18,7 @@
   <refnamediv>
     <refname>nss-resolve</refname>
     <refname>libnss_resolve.so.2</refname>
-    <refpurpose>Provide hostname resolution via <filename>systemd-resolved.service</filename></refpurpose>
+    <refpurpose>Hostname resolution via <filename>systemd-resolved.service</filename></refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index ac22452bc3ed4ab0c23857eba8ccd247d36f6171..26730f0fe90dd1cefc22e9efac47e51c502771ae 100644 (file)
@@ -18,7 +18,7 @@
   <refnamediv>
     <refname>nss-systemd</refname>
     <refname>libnss_systemd.so.2</refname>
-    <refpurpose>Provide UNIX user and group name resolution for user/group lookup via Varlink</refpurpose>
+    <refpurpose>UNIX user and group name resolution for user/group lookup via Varlink</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index 385e29031206af1bb8141dde641b84cf86dbeda1..d8ce392c879237609478b7a81122212484c2d282 100644 (file)
@@ -83,7 +83,7 @@ node /org/freedesktop/LogControl1 {
       <para><varname>LogTarget</varname> describes the log target (mechanism). It should be one of
       <literal>console</literal> (log to the console or standard output),
       <literal>kmsg</literal> (log to the kernel ring buffer),
-      <literal>journal</literal> (log the the journal natively, see
+      <literal>journal</literal> (log to the journal natively, see
       <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>),
       <literal>syslog</literal> (log using the
       <citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry> call).
index cf9ba71adcb31d6f68f51d302c35bbd66992348c..609743be6b2f24b34725c76daa11d3bf146d9d16 100644 (file)
         hence be used to uniquely label files or other resources of this session. Combine this ID with the boot
         identifier, as returned by
         <citerefentry><refentrytitle>sd_id128_get_boot</refentrytitle><manvolnum>3</manvolnum></citerefentry>, for a
-        globally unique identifier for the current session.</para></listitem>
+        globally unique identifier.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index f2d8da40c410ffaff35f3100273fa5b2f09ed61c..962429683d6b38fcbe3424713f575c0c798a1143 100644 (file)
 
         <para>By default all unit files whose names start with a prefix generated from the image's file name are copied
         out. Specifically, the prefix is determined from the image file name with any suffix such as
-        <filename>.raw</filename> removed, truncated at the first occurrence of and underscore character
+        <filename>.raw</filename> removed, truncated at the first occurrence of an underscore character
         (<literal>_</literal>), if there is one. The underscore logic is supposed to be used to versioning so that the
         an image file <filename>foobar_47.11.raw</filename> will result in a unit file matching prefix of
         <filename>foobar</filename>. This prefix is then compared with all unit files names contained in the image in
       </tgroup>
     </table>
 
-    <para>For details on this profiles, and their effects please have a look at their precise definitions,
+    <para>For details on these profiles and their effects see their precise definitions,
     e.g. <filename>/usr/lib/systemd/portable/profile/default/service.conf</filename> and similar.</para>
   </refsect1>
 
index 2b9c8b1a7145ca015ab1079d52c02bdb3a55afe1..501171e78a887f245756cfce1206ef43c6a02816 100644 (file)
@@ -44,7 +44,7 @@
     <title>Options</title>
 
     <para>All options are configured in the
-    <literal>[PStore]</literal> section:</para>
+    [PStore] section:</para>
 
     <variablelist class='config-directives'>
 
@@ -82,7 +82,7 @@
   <refsect1>
     <title>See Also</title>
     <para>
-      <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index a39da086bf3d113e2df0a98a65809dad81fb860e..0e7a9f4bc664cd8eceaa10ab674faa469e013b74 100644 (file)
@@ -41,7 +41,7 @@
   <refsect1>
     <title>Options</title>
 
-    <para>The following options are available in the <literal>[Resolve]</literal> section:</para>
+    <para>The following options are available in the [Resolve] section:</para>
 
     <variablelist class='network-directives'>
 
index 3318a3031bc660caec63466374561a0ab619194b..f948b5914f54bcb42a3377b34be07729dfec633a 100644 (file)
@@ -19,7 +19,7 @@
   <refnamediv>
     <refname>sd_bus_enqueue_for_read</refname>
 
-    <refpurpose>Re-enqueue a bus message on a bus connection, for reading.</refpurpose>
+    <refpurpose>Re-enqueue a bus message on a bus connection, for reading</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index d993142cb5c9d45d9bfd0272685a050fa7942b57..4a21189c1d2a5f4cd6fb8f2645b485ef3e16aa84 100644 (file)
@@ -20,7 +20,7 @@
     <refname>sd_bus_is_open</refname>
     <refname>sd_bus_is_ready</refname>
 
-    <refpurpose>Check whether the a bus connection is open or ready.</refpurpose>
+    <refpurpose>Check whether the bus connection is open or ready</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index aa29007c27e551a19b8f68d4ecd419efec234606..39bb24c3a53613b10f0f7d45b050f68ee1dffae1 100644 (file)
@@ -22,7 +22,7 @@
     <refname>sd_bus_message_new_method_errno</refname>
     <refname>sd_bus_message_new_method_errnof</refname>
 
-    <refpurpose>Create a an error reply for a method call</refpurpose>
+    <refpurpose>Create an error reply for a method call</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index edb0df205a88ba1e08de355657c5d125e65e4d70..b025112b6393e7503a8a86922344c6b0b4732c20 100644 (file)
@@ -20,7 +20,7 @@
     <refname>sd_bus_set_connected_signal</refname>
     <refname>sd_bus_get_connected_signal</refname>
 
-    <refpurpose>Control emmission of local connection establishment signal on bus connections</refpurpose>
+    <refpurpose>Control emission of local connection establishment signal on bus connections</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index c6fe72e37b5ff541488db1cb523793d3678de9d2..c2a0876e212506cd0f3d8b9e8476e21874ba418a 100644 (file)
@@ -23,7 +23,7 @@
     <refname>sd_bus_track_get_destroy_callback</refname>
     <refname>sd_bus_destroy_t</refname>
 
-    <refpurpose>Define the callback function for resource cleanup.</refpurpose>
+    <refpurpose>Define the callback function for resource cleanup</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index f63907aa405f48702cede38f0db8b19bd5b065d6..ecfc07951415536cbc86328e9a43b6d77b2da213 100644 (file)
@@ -19,7 +19,7 @@
     <refname>sd_bus_slot_set_floating</refname>
     <refname>sd_bus_slot_get_floating</refname>
 
-    <refpurpose>Control whether a bus slot object is "floating".</refpurpose>
+    <refpurpose>Control whether a bus slot object is "floating"</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index 2ffca9ec365bcec45c80619611c4da955763f292..3df926b8634f02728fa71ed3078e850fa177c8fb 100644 (file)
@@ -21,7 +21,7 @@
     <refname>sd_event_source_get_destroy_callback</refname>
     <refname>sd_event_destroy_t</refname>
 
-    <refpurpose>Define the callback function for resource cleanup.</refpurpose>
+    <refpurpose>Define the callback function for resource cleanup</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index 1372f4d80cdb029f14ca1afb28e3e34cbfaf7221..dbaaf6292cff8620b09aa9894f77351e05bc0786 100644 (file)
@@ -64,7 +64,7 @@
     <para><function>sd_hwdb_get()</function> queries the <parameter>hwdb</parameter> object created earlier
     with <citerefentry><refentrytitle>sd_hwdb_new</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
     entries matching the specified string <parameter>modalias</parameter>, and returns the value
-    corresponding to the the key <parameter>key</parameter>. The value is returned as a
+    corresponding to the key <parameter>key</parameter>. The value is returned as a
     <constant>NUL</constant>-terminated string in <parameter>value</parameter>. It must not be modified by
     the caller and is valid as long as a reference to <parameter>hwdb</parameter> is kept. When multiple
     patterns in the database match <parameter>modalias</parameter>, the one with the highest priority is
index 7e6e7d4b9d29d1238bc4e859cc17fd2d3e3098eb..02fdc12473e57176acb2adb03b923f11fd15fb4c 100644 (file)
@@ -21,7 +21,7 @@
   <refnamediv>
     <refname>sd_journal_has_runtime_files</refname>
     <refname>sd_journal_has_persistent_files</refname>
-    <refpurpose>Query availability of runtime or persistent journal files.</refpurpose>
+    <refpurpose>Query availability of runtime or persistent journal files</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index db6cf0dbc09b30c8a3244d419507e39b22f2f44d..cd259c863f9fa5e6a8125ba6b7e83028ff1430c9 100644 (file)
@@ -19,7 +19,7 @@
     <refname>sd_machine_get_class</refname>
     <refname>sd_machine_get_ifindices</refname>
     <refpurpose>Determine the class and network interface indices of a
-    locally running virtual machine or container.</refpurpose>
+    locally running virtual machine or container</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index e9d7a8eb696899aa4119f66233c7b9ff78a58541..9c16d5bc9c01c6841f5eebfc62727ab950af78d0 100644 (file)
@@ -35,7 +35,7 @@
     <refname>sd_peer_get_cgroup</refname>
     <refpurpose>Determine the owner uid of the user unit or session,
     or the session, user unit, system unit, container/VM or slice that
-    a specific PID or socket peer belongs to.</refpurpose>
+    a specific PID or socket peer belongs to</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index a58c76d85fa7da19863361beaf8f05bfd6b9eddc..ee8cc7bc0a11b050127416007fd3a22ba6e03fab 100644 (file)
@@ -13,7 +13,7 @@
 
     <para>Configuration files are read from directories in <filename>/etc/</filename>,
     <filename>/run/</filename>, <filename>/usr/local/lib/</filename>, and <filename>/usr/lib/</filename>, in
-    order of precedence, as listed in the SYNOPSIS section above. Files must have the the
+    order of precedence, as listed in the SYNOPSIS section above. Files must have the
     <literal>.conf</literal> extension. Files in <filename>/etc/</filename> override files with the same name
     in <filename>/run/</filename>, <filename>/usr/local/lib/</filename>, and
     <filename>/usr/lib/</filename>. Files in <filename>/run/</filename> override files with the same name
index d26f82475b80f613c26deb719bfbc77944fe016d..70504510f9ca6354889df72f6ab06f0cf2c4ae6e 100644 (file)
@@ -70,11 +70,11 @@ key.pattern.overridden.with.glob = custom
     followed by <literal>=</literal>, see SYNOPSIS.</para>
 
     <para>Any access permission errors and attempts to write variables not present on the local system are
-    logged, but do not cause the service to fail. Debug log level is used, which means that the message will
-    not show up at all by default. Moreover, if a variable assignment is prefixed with a single
-    <literal>-</literal> character, any failure to set the variable will be logged at debug level, but will
-    not cause the service to fail. All other errors when setting variables are logged with higher priority
-    and cause the service to return failure at the end (other variables are still processed).</para>
+    logged at debug level and do not cause the service to fail. Moreover, if a variable assignment is
+    prefixed with a single <literal>-</literal> character, failure to set the variable for other reasons will
+    be logged at debug level and will not cause the service to fail. In other cases, errors when setting
+    variables are logged with higher priority and cause the service to return failure at the end (after
+    processing other variables).</para>
 
     <para>The settings configured with <filename>sysctl.d</filename> files will be applied early on boot. The
     network interface-specific options will also be applied individually for each network interface as it
index 53342c4b9d4405af46f66d58eaca8558d0356635..28c2194285f6d039e124443c98fc797f77bb25b0 100644 (file)
@@ -412,7 +412,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
 
             <para>The "Loaded:" line in the output will show <literal>loaded</literal> if the unit has been loaded into
             memory. Other possible values for "Loaded:" include: <literal>error</literal> if there was a problem
-            loading it, <literal>not-found</literal> if not unit file was found for this unit,
+            loading it, <literal>not-found</literal> if no unit file was found for this unit,
             <literal>bad-setting</literal> if an essential unit file setting could not be parsed and
             <literal>masked</literal> if the unit file has been masked. Along with showing the path to the unit file,
             this line will also show the enablement state.  Enabled commands start at boot.  See the full table of
@@ -582,7 +582,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
 
           <listitem>
             <para>Enable one or more units or unit instances. This will create a set of symlinks, as encoded in the
-            <literal>[Install]</literal> sections of the indicated unit files. After the symlinks have been created,
+            [Install] sections of the indicated unit files. After the symlinks have been created,
             the system manager configuration is reloaded (in a way equivalent to <command>daemon-reload</command>), in
             order to ensure the changes are taken into account immediately. Note that this does
             <emphasis>not</emphasis> have the effect of also starting any of the units being enabled. If this is
@@ -605,7 +605,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
             <option>--quiet</option>.
             </para>
 
-            <para>Note that this operation creates only the symlinks suggested in the <literal>[Install]</literal>
+            <para>Note that this operation creates only the symlinks suggested in the [Install]
             section of the unit files. While this command is the recommended way to manipulate the unit configuration
             directory, the administrator is free to make additional changes manually by placing or removing symlinks
             below this directory. This is particularly useful to create configurations that deviate from the suggested
@@ -645,7 +645,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
             <para>This command expects valid unit names only, it does not accept paths to unit files.</para>
 
             <para>In addition to the units specified as arguments, all units are disabled that are listed in the
-            <varname>Also=</varname> setting contained in the <literal>[Install]</literal> section of any of the unit
+            <varname>Also=</varname> setting contained in the [Install] section of any of the unit
             files being operated on.</para>
 
             <para>This command implicitly reloads the system manager configuration after completing the operation. Note
@@ -668,7 +668,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
           <listitem>
             <para>Reenable one or more units, as specified on the command line. This is a combination of
             <command>disable</command> and <command>enable</command> and is useful to reset the symlinks a unit file is
-            enabled with to the defaults configured in its <literal>[Install]</literal> section. This command expects
+            enabled with to the defaults configured in its [Install] section. This command expects
             a unit name only, it does not accept paths to unit files.</para>
           </listitem>
         </varlistentry>
@@ -768,17 +768,17 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
                   </row>
                   <row>
                     <entry><literal>static</literal></entry>
-                    <entry>The unit file is not enabled, and has no provisions for enabling in the <literal>[Install]</literal> unit file section.</entry>
+                    <entry>The unit file is not enabled, and has no provisions for enabling in the [Install] unit file section.</entry>
                     <entry>0</entry>
                   </row>
                   <row>
                     <entry><literal>indirect</literal></entry>
-                    <entry>The unit file itself is not enabled, but it has a non-empty <varname>Also=</varname> setting in the <literal>[Install]</literal> unit file section, listing other unit files that might be enabled, or it has an alias under a different name through a symlink that is not specified in <varname>Also=</varname>. For template unit file, an instance different than the one specified in <varname>DefaultInstance=</varname> is enabled.</entry>
+                    <entry>The unit file itself is not enabled, but it has a non-empty <varname>Also=</varname> setting in the [Install] unit file section, listing other unit files that might be enabled, or it has an alias under a different name through a symlink that is not specified in <varname>Also=</varname>. For template unit files, an instance different than the one specified in <varname>DefaultInstance=</varname> is enabled.</entry>
                     <entry>0</entry>
                   </row>
                   <row>
                     <entry><literal>disabled</literal></entry>
-                    <entry>The unit file is not enabled, but contains an <literal>[Install]</literal> section with installation instructions.</entry>
+                    <entry>The unit file is not enabled, but contains an [Install] section with installation instructions.</entry>
                     <entry>&gt; 0</entry>
                   </row>
                   <row>
index ba8e67dff4c2b0b16f308dd4cb0a8335718f660d..3d6de1710d3946d9d48d7ec251b19f3e5cefbd12 100644 (file)
@@ -509,9 +509,9 @@ NAutoVTs=8
       <para>This command will load unit files and print warnings if any errors are detected. Files specified
       on the command line will be loaded, but also any other units referenced by them. The full unit search
       path is formed by combining the directories for all command line arguments, and the usual unit load
-      paths (variable <varname>$SYSTEMD_UNIT_PATH</varname> is supported, and may be used to replace or
+      paths. The variable <varname>$SYSTEMD_UNIT_PATH</varname> is supported, and may be used to replace or
       augment the compiled in set of unit load paths; see
-      <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>).  All
+      <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>. All
       units files present in the directories containing the command line arguments will be used in preference
       to the other paths.</para>
 
@@ -700,9 +700,9 @@ Service b@0.service not loaded, b.socket cannot be started.
       <varlistentry>
         <term><option>--man=no</option></term>
 
-        <listitem><para>Do not invoke man to verify the existence of
-        man pages listed in <varname>Documentation=</varname>.
-        </para></listitem>
+        <listitem><para>Do not invoke
+        <citerefentry project='man-pages'><refentrytitle>man</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+        to verify the existence of man pages listed in <varname>Documentation=</varname>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index 0c5144f6b89a87bc2eecdb1df3602cb17f43701d..8275838bce0f3cba40a6241d754796eb05b0fd72 100644 (file)
@@ -17,7 +17,7 @@
 
   <refnamediv>
     <refname>systemd-bless-boot-generator</refname>
-    <refpurpose>Pull <filename>systemd-bless-boot.service</filename> into the initial boot transaction when boot counting is in effect.</refpurpose>
+    <refpurpose>Pull <filename>systemd-bless-boot.service</filename> into the initial boot transaction when boot counting is in effect</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index b666ae1e535d0ccbc8c746a71ad168ea1dd2cace..3288728b2cf0e9e8f61be30249b84a900f0c448a 100644 (file)
@@ -91,7 +91,7 @@
 
       <listitem><para>The boot manager optionally reads a random seed from the ESP partition, combines it
       with a 'system token' stored in a persistent EFI variable and derives a random seed to use by the OS as
-      entropy pool initializaton, providing a full entropy pool during early boot.</para></listitem>
+      entropy pool initialization, providing a full entropy pool during early boot.</para></listitem>
     </itemizedlist>
 
     <para><citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
         is maintained persistently, while <varname>LoaderConfigTimeoutOneShot</varname> is a one-time override which is
         read once (in which case it takes precedence over <varname>LoaderConfigTimeout</varname>) and then
         removed. <varname>LoaderConfigTimeout</varname> may be manipulated with the
-        <keycap>t</keycap>/<keycap>T</keycap> keys, see above.)</para></listitem>
+        <keycap>t</keycap>/<keycap>T</keycap> keys, see above.</para></listitem>
       </varlistentry>
 
       <varlistentry>
       <varlistentry>
         <term><varname>LoaderSystemToken</varname></term>
 
-        <listitem><para>A binary random data field, that is used for generating the random see to pass to the
-        OS (see above). Note that this random data is generally only generated once, during OS installation,
-        and is then never updated again.</para></listitem>
+        <listitem><para>A binary random data field, that is used for generating the random seed to pass to
+        the OS (see above). Note that this random data is generally only generated once, during OS
+        installation, and is then never updated again.</para></listitem>
       </varlistentry>
     </variablelist>
 
index b0fa617d637899aabde433fdf129b3badbd5a955..78fdacccabac49abf0e8d06c57365b86ba66d569 100644 (file)
@@ -19,7 +19,7 @@
     <refname>systemd-gpt-auto-generator</refname>
     <refpurpose>Generator for automatically discovering and mounting root, <filename>/home/</filename>,
     <filename>/srv/</filename>, <filename>/var/</filename> and <filename>/var/tmp/</filename> partitions, as
-    well as discovering and enabling swap partitions, based on GPT partition type GUIDs.</refpurpose>
+    well as discovering and enabling swap partitions, based on GPT partition type GUIDs</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index 633b4cb55ba60f1029d15e3c1c96fd4f120c89c7..0f7aaab624f19eb3d351db5d1ce350aa2896cce1 100644 (file)
     </para>
 
     <para>where
-      <option>cursor</option> is a cursor string,
-      <option>num_skip</option> is an integer,
-      <option>num_entries</option> is an unsigned integer.
+      <replaceable>cursor</replaceable> is a cursor string,
+      <replaceable>num_skip</replaceable> is an integer,
+      <replaceable>num_entries</replaceable> is an unsigned integer.
     </para>
 
     <para>Range defaults to all available events.</para>
index a073a37a29565dad3c44aa4f3b97354fea851d4a..174bef803f45401f3d231f5df968abea3b9d262d 100644 (file)
@@ -267,7 +267,7 @@ openssl ca -batch -config ca.conf -notext -in $CLIENT.csr -out $CLIENT.pem
       those files can be specified using
       <varname>TrustedCertificateFile=</varname>,
       <varname>ServerCertificateFile=</varname>,
-      <varname>ServerKeyFile=</varname>, in
+      and <varname>ServerKeyFile=</varname> in
       <filename>/etc/systemd/journal-remote.conf</filename> and
       <filename>/etc/systemd/journal-upload.conf</filename>,
       respectively. The default locations can be queried by using
index c602b2e42fe800fcac5a0dd420a01180d3e2f36b..bbfcad4af6b354303b00cdcec9d27dc9d9e1930f 100644 (file)
       <citerefentry><refentrytitle>systemd-user-sessions.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>loginctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>logind.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd-login</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     </para>
   </refsect1>
index a6e6dd9b483bd7baede8d74b7c73caeb0318a8c6..f1089eed3b0f3d6ed58fff63be41dca84979c290 100644 (file)
     <replaceable>WHERE</replaceable>.</para>
 
     <para>In many ways, <command>systemd-mount</command> is similar to the lower-level
-    <citerefentry project='man-pages'><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry> command, however instead
-    of executing the mount operation directly and immediately, <command>systemd-mount</command> schedules it through
-    the service manager job queue, so that it may pull in further dependencies (such as parent mounts, or a file system
-    checker to execute a priori), and may make use of the auto-mounting logic.</para>
+    <citerefentry project='man-pages'><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    command, however instead of executing the mount operation directly and immediately,
+    <command>systemd-mount</command> schedules it through the service manager job queue, so that it may pull
+    in further dependencies (such as parent mounts, or a file system checker to execute a priori), and may
+    make use of the auto-mounting logic.</para>
 
     <para>The command takes either one or two arguments. If only one argument is specified it should refer to
     a block device or regular file containing a file system (e.g. <literal>/dev/sdb1</literal> or
     label and other metadata, and is mounted to a directory below <filename>/run/media/system/</filename>
     whose name is generated from the file system label. In this mode the block device or image file must
     exist at the time of invocation of the command, so that it may be probed. If the device is found to be a
-    removable block device (e.g. a USB stick) an automount point instead of a regular mount point is created
+    removable block device (e.g. a USB stick), an automount point is created instead of a regular mount point
     (i.e. the <option>--automount=</option> option is implied, see below).</para>
 
-    <para>If two arguments are specified the first indicates the mount source (the <replaceable>WHAT</replaceable>) and
-    the second indicates the path to mount it on (the <replaceable>WHERE</replaceable>). In this mode no probing of the
-    source is attempted, and a backing device node doesn't have to exist yet. However, if this mode is combined with
-    <option>--discover</option>, device node probing for additional metadata is enabled, and – much like in the
-    single-argument case discussed above – the specified device has to exist at the time of invocation of the
-    command.</para>
+    <para>If two arguments are specified, the first indicates the mount source (the
+    <replaceable>WHAT</replaceable>) and the second indicates the path to mount it on (the
+    <replaceable>WHERE</replaceable>). In this mode no probing of the source is attempted, and a backing
+    device node doesn't have to exist. However, if this mode is combined with <option>--discover</option>,
+    device node probing for additional metadata is enabled, and – much like in the single-argument case
+    discussed above – the specified device has to exist at the time of invocation of the command.</para>
 
     <para>Use the <option>--list</option> command to show a terse table of all local, known block devices with file
     systems that may be mounted with this command.</para>
index e28572db6ddffec3d1ca9e87a5e14ba5ea800645..7b643908e091ac74d8c6e43d5aee32634a99b63f 100644 (file)
         <term><option>-u</option></term>
         <term><option>--user=</option></term>
 
-        <listitem><para>After transitioning into the container, change
-        to the specified user-defined in the container's user
-        database. Like all other systemd-nspawn features, this is not
-        a security feature and provides protection against accidental
-        destructive operations only.</para></listitem>
+        <listitem><para>After transitioning into the container, change to the specified user defined in the
+        container's user database. Like all other systemd-nspawn features, this is not a security feature and
+        provides protection against accidental destructive operations only.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
         <para>Finally, if set to <literal>auto</literal> the file is left as it is if private networking is
         turned on (see <option>--private-network</option>). Otherwise, if
-        <filename>systemd-resolved.service</filename> is connectible its stub
-        <filename>resolv.conf</filename> file is used, and if not the host's
-        <filename>/etc/resolv.conf</filename> file is used. In the latter cases the file is copied if the
-        image is writable, and bind mounted otherwise.</para>
+        <filename>systemd-resolved.service</filename> is running its stub <filename>resolv.conf</filename>
+        file is used, and if not the host's <filename>/etc/resolv.conf</filename> file. In the latter cases
+        the file is copied if the image is writable, and bind mounted otherwise.</para>
 
         <para>It's recommended to use <literal>copy-…</literal> or <literal>replace-…</literal> if the
         container shall be able to make changes to the DNS configuration on its own, deviating from the
       <varlistentry>
         <term><option>--timezone=</option></term>
 
-        <listitem><para>Configures how <filename>/etc/localtime</filename> inside of the container (i.e. local timezone
-        synchronization from host to container) shall be handled. Takes one of <literal>off</literal>,
-        <literal>copy</literal>, <literal>bind</literal>, <literal>symlink</literal>, <literal>delete</literal> or
-        <literal>auto</literal>. If set to <literal>off</literal> the <filename>/etc/localtime</filename> file in the
-        container is left as it is included in the image, and neither modified nor bind mounted over. If set to
-        <literal>copy</literal> the <filename>/etc/localtime</filename> file of the host is copied into the
-        container. Similar, if <literal>bind</literal> is used, it is bind mounted from the host into the container. If
-        set to <literal>symlink</literal> a symlink from <filename>/etc/localtime</filename> in the container is
-        created pointing to the matching the timezone file of the container that matches the timezone setting on the
-        host. If set to <literal>delete</literal> the file in the container is deleted, should it exist. If set to
-        <literal>auto</literal> and the <filename>/etc/localtime</filename> file of the host is a symlink, then
-        <literal>symlink</literal> mode is used, and <literal>copy</literal> otherwise, except if the image is
-        read-only in which case <literal>bind</literal> is used instead. Defaults to
+        <listitem><para>Configures how <filename>/etc/localtime</filename> inside of the container
+        (i.e. local timezone synchronization from host to container) shall be handled. Takes one of
+        <literal>off</literal>, <literal>copy</literal>, <literal>bind</literal>, <literal>symlink</literal>,
+        <literal>delete</literal> or <literal>auto</literal>. If set to <literal>off</literal> the
+        <filename>/etc/localtime</filename> file in the container is left as it is included in the image, and
+        neither modified nor bind mounted over. If set to <literal>copy</literal> the
+        <filename>/etc/localtime</filename> file of the host is copied into the container. Similarly, if
+        <literal>bind</literal> is used, the file is bind mounted from the host into the container. If set to
+        <literal>symlink</literal>, a symlink is created pointing from <filename>/etc/localtime</filename> in
+        the container to the timezone file in the container that matches the timezone setting on the host. If
+        set to <literal>delete</literal>, the file in the container is deleted, should it exist. If set to
+        <literal>auto</literal> and the <filename>/etc/localtime</filename> file of the host is a symlink,
+        then <literal>symlink</literal> mode is used, and <literal>copy</literal> otherwise, except if the
+        image is read-only in which case <literal>bind</literal> is used instead. Defaults to
         <literal>auto</literal>.</para></listitem>
       </varlistentry>
 
 
       <para>This installs a minimal Fedora distribution into the
       directory <filename index="false">/var/lib/machines/f&fedora_latest_version;</filename>
-      and then boots an OS in a namespace container in it. Because the installation
+      and then boots that OS in a namespace container. Because the installation
       is located underneath the standard <filename>/var/lib/machines/</filename>
       directory, it is also possible to start the machine using
       <command>systemd-nspawn -M f&fedora_latest_version;</command>.</para>
 
       <para>This installs a minimal Debian unstable distribution into
       the directory <filename>~/debian-tree/</filename> and then
-      spawns a shell in a namespace container in it.</para>
+      spawns a shell from this image in a namespace container.</para>
 
       <para><command>debootstrap</command> supports
       <ulink url="https://www.debian.org">Debian</ulink>,
index 28783a15e9266099b41c59735fc9e741ae670de2..a9e322425f5dfe2fdafa597249def2446afb6a61 100644 (file)
@@ -44,7 +44,7 @@
     <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>, with
     its <command>bootctl random-seed</command> functionality.</para>
 
-    <para>When loading the random seed from disk its file is immediately updated with a new seed retrieved
+    <para>When loading the random seed from disk, the file is immediately updated with a new seed retrieved
     from the kernel, in order to ensure no two boots operate with the same random seed. This new seed is
     retrieved synchronously from the kernel, which means the service will not complete start-up until the
     random pool is fully initialized. On entropy-starved systems this may take a while. This functionality is
index 74c6ba95b1400fd36f0696fcd747182fc29b3491..ffa88baf67fe48f50754be3428fb6e62cc235e2c 100644 (file)
     available but not yet used. Specifically the following use cases are among those covered:</para>
 
     <itemizedlist>
-      <listitem><para>The root partition may be grown to cover the whole available disk space</para></listitem>
-      <listitem><para>A <filename>/home/</filename>, swap or <filename>/srv/</filename> partition can be added in</para></listitem>
-      <listitem><para>A second (or third, …) root partition may be added in, to cover A/B style setups
+      <listitem><para>The root partition may be grown to cover the whole available disk space.</para></listitem>
+      <listitem><para>A <filename>/home/</filename>, swap or <filename>/srv/</filename> partition can be
+      added.</para></listitem>
+      <listitem><para>A second (or third, …) root partition may be added, to cover A/B style setups
       where a second version of the root file system is alternatingly used for implementing update
       schemes. The deployed image would carry only a single partition ("A") but on first boot a second
       partition ("B") for this purpose is automatically created.</para></listitem>
@@ -69,7 +70,7 @@
 
     <orderedlist>
       <listitem><para>The <filename>repart.d/*.conf</filename> configuration files are loaded and parsed,
-      and ordered by filename (without the directory suffix). </para></listitem>
+      and ordered by filename (without the directory prefix).</para></listitem>
 
       <listitem><para>The partition table already existing on the block device is loaded and
       parsed.</para></listitem>
     </orderedlist>
 
     <para>As exception to the normally strictly incremental operation, when called in a special "factory
-    reset" mode <command>systemd-repart</command> may also be used to erase select existing partitions to
+    reset" mode, <command>systemd-repart</command> may also be used to erase existing partitions to
     reset an installation back to vendor defaults. This mode of operation is used when either the
     <option>--factory-reset=yes</option> switch is passed on the tool's command line, or the
     <option>systemd.factory_reset=yes</option> option specified on the kernel command line, or the
     <varname>FactoryReset</varname> EFI variable (vendor UUID
     <constant>8cf2644b-4b0b-428f-9387-6d876050dc67</constant>) is set to "yes". It alters the algorithm above
-    slightly: between the 3rd and the 4th step above the any partition marked explicitly via the
+    slightly: between the 3rd and the 4th step above any partition marked explicitly via the
     <varname>FactoryReset=</varname> boolean is deleted, and the algorithm restarted, thus immediately
     re-creating these partitions anew empty.</para>
 
       <varlistentry>
         <term><option>--definitions=</option></term>
 
-        <listitem><para>Takes a file system path. If specified the <filename>*.conf</filename> are directly
-        read from the specified directory instead of searching in
-        <filename>/usr/lib/repart.d/*.conf</filename>, <filename>/etc/repart.d/*.conf</filename>,
+        <listitem><para>Takes a file system path. If specified the <filename>*.conf</filename> files are read
+        from the specified directory instead of searching in <filename>/usr/lib/repart.d/*.conf</filename>,
+        <filename>/etc/repart.d/*.conf</filename>,
         <filename>/run/repart.d/*.conf</filename>.</para></listitem>
       </varlistentry>
 
index a6949b0c3b1ef5283a17666cee2bb82b8b0b5b86..1acf0e890512f73d0fc9e7429178722e09ee969e 100644 (file)
     <title>Options</title>
 
     <para>The following options can be configured in the
-    <literal>[Sleep]</literal> section of
+    [Sleep] section of
     <filename>/etc/systemd/sleep.conf</filename> or a
     <filename>sleep.conf.d</filename> file:</para>
 
index 58b26aad87f4e785de9e5f6d4710f808ecf85a0b..a4e18989fc106900eeada51dd70a153490c1166e 100644 (file)
@@ -16,7 +16,7 @@
   </refmeta>
   <refnamediv>
     <refname>systemd-socket-proxyd</refname>
-    <refpurpose>Bidirectionally proxy local sockets to another (possibly remote) socket.</refpurpose>
+    <refpurpose>Bidirectionally proxy local sockets to another (possibly remote) socket</refpurpose>
   </refnamediv>
   <refsynopsisdiv>
     <cmdsynopsis>
index 2310e6f52634b886fe619b49b04058f974381f80..e57000e09ad91443e04fe66892ed3e9a19f93b4e 100644 (file)
     url="https://www.freedesktop.org/wiki/Software/systemd/inhibit">Inhibitor
     interface</ulink>.</para>
 
-    <para>Note that
-    <filename>systemd-suspend.service</filename>,
-    <filename>systemd-hibernate.service</filename>, and
-    <filename>systemd-hybrid-sleep.service</filename>
-    <filename>systemd-suspend-then-hibernate.service</filename>
-    should never be executed directly. Instead, trigger system sleep
-    states with a command such as <literal>systemctl suspend</literal>
-    or similar.</para>
+    <para>Note that <filename>systemd-suspend.service</filename>,
+    <filename>systemd-hibernate.service</filename>, <filename>systemd-hybrid-sleep.service</filename>, and
+    <filename>systemd-suspend-then-hibernate.service</filename> should never be executed directly. Instead,
+    trigger system sleep with a command such as <command>systemctl suspend</command> or <command>systemctl
+    hibernate</command>.</para>
 
     <para>Internally, this service will echo a string like
     <literal>mem</literal> into <filename>/sys/power/state</filename>,
     to trigger the actual system suspend. What exactly is written
-    where can be configured in the <literal>[Sleep]</literal> section
+    where can be configured in the [Sleep] section
     of <filename>/etc/systemd/sleep.conf</filename> or a
     <filename>sleep.conf.d</filename> file. See
     <citerefentry><refentrytitle>systemd-sleep.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
index 435c3882dbeb4b1e58eb1a71f6e31329f5e86e91..8cf5666f4bdbdf465749e7edbd65d90b06a93a0a 100644 (file)
@@ -58,7 +58,7 @@
     <title>Options</title>
 
     <para>All options are configured in the
-    <literal>[Manager]</literal> section:</para>
+    [Manager] section:</para>
 
     <variablelist class='config-directives'>
 
         for details. During the first phase of the shutdown operation the system and service manager remains running
         and hence <varname>RuntimeWatchdogSec=</varname> is still honoured. In order to define a timeout on this first
         phase of system shutdown, configure <varname>JobTimeoutSec=</varname> and <varname>JobTimeoutAction=</varname>
-        in the <literal>[Unit]</literal> section of the <filename>shutdown.target</filename> unit. By default
+        in the [Unit] section of the <filename>shutdown.target</filename> unit. By default
         <varname>RuntimeWatchdogSec=</varname> defaults to 0 (off), and <varname>RebootWatchdogSec=</varname> to
         10min. <varname>KExecWatchdogSec=</varname> may be used to additionally enable the watchdog when kexec
         is being executed rather than when rebooting. Note that if the kernel does not reset the watchdog on kexec (depending
         units. See
         <citerefentry><refentrytitle>setrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry> for
         details. These settings may be overridden in individual units using the corresponding
-        <varname>LimitXXX=</varname> directives, see
-        <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>, for
-        details, and they accept the same parameter syntax. Note that these resource limits are only defaults
+        <varname>LimitXXX=</varname> directives and they accept the same parameter syntax,
+        see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        for details. Note that these resource limits are only defaults
         for units, they are not applied to the service manager process (i.e. PID 1) itself.</para></listitem>
       </varlistentry>
 
index c8a92f94a83b6a3dc65ec7892cb16ec88b6287f2..685fe74339e999067ac3ed65f34d20ff1efc8bcc 100644 (file)
@@ -18,7 +18,7 @@
   <refnamediv>
     <refname>systemd-time-wait-sync.service</refname>
     <refname>systemd-time-wait-sync</refname>
-    <refpurpose>Wait Until Kernel Time Synchronized</refpurpose>
+    <refpurpose>Wait until kernel time is synchronized</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index fd65f5da798b1377d2047a3c923e23f074f1c381..29b9bb14e14fc659a36a30d0b626f90c3fafe1bc 100644 (file)
@@ -35,9 +35,9 @@
     this unit type. See
     <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     for the common options of all unit configuration files. The common
-    configuration items are configured in the generic <literal>[Unit]</literal> and
-    <literal>[Install]</literal> sections. The automount specific configuration options
-    are configured in the <literal>[Automount]</literal> section.</para>
+    configuration items are configured in the generic [Unit] and
+    [Install] sections. The automount specific configuration options
+    are configured in the [Automount] section.</para>
 
     <para>Automount units must be named after the automount directories they control. Example: the automount point
     <filename index="false">/home/lennart</filename> must be configured in a unit file
index ae786a32980843bfbae9953bac31072890735a88..085fd62bceef8d2428d48d7b818df4ec375e2d6e 100644 (file)
@@ -36,8 +36,8 @@
     <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     for the common options of all unit configuration files. The common
     configuration items are configured in the generic
-    <literal>[Unit]</literal> and <literal>[Install]</literal>
-    sections. A separate <literal>[Device]</literal> section does not
+    [Unit] and [Install]
+    sections. A separate [Device] section does not
     exist, since no device-specific options may be configured.</para>
 
     <para>systemd will dynamically create device units for all kernel
@@ -60,7 +60,7 @@
     <para>Device units will be reloaded by systemd whenever the
     corresponding device generates a <literal>changed</literal> event.
     Other units can use <varname>ReloadPropagatedFrom=</varname> to react
-    to that event</para>
+    to that event.</para>
   </refsect1>
 
   <refsect1>
index 2fb0a8ee7067b2924adfa876fca8a94f64375a9a..d7e6caddf1e9bcea4794653cc1e900d0b693fdb1 100644 (file)
@@ -64,7 +64,7 @@
   <refsect1>
     <title>[Service] Section Options</title>
 
-      <para>The network service file contains a <literal>[Service]</literal>
+      <para>The network service file contains a [Service]
       section, which specifies a discoverable network service announced in a
       local network with Multicast DNS broadcasts.</para>
 
index 64799edd99b5bd9465ed1f5b4f58a6b86cd92636..b94bc2e4f45fe5106fe8d4c7fd368d80ac1f5579 100644 (file)
@@ -511,10 +511,11 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
       <varlistentry>
         <term><varname>AppArmorProfile=</varname></term>
 
-        <listitem><para>Takes a profile name as argument. The process executed by the unit will switch to this profile
-        when started.  Profiles must already be loaded in the kernel, or the unit will fail. This result in a non
-        operation if AppArmor is not enabled. If prefixed by <literal>-</literal>, all errors will be ignored. This
-        does not affect commands prefixed with <literal>+</literal>.</para></listitem>
+        <listitem><para>Takes a profile name as argument. The process executed by the unit will switch to
+        this profile when started. Profiles must already be loaded in the kernel, or the unit will fail. If
+        prefixed by <literal>-</literal>, all errors will be ignored. This setting has no effect if AppArmor
+        is not enabled. This setting not affect commands prefixed with <literal>+</literal>.</para>
+        </listitem>
       </varlistentry>
 
       <varlistentry>
@@ -880,7 +881,7 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
         in <varname>NUMAMask=</varname>. For more details on each policy please see,
         <citerefentry><refentrytitle>set_mempolicy</refentrytitle><manvolnum>2</manvolnum></citerefentry>. For overall
         overview of NUMA support in Linux see,
-        <citerefentry project='man-pages'><refentrytitle>numa</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+        <citerefentry project='man-pages'><refentrytitle>numa</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
         </para></listitem>
       </varlistentry>
 
@@ -1067,14 +1068,16 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
         <varname>RootDirectory=</varname> or <varname>RootImage=</varname> these paths always reside on the host and
         are mounted from there into the unit's file system namespace.</para>
 
-        <para>If <varname>DynamicUser=</varname> is used in conjunction with <varname>StateDirectory=</varname>,
-        <varname>CacheDirectory=</varname> and <varname>LogsDirectory=</varname> is slightly altered: the directories
-        are created below <filename>/var/lib/private</filename>, <filename>/var/cache/private</filename> and
+        <para>If <varname>DynamicUser=</varname> is used in conjunction with
+        <varname>StateDirectory=</varname>, the logic for <varname>CacheDirectory=</varname> and
+        <varname>LogsDirectory=</varname> is slightly altered: the directories are created below
+        <filename>/var/lib/private</filename>, <filename>/var/cache/private</filename> and
         <filename>/var/log/private</filename>, respectively, which are host directories made inaccessible to
-        unprivileged users, which ensures that access to these directories cannot be gained through dynamic user ID
-        recycling. Symbolic links are created to hide this difference in behaviour. Both from perspective of the host
-        and from inside the unit, the relevant directories hence always appear directly below
-        <filename>/var/lib</filename>, <filename>/var/cache</filename> and <filename>/var/log</filename>.</para>
+        unprivileged users, which ensures that access to these directories cannot be gained through dynamic
+        user ID recycling. Symbolic links are created to hide this difference in behaviour. Both from
+        perspective of the host and from inside the unit, the relevant directories hence always appear
+        directly below <filename>/var/lib</filename>, <filename>/var/cache</filename> and
+        <filename>/var/log</filename>.</para>
 
         <para>Use <varname>RuntimeDirectory=</varname> to manage one or more runtime directories for the unit and bind
         their lifetime to the daemon runtime. This is particularly useful for unprivileged daemons that cannot create
@@ -1238,8 +1241,8 @@ BindReadOnlyPaths=/var/lib/systemd</programlisting>
         <term><varname>PrivateTmp=</varname></term>
 
         <listitem><para>Takes a boolean argument. If true, sets up a new file system namespace for the executed
-        processes and mounts private <filename>/tmp</filename> and <filename>/var/tmp</filename> directories inside it
-        that is not shared by processes outside of the namespace. This is useful to secure access to temporary files of
+        processes and mounts private <filename>/tmp/</filename> and <filename>/var/tmp/</filename> directories inside it
+        that are not shared by processes outside of the namespace. This is useful to secure access to temporary files of
         the process, but makes sharing between processes via <filename>/tmp</filename> or <filename>/var/tmp</filename>
         impossible. If this is enabled, all temporary files created by a service in these directories will be removed
         after the service is stopped.  Defaults to false. It is possible to run two or more units within the same
@@ -1399,7 +1402,7 @@ BindReadOnlyPaths=/var/lib/systemd</programlisting>
         this option removes <constant>CAP_SYS_TIME</constant> and <constant>CAP_WAKE_ALARM</constant> from the
         capability bounding set for this unit, installs a system call filter to block calls that can set the
         clock, and <varname>DeviceAllow=char-rtc r</varname> is implied. This ensures <filename>/dev/rtc0</filename>,
-        <filename>/dev/rtc1</filename>, etc are made read only to the service. See
+        <filename>/dev/rtc1</filename>, etc. are made read-only to the service. See
         <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
         for the details about <varname>DeviceAllow=</varname>.</para>
 
@@ -1495,7 +1498,7 @@ BindReadOnlyPaths=/var/lib/systemd</programlisting>
         <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry>)
         are unaffected. Also, sockets created with <function>socketpair()</function> (which creates connected
         AF_UNIX sockets only) are unaffected. Note that this option has no effect on 32-bit x86, s390, s390x,
-        mips, mips-le, ppc, ppc-le, pcc64, ppc64-le and is ignored (but works correctly on other ABIs,
+        mips, mips-le, ppc, ppc-le, ppc64, ppc64-le and is ignored (but works correctly on other ABIs,
         including x86-64). Note that on systems supporting multiple ABIs (such as x86/x86-64) it is
         recommended to turn off alternative ABIs for services, so that they cannot be used to circumvent the
         restrictions of this option. Specifically, it is recommended to combine this option with
@@ -1803,7 +1806,7 @@ RestrictNamespaces=~cgroup net</programlisting>
               </row>
               <row>
                 <entry>@file-system</entry>
-                <entry>File system operations: opening, creating files and directories for read and write, renaming and removing them, reading file properties, or creating hard and symbolic links.</entry>
+                <entry>File system operations: opening, creating files and directories for read and write, renaming and removing them, reading file properties, or creating hard and symbolic links</entry>
               </row>
               <row>
                 <entry>@io-event</entry>
@@ -1819,7 +1822,7 @@ RestrictNamespaces=~cgroup net</programlisting>
               </row>
               <row>
                 <entry>@memlock</entry>
-                <entry>Locking of memory into RAM (<citerefentry project='man-pages'><refentrytitle>mlock</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>mlockall</refentrytitle><manvolnum>2</manvolnum></citerefentry> and related calls)</entry>
+                <entry>Locking of memory in RAM (<citerefentry project='man-pages'><refentrytitle>mlock</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>mlockall</refentrytitle><manvolnum>2</manvolnum></citerefentry> and related calls)</entry>
               </row>
               <row>
                 <entry>@module</entry>
@@ -1843,7 +1846,7 @@ RestrictNamespaces=~cgroup net</programlisting>
               </row>
               <row>
                 <entry>@process</entry>
-                <entry>Process control, execution, namespaceing operations (<citerefentry project='man-pages'><refentrytitle>clone</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>kill</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>namespaces</refentrytitle><manvolnum>7</manvolnum></citerefentry>, …</entry>
+                <entry>Process control, execution, namespaceing operations (<citerefentry project='man-pages'><refentrytitle>clone</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>kill</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>namespaces</refentrytitle><manvolnum>7</manvolnum></citerefentry>, …)</entry>
               </row>
               <row>
                 <entry>@raw-io</entry>
@@ -1871,7 +1874,7 @@ RestrictNamespaces=~cgroup net</programlisting>
               </row>
               <row>
                 <entry>@sync</entry>
-                <entry>Synchronizing files and memory to disk: (<citerefentry project='man-pages'><refentrytitle>fsync</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>msync</refentrytitle><manvolnum>2</manvolnum></citerefentry>, and related calls)</entry>
+                <entry>Synchronizing files and memory to disk (<citerefentry project='man-pages'><refentrytitle>fsync</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>msync</refentrytitle><manvolnum>2</manvolnum></citerefentry>, and related calls)</entry>
               </row>
               <row>
                 <entry>@system-service</entry>
@@ -1949,7 +1952,7 @@ SystemCallErrorNumber=EPERM</programlisting>
         manager is compiled for). If running in user mode, or in system mode, but without the
         <constant>CAP_SYS_ADMIN</constant> capability (e.g. setting <varname>User=nobody</varname>),
         <varname>NoNewPrivileges=yes</varname> is implied. By default, this option is set to the empty list, i.e. no
-        system call architecture filtering is applied.</para>
+        filtering is applied.</para>
 
         <para>If this setting is used, processes of this unit will only be permitted to call native system calls, and
         system calls of the specified architectures. For the purposes of this option, the x32 architecture is treated
@@ -2213,8 +2216,9 @@ SystemCallErrorNumber=EPERM</programlisting>
         <constant>AF_UNIX</constant> socket in the file system, as in that case only a
         single stream connection is created for both input and output.</para>
 
-        <para><option>append:<replaceable>path</replaceable></option> is similar to <option>file:<replaceable>path
-        </replaceable></option> above, but it opens the file in append mode.</para>
+        <para><option>append:<replaceable>path</replaceable></option> is similar to
+        <option>file:<replaceable>path</replaceable></option> above, but it opens the file in append mode.
+        </para>
 
         <para><option>socket</option> connects standard output to a socket acquired via socket activation. The
         semantics are similar to the same option of <varname>StandardInput=</varname>, see above.</para>
@@ -2551,7 +2555,7 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
     <varname>UnsetEnvironment=</varname> are removed again from the compiled environment variable list, immediately
     before it is passed to the executed process.</para>
 
-    <para>The following select environment variables are set or propagated by the service manager for each invoked
+    <para>The following environment variables are set or propagated by the service manager for each invoked
     process:</para>
 
     <variablelist class='environment-variables'>
@@ -2622,7 +2626,7 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
         <term><varname>$LOGS_DIRECTORY</varname></term>
         <term><varname>$CONFIGURATION_DIRECTORY</varname></term>
 
-        <listitem><para>Contains and absolute paths to the directories defined with
+        <listitem><para>Absolute paths to the directories defined with
         <varname>RuntimeDirectory=</varname>, <varname>StateDirectory=</varname>,
         <varname>CacheDirectory=</varname>, <varname>LogsDirectory=</varname>, and
         <varname>ConfigurationDirectory=</varname> when those settings are used.</para>
@@ -3228,7 +3232,7 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
           <row>
             <entry>242</entry>
             <entry><constant>EXIT_NUMA_POLICY</constant></entry>
-            <entry>Failed to set up unit's NUMA memory policy. See <varname>NUMAPolicy=</varname> and <varname>NUMAMask=</varname>above.</entry>
+            <entry>Failed to set up unit's NUMA memory policy. See <varname>NUMAPolicy=</varname> and <varname>NUMAMask=</varname> above.</entry>
           </row>
 
         </tbody>
index 31ad143113c6f190021bdc33438fa8b2318e4822..197a468f25de8c23e2a73a359fd06b3538f5b6f3 100644 (file)
     structured log entries via calls such as
     <citerefentry><refentrytitle>sd_journal_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
     They may also not be used as matches for
-    <citerefentry><refentrytitle>sd_journal_add_match</refentrytitle><manvolnum>3</manvolnum></citerefentry></para>
+    <citerefentry><refentrytitle>sd_journal_add_match</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    </para>
 
     <variablelist class='journal-directives'>
       <varlistentry>
index 5e4d430031c60ab27fc0ef1f1f1f9b1fac800540..73f61c80e4fdf28c5c9a5bf9f2a3556b8f334e9c 100644 (file)
         terminate upon receiving the initial <constant>SIGTERM</constant>
         signal. This can be achieved by configuring <varname>LimitCORE=</varname>
         and setting <varname>FinalKillSignal=</varname> to either
-        <constant>SIGQUIT</constant> or <constant>SIGABRT</constant>
+        <constant>SIGQUIT</constant> or <constant>SIGABRT</constant>.
         Defaults to <constant>SIGKILL</constant>.
         </para></listitem>
       </varlistentry>
index d7724a0a59c5696685e057e1d85ae07a1dc6efe5..dc59ee4e9a7df38f73727d2cb95e5cfd2b60ccf3 100644 (file)
@@ -64,8 +64,8 @@
     <title>[Match] Section Options</title>
 
     <para>A link file is said to match a device if all matches specified by the
-    <literal>[Match]</literal> section are satisfied. When a link file does not contain valid settings
-    in <literal>[Match]</literal> section, then the file will match all devices and
+    [Match] section are satisfied. When a link file does not contain valid settings
+    in [Match] section, then the file will match all devices and
     <command>systemd-udevd</command> warns about that. Hint: to avoid the warning and to make it clear
     that all interfaces shall be matched, add the following:
     <programlisting>OriginalName=*</programlisting>
index 07feccb54a06a38869c754888e2dc61f3a8c9634..f5a9d446e9b51d06aa2203d54d2e3c56d22c5388 100644 (file)
@@ -34,9 +34,9 @@
     this unit type. See
     <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     for the common options of all unit configuration files. The common
-    configuration items are configured in the generic <literal>[Unit]</literal> and
-    <literal>[Install]</literal> sections. The mount specific configuration options are
-    configured in the <literal>[Mount]</literal> section.</para>
+    configuration items are configured in the generic [Unit] and
+    [Install] sections. The mount specific configuration options are
+    configured in the [Mount] section.</para>
 
     <para>Additional options are listed in
     <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
index 4fc85d40ec3892468683d998fc33699f5a13b00f..324c94dbd9fe3a95380916f4bdbc2d72dfb6694c 100644 (file)
       </variablelist>
 
     <para>Note that <constant>latest</constant> may be used to denote the latest scheme known (to this
-    particular version of systemd.</para>
+    particular version of systemd).</para>
   </refsect1>
 
   <refsect1>
index 6ebaf1143cf94c2389cb602aee349c79065af348..37659326e36f9070997bc617b813c81c0fc6561d 100644 (file)
           <entry>An IPv4 over IPv4 tunnel.</entry></row>
 
           <row><entry><varname>ipvlan</varname></entry>
-          <entry>An ipvlan device is a stacked device which receives packets from its underlying device based on IP address filtering.</entry></row>
+          <entry>An IPVLAN device is a stacked device which receives packets from its underlying device based on IP address filtering.</entry></row>
 
           <row><entry><varname>ipvtap</varname></entry>
-          <entry>An ipvtap device is a stacked device which receives packets from its underlying device based on IP address filtering and can be accessed using the tap user space interface.</entry></row>
+          <entry>An IPVTAP device is a stacked device which receives packets from its underlying device based on IP address filtering and can be accessed using the tap user space interface.</entry></row>
 
           <row><entry><varname>macvlan</varname></entry>
           <entry>A macvlan device is a stacked device which receives packets from its underlying device based on MAC address filtering.</entry></row>
     <title>[Match] Section Options</title>
 
     <para>A virtual network device is only created if the
-    <literal>[Match]</literal> section matches the current
+    [Match] section matches the current
     environment, or if the section is empty. The following keys are
     accepted:</para>
 
   <refsect1>
     <title>[NetDev] Section Options</title>
 
-    <para>The <literal>[NetDev]</literal> section accepts the
+    <para>The [NetDev] section accepts the
     following keys:</para>
 
     <variablelist class='network-directives'>
         <term><varname>Name=</varname></term>
         <listitem>
           <para>The interface name used when creating the netdev.
-          This option is compulsory.</para>
+          This setting is compulsory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>Kind=</varname></term>
         <listitem>
-          <para>The netdev kind. This option is compulsory. See the
+          <para>The netdev kind. This setting is compulsory. See the
           <literal>Supported netdev kinds</literal> section for the
           valid keys.</para>
         </listitem>
       <varlistentry>
         <term><varname>MTUBytes=</varname></term>
         <listitem>
-          <para>The maximum transmission unit in bytes to set for the device. The usual suffixes K, M, G,
+          <para>The maximum transmission unit in bytes to set for the device. The usual suffixes K, M, G
           are supported and are understood to the base of 1024. For <literal>tun</literal> or
           <literal>tap</literal> devices, <varname>MTUBytes=</varname> setting is not currently supported in
-          <literal>[NetDev]</literal> section. Please specify it in <literal>[Link]</literal> section of
+          [NetDev] section. Please specify it in [Link] section of
           corresponding
           <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
           files.</para>
         <term><varname>MACAddress=</varname></term>
         <listitem>
           <para>The MAC address to use for the device. For <literal>tun</literal> or <literal>tap</literal>
-          devices, setting <varname>MACAddress=</varname> in the <literal>[NetDev]</literal> section is not
-          supported. Please specify it in <literal>[Link]</literal> section of the corresponding
+          devices, setting <varname>MACAddress=</varname> in the [NetDev] section is not
+          supported. Please specify it in [Link] section of the corresponding
           <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
           file. If this option is not set, <literal>vlan</literal> devices inherit the MAC address of the
           physical interface. For other kind of netdevs, if this option is not set, then MAC address is
   <refsect1>
     <title>[Bridge] Section Options</title>
 
-    <para>The <literal>[Bridge]</literal> section only applies for
+    <para>The [Bridge] section only applies for
     netdevs of kind <literal>bridge</literal>, and accepts the
     following keys:</para>
 
   <refsect1>
     <title>[VLAN] Section Options</title>
 
-    <para>The <literal>[VLAN]</literal> section only applies for
+    <para>The [VLAN] section only applies for
     netdevs of kind <literal>vlan</literal>, and accepts the
     following key:</para>
 
         <term><varname>Id=</varname></term>
         <listitem>
           <para>The VLAN ID to use. An integer in the range 0–4094.
-          This option is compulsory.</para>
+          This setting is compulsory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
       <varlistentry>
         <term><varname>ReorderHeader=</varname></term>
         <listitem>
-          <para>Takes a boolean. The VLAN reorder header is set VLAN interfaces behave like physical interfaces.
-          When unset, the kernel's default will be used.</para>
+          <para>Takes a boolean. When enabled, the VLAN reorder header is used and VLAN interfaces behave
+          like physical interfaces. When unset, the kernel's default will be used.</para>
         </listitem>
       </varlistentry>
     </variablelist>
   <refsect1>
     <title>[MACVLAN] Section Options</title>
 
-    <para>The <literal>[MACVLAN]</literal> section only applies for
+    <para>The [MACVLAN] section only applies for
     netdevs of kind <literal>macvlan</literal>, and accepts the
     following key:</para>
 
   <refsect1>
     <title>[MACVTAP] Section Options</title>
 
-    <para>The <literal>[MACVTAP]</literal> section applies for
+    <para>The [MACVTAP] section applies for
     netdevs of kind <literal>macvtap</literal> and accepts the
-    same key as <literal>[MACVLAN]</literal>.</para>
+    same key as [MACVLAN].</para>
   </refsect1>
 
   <refsect1>
     <title>[IPVLAN] Section Options</title>
 
-    <para>The <literal>[IPVLAN]</literal> section only applies for
+    <para>The [IPVLAN] section only applies for
     netdevs of kind <literal>ipvlan</literal>, and accepts the
     following key:</para>
 
   <refsect1>
     <title>[IPVTAP] Section Options</title>
 
-    <para>The <literal>[IPVTAP]</literal> section only applies for
+    <para>The [IPVTAP] section only applies for
     netdevs of kind <literal>ipvtap</literal> and accepts the
-    same key as <literal>[IPVLAN]</literal>.</para>
+    same key as [IPVLAN].</para>
   </refsect1>
 
   <refsect1>
     <title>[VXLAN] Section Options</title>
 
-    <para>The <literal>[VXLAN]</literal> section only applies for
+    <para>The [VXLAN] section only applies for
     netdevs of kind <literal>vxlan</literal>, and accepts the
     following keys:</para>
 
       <varlistentry>
         <term><varname>Group=</varname></term>
         <listitem>
-          <para>Configures VXLAN multicast group IP address. All members of a VXLAN must use the same multicast group address.</para>
+          <para>Configures VXLAN multicast group IP address. All members of a VXLAN must use the same
+          multicast group address.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
       <varlistentry>
         <term><varname>L3MissNotification=</varname></term>
         <listitem>
-          <para>Takes a boolean. When true, enables netlink IP address miss
-          notifications.</para>
+          <para>Takes a boolean. When true, enables netlink IP address miss notifications.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
   <refsect1>
     <title>[GENEVE] Section Options</title>
 
-    <para>The <literal>[GENEVE]</literal> section only applies for
+    <para>The [GENEVE] section only applies for
     netdevs of kind <literal>geneve</literal>, and accepts the
     following keys:</para>
 
       <varlistentry>
         <term><varname>TTL=</varname></term>
         <listitem>
-          <para>Accepts the same key in <literal>[VXLAN]</literal> section except when unset or
-          set to 0, the kernel's default will be used meaning that packets TTL will be set from
+          <para>Accepts the same values as in the [VXLAN] section, except that when unset
+          or set to 0, the kernel's default will be used, meaning that packet TTL will be set from
           <filename>/proc/sys/net/ipv4/ip_default_ttl</filename>.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>UDPChecksum=</varname></term>
         <listitem>
-          <para>Takes a boolean. When true, specifies if UDP checksum is calculated for transmitted packets over IPv4.</para>
+          <para>Takes a boolean. When true, specifies that UDP checksum is calculated for transmitted packets
+          over IPv4.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
       <varlistentry>
         <term><varname>IPDoNotFragment=</varname></term>
         <listitem>
-          <para>Accepts the same key in <literal>[VXLAN]</literal> section.</para>
+          <para>Accepts the same key in [VXLAN] section.</para>
         </listitem>
       </varlistentry>
     </variablelist>
   <refsect1>
     <title>[L2TP] Section Options</title>
 
-    <para>The <literal>[L2TP]</literal> section only applies for
+    <para>The [L2TP] section only applies for
     netdevs of kind <literal>l2tp</literal>, and accepts the
     following keys:</para>
 
       <varlistentry>
         <term><varname>TunnelId=</varname></term>
         <listitem>
-          <para>Specifies the tunnel id. The value used must match the <literal>PeerTunnelId=</literal> value being used at the peer.
-          Ranges a number between 1 and 4294967295). This option is compulsory.</para>
+          <para>Specifies the tunnel identifier. Takes an number in the range 1–4294967295. The value used
+          must match the <literal>PeerTunnelId=</literal> value being used at the peer. This setting is
+          compulsory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>PeerTunnelId=</varname></term>
         <listitem>
-          <para>Specifies the peer tunnel id. The value used must match the <literal>PeerTunnelId=</literal> value being used at the peer.
-          Ranges a number between 1 and 4294967295). This option is compulsory.</para>
+          <para>Specifies the peer tunnel id. Takes a number in the range 1—4294967295. The value used must
+          match the <literal>PeerTunnelId=</literal> value being used at the peer. This setting is
+          compulsory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>Remote=</varname></term>
         <listitem>
-          <para>Specifies the IP address of the remote peer. This option is compulsory.</para>
+          <para>Specifies the IP address of the remote peer. This setting is compulsory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
       <varlistentry>
         <term><varname>EncapsulationType=</varname></term>
         <listitem>
-          <para>Specifies the encapsulation type of the tunnel. Takes one of <literal>udp</literal> or <literal>ip</literal>.</para>
+          <para>Specifies the encapsulation type of the tunnel. Takes one of <literal>udp</literal> or
+          <literal>ip</literal>.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>UDPSourcePort=</varname></term>
         <listitem>
-          <para>Specifies the UDP source port to be used for the tunnel. When UDP encapsulation is selected it's mandotory. Ignored when ip
-          encapsulation is selected.</para>
+          <para>Specifies the UDP source port to be used for the tunnel. When UDP encapsulation is selected
+          it's mandatory. Ignored when IP encapsulation is selected.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>UDPDestinationPort=</varname></term>
         <listitem>
-          <para>Specifies destination port. When UDP encapsulation is selected it's mandotory. Ignored when ip
+          <para>Specifies destination port. When UDP encapsulation is selected it's mandatory. Ignored when IP
           encapsulation is selected.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>UDPChecksum=</varname></term>
         <listitem>
-          <para>Takes a boolean. When true, specifies if UDP checksum is calculated for transmitted packets over IPv4.</para>
+          <para>Takes a boolean. When true, specifies that UDP checksum is calculated for transmitted packets
+          over IPv4.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
   <refsect1>
     <title>[L2TPSession] Section Options</title>
 
-    <para>The <literal>[L2TPSession]</literal> section only applies for
+    <para>The [L2TPSession] section only applies for
     netdevs of kind <literal>l2tp</literal>, and accepts the
     following keys:</para>
     <variablelist class='network-directives'>
       <varlistentry>
         <term><varname>Name=</varname></term>
         <listitem>
-          <para>Specifies the name of the session. This option is compulsory.</para>
+          <para>Specifies the name of the session. This setting is compulsory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>SessionId=</varname></term>
         <listitem>
-          <para>Specifies the session id. The value used must match the <literal>SessionId=</literal> value being used at the peer.
-          Ranges a number between 1 and 4294967295). This option is compulsory.</para>
+          <para>Specifies the session identifier. Takes an number in the range 1–4294967295. The value used
+          must match the <literal>SessionId=</literal> value being used at the peer. This setting is
+          compulsory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>PeerSessionId=</varname></term>
         <listitem>
-          <para>Specifies the peer session id. The value used must match the <literal>PeerSessionId=</literal> value being used at the peer.
-          Ranges a number between 1 and 4294967295). This option is compulsory.</para>
+          <para>Specifies the peer session identifier. Takes an number in the range 1–4294967295.
+          The value used must match the <literal>PeerSessionId=</literal> value being used at the peer.
+          This setting is compulsory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
   <refsect1>
     <title>[MACsec] Section Options</title>
 
-    <para>The <literal>[MACsec]</literal> section only applies for network devices of kind
+    <para>The [MACsec] section only applies for network devices of kind
     <literal>macsec</literal>, and accepts the following keys:</para>
 
     <variablelist class='network-directives'>
 
   <refsect1>
     <title>[MACsecReceiveChannel] Section Options</title>
-    <para>The <literal>[MACsecReceiveChannel]</literal> section only applies for network devices of
+    <para>The [MACsecReceiveChannel] section only applies for network devices of
     kind <literal>macsec</literal>, and accepts the following keys:</para>
 
     <variablelist class='network-directives'>
         <term><varname>MACAddress=</varname></term>
         <listitem>
           <para>Specifies the MAC address to be used for the MACsec receive channel. The MAC address
-          used to make secure channel identifier (SCI). This option is compulsory, and is not set by
+          used to make secure channel identifier (SCI). This setting is compulsory, and is not set by
           default.</para>
         </listitem>
       </varlistentry>
   <refsect1>
     <title>[MACsecTransmitAssociation] Section Options</title>
 
-    <para>The <literal>[MACsecTransmitAssociation]</literal> section only applies for network devices
+    <para>The [MACsecTransmitAssociation] section only applies for network devices
     of kind <literal>macsec</literal>, and accepts the following keys:</para>
 
     <variablelist class='network-directives'>
         <term><varname>Key=</varname></term>
         <listitem>
           <para>Specifies the encryption key used in the transmission channel. The same key must be
-          configured on the peer’s matching receive channel. This option is compulsory, and is not set
+          configured on the peer’s matching receive channel. This setting is compulsory, and is not set
           by default. Takes a 128-bit key encoded in a hexadecimal string, for example
           <literal>dffafc8d7b9a43d5b9a3dfbbf6a30c16</literal>.</para>
         </listitem>
         <term><varname>UseForEncoding=</varname></term>
         <listitem>
           <para>Takes a boolean. If enabled, then the security association is used for encoding. Only
-          one <literal>[MACsecTransmitAssociation]</literal> section can enable this option. When enabled,
+          one [MACsecTransmitAssociation] section can enable this option. When enabled,
           <varname>Activate=yes</varname> is implied. Defaults to unset.</para>
         </listitem>
       </varlistentry>
   <refsect1>
     <title>[MACsecReceiveAssociation] Section Options</title>
 
-    <para>The <literal>[MACsecReceiveAssociation]</literal> section only applies for
+    <para>The [MACsecReceiveAssociation] section only applies for
     network devices of kind <literal>macsec</literal>, and accepts the
     following keys:</para>
 
       <varlistentry>
         <term><varname>Port=</varname></term>
         <listitem>
-          <para>Accepts the same key in <literal>[MACsecReceiveChannel]</literal> section.</para>
+          <para>Accepts the same key in [MACsecReceiveChannel] section.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>MACAddress=</varname></term>
         <listitem>
-          <para>Accepts the same key in <literal>[MACsecReceiveChannel]</literal> section.</para>
+          <para>Accepts the same key in [MACsecReceiveChannel] section.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>PacketNumber=</varname></term>
         <listitem>
-          <para>Accepts the same key in <literal>[MACsecTransmitAssociation]</literal> section.</para>
+          <para>Accepts the same key in [MACsecTransmitAssociation] section.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>KeyId=</varname></term>
         <listitem>
-          <para>Accepts the same key in <literal>[MACsecTransmitAssociation]</literal> section.</para>
+          <para>Accepts the same key in [MACsecTransmitAssociation] section.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>Key=</varname></term>
         <listitem>
-          <para>Accepts the same key in <literal>[MACsecTransmitAssociation]</literal> section.</para>
+          <para>Accepts the same key in [MACsecTransmitAssociation] section.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>KeyFile=</varname></term>
         <listitem>
-          <para>Accepts the same key in <literal>[MACsecTransmitAssociation]</literal> section.</para>
+          <para>Accepts the same key in [MACsecTransmitAssociation] section.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>Activate=</varname></term>
         <listitem>
-          <para>Accepts the same key in <literal>[MACsecTransmitAssociation]</literal> section.</para>
+          <para>Accepts the same key in [MACsecTransmitAssociation] section.</para>
         </listitem>
       </varlistentry>
     </variablelist>
   <refsect1>
     <title>[Tunnel] Section Options</title>
 
-    <para>The <literal>[Tunnel]</literal> section only applies for
+    <para>The [Tunnel] section only applies for
     netdevs of kind
     <literal>ipip</literal>,
     <literal>sit</literal>,
           <para>A fixed Time To Live N on tunneled packets. N is a
           number in the range 1–255. 0 is a special value meaning that
           packets inherit the TTL value. The default value for IPv4
-          tunnels is: inherit. The default value for IPv6 tunnels is
+          tunnels is 0 (inherit). The default value for IPv6 tunnels is
           64.</para>
         </listitem>
       </varlistentry>
           both directions (<varname>InputKey=</varname> and <varname>OutputKey=</varname>).
           The <varname>Key=</varname> is either a number or an IPv4 address-like dotted quad.
           It is used as mark-configured SAD/SPD entry as part of the lookup key (both in data
-          and control path) in ip xfrm (framework used to implement IPsec protocol).
+          and control path) in IP XFRM (framework used to implement IPsec protocol).
           See <ulink url="http://man7.org/linux/man-pages/man8/ip-xfrm.8.html">
           ip-xfrm — transform configuration</ulink> for details. It is only used for VTI/VTI6,
           GRE, GRETAP, and ERSPAN tunnels.</para>
       <varlistentry>
         <term><varname>Encapsulation=</varname></term>
         <listitem>
-          <para>Accepts the same key as in the <literal>[FooOverUDP]</literal> section.</para>
+          <para>Accepts the same key as in the [FooOverUDP] section.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
   <refsect1>
     <title>[FooOverUDP] Section Options</title>
 
-    <para>The <literal>[FooOverUDP]</literal> section only applies for
+    <para>The [FooOverUDP] section only applies for
     netdevs of kind <literal>fou</literal> and accepts the
     following keys:</para>
 
       <varlistentry>
         <term><varname>Encapsulation=</varname></term>
         <listitem>
-          <para>Specifies the encapsulation mechanism used to store networking packets of various protocols inside the UDP packets. Supports the following values:
+          <para>Specifies the encapsulation mechanism used to store networking packets of various protocols
+          inside the UDP packets. Supports the following values:
 
-          <literal>FooOverUDP</literal> provides the simplest no frills model of UDP encapsulation, it simply encapsulates
-          packets directly in the UDP payload.
-          <literal>GenericUDPEncapsulation</literal> is a generic and extensible encapsulation, it allows encapsulation of packets for any IP
-          protocol and optional data as part of the encapsulation.
-          For more detailed information see <ulink url="https://lwn.net/Articles/615044">Generic UDP Encapsulation</ulink>.
-          Defaults to <literal>FooOverUDP</literal>.
+          <literal>FooOverUDP</literal> provides the simplest no frills model of UDP encapsulation, it simply
+          encapsulates packets directly in the UDP payload. <literal>GenericUDPEncapsulation</literal> is a
+          generic and extensible encapsulation, it allows encapsulation of packets for any IP protocol and
+          optional data as part of the encapsulation. For more detailed information see <ulink
+          url="https://lwn.net/Articles/615044">Generic UDP Encapsulation</ulink>. Defaults to
+          <literal>FooOverUDP</literal>.
           </para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>Port=</varname></term>
         <listitem>
-          <para>Specifies the port number, where the IP encapsulation packets will arrive. Please take note that the packets
-          will arrive with the encapsulation will be removed. Then they will be manually fed back into the network stack, and sent ahead
-          for delivery to the real destination. This option is mandatory.</para>
+          <para>Specifies the port number, where the IP encapsulation packets will arrive. Please take note
+          that the packets will arrive with the encapsulation will be removed. Then they will be manually fed
+          back into the network stack, and sent ahead for delivery to the real destination. This option is
+          mandatory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>PeerPort=</varname></term>
         <listitem>
-          <para>Specifies the peer port number. Defaults to unset. Note that when peer port is set <literal>Peer=</literal> address is mandotory.</para>
+          <para>Specifies the peer port number. Defaults to unset. Note that when peer port is set
+          <literal>Peer=</literal> address is mandatory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
       <varlistentry>
         <term><varname>Peer=</varname></term>
         <listitem>
-          <para>Configures peer IP address. Note that when peer address is set <literal>PeerPort=</literal> is mandotory.</para>
+          <para>Configures peer IP address. Note that when peer address is set <literal>PeerPort=</literal>
+          is mandatory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
   <refsect1>
     <title>[Peer] Section Options</title>
 
-    <para>The <literal>[Peer]</literal> section only applies for
+    <para>The [Peer] section only applies for
     netdevs of kind <literal>veth</literal> and accepts the
     following keys:</para>
 
         <term><varname>Name=</varname></term>
         <listitem>
           <para>The interface name used when creating the netdev.
-          This option is compulsory.</para>
+          This setting is compulsory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
   <refsect1>
     <title>[VXCAN] Section Options</title>
 
-    <para>The <literal>[VXCAN]</literal> section only applies for
+    <para>The [VXCAN] section only applies for
     netdevs of kind <literal>vxcan</literal> and accepts the
     following key:</para>
 
         <term><varname>Peer=</varname></term>
         <listitem>
           <para>The peer interface name used when creating the netdev.
-          This option is compulsory.</para>
+          This setting is compulsory.</para>
         </listitem>
       </varlistentry>
     </variablelist>
   <refsect1>
     <title>[Tun] Section Options</title>
 
-    <para>The <literal>[Tun]</literal> section only applies for
+    <para>The [Tun] section only applies for
     netdevs of kind <literal>tun</literal>, and accepts the following
     keys:</para>
 
   <refsect1>
     <title>[Tap] Section Options</title>
 
-    <para>The <literal>[Tap]</literal> section only applies for
+    <para>The [Tap] section only applies for
     netdevs of kind <literal>tap</literal>, and accepts the same keys
-    as the <literal>[Tun]</literal> section.</para>
+    as the [Tun] section.</para>
   </refsect1>
 
   <refsect1>
     <title>[WireGuard] Section Options</title>
 
-    <para>The <literal>[WireGuard]</literal> section accepts the following
+    <para>The [WireGuard] section accepts the following
     keys:</para>
 
     <variablelist class='network-directives'>
   <refsect1>
     <title>[WireGuardPeer] Section Options</title>
 
-    <para>The <literal>[WireGuardPeer]</literal> section accepts the following
+    <para>The [WireGuardPeer] section accepts the following
     keys:</para>
 
     <variablelist class='network-directives'>
   <refsect1>
     <title>[Bond] Section Options</title>
 
-    <para>The <literal>[Bond]</literal> section accepts the following
+    <para>The [Bond] section accepts the following
     key:</para>
 
     <variablelist class='network-directives'>
       <varlistentry>
         <term><varname>AdActorSystemPriority=</varname></term>
         <listitem>
-          <para>Specifies the 802.3ad actor system priority. Ranges [1-65535].</para>
+          <para>Specifies the 802.3ad actor system priority. Takes a number in the range 1—65535.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>AdUserPortKey=</varname></term>
         <listitem>
-          <para>Specifies the 802.3ad user defined portion of the port key. Ranges [0-1023].</para>
+          <para>Specifies the 802.3ad user defined portion of the port key. Takes a number in the range
+          0–1023.</para>
         </listitem>
       </varlistentry>
 
   <refsect1>
     <title>[Xfrm] Section Options</title>
 
-    <para>The <literal>[Xfrm]</literal> section accepts the following
+    <para>The [Xfrm] section accepts the following
     keys:</para>
 
     <variablelist class='network-directives'>
     </variablelist>
 
     <para>For more detail information see
-      <ulink url="https://lwn.net/Articles/757391">
-        Virtual xfrm interfaces</ulink></para>
+      <ulink url="https://lwn.net/Articles/757391">Virtual XFRM Interfaces</ulink>.</para>
   </refsect1>
 
   <refsect1>
     <title>[VRF] Section Options</title>
-    <para>The <literal>[VRF]</literal> section only applies for
+    <para>The [VRF] section only applies for
     netdevs of kind <literal>vrf</literal> and accepts the
     following key:</para>
 
       <varlistentry>
         <term><varname>Table=</varname></term>
         <listitem>
-          <para>The numeric routing table identifier. This option is compulsory.</para>
+          <para>The numeric routing table identifier. This setting is compulsory.</para>
         </listitem>
       </varlistentry>
     </variablelist>
index dce81692dfe01060718a16bea6d35b55a47d5e50..88f00bdca633f6f5e8c54907c11e26921069c3d9 100644 (file)
   <refsect1>
     <title>[Match] Section Options</title>
 
-      <para>The network file contains a <literal>[Match]</literal>
-      section, which determines if a given network file may be applied
-      to a given device; and a <literal>[Network]</literal> section
-      specifying how the device should be configured. The first (in
-      lexical order) of the network files that matches a given device
-      is applied, all later files are ignored, even if they match as
-      well.</para>
-
-      <para>A network file is said to match a network interface if all matches specified by the
-      <literal>[Match]</literal> section are satisfied. When a network file does not contain valid
-      settings in <literal>[Match]</literal> section, then the file will match all interfaces and
-      <command>systemd-networkd</command> warns about that. Hint: to avoid the warning and to make it
-      clear that all interfaces shall be matched, add the following:
-      <programlisting>Name=*</programlisting>
-      The following keys are accepted:</para>
+      <para>The network file contains a [Match] section, which determines if a given network file may be
+      applied to a given device; and a [Network] section specifying how the device should be configured. The
+      first (in lexical order) of the network files that matches a given device is applied, all later files
+      are ignored, even if they match as well.</para>
+
+      <para>A network file is said to match a network interface if all matches specified by the [Match]
+      section are satisfied. When a network file does not contain valid settings in [Match] section, then the
+      file will match all interfaces and <command>systemd-networkd</command> warns about that. Hint: to avoid
+      the warning and to make it clear that all interfaces shall be matched, add the following:
+      <programlisting>Name=*</programlisting> The following keys are accepted:</para>
 
       <variablelist class='network-directives'>
         <xi:include href="systemd.link.xml" xpointer="mac-address" />
           <listitem>
             <para>A whitespace-separated list of hardware address of the currently connected wireless
             LAN. Use full colon-, hyphen- or dot-delimited hexadecimal. See the example in
-            <varname>MACAddress=</varname>. This option may appear more than one, in which case the
-            lists are merged. If the empty string is assigned to this option, the list of BSSID defined
-            prior to this is reset.</para>
+            <varname>MACAddress=</varname>. This option may appear more than once, in which case the
+            lists are merged. If the empty string is assigned to this option, the list is reset.</para>
           </listitem>
         </varlistentry>
 
   <refsect1>
     <title>[Link] Section Options</title>
 
-    <para> The <literal>[Link]</literal> section accepts the following keys:</para>
+    <para> The [Link] section accepts the following keys:</para>
 
     <variablelist class='network-directives'>
       <varlistentry>
           <para>Link groups are similar to port ranges found in managed switches.
           When network interfaces are added to a numbered group, operations on
           all the interfaces from that group can be performed at once. An unsigned
-          integer ranges 0 to 4294967294. Default to unset.</para>
+          integer in the range 0—4294967294. Defaults to unset.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
     </variablelist>
   </refsect1>
 
+  <refsect1>
+      <title>[SR-IOV] Section Options</title>
+      <para>The [SR-IOV] section accepts the following keys. Specify several [SR-IOV] sections to configure
+      several SR-IOVs. SR-IOV provides the ability to partition a single physical PCI resource into virtual
+      PCI functions which can then be injected into a VM. In the case of network VFs, SR-IOV improves
+      north-south network performance (that is, traffic with endpoints outside the host machine) by allowing
+      traffic to bypass the host machine’s network stack.</para>
+
+      <variablelist class='network-directives'>
+        <varlistentry>
+          <term><varname>VirtualFunction=</varname></term>
+          <listitem>
+            <para>Specifies a Virtual Function (VF), lightweight PCIe function designed solely to move data
+            in and out. Takes an unsigned integer in the range 0..2147483646. This option is compulsory.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>VLANId=</varname></term>
+          <listitem>
+            <para>Specifies VLAN ID of the virtual function. Takes an unsigned integer in the range 1..4095.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>QualityOfService=</varname></term>
+          <listitem>
+            <para>Specifies quality of service of the virtual function. Takes an unsigned integer in the range 1..4294967294.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>VLANProtocol=</varname></term>
+          <listitem>
+            <para>Specifies VLAN protocol of the virtual function. Takes <literal>802.1Q</literal> or
+            <literal>802.1ad</literal>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>MACSpoofCheck=</varname></term>
+          <listitem>
+            <para>Takes a boolean. Controls the MAC spoof checking. When unset, the kernel's default will be used.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>QueryReceiveSideScaling=</varname></term>
+          <listitem>
+            <para>Takes a boolean. Toggle the ability of querying the receive side scaling (RSS)
+            configuration of the virtual function (VF). The VF RSS information like RSS hash key may be
+            considered sensitive on some devices where this information is shared between VF and the
+            physical function (PF). When unset, the kernel's default will be used.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>Trust=</varname></term>
+          <listitem>
+            <para>Takes a boolean. Allows to set trust mode of the virtual function (VF). When set, VF
+            users can set a specific feature which may impact security and/or performance. When unset,
+            the kernel's default will be used.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>LinkState=</varname></term>
+          <listitem>
+            <para>Allows to set the link state of the virtual function (VF). Takes a boolean or a
+            special value <literal>auto</literal>. Setting to <literal>auto</literal> means a
+            reflection of the physical function (PF) link state, <literal>yes</literal> lets the VF to
+            communicate with other VFs on this host even if the PF link state is down,
+            <literal>no</literal> causes the hardware to drop any packets sent by the VF. When unset,
+            the kernel's default will be used.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>MACAddress=</varname></term>
+          <listitem>
+            <para>Specifies the MAC address for the virtual function.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>[Network] Section Options</title>
 
-      <para>The <literal>[Network]</literal> section accepts the following keys:</para>
+      <para>The [Network] section accepts the following keys:</para>
 
       <variablelist class='network-directives'>
         <varlistentry>
             specified through DHCP is not used for name resolution.
             See option <option>UseDomains=</option> below.</para>
 
-            <para>See the <literal>[DHCPv4]</literal> or <literal>[DHCPv6]</literal> section below for
-            further configuration options for the DHCP client support.</para>
+            <para>See the [DHCPv4] or [DHCPv6] sections below for further configuration options for the DHCP
+            client support.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
           <term><varname>DHCPServer=</varname></term>
           <listitem>
             <para>Takes a boolean. If set to <literal>yes</literal>, DHCPv4 server will be started. Defaults
-            to <literal>no</literal>. Further settings for the DHCP
-            server may be set in the <literal>[DHCPServer]</literal>
+            to <literal>no</literal>. Further settings for the DHCP server may be set in the [DHCPServer]
             section described below.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
           <term><varname>DNSSEC=</varname></term>
           <listitem>
-            <para>Takes a boolean. or
-            <literal>allow-downgrade</literal>. When true, enables
-            <ulink
-            url="https://tools.ietf.org/html/rfc4033">DNSSEC</ulink>
+            <para>Takes a boolean or <literal>allow-downgrade</literal>. When true, enables
+            <ulink url="https://tools.ietf.org/html/rfc4033">DNSSEC</ulink>
             DNS validation support on the link. When set to
             <literal>allow-downgrade</literal>, compatibility with
             non-DNSSEC capable networks is increased, by automatically
           forwarding is enabled, and to enable it otherwise. Cannot be enabled on bond devices and when link
           local addressing is disabled.</para>
 
-          <para>Further settings for the IPv6 RA support may be configured in the
-          <literal>[IPv6AcceptRA]</literal> section, see below.</para>
+          <para>Further settings for the IPv6 RA support may be configured in the [IPv6AcceptRA] section, see
+          below.</para>
 
           <para>Also see <ulink
           url="https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt">ip-sysctl.txt</ulink> in the kernel
           <term><varname>IPv4ProxyARP=</varname></term>
           <listitem><para>Takes a boolean. Configures proxy ARP for IPv4. Proxy ARP is the technique in which one host,
           usually a router, answers ARP requests intended for another machine. By "faking" its identity,
-          the router accepts responsibility for routing packets to the "real" destination. (see <ulink
+          the router accepts responsibility for routing packets to the "real" destination. See <ulink
           url="https://tools.ietf.org/html/rfc1027">RFC 1027</ulink>.
           When unset, the kernel's default will be used.
         </para></listitem>
         </varlistentry>
         <varlistentry>
           <term><varname>IPv6PrefixDelegation=</varname></term>
-          <listitem><para>Whether to enable or disable Router Advertisement sending on a link.
-          Allowed values are <literal>static</literal> which distributes prefixes as defined in
-          the <literal>[IPv6PrefixDelegation]</literal> and any <literal>[IPv6Prefix]</literal>
-          sections, <literal>dhcpv6</literal> which requests prefixes using a DHCPv6 client
-          configured for another link and any values configured in the
-          <literal>[IPv6PrefixDelegation]</literal> section while ignoring all static prefix
-          configuration sections, <literal>yes</literal> which uses both static configuration
-          and DHCPv6, and <literal>false</literal> which turns off IPv6 prefix delegation
-          altogether. Defaults to <literal>false</literal>. See the
-          <literal>[IPv6PrefixDelegation]</literal> and the <literal>[IPv6Prefix]</literal>
-          sections for more configuration options.
-          </para></listitem>
+          <listitem><para>Whether to enable or disable Router Advertisement sending on a link.  Allowed
+          values are <literal>static</literal> which distributes prefixes as defined in the
+          [IPv6PrefixDelegation] and any [IPv6Prefix] sections, <literal>dhcpv6</literal> which requests
+          prefixes using a DHCPv6 client configured for another link and any values configured in the
+          [IPv6PrefixDelegation] section while ignoring all static prefix configuration sections,
+          <literal>yes</literal> which uses both static configuration and DHCPv6, and
+          <literal>false</literal> which turns off IPv6 prefix delegation altogether. Defaults to
+          <literal>false</literal>. See the [IPv6PrefixDelegation] and the [IPv6Prefix] sections for more
+          configuration options.</para></listitem>
         </varlistentry>
         <varlistentry>
           <term><varname>IPv6PDSubnetId=</varname></term>
   <refsect1>
     <title>[Address] Section Options</title>
 
-      <para>An <literal>[Address]</literal> section accepts the
-      following keys. Specify several <literal>[Address]</literal>
+      <para>An [Address] section accepts the following keys. Specify several [Address]
       sections to configure several addresses.</para>
 
       <variablelist class='network-directives'>
         <varlistentry>
           <term><varname>Address=</varname></term>
           <listitem>
-            <para>As in the <literal>[Network]</literal> section. This key is mandatory. Each
-            <literal>[Address]</literal> section can contain one <varname>Address=</varname> setting.</para>
+            <para>As in the [Network] section. This key is mandatory. Each [Address] section can contain one
+            <varname>Address=</varname> setting.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
           <term><varname>Scope=</varname></term>
           <listitem>
             <para>The scope of the address, which can be <literal>global</literal>,
-            <literal>link</literal> or <literal>host</literal> or an unsigned integer ranges 0 to 255.
+            <literal>link</literal> or <literal>host</literal> or an unsigned integer in the range 0—255.
             Defaults to <literal>global</literal>.</para>
           </listitem>
         </varlistentry>
 
   <refsect1>
     <title>[Neighbor] Section Options</title>
-      <para>A <literal>[Neighbor]</literal> section accepts the
-      following keys. The neighbor section adds a permanent, static
-      entry to the neighbor table (IPv6) or ARP table (IPv4) for
-      the given hardware address on the links matched for the network.
-      Specify several <literal>[Neighbor]</literal> sections to configure
-      several static neighbors.</para>
+      <para>A [Neighbor] section accepts the following keys. The neighbor section adds a permanent, static
+      entry to the neighbor table (IPv6) or ARP table (IPv4) for the given hardware address on the links
+      matched for the network. Specify several [Neighbor] sections to configure several static neighbors.
+      </para>
 
       <variablelist class='network-directives'>
         <varlistentry>
     <refsect1>
     <title>[IPv6AddressLabel] Section Options</title>
 
-      <para>An <literal>[IPv6AddressLabel]</literal> section accepts the
-      following keys. Specify several <literal>[IPv6AddressLabel]</literal>
-      sections to configure several address labels. IPv6 address labels are
-      used for address selection. See <ulink url="https://tools.ietf.org/html/rfc3484">RFC 3484</ulink>.
-      Precedence is managed by userspace, and only the label itself is stored in the kernel</para>
+      <para>An [IPv6AddressLabel] section accepts the following keys. Specify several [IPv6AddressLabel]
+      sections to configure several address labels. IPv6 address labels are used for address selection. See
+      <ulink url="https://tools.ietf.org/html/rfc3484">RFC 3484</ulink>. Precedence is managed by userspace,
+      and only the label itself is stored in the kernel</para>
 
       <variablelist class='network-directives'>
         <varlistentry>
           <term><varname>Label=</varname></term>
           <listitem>
-            <para> The label for the prefix (an unsigned integer) ranges 0 to 4294967294.
-            0xffffffff is reserved. This key is mandatory.</para>
+            <para>The label for the prefix, an unsigned integer in the range 0–4294967294.
+            0xffffffff is reserved. This setting is mandatory.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
  <refsect1>
     <title>[RoutingPolicyRule] Section Options</title>
 
-      <para>An <literal>[RoutingPolicyRule]</literal> section accepts the
-      following keys. Specify several <literal>[RoutingPolicyRule]</literal>
+      <para>An [RoutingPolicyRule] section accepts the following keys. Specify several [RoutingPolicyRule]
       sections to configure several rules.</para>
 
       <variablelist class='network-directives'>
         <varlistentry>
           <term><varname>TypeOfService=</varname></term>
           <listitem>
-            <para>Specifies the type of service to match a number between 0 to 255.</para>
+            <para>Takes a number between 0 and 255 that specifies the type of service to match.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
 
     <refsect1>
       <title>[NextHop] Section Options</title>
-      <para>The <literal>[NextHop]</literal> section accepts the
-      following keys. Specify several <literal>[NextHop]</literal>
-      sections to configure several nexthop. Nexthop is used to manipulate entries in the kernel's nexthop
-      tables.</para>
+      <para>The [NextHop] section is used to manipulate entries in the kernel's "nexthop" tables.  The
+      [NextHop] section accepts the following keys. Specify several [NextHop] sections to configure several
+      hops.</para>
 
       <variablelist class='network-directives'>
         <varlistentry>
           <term><varname>Gateway=</varname></term>
           <listitem>
-            <para>As in the <literal>[Network]</literal> section. This is mandatory.</para>
+            <para>As in the [Network] section. This is mandatory.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
 
   <refsect1>
     <title>[Route] Section Options</title>
-      <para>The <literal>[Route]</literal> section accepts the
-      following keys. Specify several <literal>[Route]</literal>
-      sections to configure several routes.</para>
+      <para>The [Route] section accepts the following keys. Specify several [Route] sections to configure
+      several routes.</para>
 
       <variablelist class='network-directives'>
         <varlistentry>
           <term><varname>IPv6Preference=</varname></term>
           <listitem>
             <para>Specifies the route preference as defined in <ulink
-            url="https://tools.ietf.org/html/rfc4191">RFC4191</ulink> for Router Discovery messages.
-            Which can be one of <literal>low</literal> the route has a lowest priority,
-            <literal>medium</literal> the route has a default priority or
-            <literal>high</literal> the route has a highest priority.</para>
+            url="https://tools.ietf.org/html/rfc4191">RFC 4191</ulink> for Router Discovery messages.  Which
+            can be one of <literal>low</literal> the route has a lowest priority, <literal>medium</literal>
+            the route has a default priority or <literal>high</literal> the route has a highest priority.
+            </para>
           </listitem>
         </varlistentry>
         <varlistentry>
 
   <refsect1>
     <title>[DHCPv4] Section Options</title>
-      <para>The <literal>[DHCPv4]</literal> section configures the
-      DHCPv4 client, if it is enabled with the
+      <para>The [DHCPv4] section configures the DHCPv4 client, if it is enabled with the
       <varname>DHCP=</varname> setting described above:</para>
 
       <variablelist class='network-directives'>
             <para>The table identifier for DHCP routes (a number between 1 and 4294967295, or 0 to unset).
             The table can be retrieved using <command>ip route show table <replaceable>num</replaceable></command>.
             </para>
-            <para>When used in combination with <varname>VRF=</varname> the
-            VRF's routing table is used unless this parameter is specified.
+            <para>When used in combination with <varname>VRF=</varname>, the
+            VRF's routing table is used when this parameter is not specified.
             </para>
           </listitem>
         </varlistentry>
         <varlistentry>
           <term><varname>SendDecline=</varname></term>
           <listitem>
-            <para>A boolean. When <literal>true</literal>, DHCPv4 clients receives IP address from DHCP server.
-            After new IP is received, DHCPv4 performs IPv4 Duplicate Address Detection. If duplicate use of IP is detected
-            the DHCPv4 client rejects the IP by sending a DHCPDECLINE packet DHCP clients try to obtain an IP address again.
-            See <ulink url="https://tools.ietf.org/html/rfc5227">RFC 5224</ulink>.
-            Defaults to <literal>unset</literal>.</para>
+            <para>A boolean. When <literal>true</literal>, the DHCPv4 client receives the IP address from the
+            DHCP server.  After a new IP is received, the DHCPv4 client performs IPv4 Duplicate Address
+            Detection. If duplicate use is detected, the DHCPv4 client rejects the IP by sending a
+            DHCPDECLINE packet and tries to obtain an IP address again. See <ulink
+            url="https://tools.ietf.org/html/rfc5227">RFC 5224</ulink>. Defaults to
+            <literal>unset</literal>.</para>
           </listitem>
         </varlistentry>
 
 
   <refsect1>
     <title>[DHCPv6] Section Options</title>
-      <para>The <literal>[DHCPv6]</literal> section configures the DHCPv6 client, if it is enabled with the
+      <para>The [DHCPv6] section configures the DHCPv6 client, if it is enabled with the
       <varname>DHCP=</varname> setting described above, or invoked by the IPv6 Router Advertisement:</para>
 
       <variablelist class='network-directives'>
           <term><varname>UseDNS=</varname></term>
           <term><varname>UseNTP=</varname></term>
           <listitem>
-            <para>As in the <literal>[DHCPv4]</literal> section.</para>
+            <para>As in the [DHCPv4] section.</para>
           </listitem>
         </varlistentry>
 
             <para>Takes a boolean. The DHCPv6 client can obtain configuration parameters from a DHCPv6 server through
             a rapid two-message exchange (solicit and reply). When the rapid commit option is enabled by both
             the DHCPv6 client and the DHCPv6 server, the two-message exchange is used, rather than the default
-            four-method exchange (solicit, advertise, request, and reply). The two-message exchange provides
+            four-message exchange (solicit, advertise, request, and reply). The two-message exchange provides
             faster client configuration and is beneficial in environments in which networks are under a heavy load.
             See <ulink url="https://tools.ietf.org/html/rfc3315#section-17.2.1">RFC 3315</ulink> for details.
             Defaults to true.</para>
         <varlistentry>
           <term><varname>SendVendorOption=</varname></term>
           <listitem>
-            <para>Send an arbitrary vendor option in the DHCPv6 request. Takes an enterprise identifier, DHCP option number,
-            data type, and data separated with a colon
-            (<literal><replaceable>enterprise identifier</replaceable>:<replaceable>option</replaceable>:<replaceable>type</replaceable>:
-            <replaceable>value</replaceable></literal>). Enterprise identifier is an unsigned integer ranges 1..4294967294.
-            The option number must be an integer in the range 1..254. Data type takes one of <literal>uint8</literal>,
-            <literal>uint16</literal>, <literal>uint32</literal>, <literal>ipv4address</literal>, <literal>ipv6address</literal>, or
-            <literal>string</literal>. Special characters in the data string may be escaped using
-            <ulink url="https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences">C-style
+            <para>Send an arbitrary vendor option in the DHCPv6 request. Takes an enterprise identifier, DHCP
+            option number, data type, and data separated with a colon (<literal><replaceable>enterprise
+            identifier</replaceable>:<replaceable>option</replaceable>:<replaceable>type</replaceable>:
+            <replaceable>value</replaceable></literal>). Enterprise identifier is an unsigned integer in the
+            range 1–4294967294. The option number must be an integer in the range 1–254. Data type takes one
+            of <literal>uint8</literal>, <literal>uint16</literal>, <literal>uint32</literal>,
+            <literal>ipv4address</literal>, <literal>ipv6address</literal>, or
+            <literal>string</literal>. Special characters in the data string may be escaped using <ulink
+            url="https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences">C-style
             escapes</ulink>. This setting can be specified multiple times. If an empty string is specified,
             then all options specified earlier are cleared. Defaults to unset.</para>
           </listitem>
         <varlistentry>
           <term><varname>PrefixDelegationHint=</varname></term>
           <listitem>
-            <para>Takes an IPv6 address with prefix length as <varname>Address=</varname> in
-            the "[Network]" section. Specifies the DHCPv6 client for the requesting router to include
-            a prefix-hint in the DHCPv6 solicitation. Prefix ranges 1..128. Defaults to unset.</para>
+            <para>Takes an IPv6 address with prefix length in the same format as the
+            <varname>Address=</varname> in the [Network] section. The DHCPv6 client will include a prefix
+            hint in the DHCPv6 solicitation sent to the server. The prefix length must be in the range
+            1–128. Defaults to unset.</para>
           </listitem>
         </varlistentry>
 
         <varlistentry>
           <term><varname>WithoutRA=</varname></term>
           <listitem>
-            <para>When true, DHCPv6 client starts without router advertisements's managed or other address configuration flag.
-            Defaults to false.</para>
+            <para>Allows DHCPv6 client to start without router advertisements's managed or other address
+            configuration flag. Takes one of <literal>solicit</literal> or
+            <literal>information-request</literal>. Defaults to unset.</para>
           </listitem>
         </varlistentry>
 
         <varlistentry>
           <term><varname>SendOption=</varname></term>
           <listitem>
-            <para>As in the <literal>[DHCPv4]</literal> section, however because DHCPv6 uses 16-bit fields to store
+            <para>As in the [DHCPv4] section, however because DHCPv6 uses 16-bit fields to store
             option numbers, the option number is an integer in the range 1..65536.</para>
           </listitem>
         </varlistentry>
 
   <refsect1>
     <title>[IPv6AcceptRA] Section Options</title>
-      <para>The <literal>[IPv6AcceptRA]</literal> section configures the IPv6 Router Advertisement
-      (RA) client, if it is enabled with the <varname>IPv6AcceptRA=</varname> setting described
-      above:</para>
+      <para>The [IPv6AcceptRA] section configures the IPv6 Router Advertisement (RA) client, if it is enabled
+      with the <varname>IPv6AcceptRA=</varname> setting described above:</para>
 
       <variablelist class='network-directives'>
         <varlistentry>
 
   <refsect1>
     <title>[DHCPServer] Section Options</title>
-    <para>The <literal>[DHCPServer]</literal> section contains
-    settings for the DHCP server, if enabled via the
+    <para>The [DHCPServer] section contains settings for the DHCP server, if enabled via the
     <varname>DHCPServer=</varname> option described above:</para>
 
     <variablelist class='network-directives'>
 
   <refsect1>
     <title>[IPv6PrefixDelegation] Section Options</title>
-    <para>The <literal>[IPv6PrefixDelegation]</literal> section contains
-    settings for sending IPv6 Router Advertisements and whether to act as
-    a router, if enabled via the <varname>IPv6PrefixDelegation=</varname>
-    option described above. IPv6 network prefixes are defined with one or
-    more <literal>[IPv6Prefix]</literal> sections.</para>
+    <para>The [IPv6PrefixDelegation] section contains settings for sending IPv6 Router Advertisements and
+    whether to act as a router, if enabled via the <varname>IPv6PrefixDelegation=</varname> option described
+    above. IPv6 network prefixes are defined with one or more [IPv6Prefix] sections.</para>
 
     <variablelist class='network-directives'>
 
         <term><varname>EmitDNS=</varname></term>
         <term><varname>DNS=</varname></term>
 
-        <listitem><para><varname>DNS=</varname> specifies a list of recursive DNS server IPv6 addresses
-        that are distributed via Router Advertisement messages when <varname>EmitDNS=</varname> is
-        true. <varname>DNS=</varname> also takes special value <literal>_link_local</literal>; in that
-        case the IPv6 link local address is distributed. If <varname>DNS=</varname> is empty, DNS
-        servers are read from the <literal>[Network]</literal> section. If the
-        <literal>[Network]</literal> section does not contain any DNS servers either, DNS servers from
-        the uplink with the highest priority default route are used. When <varname>EmitDNS=</varname>
-        is false, no DNS server information is sent in Router Advertisement messages.
-        <varname>EmitDNS=</varname> defaults to true.
-        </para></listitem>
+        <listitem><para><varname>DNS=</varname> specifies a list of recursive DNS server IPv6 addresses that
+        are distributed via Router Advertisement messages when <varname>EmitDNS=</varname> is
+        true. <varname>DNS=</varname> also takes special value <literal>_link_local</literal>; in that case
+        the IPv6 link local address is distributed. If <varname>DNS=</varname> is empty, DNS servers are read
+        from the [Network] section. If the [Network] section does not contain any DNS servers either, DNS
+        servers from the uplink with the highest priority default route are used. When
+        <varname>EmitDNS=</varname> is false, no DNS server information is sent in Router Advertisement
+        messages. <varname>EmitDNS=</varname> defaults to true.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>EmitDomains=</varname></term>
         <term><varname>Domains=</varname></term>
 
-        <listitem><para>A list of DNS search domains distributed via Router
-        Advertisement messages when <varname>EmitDomains=</varname> is true. If
-        <varname>Domains=</varname> is empty, DNS search domains are read from the
-        <literal>[Network]</literal> section. If the <literal>[Network]</literal>
-        section does not contain any DNS search domains either, DNS search
-        domains from the uplink with the highest priority default route are
-        used. When <varname>EmitDomains=</varname> is false, no DNS search domain
-        information is sent in Router Advertisement messages.
-        <varname>EmitDomains=</varname> defaults to true.
-        </para></listitem>
+        <listitem><para>A list of DNS search domains distributed via Router Advertisement messages when
+        <varname>EmitDomains=</varname> is true. If <varname>Domains=</varname> is empty, DNS search domains
+        are read from the [Network] section. If the [Network] section does not contain any DNS search domains
+        either, DNS search domains from the uplink with the highest priority default route are used. When
+        <varname>EmitDomains=</varname> is false, no DNS search domain information is sent in Router
+        Advertisement messages. <varname>EmitDomains=</varname> defaults to true.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
     <refsect1>
     <title>[IPv6Prefix] Section Options</title>
-    <para>One or more <literal>[IPv6Prefix]</literal> sections contain the IPv6
-    prefixes that are announced via Router Advertisements. See
-    <ulink url="https://tools.ietf.org/html/rfc4861">RFC 4861</ulink>
-    for further details.</para>
+    <para>One or more [IPv6Prefix] sections contain the IPv6 prefixes that are announced via Router
+    Advertisements. See <ulink url="https://tools.ietf.org/html/rfc4861">RFC 4861</ulink> for further
+    details.</para>
 
     <variablelist class='network-directives'>
 
       <varlistentry>
         <term><varname>Prefix=</varname></term>
 
-        <listitem><para>The IPv6 prefix that is to be distributed to hosts.
-        Similarly to configuring static IPv6 addresses, the setting is
-        configured as an IPv6 prefix and its prefix length, separated by a
-        <literal>/</literal> character. Use multiple
-        <literal>[IPv6Prefix]</literal> sections to configure multiple IPv6
-        prefixes since prefix lifetimes, address autoconfiguration and onlink
-        status may differ from one prefix to another.</para></listitem>
+        <listitem><para>The IPv6 prefix that is to be distributed to hosts.  Similarly to configuring static
+        IPv6 addresses, the setting is configured as an IPv6 prefix and its prefix length, separated by a
+        <literal>/</literal> character. Use multiple [IPv6Prefix] sections to configure multiple IPv6
+        prefixes since prefix lifetimes, address autoconfiguration and onlink status may differ from one
+        prefix to another.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
     <refsect1>
     <title>[IPv6RoutePrefix] Section Options</title>
-    <para>One or more <literal>[IPv6RoutePrefix]</literal> sections contain the IPv6
+    <para>One or more [IPv6RoutePrefix] sections contain the IPv6
     prefix routes that are announced via Router Advertisements. See
     <ulink url="https://tools.ietf.org/html/rfc4191">RFC 4191</ulink>
     for further details.</para>
       <varlistentry>
         <term><varname>Route=</varname></term>
 
-        <listitem><para>The IPv6 route that is to be distributed to hosts.
-        Similarly to configuring static IPv6 routes, the setting is
-        configured as an IPv6 prefix routes and its prefix route length,
-        separated by a<literal>/</literal> character. Use multiple
-        <literal>[IPv6PrefixRoutes]</literal> sections to configure multiple IPv6
-        prefix routes.</para></listitem>
+        <listitem><para>The IPv6 route that is to be distributed to hosts.  Similarly to configuring static
+        IPv6 routes, the setting is configured as an IPv6 prefix routes and its prefix route length,
+        separated by a <literal>/</literal> character. Use multiple [IPv6PrefixRoutes] sections to configure
+        multiple IPv6 prefix routes.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
     <refsect1>
     <title>[Bridge] Section Options</title>
-      <para>The <literal>[Bridge]</literal> section accepts the
-      following keys.</para>
+      <para>The [Bridge] section accepts the following keys:</para>
       <variablelist class='network-directives'>
         <varlistentry>
           <term><varname>UnicastFlood=</varname></term>
         <varlistentry>
           <term><varname>HairPin=</varname></term>
           <listitem>
-            <para>Takes a boolean. Configures whether traffic may be sent back
-            out of the port on which it was received. When this flag is false, and the bridge
-            will not forward traffic back out of the receiving port.
-            When unset, the kernel's default will be used.</para>
+            <para>Takes a boolean. Configures whether traffic may be sent back out of the port on which it
+            was received. When this flag is false, then the bridge will not forward traffic back out of the
+            receiving port. When unset, the kernel's default will be used.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
   </refsect1>
   <refsect1>
     <title>[BridgeFDB] Section Options</title>
-      <para>The <literal>[BridgeFDB]</literal> section manages the
-      forwarding database table of a port and accepts the following
-      keys. Specify several <literal>[BridgeFDB]</literal> sections to
-      configure several static MAC table entries.</para>
+      <para>The [BridgeFDB] section manages the forwarding database table of a port and accepts the following
+      keys. Specify several [BridgeFDB] sections to configure several static MAC table entries.</para>
 
       <variablelist class='network-directives'>
         <varlistentry>
           <term><varname>MACAddress=</varname></term>
           <listitem>
-            <para>As in the <literal>[Network]</literal> section. This
-            key is mandatory.</para>
+            <para>As in the [Network] section. This key is mandatory.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
 
   <refsect1>
     <title>[LLDP] Section Options</title>
-      <para>The <literal>[LLDP]</literal> section manages the Link Layer Discovery Protocol (LLDP) and accepts the
-      following keys.</para>
+      <para>The [LLDP] section manages the Link Layer Discovery Protocol (LLDP) and accepts the following
+      keys.</para>
       <variablelist class='network-directives'>
         <varlistentry>
           <term><varname>MUDURL=</varname></term>
 
   <refsect1>
     <title>[CAN] Section Options</title>
-      <para>The <literal>[CAN]</literal> section manages the Controller Area Network (CAN bus) and accepts the
-      following keys.</para>
+      <para>The [CAN] section manages the Controller Area Network (CAN bus) and accepts the
+      following keys:</para>
       <variablelist class='network-directives'>
         <varlistentry>
           <term><varname>BitRate=</varname></term>
 
   <refsect1>
     <title>[QDisc] Section Options</title>
-    <para>The <literal>[QDisc]</literal> section manages the traffic control queueing discipline (qdisc).</para>
+    <para>The [QDisc] section manages the traffic control queueing discipline (qdisc).</para>
 
     <variablelist class='network-directives'>
       <varlistentry>
 
   <refsect1>
     <title>[NetworkEmulator] Section Options</title>
-    <para>The <literal>[NetworkEmulator]</literal> section manages the queueing discipline (qdisc) of
-    the network emulator. It can be used to configure the kernel packet scheduler and simulate packet
-    delay and loss for UDP or TCP applications, or limit the bandwidth usage of a particular service to
-    simulate internet connections.</para>
+    <para>The [NetworkEmulator] section manages the queueing discipline (qdisc) of the network emulator. It
+    can be used to configure the kernel packet scheduler and simulate packet delay and loss for UDP or TCP
+    applications, or limit the bandwidth usage of a particular service to simulate internet connections.
+    </para>
 
     <variablelist class='network-directives'>
       <xi:include href="tc.xml" xpointer="qdisc-parent" />
         <term><varname>PacketLimit=</varname></term>
         <listitem>
           <para>Specifies the maximum number of packets the qdisc may hold queued at a time.
-          An unsigned integer ranges 0 to 4294967294. Defaults to 1000.</para>
+          An unsigned integer in the range 0–4294967294. Defaults to 1000.</para>
         </listitem>
       </varlistentry>
 
 
   <refsect1>
     <title>[TokenBucketFilter] Section Options</title>
-    <para>The <literal>[TokenBucketFilter]</literal> section manages the queueing discipline (qdisc) of
-    token bucket filter (tbf).</para>
+    <para>The [TokenBucketFilter] section manages the queueing discipline (qdisc) of token bucket filter
+    (tbf).</para>
 
     <variablelist class='network-directives'>
       <xi:include href="tc.xml" xpointer="qdisc-parent" />
       </varlistentry>
 
       <varlistentry>
-        <term><varname>LimitSize=</varname></term>
+        <term><varname>LimitBytes=</varname></term>
         <listitem>
           <para>Takes the number of bytes that can be queued waiting for tokens to become available.
           When the size is suffixed with K, M, or G, it is parsed as Kilobytes, Megabytes, or Gigabytes,
-          respectively, to the base of 1000. Defaults to unset.</para>
+          respectively, to the base of 1024. Defaults to unset.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>Burst=</varname></term>
+        <term><varname>BurstBytes=</varname></term>
         <listitem>
           <para>Specifies the size of the bucket. This is the maximum amount of bytes that tokens
           can be available for instantaneous transfer. When the size is suffixed with K, M, or G, it is
-          parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1000. Defaults to
+          parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1024. Defaults to
           unset.</para>
         </listitem>
       </varlistentry>
         <listitem>
           <para>The Minimum Packet Unit (MPU) determines the minimal token usage (specified in bytes)
           for a packet. 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 zero.</para>
+          Megabytes, or Gigabytes, respectively, to the base of 1024. Defaults to zero.</para>
         </listitem>
       </varlistentry>
 
         <term><varname>MTUBytes=</varname></term>
         <listitem>
           <para>Specifies the size of the peakrate bucket. When suffixed with K, M, or G, the specified
-          size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1000.
+          size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1024.
           Defaults to unset.</para>
         </listitem>
       </varlistentry>
 
   <refsect1>
     <title>[PIE] Section Options</title>
-    <para>The <literal>[PIE]</literal> section manages the queueing discipline
-    (qdisc) of Proportional Integral controller-Enhanced (PIE).</para>
+    <para>The [PIE] section manages the queueing discipline (qdisc) of Proportional Integral
+    controller-Enhanced (PIE).</para>
 
     <variablelist class='network-directives'>
       <xi:include href="tc.xml" xpointer="qdisc-parent" />
         <term><varname>PacketLimit=</varname></term>
         <listitem>
           <para>Specifies the hard limit on the queue size in number of packets. When this limit is reached, incoming packets are
-          dropped. An unsigned integer ranges 1 to 4294967294. Defaults to unset and kernel's default is used.</para>
+          dropped. An unsigned integer in the range 1–4294967294. Defaults to unset and kernel's default is used.</para>
         </listitem>
       </varlistentry>
     </variablelist>
 
   <refsect1>
     <title>[StochasticFairBlue] Section Options</title>
-    <para>The <literal>[StochasticFairBlue]</literal> section manages the queueing discipline
-    (qdisc) of stochastic fair blue (sfb).</para>
+    <para>The [StochasticFairBlue] section manages the queueing discipline (qdisc) of stochastic fair blue
+    (sfb).</para>
 
     <variablelist class='network-directives'>
       <xi:include href="tc.xml" xpointer="qdisc-parent" />
       <varlistentry>
         <term><varname>PacketLimit=</varname></term>
         <listitem>
-          <para>Specifies the hard limit on the queue size in number of packets. When this limit is reached, incoming packets are
-          dropped. An unsigned integer ranges 0 to 4294967294. Defaults to unset and kernel's default is used.</para>
+          <para>Specifies the hard limit on the queue size in number of packets. When this limit is reached,
+          incoming packets are dropped. An unsigned integer in the range 0–4294967294. Defaults to unset and
+          kernel's default is used.</para>
         </listitem>
       </varlistentry>
     </variablelist>
 
   <refsect1>
     <title>[StochasticFairnessQueueing] Section Options</title>
-    <para>The <literal>[StochasticFairnessQueueing]</literal> section manages the queueing discipline
-    (qdisc) of stochastic fairness queueing (sfq).</para>
+    <para>The [StochasticFairnessQueueing] section manages the queueing discipline (qdisc) of stochastic
+    fairness queueing (sfq).</para>
 
     <variablelist class='network-directives'>
       <xi:include href="tc.xml" xpointer="qdisc-parent" />
 
   <refsect1>
     <title>[BFIFO] Section Options</title>
-    <para>The <literal>[BFIFO]</literal> section manages the queueing discipline (qdisc) of
-    Byte limited Packet First In First Out (bfifo).</para>
+    <para>The [BFIFO] section manages the queueing discipline (qdisc) of Byte limited Packet First In First
+    Out (bfifo).</para>
 
     <variablelist class='network-directives'>
       <xi:include href="tc.xml" xpointer="qdisc-parent" />
       <xi:include href="tc.xml" xpointer="qdisc-handle" />
 
       <varlistentry>
-        <term><varname>LimitSize=</varname></term>
+        <term><varname>LimitBytes=</varname></term>
         <listitem>
-          <para>Specifies the hard limit on the FIFO size in bytes. The size limit (a buffer size) to prevent it
-          from overflowing in case it is unable to dequeue packets as quickly as it receives them. When this limit
-          is reached, incoming packets are dropped. 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>
+          <para>Specifies the hard limit on the FIFO size in bytes. The size limit (a buffer size) to prevent
+          it from overflowing in case it is unable to dequeue packets as quickly as it receives them. When
+          this limit is reached, incoming packets are dropped. 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>
     </variablelist>
 
   <refsect1>
     <title>[PFIFO] Section Options</title>
-    <para>The <literal>[PFIFO]</literal> section manages the queueing discipline (qdisc) of
-    Packet First In First Out (pfifo).</para>
+    <para>The [PFIFO] section manages the queueing discipline (qdisc) of Packet First In First Out
+    (pfifo).</para>
 
     <variablelist class='network-directives'>
       <xi:include href="tc.xml" xpointer="qdisc-parent" />
       <varlistentry>
         <term><varname>PacketLimit=</varname></term>
         <listitem>
-          <para>Specifies the hard limit on the FIFO size in number of packets. The size limit (a buffer size) to prevent it
-          from overflowing in case it is unable to dequeue packets as quickly as it receives them. When this limit is reached,
-          incoming packets are dropped. An unsigned integer ranges 0 to 4294967294. Defaults to unset and kernel's default is used.</para>
+          <para>Specifies the hard limit on the FIFO size in number of packets. The size limit (a buffer
+          size) to prevent it from overflowing in case it is unable to dequeue packets as quickly as it
+          receives them. When this limit is reached, incoming packets are dropped. An unsigned integer in the
+          range 0–4294967294. Defaults to unset and kernel's default is used.</para>
         </listitem>
       </varlistentry>
     </variablelist>
 
   <refsect1>
     <title>[PFIFOHeadDrop] Section Options</title>
-    <para>The <literal>[PFIFOHeadDrop]</literal> section manages the queueing discipline (qdisc) of
-    Packet First In First Out Head Drop (pfifo_head_drop).</para>
+    <para>The [PFIFOHeadDrop] section manages the queueing discipline (qdisc) of Packet First In First Out
+    Head Drop (pfifo_head_drop).</para>
 
     <variablelist class='network-directives'>
       <xi:include href="tc.xml" xpointer="qdisc-parent" />
       <varlistentry>
         <term><varname>PacketLimit=</varname></term>
         <listitem>
-          <para>As in <literal>[PFIFO]</literal> section.</para></listitem>
+          <para>As in [PFIFO] section.</para></listitem>
       </varlistentry>
     </variablelist>
   </refsect1>
 
   <refsect1>
      <title>[PFIFOFast] Section Options</title>
-     <para>The <literal>[PFIFOFast]</literal> section manages the queueing discipline (qdisc) of
-     Packet First In First Out Fast (pfifo_fast).</para>
+     <para>The [PFIFOFast] section manages the queueing discipline (qdisc) of Packet First In First Out Fast
+     (pfifo_fast).</para>
 
      <variablelist class='network-directives'>
        <xi:include href="tc.xml" xpointer="qdisc-parent" />
 
   <refsect1>
     <title>[CAKE] Section Options</title>
-    <para>The <literal>[CAKE]</literal> section manages the queueing discipline (qdisc) of
-    Common Applications Kept Enhanced (CAKE).</para>
+    <para>The [CAKE] section manages the queueing discipline (qdisc) of Common Applications Kept Enhanced
+    (CAKE).</para>
 
     <variablelist class='network-directives'>
       <xi:include href="tc.xml" xpointer="qdisc-parent" />
       <xi:include href="tc.xml" xpointer="qdisc-handle" />
 
       <varlistentry>
-        <term><varname>Overhead=</varname></term>
+        <term><varname>OverheadBytes=</varname></term>
         <listitem>
-          <para>Specifies that bytes to be addeded to the size of each packet. Bytes may be negative.
-          Takes an integer ranges -64 to 256. Defaults to unset and kernel's default is used.</para>
+          <para>Specifies that bytes to be addeded to the size of each packet. Bytes may be negative. Takes
+          an integer in the range from -64 to 256. Defaults to unset and kernel's default is used.</para>
         </listitem>
       </varlistentry>
 
 
   <refsect1>
     <title>[ControlledDelay] Section Options</title>
-    <para>The <literal>[ControlledDelay]</literal> section manages the queueing discipline (qdisc) of
+    <para>The [ControlledDelay] section manages the queueing discipline (qdisc) of
     controlled delay (CoDel).</para>
 
     <variablelist class='network-directives'>
       <varlistentry>
         <term><varname>PacketLimit=</varname></term>
         <listitem>
-          <para>Specifies the hard limit on the queue size in number of packets. When this limit is reached, incoming packets are
-          dropped. An unsigned integer ranges 0 to 4294967294. Defaults to unset and kernel's default is used.</para>
+          <para>Specifies the hard limit on the queue size in number of packets. When this limit is reached,
+          incoming packets are dropped. An unsigned integer in the range 0–4294967294. Defaults to unset and
+          kernel's default is used.</para>
         </listitem>
       </varlistentry>
 
 
   <refsect1>
     <title>[DeficitRoundRobinScheduler] Section Options</title>
-    <para>The <literal>[DeficitRoundRobinScheduler]</literal> section manages the queueing discipline (qdisc) of
-    Deficit Round Robin Scheduler (DRR).</para>
+    <para>The [DeficitRoundRobinScheduler] section manages the queueing discipline (qdisc) of Deficit Round
+    Robin Scheduler (DRR).</para>
 
     <variablelist class='network-directives'>
       <xi:include href="tc.xml" xpointer="qdisc-parent" />
 
   <refsect1>
     <title>[DeficitRoundRobinSchedulerClass] Section Options</title>
-    <para>The <literal>[DeficitRoundRobinSchedulerClass]</literal> section manages the traffic control class of
-    Deficit Round Robin Scheduler (DRR).</para>
+    <para>The [DeficitRoundRobinSchedulerClass] section manages the traffic control class of Deficit Round
+    Robin Scheduler (DRR).</para>
 
     <variablelist class='network-directives'>
       <xi:include href="tc.xml" xpointer="tclass-parent" />
       <xi:include href="tc.xml" xpointer="tclass-classid" />
 
       <varlistentry>
-        <term><varname>Quantum=</varname></term>
+        <term><varname>QuantumBytes=</varname></term>
         <listitem>
-          <para>Specifies the amount of bytes a flow is allowed to dequeue before the
-          scheduler moves to the next class. An unsigned integer ranges 1 to 4294967294.
-          Defaults to the MTU of the interface.</para>
+          <para>Specifies the amount of bytes a flow is allowed to dequeue before the scheduler moves
+          to the next class. 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 the MTU of the
+          interface.</para>
         </listitem>
       </varlistentry>
 
 
   <refsect1>
     <title>[EnhancedTransmissionSelection] Section Options</title>
-    <para>The <literal>[EnhancedTransmissionSelection]</literal> section manages the queueing discipline (qdisc) of
-    Enhanced Transmission Selection (ETS).</para>
+    <para>The [EnhancedTransmissionSelection] section manages the queueing discipline (qdisc) of Enhanced
+    Transmission Selection (ETS).</para>
 
     <variablelist class='network-directives'>
       <xi:include href="tc.xml" xpointer="qdisc-parent" />
       <varlistentry>
         <term><varname>Bands=</varname></term>
         <listitem>
-          <para>Specifies the number of bands. An unsigned integer ranges 1 to 16. This value has to be
-          at least large enough to cover the strict bands specified through the
-          <varname>StrictBands=</varname> and bandwidth-sharing bands specified in
-          <varname>QuantumBytes=</varname>.</para>
+          <para>Specifies the number of bands. An unsigned integer in the range 1–16. This value has to be at
+          least large enough to cover the strict bands specified through the <varname>StrictBands=</varname>
+          and bandwidth-sharing bands specified in <varname>QuantumBytes=</varname>.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>StrictBands=</varname></term>
         <listitem>
-          <para>Specifies the number of bands that should be created in strict mode. An unsigned integer
-          ranges 1 to 16.</para>
+          <para>Specifies the number of bands that should be created in strict mode. An unsigned integer in
+          the range 1–16.</para>
         </listitem>
       </varlistentry>
 
 
   <refsect1>
     <title>[GenericRandomEarlyDetection] Section Options</title>
-    <para>The <literal>[GenericRandomEarlyDetection]</literal> section manages the queueing discipline
-    (qdisc) of Generic Random Early Detection (GRED).</para>
+    <para>The [GenericRandomEarlyDetection] section manages the queueing discipline (qdisc) of Generic Random
+    Early Detection (GRED).</para>
 
     <variablelist class='network-directives'>
       <xi:include href="tc.xml" xpointer="qdisc-parent" />
 
   <refsect1>
     <title>[FairQueueingControlledDelay] Section Options</title>
-    <para>The <literal>[FairQueueingControlledDelay]</literal> section manages the queueing discipline
-    (qdisc) of fair queuing controlled delay (FQ-CoDel).</para>
+    <para>The [FairQueueingControlledDelay] section manages the queueing discipline (qdisc) of fair queuing
+    controlled delay (FQ-CoDel).</para>
 
     <variablelist class='network-directives'>
       <xi:include href="tc.xml" xpointer="qdisc-parent" />
       </varlistentry>
 
       <varlistentry>
-        <term><varname>MemoryLimit=</varname></term>
+        <term><varname>MemoryLimitBytes=</varname></term>
         <listitem>
           <para>Specifies the limit on the total number of bytes that can be queued in this FQ-CoDel instance.
           When suffixed with K, M, or G, the specified size is parsed as Kilobytes, Megabytes, or Gigabytes,
       </varlistentry>
 
       <varlistentry>
-        <term><varname>Quantum=</varname></term>
+        <term><varname>QuantumBytes=</varname></term>
         <listitem>
-          <para>Specifies the number of bytes used as 'deficit' in the fair queuing algorithmtimespan.
+          <para>Specifies the number of bytes used as the "deficit" in the fair queuing algorithm timespan.
           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>
 
   <refsect1>
     <title>[FairQueueing] Section Options</title>
-    <para>The <literal>[FairQueueing]</literal> section manages the queueing discipline
-    (qdisc) of fair queue traffic policing (FQ).</para>
+    <para>The [FairQueueing] section manages the queueing discipline (qdisc) of fair queue traffic policing
+    (FQ).</para>
 
     <variablelist class='network-directives'>
       <xi:include href="tc.xml" xpointer="qdisc-parent" />
       </varlistentry>
 
       <varlistentry>
-        <term><varname>Quantum=</varname></term>
+        <term><varname>QuantumBytes=</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,
       </varlistentry>
 
       <varlistentry>
-        <term><varname>InitialQuantum=</varname></term>
+        <term><varname>InitialQuantumBytes=</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
 
   <refsect1>
     <title>[TrivialLinkEqualizer] Section Options</title>
-    <para>The <literal>[TrivialLinkEqualizer]</literal> section manages the queueing discipline (qdisc) of
-    trivial link equalizer (teql).</para>
+    <para>The [TrivialLinkEqualizer] section manages the queueing discipline (qdisc) of trivial link
+    equalizer (teql).</para>
 
     <variablelist class='network-directives'>
       <xi:include href="tc.xml" xpointer="qdisc-parent" />
 
   <refsect1>
     <title>[HierarchyTokenBucket] Section Options</title>
-    <para>The <literal>[HierarchyTokenBucket]</literal> section manages the queueing discipline (qdisc) of
-    hierarchy token bucket (htb).</para>
+    <para>The [HierarchyTokenBucket] section manages the queueing discipline (qdisc) of hierarchy token
+    bucket (htb).</para>
 
     <variablelist class='network-directives'>
       <xi:include href="tc.xml" xpointer="qdisc-parent" />
 
   <refsect1>
     <title>[HierarchyTokenBucketClass] Section Options</title>
-    <para>The <literal>[HierarchyTokenBucketClass]</literal> section manages the traffic control class of
-    hierarchy token bucket (htb).</para>
+    <para>The [HierarchyTokenBucketClass] section manages the traffic control class of hierarchy token bucket
+    (htb).</para>
 
     <variablelist class='network-directives'>
       <xi:include href="tc.xml" xpointer="tclass-parent" />
 
   <refsect1>
     <title>[HeavyHitterFilter] Section Options</title>
-    <para>The <literal>[HeavyHitterFilter]</literal> section manages the queueing discipline
-    (qdisc) of Heavy Hitter Filter (hhf).</para>
+    <para>The [HeavyHitterFilter] section manages the queueing discipline (qdisc) of Heavy Hitter Filter
+    (hhf).</para>
 
     <variablelist class='network-directives'>
       <xi:include href="tc.xml" xpointer="qdisc-parent" />
       <varlistentry>
         <term><varname>PacketLimit=</varname></term>
         <listitem>
-          <para>Specifies the hard limit on the queue size in number of packets. When this limit is reached, incoming packets are
-          dropped. An unsigned integer ranges 0 to 4294967294. Defaults to unset and kernel's default is used.</para>
+          <para>Specifies the hard limit on the queue size in number of packets. When this limit is reached,
+          incoming packets are dropped. An unsigned integer in the range 0–4294967294. Defaults to unset and
+          kernel's default is used.</para>
         </listitem>
       </varlistentry>
     </variablelist>
 
   <refsect1>
     <title>[QuickFairQueueing] Section Options</title>
-    <para>The <literal>[QuickFairQueueing]</literal> section manages the queueing discipline
-    (qdisc) of Quick Fair Queueing (QFQ).</para>
+    <para>The [QuickFairQueueing] section manages the queueing discipline (qdisc) of Quick Fair Queueing
+    (QFQ).</para>
 
     <variablelist class='network-directives'>
       <xi:include href="tc.xml" xpointer="qdisc-parent" />
 
   <refsect1>
     <title>[QuickFairQueueingClass] Section Options</title>
-    <para>The <literal>[QuickFairQueueingClass]</literal> section manages the traffic control class of
-    Quick Fair Queueing (qfq).</para>
+    <para>The [QuickFairQueueingClass] section manages the traffic control class of Quick Fair Queueing
+    (qfq).</para>
 
     <variablelist class='network-directives'>
       <xi:include href="tc.xml" xpointer="tclass-parent" />
       </varlistentry>
 
       <varlistentry>
-        <term><varname>MaxPacketSize=</varname></term>
+        <term><varname>MaxPacketBytes=</varname></term>
         <listitem>
           <para>Specifies the maximum packet size in bytes for the class. When suffixed with K, M, or G, the specified
-          size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1000. When unset,
+          size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1024. When unset,
           the kernel default is used.</para>
         </listitem>
       </varlistentry>
 
   <refsect1>
     <title>[BridgeVLAN] Section Options</title>
-      <para>The <literal>[BridgeVLAN]</literal> section manages the VLAN ID configuration of a bridge port and accepts
-      the following keys. Specify several <literal>[BridgeVLAN]</literal> sections to configure several VLAN entries.
-      The <varname>VLANFiltering=</varname> option has to be enabled, see <literal>[Bridge]</literal> section in
+      <para>The [BridgeVLAN] section manages the VLAN ID configuration of a bridge port and accepts the
+      following keys. Specify several [BridgeVLAN] sections to configure several VLAN entries. The
+      <varname>VLANFiltering=</varname> option has to be enabled, see the [Bridge] section in
       <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
 
       <variablelist class='network-directives'>
index 27eae50b5170780d7c2c0845144f5ac383d4cf95..4b37c5dc4400b9cb24b18d927e2f63e299d3a8fc 100644 (file)
@@ -80,7 +80,7 @@
   <refsect1>
     <title>[Exec] Section Options</title>
 
-    <para>Settings files may include an <literal>[Exec]</literal>
+    <para>Settings files may include an [Exec]
     section, which carries various execution parameters:</para>
 
     <variablelist class='nspawn-directives'>
   <refsect1>
     <title>[Files] Section Options</title>
 
-    <para>Settings files may include a <literal>[Files]</literal>
+    <para>Settings files may include a [Files]
     section, which carries various parameters configuring the file
     system of the container:</para>
 
       <varlistentry>
         <term><varname>Inaccessible=</varname></term>
 
-        <listitem><para>Masks the specified file or directly in the container, by over-mounting it with an empty file
+        <listitem><para>Masks the specified file or directory in the container, by over-mounting it with an empty file
         node of the same type with the most restrictive access mode. Takes a file system path as argument. This option
         may be used multiple times to mask multiple files or directories. This option is equivalent to the command line
         switch <option>--inaccessible=</option>, see
   <refsect1>
     <title>[Network] Section Options</title>
 
-    <para>Settings files may include a <literal>[Network]</literal>
+    <para>Settings files may include a [Network]
     section, which carries various parameters configuring the network
     connectivity of the container:</para>
 
index 89c12b598bdacc2b6e2565c72f9c2bc5070706a0..81a37f67898c649bc1d42da5fe6b0460167f28e8 100644 (file)
@@ -33,7 +33,7 @@
 
     <orderedlist>
       <listitem>
-        <para>The package manager prepares system updates by downloading all (RPM or DEB or
+        <para>The package manager prepares system updates by downloading all (.rpm or .deb or
         whatever) packages to update off-line in a special directory
         <filename index="false">/var/lib/system-update</filename> (or
         another directory of the package/upgrade manager's choice).</para>
@@ -85,8 +85,8 @@
       </listitem>
 
       <listitem>
-        <para>The upgrade scripts should exit only after the update is finished. It is expected
-        that the service which performs the upgrade will cause the machine to reboot after it
+        <para>The update scripts should exit only after the update is finished. It is expected
+        that the service which performs the update will cause the machine to reboot after it
         is done. If the <filename>system-update.target</filename> is successfully reached, i.e.
         all update services have run, and the <filename>/system-update</filename> symlink still
         exists, it will be removed and the machine rebooted as a safety measure.</para>
index f6fe3d83883eca518563535f522ed27c3d169503..604bf494ba71a6222a4df753a18e3be622a5ebe5 100644 (file)
@@ -34,9 +34,9 @@
     this unit type. See
     <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     for the common options of all unit configuration files. The common
-    configuration items are configured in the generic <literal>[Unit]</literal> and
-    <literal>[Install]</literal> sections. The path specific configuration options are
-    configured in the <literal>[Path]</literal> section.</para>
+    configuration items are configured in the generic [Unit] and
+    [Install] sections. The path specific configuration options are
+    configured in the [Path] section.</para>
 
     <para>For each path file, a matching unit file must exist,
     describing the unit to activate when the path changes. By default,
index b624ac5f9303990683bf502ee1672dd2e4dcc80d..449b90b48455733f35df7712862adf1c1ae90dcf 100644 (file)
@@ -89,7 +89,7 @@
   <refsect1>
     <title>Options</title>
 
-    <para>Scope files may include a <literal>[Scope]</literal>
+    <para>Scope files may include a [Scope]
     section, which carries information about the scope and the
     units it contains. A number of options that may be used in
     this section are shared with other unit types. These options are
@@ -97,7 +97,7 @@
     <citerefentry><refentrytitle>systemd.kill</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     and
     <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
-    The options specific to the <literal>[Scope]</literal> section
+    The options specific to the [Scope] section
     of scope units are the following:</para>
 
     <variablelist class='unit-directives'>
index 61484d530c486fdb907a176fea8461f0a8c0cf98..4e281ec6d4f80d43151bdce6d91c9d04160b8852 100644 (file)
@@ -35,9 +35,9 @@
     <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     for the common options of all unit configuration files. The common
     configuration items are configured in the generic
-    <literal>[Unit]</literal> and <literal>[Install]</literal>
+    [Unit] and [Install]
     sections. The service specific configuration options are
-    configured in the <literal>[Service]</literal> section.</para>
+    configured in the [Service] section.</para>
 
     <para>Additional options are listed in
     <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
   <refsect1>
     <title>Options</title>
 
-    <para>Service files must include a <literal>[Service]</literal>
+    <para>Service files must include a [Service]
     section, which carries information about the service and the
     process it supervises. A number of options that may be used in
     this section are shared with other unit types. These options are
     <citerefentry><refentrytitle>systemd.kill</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     and
     <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
-    The options specific to the <literal>[Service]</literal> section
+    The options specific to the [Service] section
     of service units are the following:</para>
 
     <variablelist class='unit-directives'>
         project='man-pages'><refentrytitle>signal</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
         a list of signal names.</para>
 
-        <para>Note that this setting does not change the the mapping between numeric exit statuses and their
+        <para>Note that this setting does not change the mapping between numeric exit statuses and their
         names, i.e. regardless how this setting is used 0 will still be mapped to <literal>SUCCESS</literal>
         (and thus typically shown as <literal>0/SUCCESS</literal> in tool outputs) and 1 to
         <literal>FAILURE</literal> (and thus typically shown as <literal>1/FAILURE</literal>), and so on. It
         this option will have no effect.</para>
 
         <example>
-          <title>A service with with the <varname>SuccessExitStatus=</varname> setting</title>
+          <title>A service with the <varname>SuccessExitStatus=</varname> setting</title>
 
           <programlisting>SuccessExitStatus=TEMPFAIL 250 SIGUSR1</programlisting>
 
@@ -1495,7 +1495,7 @@ ExecStart=/usr/sbin/simple-dbus-service
 WantedBy=multi-user.target</programlisting>
 
       <para>For <emphasis>bus-activatable</emphasis> services, do not
-      include a <literal>[Install]</literal> section in the systemd
+      include a [Install] section in the systemd
       service file, but use the <varname>SystemdService=</varname>
       option in the corresponding DBus service file, for example
       (<filename>/usr/share/dbus-1/system-services/org.example.simple-dbus-service.service</filename>):</para>
index 7157dfa32d390ed3f4dda254f64146b0f4d78f70..5019bf9976a4e442685d76b75d18fcad146b1d6d 100644 (file)
@@ -55,9 +55,9 @@
     <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     for the common options of all unit configuration
     files. The common configuration items are configured
-    in the generic <literal>[Unit]</literal> and <literal>[Install]</literal> sections. The
+    in the generic [Unit] and [Install] sections. The
     slice specific configuration options are configured in
-    the <literal>[Slice]</literal> section. Currently, only generic resource control settings
+    the [Slice] section. Currently, only generic resource control settings
     as described in
     <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry> are allowed.
     </para>
index 46a2dbc7edfb127e185c72b6adf1c0c39d4db84b..f989b99f955cb14d0ffac00ba462229194a2cdf4 100644 (file)
@@ -35,9 +35,9 @@
     this unit type. See
     <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     for the common options of all unit configuration files. The common
-    configuration items are configured in the generic <literal>[Unit]</literal> and
-    <literal>[Install]</literal> sections. The socket specific configuration options are
-    configured in the <literal>[Socket]</literal> section.</para>
+    configuration items are configured in the generic [Unit] and
+    [Install] sections. The socket specific configuration options are
+    configured in the [Socket] section.</para>
 
     <para>Additional options are listed in
     <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
         <listitem><para>Socket units automatically gain a <varname>Before=</varname>
         dependency on the service units they activate.</para></listitem>
 
-        <listitem><para>Socket units referring to file system paths (such as AF_UNIX
-        sockets or FIFOs) implicitly gain <varname>Requires=</varname> and
-        <varname>After=</varname> dependencies on all mount units
-        necessary to access those paths.</para></listitem>
+        <listitem><para>Socket units referring to file system paths (such as <constant>AF_UNIX</constant>
+        sockets or FIFOs) implicitly gain <varname>Requires=</varname> and <varname>After=</varname>
+        dependencies on all mount units necessary to access those paths.</para></listitem>
 
         <listitem><para>Socket units using the <varname>BindToDevice=</varname>
         setting automatically gain a <varname>BindsTo=</varname> and
         url="https://www.kernel.org/doc/Documentation/usb/functionfs.txt">USB
         FunctionFS</ulink> endpoints location to listen on, for
         implementation of USB gadget functions. This expects an
-        absolute file system path of functionfs mount point as the argument.
+        absolute file system path of FunctionFS mount point as the argument.
         Behavior otherwise is very similar to the <varname>ListenFIFO=</varname>
         directive above. Use this to open the FunctionFS endpoint
         <filename>ep0</filename>. When using this option, the
       <varlistentry>
         <term><varname>SocketProtocol=</varname></term>
         <listitem><para>Takes one of <option>udplite</option>
-        or <option>sctp</option>. Specifies a socket protocol
-        (<constant>IPPROTO_UDPLITE</constant>) UDP-Lite
-        (<constant>IPPROTO_SCTP</constant>) SCTP socket respectively. </para>
+        or <option>sctp</option>. The socket will use the UDP-Lite
+        (<constant>IPPROTO_UDPLITE</constant>) or SCTP
+        (<constant>IPPROTO_SCTP</constant>) protocol, respectively.</para>
         </listitem>
       </varlistentry>
 
 
       <varlistentry>
         <term><varname>BindToDevice=</varname></term>
-        <listitem><para>Specifies a network interface name to bind
-        this socket to. If set, traffic will only be accepted from the
-        specified network interfaces. This controls the
-        SO_BINDTODEVICE socket option (see <citerefentry
-        project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry>
-        for details). If this option is used, an implicit dependency
-        from this socket unit on the network interface device unit
-        (<citerefentry><refentrytitle>systemd.device</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-        is created. Note that setting this parameter might result in
-        additional dependencies to be added to the unit (see
+        <listitem><para>Specifies a network interface name to bind this socket to. If set, traffic will only
+        be accepted from the specified network interfaces. This controls the
+        <constant>SO_BINDTODEVICE</constant> socket option (see <citerefentry
+        project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+        details). If this option is used, an implicit dependency from this socket unit on the network
+        interface device unit is created
+        (see <citerefentry><refentrytitle>systemd.device</refentrytitle><manvolnum>5</manvolnum></citerefentry>).
+        Note that setting this parameter might result in additional dependencies to be added to the unit (see
         above).</para></listitem>
       </varlistentry>
 
         <term><varname>SocketUser=</varname></term>
         <term><varname>SocketGroup=</varname></term>
 
-        <listitem><para>Takes a UNIX user/group name. When specified,
-        all AF_UNIX sockets and FIFO nodes in the file system are
-        owned by the specified user and group. If unset (the default),
-        the nodes are owned by the root user/group (if run in system
-        context) or the invoking user/group (if run in user context).
-        If only a user is specified but no group, then the group is
+        <listitem><para>Takes a UNIX user/group name. When specified, all <constant>AF_UNIX</constant>
+        sockets and FIFO nodes in the file system are owned by the specified user and group. If unset (the
+        default), the nodes are owned by the root user/group (if run in system context) or the invoking
+        user/group (if run in user context).  If only a user is specified but no group, then the group is
         derived from the user's default group.</para></listitem>
       </varlistentry>
 
         to work unmodified with systemd socket
         activation.</para>
 
-        <para>For IPv4 and IPv6 connections, the <varname>REMOTE_ADDR</varname>
-        environment variable will contain the remote IP address, and <varname>REMOTE_PORT</varname>
-        will contain the remote port. This is the same as the format used by CGI.
-        For SOCK_RAW, the port is the IP protocol.</para></listitem>
+        <para>For IPv4 and IPv6 connections, the <varname>REMOTE_ADDR</varname> environment variable will
+        contain the remote IP address, and <varname>REMOTE_PORT</varname> will contain the remote port. This
+        is the same as the format used by CGI. For <constant>SOCK_RAW</constant>, the port is the IP
+        protocol.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
        <varlistentry>
         <term><varname>KeepAlive=</varname></term>
-        <listitem><para>Takes a boolean argument. If true, the TCP/IP
-        stack will send a keep alive message after 2h (depending on
-        the configuration of
-        <filename>/proc/sys/net/ipv4/tcp_keepalive_time</filename>)
-        for all TCP streams accepted on this socket. This controls the
-        SO_KEEPALIVE socket option (see
-        <citerefentry project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry>
-        and the <ulink
-        url="http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/">TCP
-        Keepalive HOWTO</ulink> for details.) Defaults to
-        <option>false</option>.</para></listitem>
+        <listitem><para>Takes a boolean argument. If true, the TCP/IP stack will send a keep alive message
+        after 2h (depending on the configuration of
+        <filename>/proc/sys/net/ipv4/tcp_keepalive_time</filename>) for all TCP streams accepted on this
+        socket. This controls the <constant>SO_KEEPALIVE</constant> socket option (see <citerefentry
+        project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry> and
+        the <ulink url="http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/">TCP Keepalive
+        HOWTO</ulink> for details.) Defaults to <option>false</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
       <varlistentry>
         <term><varname>KeepAliveIntervalSec=</varname></term>
-        <listitem><para>Takes time (in seconds) as argument between
-        individual keepalive probes, if the socket option SO_KEEPALIVE
-        has been set on this socket. This controls
-        the TCP_KEEPINTVL socket option (see
-        <citerefentry project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry>
-        and the <ulink
-        url="http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/">TCP
-        Keepalive HOWTO</ulink> for details.) Defaults value is 75
-        seconds.</para></listitem>
+        <listitem><para>Takes time (in seconds) as argument between individual keepalive probes, if the
+        socket option <constant>SO_KEEPALIVE</constant> has been set on this socket. This controls the
+        <constant>TCP_KEEPINTVL</constant> socket option (see <citerefentry
+        project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry> and
+        the <ulink url="http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/">TCP Keepalive
+        HOWTO</ulink> for details.) Defaults value is 75 seconds.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         algorithm works by combining a number of small outgoing
         messages, and sending them all at once. This controls the
         TCP_NODELAY socket option (see
-        <citerefentry project='die-net'><refentrytitle>tcp</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+        <citerefentry project='die-net'><refentrytitle>tcp</refentrytitle><manvolnum>7</manvolnum></citerefentry>).
         Defaults to <option>false</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>Priority=</varname></term>
-        <listitem><para>Takes an integer argument controlling the
-        priority for all traffic sent from this socket. This controls
-        the SO_PRIORITY socket option (see
-        <citerefentry project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry>
-        for details.).</para></listitem>
+        <listitem><para>Takes an integer argument controlling the priority for all traffic sent from this
+        socket. This controls the <constant>SO_PRIORITY</constant> socket option (see <citerefentry
+        project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+        details.).</para></listitem>
       </varlistentry>
 
       <varlistentry>
       <varlistentry>
         <term><varname>ReceiveBuffer=</varname></term>
         <term><varname>SendBuffer=</varname></term>
-        <listitem><para>Takes an integer argument controlling the
-        receive or send buffer sizes of this socket, respectively.
-        This controls the SO_RCVBUF and SO_SNDBUF socket options (see
-        <citerefentry project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry>
-        for details.). The usual suffixes K, M, G are supported and
-        are understood to the base of 1024.</para></listitem>
+        <listitem><para>Takes an integer argument controlling the receive or send buffer sizes of this
+        socket, respectively.  This controls the <constant>SO_RCVBUF</constant> and
+        <constant>SO_SNDBUF</constant> socket options (see <citerefentry
+        project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+        details.). The usual suffixes K, M, G are supported and are understood to the base of
+        1024.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
       <varlistentry>
         <term><varname>Mark=</varname></term>
-        <listitem><para>Takes an integer value. Controls the firewall
-        mark of packets generated by this socket. This can be used in
-        the firewall logic to filter packets from this socket. This
-        sets the SO_MARK socket option. See
-        <citerefentry project='die-net'><refentrytitle>iptables</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-        for details.</para></listitem>
+        <listitem><para>Takes an integer value. Controls the firewall mark of packets generated by this
+        socket. This can be used in the firewall logic to filter packets from this socket. This sets the
+        <constant>SO_MARK</constant> socket option. See <citerefentry
+        project='die-net'><refentrytitle>iptables</refentrytitle><manvolnum>8</manvolnum></citerefentry> for
+        details.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>ReusePort=</varname></term>
-        <listitem><para>Takes a boolean value. If true, allows
-        multiple
-        <citerefentry><refentrytitle>bind</refentrytitle><manvolnum>2</manvolnum></citerefentry>s
-        to this TCP or UDP port. This controls the SO_REUSEPORT socket
-        option. See
-        <citerefentry project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry>
-        for details.</para></listitem>
+        <listitem><para>Takes a boolean value. If true, allows multiple
+        <citerefentry><refentrytitle>bind</refentrytitle><manvolnum>2</manvolnum></citerefentry>s to this TCP
+        or UDP port. This controls the <constant>SO_REUSEPORT</constant> socket option. See <citerefentry
+        project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+        details.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
       <varlistentry>
         <term><varname>Broadcast=</varname></term>
-        <listitem><para>Takes a boolean value. This controls the
-        SO_BROADCAST socket option, which allows broadcast datagrams
-        to be sent from this socket. Defaults to
+        <listitem><para>Takes a boolean value. This controls the <constant>SO_BROADCAST</constant> socket
+        option, which allows broadcast datagrams to be sent from this socket. Defaults to
         <option>false</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>PassCredentials=</varname></term>
-        <listitem><para>Takes a boolean value. This controls the
-        SO_PASSCRED socket option, which allows
-        <constant>AF_UNIX</constant> sockets to receive the
-        credentials of the sending process in an ancillary message.
-        Defaults to <option>false</option>.</para></listitem>
+        <listitem><para>Takes a boolean value. This controls the <constant>SO_PASSCRED</constant> socket
+        option, which allows <constant>AF_UNIX</constant> sockets to receive the credentials of the sending
+        process in an ancillary message. Defaults to <option>false</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>PassSecurity=</varname></term>
-        <listitem><para>Takes a boolean value. This controls the
-        SO_PASSSEC socket option, which allows
-        <constant>AF_UNIX</constant> sockets to receive the security
-        context of the sending process in an ancillary message.
-        Defaults to <option>false</option>.</para></listitem>
+        <listitem><para>Takes a boolean value. This controls the <constant>SO_PASSSEC</constant> socket
+        option, which allows <constant>AF_UNIX</constant> sockets to receive the security context of the
+        sending process in an ancillary message.  Defaults to <option>false</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
       <varlistentry>
         <term><varname>TCPCongestion=</varname></term>
-        <listitem><para>Takes a string value. Controls the TCP
-        congestion algorithm used by this socket. Should be one of
-        "westwood", "veno", "cubic", "lp" or any other available
-        algorithm supported by the IP stack. This setting applies only
-        to stream sockets.</para></listitem>
+        <listitem><para>Takes a string value. Controls the TCP congestion algorithm used by this
+        socket. Should be one of <literal>westwood</literal>, <literal>veno</literal>,
+        <literal>cubic</literal>, <literal>lp</literal> or any other available algorithm supported by the IP
+        stack. This setting applies only to stream sockets.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
       <varlistentry>
         <term><varname>RemoveOnStop=</varname></term>
-        <listitem><para>Takes a boolean argument. If enabled, any file
-        nodes created by this socket unit are removed when it is
-        stopped. This applies to AF_UNIX sockets in the file system,
-        POSIX message queues, FIFOs, as well as any symlinks to them
-        configured with <varname>Symlinks=</varname>. Normally, it
-        should not be necessary to use this option, and is not
-        recommended as services might continue to run after the socket
-        unit has been terminated and it should still be possible to
-        communicate with them via their file system node. Defaults to
+        <listitem><para>Takes a boolean argument. If enabled, any file nodes created by this socket unit are
+        removed when it is stopped. This applies to <constant>AF_UNIX</constant> sockets in the file system,
+        POSIX message queues, FIFOs, as well as any symlinks to them configured with
+        <varname>Symlinks=</varname>. Normally, it should not be necessary to use this option, and is not
+        recommended as services might continue to run after the socket unit has been terminated and it should
+        still be possible to communicate with them via their file system node. Defaults to
         off.</para></listitem>
       </varlistentry>
 
index 9f368f903dc8dd2421225c42ab786d863379609a..a948969a8f8efc07ad2700971c7fc8916933ec76 100644 (file)
             this unit (or <filename>multi-user.target</filename>) during
             installation. This is best configured via
             <varname>WantedBy=graphical.target</varname> in the unit's
-            <literal>[Install]</literal> section.</para>
+            [Install] section.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
             add <varname>Wants=</varname> dependencies for their unit to
             this unit during installation. This is best configured via
             <varname>WantedBy=multi-user.target</varname> in the unit's
-            <literal>[Install]</literal> section.</para>
+            [Install] section.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
             applications get pulled in via <varname>Wants=</varname>
             dependencies from this unit. This is best configured via a
             <varname>WantedBy=paths.target</varname> in the path unit's
-            <literal>[Install]</literal> section.</para>
+            [Install] section.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
             <para>Adding slice units to <filename>slices.target</filename> is generally not
             necessary. Instead, when some unit that uses <varname>Slice=</varname> is started, the
             specified slice will be started automatically. Adding
-            <varname>WantedBy=slices.target</varname> lines to the <literal>[Install]</literal>
+            <varname>WantedBy=slices.target</varname> lines to the [Install]
             section should only be done for units that need to be always active. In that case care
             needs to be taken to avoid creating a loop through the automatic dependencies on
             "parent" slices.</para>
             <varname>Wants=</varname> dependencies to this unit for
             their socket unit during installation. This is best
             configured via a <varname>WantedBy=sockets.target</varname>
-            in the socket unit's <literal>[Install]</literal>
+            in the socket unit's [Install]
             section.</para>
           </listitem>
         </varlistentry>
             applications get pulled in via <varname>Wants=</varname>
             dependencies from this unit. This is best configured via
             <varname>WantedBy=timers.target</varname> in the timer
-            unit's <literal>[Install]</literal> section.</para>
+            unit's [Install] section.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
             <para>By default, all user processes and services started on
             behalf of the user, including the per-user systemd instance
             are found in this slice.  This is pulled in by
-            <filename>systemd-logind.service</filename></para>
+            <filename>systemd-logind.service</filename>.</para>
           </listitem>
         </varlistentry>
 
           <listitem>
             <para>By default, all virtual machines and containers
             registered with <command>systemd-machined</command> are
-            found in this slice.  This is pulled in by
-            <filename>systemd-machined.service</filename></para>
+            found in this slice. This is pulled in by
+            <filename>systemd-machined.service</filename>.</para>
           </listitem>
         </varlistentry>
       </variablelist>
             <para>This target is active whenever any graphical session is running. It is used to
             stop user services which only apply to a graphical (X, Wayland, etc.) session when the
             session is terminated. Such services should have
-            <literal>PartOf=graphical-session.target</literal> in their <literal>[Unit]</literal>
+            <literal>PartOf=graphical-session.target</literal> in their [Unit]
             section. A target for a particular session (e. g.
             <filename>gnome-session.target</filename>) starts and stops
             <literal>graphical-session.target</literal> with
index c4160290a5142787f38c00cf9c95c7a0fa0d8efc..4b1f850af4f3c1444e758f8d00f16312065b98b2 100644 (file)
@@ -37,9 +37,9 @@
     this unit type. See
     <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     for the common options of all unit configuration files. The common
-    configuration items are configured in the generic <literal>[Unit]</literal> and
-    <literal>[Install]</literal> sections. The swap specific configuration options are
-    configured in the <literal>[Swap]</literal> section.</para>
+    configuration items are configured in the generic [Unit] and
+    [Install] sections. The swap specific configuration options are
+    configured in the [Swap] section.</para>
 
     <para>Additional options are listed in
     <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
   <refsect1>
     <title>Options</title>
 
-    <para>Swap files must include a [Swap] section, which carries
+    <para>Swap unit files must include a [Swap] section, which carries
     information about the swap device it supervises. A number of
     options that may be used in this section are shared with other
     unit types. These options are documented in
index 04b1564b1747e8bf62f0c985354cae431b3471ce..df100ec4e7b3634eaa517e22d89ef3ed8ea161f1 100644 (file)
@@ -98,10 +98,10 @@ KeyTwo=value 2 \
        value 2 continued
 
 [Section C]
-KeyThree=value 2\
+KeyThree=value 3\
 # this line is ignored
 ; this line is ignored too
-       value 2 continued
+       value 3 continued
 </programlisting></example>
 
     <para>Boolean arguments used in configuration files can be written in
index 3052b177864c56f5123ba1b4ef642c64388083f6..a706a4588af78c3330901f045e101eb44825dfe7 100644 (file)
@@ -34,8 +34,8 @@
     <para>This unit type has no specific options. See
     <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     for the common options of all unit configuration files. The common
-    configuration items are configured in the generic <literal>[Unit]</literal> and
-    <literal>[Install]</literal> sections. A separate <literal>[Target]</literal> section does not exist,
+    configuration items are configured in the generic [Unit] and
+    [Install] sections. A separate [Target] section does not exist,
     since no target-specific options may be configured.</para>
 
     <para>Target units do not offer any additional functionality on
index b4656b05d10ba07e108cd3023e75f0410ff82206..5b7800e78b467b329676f6095bbf137165b31dcb 100644 (file)
   <refsect1>
     <title>Parsing Timestamps</title>
 
-    <para>When parsing, systemd will accept a similar syntax, but expects no timezone specification, unless it is given
-    as the literal string <literal>UTC</literal> (for the UTC timezone), or is specified to be the locally configured
-    timezone, or the timezone name in the IANA timezone database format. The complete list of timezones
-    supported on your system can be obtained using the <literal>timedatectl list-timezones</literal>
-    (see <citerefentry><refentrytitle>timedatectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>).
-    Using IANA format is recommended over local timezone names, as less prone to errors (eg: with local timezone it's possible to
-    specify daylight saving time in winter, while it's incorrect). The weekday specification is optional, but when
-    the weekday is specified, it must either be in the abbreviated (<literal>Wed</literal>) or non-abbreviated
-    (<literal>Wednesday</literal>) English language form (case does not matter), and is not subject to the locale
-    choice of the user.  Either the date, or the time part may be omitted, in which case the current date or 00:00:00,
-    respectively, is assumed. The seconds component of the time may also be omitted, in which case ":00" is
-    assumed. Year numbers may be specified in full or may be abbreviated (omitting the century).</para>
+    <para>When parsing, systemd will accept a similar syntax, but expects no timezone specification, unless
+    it is given as the literal string <literal>UTC</literal> (for the UTC timezone), or is specified to be
+    the locally configured timezone, or the timezone name in the IANA timezone database format. The complete
+    list of timezones supported on your system can be obtained using the <literal>timedatectl
+    list-timezones</literal> (see
+    <citerefentry><refentrytitle>timedatectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>).  Using
+    IANA format is recommended over local timezone names, as less prone to errors (e.g. with local timezone
+    it's possible to specify daylight saving time in winter, even though that is not correct). The weekday
+    specification is optional, but when the weekday is specified, it must either be in the abbreviated
+    (<literal>Wed</literal>) or non-abbreviated (<literal>Wednesday</literal>) English language form (case
+    does not matter), and is not subject to the locale choice of the user. Either the date, or the time part
+    may be omitted, in which case the current date or 00:00:00, respectively, is assumed. The seconds
+    component of the time may also be omitted, in which case ":00" is assumed. Year numbers may be specified
+    in full or may be abbreviated (omitting the century).</para>
 
     <para>A timestamp is considered invalid if a weekday is specified and the date does not match the specified day of
     the week.</para>
@@ -282,7 +284,7 @@ Wed..Sat,Tue 12-10-15 1:2:3 → Tue..Sat 2012-10-15 01:02:03
       <para>Use the <command>calendar</command> command of
       <citerefentry><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry> to validate
       and normalize calendar time specifications for testing purposes. The tool also calculates when a specified
-      calendar event would elapse next.</para>
+      calendar event would occur next.</para>
   </refsect1>
 
   <refsect1>
index 040b8e28939eadd20bbc887823f67c21e0c008e7..582240271284781312d0ed98c6674f9da8e04d77 100644 (file)
@@ -35,9 +35,9 @@
     this unit type. See
     <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     for the common options of all unit configuration files. The common
-    configuration items are configured in the generic <literal>[Unit]</literal> and
-    <literal>[Install]</literal> sections. The timer specific configuration options are
-    configured in the <literal>[Timer]</literal> section.</para>
+    configuration items are configured in the generic [Unit] and
+    [Install] sections. The timer specific configuration options are
+    configured in the [Timer] section.</para>
 
     <para>For each timer file, a matching unit file must exist,
     describing the unit to activate when the timer elapses. By
index 8c0815488b1062e9f338ddf4a4da6cda393cb908..af45900cd0107a095ec4cce9832b14a6521bc297 100644 (file)
         that the listed unit is fully started up before the configured unit is started.</para>
 
         <para>When two units with an ordering dependency between them are shut down, the inverse of the
-        start-up order is applied. i.e. if a unit is configured with <varname>After=</varname> on another
+        start-up order is applied. I.e. if a unit is configured with <varname>After=</varname> on another
         unit, the former is stopped before the latter if both are shut down. Given two units with any
         ordering dependency between them, if one unit is shut down and the other is started up, the shutdown
         is ordered before the start-up. It doesn't matter if the ordering dependency is
         <option>--job-mode=</option> option for details on the
         possible values. If this is set to <literal>isolate</literal>,
         only a single unit may be listed in
-        <varname>OnFailure=</varname>..</para></listitem>
+        <varname>OnFailure=</varname>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>StartLimitAction=</varname></term>
 
         <listitem><para>Configure an additional action to take if the rate limit configured with
-        <varname>StartLimitIntervalSec=</varname> and <varname>StartLimitBurst=</varname> is hit.  Takes the same
-        values as the setting <varname>FailureAction=</varname>/<varname>SuccessAction=</varname> settings and executes
-        the same actions. If <option>none</option> is set, hitting the rate limit will trigger no action besides that
+        <varname>StartLimitIntervalSec=</varname> and <varname>StartLimitBurst=</varname> is hit. Takes the same
+        values as the <varname>FailureAction=</varname>/<varname>SuccessAction=</varname> settings. If
+        <option>none</option> is set, hitting the rate limit will trigger no action except that
         the start will not be permitted. Defaults to <option>none</option>.</para></listitem>
       </varlistentry>
 
   <refsect1>
     <title>[Install] Section Options</title>
 
-    <para>Unit files may include an <literal>[Install]</literal> section, which carries installation information for
+    <para>Unit files may include an [Install] section, which carries installation information for
     the unit. This section is not interpreted by
     <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> during runtime; it is
     used by the <command>enable</command> and <command>disable</command> commands of the
index 11954a78da8ed2ef14686298245dd50ee78b0ad0..a13d53e72cb8c558ab315a115f46e0012ae4d7b2 100644 (file)
       <varlistentry>
         <term><varname>systemd.crash_chvt</varname></term>
 
-        <listitem><para>Takes a positive integer, or a boolean argument. Can be also
-        specified without an argument, with the same effect as a positive boolean. If
-        a positive integer (in the range 1–63) is specified, the system manager (PID
-        1) will activate the specified virtual terminal (VT) when it
-        crashes. Defaults to disabled, meaning that no such switch is attempted. If
-        set to enabled, the VT the kernel messages are written to is selected.
-        </para></listitem>
+        <listitem><para>Takes a positive integer, or a boolean argument. Can be also specified without an
+        argument, with the same effect as a positive boolean. If a positive integer (in the range 1–63) is
+        specified, the system manager (PID 1) will activate the specified virtual terminal when it crashes.
+        Defaults to disabled, meaning that no such switch is attempted. If set to enabled, the virtual
+        terminal the kernel messages are written to is used instead.</para></listitem>
       </varlistentry>
 
       <varlistentry>
       this context, because they are properly namespaced. When an option is specified both on the kernel
       command line, and as a normal command line argument, the latter has higher precedence.</para>
 
-      <para>When <command>systemd</command> is used a user manager, the kernel command line is ignored and
+      <para>When <command>systemd</command> is used as a user manager, the kernel command line is ignored and
       the options described are understood. Nevertheless, <command>systemd</command> is usually started in
       this mode through the
       <citerefentry><refentrytitle>user@.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
       service, which is shared between all users, and it may be more convenient to use configuration files to
       modify settings, see
       <citerefentry><refentrytitle>systemd-user.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
-      or a drop-in that specifies one of the environment variables listed above in "Environment, see
+      or a drop-in that specifies one of the environment variables listed above in the Environment section,
+      see
       <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
 
       <variablelist>
         <varlistentry>
           <term><option>--show-status</option></term>
 
-          <listitem><para>Show terse unit status information is shown on the console during boot-up and
-          shutdown. See <varname>systemd.show_status</varname> above.</para></listitem>
+          <listitem><para>Show terse unit status information on the console during boot-up and shutdown. See
+          <varname>systemd.show_status</varname> above.</para></listitem>
         </varlistentry>
 
         <varlistentry>
index 1711915c2204787d49b0ff3df44f8b94308cb174..f312ac269289c55d89b4694c4d94458f14d9e1e2 100644 (file)
     <varlistentry id='qdisc-parent'>
       <term><varname>Parent=</varname></term>
       <listitem>
-        <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
-        <literal>clsact</literal>, <literal>ingress</literal> or a class id. The class id takes the
-        major and minor number in hexadecimal ranges 1 to ffff separated with a colon
-        (<literal>major:minor</literal>). Defaults to <literal>root</literal>.</para>
+        <para>Configures the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
+        <literal>clsact</literal>, <literal>ingress</literal> or a class identifier. The class identifier is
+        specified as the major and minor numbers in hexadecimal in the range 0x1–Oxffff separated with a
+        colon (<literal>major:minor</literal>). Defaults to <literal>root</literal>.</para>
       </listitem>
     </varlistentry>
 
     <varlistentry id='qdisc-handle'>
       <term><varname>Handle=</varname></term>
       <listitem>
-        <para>Specifies the major number of unique identifier of the qdisc, known as the handle.
-        Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
+        <para>Configures the major number of unique identifier of the qdisc, known as the handle.
+        Takes a hexadecimal number in the range 0x1–0xffff. Defaults to unset.</para>
       </listitem>
     </varlistentry>
 
     <varlistentry id='tclass-parent'>
       <term><varname>Parent=</varname></term>
       <listitem>
-        <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
-        or a qdisc id. The qdisc id takes the major and minor number in hexadecimal ranges 1 to ffff
-        separated with a colon (<literal>major:minor</literal>). Defaults to <literal>root</literal>.
+        <para>Configures the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>, or a
+        qdisc identifier. The qdisc identifier is specified as the major and minor numbers in hexadecimal in
+        the range 0x1–Oxffff separated with a colon (<literal>major:minor</literal>). Defaults to
+        <literal>root</literal>.
         </para>
       </listitem>
     </varlistentry>
@@ -38,8 +39,9 @@
     <varlistentry id='tclass-classid'>
       <term><varname>ClassId=</varname></term>
       <listitem>
-        <para>Specifies the major and minur number of unique identifier of the class, known as the
-        class ID. Each number is in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
+        <para>Configues the unique identifier of the class. It is specified as the major and minor numbers in
+        hexadecimal in the range 0x1–Oxffff separated with a colon (<literal>major:minor</literal>).
+        Defaults to unset.</para>
       </listitem>
     </varlistentry>
   </variablelist>
index c64d015400cdb67ed017fee25b194fd5f65e9589..44694e803319372d0c4fbd7da29523ae83c1b07a 100644 (file)
@@ -41,7 +41,7 @@
   <refsect1>
     <title>Options</title>
 
-    <para>The following settings are configured in the <literal>[Time]</literal> section:</para>
+    <para>The following settings are configured in the [Time] section:</para>
 
     <variablelist class='network-directives'>
 
index 606ce673b5645d8ba33a12e8444b8d4702111d93..777393ab750f5754d811ad465a28ca9970c563e2 100644 (file)
     <title>Well-Known Services</title>
 
     <para>The <command>userdbctl services</command> command will list all currently running services that
-    provide user or group definitions to the system. The following are well-known services are shown among
-    this list.</para>
+    provide user or group definitions to the system. The following well-known services are shown among
+    this list:</para>
 
     <variablelist>
-
       <varlistentry>
         <term><constant>io.systemd.DynamicUser</constant></term>
 
 
     <para>Note that <command>userdbctl</command> has internal support for NSS-based lookups too. This means
     that if neither <constant>io.systemd.Multiplexer</constant> nor
-    <constant>io.systemd.NameSeviceSwitch</constant> are running look-ups into the the basic user/group
+    <constant>io.systemd.NameSeviceSwitch</constant> are running look-ups into the basic user/group
     databases will still work.</para>
   </refsect1>
 
index 08f322117f5cc0df37a005a5c90b0126aba500c4..fc75fddf6b8184b3801a06c1af42c7c49556371c 100644 (file)
@@ -1153,6 +1153,17 @@ else
 endif
 conf.set10('HAVE_P11KIT', have)
 
+want_libfido2 = get_option('libfido2')
+if want_libfido2 != 'false' and not skip_deps
+        libfido2 = dependency('libfido2',
+                              required : want_libfido2 == 'true')
+        have = libfido2.found()
+else
+        have = false
+        libfido2 = []
+endif
+conf.set10('HAVE_LIBFIDO2', have)
+
 want_elfutils = get_option('elfutils')
 if want_elfutils != 'false' and not skip_deps
         libdw = dependency('libdw',
@@ -2146,7 +2157,8 @@ if conf.get('ENABLE_HOMED') == 1
                                 libcrypt,
                                 libopenssl,
                                 libfdisk,
-                                libp11kit],
+                                libp11kit,
+                                libfido2],
                 install_rpath : rootlibexecdir,
                 install : true,
                 install_dir : rootlibexecdir)
@@ -2173,6 +2185,7 @@ if conf.get('ENABLE_HOMED') == 1
                                 libcrypt,
                                 libopenssl,
                                 libp11kit,
+                                libfido2,
                                 libpwquality],
                 install_rpath : rootlibexecdir,
                 install : true,
@@ -3575,6 +3588,7 @@ foreach tuple : [
         ['pwquality'],
         ['libfdisk'],
         ['p11kit'],
+        ['libfido2'],
         ['AUDIT'],
         ['IMA'],
         ['AppArmor'],
index e13bfb0c4b9c2de63235c8c1a413bff70648ea96..fd73d5e681f4e50b53b560c8f6a7d6bc4c8de990 100644 (file)
@@ -312,6 +312,8 @@ option('openssl', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'openssl support')
 option('p11kit', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'p11kit support')
+option('libfido2', type : 'combo', choices : ['auto', 'true', 'false'],
+       description : 'FIDO2 support')
 option('elfutils', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'elfutils support')
 option('zlib', type : 'combo', choices : ['auto', 'true', 'false'],
index 78180f638956961d420a4fb2cfc1cabbefbe072e..8805dce0dbb53c24be67efd925e9a617148f9c63 100644 (file)
--- a/po/cs.po
+++ b/po/cs.po
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: systemd master\n"
 "Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
-"POT-Creation-Date: 2020-02-29 15:12+0000\n"
-"PO-Revision-Date: 2020-03-01 13:58+0100\n"
+"POT-Creation-Date: 2020-05-30 13:27+0000\n"
+"PO-Revision-Date: 2020-07-01 16:40+0200\n"
 "Last-Translator: Daniel Rusek <mail@asciiwolf.com>\n"
 "Language-Team: Czech\n"
 "Language: cs\n"
@@ -17,7 +17,7 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
 "|| n%100>=20) ? 1 : 2);\n"
-"X-Generator: Poedit 2.3\n"
+"X-Generator: Poedit 2.3.1\n"
 
 #: src/core/org.freedesktop.systemd1.policy.in:22
 msgid "Send passphrase back to system"
@@ -740,26 +740,34 @@ msgid "Authentication is required to reset DNS settings."
 msgstr "Pro resetování nastavení DNS je vyžadováno ověření."
 
 #: src/network/org.freedesktop.network1.policy:143
+msgid "DHCP server sends force renew message"
+msgstr "DHCP server posílá zprávu vynuceného obnovení"
+
+#: src/network/org.freedesktop.network1.policy:144
+msgid "Authentication is required to send force renew message."
+msgstr "Pro poslání zprávy vynuceného obnovení je vyžadováno ověření."
+
+#: src/network/org.freedesktop.network1.policy:154
 msgid "Renew dynamic addresses"
 msgstr "Obnovit dynamické adresy"
 
-#: src/network/org.freedesktop.network1.policy:144
+#: src/network/org.freedesktop.network1.policy:155
 msgid "Authentication is required to renew dynamic addresses."
 msgstr "Pro obnovení dynamických adres je vyžadováno ověření."
 
-#: src/network/org.freedesktop.network1.policy:154
+#: src/network/org.freedesktop.network1.policy:165
 msgid "Reload network settings"
 msgstr "Znovu načíst nastavení sítě"
 
-#: src/network/org.freedesktop.network1.policy:155
+#: src/network/org.freedesktop.network1.policy:166
 msgid "Authentication is required to reload network settings."
 msgstr "Pro opětovné načtení nastavení sítě je vyžadováno ověření."
 
-#: src/network/org.freedesktop.network1.policy:165
+#: src/network/org.freedesktop.network1.policy:176
 msgid "Reconfigure network interface"
 msgstr "Přenastavit síťové rozhraní"
 
-#: src/network/org.freedesktop.network1.policy:166
+#: src/network/org.freedesktop.network1.policy:177
 msgid "Authentication is required to reconfigure network interface."
 msgstr "Pro přenastavení síťového rozhraní je vyžadováno ověření."
 
@@ -853,40 +861,47 @@ msgid ""
 "shall be enabled."
 msgstr "Pro kontrolu synchronizace času ze sítě je vyžadováno ověření."
 
-#: src/core/dbus-unit.c:356
+#: src/core/dbus-unit.c:358
 msgid "Authentication is required to start '$(unit)'."
 msgstr "Pro spuštění „$(unit)” je vyžadováno ověření."
 
-#: src/core/dbus-unit.c:357
+#: src/core/dbus-unit.c:359
 msgid "Authentication is required to stop '$(unit)'."
 msgstr "Pro vypnutí „$(unit)” je vyžadováno ověření."
 
-#: src/core/dbus-unit.c:358
+#: src/core/dbus-unit.c:360
 msgid "Authentication is required to reload '$(unit)'."
 msgstr "Pro opětovné načtení „$(unit)” je vyžadováno ověření."
 
-#: src/core/dbus-unit.c:359 src/core/dbus-unit.c:360
+#: src/core/dbus-unit.c:361 src/core/dbus-unit.c:362
 msgid "Authentication is required to restart '$(unit)'."
 msgstr "Pro restart „$(unit)” je vyžadováno ověření."
 
-#: src/core/dbus-unit.c:532
+#: src/core/dbus-unit.c:534
 msgid ""
 "Authentication is required to send a UNIX signal to the processes of "
 "'$(unit)'."
 msgstr "Pro odeslání UNIX signálu procesům „$(unit)” je vyžadováno ověření."
 
-#: src/core/dbus-unit.c:563
+#: src/core/dbus-unit.c:565
 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
 msgstr "Pro resetování chybného stavu „$(unit)” je vyžadováno ověření."
 
-#: src/core/dbus-unit.c:596
+#: src/core/dbus-unit.c:598
 msgid "Authentication is required to set properties on '$(unit)'."
 msgstr "Pro nastavení vlastností na „$(unit)” je vyžadováno ověření."
 
-#: src/core/dbus-unit.c:705
+#: src/core/dbus-unit.c:707
 msgid ""
 "Authentication is required to delete files and directories associated with "
 "'$(unit)'."
 msgstr ""
 "Pro odstranění souborů nebo adresářů souvisejících s „$(unit)” je vyžadováno "
 "ověření."
+
+#: src/core/dbus-unit.c:756
+msgid ""
+"Authentication is required to freeze or thaw the processes of '$(unit)' unit."
+msgstr ""
+"Pro zmrazení nebo rozmrazení procesů jednotky „$(unit)” je vyžadováno "
+"ověření."
index 01af759a7dc6a7334a3f7edf880dac8b2a4b6b52..8e6a12b602fc3f4a363da9fe73df07862b40e14d 100644 (file)
@@ -373,7 +373,8 @@ const char *special_glyph(SpecialGlyph code) {
                         [SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = ":-(",
                         [SPECIAL_GLYPH_UNHAPPY_SMILEY]          = ":-{",
                         [SPECIAL_GLYPH_DEPRESSED_SMILEY]        = ":-[",
-                        [SPECIAL_GLYPH_LOCK_AND_KEY]            = "o-,"
+                        [SPECIAL_GLYPH_LOCK_AND_KEY]            = "o-,",
+                        [SPECIAL_GLYPH_TOUCH]                   = "O=",    /* Yeah, not very convincing, can you do it better? */
                 },
 
                 /* UTF-8 */
@@ -415,6 +416,9 @@ const char *special_glyph(SpecialGlyph code) {
 
                         /* This emoji is a single character cell glyph in Unicode, and three in ASCII */
                         [SPECIAL_GLYPH_LOCK_AND_KEY]            = "\360\237\224\220",         /* 🔐 (actually called: CLOSED LOCK WITH KEY) */
+
+                        /* This emoji is a single character cell glyph in Unicode, and two in ASCII */
+                        [SPECIAL_GLYPH_TOUCH]                   = "\360\237\221\206",         /* 👆 (actually called: BACKHAND INDEX POINTING UP */
                 },
         };
 
index e4f9711b087a3d90cb3f403fa0ce40019a1c94b0..aa25e17f154596ce6803efbd6d1e4f0bc6bbb762 100644 (file)
@@ -65,7 +65,8 @@ typedef enum {
         SPECIAL_GLYPH_UNHAPPY_SMILEY,
         SPECIAL_GLYPH_DEPRESSED_SMILEY,
         SPECIAL_GLYPH_LOCK_AND_KEY,
-        _SPECIAL_GLYPH_MAX
+        SPECIAL_GLYPH_TOUCH,
+        _SPECIAL_GLYPH_MAX,
 } SpecialGlyph;
 
 const char *special_glyph(SpecialGlyph code) _const_;
index fd8772f377f04e36de3e612a274017f1d0f303e8..ceea8176f5b85321e7966b4e747c386f8ed79980 100644 (file)
@@ -538,6 +538,12 @@ static inline int __coverity_check_and_return__(int condition) {
                 (y) = (_t);                        \
         } while (false)
 
+/* Iterates through a specified list of pointers. Accepts NULL pointers, but uses (void*) -1 as internal marker for EOL. */
+#define FOREACH_POINTER(p, x, ...)                                                      \
+        for (typeof(p) *_l = (typeof(p)[]) { ({ p = x; }), ##__VA_ARGS__, (void*) -1 }; \
+             p != (typeof(p)) (void*) -1;                                               \
+             p = *(++_l))
+
 /* Define C11 thread_local attribute even on older gcc compiler
  * version */
 #ifndef thread_local
index 07f534f34d1f174972087c79f5df48887683eeb2..5218374b2b393355e79cb896ccd309ddc7e8b954 100644 (file)
@@ -1130,6 +1130,7 @@ int socket_bind_to_ifname(int fd, const char *ifname) {
 
 int socket_bind_to_ifindex(int fd, int ifindex) {
         char ifname[IF_NAMESIZE + 1];
+        int r;
 
         assert(fd >= 0);
 
@@ -1141,10 +1142,9 @@ int socket_bind_to_ifindex(int fd, int ifindex) {
                 return 0;
         }
 
-        if (setsockopt(fd, SOL_SOCKET, SO_BINDTOIFINDEX, &ifindex, sizeof(ifindex)) >= 0)
-                return 0;
-        if (errno != ENOPROTOOPT)
-                return -errno;
+        r = setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex);
+        if (r != -ENOPROTOOPT)
+                return r;
 
         /* Fall back to SO_BINDTODEVICE on kernels < 5.0 which didn't have SO_BINDTOIFINDEX */
         if (!format_ifname(ifindex, ifname))
index 10405b711f671bbfdd50575523e34731ec13288a..43d8b3477e17ed4ece831387afdf4aa63ac7cf24 100644 (file)
@@ -537,8 +537,7 @@ int unit_name_from_path(const char *path, const char *suffix, char **ret) {
 }
 
 int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) {
-        _cleanup_free_ char *p = NULL;
-        char *s;
+        _cleanup_free_ char *p = NULL, *s = NULL;
         int r;
 
         assert(prefix);
@@ -564,7 +563,7 @@ int unit_name_from_path_instance(const char *prefix, const char *path, const cha
         if (!unit_name_is_valid(s, UNIT_NAME_INSTANCE))
                 return -EINVAL;
 
-        *ret = s;
+        *ret = TAKE_PTR(s);
         return 0;
 }
 
index 7dd2f6664af6afee792110f95fd64e4ba77482b6..66f8856fdfdfb50f24beb61fa787767ff6d0b625 100644 (file)
@@ -777,7 +777,7 @@ bool valid_user_group_name(const char *u, ValidUserFlags flags) {
                         return false;
 
                 if (in_charset(u, "0123456789")) /* Don't allow fully numeric strings, they might be confused
-                                                  * with with UIDs (note that this test is more broad than
+                                                  * with UIDs (note that this test is more broad than
                                                   * the parse_uid() test above, as it will cover more than
                                                   * the 32bit range, and it will detect 65535 (which is in
                                                   * invalid UID, even though in the unsigned 32 bit range) */
index 77420b929fd39c0d60b6e52783d7b699a29a28ba..1f0519876653440831df9799e32cb7bdd6ba4be0 100644 (file)
@@ -1085,6 +1085,7 @@ const UnitVTable automount_vtable = {
                 "Unit\0"
                 "Automount\0"
                 "Install\0",
+        .private_section = "Automount",
 
         .can_transient = true,
         .can_fail = true,
index 2ec274df018bfe498981fbe6a208cdb0b8eb37f3..bceb049b58b6604d206000d6aafec58bf329ca9e 100644 (file)
@@ -544,7 +544,7 @@ int bpf_firewall_compile(Unit *u) {
                                             "BPF_F_ALLOW_MULTI is not supported on this manager, not doing BPF firewall on slice units.");
 
         /* Note that when we compile a new firewall we first flush out the access maps and the BPF programs themselves,
-         * but we reuse the the accounting maps. That way the firewall in effect always maps to the actual
+         * but we reuse the accounting maps. That way the firewall in effect always maps to the actual
          * configuration, but we don't flush out the accounting unnecessarily */
 
         u->ip_bpf_ingress = bpf_program_unref(u->ip_bpf_ingress);
index 4bee1b19665edcdb8f9597f8d7b393dd335b8a34..078aa1441805b56e445953e1d740b53a67194ac4 100644 (file)
@@ -2855,7 +2855,7 @@ static int setup_keyring(
         }
 
 out:
-        /* Revert back uid & gid for the the last time, and exit */
+        /* Revert back uid & gid for the last time, and exit */
         /* no extra logging, as only the first already reported error matters */
         if (getuid() != saved_uid)
                 (void) setreuid(saved_uid, -1);
index f9c974f9b5c5e0e0e1a621b432de5a206d3d39a6..526ed210b34e7cd477f290ccc04b4cbe57dc305f 100644 (file)
@@ -668,7 +668,7 @@ int config_parse_kill_mode(
         if (m == KILL_NONE)
                 log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Unit configured to use KillMode=none. "
-                           "This is unsafe, as it disables systemd's process life-cycle management for the service. "
+                           "This is unsafe, as it disables systemd's process lifecycle management for the service. "
                            "Please update your service to use a safer KillMode=, such as 'mixed' or 'control-group'. "
                            "Support for KillMode=none is deprecated and will eventually be removed.");
 
index 2154c8eb1210f727760d51ca6815df669b6839ee..4a376976e94f987c416a60fe26e6c3f48bb8d142 100644 (file)
@@ -2320,6 +2320,7 @@ static void reset_arguments(void) {
 
         arg_random_seed = mfree(arg_random_seed);
         arg_random_seed_size = 0;
+        arg_clock_usec = 0;
 }
 
 static int parse_configuration(const struct rlimit *saved_rlimit_nofile,
index 785a46947895647a81e6a7678d23bd90ee8fd72f..93e077ce6fb7ce1d8d6d69b0fda4987caaaa05d4 100644 (file)
@@ -747,7 +747,7 @@ static int mount_private_dev(MountEntry *m) {
 
         NULSTR_FOREACH(d, devnodes) {
                 r = clone_device_node(d, temporary_mount, &can_mknod);
-                /* ENXIO means the the *source* is not a device file, skip creation in that case */
+                /* ENXIO means the *source* is not a device file, skip creation in that case */
                 if (r < 0 && r != -ENXIO)
                         goto fail;
         }
diff --git a/src/home/homectl-fido2.c b/src/home/homectl-fido2.c
new file mode 100644 (file)
index 0000000..b7b2c1a
--- /dev/null
@@ -0,0 +1,539 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#if HAVE_LIBFIDO2
+#include <fido.h>
+#endif
+
+#include "ask-password-api.h"
+#include "errno-util.h"
+#include "format-table.h"
+#include "hexdecoct.h"
+#include "homectl-fido2.h"
+#include "homectl-pkcs11.h"
+#include "libcrypt-util.h"
+#include "locale-util.h"
+#include "memory-util.h"
+#include "random-util.h"
+#include "strv.h"
+
+#if HAVE_LIBFIDO2
+static int add_fido2_credential_id(
+                JsonVariant **v,
+                const void *cid,
+                size_t cid_size) {
+
+        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+        _cleanup_strv_free_ char **l = NULL;
+        _cleanup_free_ char *escaped = NULL;
+        int r;
+
+        assert(v);
+        assert(cid);
+
+        r = base64mem(cid, cid_size, &escaped);
+        if (r < 0)
+                return log_error_errno(r, "Failed to base64 encode FIDO2 credential ID: %m");
+
+        w = json_variant_ref(json_variant_by_key(*v, "fido2HmacCredential"));
+        if (w) {
+                r = json_variant_strv(w, &l);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse FIDO2 credential ID list: %m");
+
+                if (strv_contains(l, escaped))
+                        return 0;
+        }
+
+        r = strv_extend(&l, escaped);
+        if (r < 0)
+                return log_oom();
+
+        w = json_variant_unref(w);
+        r = json_variant_new_array_strv(&w, l);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create FIDO2 credential ID JSON: %m");
+
+        r = json_variant_set_field(v, "fido2HmacCredential", w);
+        if (r < 0)
+                return log_error_errno(r, "Failed to update FIDO2 credential ID: %m");
+
+        return 0;
+}
+
+static int add_fido2_salt(
+                JsonVariant **v,
+                const void *cid,
+                size_t cid_size,
+                const void *fido2_salt,
+                size_t fido2_salt_size,
+                const void *secret,
+                size_t secret_size) {
+
+        _cleanup_(json_variant_unrefp) JsonVariant *l = NULL, *w = NULL, *e = NULL;
+        _cleanup_(erase_and_freep) char *base64_encoded = NULL;
+        _cleanup_free_ char *unix_salt = NULL;
+        struct crypt_data cd = {};
+        char *k;
+        int r;
+
+        r = make_salt(&unix_salt);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate salt: %m");
+
+        /* Before using UNIX hashing on the supplied key we base64 encode it, since crypt_r() and friends
+         * expect a NUL terminated string, and we use a binary key */
+        r = base64mem(secret, secret_size, &base64_encoded);
+        if (r < 0)
+                return log_error_errno(r, "Failed to base64 encode secret key: %m");
+
+        errno = 0;
+        k = crypt_r(base64_encoded, unix_salt, &cd);
+        if (!k)
+                return log_error_errno(errno_or_else(EINVAL), "Failed to UNIX hash secret key: %m");
+
+        r = json_build(&e, JSON_BUILD_OBJECT(
+                                       JSON_BUILD_PAIR("credential", JSON_BUILD_BASE64(cid, cid_size)),
+                                       JSON_BUILD_PAIR("salt", JSON_BUILD_BASE64(fido2_salt, fido2_salt_size)),
+                                       JSON_BUILD_PAIR("hashedPassword", JSON_BUILD_STRING(k))));
+        if (r < 0)
+                return log_error_errno(r, "Failed to build FIDO2 salt JSON key object: %m");
+
+        w = json_variant_ref(json_variant_by_key(*v, "privileged"));
+        l = json_variant_ref(json_variant_by_key(w, "fido2HmacSalt"));
+
+        r = json_variant_append_array(&l, e);
+        if (r < 0)
+                return log_error_errno(r, "Failed append FIDO2 salt: %m");
+
+        r = json_variant_set_field(&w, "fido2HmacSalt", l);
+        if (r < 0)
+                return log_error_errno(r, "Failed to set FDO2 salt: %m");
+
+        r = json_variant_set_field(v, "privileged", w);
+        if (r < 0)
+                return log_error_errno(r, "Failed to update privileged field: %m");
+
+        return 0;
+}
+#endif
+
+#define FIDO2_SALT_SIZE 32
+
+int identity_add_fido2_parameters(
+                JsonVariant **v,
+                const char *device) {
+
+#if HAVE_LIBFIDO2
+        _cleanup_(fido_cbor_info_free) fido_cbor_info_t *di = NULL;
+        _cleanup_(fido_assert_free) fido_assert_t *a = NULL;
+        _cleanup_(erase_and_freep) char *used_pin = NULL;
+        _cleanup_(fido_cred_free) fido_cred_t *c = NULL;
+        _cleanup_(fido_dev_free) fido_dev_t *d = NULL;
+        _cleanup_(erase_and_freep) void *salt = NULL;
+        JsonVariant *un, *realm, *rn;
+        bool found_extension = false;
+        const void *cid, *secret;
+        const char *fido_un;
+        size_t n, cid_size, secret_size;
+        char **e;
+        int r;
+
+        /* Construction is like this: we generate a salt of 32 bytes. We then ask the FIDO2 device to
+         * HMAC-SHA256 it for us with its internal key. The result is the key used by LUKS and account
+         * authentication. LUKS and UNIX password auth all do their own salting before hashing, so that FIDO2
+         * device never sees the volume key.
+         *
+         * S = HMAC-SHA256(I, D)
+         *
+         * with: S → LUKS/account authentication key                                         (never stored)
+         *       I → internal key on FIDO2 device                              (stored in the FIDO2 device)
+         *       D → salt we generate here               (stored in the privileged part of the JSON record)
+         *
+         */
+
+        assert(v);
+        assert(device);
+
+        salt = malloc(FIDO2_SALT_SIZE);
+        if (!salt)
+                return log_oom();
+
+        r = genuine_random_bytes(salt, FIDO2_SALT_SIZE, RANDOM_BLOCK);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate salt: %m");
+
+        d = fido_dev_new();
+        if (!d)
+                return log_oom();
+
+        r = fido_dev_open(d, device);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to open FIDO2 device %s: %s", device, fido_strerr(r));
+
+        if (!fido_dev_is_fido2(d))
+                return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
+                                       "Specified device %s is not a FIDO2 device.", device);
+
+        di = fido_cbor_info_new();
+        if (!di)
+                return log_oom();
+
+        r = fido_dev_get_cbor_info(d, di);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to get CBOR device info for %s: %s", device, fido_strerr(r));
+
+        e = fido_cbor_info_extensions_ptr(di);
+        n = fido_cbor_info_extensions_len(di);
+
+        for (size_t i = 0; i < n; i++)
+                if (streq(e[i], "hmac-secret")) {
+                        found_extension = true;
+                        break;
+                }
+
+        if (!found_extension)
+                return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
+                                       "Specified device %s is a FIDO2 device, but does not support the required HMAC-SECRET extension.", device);
+
+        c = fido_cred_new();
+        if (!c)
+                return log_oom();
+
+        r = fido_cred_set_extensions(c, FIDO_EXT_HMAC_SECRET);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to enable HMAC-SECRET extension on FIDO2 credential: %s", fido_strerr(r));
+
+        r = fido_cred_set_rp(c, "io.systemd.home", "Home Directory");
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 credential relying party ID/name: %s", fido_strerr(r));
+
+        r = fido_cred_set_type(c, COSE_ES256);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 credential type to ES256: %s", fido_strerr(r));
+
+        un = json_variant_by_key(*v, "userName");
+        if (!un)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "userName field of user record is missing");
+        if (!json_variant_is_string(un))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "userName field of user record is not a string");
+
+        realm = json_variant_by_key(*v, "realm");
+        if (realm) {
+                if (!json_variant_is_string(realm))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "realm field of user record is not a string");
+
+                fido_un = strjoina(json_variant_string(un), json_variant_string(realm));
+        } else
+                fido_un = json_variant_string(un);
+
+        rn = json_variant_by_key(*v, "realName");
+        if (rn && !json_variant_is_string(rn))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "realName field of user record is not a string");
+
+        r = fido_cred_set_user(c,
+                               (const unsigned char*) fido_un, strlen(fido_un), /* We pass the user ID and name as the same */
+                               fido_un,
+                               rn ? json_variant_string(rn) : NULL,
+                               NULL /* icon URL */);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 credential user data: %s", fido_strerr(r));
+
+        r = fido_cred_set_clientdata_hash(c, (const unsigned char[32]) {}, 32);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 client data hash: %s", fido_strerr(r));
+
+        r = fido_cred_set_rk(c, FIDO_OPT_FALSE);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to turn off FIDO2 resident key option of credential: %s", fido_strerr(r));
+
+        r = fido_cred_set_uv(c, FIDO_OPT_FALSE);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to turn off FIDO2 user verification option of credential: %s", fido_strerr(r));
+
+        log_info("Initializing FIDO2 credential on security token.");
+
+        log_notice("%s%s(Hint: This might require verification of user presence on security token.)",
+                   emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
+                   emoji_enabled() ? " " : "");
+
+        r = fido_dev_make_cred(d, c, NULL);
+        if (r == FIDO_ERR_PIN_REQUIRED) {
+                _cleanup_free_ char *text = NULL;
+
+                if (asprintf(&text, "Please enter security token PIN:") < 0)
+                        return log_oom();
+
+                for (;;) {
+                        _cleanup_(strv_free_erasep) char **pin = NULL;
+                        char **i;
+
+                        r = ask_password_auto(text, "user-home", NULL, "fido2-pin", USEC_INFINITY, 0, &pin);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to acquire user PIN: %m");
+
+                        r = FIDO_ERR_PIN_INVALID;
+                        STRV_FOREACH(i, pin) {
+                                if (isempty(*i)) {
+                                        log_info("PIN may not be empty.");
+                                        continue;
+                                }
+
+                                r = fido_dev_make_cred(d, c, *i);
+                                if (r == FIDO_OK) {
+                                        used_pin = strdup(*i);
+                                        if (!used_pin)
+                                                return log_oom();
+                                        break;
+                                }
+                                if (r != FIDO_ERR_PIN_INVALID)
+                                        break;
+                        }
+
+                        if (r != FIDO_ERR_PIN_INVALID)
+                                break;
+
+                        log_notice("PIN incorrect, please try again.");
+                }
+        }
+        if (r == FIDO_ERR_PIN_AUTH_BLOCKED)
+                return log_notice_errno(SYNTHETIC_ERRNO(EPERM),
+                                        "Token PIN is currently blocked, please remove and reinsert token.");
+        if (r == FIDO_ERR_ACTION_TIMEOUT)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
+                                       "Token action timeout. (User didn't interact with token quickly enough.)");
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to generate FIDO2 credential: %s", fido_strerr(r));
+
+        cid = fido_cred_id_ptr(c);
+        if (!cid)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get FIDO2 credential ID.");
+
+        cid_size = fido_cred_id_len(c);
+
+        a = fido_assert_new();
+        if (!a)
+                return log_oom();
+
+        r = fido_assert_set_extensions(a, FIDO_EXT_HMAC_SECRET);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to enable HMAC-SECRET extension on FIDO2 assertion: %s", fido_strerr(r));
+
+        r = fido_assert_set_hmac_salt(a, salt, FIDO2_SALT_SIZE);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set salt on FIDO2 assertion: %s", fido_strerr(r));
+
+        r = fido_assert_set_rp(a, "io.systemd.home");
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 assertion ID: %s", fido_strerr(r));
+
+        r = fido_assert_set_clientdata_hash(a, (const unsigned char[32]) {}, 32);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 assertion client data hash: %s", fido_strerr(r));
+
+        r = fido_assert_allow_cred(a, cid, cid_size);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to add FIDO2 assertion credential ID: %s", fido_strerr(r));
+
+        r = fido_assert_set_up(a, FIDO_OPT_FALSE);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to turn off FIDO2 assertion user presence: %s", fido_strerr(r));
+
+        log_info("Generating secret key on FIDO2 security token.");
+
+        r = fido_dev_get_assert(d, a, used_pin);
+        if (r == FIDO_ERR_UP_REQUIRED) {
+                r = fido_assert_set_up(a, FIDO_OPT_TRUE);
+                if (r != FIDO_OK)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                               "Failed to turn on FIDO2 assertion user presence: %s", fido_strerr(r));
+
+                log_notice("%s%sIn order to allow secret key generation, please verify presence on security token.",
+                           emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
+                           emoji_enabled() ? " " : "");
+
+                r = fido_dev_get_assert(d, a, used_pin);
+        }
+        if (r == FIDO_ERR_ACTION_TIMEOUT)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
+                                       "Token action timeout. (User didn't interact with token quickly enough.)");
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to ask token for assertion: %s", fido_strerr(r));
+
+        secret = fido_assert_hmac_secret_ptr(a, 0);
+        if (!secret)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve HMAC secret.");
+
+        secret_size = fido_assert_hmac_secret_len(a, 0);
+
+        r = add_fido2_credential_id(v, cid, cid_size);
+        if (r < 0)
+                return r;
+
+        r = add_fido2_salt(v,
+                           cid,
+                           cid_size,
+                           salt,
+                           FIDO2_SALT_SIZE,
+                           secret,
+                           secret_size);
+        if (r < 0)
+                return r;
+
+        /* If we acquired the PIN also include it in the secret section of the record, so that systemd-homed
+         * can use it if it needs to, given that it likely needs to decrypt the key again to pass to LUKS or
+         * fscrypt. */
+        r = identity_add_token_pin(v, used_pin);
+        if (r < 0)
+                return r;
+
+        return 0;
+#else
+        return log_error_errno(EOPNOTSUPP, "FIDO2 tokens not supported on this build.");
+#endif
+}
+
+int list_fido2_devices(void) {
+#if HAVE_LIBFIDO2
+        _cleanup_(table_unrefp) Table *t = NULL;
+        size_t allocated = 64, found = 0;
+        fido_dev_info_t *di = NULL;
+        int r;
+
+        di = fido_dev_info_new(allocated);
+        if (!di)
+                return log_oom();
+
+        r = fido_dev_info_manifest(di, allocated, &found);
+        if (r == FIDO_ERR_INTERNAL || (r == FIDO_OK && found == 0)) {
+                /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
+                log_info("No FIDO2 devices found.");
+                r = 0;
+                goto finish;
+        }
+        if (r != FIDO_OK) {
+                r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enumerate FIDO2 devices: %s", fido_strerr(r));
+                goto finish;
+        }
+
+        t = table_new("path", "manufacturer", "product");
+        if (!t) {
+                r = log_oom();
+                goto finish;
+        }
+
+        for (size_t i = 0; i < found; i++) {
+                const fido_dev_info_t *entry;
+
+                entry = fido_dev_info_ptr(di, i);
+                if (!entry) {
+                        r = log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                            "Failed to get device information for FIDO device %zu.", i);
+                        goto finish;
+                }
+
+                r = table_add_many(
+                                t,
+                                TABLE_PATH, fido_dev_info_path(entry),
+                                TABLE_STRING, fido_dev_info_manufacturer_string(entry),
+                                TABLE_STRING, fido_dev_info_product_string(entry));
+                if (r < 0) {
+                        table_log_add_error(r);
+                        goto finish;
+                }
+        }
+
+        r = table_print(t, stdout);
+        if (r < 0) {
+                log_error_errno(r, "Failed to show device table: %m");
+                goto finish;
+        }
+
+        r = 0;
+
+finish:
+        fido_dev_info_free(&di, allocated);
+        return r;
+#else
+        return log_error_errno(EOPNOTSUPP, "FIDO2 tokens not supported on this build.");
+#endif
+}
+
+int find_fido2_auto(char **ret) {
+#if HAVE_LIBFIDO2
+        _cleanup_free_ char *copy = NULL;
+        size_t di_size = 64, found = 0;
+        const fido_dev_info_t *entry;
+        fido_dev_info_t *di = NULL;
+        const char *path;
+        int r;
+
+        di = fido_dev_info_new(di_size);
+        if (!di)
+                return log_oom();
+
+        r = fido_dev_info_manifest(di, di_size, &found);
+        if (r == FIDO_ERR_INTERNAL || (r == FIDO_OK && found == 0)) {
+                /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
+                r = log_error_errno(SYNTHETIC_ERRNO(ENODEV), "No FIDO2 devices found.");
+                goto finish;
+        }
+        if (r != FIDO_OK) {
+                r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enumerate FIDO2 devices: %s", fido_strerr(r));
+                goto finish;
+        }
+        if (found > 1) {
+                r = log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ), "More than one FIDO2 device found.");
+                goto finish;
+        }
+
+        entry = fido_dev_info_ptr(di, 0);
+        if (!entry) {
+                r = log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                    "Failed to get device information for FIDO device 0.");
+                goto finish;
+        }
+
+        path = fido_dev_info_path(entry);
+        if (!path) {
+                r = log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                    "Failed to query FIDO device path.");
+                goto finish;
+        }
+
+        copy = strdup(path);
+        if (!copy) {
+                r = log_oom();
+                goto finish;
+        }
+
+        *ret = TAKE_PTR(copy);
+        r = 0;
+
+finish:
+        fido_dev_info_free(&di, di_size);
+        return r;
+#else
+        return log_error_errno(EOPNOTSUPP, "FIDO2 tokens not supported on this build.");
+#endif
+}
diff --git a/src/home/homectl-fido2.h b/src/home/homectl-fido2.h
new file mode 100644 (file)
index 0000000..0d9faef
--- /dev/null
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "json.h"
+
+int identity_add_fido2_parameters(JsonVariant **v, const char *device);
+
+int list_fido2_devices(void);
+
+int find_fido2_auto(char **ret);
diff --git a/src/home/homectl-pkcs11.c b/src/home/homectl-pkcs11.c
new file mode 100644 (file)
index 0000000..830aafa
--- /dev/null
@@ -0,0 +1,480 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "errno-util.h"
+#include "format-table.h"
+#include "hexdecoct.h"
+#include "homectl-pkcs11.h"
+#include "libcrypt-util.h"
+#include "memory-util.h"
+#include "openssl-util.h"
+#include "pkcs11-util.h"
+#include "random-util.h"
+#include "strv.h"
+
+struct pkcs11_callback_data {
+        char *pin_used;
+        X509 *cert;
+};
+
+static void pkcs11_callback_data_release(struct pkcs11_callback_data *data) {
+        erase_and_free(data->pin_used);
+        X509_free(data->cert);
+}
+
+#if HAVE_P11KIT
+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) {
+
+        _cleanup_(erase_and_freep) char *pin_used = NULL;
+        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, "home directory operation", "user-home", "pkcs11-pin", UINT64_MAX, &pin_used);
+        if (r < 0)
+                return r;
+
+        r = pkcs11_token_find_x509_certificate(m, session, uri, &object);
+        if (r < 0)
+                return r;
+
+        r = pkcs11_token_read_x509_certificate(m, session, object, &data->cert);
+        if (r < 0)
+                return r;
+
+        /* Let's read some random data off the token and write it to the kernel pool before we generate our
+         * random key from it. This way we can claim the quality of the RNG is at least as good as the
+         * kernel's and the token's pool */
+        (void) pkcs11_token_acquire_rng(m, session);
+
+        data->pin_used = TAKE_PTR(pin_used);
+        return 1;
+}
+#endif
+
+static int acquire_pkcs11_certificate(
+                const char *uri,
+                X509 **ret_cert,
+                char **ret_pin_used) {
+
+#if HAVE_P11KIT
+        _cleanup_(pkcs11_callback_data_release) struct pkcs11_callback_data data = {};
+        int r;
+
+        r = pkcs11_find_token(uri, pkcs11_callback, &data);
+        if (r == -EAGAIN) /* pkcs11_find_token() doesn't log about this error, but all others */
+                return log_error_errno(ENXIO, "Specified PKCS#11 token with URI '%s' not found.", uri);
+        if (r < 0)
+                return r;
+
+        *ret_cert = TAKE_PTR(data.cert);
+        *ret_pin_used = TAKE_PTR(data.pin_used);
+
+        return 0;
+#else
+        return log_error_errno(EOPNOTSUPP, "PKCS#11 tokens not supported on this build.");
+#endif
+}
+
+static int encrypt_bytes(
+                EVP_PKEY *pkey,
+                const void *decrypted_key,
+                size_t decrypted_key_size,
+                void **ret_encrypt_key,
+                size_t *ret_encrypt_key_size) {
+
+        _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = NULL;
+        _cleanup_free_ void *b = NULL;
+        size_t l;
+
+        ctx = EVP_PKEY_CTX_new(pkey, NULL);
+        if (!ctx)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to allocate public key context");
+
+        if (EVP_PKEY_encrypt_init(ctx) <= 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to initialize public key context");
+
+        if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to configure PKCS#1 padding");
+
+        if (EVP_PKEY_encrypt(ctx, NULL, &l, decrypted_key, decrypted_key_size) <= 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to determine encrypted key size");
+
+        b = malloc(l);
+        if (!b)
+                return log_oom();
+
+        if (EVP_PKEY_encrypt(ctx, b, &l, decrypted_key, decrypted_key_size) <= 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to determine encrypted key size");
+
+        *ret_encrypt_key = TAKE_PTR(b);
+        *ret_encrypt_key_size = l;
+
+        return 0;
+}
+
+static int add_pkcs11_encrypted_key(
+                JsonVariant **v,
+                const char *uri,
+                const void *encrypted_key, size_t encrypted_key_size,
+                const void *decrypted_key, size_t decrypted_key_size) {
+
+        _cleanup_(json_variant_unrefp) JsonVariant *l = NULL, *w = NULL, *e = NULL;
+        _cleanup_(erase_and_freep) char *base64_encoded = NULL;
+        _cleanup_free_ char *salt = NULL;
+        struct crypt_data cd = {};
+        char *k;
+        int r;
+
+        assert(v);
+        assert(uri);
+        assert(encrypted_key);
+        assert(encrypted_key_size > 0);
+        assert(decrypted_key);
+        assert(decrypted_key_size > 0);
+
+        r = make_salt(&salt);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate salt: %m");
+
+        /* Before using UNIX hashing on the supplied key we base64 encode it, since crypt_r() and friends
+         * expect a NUL terminated string, and we use a binary key */
+        r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+        if (r < 0)
+                return log_error_errno(r, "Failed to base64 encode secret key: %m");
+
+        errno = 0;
+        k = crypt_r(base64_encoded, salt, &cd);
+        if (!k)
+                return log_error_errno(errno_or_else(EINVAL), "Failed to UNIX hash secret key: %m");
+
+        r = json_build(&e, JSON_BUILD_OBJECT(
+                                       JSON_BUILD_PAIR("uri", JSON_BUILD_STRING(uri)),
+                                       JSON_BUILD_PAIR("data", JSON_BUILD_BASE64(encrypted_key, encrypted_key_size)),
+                                       JSON_BUILD_PAIR("hashedPassword", JSON_BUILD_STRING(k))));
+        if (r < 0)
+                return log_error_errno(r, "Failed to build encrypted JSON key object: %m");
+
+        w = json_variant_ref(json_variant_by_key(*v, "privileged"));
+        l = json_variant_ref(json_variant_by_key(w, "pkcs11EncryptedKey"));
+
+        r = json_variant_append_array(&l, e);
+        if (r < 0)
+                return log_error_errno(r, "Failed append PKCS#11 encrypted key: %m");
+
+        r = json_variant_set_field(&w, "pkcs11EncryptedKey", l);
+        if (r < 0)
+                return log_error_errno(r, "Failed to set PKCS#11 encrypted key: %m");
+
+        r = json_variant_set_field(v, "privileged", w);
+        if (r < 0)
+                return log_error_errno(r, "Failed to update privileged field: %m");
+
+        return 0;
+}
+
+static int add_pkcs11_token_uri(JsonVariant **v, const char *uri) {
+        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+        _cleanup_strv_free_ char **l = NULL;
+        int r;
+
+        assert(v);
+        assert(uri);
+
+        w = json_variant_ref(json_variant_by_key(*v, "pkcs11TokenUri"));
+        if (w) {
+                r = json_variant_strv(w, &l);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse PKCS#11 token list: %m");
+
+                if (strv_contains(l, uri))
+                        return 0;
+        }
+
+        r = strv_extend(&l, uri);
+        if (r < 0)
+                return log_oom();
+
+        w = json_variant_unref(w);
+        r = json_variant_new_array_strv(&w, l);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create PKCS#11 token URI JSON: %m");
+
+        r = json_variant_set_field(v, "pkcs11TokenUri", w);
+        if (r < 0)
+                return log_error_errno(r, "Failed to update PKCS#11 token URI list: %m");
+
+        return 0;
+}
+
+int identity_add_token_pin(JsonVariant **v, const char *pin) {
+        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL, *l = NULL;
+        _cleanup_(strv_free_erasep) char **pins = NULL;
+        int r;
+
+        assert(v);
+
+        if (isempty(pin))
+                return 0;
+
+        w = json_variant_ref(json_variant_by_key(*v, "secret"));
+        l = json_variant_ref(json_variant_by_key(w, "tokenPin"));
+
+        r = json_variant_strv(l, &pins);
+        if (r < 0)
+                return log_error_errno(r, "Failed to convert PIN array: %m");
+
+        if (strv_find(pins, pin))
+                return 0;
+
+        r = strv_extend(&pins, pin);
+        if (r < 0)
+                return log_oom();
+
+        strv_uniq(pins);
+
+        l = json_variant_unref(l);
+
+        r = json_variant_new_array_strv(&l, pins);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate new PIN array JSON: %m");
+
+        json_variant_sensitive(l);
+
+        r = json_variant_set_field(&w, "tokenPin", l);
+        if (r < 0)
+                return log_error_errno(r, "Failed to update PIN field: %m");
+
+        r = json_variant_set_field(v, "secret", w);
+        if (r < 0)
+                return log_error_errno(r, "Failed to update secret object: %m");
+
+        return 1;
+}
+
+int identity_add_pkcs11_key_data(JsonVariant **v, const char *uri) {
+        _cleanup_(erase_and_freep) void *decrypted_key = NULL, *encrypted_key = NULL;
+        _cleanup_(erase_and_freep) char *pin = NULL;
+        size_t decrypted_key_size, encrypted_key_size;
+        _cleanup_(X509_freep) X509 *cert = NULL;
+        EVP_PKEY *pkey;
+        RSA *rsa;
+        int bits;
+        int r;
+
+        assert(v);
+
+        r = acquire_pkcs11_certificate(uri, &cert, &pin);
+        if (r < 0)
+                return r;
+
+        pkey = X509_get0_pubkey(cert);
+        if (!pkey)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to extract public key from X.509 certificate.");
+
+        if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA)
+                return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "X.509 certificate does not refer to RSA key.");
+
+        rsa = EVP_PKEY_get0_RSA(pkey);
+        if (!rsa)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to acquire RSA public key from X.509 certificate.");
+
+        bits = RSA_bits(rsa);
+        log_debug("Bits in RSA key: %i", bits);
+
+        /* We use PKCS#1 padding for the RSA cleartext, hence let's leave some extra space for it, hence only
+         * generate a random key half the size of the RSA length */
+        decrypted_key_size = bits / 8 / 2;
+
+        if (decrypted_key_size < 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Uh, RSA key size too short?");
+
+        log_debug("Generating %zu bytes random key.", decrypted_key_size);
+
+        decrypted_key = malloc(decrypted_key_size);
+        if (!decrypted_key)
+                return log_oom();
+
+        r = genuine_random_bytes(decrypted_key, decrypted_key_size, RANDOM_BLOCK);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate random key: %m");
+
+        r = encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &encrypted_key, &encrypted_key_size);
+        if (r < 0)
+                return log_error_errno(r, "Failed to encrypt key: %m");
+
+        /* Add the token URI to the public part of the record. */
+        r = add_pkcs11_token_uri(v, uri);
+        if (r < 0)
+                return r;
+
+        /* Include the encrypted version of the random key we just generated in the privileged part of the record */
+        r = add_pkcs11_encrypted_key(
+                        v,
+                        uri,
+                        encrypted_key, encrypted_key_size,
+                        decrypted_key, decrypted_key_size);
+        if (r < 0)
+                return r;
+
+        /* If we acquired the PIN also include it in the secret section of the record, so that systemd-homed
+         * can use it if it needs to, given that it likely needs to decrypt the key again to pass to LUKS or
+         * fscrypt. */
+        r = identity_add_token_pin(v, pin);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+#if HAVE_P11KIT
+static int list_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) {
+
+        _cleanup_free_ char *token_uri_string = NULL, *token_label = NULL, *token_manufacturer_id = NULL, *token_model = NULL;
+        _cleanup_(p11_kit_uri_freep) P11KitUri *token_uri = NULL;
+        Table *t = userdata;
+        int uri_result, r;
+
+        assert(slot_info);
+        assert(token_info);
+
+        /* We only care about hardware devices here with a token inserted. Let's filter everything else
+         * out. (Note that the user can explicitly specify non-hardware tokens if they like, but during
+         * enumeration we'll filter those, since software tokens are typically the system certificate store
+         * and such, and it's typically not what people want to bind their home directories to.) */
+        if (!FLAGS_SET(token_info->flags, CKF_HW_SLOT|CKF_TOKEN_PRESENT))
+                return -EAGAIN;
+
+        token_label = pkcs11_token_label(token_info);
+        if (!token_label)
+                return log_oom();
+
+        token_manufacturer_id = pkcs11_token_manufacturer_id(token_info);
+        if (!token_manufacturer_id)
+                return log_oom();
+
+        token_model = pkcs11_token_model(token_info);
+        if (!token_model)
+                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));
+
+        r = table_add_many(
+                        t,
+                        TABLE_STRING, token_uri_string,
+                        TABLE_STRING, token_label,
+                        TABLE_STRING, token_manufacturer_id,
+                        TABLE_STRING, token_model);
+        if (r < 0)
+                return table_log_add_error(r);
+
+        return -EAGAIN; /* keep scanning */
+}
+#endif
+
+int list_pkcs11_tokens(void) {
+#if HAVE_P11KIT
+        _cleanup_(table_unrefp) Table *t = NULL;
+        int r;
+
+        t = table_new("uri", "label", "manufacturer", "model");
+        if (!t)
+                return log_oom();
+
+        r = pkcs11_find_token(NULL, list_callback, t);
+        if (r < 0 && r != -EAGAIN)
+                return r;
+
+        if (table_get_rows(t) <= 1) {
+                log_info("No suitable PKCS#11 tokens found.");
+                return 0;
+        }
+
+        r = table_print(t, stdout);
+        if (r < 0)
+                return log_error_errno(r, "Failed to show device table: %m");
+
+        return 0;
+#else
+        return log_error_errno(EOPNOTSUPP, "PKCS#11 tokens not supported on this build.");
+#endif
+}
+
+#if HAVE_P11KIT
+static int auto_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) {
+
+        _cleanup_(p11_kit_uri_freep) P11KitUri *token_uri = NULL;
+        char **t = userdata;
+        int uri_result;
+
+        assert(slot_info);
+        assert(token_info);
+
+        if (!FLAGS_SET(token_info->flags, CKF_HW_SLOT|CKF_TOKEN_PRESENT))
+                return -EAGAIN;
+
+        if (*t)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
+                                       "More than one suitable PKCS#11 token found.");
+
+        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, t);
+        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));
+
+        return 0;
+}
+#endif
+
+int find_pkcs11_token_auto(char **ret) {
+#if HAVE_P11KIT
+        int r;
+
+        r = pkcs11_find_token(NULL, auto_callback, ret);
+        if (r == -EAGAIN)
+                return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "No suitable PKCS#11 tokens found.");
+        if (r < 0)
+                return r;
+
+        return 0;
+#else
+        return log_error_errno(EOPNOTSUPP, "PKCS#11 tokens not supported on this build.");
+#endif
+}
diff --git a/src/home/homectl-pkcs11.h b/src/home/homectl-pkcs11.h
new file mode 100644 (file)
index 0000000..0403c73
--- /dev/null
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "json.h"
+
+int identity_add_token_pin(JsonVariant **v, const char *pin);
+
+int identity_add_pkcs11_key_data(JsonVariant **v, const char *token_uri);
+
+int list_pkcs11_tokens(void);
+int find_pkcs11_token_auto(char **ret);
index 47a506f5e0a4f4b56f0e80f723036ae4f9ca7af4..149f960f76aaf00fc51c34c3809e9273d7a057ee 100644 (file)
@@ -4,7 +4,6 @@
 
 #include "sd-bus.h"
 
-#include "alloc-util.h"
 #include "ask-password-api.h"
 #include "bus-common-errors.h"
 #include "bus-error.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "format-table.h"
-#include "format-util.h"
-#include "fs-util.h"
-#include "hexdecoct.h"
 #include "home-util.h"
-#include "libcrypt-util.h"
+#include "homectl-fido2.h"
+#include "homectl-pkcs11.h"
 #include "locale-util.h"
 #include "main-func.h"
 #include "memory-util.h"
-#include "openssl-util.h"
 #include "pager.h"
 #include "parse-util.h"
 #include "path-util.h"
@@ -31,7 +27,6 @@
 #include "pretty-print.h"
 #include "process-util.h"
 #include "pwquality-util.h"
-#include "random-util.h"
 #include "rlimit-util.h"
 #include "spawn-polkit-agent.h"
 #include "terminal-util.h"
@@ -56,6 +51,7 @@ static char **arg_identity_filter_rlimits = NULL;
 static uint64_t arg_disk_size = UINT64_MAX;
 static uint64_t arg_disk_size_relative = UINT64_MAX;
 static char **arg_pkcs11_token_uri = NULL;
+static char **arg_fido2_device = NULL;
 static bool arg_json = false;
 static JsonFormatFlags arg_json_format_flags = 0;
 static bool arg_and_resize = false;
@@ -73,6 +69,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_rlimits, json_variant_unrefp);
 STATIC_DESTRUCTOR_REGISTER(arg_identity_filter, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_identity_filter_rlimits, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_fido2_device, strv_freep);
 
 static bool identity_properties_specified(void) {
         return
@@ -83,7 +80,8 @@ static bool identity_properties_specified(void) {
                 !json_variant_is_blank_object(arg_identity_extra_rlimits) ||
                 !strv_isempty(arg_identity_filter) ||
                 !strv_isempty(arg_identity_filter_rlimits) ||
-                !strv_isempty(arg_pkcs11_token_uri);
+                !strv_isempty(arg_pkcs11_token_uri) ||
+                !strv_isempty(arg_fido2_device);
 }
 
 static int acquire_bus(sd_bus **bus) {
@@ -144,12 +142,12 @@ static int list_homes(int argc, char *argv[], void *userdata) {
                                    TABLE_UID, uid,
                                    TABLE_GID, gid);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to add row to table: %m");
+                        return table_log_add_error(r);
 
 
                 r = table_add_cell(table, &cell, TABLE_STRING, state);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to add field to table: %m");
+                        return table_log_add_error(r);
 
                 color = user_record_state_color(state);
                 if (color)
@@ -160,7 +158,7 @@ static int list_homes(int argc, char *argv[], void *userdata) {
                                    TABLE_STRING, home,
                                    TABLE_STRING, strna(empty_to_null(shell)));
                 if (r < 0)
-                        return log_error_errno(r, "Failed to add row to table: %m");
+                        return table_log_add_error(r);
         }
 
         r = sd_bus_message_exit_container(reply);
@@ -236,7 +234,7 @@ static int acquire_existing_password(const char *user_name, UserRecord *hr, bool
         return 0;
 }
 
-static int acquire_pkcs11_pin(const char *user_name, UserRecord *hr) {
+static int acquire_token_pin(const char *user_name, UserRecord *hr) {
         _cleanup_(strv_free_erasep) char **pin = NULL;
         _cleanup_free_ char *question = NULL;
         char *e;
@@ -247,9 +245,9 @@ static int acquire_pkcs11_pin(const char *user_name, UserRecord *hr) {
 
         e = getenv("PIN");
         if (e) {
-                r = user_record_set_pkcs11_pin(hr, STRV_MAKE(e), false);
+                r = user_record_set_token_pin(hr, STRV_MAKE(e), false);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to store PKCS#11 PIN: %m");
+                        return log_error_errno(r, "Failed to store token PIN: %m");
 
                 string_erase(e);
 
@@ -263,11 +261,11 @@ static int acquire_pkcs11_pin(const char *user_name, UserRecord *hr) {
                 return log_oom();
 
         /* We never cache or use cached PINs, since usually there are only very few attempts allowed before the PIN is blocked */
-        r = ask_password_auto(question, "user-home", NULL, "pkcs11-pin", USEC_INFINITY, 0, &pin);
+        r = ask_password_auto(question, "user-home", NULL, "token-pin", USEC_INFINITY, 0, &pin);
         if (r < 0)
                 return log_error_errno(r, "Failed to acquire security token PIN: %m");
 
-        r = user_record_set_pkcs11_pin(hr, pin, false);
+        r = user_record_set_token_pin(hr, pin, false);
         if (r < 0)
                 return log_error_errno(r, "Failed to store security token PIN: %m");
 
@@ -315,26 +313,38 @@ static int handle_generic_user_record_error(
 
         } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_NEEDED)) {
 
-                r = acquire_pkcs11_pin(user_name, hr);
+                r = acquire_token_pin(user_name, hr);
                 if (r < 0)
                         return r;
 
         } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED)) {
 
-                log_notice("Please authenticate physically on security token.");
+                log_notice("%s%sPlease authenticate physically on security token.",
+                           emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
+                           emoji_enabled() ? " " : "");
 
                 r = user_record_set_pkcs11_protected_authentication_path_permitted(hr, true);
                 if (r < 0)
                         return log_error_errno(r, "Failed to set PKCS#11 protected authentication path permitted flag: %m");
 
+        } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED)) {
+
+                log_notice("%s%sAuthentication requires presence verification on security token.",
+                           emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
+                           emoji_enabled() ? " " : "");
+
+                r = user_record_set_fido2_user_presence_permitted(hr, true);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to set FIDO2 user presence permitted flag: %m");
+
         } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_LOCKED))
-                return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Security token PIN is locked, please unlock security token PIN first.");
+                return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)");
 
         else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN)) {
 
                 log_notice("Security token PIN incorrect, please try again.");
 
-                r = acquire_pkcs11_pin(user_name, hr);
+                r = acquire_token_pin(user_name, hr);
                 if (r < 0)
                         return r;
 
@@ -342,7 +352,7 @@ static int handle_generic_user_record_error(
 
                 log_notice("Security token PIN incorrect, please try again (only a few tries left!).");
 
-                r = acquire_pkcs11_pin(user_name, hr);
+                r = acquire_token_pin(user_name, hr);
                 if (r < 0)
                         return r;
 
@@ -350,7 +360,7 @@ static int handle_generic_user_record_error(
 
                 log_notice("Security token PIN incorrect, please try again (only one try left!).");
 
-                r = acquire_pkcs11_pin(user_name, hr);
+                r = acquire_token_pin(user_name, hr);
                 if (r < 0)
                         return r;
         } else
@@ -889,336 +899,6 @@ static int add_disposition(JsonVariant **v) {
         return 1;
 }
 
-struct pkcs11_callback_data {
-        char *pin_used;
-        X509 *cert;
-};
-
-static void pkcs11_callback_data_release(struct pkcs11_callback_data *data) {
-        erase_and_free(data->pin_used);
-        X509_free(data->cert);
-}
-
-#if HAVE_P11KIT
-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) {
-
-        _cleanup_(erase_and_freep) char *pin_used = NULL;
-        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, "home directory operation", "user-home", "pkcs11-pin", UINT64_MAX, &pin_used);
-        if (r < 0)
-                return r;
-
-        r = pkcs11_token_find_x509_certificate(m, session, uri, &object);
-        if (r < 0)
-                return r;
-
-        r = pkcs11_token_read_x509_certificate(m, session, object, &data->cert);
-        if (r < 0)
-                return r;
-
-        /* Let's read some random data off the token and write it to the kernel pool before we generate our
-         * random key from it. This way we can claim the quality of the RNG is at least as good as the
-         * kernel's and the token's pool */
-        (void) pkcs11_token_acquire_rng(m, session);
-
-        data->pin_used = TAKE_PTR(pin_used);
-        return 1;
-}
-#endif
-
-static int acquire_pkcs11_certificate(
-                const char *uri,
-                X509 **ret_cert,
-                char **ret_pin_used) {
-
-#if HAVE_P11KIT
-        _cleanup_(pkcs11_callback_data_release) struct pkcs11_callback_data data = {};
-        int r;
-
-        r = pkcs11_find_token(uri, pkcs11_callback, &data);
-        if (r == -EAGAIN) /* pkcs11_find_token() doesn't log about this error, but all others */
-                return log_error_errno(ENXIO, "Specified PKCS#11 token with URI '%s' not found.", uri);
-        if (r < 0)
-                return r;
-
-        *ret_cert = TAKE_PTR(data.cert);
-        *ret_pin_used = TAKE_PTR(data.pin_used);
-
-        return 0;
-#else
-        return log_error_errno(EOPNOTSUPP, "PKCS#11 tokens not supported on this build.");
-#endif
-}
-
-static int encrypt_bytes(
-                EVP_PKEY *pkey,
-                const void *decrypted_key,
-                size_t decrypted_key_size,
-                void **ret_encrypt_key,
-                size_t *ret_encrypt_key_size) {
-
-        _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = NULL;
-        _cleanup_free_ void *b = NULL;
-        size_t l;
-
-        ctx = EVP_PKEY_CTX_new(pkey, NULL);
-        if (!ctx)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to allocate public key context");
-
-        if (EVP_PKEY_encrypt_init(ctx) <= 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to initialize public key context");
-
-        if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to configure PKCS#1 padding");
-
-        if (EVP_PKEY_encrypt(ctx, NULL, &l, decrypted_key, decrypted_key_size) <= 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to determine encrypted key size");
-
-        b = malloc(l);
-        if (!b)
-                return log_oom();
-
-        if (EVP_PKEY_encrypt(ctx, b, &l, decrypted_key, decrypted_key_size) <= 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to determine encrypted key size");
-
-        *ret_encrypt_key = TAKE_PTR(b);
-        *ret_encrypt_key_size = l;
-
-        return 0;
-}
-
-static int add_pkcs11_pin(JsonVariant **v, const char *pin) {
-        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL, *l = NULL;
-        _cleanup_(strv_free_erasep) char **pins = NULL;
-        int r;
-
-        assert(v);
-
-        if (isempty(pin))
-                return 0;
-
-        w = json_variant_ref(json_variant_by_key(*v, "secret"));
-        l = json_variant_ref(json_variant_by_key(w, "pkcs11Pin"));
-
-        r = json_variant_strv(l, &pins);
-        if (r < 0)
-                return log_error_errno(r, "Failed to convert PIN array: %m");
-
-        if (strv_find(pins, pin))
-                return 0;
-
-        r = strv_extend(&pins, pin);
-        if (r < 0)
-                return log_oom();
-
-        strv_uniq(pins);
-
-        l = json_variant_unref(l);
-
-        r = json_variant_new_array_strv(&l, pins);
-        if (r < 0)
-                return log_error_errno(r, "Failed to allocate new PIN array JSON: %m");
-
-        json_variant_sensitive(l);
-
-        r = json_variant_set_field(&w, "pkcs11Pin", l);
-        if (r < 0)
-                return log_error_errno(r, "Failed to update PIN field: %m");
-
-        r = json_variant_set_field(v, "secret", w);
-        if (r < 0)
-                return log_error_errno(r, "Failed to update secret object: %m");
-
-        return 1;
-}
-
-static int add_pkcs11_encrypted_key(
-                JsonVariant **v,
-                const char *uri,
-                const void *encrypted_key, size_t encrypted_key_size,
-                const void *decrypted_key, size_t decrypted_key_size) {
-
-        _cleanup_(json_variant_unrefp) JsonVariant *l = NULL, *w = NULL, *e = NULL;
-        _cleanup_(erase_and_freep) char *base64_encoded = NULL;
-        _cleanup_free_ char *salt = NULL;
-        struct crypt_data cd = {};
-        char *k;
-        int r;
-
-        assert(v);
-        assert(uri);
-        assert(encrypted_key);
-        assert(encrypted_key_size > 0);
-        assert(decrypted_key);
-        assert(decrypted_key_size > 0);
-
-        r = make_salt(&salt);
-        if (r < 0)
-                return log_error_errno(r, "Failed to generate salt: %m");
-
-        /* Before using UNIX hashing on the supplied key we base64 encode it, since crypt_r() and friends
-         * expect a NUL terminated string, and we use a binary key */
-        r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
-        if (r < 0)
-                return log_error_errno(r, "Failed to base64 encode secret key: %m");
-
-        errno = 0;
-        k = crypt_r(base64_encoded, salt, &cd);
-        if (!k)
-                return log_error_errno(errno_or_else(EINVAL), "Failed to UNIX hash secret key: %m");
-
-        r = json_build(&e, JSON_BUILD_OBJECT(
-                                       JSON_BUILD_PAIR("uri", JSON_BUILD_STRING(uri)),
-                                       JSON_BUILD_PAIR("data", JSON_BUILD_BASE64(encrypted_key, encrypted_key_size)),
-                                       JSON_BUILD_PAIR("hashedPassword", JSON_BUILD_STRING(k))));
-        if (r < 0)
-                return log_error_errno(r, "Failed to build encrypted JSON key object: %m");
-
-        w = json_variant_ref(json_variant_by_key(*v, "privileged"));
-        l = json_variant_ref(json_variant_by_key(w, "pkcs11EncryptedKey"));
-
-        r = json_variant_append_array(&l, e);
-        if (r < 0)
-                return log_error_errno(r, "Failed append PKCS#11 encrypted key: %m");
-
-        r = json_variant_set_field(&w, "pkcs11EncryptedKey", l);
-        if (r < 0)
-                return log_error_errno(r, "Failed to set PKCS#11 encrypted key: %m");
-
-        r = json_variant_set_field(v, "privileged", w);
-        if (r < 0)
-                return log_error_errno(r, "Failed to update privileged field: %m");
-
-        return 0;
-}
-
-static int add_pkcs11_token_uri(JsonVariant **v, const char *uri) {
-        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
-        _cleanup_strv_free_ char **l = NULL;
-        int r;
-
-        assert(v);
-        assert(uri);
-
-        w = json_variant_ref(json_variant_by_key(*v, "pkcs11TokenUri"));
-        if (w) {
-                r = json_variant_strv(w, &l);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to parse PKCS#11 token list: %m");
-
-                if (strv_contains(l, uri))
-                        return 0;
-        }
-
-        r = strv_extend(&l, uri);
-        if (r < 0)
-                return log_oom();
-
-        w = json_variant_unref(w);
-        r = json_variant_new_array_strv(&w, l);
-        if (r < 0)
-                return log_error_errno(r, "Failed to create PKCS#11 token URI JSON: %m");
-
-        r = json_variant_set_field(v, "pkcs11TokenUri", w);
-        if (r < 0)
-                return log_error_errno(r, "Failed to update PKCS#11 token URI list: %m");
-
-        return 0;
-}
-
-static int add_pkcs11_key_data(JsonVariant **v, const char *uri) {
-        _cleanup_(erase_and_freep) void *decrypted_key = NULL, *encrypted_key = NULL;
-        _cleanup_(erase_and_freep) char *pin = NULL;
-        size_t decrypted_key_size, encrypted_key_size;
-        _cleanup_(X509_freep) X509 *cert = NULL;
-        EVP_PKEY *pkey;
-        RSA *rsa;
-        int bits;
-        int r;
-
-        assert(v);
-
-        r = acquire_pkcs11_certificate(uri, &cert, &pin);
-        if (r < 0)
-                return r;
-
-        pkey = X509_get0_pubkey(cert);
-        if (!pkey)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to extract public key from X.509 certificate.");
-
-        if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA)
-                return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "X.509 certificate does not refer to RSA key.");
-
-        rsa = EVP_PKEY_get0_RSA(pkey);
-        if (!rsa)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to acquire RSA public key from X.509 certificate.");
-
-        bits = RSA_bits(rsa);
-        log_debug("Bits in RSA key: %i", bits);
-
-        /* We use PKCS#1 padding for the RSA cleartext, hence let's leave some extra space for it, hence only
-         * generate a random key half the size of the RSA length */
-        decrypted_key_size = bits / 8 / 2;
-
-        if (decrypted_key_size < 1)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Uh, RSA key size too short?");
-
-        log_debug("Generating %zu bytes random key.", decrypted_key_size);
-
-        decrypted_key = malloc(decrypted_key_size);
-        if (!decrypted_key)
-                return log_oom();
-
-        r = genuine_random_bytes(decrypted_key, decrypted_key_size, RANDOM_BLOCK);
-        if (r < 0)
-                return log_error_errno(r, "Failed to generate random key: %m");
-
-        r = encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &encrypted_key, &encrypted_key_size);
-        if (r < 0)
-                return log_error_errno(r, "Failed to encrypt key: %m");
-
-        /* Add the token URI to the public part of the record. */
-        r = add_pkcs11_token_uri(v, uri);
-        if (r < 0)
-                return r;
-
-        /* Include the encrypted version of the random key we just generated in the privileged part of the record */
-        r = add_pkcs11_encrypted_key(
-                        v,
-                        uri,
-                        encrypted_key, encrypted_key_size,
-                        decrypted_key, decrypted_key_size);
-        if (r < 0)
-                return r;
-
-        /* If we acquired the PIN also include it in the secret section of the record, so that systemd-homed
-         * can use it if it needs to, given that it likely needs to decrypt the key again to pass to LUKS or
-         * fscrypt. */
-        r = add_pkcs11_pin(v, pin);
-        if (r < 0)
-                return r;
-
-        return 0;
-}
-
 static int acquire_new_home_record(UserRecord **ret) {
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
         _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
@@ -1246,7 +926,13 @@ static int acquire_new_home_record(UserRecord **ret) {
                 return r;
 
         STRV_FOREACH(i, arg_pkcs11_token_uri) {
-                r = add_pkcs11_key_data(&v, *i);
+                r = identity_add_pkcs11_key_data(&v, *i);
+                if (r < 0)
+                        return r;
+        }
+
+        STRV_FOREACH(i, arg_fido2_device) {
+                r = identity_add_fido2_parameters(&v, *i);
                 if (r < 0)
                         return r;
         }
@@ -1423,7 +1109,7 @@ static int create_home(int argc, char *argv[], void *userdata) {
 
                 r = json_variant_format(hr->json, 0, &formatted);
                 if (r < 0)
-                        return r;
+                        return log_error_errno(r, "Failed to format user record: %m");
 
                 r = bus_message_new_method_call(bus, &m, bus_home_mgr, "CreateHome");
                 if (r < 0)
@@ -1437,25 +1123,28 @@ static int create_home(int argc, char *argv[], void *userdata) {
 
                 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
                 if (r < 0) {
-                        if (!sd_bus_error_has_name(&error, BUS_ERROR_LOW_PASSWORD_QUALITY))
-                                return log_error_errno(r, "Failed to create user home: %s", bus_error_message(&error, r));
-
-                        log_error_errno(r, "%s", bus_error_message(&error, r));
-                        log_info("(Use --enforce-password-policy=no to turn off password quality checks for this account.)");
-                } else
-                        break; /* done */
+                        if (sd_bus_error_has_name(&error, BUS_ERROR_LOW_PASSWORD_QUALITY)) {
+                                log_error_errno(r, "%s", bus_error_message(&error, r));
+                                log_info("(Use --enforce-password-policy=no to turn off password quality checks for this account.)");
 
-                r = user_record_set_hashed_password(hr, original_hashed_passwords);
-                if (r < 0)
-                        return r;
+                                r = user_record_set_hashed_password(hr, original_hashed_passwords);
+                                if (r < 0)
+                                        return r;
 
-                r = acquire_new_password(hr->user_name, hr, /* suggest = */ false);
-                if (r < 0)
-                        return r;
+                                r = acquire_new_password(hr->user_name, hr, /* suggest = */ false);
+                                if (r < 0)
+                                        return r;
 
-                r = user_record_make_hashed_password(hr, hr->password, /* extend = */ true);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to hash passwords: %m");
+                                r = user_record_make_hashed_password(hr, hr->password, /* extend = */ true);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to hash passwords: %m");
+                        } else {
+                                r = handle_generic_user_record_error(hr->user_name, hr, &error, r, false);
+                                if (r < 0)
+                                        return r;
+                        }
+                } else
+                        break; /* done */
         }
 
         return 0;
@@ -1566,14 +1255,20 @@ static int acquire_updated_home_record(
                 return r;
 
         STRV_FOREACH(i, arg_pkcs11_token_uri) {
-                r = add_pkcs11_key_data(&json, *i);
+                r = identity_add_pkcs11_key_data(&json, *i);
+                if (r < 0)
+                        return r;
+        }
+
+        STRV_FOREACH(i, arg_fido2_device) {
+                r = identity_add_fido2_parameters(&json, *i);
                 if (r < 0)
                         return r;
         }
 
         /* If the user supplied a full record, then add in lastChange, but do not override. Otherwise always
          * override. */
-        r = update_last_change(&json, !!arg_pkcs11_token_uri, !arg_identity);
+        r = update_last_change(&json, arg_pkcs11_token_uri || arg_fido2_device, !arg_identity);
         if (r < 0)
                 return r;
 
@@ -1592,6 +1287,26 @@ static int acquire_updated_home_record(
         return 0;
 }
 
+static int home_record_reset_human_interaction_permission(UserRecord *hr) {
+        int r;
+
+        assert(hr);
+
+        /* When we execute multiple operations one after the other, let's reset the permission to ask the
+         * user each time, so that if interaction is necessary we will be told so again and thus can print a
+         * nice message to the user, telling the user so. */
+
+        r = user_record_set_pkcs11_protected_authentication_path_permitted(hr, -1);
+        if (r < 0)
+                return log_error_errno(r, "Failed to reset PKCS#11 protected authentication path permission flag: %m");
+
+        r = user_record_set_fido2_user_presence_permitted(hr, -1);
+        if (r < 0)
+                return log_error_errno(r, "Failed to reset FIDO2 user presence permission flag: %m");
+
+        return 0;
+}
+
 static int update_home(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
@@ -1620,6 +1335,12 @@ static int update_home(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return r;
 
+        /* If we do multiple operations, let's output things more verbosely, since otherwise the repeated
+         * authentication might be confusing. */
+
+        if (arg_and_resize || arg_and_change_password)
+                log_info("Updating home directory.");
+
         for (;;) {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
@@ -1631,7 +1352,7 @@ static int update_home(int argc, char *argv[], void *userdata) {
 
                 r = json_variant_format(hr->json, 0, &formatted);
                 if (r < 0)
-                        return r;
+                        return log_error_errno(r, "Failed to format user record: %m");
 
                 (void) sd_bus_message_sensitive(m);
 
@@ -1655,13 +1376,16 @@ static int update_home(int argc, char *argv[], void *userdata) {
                         break;
         }
 
+        if (arg_and_resize)
+                log_info("Resizing home.");
+
+        (void) home_record_reset_human_interaction_permission(hr);
+
         /* Also sync down disk size to underlying LUKS/fscrypt/quota */
         while (arg_and_resize) {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
 
-                log_debug("Resizing");
-
                 r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ResizeHome");
                 if (r < 0)
                         return bus_log_create_error(r);
@@ -1688,13 +1412,16 @@ static int update_home(int argc, char *argv[], void *userdata) {
                         break;
         }
 
+        if (arg_and_change_password)
+                log_info("Synchronizing passwords and encryption keys.");
+
+        (void) home_record_reset_human_interaction_permission(hr);
+
         /* Also sync down passwords to underlying LUKS/fscrypt */
         while (arg_and_change_password) {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
 
-                log_debug("Propagating password");
-
                 r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ChangePasswordHome");
                 if (r < 0)
                         return bus_log_create_error(r);
@@ -1732,6 +1459,8 @@ static int passwd_home(int argc, char *argv[], void *userdata) {
 
         if (arg_pkcs11_token_uri)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "To change the PKCS#11 security token use 'homectl update --pkcs11-token-uri=…'.");
+        if (arg_fido2_device)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "To change the FIDO2 security token use 'homectl update --fido2-device=…'.");
         if (identity_properties_specified())
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The 'passwd' verb does not permit changing other record properties at the same time.");
 
@@ -2182,6 +1911,8 @@ static int help(int argc, char *argv[], void *userdata) {
                "                              Specify SSH public keys\n"
                "     --pkcs11-token-uri=URI   URI to PKCS#11 security token containing\n"
                "                              private key and matching X.509 certificate\n"
+               "     --fido2-device=PATH      Path to FIDO2 hidraw device with hmac-secret\n"
+               "                              extension\n"
                "\n%4$sAccount Management User Record Properties:%5$s\n"
                "     --locked=BOOL            Set locked account state\n"
                "     --not-before=TIMESTAMP   Do not allow logins before\n"
@@ -2328,6 +2059,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_EXPORT_FORMAT,
                 ARG_AUTO_LOGIN,
                 ARG_PKCS11_TOKEN_URI,
+                ARG_FIDO2_DEVICE,
                 ARG_AND_RESIZE,
                 ARG_AND_CHANGE_PASSWORD,
         };
@@ -2405,6 +2137,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "json",                        required_argument, NULL, ARG_JSON                        },
                 { "export-format",               required_argument, NULL, ARG_EXPORT_FORMAT               },
                 { "pkcs11-token-uri",            required_argument, NULL, ARG_PKCS11_TOKEN_URI            },
+                { "fido2-device",                required_argument, NULL, ARG_FIDO2_DEVICE                },
                 { "and-resize",                  required_argument, NULL, ARG_AND_RESIZE                  },
                 { "and-change-password",         required_argument, NULL, ARG_AND_CHANGE_PASSWORD         },
                 {}
@@ -3365,6 +3098,9 @@ static int parse_argv(int argc, char *argv[]) {
                 case ARG_PKCS11_TOKEN_URI: {
                         const char *p;
 
+                        if (streq(optarg, "list"))
+                                return list_pkcs11_tokens();
+
                         /* If --pkcs11-token-uri= is specified we always drop everything old */
                         FOREACH_STRING(p, "pkcs11TokenUri", "pkcs11EncryptedKey") {
                                 r = drop_from_identity(p);
@@ -3377,10 +3113,19 @@ static int parse_argv(int argc, char *argv[]) {
                                 break;
                         }
 
-                        if (!pkcs11_uri_valid(optarg))
-                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid PKCS#11 URI: %s", optarg);
+                        if (streq(optarg, "auto")) {
+                                _cleanup_free_ char *found = NULL;
+
+                                r = find_pkcs11_token_auto(&found);
+                                if (r < 0)
+                                        return r;
+                                r = strv_consume(&arg_pkcs11_token_uri, TAKE_PTR(found));
+                        } else {
+                                if (!pkcs11_uri_valid(optarg))
+                                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid PKCS#11 URI: %s", optarg);
 
-                        r = strv_extend(&arg_pkcs11_token_uri, optarg);
+                                r = strv_extend(&arg_pkcs11_token_uri, optarg);
+                        }
                         if (r < 0)
                                 return r;
 
@@ -3388,6 +3133,41 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
                 }
 
+                case ARG_FIDO2_DEVICE: {
+                        const char *p;
+
+                        if (streq(optarg, "list"))
+                                return list_fido2_devices();
+
+                        FOREACH_STRING(p, "fido2HmacCredential", "fido2HmacSalt") {
+                                r = drop_from_identity(p);
+                                if (r < 0)
+                                        return r;
+                        }
+
+                        if (isempty(optarg)) {
+                                arg_fido2_device = strv_free(arg_fido2_device);
+                                break;
+                        }
+
+                        if (streq(optarg, "auto")) {
+                                _cleanup_free_ char *found = NULL;
+
+                                r = find_fido2_auto(&found);
+                                if (r < 0)
+                                        return r;
+
+                                r = strv_consume(&arg_fido2_device, TAKE_PTR(found));
+                        } else
+                                r = strv_extend(&arg_fido2_device, optarg);
+
+                        if (r < 0)
+                                return r;
+
+                        strv_uniq(arg_fido2_device);
+                        break;
+                }
+
                 case 'j':
                         arg_json = true;
                         arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO;
@@ -3458,7 +3238,7 @@ static int parse_argv(int argc, char *argv[]) {
                 }
         }
 
-        if (!strv_isempty(arg_pkcs11_token_uri))
+        if (!strv_isempty(arg_pkcs11_token_uri) || !strv_isempty(arg_fido2_device))
                 arg_and_change_password = true;
 
         if (arg_disk_size != UINT64_MAX || arg_disk_size_relative != UINT64_MAX)
index 47ee7d23281c5e80936b6ee0fa85dc4c71c3c808..f0c157cb7d882a117e8d1923aaaea6b41aa0c7ac 100644 (file)
@@ -457,6 +457,10 @@ static int convert_worker_errno(Home *h, int e, sd_bus_error *error) {
                 return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PIN_NEEDED, "PIN for security token required.");
         case -ERFKILL:
                 return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED, "Security token requires protected authentication path.");
+        case -EMEDIUMTYPE:
+                return sd_bus_error_setf(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED, "Security token requires user presence.");
+        case -ENOSTR:
+                return sd_bus_error_setf(error, BUS_ERROR_TOKEN_ACTION_TIMEOUT, "Token action timeout. (User was supposed to verify presence or similar, by interacting with the token, and didn't do that in time.)");
         case -EOWNERDEAD:
                 return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PIN_LOCKED, "PIN of security token locked.");
         case -ENOLCK:
@@ -1357,7 +1361,13 @@ static int user_record_extend_with_binding(UserRecord *hr, UserRecord *with_bind
         return 0;
 }
 
-static int home_update_internal(Home *h, const char *verb, UserRecord *hr, UserRecord *secret, sd_bus_error *error) {
+static int home_update_internal(
+                Home *h,
+                const char *verb,
+                UserRecord *hr,
+                UserRecord *secret,
+                sd_bus_error *error) {
+
         _cleanup_(user_record_unrefp) UserRecord *new_hr = NULL, *saved_secret = NULL, *signed_hr = NULL;
         int r, c;
 
index 27c92e16e7281895b29b1c99a17a6e0f9bdfb0d5..cfceaed74274fdd2dd98ccc41d5d5103a5b03371 100644 (file)
@@ -98,7 +98,7 @@ int home_prepare_cifs(
 
 int home_activate_cifs(
                 UserRecord *h,
-                char ***pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 UserRecord **ret_home) {
 
         _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
@@ -120,7 +120,7 @@ int home_activate_cifs(
         if (r < 0)
                 return r;
 
-        r = home_refresh(h, &setup, NULL, pkcs11_decrypted_passwords, NULL, &new_home);
+        r = home_refresh(h, &setup, NULL, cache, NULL, &new_home);
         if (r < 0)
                 return r;
 
index 346be8826ea0e2f0d118605e8327626ef7adef4d..ee799e2a4b8ed9ed2dc7bb3c62c5f5011b3e14df 100644 (file)
@@ -6,6 +6,6 @@
 
 int home_prepare_cifs(UserRecord *h, bool already_activated, HomeSetup *setup);
 
-int home_activate_cifs(UserRecord *h, char ***pkcs11_decrypted_passwords, UserRecord **ret_home);
+int home_activate_cifs(UserRecord *h, PasswordCache *cache, UserRecord **ret_home);
 
 int home_create_cifs(UserRecord *h, UserRecord **ret_home);
index 8a4cb1732ac771749b53dd474724d6836e8503fe..7d00da214aa1a563ac0b8282a2775897b0a87582 100644 (file)
@@ -26,7 +26,7 @@ int home_prepare_directory(UserRecord *h, bool already_activated, HomeSetup *set
 
 int home_activate_directory(
                 UserRecord *h,
-                char ***pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 UserRecord **ret_home) {
 
         _cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *header_home = NULL;
@@ -44,11 +44,11 @@ int home_activate_directory(
         assert_se(hdo = user_record_home_directory(h));
         hd = strdupa(hdo);
 
-        r = home_prepare(h, false, pkcs11_decrypted_passwords, &setup, &header_home);
+        r = home_prepare(h, false, cache, &setup, &header_home);
         if (r < 0)
                 return r;
 
-        r = home_refresh(h, &setup, header_home, pkcs11_decrypted_passwords, NULL, &new_home);
+        r = home_refresh(h, &setup, header_home, cache, NULL, &new_home);
         if (r < 0)
                 return r;
 
@@ -193,7 +193,7 @@ int home_create_directory_or_subvolume(UserRecord *h, UserRecord **ret_home) {
 int home_resize_directory(
                 UserRecord *h,
                 bool already_activated,
-                char ***pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 HomeSetup *setup,
                 UserRecord **ret_home) {
 
@@ -205,11 +205,11 @@ int home_resize_directory(
         assert(ret_home);
         assert(IN_SET(user_record_storage(h), USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT));
 
-        r = home_prepare(h, already_activated, pkcs11_decrypted_passwords, setup, NULL);
+        r = home_prepare(h, already_activated, cache, setup, NULL);
         if (r < 0)
                 return r;
 
-        r = home_load_embedded_identity(h, setup->root_fd, NULL, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, pkcs11_decrypted_passwords, &embedded_home, &new_home);
+        r = home_load_embedded_identity(h, setup->root_fd, NULL, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, cache, &embedded_home, &new_home);
         if (r < 0)
                 return r;
 
index 047c3a70a03260f10da837a20a4829db6e7fb676..717837f348f6078ff54901d5aeff03191321db49 100644 (file)
@@ -5,6 +5,6 @@
 #include "user-record.h"
 
 int home_prepare_directory(UserRecord *h, bool already_activated, HomeSetup *setup);
-int home_activate_directory(UserRecord *h, char ***pkcs11_decrypted_passwords, UserRecord **ret_home);
+int home_activate_directory(UserRecord *h, PasswordCache *cache, UserRecord **ret_home);
 int home_create_directory_or_subvolume(UserRecord *h, UserRecord **ret_home);
-int home_resize_directory(UserRecord *h, bool already_activated, char ***pkcs11_decrypted_passwords, HomeSetup *setup, UserRecord **ret_home);
+int home_resize_directory(UserRecord *h, bool already_activated, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_home);
diff --git a/src/home/homework-fido2.c b/src/home/homework-fido2.c
new file mode 100644 (file)
index 0000000..36fe059
--- /dev/null
@@ -0,0 +1,197 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <fido.h>
+
+#include "hexdecoct.h"
+#include "homework-fido2.h"
+#include "strv.h"
+
+static int fido2_use_specific_token(
+                const char *path,
+                UserRecord *h,
+                UserRecord *secret,
+                const Fido2HmacSalt *salt,
+                char **ret) {
+
+        _cleanup_(fido_cbor_info_free) fido_cbor_info_t *di = NULL;
+        _cleanup_(fido_assert_free) fido_assert_t *a = NULL;
+        _cleanup_(fido_dev_free) fido_dev_t *d = NULL;
+        bool found_extension = false;
+        size_t n, hmac_size;
+        const void *hmac;
+        char **e;
+        int r;
+
+        d = fido_dev_new();
+        if (!d)
+                return log_oom();
+
+        r = fido_dev_open(d, path);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to open FIDO2 device %s: %s", path, fido_strerr(r));
+
+        if (!fido_dev_is_fido2(d))
+                return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
+                                       "Specified device %s is not a FIDO2 device.", path);
+
+        di = fido_cbor_info_new();
+        if (!di)
+                return log_oom();
+
+        r = fido_dev_get_cbor_info(d, di);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to get CBOR device info for %s: %s", path, fido_strerr(r));
+
+        e = fido_cbor_info_extensions_ptr(di);
+        n = fido_cbor_info_extensions_len(di);
+
+        for (size_t i = 0; i < n; i++)
+                if (streq(e[i], "hmac-secret")) {
+                        found_extension = true;
+                        break;
+                }
+
+        if (!found_extension)
+                return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
+                                       "Specified device %s is a FIDO2 device, but does not support the required HMAC-SECRET extension.", path);
+
+        a = fido_assert_new();
+        if (!a)
+                return log_oom();
+
+        r = fido_assert_set_extensions(a, FIDO_EXT_HMAC_SECRET);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to enable HMAC-SECRET extension on FIDO2 assertion: %s", fido_strerr(r));
+
+        r = fido_assert_set_hmac_salt(a, salt->salt, salt->salt_size);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set salt on FIDO2 assertion: %s", fido_strerr(r));
+
+        r = fido_assert_set_rp(a, "io.systemd.home");
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 assertion ID: %s", fido_strerr(r));
+
+        r = fido_assert_set_clientdata_hash(a, (const unsigned char[32]) {}, 32);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 assertion client data hash: %s", fido_strerr(r));
+
+        r = fido_assert_allow_cred(a, salt->credential.id, salt->credential.size);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to add FIDO2 assertion credential ID: %s", fido_strerr(r));
+
+        r = fido_assert_set_up(a, h->fido2_user_presence_permitted <= 0 ? FIDO_OPT_FALSE : FIDO_OPT_TRUE);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 assertion user presence: %s", fido_strerr(r));
+
+        log_info("Asking FIDO2 token for authentication.");
+
+        r = fido_dev_get_assert(d, a, NULL); /* try without pin first */
+        if (r == FIDO_ERR_PIN_REQUIRED) {
+                char **i;
+
+                /* OK, we needed a pin, try with all pins in turn */
+                STRV_FOREACH(i, secret->token_pin) {
+                        r = fido_dev_get_assert(d, a, *i);
+                        if (r != FIDO_ERR_PIN_INVALID)
+                                break;
+                }
+        }
+
+        switch (r) {
+        case FIDO_OK:
+                break;
+        case FIDO_ERR_NO_CREDENTIALS:
+                return log_error_errno(SYNTHETIC_ERRNO(EBADSLT),
+                                       "Wrong security token; needed credentials not present on token.");
+        case FIDO_ERR_PIN_REQUIRED:
+                return log_error_errno(SYNTHETIC_ERRNO(ENOANO),
+                                       "Security token requires PIN.");
+        case FIDO_ERR_PIN_AUTH_BLOCKED:
+                return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD),
+                                       "PIN of security token is blocked, please remove/reinsert token.");
+        case FIDO_ERR_PIN_INVALID:
+                return log_error_errno(SYNTHETIC_ERRNO(ENOLCK),
+                                       "PIN of security token incorrect.");
+        case FIDO_ERR_UP_REQUIRED:
+                return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE),
+                                       "User presence required.");
+        case FIDO_ERR_ACTION_TIMEOUT:
+                return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
+                                       "Token action timeout. (User didn't interact with token quickly enough.)");
+        default:
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to ask token for assertion: %s", fido_strerr(r));
+        }
+
+        hmac = fido_assert_hmac_secret_ptr(a, 0);
+        if (!hmac)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve HMAC secret.");
+
+        hmac_size = fido_assert_hmac_secret_len(a, 0);
+
+        r = base64mem(hmac, hmac_size, ret);
+        if (r < 0)
+                return log_error_errno(r, "Failed to base64 encode HMAC secret: %m");
+
+        return 0;
+}
+
+int fido2_use_token(UserRecord *h, UserRecord *secret, const Fido2HmacSalt *salt, char **ret) {
+        size_t allocated = 64, found = 0;
+        fido_dev_info_t *di = NULL;
+        int r;
+
+        di = fido_dev_info_new(allocated);
+        if (!di)
+                return log_oom();
+
+        r = fido_dev_info_manifest(di, allocated, &found);
+        if (r == FIDO_ERR_INTERNAL) {
+                /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
+                r = log_debug_errno(SYNTHETIC_ERRNO(EAGAIN), "Got FIDO_ERR_INTERNAL, assuming no devices.");
+                goto finish;
+        }
+        if (r != FIDO_OK) {
+                r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enumerate FIDO2 devices: %s", fido_strerr(r));
+                goto finish;
+        }
+
+        for (size_t i = 0; i < found; i++) {
+                const fido_dev_info_t *entry;
+                const char *path;
+
+                entry = fido_dev_info_ptr(di, i);
+                if (!entry) {
+                        r = log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                            "Failed to get device information for FIDO device %zu.", i);
+                        goto finish;
+                }
+
+                path = fido_dev_info_path(entry);
+                if (!path) {
+                        r = log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                            "Failed to query FIDO device path.");
+                        goto finish;
+                }
+
+                r = fido2_use_specific_token(path, h, secret, salt, ret);
+                if (!IN_SET(r,
+                            -EBADSLT, /* device doesn't understand our credential hash */
+                            -ENODEV   /* device is not a FIDO2 device with HMAC-SECRET */))
+                        goto finish;
+        }
+
+        r = -EAGAIN;
+
+finish:
+        fido_dev_info_free(&di, allocated);
+        return r;
+}
diff --git a/src/home/homework-fido2.h b/src/home/homework-fido2.h
new file mode 100644 (file)
index 0000000..d3b142a
--- /dev/null
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "user-record.h"
+
+int fido2_use_token(UserRecord *h, UserRecord *secret, const Fido2HmacSalt *salt, char **ret);
index 696e265397e2b941d775ae8f4ecfa4a96d56817e..da9bb64b712cb7b5e5a35e4094cfecdb83a69b07 100644 (file)
@@ -208,7 +208,7 @@ static int fscrypt_slot_try_many(
 }
 
 static int fscrypt_setup(
-                char **pkcs11_decrypted_passwords,
+                const PasswordCache *cache,
                 char **password,
                 HomeSetup *setup,
                 void **ret_volume_key,
@@ -230,6 +230,7 @@ static int fscrypt_setup(
                 _cleanup_free_ char *value = NULL;
                 size_t salt_size, encrypted_size;
                 const char *nr, *e;
+                char **list;
                 int n;
 
                 /* Check if this xattr has the format 'trusted.fscrypt_slot<nr>' where '<nr>' is a 32bit unsigned integer */
@@ -256,19 +257,17 @@ static int fscrypt_setup(
                 if (r < 0)
                         return log_error_errno(r, "Failed to decode encrypted key of %s: %m", xa);
 
-                r = fscrypt_slot_try_many(
-                                pkcs11_decrypted_passwords,
-                                salt, salt_size,
-                                encrypted, encrypted_size,
-                                setup->fscrypt_key_descriptor,
-                                ret_volume_key, ret_volume_key_size);
-                if (r == -ENOANO)
+                r = -ENOANO;
+                FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, password) {
                         r = fscrypt_slot_try_many(
-                                        password,
+                                        list,
                                         salt, salt_size,
                                         encrypted, encrypted_size,
                                         setup->fscrypt_key_descriptor,
                                         ret_volume_key, ret_volume_key_size);
+                        if (r != -ENOANO)
+                                break;
+                }
                 if (r < 0) {
                         if (r != -ENOANO)
                                 return r;
@@ -282,7 +281,7 @@ static int fscrypt_setup(
 int home_prepare_fscrypt(
                 UserRecord *h,
                 bool already_activated,
-                char ***pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 HomeSetup *setup) {
 
         _cleanup_(erase_and_freep) void *volume_key = NULL;
@@ -314,7 +313,7 @@ int home_prepare_fscrypt(
         memcpy(setup->fscrypt_key_descriptor, policy.master_key_descriptor, FS_KEY_DESCRIPTOR_SIZE);
 
         r = fscrypt_setup(
-                        pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL,
+                        cache,
                         h->password,
                         setup,
                         &volume_key,
@@ -584,7 +583,7 @@ int home_create_fscrypt(
 int home_passwd_fscrypt(
                 UserRecord *h,
                 HomeSetup *setup,
-                char **pkcs11_decrypted_passwords, /* the passwords acquired via PKCS#11 security tokens */
+                PasswordCache *cache,               /* the passwords acquired via PKCS#11/FIDO2 security tokens */
                 char **effective_passwords          /* new passwords */) {
 
         _cleanup_(erase_and_freep) void *volume_key = NULL;
@@ -600,7 +599,7 @@ int home_passwd_fscrypt(
         assert(setup);
 
         r = fscrypt_setup(
-                        pkcs11_decrypted_passwords,
+                        cache,
                         h->password,
                         setup,
                         &volume_key,
index aa3bcd3a69faa358222012d0563e35a5a7e08660..e5cf7baaaa456585ea50b0268fa1e8d3021a3734 100644 (file)
@@ -4,7 +4,7 @@
 #include "homework.h"
 #include "user-record.h"
 
-int home_prepare_fscrypt(UserRecord *h, bool already_activated, char ***pkcs11_decrypted_passwords, HomeSetup *setup);
+int home_prepare_fscrypt(UserRecord *h, bool already_activated, PasswordCache *cache, HomeSetup *setup);
 int home_create_fscrypt(UserRecord *h, char **effective_passwords, UserRecord **ret_home);
 
-int home_passwd_fscrypt(UserRecord *h, HomeSetup *setup, char **pkcs11_decrypted_passwords, char **effective_passwords);
+int home_passwd_fscrypt(UserRecord *h, HomeSetup *setup, PasswordCache *cache, char **effective_passwords);
index 2a782e34bc20b8c567300e4f897cf7392f861eb1..99cab0929e55e2dcace842a58504a7e986362743 100644 (file)
@@ -216,7 +216,7 @@ static int luks_setup(
                 const char *cipher_mode,
                 uint64_t volume_key_size,
                 char **passwords,
-                char **pkcs11_decrypted_passwords,
+                const PasswordCache *cache,
                 bool discard,
                 struct crypt_device **ret,
                 sd_id128_t *ret_found_uuid,
@@ -227,6 +227,7 @@ static int luks_setup(
         _cleanup_(erase_and_freep) void *vk = NULL;
         sd_id128_t p;
         size_t vks;
+        char **list;
         int r;
 
         assert(node);
@@ -278,12 +279,14 @@ static int luks_setup(
         if (!vk)
                 return log_oom();
 
-        r = luks_try_passwords(cd, pkcs11_decrypted_passwords, vk, &vks);
-        if (r == -ENOKEY) {
-                r = luks_try_passwords(cd, passwords, vk, &vks);
-                if (r == -ENOKEY)
-                        return log_error_errno(r, "No valid password for LUKS superblock.");
+        r = -ENOKEY;
+        FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, passwords) {
+                r = luks_try_passwords(cd, list, vk, &vks);
+                if (r != -ENOKEY)
+                        break;
         }
+        if (r == -ENOKEY)
+                return log_error_errno(r, "No valid password for LUKS superblock.");
         if (r < 0)
                 return log_error_errno(r, "Failed to unlocks LUKS superblock: %m");
 
@@ -312,7 +315,7 @@ static int luks_setup(
 static int luks_open(
                 const char *dm_name,
                 char **passwords,
-                char **pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 struct crypt_device **ret,
                 sd_id128_t *ret_found_uuid,
                 void **ret_volume_key,
@@ -321,6 +324,7 @@ static int luks_open(
         _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
         _cleanup_(erase_and_freep) void *vk = NULL;
         sd_id128_t p;
+        char **list;
         size_t vks;
         int r;
 
@@ -361,12 +365,14 @@ static int luks_open(
         if (!vk)
                 return log_oom();
 
-        r = luks_try_passwords(cd, pkcs11_decrypted_passwords, vk, &vks);
-        if (r == -ENOKEY) {
-                r = luks_try_passwords(cd, passwords, vk, &vks);
-                if (r == -ENOKEY)
-                        return log_error_errno(r, "No valid password for LUKS superblock.");
+        r = -ENOKEY;
+        FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, passwords) {
+                r = luks_try_passwords(cd, list, vk, &vks);
+                if (r != -ENOKEY)
+                        break;
         }
+        if (r == -ENOKEY)
+                return log_error_errno(r, "No valid password for LUKS superblock.");
         if (r < 0)
                 return log_error_errno(r, "Failed to unlocks LUKS superblock: %m");
 
@@ -622,7 +628,7 @@ static int luks_validate_home_record(
                 struct crypt_device *cd,
                 UserRecord *h,
                 const void *volume_key,
-                char ***pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 UserRecord **ret_luks_home_record) {
 
         int r, token;
@@ -727,7 +733,7 @@ static int luks_validate_home_record(
                 if (!user_record_compatible(h, lhr))
                         return log_error_errno(SYNTHETIC_ERRNO(EREMCHG), "LUKS home record not compatible with host record, refusing.");
 
-                r = user_record_authenticate(lhr, h, pkcs11_decrypted_passwords, /* strict_verify= */ true);
+                r = user_record_authenticate(lhr, h, cache, /* strict_verify= */ true);
                 if (r < 0)
                         return r;
                 assert(r > 0); /* Insist that a password was verified */
@@ -982,7 +988,7 @@ int home_prepare_luks(
                 UserRecord *h,
                 bool already_activated,
                 const char *force_image_path,
-                char ***pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 HomeSetup *setup,
                 UserRecord **ret_luks_home) {
 
@@ -1010,7 +1016,7 @@ int home_prepare_luks(
 
                 r = luks_open(setup->dm_name,
                               h->password,
-                              pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL,
+                              cache,
                               &cd,
                               &found_luks_uuid,
                               &volume_key,
@@ -1018,7 +1024,7 @@ int home_prepare_luks(
                 if (r < 0)
                         return r;
 
-                r = luks_validate_home_record(cd, h, volume_key, pkcs11_decrypted_passwords, &luks_home);
+                r = luks_validate_home_record(cd, h, volume_key, cache, &luks_home);
                 if (r < 0)
                         return r;
 
@@ -1133,7 +1139,7 @@ int home_prepare_luks(
                                h->luks_cipher_mode,
                                h->luks_volume_key_size,
                                h->password,
-                               pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL,
+                               cache,
                                user_record_luks_discard(h) || user_record_luks_offline_discard(h),
                                &cd,
                                &found_luks_uuid,
@@ -1144,7 +1150,7 @@ int home_prepare_luks(
 
                 dm_activated = true;
 
-                r = luks_validate_home_record(cd, h, volume_key, pkcs11_decrypted_passwords, &luks_home);
+                r = luks_validate_home_record(cd, h, volume_key, cache, &luks_home);
                 if (r < 0)
                         goto fail;
 
@@ -1218,7 +1224,7 @@ static void print_size_summary(uint64_t host_size, uint64_t encrypted_size, stru
 
 int home_activate_luks(
                 UserRecord *h,
-                char ***pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 UserRecord **ret_home) {
 
         _cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *luks_home_record = NULL;
@@ -1250,7 +1256,7 @@ int home_activate_luks(
                         h,
                         false,
                         NULL,
-                        pkcs11_decrypted_passwords,
+                        cache,
                         &setup,
                         &luks_home_record);
         if (r < 0)
@@ -1268,7 +1274,7 @@ int home_activate_luks(
                         h,
                         &setup,
                         luks_home_record,
-                        pkcs11_decrypted_passwords,
+                        cache,
                         &sfs,
                         &new_home);
         if (r < 0)
@@ -1464,7 +1470,7 @@ static int luks_format(
                 const char *dm_name,
                 sd_id128_t uuid,
                 const char *label,
-                char **pkcs11_decrypted_passwords,
+                const PasswordCache *cache,
                 char **effective_passwords,
                 bool discard,
                 UserRecord *hr,
@@ -1533,7 +1539,8 @@ static int luks_format(
 
         STRV_FOREACH(pp, effective_passwords) {
 
-                if (strv_contains(pkcs11_decrypted_passwords, *pp)) {
+                if (strv_contains(cache->pkcs11_passwords, *pp) ||
+                    strv_contains(cache->fido2_passwords, *pp)) {
                         log_debug("Using minimal PBKDF for slot %i", slot);
                         r = crypt_set_pbkdf_type(cd, &minimal_pbkdf);
                 } else {
@@ -1858,7 +1865,7 @@ static int home_truncate(
 
 int home_create_luks(
                 UserRecord *h,
-                char **pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 char **effective_passwords,
                 UserRecord **ret_home) {
 
@@ -2055,7 +2062,7 @@ int home_create_luks(
                         dm_name,
                         luks_uuid,
                         user_record_user_name_and_realm(h),
-                        pkcs11_decrypted_passwords,
+                        cache,
                         effective_passwords,
                         user_record_luks_discard(h) || user_record_luks_offline_discard(h),
                         h,
@@ -2561,7 +2568,7 @@ static int apply_resize_partition(int fd, sd_id128_t disk_uuids, struct fdisk_ta
 int home_resize_luks(
                 UserRecord *h,
                 bool already_activated,
-                char ***pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 HomeSetup *setup,
                 UserRecord **ret_home) {
 
@@ -2647,11 +2654,11 @@ int home_resize_luks(
                 }
         }
 
-        r = home_prepare_luks(h, already_activated, whole_disk, pkcs11_decrypted_passwords, setup, &header_home);
+        r = home_prepare_luks(h, already_activated, whole_disk, cache, setup, &header_home);
         if (r < 0)
                 return r;
 
-        r = home_load_embedded_identity(h, setup->root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, pkcs11_decrypted_passwords, &embedded_home, &new_home);
+        r = home_load_embedded_identity(h, setup->root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, cache, &embedded_home, &new_home);
         if (r < 0)
                 return r;
 
@@ -2855,13 +2862,14 @@ int home_resize_luks(
 int home_passwd_luks(
                 UserRecord *h,
                 HomeSetup *setup,
-                char **pkcs11_decrypted_passwords, /* the passwords acquired via PKCS#11 security tokens */
-                char **effective_passwords         /* new passwords */) {
+                PasswordCache *cache,      /* the passwords acquired via PKCS#11/FIDO2 security tokens */
+                char **effective_passwords /* new passwords */) {
 
         size_t volume_key_size, i, max_key_slots, n_effective;
         _cleanup_(erase_and_freep) void *volume_key = NULL;
         struct crypt_pbkdf_type good_pbkdf, minimal_pbkdf;
         const char *type;
+        char **list;
         int r;
 
         assert(h);
@@ -2886,12 +2894,14 @@ int home_passwd_luks(
         if (!volume_key)
                 return log_oom();
 
-        r = luks_try_passwords(setup->crypt_device, pkcs11_decrypted_passwords, volume_key, &volume_key_size);
-        if (r == -ENOKEY) {
-                r = luks_try_passwords(setup->crypt_device, h->password, volume_key, &volume_key_size);
-                if (r == -ENOKEY)
-                        return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "Failed to unlock LUKS superblock with supplied passwords.");
+        r = -ENOKEY;
+        FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, h->password) {
+                r = luks_try_passwords(setup->crypt_device, list, volume_key, &volume_key_size);
+                if (r != -ENOKEY)
+                        break;
         }
+        if (r == -ENOKEY)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "Failed to unlock LUKS superblock with supplied passwords.");
         if (r < 0)
                 return log_error_errno(r, "Failed to unlocks LUKS superblock: %m");
 
@@ -2911,7 +2921,8 @@ int home_passwd_luks(
                         continue;
                 }
 
-                if (strv_find(pkcs11_decrypted_passwords, effective_passwords[i])) {
+                if (strv_contains(cache->pkcs11_passwords, effective_passwords[i]) ||
+                    strv_contains(cache->fido2_passwords, effective_passwords[i])) {
                         log_debug("Using minimal PBKDF for slot %zu", i);
                         r = crypt_set_pbkdf_type(setup->crypt_device, &minimal_pbkdf);
                 } else {
@@ -3008,9 +3019,10 @@ static int luks_try_resume(
         return -ENOKEY;
 }
 
-int home_unlock_luks(UserRecord *h, char ***pkcs11_decrypted_passwords) {
+int home_unlock_luks(UserRecord *h, PasswordCache *cache) {
         _cleanup_free_ char *dm_name = NULL, *dm_node = NULL;
         _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+        char **list;
         int r;
 
         assert(h);
@@ -3026,12 +3038,14 @@ int home_unlock_luks(UserRecord *h, char ***pkcs11_decrypted_passwords) {
         log_info("Discovered used LUKS device %s.", dm_node);
         crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
 
-        r = luks_try_resume(cd, dm_name, pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL);
-        if (r == -ENOKEY) {
-                r = luks_try_resume(cd, dm_name, h->password);
-                if (r == -ENOKEY)
-                        return log_error_errno(r, "No valid password for LUKS superblock.");
+        r = -ENOKEY;
+        FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, h->password) {
+                r = luks_try_resume(cd, dm_name, list);
+                if (r != -ENOKEY)
+                        break;
         }
+        if (r == -ENOKEY)
+                return log_error_errno(r, "No valid password for LUKS superblock.");
         if (r < 0)
                 return log_error_errno(r, "Failed to resume LUKS superblock: %m");
 
index bd51f5da50ae753065ca216fbd98350892e749ab..b51f1ad7a01d07f747ed67b9f6d1ad86c8c359f7 100644 (file)
@@ -5,24 +5,24 @@
 #include "homework.h"
 #include "user-record.h"
 
-int home_prepare_luks(UserRecord *h, bool already_activated, const char *force_image_path, char ***pkcs11_decrypted_passwords, HomeSetup *setup, UserRecord **ret_luks_home);
+int home_prepare_luks(UserRecord *h, bool already_activated, const char *force_image_path, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_luks_home);
 
-int home_activate_luks(UserRecord *h, char ***pkcs11_decrypted_passwords, UserRecord **ret_home);
+int home_activate_luks(UserRecord *h, PasswordCache *cache, UserRecord **ret_home);
 int home_deactivate_luks(UserRecord *h);
 int home_trim_luks(UserRecord *h);
 
 int home_store_header_identity_luks(UserRecord *h, HomeSetup *setup, UserRecord *old_home);
 
-int home_create_luks(UserRecord *h, char **pkcs11_decrypted_passwords, char **effective_passwords, UserRecord **ret_home);
+int home_create_luks(UserRecord *h, PasswordCache *cache, char **effective_passwords, UserRecord **ret_home);
 
 int home_validate_update_luks(UserRecord *h, HomeSetup *setup);
 
-int home_resize_luks(UserRecord *h, bool already_activated, char ***pkcs11_decrypted_passwords, HomeSetup *setup, UserRecord **ret_home);
+int home_resize_luks(UserRecord *h, bool already_activated, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_home);
 
-int home_passwd_luks(UserRecord *h, HomeSetup *setup, char **pkcs11_decrypted_passwords, char **effective_passwords);
+int home_passwd_luks(UserRecord *h, HomeSetup *setup, PasswordCache *cache, char **effective_passwords);
 
 int home_lock_luks(UserRecord *h);
-int home_unlock_luks(UserRecord *h, char ***pkcs11_decrypted_passwords);
+int home_unlock_luks(UserRecord *h, PasswordCache *cache);
 
 static inline uint64_t luks_volume_key_size_convert(struct crypt_device *cd) {
         int k;
index 915bc0e57ec8880af77e029bfe9d866fffa12b41..3a03fb7200d1b6cc2bcc266d049181f546469837 100644 (file)
@@ -62,10 +62,10 @@ int pkcs11_callback(
                 goto decrypt;
         }
 
-        if (strv_isempty(data->secret->pkcs11_pin))
-                return log_error_errno(SYNTHETIC_ERRNO(ENOANO), "Security Token requires PIN.");
+        if (strv_isempty(data->secret->token_pin))
+                return log_error_errno(SYNTHETIC_ERRNO(ENOANO), "Security token requires PIN.");
 
-        STRV_FOREACH(i, data->secret->pkcs11_pin) {
+        STRV_FOREACH(i, data->secret->token_pin) {
                 rv = m->C_Login(session, CKU_USER, (CK_UTF8CHAR*) *i, strlen(*i));
                 if (rv == CKR_OK) {
                         log_info("Successfully logged into security token '%s' with PIN.", token_label);
index 316933cf4ebf69b3be051d050a874e6e97e250ed..83bd875d2d6f39529b942465005896298ca516a0 100644 (file)
@@ -11,6 +11,7 @@
 #include "home-util.h"
 #include "homework-cifs.h"
 #include "homework-directory.h"
+#include "homework-fido2.h"
 #include "homework-fscrypt.h"
 #include "homework-luks.h"
 #include "homework-mount.h"
@@ -21,7 +22,6 @@
 #include "missing_magic.h"
 #include "mount-util.h"
 #include "path-util.h"
-#include "pkcs11-util.h"
 #include "rm-rf.h"
 #include "stat-util.h"
 #include "strv.h"
 /* Make sure a bad password always results in a 3s delay, no matter what */
 #define BAD_PASSWORD_DELAY_USEC (3 * USEC_PER_SEC)
 
+void password_cache_free(PasswordCache *cache) {
+        if (!cache)
+                return;
+
+        cache->pkcs11_passwords = strv_free_erase(cache->pkcs11_passwords);
+        cache->fido2_passwords = strv_free_erase(cache->fido2_passwords);
+}
+
 int user_record_authenticate(
                 UserRecord *h,
                 UserRecord *secret,
-                char ***pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 bool strict_verify) {
 
-        bool need_password = false, need_token = false, need_pin = false, need_protected_authentication_path_permitted = false,
-                pin_locked = false, pin_incorrect = false, pin_incorrect_few_tries_left = false, pin_incorrect_one_try_left = false;
+        bool need_password = false, need_token = false, need_pin = false, need_protected_authentication_path_permitted = false, need_user_presence_permitted = false,
+                pin_locked = false, pin_incorrect = false, pin_incorrect_few_tries_left = false, pin_incorrect_one_try_left = false, token_action_timeout = false;
         int r;
 
         assert(h);
@@ -47,14 +55,14 @@ int user_record_authenticate(
 
         /* Tries to authenticate a user record with the supplied secrets. i.e. checks whether at least one
          * supplied plaintext passwords matches a hashed password field of the user record. Or if a
-         * configured PKCS#11 token is around and can unlock the record.
+         * configured PKCS#11 or FIDO2 token is around and can unlock the record.
          *
-         * Note that the pkcs11_decrypted_passwords parameter is both an input and and output parameter: it
-         * is a list of configured, decrypted PKCS#11 passwords. We typically have to call this function
-         * multiple times over the course of an operation (think: on login we authenticate the host user
-         * record, the record embedded in the LUKS record and the one embedded in $HOME). Hence we keep a
-         * list of passwords we already decrypted, so that we don't have to do the (slow an potentially
-         * interactive) PKCS#11 dance for the relevant token again and again. */
+         * Note that the 'cache' parameter is both an input and output parameter: it contains lists of
+         * configured, decrypted PKCS#11/FIDO2 passwords. We typically have to call this function multiple
+         * times over the course of an operation (think: on login we authenticate the host user record, the
+         * record embedded in the LUKS record and the one embedded in $HOME). Hence we keep a list of
+         * passwords we already decrypted, so that we don't have to do the (slow and potentially interactive)
+         * PKCS#11/FIDO2 dance for the relevant token again and again. */
 
         /* First, let's see if the supplied plain-text passwords work? */
         r = user_record_test_secret(h, secret);
@@ -70,19 +78,12 @@ int user_record_authenticate(
                 return 1;
         }
 
-        /* Second, let's see if any of the PKCS#11 security tokens are plugged in and help us */
+        /* Second, test cached PKCS#11 passwords */
         for (size_t n = 0; n < h->n_pkcs11_encrypted_key; n++) {
-#if HAVE_P11KIT
-                _cleanup_(pkcs11_callback_data_release) struct pkcs11_callback_data data = {
-                        .user_record = h,
-                        .secret = secret,
-                        .encrypted_key = h->pkcs11_encrypted_key + n,
-                };
                 char **pp;
 
-                /* See if any of the previously calculated passwords work */
-                STRV_FOREACH(pp, *pkcs11_decrypted_passwords) {
-                        r = test_password_one(data.encrypted_key->hashed_password, *pp);
+                STRV_FOREACH(pp, cache->pkcs11_passwords) {
+                        r = test_password_one(h->pkcs11_encrypted_key[n].hashed_password, *pp);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to check supplied PKCS#11 password: %m");
                         if (r > 0) {
@@ -90,6 +91,32 @@ int user_record_authenticate(
                                 return 1;
                         }
                 }
+        }
+
+        /* Third, test cached FIDO2 passwords */
+        for (size_t n = 0; n < h->n_fido2_hmac_salt; n++) {
+                char **pp;
+
+                /* See if any of the previously calculated passwords work */
+                STRV_FOREACH(pp, cache->fido2_passwords) {
+                        r = test_password_one(h->fido2_hmac_salt[n].hashed_password, *pp);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to check supplied FIDO2 password: %m");
+                        if (r > 0) {
+                                log_info("Previously acquired FIDO2 password unlocks user record.");
+                                return 0;
+                        }
+                }
+        }
+
+        /* Fourth, let's see if any of the PKCS#11 security tokens are plugged in and help us */
+        for (size_t n = 0; n < h->n_pkcs11_encrypted_key; n++) {
+#if HAVE_P11KIT
+                _cleanup_(pkcs11_callback_data_release) struct pkcs11_callback_data data = {
+                        .user_record = h,
+                        .secret = secret,
+                        .encrypted_key = h->pkcs11_encrypted_key + n,
+                };
 
                 r = pkcs11_find_token(data.encrypted_key->uri, pkcs11_callback, &data);
                 switch (r) {
@@ -126,7 +153,56 @@ int user_record_authenticate(
 
                         log_info("Decrypted password from PKCS#11 security token %s unlocks user record.", data.encrypted_key->uri);
 
-                        r = strv_extend(pkcs11_decrypted_passwords, data.decrypted_password);
+                        r = strv_extend(&cache->pkcs11_passwords, data.decrypted_password);
+                        if (r < 0)
+                                return log_oom();
+
+                        return 0;
+                }
+#else
+                need_token = true;
+                break;
+#endif
+        }
+
+        /* Fifth, let's see if any of the FIDO2 security tokens are plugged in and help us */
+        for (size_t n = 0; n < h->n_fido2_hmac_salt; n++) {
+#if HAVE_LIBFIDO2
+                _cleanup_(erase_and_freep) char *decrypted_password = NULL;
+
+                r = fido2_use_token(h, secret, h->fido2_hmac_salt + n, &decrypted_password);
+                switch (r) {
+                case -EAGAIN:
+                        need_token = true;
+                        break;
+                case -ENOANO:
+                        need_pin = true;
+                        break;
+                case -EOWNERDEAD:
+                        pin_locked = true;
+                        break;
+                case -ENOLCK:
+                        pin_incorrect = true;
+                        break;
+                case -EMEDIUMTYPE:
+                        need_user_presence_permitted = true;
+                        break;
+                case -ENOSTR:
+                        token_action_timeout = true;
+                        break;
+                default:
+                        if (r < 0)
+                                return r;
+
+                        r = test_password_one(h->fido2_hmac_salt[n].hashed_password, decrypted_password);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to test FIDO2 password: %m");
+                        if (r == 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Configured FIDO2 security token does not decrypt encrypted key correctly.");
+
+                        log_info("Decrypted password from FIDO2 security token unlocks user record.");
+
+                        r = strv_extend(&cache->fido2_passwords, decrypted_password);
                         if (r < 0)
                                 return log_oom();
 
@@ -147,8 +223,12 @@ int user_record_authenticate(
                 return -ENOLCK;
         if (pin_locked)
                 return -EOWNERDEAD;
+        if (token_action_timeout)
+                return -ENOSTR;
         if (need_protected_authentication_path_permitted)
                 return -ERFKILL;
+        if (need_user_presence_permitted)
+                return -EMEDIUMTYPE;
         if (need_pin)
                 return -ENOANO;
         if (need_token)
@@ -156,10 +236,11 @@ int user_record_authenticate(
         if (need_password)
                 return -ENOKEY;
 
-        /* Hmm, this means neither PCKS#11 nor classic hashed passwords were supplied, we cannot authenticate this reasonably */
+        /* Hmm, this means neither PCKS#11/FIDO2 nor classic hashed passwords were supplied, we cannot
+         * authenticate this reasonably */
         if (strict_verify)
                 return log_debug_errno(SYNTHETIC_ERRNO(EKEYREVOKED),
-                                       "No hashed passwords and no PKCS#11 tokens defined, cannot authenticate user record, refusing.");
+                                       "No hashed passwords and no PKCS#11/FIDO2 tokens defined, cannot authenticate user record, refusing.");
 
         /* If strict verification is off this means we are possibly in the case where we encountered an
          * unfixated record, i.e. a synthetic one that accordingly lacks any authentication data. In this
@@ -230,7 +311,7 @@ int home_setup_undo(HomeSetup *setup) {
 int home_prepare(
                 UserRecord *h,
                 bool already_activated,
-                char ***pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 HomeSetup *setup,
                 UserRecord **ret_header_home) {
 
@@ -249,7 +330,7 @@ int home_prepare(
         switch (user_record_storage(h)) {
 
         case USER_LUKS:
-                return home_prepare_luks(h, already_activated, NULL, pkcs11_decrypted_passwords, setup, ret_header_home);
+                return home_prepare_luks(h, already_activated, NULL, cache, setup, ret_header_home);
 
         case USER_SUBVOLUME:
         case USER_DIRECTORY:
@@ -257,7 +338,7 @@ int home_prepare(
                 break;
 
         case USER_FSCRYPT:
-                r = home_prepare_fscrypt(h, already_activated, pkcs11_decrypted_passwords, setup);
+                r = home_prepare_fscrypt(h, already_activated, cache, setup);
                 break;
 
         case USER_CIFS:
@@ -387,7 +468,7 @@ int home_load_embedded_identity(
                 int root_fd,
                 UserRecord *header_home,
                 UserReconcileMode mode,
-                char ***pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 UserRecord **ret_embedded_home,
                 UserRecord **ret_new_home) {
 
@@ -414,7 +495,7 @@ int home_load_embedded_identity(
                 return log_error_errno(SYNTHETIC_ERRNO(EREMCHG), "Embedded home record not compatible with host record, refusing.");
 
         /* Insist that credentials the user supplies also unlocks any embedded records. */
-        r = user_record_authenticate(embedded_home, h, pkcs11_decrypted_passwords, /* strict_verify= */ true);
+        r = user_record_authenticate(embedded_home, h, cache, /* strict_verify= */ true);
         if (r < 0)
                 return r;
         assert(r > 0); /* Insist that a password was verified */
@@ -576,7 +657,7 @@ int home_refresh(
                 UserRecord *h,
                 HomeSetup *setup,
                 UserRecord *header_home,
-                char ***pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 struct statfs *ret_statfs,
                 UserRecord **ret_new_home) {
 
@@ -590,7 +671,7 @@ int home_refresh(
         /* When activating a home directory, does the identity work: loads the identity from the $HOME
          * directory, reconciles it with our idea, chown()s everything. */
 
-        r = home_load_embedded_identity(h, setup->root_fd, header_home, USER_RECONCILE_ANY, pkcs11_decrypted_passwords, &embedded_home, &new_home);
+        r = home_load_embedded_identity(h, setup->root_fd, header_home, USER_RECONCILE_ANY, cache, &embedded_home, &new_home);
         if (r < 0)
                 return r;
 
@@ -615,7 +696,7 @@ int home_refresh(
 }
 
 static int home_activate(UserRecord *h, UserRecord **ret_home) {
-        _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL;
+        _cleanup_(password_cache_free) PasswordCache cache = {};
         _cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
         int r;
 
@@ -628,7 +709,7 @@ static int home_activate(UserRecord *h, UserRecord **ret_home) {
         if (!IN_SET(user_record_storage(h), USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT, USER_CIFS))
                 return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Activating home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
 
-        r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords, /* strict_verify= */ false);
+        r = user_record_authenticate(h, h, &cache, /* strict_verify= */ false);
         if (r < 0)
                 return r;
 
@@ -647,7 +728,7 @@ static int home_activate(UserRecord *h, UserRecord **ret_home) {
         switch (user_record_storage(h)) {
 
         case USER_LUKS:
-                r = home_activate_luks(h, &pkcs11_decrypted_passwords, &new_home);
+                r = home_activate_luks(h, &cache, &new_home);
                 if (r < 0)
                         return r;
 
@@ -656,14 +737,14 @@ static int home_activate(UserRecord *h, UserRecord **ret_home) {
         case USER_SUBVOLUME:
         case USER_DIRECTORY:
         case USER_FSCRYPT:
-                r = home_activate_directory(h, &pkcs11_decrypted_passwords, &new_home);
+                r = home_activate_directory(h, &cache, &new_home);
                 if (r < 0)
                         return r;
 
                 break;
 
         case USER_CIFS:
-                r = home_activate_cifs(h, &pkcs11_decrypted_passwords, &new_home);
+                r = home_activate_cifs(h, &cache, &new_home);
                 if (r < 0)
                         return r;
 
@@ -783,15 +864,16 @@ int home_populate(UserRecord *h, int dir_fd) {
 
 static int user_record_compile_effective_passwords(
                 UserRecord *h,
-                char ***ret_effective_passwords,
-                char ***ret_pkcs11_decrypted_passwords) {
+                PasswordCache *cache,
+                char ***ret_effective_passwords) {
 
-        _cleanup_(strv_free_erasep) char **effective = NULL, **pkcs11_passwords = NULL;
+        _cleanup_(strv_free_erasep) char **effective = NULL;
         size_t n;
         char **i;
         int r;
 
         assert(h);
+        assert(cache);
 
         /* We insist on at least one classic hashed password to be defined in addition to any PKCS#11 one, as
          * a safe fallback, but also to simplify the password changing algorithm: there we require providing
@@ -858,11 +940,37 @@ static int user_record_compile_effective_passwords(
                                 return log_oom();
                 }
 
-                if (ret_pkcs11_decrypted_passwords) {
-                        r = strv_extend(&pkcs11_passwords, data.decrypted_password);
+                r = strv_extend(&cache->pkcs11_passwords, data.decrypted_password);
+                if (r < 0)
+                        return log_oom();
+#else
+                return -EBADSLT;
+#endif
+        }
+
+        for (n = 0; n < h->n_fido2_hmac_salt; n++) {
+#if HAVE_LIBFIDO2
+                _cleanup_(erase_and_freep) char *decrypted_password = NULL;
+
+                r = fido2_use_token(h, h, h->fido2_hmac_salt + n, &decrypted_password);
+                if (r < 0)
+                        return r;
+
+                r = test_password_one(h->fido2_hmac_salt[n].hashed_password, decrypted_password);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to test FIDO2 password: %m");
+                if (r == 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Decrypted password from token is not correct, refusing.");
+
+                if (ret_effective_passwords) {
+                        r = strv_extend(&effective, decrypted_password);
                         if (r < 0)
                                 return log_oom();
                 }
+
+                r = strv_extend(&cache->fido2_passwords, decrypted_password);
+                if (r < 0)
+                        return log_oom();
 #else
                 return -EBADSLT;
 #endif
@@ -870,8 +978,6 @@ static int user_record_compile_effective_passwords(
 
         if (ret_effective_passwords)
                 *ret_effective_passwords = TAKE_PTR(effective);
-        if (ret_pkcs11_decrypted_passwords)
-                *ret_pkcs11_decrypted_passwords = TAKE_PTR(pkcs11_passwords);
 
         return 0;
 }
@@ -934,8 +1040,9 @@ static int determine_default_storage(UserStorage *ret) {
 }
 
 static int home_create(UserRecord *h, UserRecord **ret_home) {
-        _cleanup_(strv_free_erasep) char **effective_passwords = NULL, **pkcs11_decrypted_passwords = NULL;
+        _cleanup_(strv_free_erasep) char **effective_passwords = NULL;
         _cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
+        _cleanup_(password_cache_free) PasswordCache cache = {};
         UserStorage new_storage = _USER_STORAGE_INVALID;
         const char *new_fs = NULL;
         int r;
@@ -947,7 +1054,7 @@ static int home_create(UserRecord *h, UserRecord **ret_home) {
         if (!uid_is_valid(h->uid))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks UID, refusing.");
 
-        r = user_record_compile_effective_passwords(h, &effective_passwords, &pkcs11_decrypted_passwords);
+        r = user_record_compile_effective_passwords(h, &cache, &effective_passwords);
         if (r < 0)
                 return r;
 
@@ -996,7 +1103,7 @@ static int home_create(UserRecord *h, UserRecord **ret_home) {
         switch (user_record_storage(h)) {
 
         case USER_LUKS:
-                r = home_create_luks(h, pkcs11_decrypted_passwords, effective_passwords, &new_home);
+                r = home_create_luks(h, &cache, effective_passwords, &new_home);
                 break;
 
         case USER_DIRECTORY:
@@ -1182,15 +1289,15 @@ static int home_validate_update(UserRecord *h, HomeSetup *setup) {
 
 static int home_update(UserRecord *h, UserRecord **ret) {
         _cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *header_home = NULL, *embedded_home = NULL;
-        _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL;
         _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
+        _cleanup_(password_cache_free) PasswordCache cache = {};
         bool already_activated = false;
         int r;
 
         assert(h);
         assert(ret);
 
-        r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords, /* strict_verify= */ true);
+        r = user_record_authenticate(h, h, &cache, /* strict_verify= */ true);
         if (r < 0)
                 return r;
         assert(r > 0); /* Insist that a password was verified */
@@ -1201,11 +1308,11 @@ static int home_update(UserRecord *h, UserRecord **ret) {
 
         already_activated = r > 0;
 
-        r = home_prepare(h, already_activated, &pkcs11_decrypted_passwords, &setup, &header_home);
+        r = home_prepare(h, already_activated, &cache, &setup, &header_home);
         if (r < 0)
                 return r;
 
-        r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER, &pkcs11_decrypted_passwords, &embedded_home, &new_home);
+        r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER, &cache, &embedded_home, &new_home);
         if (r < 0)
                 return r;
 
@@ -1237,7 +1344,7 @@ static int home_update(UserRecord *h, UserRecord **ret) {
 
 static int home_resize(UserRecord *h, UserRecord **ret) {
         _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
-        _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL;
+        _cleanup_(password_cache_free) PasswordCache cache = {};
         bool already_activated = false;
         int r;
 
@@ -1247,7 +1354,7 @@ static int home_resize(UserRecord *h, UserRecord **ret) {
         if (h->disk_size == UINT64_MAX)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No target size specified, refusing.");
 
-        r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords, /* strict_verify= */ true);
+        r = user_record_authenticate(h, h, &cache, /* strict_verify= */ true);
         if (r < 0)
                 return r;
         assert(r > 0); /* Insist that a password was verified */
@@ -1261,12 +1368,12 @@ static int home_resize(UserRecord *h, UserRecord **ret) {
         switch (user_record_storage(h)) {
 
         case USER_LUKS:
-                return home_resize_luks(h, already_activated, &pkcs11_decrypted_passwords, &setup, ret);
+                return home_resize_luks(h, already_activated, &cache, &setup, ret);
 
         case USER_DIRECTORY:
         case USER_SUBVOLUME:
         case USER_FSCRYPT:
-                return home_resize_directory(h, already_activated, &pkcs11_decrypted_passwords, &setup, ret);
+                return home_resize_directory(h, already_activated, &cache, &setup, ret);
 
         default:
                 return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Resizing home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
@@ -1275,8 +1382,9 @@ static int home_resize(UserRecord *h, UserRecord **ret) {
 
 static int home_passwd(UserRecord *h, UserRecord **ret_home) {
         _cleanup_(user_record_unrefp) UserRecord *header_home = NULL, *embedded_home = NULL, *new_home = NULL;
-        _cleanup_(strv_free_erasep) char **effective_passwords = NULL, **pkcs11_decrypted_passwords = NULL;
+        _cleanup_(strv_free_erasep) char **effective_passwords = NULL;
         _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
+        _cleanup_(password_cache_free) PasswordCache cache = {};
         bool already_activated = false;
         int r;
 
@@ -1286,7 +1394,7 @@ static int home_passwd(UserRecord *h, UserRecord **ret_home) {
         if (!IN_SET(user_record_storage(h), USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT))
                 return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Changing password of home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
 
-        r = user_record_compile_effective_passwords(h, &effective_passwords, &pkcs11_decrypted_passwords);
+        r = user_record_compile_effective_passwords(h, &cache, &effective_passwords);
         if (r < 0)
                 return r;
 
@@ -1296,24 +1404,24 @@ static int home_passwd(UserRecord *h, UserRecord **ret_home) {
 
         already_activated = r > 0;
 
-        r = home_prepare(h, already_activated, &pkcs11_decrypted_passwords, &setup, &header_home);
+        r = home_prepare(h, already_activated, &cache, &setup, &header_home);
         if (r < 0)
                 return r;
 
-        r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, &pkcs11_decrypted_passwords, &embedded_home, &new_home);
+        r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, &cache, &embedded_home, &new_home);
         if (r < 0)
                 return r;
 
         switch (user_record_storage(h)) {
 
         case USER_LUKS:
-                r = home_passwd_luks(h, &setup, pkcs11_decrypted_passwords, effective_passwords);
+                r = home_passwd_luks(h, &setup, &cache, effective_passwords);
                 if (r < 0)
                         return r;
                 break;
 
         case USER_FSCRYPT:
-                r = home_passwd_fscrypt(h, &setup, pkcs11_decrypted_passwords, effective_passwords);
+                r = home_passwd_fscrypt(h, &setup, &cache, effective_passwords);
                 if (r < 0)
                         return r;
                 break;
@@ -1351,14 +1459,14 @@ static int home_passwd(UserRecord *h, UserRecord **ret_home) {
 static int home_inspect(UserRecord *h, UserRecord **ret_home) {
         _cleanup_(user_record_unrefp) UserRecord *header_home = NULL, *new_home = NULL;
         _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
-        _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL;
+        _cleanup_(password_cache_free) PasswordCache cache = {};
         bool already_activated = false;
         int r;
 
         assert(h);
         assert(ret_home);
 
-        r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords, /* strict_verify= */ false);
+        r = user_record_authenticate(h, h, &cache, /* strict_verify= */ false);
         if (r < 0)
                 return r;
 
@@ -1368,11 +1476,11 @@ static int home_inspect(UserRecord *h, UserRecord **ret_home) {
 
         already_activated = r > 0;
 
-        r = home_prepare(h, already_activated, &pkcs11_decrypted_passwords, &setup, &header_home);
+        r = home_prepare(h, already_activated, &cache, &setup, &header_home);
         if (r < 0)
                 return r;
 
-        r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_ANY, &pkcs11_decrypted_passwords, NULL, &new_home);
+        r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_ANY, &cache, NULL, &new_home);
         if (r < 0)
                 return r;
 
@@ -1415,7 +1523,7 @@ static int home_lock(UserRecord *h) {
 }
 
 static int home_unlock(UserRecord *h) {
-        _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL;
+        _cleanup_(password_cache_free) PasswordCache cache = {};
         int r;
 
         assert(h);
@@ -1428,11 +1536,11 @@ static int home_unlock(UserRecord *h) {
         /* Note that we don't check if $HOME is actually mounted, since we want to avoid disk accesses on
          * that mount until we have resumed the device. */
 
-        r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords, /* strict_verify= */ false);
+        r = user_record_authenticate(h, h, &cache, /* strict_verify= */ false);
         if (r < 0)
                 return r;
 
-        r = home_unlock_luks(h, &pkcs11_decrypted_passwords);
+        r = home_unlock_luks(h, &cache);
         if (r < 0)
                 return r;
 
@@ -1495,10 +1603,12 @@ static int run(int argc, char *argv[]) {
          * ESOCKTNOSUPPORT → operation not support on this file system
          * ENOKEY          → password incorrect (or not sufficient, or not supplied)
          * EBADSLT         → similar, but PKCS#11 device is defined and might be able to provide password, if it was plugged in which it is not
-         * ENOANO          → suitable PKCS#11 device found, but PIN is missing to unlock it
+         * ENOANO          → suitable PKCS#11/FIDO2 device found, but PIN is missing to unlock it
          * ERFKILL         → suitable PKCS#11 device found, but OK to ask for on-device interactive authentication not given
-         * EOWNERDEAD      → suitable PKCS#11 device found, but its PIN is locked
-         * ENOLCK          → suitable PKCS#11 device found, but PIN incorrect
+         * EMEDIUMTYPE     → suitable FIDO2 device found, but OK to ask for user presence not given
+         * ENOSTR          → suitable FIDO2 device found, but user didn't react to action request on token quickly enough
+         * EOWNERDEAD      → suitable PKCS#11/FIDO2 device found, but its PIN is locked
+         * ENOLCK          → suitable PKCS#11/FIDO2 device found, but PIN incorrect
          * ETOOMANYREFS    → suitable PKCS#11 device found, but PIN incorrect, and only few tries left
          * EUCLEAN         → suitable PKCS#11 device found, but PIN incorrect, and only one try left
          * EBUSY           → file system is currently active
index 46641172a421e2e5fab5243b3455f94adc95cb4d..ce8f2a461fe827e81f2669886de97f206171b4da 100644 (file)
@@ -36,6 +36,14 @@ typedef struct HomeSetup {
         uint64_t partition_size;
 } HomeSetup;
 
+typedef struct PasswordCache {
+        /* Decoding passwords from security tokens is expensive and typically requires user interaction, hence cache any we already figured out. */
+        char **pkcs11_passwords;
+        char **fido2_passwords;
+} PasswordCache;
+
+void password_cache_free(PasswordCache *cache);
+
 #define HOME_SETUP_INIT                                 \
         {                                               \
                 .root_fd = -1,                          \
@@ -46,16 +54,16 @@ typedef struct HomeSetup {
 
 int home_setup_undo(HomeSetup *setup);
 
-int home_prepare(UserRecord *h, bool already_activated, char ***pkcs11_decrypted_passwords, HomeSetup *setup, UserRecord **ret_header_home);
+int home_prepare(UserRecord *h, bool already_activated, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_header_home);
 
-int home_refresh(UserRecord *h, HomeSetup *setup, UserRecord *header_home, char ***pkcs11_decrypted_passwords, struct statfs *ret_statfs, UserRecord **ret_new_home);
+int home_refresh(UserRecord *h, HomeSetup *setup, UserRecord *header_home, PasswordCache *cache, struct statfs *ret_statfs, UserRecord **ret_new_home);
 
 int home_populate(UserRecord *h, int dir_fd);
 
-int home_load_embedded_identity(UserRecord *h, int root_fd, UserRecord *header_home, UserReconcileMode mode, char ***pkcs11_decrypted_passwords, UserRecord **ret_embedded_home, UserRecord **ret_new_home);
+int home_load_embedded_identity(UserRecord *h, int root_fd, UserRecord *header_home, UserReconcileMode mode, PasswordCache *cache, UserRecord **ret_embedded_home, UserRecord **ret_new_home);
 int home_store_embedded_identity(UserRecord *h, int root_fd, uid_t uid, UserRecord *old_home);
 int home_extend_embedded_identity(UserRecord *h, UserRecord *used, HomeSetup *setup);
 
-int user_record_authenticate(UserRecord *h, UserRecord *secret, char ***pkcs11_decrypted_passwords, bool strict_verify);
+int user_record_authenticate(UserRecord *h, UserRecord *secret, PasswordCache *cache, bool strict_verify);
 
 int home_sync_and_statfs(int root_fd, struct statfs *ret);
index 2c5664aae1d9702b1b3d808921c06e6afc1503b3..797f3a3c6d0a3a9b772c493e85b65fa25a3acc02 100644 (file)
@@ -14,6 +14,7 @@ systemd_homework_sources = files('''
         homework-mount.c
         homework-mount.h
         homework-pkcs11.h
+        homework-fido2.h
         homework-quota.c
         homework-quota.h
         homework.c
@@ -25,6 +26,9 @@ systemd_homework_sources = files('''
 if conf.get('HAVE_P11KIT') == 1
         systemd_homework_sources += files('homework-pkcs11.c')
 endif
+if conf.get('HAVE_LIBFIDO2') == 1
+        systemd_homework_sources += files('homework-fido2.c')
+endif
 
 systemd_homed_sources = files('''
         home-util.c
@@ -65,6 +69,10 @@ systemd_homed_sources += [homed_gperf_c]
 homectl_sources = files('''
         home-util.c
         home-util.h
+        homectl-fido2.c
+        homectl-fido2.h
+        homectl-pkcs11.c
+        homectl-pkcs11.h
         homectl.c
         pwquality-util.c
         pwquality-util.h
index 34dfd134fc3fc13e672a5d819b1ea3b7511d8d02..2c2c7a0819c34be2f91f483cc4b82795a384f0fb 100644 (file)
@@ -359,7 +359,7 @@ static int handle_generic_user_record_error(
                         return PAM_AUTHTOK_ERR;
                 }
 
-                r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false);
+                r = user_record_set_token_pin(secret, STRV_MAKE(newp), false);
                 if (r < 0) {
                         pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r));
                         return PAM_SERVICE_ERR;
@@ -375,6 +375,21 @@ static int handle_generic_user_record_error(
                         return PAM_SERVICE_ERR;
                 }
 
+        } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED)) {
+
+                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Please verify presence on security token of user %s.", user_name);
+
+                r = user_record_set_fido2_user_presence_permitted(secret, true);
+                if (r < 0) {
+                        pam_syslog(handle, LOG_ERR, "Failed to set FIDO2 user presence permitted flag: %s", strerror_safe(r));
+                        return PAM_SERVICE_ERR;
+                }
+
+        } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_LOCKED)) {
+
+                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)");
+                return PAM_SERVICE_ERR;
+
         } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN)) {
                 _cleanup_(erase_and_freep) char *newp = NULL;
 
@@ -388,7 +403,7 @@ static int handle_generic_user_record_error(
                         return PAM_AUTHTOK_ERR;
                 }
 
-                r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false);
+                r = user_record_set_token_pin(secret, STRV_MAKE(newp), false);
                 if (r < 0) {
                         pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r));
                         return PAM_SERVICE_ERR;
@@ -407,7 +422,7 @@ static int handle_generic_user_record_error(
                         return PAM_AUTHTOK_ERR;
                 }
 
-                r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false);
+                r = user_record_set_token_pin(secret, STRV_MAKE(newp), false);
                 if (r < 0) {
                         pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r));
                         return PAM_SERVICE_ERR;
@@ -426,7 +441,7 @@ static int handle_generic_user_record_error(
                         return PAM_AUTHTOK_ERR;
                 }
 
-                r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false);
+                r = user_record_set_token_pin(secret, STRV_MAKE(newp), false);
                 if (r < 0) {
                         pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r));
                         return PAM_SERVICE_ERR;
@@ -499,7 +514,7 @@ static int acquire_home(
                 return r;
 
         /* Implement our own retry loop here instead of relying on the PAM client's one. That's because it
-         * might happen that the the record we stored on the host does not match the encryption password of
+         * might happen that the record we stored on the host does not match the encryption password of
          * the LUKS image in case the image was used in a different system where the password was
          * changed. In that case it will happen that the LUKS password and the host password are
          * different, and we handle that by collecting and passing multiple passwords in that case. Hence we
index 8f51f8d6e82ea40442abf1cee632814d0566bdba..5d0ac8653387de240f98b727e84efe86eff73f2c 100644 (file)
@@ -172,7 +172,7 @@ int user_record_reconcile(
          *     -REMCHG: identity records are not about the same user
          *     -ESTALE: embedded identity record is equally new or newer than supplied record
          *
-         * Return the new record to use, which is either the the embedded record updated with the host
+         * Return the new record to use, which is either the embedded record updated with the host
          * binding or the host record. In both cases the secret data is stripped. */
 
         assert(host);
@@ -887,7 +887,7 @@ int user_record_set_password(UserRecord *h, char **password, bool prepend) {
         return 0;
 }
 
-int user_record_set_pkcs11_pin(UserRecord *h, char **pin, bool prepend) {
+int user_record_set_token_pin(UserRecord *h, char **pin, bool prepend) {
         _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
         _cleanup_(strv_free_erasep) char **e = NULL;
         int r;
@@ -899,17 +899,17 @@ int user_record_set_pkcs11_pin(UserRecord *h, char **pin, bool prepend) {
                 if (!e)
                         return -ENOMEM;
 
-                r = strv_extend_strv(&e, h->pkcs11_pin, true);
+                r = strv_extend_strv(&e, h->token_pin, true);
                 if (r < 0)
                         return r;
 
                 strv_uniq(e);
 
-                if (strv_equal(h->pkcs11_pin, e))
+                if (strv_equal(h->token_pin, e))
                         return 0;
 
         } else {
-                if (strv_equal(h->pkcs11_pin, pin))
+                if (strv_equal(h->token_pin, pin))
                         return 0;
 
                 e = strv_copy(pin);
@@ -922,7 +922,7 @@ int user_record_set_pkcs11_pin(UserRecord *h, char **pin, bool prepend) {
         w = json_variant_ref(json_variant_by_key(h->json, "secret"));
 
         if (strv_isempty(e))
-                r = json_variant_filter(&w, STRV_MAKE("pkcs11Pin"));
+                r = json_variant_filter(&w, STRV_MAKE("tokenPin"));
         else {
                 _cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
 
@@ -932,7 +932,7 @@ int user_record_set_pkcs11_pin(UserRecord *h, char **pin, bool prepend) {
 
                 json_variant_sensitive(l);
 
-                r = json_variant_set_field(&w, "pkcs11Pin", l);
+                r = json_variant_set_field(&w, "tokenPin", l);
         }
         if (r < 0)
                 return r;
@@ -943,7 +943,7 @@ int user_record_set_pkcs11_pin(UserRecord *h, char **pin, bool prepend) {
         if (r < 0)
                 return r;
 
-        strv_free_and_replace(h->pkcs11_pin, e);
+        strv_free_and_replace(h->token_pin, e);
 
         SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
         return 0;
@@ -980,6 +980,34 @@ int user_record_set_pkcs11_protected_authentication_path_permitted(UserRecord *h
         return 0;
 }
 
+int user_record_set_fido2_user_presence_permitted(UserRecord *h, int b) {
+        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+        int r;
+
+        assert(h);
+
+        w = json_variant_ref(json_variant_by_key(h->json, "secret"));
+
+        if (b < 0)
+                r = json_variant_filter(&w, STRV_MAKE("fido2UserPresencePermitted"));
+        else
+                r = json_variant_set_field_boolean(&w, "fido2UserPresencePermitted", b);
+        if (r < 0)
+                return r;
+
+        if (json_variant_is_blank_object(w))
+                r = json_variant_filter(&h->json, STRV_MAKE("secret"));
+        else
+                r = json_variant_set_field(&h->json, "secret", w);
+        if (r < 0)
+                return r;
+
+        h->fido2_user_presence_permitted = b;
+
+        SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
+        return 0;
+}
+
 static bool per_machine_entry_empty(JsonVariant *v) {
         const char *k;
         _unused_ JsonVariant *e;
@@ -1062,12 +1090,22 @@ int user_record_merge_secret(UserRecord *h, UserRecord *secret) {
         if (r < 0)
                 return r;
 
-        r = user_record_set_pkcs11_pin(h, secret->pkcs11_pin, true);
+        r = user_record_set_token_pin(h, secret->token_pin, true);
         if (r < 0)
                 return r;
 
         if (secret->pkcs11_protected_authentication_path_permitted >= 0) {
-                r = user_record_set_pkcs11_protected_authentication_path_permitted(h, secret->pkcs11_protected_authentication_path_permitted);
+                r = user_record_set_pkcs11_protected_authentication_path_permitted(
+                                h,
+                                secret->pkcs11_protected_authentication_path_permitted);
+                if (r < 0)
+                        return r;
+        }
+
+        if (secret->fido2_user_presence_permitted >= 0) {
+                r = user_record_set_fido2_user_presence_permitted(
+                                h,
+                                secret->fido2_user_presence_permitted);
                 if (r < 0)
                         return r;
         }
index 6afc8df19a7097c6ee036d4ead14f707788bc5f0..24582988257723b2220d59fe2a32a83bcad678a6 100644 (file)
@@ -47,8 +47,9 @@ int user_record_set_disk_size(UserRecord *h, uint64_t disk_size);
 int user_record_set_password(UserRecord *h, char **password, bool prepend);
 int user_record_make_hashed_password(UserRecord *h, char **password, bool extend);
 int user_record_set_hashed_password(UserRecord *h, char **hashed_password);
-int user_record_set_pkcs11_pin(UserRecord *h, char **pin, bool prepend);
+int user_record_set_token_pin(UserRecord *h, char **pin, bool prepend);
 int user_record_set_pkcs11_protected_authentication_path_permitted(UserRecord *h, int b);
+int user_record_set_fido2_user_presence_permitted(UserRecord *h, int b);
 int user_record_set_password_change_now(UserRecord *h, int b);
 int user_record_merge_secret(UserRecord *h, UserRecord *secret);
 int user_record_good_authentication(UserRecord *h);
index 9a08de1ba414299af43c3684365d8e7bf854a542..cdcded2e24b4805a247f24b9ae7cc117381804d5 100644 (file)
@@ -625,7 +625,7 @@ int journal_file_fstat(JournalFile *f) {
 
         f->last_stat_usec = now(CLOCK_MONOTONIC);
 
-        /* Refuse dealing with with files that aren't regular */
+        /* Refuse dealing with files that aren't regular */
         r = stat_verify_regular(&f->last_stat);
         if (r < 0)
                 return r;
index b0d1216eed847fbd80424f2fc66dc99c96376d43..baf7bb2ef4ad79714fd01f624fdf486b1dc40f1b 100644 (file)
@@ -112,7 +112,7 @@ int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
 int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen,
                                   char ***str_arr);
 
-int dhcp6_network_bind_udp_socket(int index, struct in6_addr *address);
+int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address);
 int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
                                   const void *packet, size_t len);
 
index f82afe6a0915258fb7fec47dc720694ee541c43d..e2efa8bbe347b12da0400688f1ec64ed284e3244 100644 (file)
 #include "fd-util.h"
 #include "socket-util.h"
 
-int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
+int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
         union sockaddr_union src = {
                 .in6.sin6_family = AF_INET6,
                 .in6.sin6_port = htobe16(DHCP6_PORT_CLIENT),
-                .in6.sin6_scope_id = index,
+                .in6.sin6_scope_id = ifindex,
         };
         _cleanup_close_ int s = -1;
         int r;
 
-        assert(index > 0);
+        assert(ifindex > 0);
         assert(local_address);
 
         src.in6.sin6_addr = *local_address;
index bdd94867ac33b88c79e0622eaefa151605e26021..d9690293f13e16786ea0331d9e0309d15799a154 100644 (file)
@@ -81,11 +81,11 @@ static int icmp6_bind_router_message(const struct icmp6_filter *filter,
         return TAKE_FD(s);
 }
 
-int icmp6_bind_router_solicitation(int index) {
+int icmp6_bind_router_solicitation(int ifindex) {
         struct icmp6_filter filter = {};
         struct ipv6_mreq mreq = {
                 .ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
-                .ipv6mr_interface = index,
+                .ipv6mr_interface = ifindex,
         };
 
         ICMP6_FILTER_SETBLOCKALL(&filter);
@@ -94,11 +94,11 @@ int icmp6_bind_router_solicitation(int index) {
         return icmp6_bind_router_message(&filter, &mreq);
 }
 
-int icmp6_bind_router_advertisement(int index) {
+int icmp6_bind_router_advertisement(int ifindex) {
         struct icmp6_filter filter = {};
         struct ipv6_mreq mreq = {
                 .ipv6mr_multiaddr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
-                .ipv6mr_interface = index,
+                .ipv6mr_interface = ifindex,
         };
 
         ICMP6_FILTER_SETBLOCKALL(&filter);
index 725a68086bc462bf8878f7447eacf3b08bb874c8..ac68ded1fe9653d6b8485a4b990c612a3d86bb95 100644 (file)
@@ -17,8 +17,8 @@
         { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }
 
-int icmp6_bind_router_solicitation(int index);
-int icmp6_bind_router_advertisement(int index);
+int icmp6_bind_router_solicitation(int ifindex);
+int icmp6_bind_router_advertisement(int ifindex);
 int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr);
 int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *dst,
                   triple_timestamp *timestamp);
index e1572703e2becc713bcb9a715c775508cd334116..7a8823cce93e45fa0161fa8a5e2eac3e82960841 100644 (file)
@@ -159,7 +159,7 @@ int sd_dhcp6_client_set_callback(
 int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
 
         assert_return(client, -EINVAL);
-        assert_return(ifindex >= -1, -EINVAL);
+        assert_return(ifindex > 0, -EINVAL);
         assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
 
         client->ifindex = ifindex;
index 7ea6f0d5612d7c3070f5e804e485a296e5506d2b..cc5c0223b5cee459785ec68f94e1176e3747a3b0 100644 (file)
@@ -415,7 +415,7 @@ _public_ int sd_radv_start(sd_radv *ra) {
 
 _public_ int sd_radv_set_ifindex(sd_radv *ra, int ifindex) {
         assert_return(ra, -EINVAL);
-        assert_return(ifindex >= -1, -EINVAL);
+        assert_return(ifindex > 0, -EINVAL);
 
         if (ra->state != SD_RADV_STATE_IDLE)
                 return -EBUSY;
index 80a85661942ea9ea1c55c94de29abef6dd43505c..8f2f4462be3513f9a0ad34c7f3c9cf2010c293b6 100644 (file)
@@ -258,7 +258,7 @@ int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, const
 }
 
 int dhcp_network_bind_raw_socket(
-                int index,
+                int ifindex,
                 union sockaddr_union *link,
                 uint32_t id,
                 const uint8_t *addr, size_t addr_len,
index 56c8c978e5817f2ca207de2906ea138a66237b82..7af7d670b5917ca2879d52c17855839f733391ae 100644 (file)
@@ -30,7 +30,7 @@ static struct ether_addr mac_addr = {
 
 static sd_event_source *hangcheck;
 static int test_dhcp_fd[2];
-static int test_index = 42;
+static int test_ifindex = 42;
 static int test_client_message_num;
 static be32_t test_iaid = 0;
 static uint8_t test_duid[14] = { };
@@ -48,7 +48,7 @@ static int test_client_basic(sd_event *e) {
 
         assert_se(sd_dhcp6_client_set_ifindex(client, 15) == 0);
         assert_se(sd_dhcp6_client_set_ifindex(client, -42) == -EINVAL);
-        assert_se(sd_dhcp6_client_set_ifindex(client, -1) == 0);
+        assert_se(sd_dhcp6_client_set_ifindex(client, -1) == -EINVAL);
         assert_se(sd_dhcp6_client_set_ifindex(client, 42) >= 0);
 
         assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr,
@@ -877,8 +877,8 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
         return len;
 }
 
-int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
-        assert_se(index == test_index);
+int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
+        assert_se(ifindex == test_ifindex);
 
         if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_dhcp_fd) < 0)
                 return -errno;
@@ -899,7 +899,7 @@ static int test_client_solicit(sd_event *e) {
 
         assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
 
-        assert_se(sd_dhcp6_client_set_ifindex(client, test_index) == 0);
+        assert_se(sd_dhcp6_client_set_ifindex(client, test_ifindex) == 0);
         assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr,
                                           sizeof (mac_addr),
                                           ARPHRD_ETHER) >= 0);
index 6051e65820455a759ca9e41fb5f1484028238dc9..310b658e188a0eb203041ac7f8c6d97e017feed5 100644 (file)
@@ -78,7 +78,7 @@ int arp_send_announcement(int fd, int ifindex,
         return arp_network_send_raw_socket(fd, ifindex, &ea);
 }
 
-int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac) {
+int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) {
         if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
                 return -errno;
 
index 7c6c4663f3b7836d519b0ab24dc859b67b428836..d759ec03a8ad9efbda2a28cfb66855bfc4a7372a 100644 (file)
@@ -159,8 +159,8 @@ static void test_radv(void) {
         assert_se(ra);
 
         assert_se(sd_radv_set_ifindex(NULL, 0) < 0);
-        assert_se(sd_radv_set_ifindex(ra, 0) >= 0);
-        assert_se(sd_radv_set_ifindex(ra, -1) >= 0);
+        assert_se(sd_radv_set_ifindex(ra, 0) < 0);
+        assert_se(sd_radv_set_ifindex(ra, -1) < 0);
         assert_se(sd_radv_set_ifindex(ra, -2) < 0);
         assert_se(sd_radv_set_ifindex(ra, 42) >= 0);
 
@@ -219,12 +219,12 @@ static void test_radv(void) {
         assert_se(!ra);
 }
 
-int icmp6_bind_router_solicitation(int index) {
+int icmp6_bind_router_solicitation(int ifindex) {
         return -ENOSYS;
 }
 
-int icmp6_bind_router_advertisement(int index) {
-        assert_se(index == 42);
+int icmp6_bind_router_advertisement(int ifindex) {
+        assert_se(ifindex == 42);
 
         return test_fd[1];
 }
index 9fc902645c8a9aedb77208de61e874f7beb36940..5d1e66fcdc96900772d66866f71edc8461c9d64e 100644 (file)
@@ -174,8 +174,8 @@ static int test_rs_hangcheck(sd_event_source *s, uint64_t usec,
         return 0;
 }
 
-int icmp6_bind_router_solicitation(int index) {
-        assert_se(index == 42);
+int icmp6_bind_router_solicitation(int ifindex) {
+        assert_se(ifindex == 42);
 
         if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
                 return -errno;
@@ -183,8 +183,7 @@ int icmp6_bind_router_solicitation(int index) {
         return test_fd[0];
 }
 
-int icmp6_bind_router_advertisement(int index) {
-
+int icmp6_bind_router_advertisement(int ifindex) {
         return -ENOSYS;
 }
 
index 28f98cebce97472e9455d1844a317414a68bdfb8..dc9a2fdc3a56f3f6c37682a31f47f817d24bc98c 100644 (file)
@@ -120,6 +120,8 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
         SD_BUS_ERROR_MAP(BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN,    EBADSLT),
         SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_PIN_NEEDED,             ENOANO),
         SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED, ERFKILL),
+        SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED,   EMEDIUMTYPE),
+        SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_ACTION_TIMEOUT,         ENOSTR),
         SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_PIN_LOCKED,             EOWNERDEAD),
         SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_BAD_PIN,                ENOLCK),
         SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT, ETOOMANYREFS),
index 68ecbd65ddddb5425a1e05b7ccda27d741ac6363..ae805438804e5257430bac99fe523716df17d5d1 100644 (file)
 #define BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN "org.freedesktop.home1.BadPasswordAndNoToken"
 #define BUS_ERROR_TOKEN_PIN_NEEDED "org.freedesktop.home1.TokenPinNeeded"
 #define BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED "org.freedesktop.home1.TokenProtectedAuthenticationPathNeeded"
+#define BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED "org.freedesktop.home1.TokenUserPresenceNeeded"
+#define BUS_ERROR_TOKEN_ACTION_TIMEOUT "org.freedesktop.home1.TokenActionTimeout"
 #define BUS_ERROR_TOKEN_PIN_LOCKED "org.freedesktop.home1.TokenPinLocked"
 #define BUS_ERROR_TOKEN_BAD_PIN "org.freedesktop.home1.BadPin"
 #define BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT "org.freedesktop.home1.BadPinFewTriesLeft"
index 7e13f4f73ba965066089852a04fcad405d0a0bff..ef2c3dbc4fde6f034fc841bb0f308fb4843df5ee 100644 (file)
@@ -43,7 +43,7 @@ struct match_callback {
 
         unsigned last_iteration;
 
-        /* Don't dispatch this slot with with messages that arrived in any iteration before or at the this
+        /* Don't dispatch this slot with messages that arrived in any iteration before or at the this
          * one. We use this to ensure that matches don't apply "retroactively" and thus can confuse the
          * caller: matches will only match incoming messages from the moment on the match was installed. */
         uint64_t after;
index 24f34dc18236783069f4c976297acefee6ab68de..13596f2303591c32ad6d4b04f08aa2d9d075955d 100644 (file)
@@ -1564,34 +1564,68 @@ _public_ const char *sd_device_get_property_next(sd_device *device, const char *
         return key;
 }
 
-static int device_sysattrs_read_all(sd_device *device) {
+static int device_sysattrs_read_all_internal(sd_device *device, const char *subdir) {
+        _cleanup_free_ char *path_dir = NULL;
         _cleanup_closedir_ DIR *dir = NULL;
-        const char *syspath;
         struct dirent *dent;
+        const char *syspath;
         int r;
 
-        assert(device);
-
-        if (device->sysattrs_read)
-                return 0;
-
         r = sd_device_get_syspath(device, &syspath);
         if (r < 0)
                 return r;
 
-        dir = opendir(syspath);
+        if (subdir) {
+                _cleanup_free_ char *p = NULL;
+
+                p = path_join(syspath, subdir, "uevent");
+                if (!p)
+                        return -ENOMEM;
+
+                if (access(p, F_OK) >= 0)
+                        /* this is a child device, skipping */
+                        return 0;
+                if (errno != ENOENT) {
+                        log_debug_errno(errno, "Failed to stat %s, ignoring subdir: %m", p);
+                        return 0;
+                }
+
+                path_dir = path_join(syspath, subdir);
+                if (!path_dir)
+                        return -ENOMEM;
+        }
+
+        dir = opendir(path_dir ?: syspath);
         if (!dir)
                 return -errno;
 
         FOREACH_DIRENT_ALL(dent, dir, return -errno) {
-                _cleanup_free_ char *path = NULL;
+                _cleanup_free_ char *path = NULL, *p = NULL;
                 struct stat statbuf;
 
-                /* only handle symlinks and regular files */
-                if (!IN_SET(dent->d_type, DT_LNK, DT_REG))
+                if (dot_or_dot_dot(dent->d_name))
+                        continue;
+
+                /* only handle symlinks, regular files, and directories */
+                if (!IN_SET(dent->d_type, DT_LNK, DT_REG, DT_DIR))
+                        continue;
+
+                if (subdir) {
+                        p = path_join(subdir, dent->d_name);
+                        if (!p)
+                                return -ENOMEM;
+                }
+
+                if (dent->d_type == DT_DIR) {
+                        /* read subdirectory */
+                        r = device_sysattrs_read_all_internal(device, p ?: dent->d_name);
+                        if (r < 0)
+                                return r;
+
                         continue;
+                }
 
-                path = path_join(syspath, dent->d_name);
+                path = path_join(syspath, p ?: dent->d_name);
                 if (!path)
                         return -ENOMEM;
 
@@ -1601,11 +1635,26 @@ static int device_sysattrs_read_all(sd_device *device) {
                 if (!(statbuf.st_mode & S_IRUSR))
                         continue;
 
-                r = set_put_strdup(&device->sysattrs, dent->d_name);
+                r = set_put_strdup(&device->sysattrs, p ?: dent->d_name);
                 if (r < 0)
                         return r;
         }
 
+        return 0;
+}
+
+static int device_sysattrs_read_all(sd_device *device) {
+        int r;
+
+        assert(device);
+
+        if (device->sysattrs_read)
+                return 0;
+
+        r = device_sysattrs_read_all_internal(device, NULL);
+        if (r < 0)
+                return r;
+
         device->sysattrs_read = true;
 
         return 0;
index 488bfb5f4fed49eaefdcf6032bff90c2c93fc531..a1e4ec9f27f7f92c079265e9b30ee9209e038050 100644 (file)
@@ -537,6 +537,43 @@ static const NLTypeSystem rtnl_prop_list_type_system = {
         .types = rtnl_prop_list_types,
 };
 
+static const NLType rtnl_vf_vlan_list_types[] = {
+        [IFLA_VF_VLAN_INFO]  = { .size = sizeof(struct ifla_vf_vlan_info) },
+};
+
+static const NLTypeSystem rtnl_vf_vlan_type_system = {
+        .count = ELEMENTSOF(rtnl_vf_vlan_list_types),
+        .types = rtnl_vf_vlan_list_types,
+};
+
+static const NLType rtnl_vf_vlan_info_types[] = {
+        [IFLA_VF_MAC]           = { .size = sizeof(struct ifla_vf_mac) },
+        [IFLA_VF_VLAN]          = { .size = sizeof(struct ifla_vf_vlan) },
+        [IFLA_VF_VLAN_LIST]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_vlan_type_system},
+        [IFLA_VF_TX_RATE]       = { .size = sizeof(struct ifla_vf_tx_rate) },
+        [IFLA_VF_SPOOFCHK]      = { .size = sizeof(struct ifla_vf_spoofchk) },
+        [IFLA_VF_RATE]          = { .size = sizeof(struct ifla_vf_rate) },
+        [IFLA_VF_LINK_STATE]    = { .size = sizeof(struct ifla_vf_link_state) },
+        [IFLA_VF_RSS_QUERY_EN]  = { .size = sizeof(struct ifla_vf_rss_query_en) },
+        [IFLA_VF_TRUST]         = { .size = sizeof(struct ifla_vf_trust) },
+        [IFLA_VF_IB_NODE_GUID]  = { .size = sizeof(struct ifla_vf_guid) },
+        [IFLA_VF_IB_PORT_GUID]  = { .size = sizeof(struct ifla_vf_guid) },
+};
+
+static const NLTypeSystem rtnl_vf_vlan_info_type_system = {
+        .count = ELEMENTSOF(rtnl_vf_vlan_info_types),
+        .types = rtnl_vf_vlan_info_types,
+};
+
+static const NLType rtnl_link_io_srv_types[] = {
+        [IFLA_VF_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_vlan_info_type_system },
+};
+
+static const NLTypeSystem rtnl_io_srv_type_system = {
+        .count = ELEMENTSOF(rtnl_link_io_srv_types),
+        .types = rtnl_link_io_srv_types,
+};
+
 static const NLType rtnl_link_types[] = {
         [IFLA_ADDRESS]          = { .type = NETLINK_TYPE_ETHER_ADDR },
         [IFLA_BROADCAST]        = { .type = NETLINK_TYPE_ETHER_ADDR },
@@ -564,10 +601,8 @@ static const NLType rtnl_link_types[] = {
         [IFLA_LINKINFO]         = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_info_type_system },
         [IFLA_NET_NS_PID]       = { .type = NETLINK_TYPE_U32 },
         [IFLA_IFALIAS]          = { .type = NETLINK_TYPE_STRING, .size = IFALIASZ - 1 },
-/*
-        [IFLA_NUM_VF],
-        [IFLA_VFINFO_LIST]      = {. type = NETLINK_TYPE_NESTED, },
-*/
+        [IFLA_NUM_VF]           = { .type = NETLINK_TYPE_U32 },
+        [IFLA_VFINFO_LIST]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_io_srv_type_system },
         [IFLA_STATS64]          = { .size = sizeof(struct rtnl_link_stats64) },
 /*
         [IFLA_VF_PORTS]         = { .type = NETLINK_TYPE_NESTED },
index 525b2a0393f1a5ff386d766cae7939d2a291845e..16f42895854df7fa5ec58c3bf5eb4728e1e680e0 100644 (file)
@@ -468,7 +468,7 @@ static bool validate_runtime_directory(pam_handle_t *handle, const char *path, u
         /* Some extra paranoia: let's not set $XDG_RUNTIME_DIR if the directory we'd set it to isn't actually
          * set up properly for us. This is supposed to provide a careful safety net for supporting su/sudo
          * type transitions: in that case the UID changes, but the session and thus the user owning it
-         * doesn't change. Since the $XDG_RUNTIME_DIR life-cycle is bound to the session's user being logged
+         * doesn't change. Since the $XDG_RUNTIME_DIR lifecycle is bound to the session's user being logged
          * in at least once we should be particularly careful when setting the environment variable, since
          * otherwise we might end up setting $XDG_RUNTIME_DIR to some directory owned by the wrong user. */
 
index 97a164091a7f1bd126bfa92fe62490a3c4a3ec2b..b3a88d991035677fbfc9d364b566f10e2edf908d 100644 (file)
@@ -105,6 +105,8 @@ sources = files('''
         networkd-routing-policy-rule.h
         networkd-speed-meter.c
         networkd-speed-meter.h
+        networkd-sriov.c
+        networkd-sriov.h
         networkd-util.c
         networkd-util.h
         networkd-wifi.c
index 27b74407af60cc0184b2c730b14782a675fe6468..ecf8a32691d4d5a599be95b4eba0afbe537d1562 100644 (file)
@@ -19,6 +19,7 @@
 #include "networkd-link.h"
 #include "networkd-manager.h"
 #include "siphash24.h"
+#include "string-table.h"
 #include "string-util.h"
 #include "radv-internal.h"
 #include "web-util.h"
@@ -1205,3 +1206,14 @@ int config_parse_dhcp6_delegated_prefix_token(
 
         return 0;
 }
+
+DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp6_client_start_mode, dhcp6_client_start_mode, DHCP6ClientStartMode,
+                         "Failed to parse WithoutRA= setting");
+
+static const char* const dhcp6_client_start_mode_table[_DHCP6_CLIENT_START_MODE_MAX] = {
+        [DHCP6_CLIENT_START_MODE_NO]                  = "no",
+        [DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST] = "information-request",
+        [DHCP6_CLIENT_START_MODE_SOLICIT]             = "solicit",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(dhcp6_client_start_mode, DHCP6ClientStartMode);
index 913410fe77b16f7f78724b43518248c87c4a83a6..858559a1e0977e90000c8d3719935350714a323e 100644 (file)
@@ -4,6 +4,15 @@
 #include "sd-dhcp6-client.h"
 
 #include "conf-parser.h"
+#include "macro.h"
+
+typedef enum DHCP6ClientStartMode {
+        DHCP6_CLIENT_START_MODE_NO,
+        DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST,
+        DHCP6_CLIENT_START_MODE_SOLICIT,
+        _DHCP6_CLIENT_START_MODE_MAX,
+        _DHCP6_CLIENT_START_MODE_INVALID = -1,
+} DHCP6ClientStartMode;
 
 typedef struct Link Link;
 typedef struct Manager Manager;
@@ -17,3 +26,7 @@ int dhcp6_prefix_remove(Manager *m, struct in6_addr *addr);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_hint);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_mud_url);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_delegated_prefix_token);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_client_start_mode);
+
+const char* dhcp6_client_start_mode_to_string(DHCP6ClientStartMode i) _const_;
+DHCP6ClientStartMode dhcp6_client_start_mode_from_string(const char *s) _pure_;
index b3c0224034b43e5fb4ce291219bc34fd48106dbe..4df56087198368c7c8142acb28dc1f6ad3b6196d 100644 (file)
@@ -3,6 +3,7 @@
 #include <netinet/in.h>
 #include <linux/if.h>
 #include <linux/if_arp.h>
+#include <linux/if_link.h>
 #include <unistd.h>
 
 #include "alloc-util.h"
@@ -31,6 +32,7 @@
 #include "networkd-manager.h"
 #include "networkd-ndisc.h"
 #include "networkd-neighbor.h"
+#include "networkd-sriov.h"
 #include "networkd-radv.h"
 #include "networkd-routing-policy-rule.h"
 #include "networkd-wifi.h"
@@ -1127,6 +1129,9 @@ void link_check_ready(Link *link) {
         if (!link->tc_configured)
                 return;
 
+        if (!link->sr_iov_configured)
+                return;
+
         if (link_has_carrier(link) || !link->network->configure_without_carrier) {
 
                 if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !link->ipv4ll_address)
@@ -1559,11 +1564,13 @@ static int link_acquire_ipv6_conf(Link *link) {
                         return log_link_warning_errno(link, r, "Could not start IPv6 Router Advertisement: %m");
         }
 
-        if (link_dhcp6_enabled(link) && link->network->dhcp6_without_ra) {
+        if (link_dhcp6_enabled(link) && IN_SET(link->network->dhcp6_without_ra,
+                                               DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST,
+                                               DHCP6_CLIENT_START_MODE_SOLICIT)) {
                 assert(link->dhcp6_client);
                 assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
 
-                r = dhcp6_request_address(link, true);
+                r = dhcp6_request_address(link, link->network->dhcp6_without_ra == DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST);
                 if (r < 0 && r != -EBUSY)
                         return log_link_warning_errno(link, r,  "Could not acquire DHCPv6 lease: %m");
                 else
@@ -2836,6 +2843,28 @@ static int link_configure_traffic_control(Link *link) {
         return 0;
 }
 
+static int link_configure_sr_iov(Link *link) {
+        SRIOV *sr_iov;
+        Iterator i;
+        int r;
+
+        link->sr_iov_configured = false;
+        link->sr_iov_messages = 0;
+
+        ORDERED_HASHMAP_FOREACH(sr_iov, link->network->sr_iov_by_section, i) {
+                r = sr_iov_configure(link, sr_iov);
+                if (r < 0)
+                        return r;
+        }
+
+        if (link->sr_iov_messages == 0)
+                link->sr_iov_configured = true;
+        else
+                log_link_debug(link, "Configuring SR-IOV");
+
+        return 0;
+}
+
 static int link_configure(Link *link) {
         int r;
 
@@ -2847,6 +2876,10 @@ static int link_configure(Link *link) {
         if (r < 0)
                 return r;
 
+        r = link_configure_sr_iov(link);
+        if (r < 0)
+                return r;
+
         if (link->iftype == ARPHRD_CAN)
                 return link_configure_can(link);
 
index 5a81805a04d11235738e212a05465e088c779aa2..f53b9da2e3603089c62039df34345ed5b6076f16 100644 (file)
@@ -82,6 +82,7 @@ typedef struct Link {
         unsigned routing_policy_rule_messages;
         unsigned routing_policy_rule_remove_messages;
         unsigned tc_messages;
+        unsigned sr_iov_messages;
         unsigned enslaving;
 
         Set *addresses;
@@ -118,6 +119,7 @@ typedef struct Link {
         bool static_nexthops_configured:1;
         bool routing_policy_rules_configured:1;
         bool tc_configured:1;
+        bool sr_iov_configured:1;
         bool setting_mtu:1;
         bool setting_genmode:1;
         bool ipv6_mtu_set:1;
index 5a0c86b5b37c917cfe20fa7981b7db98a4a8d756..087358da11b5135b52a55c7c616d306e98df9eb3 100644 (file)
@@ -15,6 +15,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
 #include "networkd-ipv4ll.h"
 #include "networkd-ndisc.h"
 #include "networkd-network.h"
+#include "networkd-sriov.h"
 #include "qdisc.h"
 #include "tclass.h"
 #include "vlan-util.h"
@@ -53,6 +54,15 @@ Link.Multicast,                              config_parse_tristate,
 Link.AllMulticast,                           config_parse_tristate,                                    0,                             offsetof(Network, allmulticast)
 Link.Unmanaged,                              config_parse_bool,                                        0,                             offsetof(Network, unmanaged)
 Link.RequiredForOnline,                      config_parse_required_for_online,                         0,                             0
+SR-IOV.VirtualFunction,                      config_parse_sr_iov_uint32,                               0,                             0
+SR-IOV.VLANId,                               config_parse_sr_iov_uint32,                               0,                             0
+SR-IOV.QualityOfService,                     config_parse_sr_iov_uint32,                               0,                             0
+SR-IOV.VLANProtocol,                         config_parse_sr_iov_vlan_proto,                           0,                             0
+SR-IOV.MACSpoofCheck,                        config_parse_sr_iov_boolean,                              0,                             0
+SR-IOV.QueryReceiveSideScaling,              config_parse_sr_iov_boolean,                              0,                             0
+SR-IOV.Trust,                                config_parse_sr_iov_boolean,                              0,                             0
+SR-IOV.LinkState,                            config_parse_sr_iov_link_state,                           0,                             0
+SR-IOV.MACAddress,                           config_parse_sr_iov_mac,                                  0,                             0
 Network.Description,                         config_parse_string,                                      0,                             offsetof(Network, description)
 Network.Bridge,                              config_parse_ifname,                                      0,                             offsetof(Network, bridge_name)
 Network.Bond,                                config_parse_ifname,                                      0,                             offsetof(Network, bond_name)
@@ -206,7 +216,7 @@ DHCPv6.ForceDHCPv6PDOtherInformation,        config_parse_bool,
 DHCPv6.AssignAcquiredDelegatedPrefixAddress, config_parse_bool,                                        0,                             offsetof(Network, dhcp6_pd_assign_prefix)
 DHCPv6.AssignAcquiredDelegatedPrefixToken,   config_parse_dhcp6_delegated_prefix_token,                0,                             0
 DHCPv6.PrefixDelegationHint,                 config_parse_dhcp6_pd_hint,                               0,                             0
-DHCPv6.WithoutRA,                            config_parse_bool,                                        0,                             offsetof(Network, dhcp6_without_ra)
+DHCPv6.WithoutRA,                            config_parse_dhcp6_client_start_mode,                     0,                             offsetof(Network, dhcp6_without_ra)
 DHCPv6.SendOption,                           config_parse_dhcp_send_option,                            AF_INET6,                      offsetof(Network, dhcp6_client_send_options)
 DHCPv6.RouteMetric,                          config_parse_dhcp_route_metric,                           0,                             0
 IPv6AcceptRA.UseAutonomousPrefix,            config_parse_bool,                                        0,                             offsetof(Network, ipv6_accept_ra_use_autonomous_prefix)
@@ -294,11 +304,11 @@ QDisc.Parent,                                config_parse_qdisc_parent,
 QDisc.Handle,                                config_parse_qdisc_handle,                                _QDISC_KIND_INVALID,           0
 BFIFO.Parent,                                config_parse_qdisc_parent,                                QDISC_KIND_BFIFO,              0
 BFIFO.Handle,                                config_parse_qdisc_handle,                                QDISC_KIND_BFIFO,              0
-BFIFO.LimitSize,                             config_parse_bfifo_size,                                  QDISC_KIND_BFIFO,              0
+BFIFO.LimitBytes,                            config_parse_bfifo_size,                                  QDISC_KIND_BFIFO,              0
 CAKE.Parent,                                 config_parse_qdisc_parent,                                QDISC_KIND_CAKE,               0
 CAKE.Handle,                                 config_parse_qdisc_handle,                                QDISC_KIND_CAKE,               0
 CAKE.Bandwidth,                              config_parse_cake_bandwidth,                              QDISC_KIND_CAKE,               0
-CAKE.Overhead,                               config_parse_cake_overhead,                               QDISC_KIND_CAKE,               0
+CAKE.OverheadBytes,                          config_parse_cake_overhead,                               QDISC_KIND_CAKE,               0
 ControlledDelay.Parent,                      config_parse_qdisc_parent,                                QDISC_KIND_CODEL,              0
 ControlledDelay.Handle,                      config_parse_qdisc_handle,                                QDISC_KIND_CODEL,              0
 ControlledDelay.PacketLimit,                 config_parse_controlled_delay_u32,                        QDISC_KIND_CODEL,              0
@@ -310,7 +320,7 @@ DeficitRoundRobinScheduler.Parent,           config_parse_qdisc_parent,
 DeficitRoundRobinScheduler.Handle,           config_parse_qdisc_handle,                                QDISC_KIND_DRR,                0
 DeficitRoundRobinSchedulerClass.Parent,      config_parse_tclass_parent,                               TCLASS_KIND_DRR,               0
 DeficitRoundRobinSchedulerClass.ClassId,     config_parse_tclass_classid,                              TCLASS_KIND_DRR,               0
-DeficitRoundRobinSchedulerClass.Quantum,     config_parse_drr_size,                                    TCLASS_KIND_DRR,               0
+DeficitRoundRobinSchedulerClass.QuantumBytes, config_parse_drr_size,                                   TCLASS_KIND_DRR,               0
 EnhancedTransmissionSelection.Parent,        config_parse_qdisc_parent,                                QDISC_KIND_ETS,                0
 EnhancedTransmissionSelection.Handle,        config_parse_qdisc_handle,                                QDISC_KIND_ETS,                0
 EnhancedTransmissionSelection.Bands,         config_parse_ets_u8,                                      QDISC_KIND_ETS,                0
@@ -330,13 +340,13 @@ QuickFairQueueing.Handle,                    config_parse_qdisc_handle,
 QuickFairQueueingClass.Parent,               config_parse_tclass_parent,                               TCLASS_KIND_QFQ,               0
 QuickFairQueueingClass.ClassId,              config_parse_tclass_classid,                              TCLASS_KIND_QFQ,               0
 QuickFairQueueingClass.Weight,               config_parse_quick_fair_queueing_weight,                  TCLASS_KIND_QFQ,               0
-QuickFairQueueingClass.MaxPacketSize,        config_parse_quick_fair_queueing_max_packet,              TCLASS_KIND_QFQ,               0
+QuickFairQueueingClass.MaxPacketBytes,       config_parse_quick_fair_queueing_max_packet,              TCLASS_KIND_QFQ,               0
 FairQueueing.Parent,                         config_parse_qdisc_parent,                                QDISC_KIND_FQ,                 0
 FairQueueing.Handle,                         config_parse_qdisc_handle,                                QDISC_KIND_FQ,                 0
 FairQueueing.PacketLimit,                    config_parse_fair_queueing_u32,                           QDISC_KIND_FQ,                 0
 FairQueueing.FlowLimit,                      config_parse_fair_queueing_u32,                           QDISC_KIND_FQ,                 0
-FairQueueing.Quantum,                        config_parse_fair_queueing_size,                          QDISC_KIND_FQ,                 0
-FairQueueing.InitialQuantum,                 config_parse_fair_queueing_size,                          QDISC_KIND_FQ,                 0
+FairQueueing.QuantumBytes,                   config_parse_fair_queueing_size,                          QDISC_KIND_FQ,                 0
+FairQueueing.InitialQuantumBytes,            config_parse_fair_queueing_size,                          QDISC_KIND_FQ,                 0
 FairQueueing.MaximumRate,                    config_parse_fair_queueing_max_rate,                      QDISC_KIND_FQ,                 0
 FairQueueing.Buckets,                        config_parse_fair_queueing_u32,                           QDISC_KIND_FQ,                 0
 FairQueueing.OrphanMask,                     config_parse_fair_queueing_u32,                           QDISC_KIND_FQ,                 0
@@ -345,9 +355,9 @@ FairQueueing.CEThresholdSec,                 config_parse_fair_queueing_usec,
 FairQueueingControlledDelay.Parent,          config_parse_qdisc_parent,                                QDISC_KIND_FQ_CODEL,           0
 FairQueueingControlledDelay.Handle,          config_parse_qdisc_handle,                                QDISC_KIND_FQ_CODEL,           0
 FairQueueingControlledDelay.PacketLimit,     config_parse_fair_queueing_controlled_delay_u32,          QDISC_KIND_FQ_CODEL,           0
-FairQueueingControlledDelay.MemoryLimit,     config_parse_fair_queueing_controlled_delay_size,         QDISC_KIND_FQ_CODEL,           0
+FairQueueingControlledDelay.MemoryLimitBytes, config_parse_fair_queueing_controlled_delay_size,        QDISC_KIND_FQ_CODEL,           0
 FairQueueingControlledDelay.Flows,           config_parse_fair_queueing_controlled_delay_u32,          QDISC_KIND_FQ_CODEL,           0
-FairQueueingControlledDelay.Quantum,         config_parse_fair_queueing_controlled_delay_size,         QDISC_KIND_FQ_CODEL,           0
+FairQueueingControlledDelay.QuantumBytes,    config_parse_fair_queueing_controlled_delay_size,         QDISC_KIND_FQ_CODEL,           0
 FairQueueingControlledDelay.TargetSec,       config_parse_fair_queueing_controlled_delay_usec,         QDISC_KIND_FQ_CODEL,           0
 FairQueueingControlledDelay.IntervalSec,     config_parse_fair_queueing_controlled_delay_usec,         QDISC_KIND_FQ_CODEL,           0
 FairQueueingControlledDelay.CEThresholdSec,  config_parse_fair_queueing_controlled_delay_usec,         QDISC_KIND_FQ_CODEL,           0
@@ -392,12 +402,12 @@ StochasticFairnessQueueing.Handle,           config_parse_qdisc_handle,
 StochasticFairnessQueueing.PerturbPeriodSec, config_parse_stochastic_fairness_queueing_perturb_period, QDISC_KIND_SFQ,                0
 TokenBucketFilter.Parent,                    config_parse_qdisc_parent,                                QDISC_KIND_TBF,                0
 TokenBucketFilter.Handle,                    config_parse_qdisc_handle,                                QDISC_KIND_TBF,                0
-TokenBucketFilter.Rate,                      config_parse_token_bucket_filter_size,                    QDISC_KIND_TBF,                0
-TokenBucketFilter.Burst,                     config_parse_token_bucket_filter_size,                    QDISC_KIND_TBF,                0
-TokenBucketFilter.LimitSize,                 config_parse_token_bucket_filter_size,                    QDISC_KIND_TBF,                0
+TokenBucketFilter.Rate,                      config_parse_token_bucket_filter_rate,                    QDISC_KIND_TBF,                0
+TokenBucketFilter.BurstBytes,                config_parse_token_bucket_filter_size,                    QDISC_KIND_TBF,                0
+TokenBucketFilter.LimitBytes,                config_parse_token_bucket_filter_size,                    QDISC_KIND_TBF,                0
 TokenBucketFilter.MTUBytes,                  config_parse_token_bucket_filter_size,                    QDISC_KIND_TBF,                0
 TokenBucketFilter.MPUBytes,                  config_parse_token_bucket_filter_size,                    QDISC_KIND_TBF,                0
-TokenBucketFilter.PeakRate,                  config_parse_token_bucket_filter_size,                    QDISC_KIND_TBF,                0
+TokenBucketFilter.PeakRate,                  config_parse_token_bucket_filter_rate,                    QDISC_KIND_TBF,                0
 TokenBucketFilter.LatencySec,                config_parse_token_bucket_filter_latency,                 QDISC_KIND_TBF,                0
 TrivialLinkEqualizer.Parent,                 config_parse_qdisc_parent,                                QDISC_KIND_TEQL,               0
 TrivialLinkEqualizer.Handle,                 config_parse_qdisc_handle,                                QDISC_KIND_TEQL,               0
@@ -436,3 +446,9 @@ TrafficControlQueueingDiscipline.NetworkEmulatorDelayJitterSec, config_parse_net
 TrafficControlQueueingDiscipline.NetworkEmulatorLossRate,       config_parse_network_emulator_rate,    0,                             0
 TrafficControlQueueingDiscipline.NetworkEmulatorDuplicateRate,  config_parse_network_emulator_rate,    0,                             0
 TrafficControlQueueingDiscipline.NetworkEmulatorPacketLimit,    config_parse_network_emulator_packet_limit, 0,                        0
+FairQueueing.Quantum,                        config_parse_fair_queueing_size,                          QDISC_KIND_FQ,                 0
+FairQueueing.InitialQuantum,                 config_parse_fair_queueing_size,                          QDISC_KIND_FQ,                 0
+FairQueueingControlledDelay.MemoryLimit,     config_parse_fair_queueing_controlled_delay_size,         QDISC_KIND_FQ_CODEL,           0
+FairQueueingControlledDelay.Quantum,         config_parse_fair_queueing_controlled_delay_size,         QDISC_KIND_FQ_CODEL,           0
+TokenBucketFilter.Burst,                     config_parse_token_bucket_filter_size,                    QDISC_KIND_TBF,                0
+TokenBucketFilter.LimitSize,                 config_parse_token_bucket_filter_size,                    QDISC_KIND_TBF,                0
index e3012b476993aa2eb67cf1564dd8ac1e566ae778..5316faeedb27e5d44d17f52428a6d91c637ef753 100644 (file)
@@ -16,6 +16,7 @@
 #include "network-internal.h"
 #include "networkd-manager.h"
 #include "networkd-network.h"
+#include "networkd-sriov.h"
 #include "parse-util.h"
 #include "path-lookup.h"
 #include "set.h"
@@ -158,6 +159,7 @@ int network_verify(Network *network) {
         Route *route, *route_next;
         FdbEntry *fdb, *fdb_next;
         TrafficControl *tc;
+        SRIOV *sr_iov;
         Iterator i;
 
         assert(network);
@@ -330,6 +332,10 @@ int network_verify(Network *network) {
                 if (traffic_control_section_verify(tc, &has_root, &has_clsact) < 0)
                         traffic_control_free(tc);
 
+        ORDERED_HASHMAP_FOREACH(sr_iov, network->sr_iov_by_section, i)
+                if (sr_iov_section_verify(sr_iov) < 0)
+                        sr_iov_free(sr_iov);
+
         return 0;
 }
 
@@ -484,6 +490,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                         filename, NETWORK_DIRS, dropin_dirname,
                         "Match\0"
                         "Link\0"
+                        "SR-IOV\0"
                         "Network\0"
                         "Address\0"
                         "Neighbor\0"
@@ -731,6 +738,7 @@ static Network *network_free(Network *network) {
         hashmap_free(network->prefixes_by_section);
         hashmap_free(network->route_prefixes_by_section);
         hashmap_free(network->rules_by_section);
+        ordered_hashmap_free_with_destructor(network->sr_iov_by_section, sr_iov_free);
         ordered_hashmap_free_with_destructor(network->tc_by_section, traffic_control_free);
 
         if (network->manager &&
index 916bfdc9a837dc946bb02f1ce33d5eee3bb81e06..2ce555bfc557f7686b5027fa9090b4f9a1f6b352 100644 (file)
@@ -17,6 +17,7 @@
 #include "networkd-brvlan.h"
 #include "networkd-dhcp-common.h"
 #include "networkd-dhcp4.h"
+#include "networkd-dhcp6.h"
 #include "networkd-dhcp-server.h"
 #include "networkd-fdb.h"
 #include "networkd-ipv6-proxy-ndp.h"
@@ -150,7 +151,6 @@ struct Network {
         bool dhcp6_use_dns_set;
         bool dhcp6_use_ntp;
         bool dhcp6_use_ntp_set;
-        bool dhcp6_without_ra;
         uint8_t dhcp6_pd_length;
         uint32_t dhcp6_route_metric;
         bool dhcp6_route_metric_set;
@@ -158,6 +158,7 @@ struct Network {
         char **dhcp6_user_class;
         char **dhcp6_vendor_class;
         struct in6_addr dhcp6_pd_address;
+        DHCP6ClientStartMode dhcp6_without_ra;
         OrderedHashmap *dhcp6_client_send_options;
         OrderedHashmap *dhcp6_client_send_vendor_options;
         Set *dhcp6_request_options;
@@ -311,6 +312,7 @@ struct Network {
         Hashmap *route_prefixes_by_section;
         Hashmap *rules_by_section;
         OrderedHashmap *tc_by_section;
+        OrderedHashmap *sr_iov_by_section;
 
         /* All kinds of DNS configuration */
         struct in_addr_data *dns;
diff --git a/src/network/networkd-sriov.c b/src/network/networkd-sriov.c
new file mode 100644 (file)
index 0000000..5ae751e
--- /dev/null
@@ -0,0 +1,501 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+
+#include "alloc-util.h"
+#include "netlink-util.h"
+#include "networkd-manager.h"
+#include "networkd-sriov.h"
+#include "parse-util.h"
+#include "set.h"
+#include "string-util.h"
+
+static int sr_iov_new(SRIOV **ret) {
+        SRIOV *sr_iov;
+
+        sr_iov = new(SRIOV, 1);
+        if (!sr_iov)
+                return -ENOMEM;
+
+        *sr_iov = (SRIOV) {
+                  .vf = (uint32_t) -1,
+                  .vlan_proto = ETH_P_8021Q,
+                  .vf_spoof_check_setting = -1,
+                  .trust = -1,
+                  .query_rss = -1,
+                  .link_state = _SR_IOV_LINK_STATE_INVALID,
+        };
+
+        *ret = TAKE_PTR(sr_iov);
+
+        return 0;
+}
+
+static int sr_iov_new_static(Network *network, const char *filename, unsigned section_line, SRIOV **ret) {
+        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        _cleanup_(sr_iov_freep) SRIOV *sr_iov = NULL;
+        SRIOV *existing = NULL;
+        int r;
+
+        assert(network);
+        assert(ret);
+        assert(filename);
+        assert(section_line > 0);
+
+        r = network_config_section_new(filename, section_line, &n);
+        if (r < 0)
+                return r;
+
+        existing = ordered_hashmap_get(network->sr_iov_by_section, n);
+        if (existing) {
+                *ret = existing;
+                return 0;
+        }
+
+        r = sr_iov_new(&sr_iov);
+        if (r < 0)
+                return r;
+
+        sr_iov->network = network;
+        sr_iov->section = TAKE_PTR(n);
+
+        r = ordered_hashmap_ensure_allocated(&network->sr_iov_by_section, &network_config_hash_ops);
+        if (r < 0)
+                return r;
+
+        r = ordered_hashmap_put(network->sr_iov_by_section, sr_iov->section, sr_iov);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(sr_iov);
+        return 0;
+}
+
+SRIOV *sr_iov_free(SRIOV *sr_iov) {
+        if (!sr_iov)
+                return NULL;
+
+        if (sr_iov->network && sr_iov->section)
+                ordered_hashmap_remove(sr_iov->network->sr_iov_by_section, sr_iov->section);
+
+        network_config_section_free(sr_iov->section);
+
+        return mfree(sr_iov);
+}
+
+static int sr_iov_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+        int r;
+
+        assert(link);
+        assert(link->sr_iov_messages > 0);
+        link->sr_iov_messages--;
+
+        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+                return 1;
+
+        r = sd_netlink_message_get_errno(m);
+        if (r < 0 && r != -EEXIST) {
+                log_link_message_error_errno(link, m, r, "Could not set up SR-IOV");
+                link_enter_failed(link);
+                return 1;
+        }
+
+        if (link->sr_iov_messages == 0) {
+                log_link_debug(link, "SR-IOV configured");
+                link->sr_iov_configured = true;
+                link_check_ready(link);
+        }
+
+        return 1;
+}
+
+int sr_iov_configure(Link *link, SRIOV *sr_iov) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+        int r;
+
+        assert(link);
+        assert(link->manager);
+        assert(link->manager->rtnl);
+        assert(link->ifindex > 0);
+
+        log_link_debug(link, "Setting SR-IOV virtual function %"PRIu32, sr_iov->vf);
+
+        r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
+
+        r = sd_netlink_message_open_container(req, IFLA_VFINFO_LIST);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not open IFLA_VFINFO_LIST container: %m");
+
+        r = sd_netlink_message_open_container(req, IFLA_VF_INFO);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not open IFLA_VF_INFO container: %m");
+
+        if (!ether_addr_is_null(&sr_iov->mac)) {
+                struct ifla_vf_mac ivm = {
+                        .vf = sr_iov->vf,
+                };
+
+                memcpy(ivm.mac, &sr_iov->mac, ETH_ALEN);
+                r = sd_netlink_message_append_data(req, IFLA_VF_MAC, &ivm, sizeof(struct ifla_vf_mac));
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append IFLA_VF_MAC: %m");
+        }
+
+        if (sr_iov->vf_spoof_check_setting >= 0) {
+                struct ifla_vf_spoofchk ivs = {
+                        .vf = sr_iov->vf,
+                        .setting = sr_iov->vf_spoof_check_setting,
+                };
+
+                r = sd_netlink_message_append_data(req, IFLA_VF_SPOOFCHK, &ivs, sizeof(struct ifla_vf_spoofchk));
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append IFLA_VF_SPOOFCHK: %m");
+        }
+
+        if (sr_iov->query_rss >= 0) {
+                struct ifla_vf_rss_query_en ivs = {
+                        .vf = sr_iov->vf,
+                        .setting = sr_iov->query_rss,
+                };
+
+                r = sd_netlink_message_append_data(req, IFLA_VF_RSS_QUERY_EN, &ivs, sizeof(struct ifla_vf_rss_query_en));
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append IFLA_VF_RSS_QUERY_EN: %m");
+        }
+
+        if (sr_iov->trust >= 0) {
+                struct ifla_vf_trust ivt = {
+                        .vf = sr_iov->vf,
+                        .setting = sr_iov->trust,
+                };
+
+                r = sd_netlink_message_append_data(req, IFLA_VF_TRUST, &ivt, sizeof(struct ifla_vf_trust));
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append IFLA_VF_TRUST: %m");
+        }
+
+        if (sr_iov->link_state >= 0) {
+                struct ifla_vf_link_state ivl = {
+                        .vf = sr_iov->vf,
+                        .link_state = sr_iov->link_state,
+                };
+
+                r = sd_netlink_message_append_data(req, IFLA_VF_LINK_STATE, &ivl, sizeof(struct ifla_vf_link_state));
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append IFLA_VF_LINK_STATE: %m");
+        }
+
+        if (sr_iov->vlan > 0) {
+                /* Because of padding, first the buffer must be initialized with 0. */
+                struct ifla_vf_vlan_info ivvi = {};
+                ivvi.vf = sr_iov->vf;
+                ivvi.vlan = sr_iov->vlan;
+                ivvi.qos = sr_iov->qos;
+                ivvi.vlan_proto = htobe16(sr_iov->vlan_proto);
+
+                r = sd_netlink_message_open_container(req, IFLA_VF_VLAN_LIST);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not open IFLA_VF_VLAN_LIST container: %m");
+
+                r = sd_netlink_message_append_data(req, IFLA_VF_VLAN_INFO, &ivvi, sizeof(struct ifla_vf_vlan_info));
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append IFLA_VF_VLAN_INFO: %m");
+
+                r = sd_netlink_message_close_container(req);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not close IFLA_VF_VLAN_LIST container: %m");
+        }
+
+        r = sd_netlink_message_close_container(req);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not close IFLA_VF_INFO container: %m");
+
+        r = sd_netlink_message_close_container(req);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not close IFLA_VFINFO_LIST container: %m");
+
+        r = netlink_call_async(link->manager->rtnl, NULL, req, sr_iov_handler,
+                               link_netlink_destroy_callback, link);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+
+        link_ref(link);
+        link->sr_iov_messages++;
+
+        return 0;
+}
+
+int sr_iov_section_verify(SRIOV *sr_iov) {
+        assert(sr_iov);
+
+        if (section_is_invalid(sr_iov->section))
+                return -EINVAL;
+
+        if (sr_iov->vf == (uint32_t) -1)
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: [SRIOV] section without VirtualFunction= field configured. "
+                                         "Ignoring [SRIOV] section from line %u.",
+                                         sr_iov->section->filename, sr_iov->section->line);
+
+        return 0;
+}
+
+int config_parse_sr_iov_uint32(
+                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_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+        Network *network = data;
+        uint32_t k;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = sr_iov_new_static(network, filename, section_line, &sr_iov);
+        if (r < 0)
+                return r;
+
+        if (isempty(rvalue)) {
+                if (streq(lvalue, "VirtualFunction"))
+                        sr_iov->vf = (uint32_t) -1;
+                else if (streq(lvalue, "VLANId"))
+                        sr_iov->vlan = 0;
+                else if (streq(lvalue, "QualityOfService"))
+                        sr_iov->qos = 0;
+                else
+                        assert_not_reached("Invalid lvalue");
+
+                TAKE_PTR(sr_iov);
+                return 0;
+        }
+
+        r = safe_atou32(rvalue, &k);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        if (streq(lvalue, "VLANId")) {
+                if (k == 0 || k > 4095) {
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid SR-IOV VLANId: %d", k);
+                        return 0;
+                }
+                sr_iov->vlan = k;
+        } else if (streq(lvalue, "VirtualFunction")) {
+                if (k >= INT_MAX) {
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid SR-IOV virtual function: %d", k);
+                        return 0;
+                }
+                sr_iov->vf = k;
+        } else if (streq(lvalue, "QualityOfService"))
+                sr_iov->qos = k;
+        else
+                assert_not_reached("Invalid lvalue");
+
+        TAKE_PTR(sr_iov);
+        return 0;
+}
+
+int config_parse_sr_iov_vlan_proto(
+                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_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = sr_iov_new_static(network, filename, section_line, &sr_iov);
+        if (r < 0)
+                return r;
+
+        if (isempty(rvalue) || streq(rvalue, "802.1Q"))
+                sr_iov->vlan_proto = ETH_P_8021Q;
+        else if (streq(rvalue, "802.1ad"))
+                sr_iov->vlan_proto = ETH_P_8021AD;
+        else {
+                log_syntax(unit, LOG_ERR, filename, line, 0,
+                           "Invalid SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        TAKE_PTR(sr_iov);
+        return 0;
+}
+
+int config_parse_sr_iov_link_state(
+                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_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = sr_iov_new_static(network, filename, section_line, &sr_iov);
+        if (r < 0)
+                return r;
+
+        /* Unfortunately, SR_IOV_LINK_STATE_DISABLE is 2, not 0. So, we cannot use
+         * DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN() macro. */
+
+        if (isempty(rvalue)) {
+                sr_iov->link_state = _SR_IOV_LINK_STATE_INVALID;
+                TAKE_PTR(sr_iov);
+                return 0;
+        }
+
+        if (streq(rvalue, "auto")) {
+                sr_iov->link_state = SR_IOV_LINK_STATE_AUTO;
+                TAKE_PTR(sr_iov);
+                return 0;
+        }
+
+        r = parse_boolean(rvalue);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        sr_iov->link_state = r ? SR_IOV_LINK_STATE_ENABLE : SR_IOV_LINK_STATE_DISABLE;
+        TAKE_PTR(sr_iov);
+        return 0;
+}
+
+int config_parse_sr_iov_boolean(
+                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_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = sr_iov_new_static(network, filename, section_line, &sr_iov);
+        if (r < 0)
+                return r;
+
+        if (isempty(rvalue)) {
+                if (streq(lvalue, "MACSpoofCheck"))
+                        sr_iov->vf_spoof_check_setting = -1;
+                else if (streq(lvalue, "QueryReceiveSideScaling"))
+                        sr_iov->query_rss = -1;
+                else if (streq(lvalue, "Trust"))
+                        sr_iov->trust = -1;
+                else
+                        assert_not_reached("Invalid lvalue");
+
+                TAKE_PTR(sr_iov);
+                return 0;
+        }
+
+        r = parse_boolean(rvalue);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse '%s=', ignoring: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        if (streq(lvalue, "MACSpoofCheck"))
+                sr_iov->vf_spoof_check_setting = r;
+        else if (streq(lvalue, "QueryReceiveSideScaling"))
+                sr_iov->query_rss = r;
+        else if (streq(lvalue, "Trust"))
+                sr_iov->trust = r;
+        else
+                assert_not_reached("Invalid lvalue");
+
+        TAKE_PTR(sr_iov);
+        return 0;
+}
+
+int config_parse_sr_iov_mac(
+                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_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = sr_iov_new_static(network, filename, section_line, &sr_iov);
+        if (r < 0)
+                return r;
+
+        if (isempty(rvalue)) {
+                sr_iov->mac = ETHER_ADDR_NULL;
+                TAKE_PTR(sr_iov);
+                return 0;
+        }
+
+        r = ether_addr_from_string(rvalue, &sr_iov->mac);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, 0,
+                           "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        TAKE_PTR(sr_iov);
+        return 0;
+}
diff --git a/src/network/networkd-sriov.h b/src/network/networkd-sriov.h
new file mode 100644 (file)
index 0000000..a545d12
--- /dev/null
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+#pragma once
+
+#include <linux/if_link.h>
+
+#include "conf-parser.h"
+#include "networkd-link.h"
+#include "networkd-network.h"
+#include "networkd-util.h"
+
+typedef enum SRIOVLinkState {
+        SR_IOV_LINK_STATE_AUTO = IFLA_VF_LINK_STATE_AUTO,
+        SR_IOV_LINK_STATE_ENABLE = IFLA_VF_LINK_STATE_ENABLE,
+        SR_IOV_LINK_STATE_DISABLE = IFLA_VF_LINK_STATE_DISABLE,
+        _SR_IOV_LINK_STATE_MAX,
+        _SR_IOV_LINK_STATE_INVALID = -1,
+} SRIOVLinkState;
+
+typedef struct SRIOV {
+        NetworkConfigSection *section;
+        Network *network;
+
+        uint32_t vf;   /* 0 - 2147483646 */
+        uint32_t vlan; /* 0 - 4095, 0 disables VLAN filter */
+        uint32_t qos;
+        uint16_t vlan_proto; /* ETH_P_8021Q or ETH_P_8021AD */
+        int vf_spoof_check_setting;
+        int query_rss;
+        int trust;
+        SRIOVLinkState link_state;
+        struct ether_addr mac;
+} SRIOV;
+
+SRIOV *sr_iov_free(SRIOV *sr_iov);
+
+int sr_iov_configure(Link *link, SRIOV *sr_iov);
+int sr_iov_section_verify(SRIOV *sr_iov);
+
+DEFINE_NETWORK_SECTION_FUNCTIONS(SRIOV, sr_iov_free);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_uint32);
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_boolean);
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_link_state);
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_vlan_proto);
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_mac);
index b499661bd3fda85bab3c3236bdafb9a7d280adc6..286c7e172ff17be5c4e31a9e3bfe37690390a856 100644 (file)
@@ -136,14 +136,14 @@ int config_parse_cake_overhead(
         r = safe_atoi32(rvalue, &v);
         if (r < 0) {
                 log_syntax(unit, LOG_ERR, filename, line, r,
-                           "Failed to parse 'Overhead=', ignoring assignment: %s",
-                           rvalue);
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
                 return 0;
         }
         if (v < -64 || v > 256) {
                 log_syntax(unit, LOG_ERR, filename, line, 0,
-                           "Invalid 'Overhead=', ignoring assignment: %s",
-                           rvalue);
+                           "Invalid '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
                 return 0;
         }
 
index ac64eb8b03e2d3e91d74f158463feb17c56c0606..a671f32d99d2bc6fbd85349fc4f51e434e95c51d 100644 (file)
@@ -79,7 +79,7 @@ int config_parse_drr_size(
                 return 0;
         }
 
-        r = parse_size(rvalue, 1000, &u);
+        r = parse_size(rvalue, 1024, &u);
         if (r < 0) {
                 log_syntax(unit, LOG_ERR, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
index c4fa5646dd53187e2393b0435e8c52daa52cbf5b..83e9d8bff38a84f463370f29bd6c12fadd3c6016 100644 (file)
@@ -283,7 +283,7 @@ int config_parse_ets_prio(
                                    lvalue, word);
                         continue;
                 }
-                if (ets->n_quanta > TC_PRIO_MAX) {
+                if (ets->n_prio > TC_PRIO_MAX) {
                         log_syntax(unit, LOG_ERR, filename, line, 0,
                                    "Too many priomap in '%s=', ignoring assignment: %s",
                                    lvalue, word);
index 8ef6e513bb1c840e4476c41ce7987ef131411cd7..1a608dd7c0004734c4c8ef1a1a031a4f81e8df6e 100644 (file)
@@ -140,7 +140,7 @@ int config_parse_bfifo_size(
                 return 0;
         }
 
-        r = parse_size(rvalue, 1000, &u);
+        r = parse_size(rvalue, 1024, &u);
         if (r < 0) {
                 log_syntax(unit, LOG_ERR, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
index 6c7932c70f738e5a441456545157a47e210a38ab..721112d3173bbdcc7a26b4c5180d488727bc2b18 100644 (file)
@@ -9,6 +9,7 @@
 #include "parse-util.h"
 #include "qdisc.h"
 #include "string-util.h"
+#include "strv.h"
 
 static int fair_queueing_controlled_delay_init(QDisc *qdisc) {
         FairQueueingControlledDelay *fqcd;
@@ -301,15 +302,15 @@ int config_parse_fair_queueing_controlled_delay_size(
 
         fqcd = FQ_CODEL(qdisc);
 
-        if (streq(lvalue, "MemoryLimit"))
+        if (STR_IN_SET(lvalue, "MemoryLimitBytes", "MemoryLimit"))
                 p = &fqcd->memory_limit;
-        else if (streq(lvalue, "Quantum"))
+        else if (STR_IN_SET(lvalue, "QuantumBytes", "Quantum"))
                 p = &fqcd->quantum;
         else
                 assert_not_reached("Invalid lvalue.");
 
         if (isempty(rvalue)) {
-                if (streq(lvalue, "MemoryLimit"))
+                if (STR_IN_SET(lvalue, "MemoryLimitBytes", "MemoryLimit"))
                         *p = UINT32_MAX;
                 else
                         *p = 0;
index c7eeec230737c9721e8e386b3014b86ea6a0ab87..f717dc790c7c0bf57ce7cc72d7f2cd1d910ee927 100644 (file)
@@ -9,7 +9,7 @@
 #include "netlink-util.h"
 #include "parse-util.h"
 #include "string-util.h"
-#include "util.h"
+#include "strv.h"
 
 static int fair_queueing_init(QDisc *qdisc) {
         FairQueueing *fq;
@@ -198,9 +198,9 @@ int config_parse_fair_queueing_size(
 
         fq = FQ(qdisc);
 
-        if (streq(lvalue, "Quantum"))
+        if (STR_IN_SET(lvalue, "QuantumBytes", "Quantum"))
                 p = &fq->quantum;
-        else if (streq(lvalue, "InitialQuantum"))
+        else if (STR_IN_SET(lvalue, "InitialQuantumBytes", "InitialQuantum"))
                 p = &fq->initial_quantum;
         else
                 assert_not_reached("Invalid lvalue");
index 71d5b15e81c75089d1182cbf28f796b84b54ada0..52c17625bf018bf91971c9e300e4ec74bdd62a3d 100644 (file)
@@ -142,7 +142,7 @@ int config_parse_quick_fair_queueing_max_packet(
                 return 0;
         }
 
-        r = parse_size(rvalue, 1000, &v);
+        r = parse_size(rvalue, 1024, &v);
         if (r < 0) {
                 log_syntax(unit, LOG_ERR, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
index 0682ab4cc6575463aa0ad0dc1654149b0d1bfd87..2c730352d2ccd3241318b810840df6828e4c9bf2 100644 (file)
@@ -12,8 +12,8 @@
 #include "parse-util.h"
 #include "qdisc.h"
 #include "string-util.h"
+#include "strv.h"
 #include "tc-util.h"
-#include "util.h"
 
 static int token_bucket_filter_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
         uint32_t rtab[256], ptab[256];
@@ -143,24 +143,22 @@ int config_parse_token_bucket_filter_size(
         tbf = TBF(qdisc);
 
         if (isempty(rvalue)) {
-                if (streq(lvalue, "Rate"))
-                        tbf->rate = 0;
-                else if (streq(lvalue, "Burst"))
+                if (STR_IN_SET(lvalue, "BurstBytes", "Burst"))
                         tbf->burst = 0;
-                else if (streq(lvalue, "LimitSize"))
+                else if (STR_IN_SET(lvalue, "LimitBytes", "LimitSize"))
                         tbf->limit = 0;
                 else if (streq(lvalue, "MTUBytes"))
                         tbf->mtu = 0;
                 else if (streq(lvalue, "MPUBytes"))
                         tbf->mpu = 0;
-                else if (streq(lvalue, "PeakRate"))
-                        tbf->peak_rate = 0;
+                else
+                        assert_not_reached("unknown lvalue");
 
                 qdisc = NULL;
                 return 0;
         }
 
-        r = parse_size(rvalue, 1000, &k);
+        r = parse_size(rvalue, 1024, &k);
         if (r < 0) {
                 log_syntax(unit, LOG_ERR, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
@@ -168,18 +166,76 @@ int config_parse_token_bucket_filter_size(
                 return 0;
         }
 
-        if (streq(lvalue, "Rate"))
-                tbf->rate = k / 8;
-        else if (streq(lvalue, "Burst"))
+        if (STR_IN_SET(lvalue, "BurstBytes", "Burst"))
                 tbf->burst = k;
-        else if (streq(lvalue, "LimitSize"))
+        else if (STR_IN_SET(lvalue, "LimitBytes", "LimitSize"))
                 tbf->limit = k;
         else if (streq(lvalue, "MPUBytes"))
                 tbf->mpu = k;
         else if (streq(lvalue, "MTUBytes"))
                 tbf->mtu = k;
+        else
+                assert_not_reached("unknown lvalue");
+
+        qdisc = NULL;
+
+        return 0;
+}
+
+int config_parse_token_bucket_filter_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;
+        Network *network = data;
+        TokenBucketFilter *tbf;
+        uint64_t k, *p;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(QDISC_KIND_TBF, 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");
+
+        tbf = TBF(qdisc);
+        if (streq(lvalue, "Rate"))
+                p = &tbf->rate;
         else if (streq(lvalue, "PeakRate"))
-                tbf->peak_rate = k / 8;
+                p = &tbf->peak_rate;
+        else
+                assert_not_reached("unknown lvalue");
+
+        if (isempty(rvalue)) {
+                *p = 0;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = parse_size(rvalue, 1000, &k);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        *p = k / 8;
 
         qdisc = NULL;
 
index b66aef206c49b7455703262ead378d81b12c0964..a785be25e0e60b287ef11458c8da9407cc6f5eca 100644 (file)
@@ -23,3 +23,4 @@ extern const QDiscVTable tbf_vtable;
 
 CONFIG_PARSER_PROTOTYPE(config_parse_token_bucket_filter_latency);
 CONFIG_PARSER_PROTOTYPE(config_parse_token_bucket_filter_size);
+CONFIG_PARSER_PROTOTYPE(config_parse_token_bucket_filter_rate);
index 6da00be2abfdea9b9526c5d09ce4df8c6f337cec..30805212798993a203ab19e16870ca2e59334387 100644 (file)
@@ -1666,7 +1666,7 @@ static int context_dump_partitions(Context *context, const char *node) {
                                 TABLE_UINT64, p->new_padding,
                                 TABLE_STRING, padding_change, TABLE_SET_COLOR, !p->partitions_next && sum_padding > 0 ? ansi_underline() : NULL);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to add row to table: %m");
+                        return table_log_add_error(r);
         }
 
         if (sum_padding > 0 || sum_size > 0) {
@@ -1689,7 +1689,7 @@ static int context_dump_partitions(Context *context, const char *node) {
                                 TABLE_EMPTY,
                                 TABLE_STRING, b);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to add row to table: %m");
+                        return table_log_add_error(r);
         }
 
         r = table_print(t, stdout);
index 7a80240b970fe48ddd31906747a6dcadbe04be39..63ede724734433921d49fe4f7cd1ccce52cce10d 100644 (file)
@@ -839,7 +839,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAns
 
         rds = p->size - saved_size;
 
-        switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
+        switch (rr->unparsable ? _DNS_TYPE_INVALID : rr->key->type) {
 
         case DNS_TYPE_SRV:
                 r = dns_packet_append_uint16(p, rr->srv.priority, NULL);
@@ -1125,7 +1125,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAns
 
         case DNS_TYPE_OPT:
         case DNS_TYPE_OPENPGPKEY:
-        case _DNS_TYPE_INVALID: /* unparseable */
+        case _DNS_TYPE_INVALID: /* unparsable */
         default:
 
                 r = dns_packet_append_blob(p, rr->generic.data, rr->generic.data_size, NULL);
@@ -1815,8 +1815,8 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
                         break;
                 } else {
                         dns_packet_rewind(p, pos);
-                        rr->unparseable = true;
-                        goto unparseable;
+                        rr->unparsable = true;
+                        goto unparsable;
                 }
         }
 
@@ -2059,7 +2059,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
         case DNS_TYPE_OPT: /* we only care about the header of OPT for now. */
         case DNS_TYPE_OPENPGPKEY:
         default:
-        unparseable:
+        unparsable:
                 r = dns_packet_read_memdup(p, rdlength, &rr->generic.data, &rr->generic.data_size, NULL);
 
                 break;
index 6ba26a24b2c3f2cc02d6f66851831e0a7844f831..fa43dd089d4019611a5b18287a3ae21c77348991 100644 (file)
@@ -474,11 +474,11 @@ static DnsResourceRecord* dns_resource_record_free(DnsResourceRecord *rr) {
 
                 case DNS_TYPE_OPENPGPKEY:
                 default:
-                        if (!rr->unparseable)
+                        if (!rr->unparsable)
                                 free(rr->generic.data);
                 }
 
-                if (rr->unparseable)
+                if (rr->unparsable)
                         free(rr->generic.data);
 
                 free(rr->wire_format);
@@ -563,10 +563,10 @@ int dns_resource_record_payload_equal(const DnsResourceRecord *a, const DnsResou
 
         /* Check if a and b are the same, but don't look at their keys */
 
-        if (a->unparseable != b->unparseable)
+        if (a->unparsable != b->unparsable)
                 return 0;
 
-        switch (a->unparseable ? _DNS_TYPE_INVALID : a->key->type) {
+        switch (a->unparsable ? _DNS_TYPE_INVALID : a->key->type) {
 
         case DNS_TYPE_SRV:
                 r = dns_name_equal(a->srv.name, b->srv.name);
@@ -828,7 +828,7 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
 
         dns_resource_key_to_string(rr->key, k, sizeof(k));
 
-        switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
+        switch (rr->unparsable ? _DNS_TYPE_INVALID : rr->key->type) {
 
         case DNS_TYPE_SRV:
                 r = asprintf(&s, "%s %u %u %u %s",
@@ -1175,7 +1175,7 @@ ssize_t dns_resource_record_payload(DnsResourceRecord *rr, void **out) {
         assert(rr);
         assert(out);
 
-        switch(rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
+        switch(rr->unparsable ? _DNS_TYPE_INVALID : rr->key->type) {
         case DNS_TYPE_SRV:
         case DNS_TYPE_PTR:
         case DNS_TYPE_NS:
@@ -1343,7 +1343,7 @@ void dns_resource_record_hash_func(const DnsResourceRecord *rr, struct siphash *
 
         dns_resource_key_hash_func(rr->key, state);
 
-        switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
+        switch (rr->unparsable ? _DNS_TYPE_INVALID : rr->key->type) {
 
         case DNS_TYPE_SRV:
                 siphash24_compress(&rr->srv.priority, sizeof(rr->srv.priority), state);
@@ -1510,9 +1510,9 @@ DnsResourceRecord *dns_resource_record_copy(DnsResourceRecord *rr) {
         copy->expiry = rr->expiry;
         copy->n_skip_labels_signer = rr->n_skip_labels_signer;
         copy->n_skip_labels_source = rr->n_skip_labels_source;
-        copy->unparseable = rr->unparseable;
+        copy->unparsable = rr->unparsable;
 
-        switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
+        switch (rr->unparsable ? _DNS_TYPE_INVALID : rr->key->type) {
 
         case DNS_TYPE_SRV:
                 copy->srv.priority = rr->srv.priority;
index 291447f00e7ba20f075cc4305eb7f367c7054490..6c824f7962e1e71b43650e85557f285405b14bdd 100644 (file)
@@ -102,7 +102,7 @@ struct DnsResourceRecord {
         /* How many labels to strip to determine "synthesizing source" of this RR, i.e. the wildcard's immediate parent. -1 if not signed. */
         unsigned n_skip_labels_source;
 
-        bool unparseable:1;
+        bool unparsable:1;
 
         bool wire_format_canonical:1;
         void *wire_format;
index 1e2ff05c3e527285b21542d1876230974cc95fb5..d4c49e673ef8a65dbca4df91e3968b7870a24fec 100644 (file)
@@ -190,7 +190,7 @@ static int dns_stream_identify(DnsStream *s) {
                 s->ifindex = manager_find_ifindex(s->manager, s->local.sa.sa_family, s->local.sa.sa_family == AF_INET ? (union in_addr_union*) &s->local.in.sin_addr : (union in_addr_union*)  &s->local.in6.sin6_addr);
 
         if (s->protocol == DNS_PROTOCOL_LLMNR && s->ifindex > 0) {
-                uint32_t ifindex = htobe32(s->ifindex);
+                be32_t ifindex = htobe32(s->ifindex);
 
                 /* Make sure all packets for this connection are sent on the same interface */
                 if (s->local.sa.sa_family == AF_INET) {
index b4a7d86afedeb9485632b51c7cd69b18774e0311..632964df4491c81de264cb1112eae0c3676b02f2 100644 (file)
@@ -151,6 +151,28 @@ char *pkcs11_token_label(const CK_TOKEN_INFO *token_info) {
         return t;
 }
 
+char *pkcs11_token_manufacturer_id(const CK_TOKEN_INFO *token_info) {
+        char *t;
+
+        t = strndup((char*) token_info->manufacturerID, sizeof(token_info->manufacturerID));
+        if (!t)
+                return NULL;
+
+        strstrip(t);
+        return t;
+}
+
+char *pkcs11_token_model(const CK_TOKEN_INFO *token_info) {
+        char *t;
+
+        t = strndup((char*) token_info->model, sizeof(token_info->model));
+        if (!t)
+                return NULL;
+
+        strstrip(t);
+        return t;
+}
+
 int pkcs11_token_login(
                 CK_FUNCTION_LIST *m,
                 CK_SESSION_HANDLE session,
@@ -165,9 +187,8 @@ int pkcs11_token_login(
         _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;
+        int uri_result, r;
         CK_RV rv;
-        int r;
 
         assert(m);
         assert(token_info);
@@ -211,28 +232,8 @@ int pkcs11_token_login(
 
         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);
-                else 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);
@@ -243,6 +244,27 @@ int pkcs11_token_login(
                         if (unsetenv("PIN") < 0)
                                 return log_error_errno(errno, "Failed to unset $PIN: %m");
                 } else {
+                        _cleanup_free_ char *text = NULL;
+
+                        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);
+                        else 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();
+
                         /* 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)
@@ -702,7 +724,6 @@ static int token_process(
         assert(m);
         assert(slot_info);
         assert(token_info);
-        assert(search_uri);
 
         token_label = pkcs11_token_label(token_info);
         if (!token_label)
@@ -740,7 +761,6 @@ static int slot_process(
         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. */
@@ -786,7 +806,7 @@ static int slot_process(
                 return -EAGAIN;
         }
 
-        if (!p11_kit_uri_match_token_info(search_uri, &token_info)) {
+        if (search_uri && !p11_kit_uri_match_token_info(search_uri, &token_info)) {
                 log_debug("Found non-matching token with URI %s.", token_uri_string);
                 return -EAGAIN;
         }
@@ -820,7 +840,6 @@ static int module_process(
         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
@@ -883,14 +902,14 @@ int pkcs11_find_token(
         _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);
+        if (pkcs11_uri) {
+                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)
index 46791eb23b74c1a627282e9da3c63ad179d343ca..959e7c3e0d9c90f63760ab1898aae978beb81dcc 100644 (file)
@@ -27,6 +27,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(CK_FUNCTION_LIST**, p11_kit_modules_finalize_and_rel
 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);
+char *pkcs11_token_manufacturer_id(const CK_TOKEN_INFO *token_info);
+char *pkcs11_token_model(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);
 
index de04859a2f10db9829236e9e5f12660603ecf2fb..84ededd86e95b719dd211b8a1908af23399fe8a1 100644 (file)
@@ -472,10 +472,13 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) {
 
                 STRV_FOREACH(i, hr->pkcs11_token_uri)
                         printf(i == hr->pkcs11_token_uri ?
-                               "  Sec. Token: %s\n" :
+                               "PKCS11 Token: %s\n" :
                                "              %s\n", *i);
         }
 
+        if (hr->n_fido2_hmac_credential > 0)
+                printf(" FIDO2 Token: %zu\n", hr->n_fido2_hmac_credential);
+
         k = strv_length(hr->hashed_password);
         if (k == 0)
                 printf("   Passwords: %snone%s\n",
index 83d86f69e761ab0a22a2366a0b20476a3a489072..16edaa45face3bb66a17f196efa47d54ba3873c3 100644 (file)
@@ -81,6 +81,7 @@ UserRecord* user_record_new(void) {
                 .password_change_inactive_usec = UINT64_MAX,
                 .password_change_now = -1,
                 .pkcs11_protected_authentication_path_permitted = -1,
+                .fido2_user_presence_permitted = -1,
         };
 
         return h;
@@ -95,6 +96,22 @@ static void pkcs11_encrypted_key_done(Pkcs11EncryptedKey *k) {
         erase_and_free(k->hashed_password);
 }
 
+static void fido2_hmac_credential_done(Fido2HmacCredential *c) {
+        if (!c)
+                return;
+
+        free(c->id);
+}
+
+static void fido2_hmac_salt_done(Fido2HmacSalt *s) {
+        if (!s)
+                return;
+
+        fido2_hmac_credential_done(&s->credential);
+        erase_and_free(s->salt);
+        erase_and_free(s->hashed_password);
+}
+
 static UserRecord* user_record_free(UserRecord *h) {
         if (!h)
                 return NULL;
@@ -120,7 +137,7 @@ static UserRecord* user_record_free(UserRecord *h) {
         strv_free_erase(h->hashed_password);
         strv_free_erase(h->ssh_authorized_keys);
         strv_free_erase(h->password);
-        strv_free_erase(h->pkcs11_pin);
+        strv_free_erase(h->token_pin);
 
         free(h->cifs_service);
         free(h->cifs_user_name);
@@ -147,6 +164,11 @@ static UserRecord* user_record_free(UserRecord *h) {
                 pkcs11_encrypted_key_done(h->pkcs11_encrypted_key + i);
         free(h->pkcs11_encrypted_key);
 
+        for (size_t i = 0; i < h->n_fido2_hmac_credential; i++)
+                fido2_hmac_credential_done(h->fido2_hmac_credential + i);
+        for (size_t i = 0; i < h->n_fido2_hmac_salt; i++)
+                fido2_hmac_salt_done(h->fido2_hmac_salt + i);
+
         json_variant_unref(h->json);
 
         return mfree(h);
@@ -620,8 +642,10 @@ static int dispatch_secret(const char *name, JsonVariant *variant, JsonDispatchF
 
         static const JsonDispatch secret_dispatch_table[] = {
                 { "password",                                   _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv,     offsetof(UserRecord, password),                                       0 },
-                { "pkcs11Pin",                                  _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv,     offsetof(UserRecord, pkcs11_pin),                                     0 },
+                { "tokenPin",                                   _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv,     offsetof(UserRecord, token_pin),                                      0 },
+                { "pkcs11Pin",   /* legacy alias */             _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv,     offsetof(UserRecord, token_pin),                                      0 },
                 { "pkcs11ProtectedAuthenticationPathPermitted", JSON_VARIANT_BOOLEAN,       json_dispatch_tristate, offsetof(UserRecord, pkcs11_protected_authentication_path_permitted), 0 },
+                { "fido2UserPresencePermitted",                 JSON_VARIANT_BOOLEAN,       json_dispatch_tristate, offsetof(UserRecord, fido2_user_presence_permitted),                  0 },
                 {},
         };
 
@@ -706,7 +730,7 @@ static int dispatch_pkcs11_key_data(const char *name, JsonVariant *variant, Json
         int r;
 
         if (json_variant_is_null(variant)) {
-                k->data = mfree(k->data);
+                k->data = erase_and_free(k->data);
                 k->size = 0;
                 return 0;
         }
@@ -766,13 +790,141 @@ static int dispatch_pkcs11_key(const char *name, JsonVariant *variant, JsonDispa
         return 0;
 }
 
+static int dispatch_fido2_hmac_credential(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        Fido2HmacCredential *k = userdata;
+        size_t l;
+        void *b;
+        int r;
+
+        if (json_variant_is_null(variant)) {
+                k->id = mfree(k->id);
+                k->size = 0;
+                return 0;
+        }
+
+        if (!json_variant_is_string(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
+
+        r = unbase64mem(json_variant_string(variant), (size_t) -1, &b, &l);
+        if (r < 0)
+                return json_log(variant, flags, r, "Failed to decode FIDO2 credential ID: %m");
+
+        free_and_replace(k->id, b);
+        k->size = l;
+
+        return 0;
+}
+
+static int dispatch_fido2_hmac_credential_array(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        UserRecord *h = userdata;
+        JsonVariant *e;
+        int r;
+
+        if (!json_variant_is_array(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of strings.", strna(name));
+
+        JSON_VARIANT_ARRAY_FOREACH(e, variant) {
+                Fido2HmacCredential *array;
+                size_t l;
+                void *b;
+
+                if (!json_variant_is_string(e))
+                        return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a string.");
+
+                array = reallocarray(h->fido2_hmac_credential, h->n_fido2_hmac_credential + 1, sizeof(Fido2HmacCredential));
+                if (!array)
+                        return log_oom();
+
+                r = unbase64mem(json_variant_string(e), (size_t) -1, &b, &l);
+                if (r < 0)
+                        return json_log(variant, flags, r, "Failed to decode FIDO2 credential ID: %m");
+
+                h->fido2_hmac_credential = array;
+
+                h->fido2_hmac_credential[h->n_fido2_hmac_credential++] = (Fido2HmacCredential) {
+                        .id = b,
+                        .size = l,
+                };
+        }
+
+        return 0;
+}
+
+static int dispatch_fido2_hmac_salt_value(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        Fido2HmacSalt *k = userdata;
+        size_t l;
+        void *b;
+        int r;
+
+        if (json_variant_is_null(variant)) {
+                k->salt = erase_and_free(k->salt);
+                k->salt_size = 0;
+                return 0;
+        }
+
+        if (!json_variant_is_string(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
+
+        r = unbase64mem(json_variant_string(variant), (size_t) -1, &b, &l);
+        if (r < 0)
+                return json_log(variant, flags, r, "Failed to decode FIDO2 salt: %m");
+
+        erase_and_free(k->salt);
+        k->salt = b;
+        k->salt_size = l;
+
+        return 0;
+}
+
+static int dispatch_fido2_hmac_salt(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        UserRecord *h = userdata;
+        JsonVariant *e;
+        int r;
+
+        if (!json_variant_is_array(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of objects.", strna(name));
+
+        JSON_VARIANT_ARRAY_FOREACH(e, variant) {
+                Fido2HmacSalt *array, *k;
+
+                static const JsonDispatch fido2_hmac_salt_dispatch_table[] = {
+                        { "credential",     JSON_VARIANT_STRING, dispatch_fido2_hmac_credential, offsetof(Fido2HmacSalt, credential),      JSON_MANDATORY },
+                        { "salt",           JSON_VARIANT_STRING, dispatch_fido2_hmac_salt_value, 0,                                        JSON_MANDATORY },
+                        { "hashedPassword", JSON_VARIANT_STRING, json_dispatch_string,           offsetof(Fido2HmacSalt, hashed_password), JSON_MANDATORY },
+                        {},
+                };
+
+                if (!json_variant_is_object(e))
+                        return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not an object.");
+
+                array = reallocarray(h->fido2_hmac_salt, h->n_fido2_hmac_salt + 1, sizeof(Fido2HmacSalt));
+                if (!array)
+                        return log_oom();
+
+                h->fido2_hmac_salt = array;
+                k = h->fido2_hmac_salt + h->n_fido2_hmac_salt;
+                *k = (Fido2HmacSalt) {};
+
+                r = json_dispatch(e, fido2_hmac_salt_dispatch_table, NULL, flags, k);
+                if (r < 0) {
+                        fido2_hmac_salt_done(k);
+                        return r;
+                }
+
+                h->n_fido2_hmac_salt++;
+        }
+
+        return 0;
+}
+
 static int dispatch_privileged(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
 
         static const JsonDispatch privileged_dispatch_table[] = {
-                { "passwordHint",       JSON_VARIANT_STRING,        json_dispatch_string, offsetof(UserRecord, password_hint),        0         },
-                { "hashedPassword",     _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv,   offsetof(UserRecord, hashed_password),      JSON_SAFE },
-                { "sshAuthorizedKeys",  _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv,   offsetof(UserRecord, ssh_authorized_keys),  0         },
-                { "pkcs11EncryptedKey", JSON_VARIANT_ARRAY,         dispatch_pkcs11_key,  0,                                          0         },
+                { "passwordHint",       JSON_VARIANT_STRING,        json_dispatch_string,     offsetof(UserRecord, password_hint),        0         },
+                { "hashedPassword",     _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv,       offsetof(UserRecord, hashed_password),      JSON_SAFE },
+                { "sshAuthorizedKeys",  _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv,       offsetof(UserRecord, ssh_authorized_keys),  0         },
+                { "pkcs11EncryptedKey", JSON_VARIANT_ARRAY,         dispatch_pkcs11_key,      0,                                          0         },
+                { "fido2HmacSalt",      JSON_VARIANT_ARRAY,         dispatch_fido2_hmac_salt, 0,                                          0         },
                 {},
         };
 
@@ -906,66 +1058,67 @@ int per_machine_hostname_match(JsonVariant *hns, JsonDispatchFlags flags) {
 static int dispatch_per_machine(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
 
         static const JsonDispatch per_machine_dispatch_table[] = {
-                { "matchMachineId",             _JSON_VARIANT_TYPE_INVALID, NULL,                              0,                                                   0         },
-                { "matchHostname",              _JSON_VARIANT_TYPE_INVALID, NULL,                              0,                                                   0         },
-                { "iconName",                   JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, icon_name),                     JSON_SAFE },
-                { "location",                   JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, location),                      0         },
-                { "shell",                      JSON_VARIANT_STRING,        json_dispatch_filename_or_path,    offsetof(UserRecord, shell),                         0         },
-                { "umask",                      JSON_VARIANT_UNSIGNED,      json_dispatch_umask,               offsetof(UserRecord, umask),                         0         },
-                { "environment",                JSON_VARIANT_ARRAY,         json_dispatch_environment,         offsetof(UserRecord, environment),                   0         },
-                { "timeZone",                   JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, time_zone),                     JSON_SAFE },
-                { "preferredLanguage",          JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, preferred_language),            JSON_SAFE },
-                { "niceLevel",                  _JSON_VARIANT_TYPE_INVALID, json_dispatch_nice,                offsetof(UserRecord, nice_level),                    0         },
-                { "resourceLimits",             _JSON_VARIANT_TYPE_INVALID, json_dispatch_rlimits,             offsetof(UserRecord, rlimits),                       0         },
-                { "locked",                     JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,            offsetof(UserRecord, locked),                        0         },
-                { "notBeforeUSec",              JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, not_before_usec),               0         },
-                { "notAfterUSec",               JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, not_after_usec),                0         },
-                { "storage",                    JSON_VARIANT_STRING,        json_dispatch_storage,             offsetof(UserRecord, storage),                       0         },
-                { "diskSize",                   JSON_VARIANT_UNSIGNED,      json_dispatch_disk_size,           offsetof(UserRecord, disk_size),                     0         },
-                { "diskSizeRelative",           JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, disk_size_relative),            0         },
-                { "skeletonDirectory",          JSON_VARIANT_STRING,        json_dispatch_path,                offsetof(UserRecord, skeleton_directory),            0         },
-                { "accessMode",                 JSON_VARIANT_UNSIGNED,      json_dispatch_access_mode,         offsetof(UserRecord, access_mode),                   0         },
-                { "tasksMax",                   JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max, offsetof(UserRecord, tasks_max),                     0         },
-                { "memoryHigh",                 JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_high),                   0         },
-                { "memoryMax",                  JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_max),                    0         },
-                { "cpuWeight",                  JSON_VARIANT_UNSIGNED,      json_dispatch_weight,              offsetof(UserRecord, cpu_weight),                    0         },
-                { "ioWeight",                   JSON_VARIANT_UNSIGNED,      json_dispatch_weight,              offsetof(UserRecord, io_weight),                     0         },
-                { "mountNoDevices",             JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,             offsetof(UserRecord, nodev),                         0         },
-                { "mountNoSuid",                JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,             offsetof(UserRecord, nosuid),                        0         },
-                { "mountNoExecute",             JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,             offsetof(UserRecord, noexec),                        0         },
-                { "cifsDomain",                 JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, cifs_domain),                   JSON_SAFE },
-                { "cifsUserName",               JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, cifs_user_name),                JSON_SAFE },
-                { "cifsService",                JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, cifs_service),                  JSON_SAFE },
-                { "imagePath",                  JSON_VARIANT_STRING,        json_dispatch_path,                offsetof(UserRecord, image_path),                    0         },
-                { "uid",                        JSON_VARIANT_UNSIGNED,      json_dispatch_uid_gid,             offsetof(UserRecord, uid),                           0         },
-                { "gid",                        JSON_VARIANT_UNSIGNED,      json_dispatch_uid_gid,             offsetof(UserRecord, gid),                           0         },
-                { "memberOf",                   JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,     offsetof(UserRecord, member_of),                     JSON_RELAX},
-                { "fileSystemType",             JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, file_system_type),              JSON_SAFE },
-                { "partitionUuid",              JSON_VARIANT_STRING,        json_dispatch_id128,               offsetof(UserRecord, partition_uuid),                0         },
-                { "luksUuid",                   JSON_VARIANT_STRING,        json_dispatch_id128,               offsetof(UserRecord, luks_uuid),                     0         },
-                { "fileSystemUuid",             JSON_VARIANT_STRING,        json_dispatch_id128,               offsetof(UserRecord, file_system_uuid),              0         },
-                { "luksDiscard",                _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate,            offsetof(UserRecord, luks_discard),                  0,        },
-                { "luksOfflineDiscard",         _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate,            offsetof(UserRecord, luks_offline_discard),          0,        },
-                { "luksCipher",                 JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, luks_cipher),                   JSON_SAFE },
-                { "luksCipherMode",             JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, luks_cipher_mode),              JSON_SAFE },
-                { "luksVolumeKeySize",          JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, luks_volume_key_size),          0         },
-                { "luksPbkdfHashAlgorithm",     JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, luks_pbkdf_hash_algorithm),     JSON_SAFE },
-                { "luksPbkdfType",              JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, luks_pbkdf_type),               JSON_SAFE },
-                { "luksPbkdfTimeCostUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_time_cost_usec),     0         },
-                { "luksPbkdfMemoryCost",        JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_memory_cost),        0         },
-                { "luksPbkdfParallelThreads",   JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_parallel_threads),   0         },
-                { "rateLimitIntervalUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, ratelimit_interval_usec),       0         },
-                { "rateLimitBurst",             JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, ratelimit_burst),               0         },
-                { "enforcePasswordPolicy",      JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,            offsetof(UserRecord, enforce_password_policy),       0         },
-                { "autoLogin",                  JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,            offsetof(UserRecord, auto_login),                    0         },
-                { "stopDelayUSec",              JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, stop_delay_usec),               0         },
-                { "killProcesses",              JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,            offsetof(UserRecord, kill_processes),                0         },
-                { "passwordChangeMinUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, password_change_min_usec),      0         },
-                { "passwordChangeMaxUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, password_change_max_usec),      0         },
-                { "passwordChangeWarnUSec",     JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, password_change_warn_usec),     0         },
-                { "passwordChangeInactiveUSec", JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, password_change_inactive_usec), 0         },
-                { "passwordChangeNow",          JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,            offsetof(UserRecord, password_change_now),           0         },
-                { "pkcs11TokenUri",             JSON_VARIANT_ARRAY,         dispatch_pkcs11_uri_array,         offsetof(UserRecord, pkcs11_token_uri),              0         },
+                { "matchMachineId",             _JSON_VARIANT_TYPE_INVALID, NULL,                                 0,                                                   0         },
+                { "matchHostname",              _JSON_VARIANT_TYPE_INVALID, NULL,                                 0,                                                   0         },
+                { "iconName",                   JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, icon_name),                     JSON_SAFE },
+                { "location",                   JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, location),                      0         },
+                { "shell",                      JSON_VARIANT_STRING,        json_dispatch_filename_or_path,       offsetof(UserRecord, shell),                         0         },
+                { "umask",                      JSON_VARIANT_UNSIGNED,      json_dispatch_umask,                  offsetof(UserRecord, umask),                         0         },
+                { "environment",                JSON_VARIANT_ARRAY,         json_dispatch_environment,            offsetof(UserRecord, environment),                   0         },
+                { "timeZone",                   JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, time_zone),                     JSON_SAFE },
+                { "preferredLanguage",          JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, preferred_language),            JSON_SAFE },
+                { "niceLevel",                  _JSON_VARIANT_TYPE_INVALID, json_dispatch_nice,                   offsetof(UserRecord, nice_level),                    0         },
+                { "resourceLimits",             _JSON_VARIANT_TYPE_INVALID, json_dispatch_rlimits,                offsetof(UserRecord, rlimits),                       0         },
+                { "locked",                     JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,               offsetof(UserRecord, locked),                        0         },
+                { "notBeforeUSec",              JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, not_before_usec),               0         },
+                { "notAfterUSec",               JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, not_after_usec),                0         },
+                { "storage",                    JSON_VARIANT_STRING,        json_dispatch_storage,                offsetof(UserRecord, storage),                       0         },
+                { "diskSize",                   JSON_VARIANT_UNSIGNED,      json_dispatch_disk_size,              offsetof(UserRecord, disk_size),                     0         },
+                { "diskSizeRelative",           JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, disk_size_relative),            0         },
+                { "skeletonDirectory",          JSON_VARIANT_STRING,        json_dispatch_path,                   offsetof(UserRecord, skeleton_directory),            0         },
+                { "accessMode",                 JSON_VARIANT_UNSIGNED,      json_dispatch_access_mode,            offsetof(UserRecord, access_mode),                   0         },
+                { "tasksMax",                   JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, tasks_max),                     0         },
+                { "memoryHigh",                 JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, memory_high),                   0         },
+                { "memoryMax",                  JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, memory_max),                    0         },
+                { "cpuWeight",                  JSON_VARIANT_UNSIGNED,      json_dispatch_weight,                 offsetof(UserRecord, cpu_weight),                    0         },
+                { "ioWeight",                   JSON_VARIANT_UNSIGNED,      json_dispatch_weight,                 offsetof(UserRecord, io_weight),                     0         },
+                { "mountNoDevices",             JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,                offsetof(UserRecord, nodev),                         0         },
+                { "mountNoSuid",                JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,                offsetof(UserRecord, nosuid),                        0         },
+                { "mountNoExecute",             JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,                offsetof(UserRecord, noexec),                        0         },
+                { "cifsDomain",                 JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, cifs_domain),                   JSON_SAFE },
+                { "cifsUserName",               JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, cifs_user_name),                JSON_SAFE },
+                { "cifsService",                JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, cifs_service),                  JSON_SAFE },
+                { "imagePath",                  JSON_VARIANT_STRING,        json_dispatch_path,                   offsetof(UserRecord, image_path),                    0         },
+                { "uid",                        JSON_VARIANT_UNSIGNED,      json_dispatch_uid_gid,                offsetof(UserRecord, uid),                           0         },
+                { "gid",                        JSON_VARIANT_UNSIGNED,      json_dispatch_uid_gid,                offsetof(UserRecord, gid),                           0         },
+                { "memberOf",                   JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,        offsetof(UserRecord, member_of),                     JSON_RELAX},
+                { "fileSystemType",             JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, file_system_type),              JSON_SAFE },
+                { "partitionUuid",              JSON_VARIANT_STRING,        json_dispatch_id128,                  offsetof(UserRecord, partition_uuid),                0         },
+                { "luksUuid",                   JSON_VARIANT_STRING,        json_dispatch_id128,                  offsetof(UserRecord, luks_uuid),                     0         },
+                { "fileSystemUuid",             JSON_VARIANT_STRING,        json_dispatch_id128,                  offsetof(UserRecord, file_system_uuid),              0         },
+                { "luksDiscard",                _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate,               offsetof(UserRecord, luks_discard),                  0,        },
+                { "luksOfflineDiscard",         _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate,               offsetof(UserRecord, luks_offline_discard),          0,        },
+                { "luksCipher",                 JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, luks_cipher),                   JSON_SAFE },
+                { "luksCipherMode",             JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, luks_cipher_mode),              JSON_SAFE },
+                { "luksVolumeKeySize",          JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_volume_key_size),          0         },
+                { "luksPbkdfHashAlgorithm",     JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, luks_pbkdf_hash_algorithm),     JSON_SAFE },
+                { "luksPbkdfType",              JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, luks_pbkdf_type),               JSON_SAFE },
+                { "luksPbkdfTimeCostUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_pbkdf_time_cost_usec),     0         },
+                { "luksPbkdfMemoryCost",        JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_pbkdf_memory_cost),        0         },
+                { "luksPbkdfParallelThreads",   JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_pbkdf_parallel_threads),   0         },
+                { "rateLimitIntervalUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, ratelimit_interval_usec),       0         },
+                { "rateLimitBurst",             JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, ratelimit_burst),               0         },
+                { "enforcePasswordPolicy",      JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,               offsetof(UserRecord, enforce_password_policy),       0         },
+                { "autoLogin",                  JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,               offsetof(UserRecord, auto_login),                    0         },
+                { "stopDelayUSec",              JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, stop_delay_usec),               0         },
+                { "killProcesses",              JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,               offsetof(UserRecord, kill_processes),                0         },
+                { "passwordChangeMinUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, password_change_min_usec),      0         },
+                { "passwordChangeMaxUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, password_change_max_usec),      0         },
+                { "passwordChangeWarnUSec",     JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, password_change_warn_usec),     0         },
+                { "passwordChangeInactiveUSec", JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, password_change_inactive_usec), 0         },
+                { "passwordChangeNow",          JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,               offsetof(UserRecord, password_change_now),           0         },
+                { "pkcs11TokenUri",             JSON_VARIANT_ARRAY,         dispatch_pkcs11_uri_array,            offsetof(UserRecord, pkcs11_token_uri),              0         },
+                { "fido2HmacCredential",        JSON_VARIANT_ARRAY,         dispatch_fido2_hmac_credential_array, 0,                                                   0         },
                 {},
         };
 
@@ -1247,84 +1400,85 @@ int user_group_record_mangle(
 int user_record_load(UserRecord *h, JsonVariant *v, UserRecordLoadFlags load_flags) {
 
         static const JsonDispatch user_dispatch_table[] = {
-                { "userName",                   JSON_VARIANT_STRING,        json_dispatch_user_group_name,     offsetof(UserRecord, user_name),                     JSON_RELAX},
-                { "realm",                      JSON_VARIANT_STRING,        json_dispatch_realm,               offsetof(UserRecord, realm),                         0         },
-                { "realName",                   JSON_VARIANT_STRING,        json_dispatch_gecos,               offsetof(UserRecord, real_name),                     0         },
-                { "emailAddress",               JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, email_address),                 JSON_SAFE },
-                { "iconName",                   JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, icon_name),                     JSON_SAFE },
-                { "location",                   JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, location),                      0         },
-                { "disposition",                JSON_VARIANT_STRING,        json_dispatch_user_disposition,    offsetof(UserRecord, disposition),                   0         },
-                { "lastChangeUSec",             JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, last_change_usec),              0         },
-                { "lastPasswordChangeUSec",     JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, last_password_change_usec),     0         },
-                { "shell",                      JSON_VARIANT_STRING,        json_dispatch_filename_or_path,    offsetof(UserRecord, shell),                         0         },
-                { "umask",                      JSON_VARIANT_UNSIGNED,      json_dispatch_umask,               offsetof(UserRecord, umask),                         0         },
-                { "environment",                JSON_VARIANT_ARRAY,         json_dispatch_environment,         offsetof(UserRecord, environment),                   0         },
-                { "timeZone",                   JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, time_zone),                     JSON_SAFE },
-                { "preferredLanguage",          JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, preferred_language),            JSON_SAFE },
-                { "niceLevel",                  _JSON_VARIANT_TYPE_INVALID, json_dispatch_nice,                offsetof(UserRecord, nice_level),                    0         },
-                { "resourceLimits",             _JSON_VARIANT_TYPE_INVALID, json_dispatch_rlimits,             offsetof(UserRecord, rlimits),                       0         },
-                { "locked",                     JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,            offsetof(UserRecord, locked),                        0         },
-                { "notBeforeUSec",              JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, not_before_usec),               0         },
-                { "notAfterUSec",               JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, not_after_usec),                0         },
-                { "storage",                    JSON_VARIANT_STRING,        json_dispatch_storage,             offsetof(UserRecord, storage),                       0         },
-                { "diskSize",                   JSON_VARIANT_UNSIGNED,      json_dispatch_disk_size,           offsetof(UserRecord, disk_size),                     0         },
-                { "diskSizeRelative",           JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, disk_size_relative),            0         },
-                { "skeletonDirectory",          JSON_VARIANT_STRING,        json_dispatch_path,                offsetof(UserRecord, skeleton_directory),            0         },
-                { "accessMode",                 JSON_VARIANT_UNSIGNED,      json_dispatch_access_mode,         offsetof(UserRecord, access_mode),                   0         },
-                { "tasksMax",                   JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max, offsetof(UserRecord, tasks_max),                     0         },
-                { "memoryHigh",                 JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_high),                   0         },
-                { "memoryMax",                  JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_max),                    0         },
-                { "cpuWeight",                  JSON_VARIANT_UNSIGNED,      json_dispatch_weight,              offsetof(UserRecord, cpu_weight),                    0         },
-                { "ioWeight",                   JSON_VARIANT_UNSIGNED,      json_dispatch_weight,              offsetof(UserRecord, io_weight),                     0         },
-                { "mountNoDevices",             JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,             offsetof(UserRecord, nodev),                         0         },
-                { "mountNoSuid",                JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,             offsetof(UserRecord, nosuid),                        0         },
-                { "mountNoExecute",             JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,             offsetof(UserRecord, noexec),                        0         },
-                { "cifsDomain",                 JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, cifs_domain),                   JSON_SAFE },
-                { "cifsUserName",               JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, cifs_user_name),                JSON_SAFE },
-                { "cifsService",                JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, cifs_service),                  JSON_SAFE },
-                { "imagePath",                  JSON_VARIANT_STRING,        json_dispatch_path,                offsetof(UserRecord, image_path),                    0         },
-                { "homeDirectory",              JSON_VARIANT_STRING,        json_dispatch_home_directory,      offsetof(UserRecord, home_directory),                0         },
-                { "uid",                        JSON_VARIANT_UNSIGNED,      json_dispatch_uid_gid,             offsetof(UserRecord, uid),                           0         },
-                { "gid",                        JSON_VARIANT_UNSIGNED,      json_dispatch_uid_gid,             offsetof(UserRecord, gid),                           0         },
-                { "memberOf",                   JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,     offsetof(UserRecord, member_of),                     JSON_RELAX},
-                { "fileSystemType",             JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, file_system_type),              JSON_SAFE },
-                { "partitionUuid",              JSON_VARIANT_STRING,        json_dispatch_id128,               offsetof(UserRecord, partition_uuid),                0         },
-                { "luksUuid",                   JSON_VARIANT_STRING,        json_dispatch_id128,               offsetof(UserRecord, luks_uuid),                     0         },
-                { "fileSystemUuid",             JSON_VARIANT_STRING,        json_dispatch_id128,               offsetof(UserRecord, file_system_uuid),              0         },
-                { "luksDiscard",                _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate,            offsetof(UserRecord, luks_discard),                  0         },
+                { "userName",                   JSON_VARIANT_STRING,        json_dispatch_user_group_name,        offsetof(UserRecord, user_name),                     JSON_RELAX},
+                { "realm",                      JSON_VARIANT_STRING,        json_dispatch_realm,                  offsetof(UserRecord, realm),                         0         },
+                { "realName",                   JSON_VARIANT_STRING,        json_dispatch_gecos,                  offsetof(UserRecord, real_name),                     0         },
+                { "emailAddress",               JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, email_address),                 JSON_SAFE },
+                { "iconName",                   JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, icon_name),                     JSON_SAFE },
+                { "location",                   JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, location),                      0         },
+                { "disposition",                JSON_VARIANT_STRING,        json_dispatch_user_disposition,       offsetof(UserRecord, disposition),                   0         },
+                { "lastChangeUSec",             JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, last_change_usec),              0         },
+                { "lastPasswordChangeUSec",     JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, last_password_change_usec),     0         },
+                { "shell",                      JSON_VARIANT_STRING,        json_dispatch_filename_or_path,       offsetof(UserRecord, shell),                         0         },
+                { "umask",                      JSON_VARIANT_UNSIGNED,      json_dispatch_umask,                  offsetof(UserRecord, umask),                         0         },
+                { "environment",                JSON_VARIANT_ARRAY,         json_dispatch_environment,            offsetof(UserRecord, environment),                   0         },
+                { "timeZone",                   JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, time_zone),                     JSON_SAFE },
+                { "preferredLanguage",          JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, preferred_language),            JSON_SAFE },
+                { "niceLevel",                  _JSON_VARIANT_TYPE_INVALID, json_dispatch_nice,                   offsetof(UserRecord, nice_level),                    0         },
+                { "resourceLimits",             _JSON_VARIANT_TYPE_INVALID, json_dispatch_rlimits,                offsetof(UserRecord, rlimits),                       0         },
+                { "locked",                     JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,               offsetof(UserRecord, locked),                        0         },
+                { "notBeforeUSec",              JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, not_before_usec),               0         },
+                { "notAfterUSec",               JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, not_after_usec),                0         },
+                { "storage",                    JSON_VARIANT_STRING,        json_dispatch_storage,                offsetof(UserRecord, storage),                       0         },
+                { "diskSize",                   JSON_VARIANT_UNSIGNED,      json_dispatch_disk_size,              offsetof(UserRecord, disk_size),                     0         },
+                { "diskSizeRelative",           JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, disk_size_relative),            0         },
+                { "skeletonDirectory",          JSON_VARIANT_STRING,        json_dispatch_path,                   offsetof(UserRecord, skeleton_directory),            0         },
+                { "accessMode",                 JSON_VARIANT_UNSIGNED,      json_dispatch_access_mode,            offsetof(UserRecord, access_mode),                   0         },
+                { "tasksMax",                   JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, tasks_max),                     0         },
+                { "memoryHigh",                 JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, memory_high),                   0         },
+                { "memoryMax",                  JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, memory_max),                    0         },
+                { "cpuWeight",                  JSON_VARIANT_UNSIGNED,      json_dispatch_weight,                 offsetof(UserRecord, cpu_weight),                    0         },
+                { "ioWeight",                   JSON_VARIANT_UNSIGNED,      json_dispatch_weight,                 offsetof(UserRecord, io_weight),                     0         },
+                { "mountNoDevices",             JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,                offsetof(UserRecord, nodev),                         0         },
+                { "mountNoSuid",                JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,                offsetof(UserRecord, nosuid),                        0         },
+                { "mountNoExecute",             JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,                offsetof(UserRecord, noexec),                        0         },
+                { "cifsDomain",                 JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, cifs_domain),                   JSON_SAFE },
+                { "cifsUserName",               JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, cifs_user_name),                JSON_SAFE },
+                { "cifsService",                JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, cifs_service),                  JSON_SAFE },
+                { "imagePath",                  JSON_VARIANT_STRING,        json_dispatch_path,                   offsetof(UserRecord, image_path),                    0         },
+                { "homeDirectory",              JSON_VARIANT_STRING,        json_dispatch_home_directory,         offsetof(UserRecord, home_directory),                0         },
+                { "uid",                        JSON_VARIANT_UNSIGNED,      json_dispatch_uid_gid,                offsetof(UserRecord, uid),                           0         },
+                { "gid",                        JSON_VARIANT_UNSIGNED,      json_dispatch_uid_gid,                offsetof(UserRecord, gid),                           0         },
+                { "memberOf",                   JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,        offsetof(UserRecord, member_of),                     JSON_RELAX},
+                { "fileSystemType",             JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, file_system_type),              JSON_SAFE },
+                { "partitionUuid",              JSON_VARIANT_STRING,        json_dispatch_id128,                  offsetof(UserRecord, partition_uuid),                0         },
+                { "luksUuid",                   JSON_VARIANT_STRING,        json_dispatch_id128,                  offsetof(UserRecord, luks_uuid),                     0         },
+                { "fileSystemUuid",             JSON_VARIANT_STRING,        json_dispatch_id128,                  offsetof(UserRecord, file_system_uuid),              0         },
+                { "luksDiscard",                _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate,               offsetof(UserRecord, luks_discard),                  0         },
                 { "luksOfflineDiscard",         _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate,            offsetof(UserRecord, luks_offline_discard),          0         },
-                { "luksCipher",                 JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, luks_cipher),                   JSON_SAFE },
-                { "luksCipherMode",             JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, luks_cipher_mode),              JSON_SAFE },
-                { "luksVolumeKeySize",          JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, luks_volume_key_size),          0         },
-                { "luksPbkdfHashAlgorithm",     JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, luks_pbkdf_hash_algorithm),     JSON_SAFE },
-                { "luksPbkdfType",              JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, luks_pbkdf_type),               JSON_SAFE },
-                { "luksPbkdfTimeCostUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_time_cost_usec),     0         },
-                { "luksPbkdfMemoryCost",        JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_memory_cost),        0         },
-                { "luksPbkdfParallelThreads",   JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_parallel_threads),   0         },
-                { "service",                    JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, service),                       JSON_SAFE },
-                { "rateLimitIntervalUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, ratelimit_interval_usec),       0         },
-                { "rateLimitBurst",             JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, ratelimit_burst),               0         },
-                { "enforcePasswordPolicy",      JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,            offsetof(UserRecord, enforce_password_policy),       0         },
-                { "autoLogin",                  JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,            offsetof(UserRecord, auto_login),                    0         },
-                { "stopDelayUSec",              JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, stop_delay_usec),               0         },
-                { "killProcesses",              JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,            offsetof(UserRecord, kill_processes),                0         },
-                { "passwordChangeMinUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, password_change_min_usec),      0         },
-                { "passwordChangeMaxUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, password_change_max_usec),      0         },
-                { "passwordChangeWarnUSec",     JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, password_change_warn_usec),     0         },
-                { "passwordChangeInactiveUSec", JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, password_change_inactive_usec), 0         },
-                { "passwordChangeNow",          JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,            offsetof(UserRecord, password_change_now),           0         },
-                { "pkcs11TokenUri",             JSON_VARIANT_ARRAY,         dispatch_pkcs11_uri_array,         offsetof(UserRecord, pkcs11_token_uri),              0         },
-
-                { "secret",                     JSON_VARIANT_OBJECT,        dispatch_secret,                   0,                                                   0         },
-                { "privileged",                 JSON_VARIANT_OBJECT,        dispatch_privileged,               0,                                                   0         },
+                { "luksCipher",                 JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, luks_cipher),                   JSON_SAFE },
+                { "luksCipherMode",             JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, luks_cipher_mode),              JSON_SAFE },
+                { "luksVolumeKeySize",          JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_volume_key_size),          0         },
+                { "luksPbkdfHashAlgorithm",     JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, luks_pbkdf_hash_algorithm),     JSON_SAFE },
+                { "luksPbkdfType",              JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, luks_pbkdf_type),               JSON_SAFE },
+                { "luksPbkdfTimeCostUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_pbkdf_time_cost_usec),     0         },
+                { "luksPbkdfMemoryCost",        JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_pbkdf_memory_cost),        0         },
+                { "luksPbkdfParallelThreads",   JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_pbkdf_parallel_threads),   0         },
+                { "service",                    JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, service),                       JSON_SAFE },
+                { "rateLimitIntervalUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, ratelimit_interval_usec),       0         },
+                { "rateLimitBurst",             JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, ratelimit_burst),               0         },
+                { "enforcePasswordPolicy",      JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,               offsetof(UserRecord, enforce_password_policy),       0         },
+                { "autoLogin",                  JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,               offsetof(UserRecord, auto_login),                    0         },
+                { "stopDelayUSec",              JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, stop_delay_usec),               0         },
+                { "killProcesses",              JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,               offsetof(UserRecord, kill_processes),                0         },
+                { "passwordChangeMinUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, password_change_min_usec),      0         },
+                { "passwordChangeMaxUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, password_change_max_usec),      0         },
+                { "passwordChangeWarnUSec",     JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, password_change_warn_usec),     0         },
+                { "passwordChangeInactiveUSec", JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, password_change_inactive_usec), 0         },
+                { "passwordChangeNow",          JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,               offsetof(UserRecord, password_change_now),           0         },
+                { "pkcs11TokenUri",             JSON_VARIANT_ARRAY,         dispatch_pkcs11_uri_array,            offsetof(UserRecord, pkcs11_token_uri),              0         },
+                { "fido2HmacCredential",        JSON_VARIANT_ARRAY,         dispatch_fido2_hmac_credential_array, 0,                                                   0         },
+
+                { "secret",                     JSON_VARIANT_OBJECT,        dispatch_secret,                      0,                                                   0         },
+                { "privileged",                 JSON_VARIANT_OBJECT,        dispatch_privileged,                  0,                                                   0         },
 
                 /* Ignore the perMachine, binding, status stuff here, and process it later, so that it overrides whatever is set above */
-                { "perMachine",                 JSON_VARIANT_ARRAY,         NULL,                              0,                                                   0         },
-                { "binding",                    JSON_VARIANT_OBJECT,        NULL,                              0,                                                   0         },
-                { "status",                     JSON_VARIANT_OBJECT,        NULL,                              0,                                                   0         },
+                { "perMachine",                 JSON_VARIANT_ARRAY,         NULL,                                 0,                                                   0         },
+                { "binding",                    JSON_VARIANT_OBJECT,        NULL,                                 0,                                                   0         },
+                { "status",                     JSON_VARIANT_OBJECT,        NULL,                                 0,                                                   0         },
 
                 /* Ignore 'signature', we check it with explicit accessors instead */
-                { "signature",                  JSON_VARIANT_ARRAY,         NULL,                              0,                                                   0         },
+                { "signature",                  JSON_VARIANT_ARRAY,         NULL,                                 0,                                                   0         },
                 {},
         };
 
@@ -1684,6 +1838,9 @@ bool user_record_can_authenticate(UserRecord *h) {
         if (h->n_pkcs11_encrypted_key > 0)
                 return true;
 
+        if (h->n_fido2_hmac_salt > 0)
+                return true;
+
         return !strv_isempty(h->hashed_password);
 }
 
index 9fd10610d926acce14ab26edb184fcdd0c2a68ab..1bfd095d27e18c1aed317428bebc78d7818de481 100644 (file)
@@ -189,6 +189,23 @@ typedef struct Pkcs11EncryptedKey {
         char *hashed_password;
 } Pkcs11EncryptedKey;
 
+typedef struct Fido2HmacCredential {
+        void *id;
+        size_t size;
+} Fido2HmacCredential;
+
+typedef struct Fido2HmacSalt {
+        /* The FIDO2 Cridential ID to use */
+        Fido2HmacCredential credential;
+
+        /* The FIDO2 salt value */
+        void *salt;
+        size_t salt_size;
+
+        /* What to test the hashed salt value against, usually UNIX password hash here. */
+        char *hashed_password;
+} Fido2HmacSalt;
+
 typedef struct UserRecord {
         /* The following three fields are not part of the JSON record */
         unsigned n_ref;
@@ -239,7 +256,7 @@ typedef struct UserRecord {
         char **hashed_password;
         char **ssh_authorized_keys;
         char **password;
-        char **pkcs11_pin;
+        char **token_pin;
 
         char *cifs_domain;
         char *cifs_user_name;
@@ -309,6 +326,12 @@ typedef struct UserRecord {
         size_t n_pkcs11_encrypted_key;
         int pkcs11_protected_authentication_path_permitted;
 
+        Fido2HmacCredential *fido2_hmac_credential;
+        size_t n_fido2_hmac_credential;
+        Fido2HmacSalt *fido2_hmac_salt;
+        size_t n_fido2_hmac_salt;
+        int fido2_user_presence_permitted;
+
         JsonVariant *json;
 } UserRecord;
 
index 1f4d5a614c9e87256491589241c30d0809118ef3..be3559dc1037efec091ba4a1843daad3e0f0f919 100644 (file)
@@ -387,7 +387,7 @@ DEFINE_TRIVIAL_REF_UNREF_FUNC(Varlink, varlink, varlink_destroy);
 static int varlink_test_disconnect(Varlink *v) {
         assert(v);
 
-        /* Tests whether we the the connection has been terminated. We are careful to not stop processing it
+        /* Tests whether we the connection has been terminated. We are careful to not stop processing it
          * prematurely, since we want to handle half-open connections as well as possible and want to flush
          * out and read data before we close down if we can. */
 
index 45b67821456d71797c0e99a42a628cb02c52ca72..347982dd5240f7c9e310d9a4f6ed0c2259fa91ac 100644 (file)
@@ -89,7 +89,7 @@ static void test_keymaps(void) {
 
 #define dump_glyph(x) log_info(STRINGIFY(x) ": %s", special_glyph(x))
 static void dump_special_glyphs(void) {
-        assert_cc(SPECIAL_GLYPH_LOCK_AND_KEY + 1 == _SPECIAL_GLYPH_MAX);
+        assert_cc(SPECIAL_GLYPH_TOUCH + 1 == _SPECIAL_GLYPH_MAX);
 
         log_info("/* %s */", __func__);
 
@@ -116,6 +116,7 @@ static void dump_special_glyphs(void) {
         dump_glyph(SPECIAL_GLYPH_UNHAPPY_SMILEY);
         dump_glyph(SPECIAL_GLYPH_DEPRESSED_SMILEY);
         dump_glyph(SPECIAL_GLYPH_LOCK_AND_KEY);
+        dump_glyph(SPECIAL_GLYPH_TOUCH);
 }
 
 int main(int argc, char *argv[]) {
index 6e294c72d627bff0ff17657522d829de2b4e0ab8..0d524f9a5626dd13511f028159f361eddd5d5f67 100644 (file)
@@ -130,6 +130,7 @@ static void test_unit_name_from_path(void) {
         test_unit_name_from_path_one("///", ".mount", "-.mount", 0);
         test_unit_name_from_path_one("/foo/../bar", ".mount", NULL, -EINVAL);
         test_unit_name_from_path_one("/foo/./bar", ".mount", NULL, -EINVAL);
+        test_unit_name_from_path_one("/waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", ".mount", NULL, -EINVAL);
 }
 
 static void test_unit_name_from_path_instance_one(const char *pattern, const char *path, const char *suffix, const char *expected, int ret) {
@@ -159,6 +160,7 @@ static void test_unit_name_from_path_instance(void) {
         test_unit_name_from_path_instance_one("waldo", "..", ".mount", NULL, -EINVAL);
         test_unit_name_from_path_instance_one("waldo", "/foo", ".waldi", NULL, -EINVAL);
         test_unit_name_from_path_instance_one("wa--ldo", "/--", ".mount", "wa--ldo@\\x2d\\x2d.mount", 0);
+        test_unit_name_from_path_instance_one("waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "/waldo", ".mount", NULL, -EINVAL);
 }
 
 static void test_unit_name_to_path_one(const char *unit, const char *path, int ret) {
index 76dd72a59807a81abb2ec309a234b3991cadbcf5..d4a6c8f5c339029c3349c3dce31213307668dd9c 100644 (file)
@@ -410,6 +410,85 @@ static void test_system_tasks_max_scale(void) {
         assert_se(system_tasks_max_scale(UINT64_MAX/4, UINT64_MAX) == UINT64_MAX);
 }
 
+static void test_foreach_pointer(void) {
+        int a, b, c, *i;
+        size_t k = 0;
+
+        FOREACH_POINTER(i, &a, &b, &c) {
+                switch (k) {
+
+                case 0:
+                        assert_se(i == &a);
+                        break;
+
+                case 1:
+                        assert_se(i == &b);
+                        break;
+
+                case 2:
+                        assert_se(i == &c);
+                        break;
+
+                default:
+                        assert_not_reached("unexpected index");
+                        break;
+                }
+
+                k++;
+        }
+
+        assert(k == 3);
+
+        FOREACH_POINTER(i, &b) {
+                assert(k == 3);
+                assert(i == &b);
+                k = 4;
+        }
+
+        assert(k == 4);
+
+        FOREACH_POINTER(i, NULL, &c, NULL, &b, NULL, &a, NULL) {
+                switch (k) {
+
+                case 4:
+                        assert_se(i == NULL);
+                        break;
+
+                case 5:
+                        assert_se(i == &c);
+                        break;
+
+                case 6:
+                        assert_se(i == NULL);
+                        break;
+
+                case 7:
+                        assert_se(i == &b);
+                        break;
+
+                case 8:
+                        assert_se(i == NULL);
+                        break;
+
+                case 9:
+                        assert_se(i == &a);
+                        break;
+
+                case 10:
+                        assert_se(i == NULL);
+                        break;
+
+                default:
+                        assert_not_reached("unexpected index");
+                        break;
+                }
+
+                k++;
+        }
+
+        assert(k == 11);
+}
+
 int main(int argc, char *argv[]) {
         test_setup_logging(LOG_INFO);
 
@@ -428,6 +507,7 @@ int main(int argc, char *argv[]) {
         test_physical_memory_scale();
         test_system_tasks_max();
         test_system_tasks_max_scale();
+        test_foreach_pointer();
 
         return 0;
 }
index 1debdf2b314cd4ecd213002191defc7db1368088..b1209e6baf81985e7beecd8245d73a5780ecce26 100644 (file)
@@ -17,6 +17,7 @@
 #include "device-util.h"
 #include "dirent-util.h"
 #include "fd-util.h"
+#include "sort-util.h"
 #include "string-table.h"
 #include "string-util.h"
 #include "udev-util.h"
@@ -56,9 +57,36 @@ static bool skip_attribute(const char *name) {
         return string_table_lookup(skip, ELEMENTSOF(skip), name) >= 0;
 }
 
-static void print_all_attributes(sd_device *device, const char *key) {
+typedef struct SysAttr {
+        const char *name;
+        const char *value;
+} SysAttr;
+
+static int sysattr_compare(const SysAttr *a, const SysAttr *b) {
+        return strcmp(a->name, b->name);
+}
+
+static int print_all_attributes(sd_device *device, bool is_parent) {
+        _cleanup_free_ SysAttr *sysattrs = NULL;
+        size_t n_items = 0, n_allocated = 0;
         const char *name, *value;
 
+        value = NULL;
+        (void) sd_device_get_devpath(device, &value);
+        printf("  looking at %sdevice '%s':\n", is_parent ? "parent " : "", strempty(value));
+
+        value = NULL;
+        (void) sd_device_get_sysname(device, &value);
+        printf("    %s==\"%s\"\n", is_parent ? "KERNELS" : "KERNEL", strempty(value));
+
+        value = NULL;
+        (void) sd_device_get_subsystem(device, &value);
+        printf("    %s==\"%s\"\n", is_parent ? "SUBSYSTEMS" : "SUBSYSTEM", strempty(value));
+
+        value = NULL;
+        (void) sd_device_get_driver(device, &value);
+        printf("    %s==\"%s\"\n", is_parent ? "DRIVERS" : "DRIVER", strempty(value));
+
         FOREACH_DEVICE_SYSATTR(device, name) {
                 size_t len;
 
@@ -79,14 +107,29 @@ static void print_all_attributes(sd_device *device, const char *key) {
                 if (len > 0)
                         continue;
 
-                printf("    %s{%s}==\"%s\"\n", key, name, value);
+                if (!GREEDY_REALLOC(sysattrs, n_allocated, n_items + 1))
+                        return log_oom();
+
+                sysattrs[n_items] = (SysAttr) {
+                        .name = name,
+                        .value = value,
+                };
+                n_items++;
         }
+
+        typesafe_qsort(sysattrs, n_items, sysattr_compare);
+
+        for (size_t i = 0; i < n_items; i++)
+                printf("    %s{%s}==\"%s\"\n", is_parent ? "ATTRS" : "ATTR", sysattrs[i].name, sysattrs[i].value);
+
         puts("");
+
+        return 0;
 }
 
 static int print_device_chain(sd_device *device) {
         sd_device *child, *parent;
-        const char *str;
+        int r;
 
         printf("\n"
                "Udevadm info starts with the device specified by the devpath and then\n"
@@ -96,30 +139,14 @@ static int print_device_chain(sd_device *device) {
                "and the attributes from one single parent device.\n"
                "\n");
 
-        (void) sd_device_get_devpath(device, &str);
-        printf("  looking at device '%s':\n", str);
-        (void) sd_device_get_sysname(device, &str);
-        printf("    KERNEL==\"%s\"\n", str);
-        if (sd_device_get_subsystem(device, &str) < 0)
-                str = "";
-        printf("    SUBSYSTEM==\"%s\"\n", str);
-        if (sd_device_get_driver(device, &str) < 0)
-                str = "";
-        printf("    DRIVER==\"%s\"\n", str);
-        print_all_attributes(device, "ATTR");
+        r = print_all_attributes(device, false);
+        if (r < 0)
+                return r;
 
         for (child = device; sd_device_get_parent(child, &parent) >= 0; child = parent) {
-                (void) sd_device_get_devpath(parent, &str);
-                printf("  looking at parent device '%s':\n", str);
-                (void) sd_device_get_sysname(parent, &str);
-                printf("    KERNELS==\"%s\"\n", str);
-                if (sd_device_get_subsystem(parent, &str) < 0)
-                        str = "";
-                printf("    SUBSYSTEMS==\"%s\"\n", str);
-                if (sd_device_get_driver(parent, &str) < 0)
-                        str = "";
-                printf("    DRIVERS==\"%s\"\n", str);
-                print_all_attributes(parent, "ATTRS");
+                r = print_all_attributes(parent, true);
+                if (r < 0)
+                        return r;
         }
 
         return 0;
index 648eacd02341ca122177242ae8bfe333bd1f3469..382a1cc9e4a6235702e868d5b566d2e2489411f3 100644 (file)
@@ -85,7 +85,7 @@ static int show_user(UserRecord *ur, Table *table) {
                                 TABLE_STRING, user_record_shell(ur),
                                 TABLE_INT, (int) user_record_disposition(ur));
                 if (r < 0)
-                        return log_error_errno(r, "Failed to add row to table: %m");
+                        return table_log_add_error(r);
 
                 break;
 
@@ -234,7 +234,7 @@ static int show_group(GroupRecord *gr, Table *table) {
                                 TABLE_GID, gr->gid,
                                 TABLE_INT, (int) group_record_disposition(gr));
                 if (r < 0)
-                        return log_error_errno(r, "Failed to add row to table: %m");
+                        return table_log_add_error(r);
 
                 break;
 
@@ -377,7 +377,7 @@ static int show_membership(const char *user, const char *group, Table *table) {
                                 TABLE_STRING, user,
                                 TABLE_STRING, group);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to add row to table: %m");
+                        return table_log_add_error(r);
 
                 break;
 
@@ -521,7 +521,7 @@ static int display_services(int argc, char *argv[], void *userdata) {
                                    TABLE_STRING, no ?: "yes",
                                    TABLE_SET_COLOR, no ? ansi_highlight_red() : ansi_highlight_green());
                 if (r < 0)
-                        return log_error_errno(r, "Failed to add table row: %m");
+                        return table_log_add_error(r);
         }
 
         if (table_get_rows(t) <= 0) {
index 703da475b6ddf54bb62b8b412c0136d87ace5016..1754a1cd824a83c91b2387414d5f36da314df271 100644 (file)
@@ -38,6 +38,16 @@ MTUBytes=
 Multicast=
 MACAddress=
 Group=
+[SR-IOV]
+VirtualFunction=
+MACSpoofCheck=
+VLANId=
+VLANProtocol=
+QualityOfService=
+QueryReceiveSideScaling=
+Trust=
+LinkState=
+MACAddress=
 [BridgeFDB]
 VLANId=
 MACAddress=
@@ -322,7 +332,9 @@ PacketLimit=
 Parent=
 Handle=
 Rate=
+BurstBytes=
 Burst=
+LimitBytes=
 LimitSize=
 MTUBytes=
 MPUBytes=
@@ -336,8 +348,10 @@ PerturbPeriodSec=
 Parent=
 Handle=
 PacketLimit=
+MemoryLimitBytes=
 MemoryLimit=
 Flows=
+QuantumBytes=
 Quantum=
 TargetSec=
 IntervalSec=
@@ -348,7 +362,9 @@ Parent=
 Handle=
 PacketLimit=
 FlowLimit=
+QuantumBytes=
 Quantum=
+InitialQuantumBytes=
 InitialQuantum=
 MaximumRate=
 Buckets=
@@ -367,7 +383,7 @@ ECN=
 Parent=
 Handle=
 Bandwidth=
-Overhead=
+OverheadBytes=
 [TrafficControlQueueingDiscipline]
 Parent=
 NetworkEmulatorDelaySec=
@@ -398,7 +414,7 @@ CeilBufferBytes=
 [BFIFO]
 Parent=
 Handle=
-LimitSize=
+LimitBytes=
 [PFIFO]
 Parent=
 Handle=
@@ -431,14 +447,14 @@ Handle=
 Parent=
 ClassId=
 Weight=
-MaxPacketSize=
+MaxPacketBytes=
 [DeficitRoundRobinScheduler]
 Parent=
 Handle=
 [DeficitRoundRobinSchedulerClass]
 Parent=
 ClassId=
-Quantum=
+QuantumBytes=
 [EnhancedTransmissionSelection]
 Parent=
 Handle=
diff --git a/test/fuzz/fuzz-network-parser/oss-fuzz-23895 b/test/fuzz/fuzz-network-parser/oss-fuzz-23895
new file mode 100644 (file)
index 0000000..a86361d
Binary files /dev/null and b/test/fuzz/fuzz-network-parser/oss-fuzz-23895 differ
index b713245dbcc05a474a49cdfa60f3b6029b3229f3..a1b00f258ffee24aabff1191027fd50e233e84e1 100644 (file)
@@ -8,5 +8,5 @@ Address=10.1.2.3/16
 [CAKE]
 Parent=root
 Handle=3a
-Overhead=128
+OverheadBytes=128
 Bandwidth=500M
index f18e2f76d95972c8ed0adcf6cd5819b5f36c5a09..fd2520db505b08b8d3ec8ce8b25fced4ed344d77 100644 (file)
@@ -58,8 +58,8 @@ Parent=2:32
 Handle=0032
 PacketLimit=1000
 FlowLimit=200
-Quantum=1500
-InitialQuantum=13000
+QuantumBytes=1500
+InitialQuantumBytes=13000
 MaximumRate=1M
 Buckets=512
 OrphanMask=511
@@ -93,11 +93,11 @@ CeilRate=0.5M
 Parent=2:34
 Handle=0034
 PacketLimit=20480
-MemoryLimit=64M
+MemoryLimitBytes=64M
 Flows=2048
 TargetSec=10ms
 IntervalSec=200ms
-Quantum=1400
+QuantumBytes=1400
 ECN=yes
 CEThresholdSec=100ms
 
@@ -112,10 +112,10 @@ CeilRate=0.5M
 Parent=2:35
 Handle=0035
 Rate=1G
-Burst=5K
+BurstBytes=5000
 LatencySec=70msec
 PeakRate=100G
-MTUBytes=1M
+MTUBytes=1000000
 
 [HierarchyTokenBucketClass]
 Parent=root
@@ -177,7 +177,7 @@ CeilRate=0.5M
 [BFIFO]
 Parent=2:3a
 Handle=003a
-LimitSize=1M
+LimitBytes=1000000
 
 [HierarchyTokenBucketClass]
 Parent=root
index 4632740061d60474d5bf8ebe6b12cd14eeed91be..dff8b0978d73324945ca98720258554c0450a9fe 100644 (file)
@@ -12,4 +12,4 @@ Handle=0002
 [DeficitRoundRobinSchedulerClass]
 Parent=root
 ClassId=0002:0030
-Quantum=2000
+QuantumBytes=2000
index c94fc9e4cbaea5bf7718334a957b56d4b0a8deab..3a24415c1295dbd7421771970de11bc3fe5992e1 100644 (file)
@@ -13,10 +13,10 @@ Handle=0002
 Parent=root
 ClassId=0002:0030
 Weight=2
-MaxPacketSize=16000
+MaxPacketBytes=16000
 
 [QuickFairQueueingClass]
 Parent=root
 ClassId=0002:0031
 Weight=10
-MaxPacketSize=8000
+MaxPacketBytes=8000
diff --git a/test/test-network/conf/25-sriov.network b/test/test-network/conf/25-sriov.network
new file mode 100644 (file)
index 0000000..c962c3d
--- /dev/null
@@ -0,0 +1,37 @@
+[Match]
+Name=eni99np1
+
+[Network]
+Address=192.168.100.100/24
+
+[SR-IOV]
+VirtualFunction=0
+VLANId=5
+VLANProtocol=802.1ad
+QualityOfService=1
+MACSpoofCheck=yes
+QueryReceiveSideScaling=yes
+Trust=yes
+LinkState=yes
+MACAddress=00:11:22:33:44:55
+
+[SR-IOV]
+VirtualFunction=1
+VLANId=6
+VLANProtocol=802.1Q
+QualityOfService=2
+MACSpoofCheck=no
+QueryReceiveSideScaling=no
+Trust=no
+LinkState=no
+MACAddress=00:11:22:33:44:56
+
+[SR-IOV]
+VirtualFunction=2
+VLANId=7
+QualityOfService=3
+MACSpoofCheck=no
+QueryReceiveSideScaling=no
+Trust=no
+LinkState=auto
+MACAddress=00:11:22:33:44:57
index c48163b055a5604b6d4051a5835f8e7825849802..caf077c8365958ed3cccc9ea09eb89a223bde107 100755 (executable)
@@ -158,6 +158,33 @@ def expectedFailureIfAlternativeNameIsNotAvailable():
 
     return f
 
+def expectedFailureIfNetdevsimWithSRIOVIsNotAvailable():
+    def f(func):
+        call('rmmod netdevsim', stderr=subprocess.DEVNULL)
+        rc = call('modprobe netdevsim', stderr=subprocess.DEVNULL)
+        if rc != 0:
+            return unittest.expectedFailure(func)
+
+        try:
+            with open('/sys/bus/netdevsim/new_device', mode='w') as f:
+                f.write('99 1')
+        except Exception as error:
+            return unittest.expectedFailure(func)
+
+        call('udevadm settle')
+        call('udevadm info -w10s /sys/devices/netdevsim99/net/eni99np1', stderr=subprocess.DEVNULL)
+        try:
+            with open('/sys/class/net/eni99np1/device/sriov_numvfs', mode='w') as f:
+                f.write('3')
+        except Exception as error:
+            call('rmmod netdevsim', stderr=subprocess.DEVNULL)
+            return unittest.expectedFailure(func)
+
+        call('rmmod netdevsim', stderr=subprocess.DEVNULL)
+        return func
+
+    return f
+
 def expectedFailureIfCAKEIsNotAvailable():
     def f(func):
         call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL)
@@ -1695,6 +1722,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         '25-route-vrf.network',
         '25-gateway-static.network',
         '25-gateway-next-static.network',
+        '25-sriov.network',
         '25-sysctl-disable-ipv6.network',
         '25-sysctl.network',
         '25-test1.network',
@@ -2237,7 +2265,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         self.assertRegex(output, 'inet6 .* scope link')
         output = check_output('ip -4 route show dev dummy98')
         print(output)
-        self.assertEqual(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
+        self.assertRegex(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
         output = check_output('ip -6 route show dev dummy98')
         print(output)
         self.assertRegex(output, 'default via 2607:5300:203:39ff:ff:ff:ff:ff proto static')
@@ -2260,7 +2288,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         self.assertRegex(output, 'inet6 .* scope link')
         output = check_output('ip -4 route show dev dummy98')
         print(output)
-        self.assertEqual(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
+        self.assertRegex(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
         output = check_output('ip -6 route show dev dummy98')
         print(output)
         self.assertRegex(output, 'default via 2607:5300:203:39ff:ff:ff:ff:ff proto static')
@@ -2508,6 +2536,32 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         self.assertRegex(output, 'quanta 1 2 3 4 5')
         self.assertRegex(output, 'priomap 3 4 5 6 7')
 
+    @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
+    def test_sriov(self):
+        call('rmmod netdevsim', stderr=subprocess.DEVNULL)
+        call('modprobe netdevsim', stderr=subprocess.DEVNULL)
+        with open('/sys/bus/netdevsim/new_device', mode='w') as f:
+            f.write('99 1')
+
+        call('udevadm settle')
+        call('udevadm info -w10s /sys/devices/netdevsim99/net/eni99np1', stderr=subprocess.DEVNULL)
+        with open('/sys/class/net/eni99np1/device/sriov_numvfs', mode='w') as f:
+            f.write('3')
+
+        copy_unit_to_networkd_unit_path('25-sriov.network')
+        start_networkd()
+        self.wait_online(['eni99np1:routable'])
+
+        output = check_output('ip link show dev eni99np1')
+        print(output)
+        self.assertRegex(output,
+                         'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
+                         'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
+                         'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
+        )
+
+        call('rmmod netdevsim', stderr=subprocess.DEVNULL)
+
 class NetworkdStateFileTests(unittest.TestCase, Utilities):
     links = [
         'dummy98',
index 374f1e6b5f98bcb64e4e40c7b9169ffc2e6ec3af..3170f979e258ecb4fc3c5c0a07a04efab6487c1b 100644 (file)
@@ -4,8 +4,8 @@ After=multi-user.target
 
 [Service]
 ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-ExecStop=sh -c 'kill -SIGKILL $MAINPID'
+ExecStop=sh -c 'kill -KILL $MAINPID'
 FailureAction=reboot
 
 [Install]
-WantedBy=multi-user.target
\ No newline at end of file
+WantedBy=multi-user.target
index cc665021d4b6bd79d1a7f8836c6021cdf6d64db9..8fe43c077109fa499e33139d4fe2b6ec32338cd7 100755 (executable)
@@ -18,11 +18,15 @@ REPO_ROOT="${REPO_ROOT:-$PWD}"
 ADDITIONAL_DEPS=(
     clang
     dnf-plugins-core
-    hostname libasan
-    jq iputils
+    hostname
+    iputils
+    jq
+    libasan
     libfdisk-devel
+    libfido2-devel
     libpwquality-devel
     libubsan
+    libzstd-devel
     llvm
     openssl-devel
     p11-kit-devel