* 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.
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.
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
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.
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
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=.
* 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.
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
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
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
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.
`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
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
`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
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.
`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`
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
#########################################
# 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
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
<title>Options</title>
<para>All options are configured in the
- <literal>[Coredump]</literal> section:</para>
+ [Coredump] section:</para>
<variablelist class='config-directives'>
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>
<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>
<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>
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
<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>
<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
# 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>
<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'>
<title>Options</title>
<para>All options are configured in the
- <literal>[Remote]</literal> section:</para>
+ [Remote] section:</para>
<variablelist class='config-directives'>
<varlistentry>
<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>
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>
<title>Options</title>
<para>All options are configured in the
- <literal>[Journal]</literal> section:</para>
+ [Journal] section:</para>
<variablelist class='config-directives'>
<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>
<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
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<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).
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>
<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>
<title>Options</title>
<para>All options are configured in the
- <literal>[PStore]</literal> section:</para>
+ [PStore] section:</para>
<variablelist class='config-directives'>
<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>
<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'>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<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
<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>
<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>
<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>
<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
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
<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
<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
<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
<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
<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>
</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>> 0</entry>
</row>
<row>
<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>
<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>
<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>
<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>
<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>
</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>
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
<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>
<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>
<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>,
<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
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>
<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>
<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>
</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>
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>.
<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>
<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>
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
<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
<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>
<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>
<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>
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>
<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
<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
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>
<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
</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>
</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>
</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>
</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>
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
<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>
<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'>
<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>
<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>
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>
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>
<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>
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>,
</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>
<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>
<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'>
<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>
<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>
</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>
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,
<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
<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'>
<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>
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>
<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>
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>
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
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
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
<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
<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>
<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>
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
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
<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>
<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>
<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>
<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'>
<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>
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',
libcrypt,
libopenssl,
libfdisk,
- libp11kit],
+ libp11kit,
+ libfido2],
install_rpath : rootlibexecdir,
install : true,
install_dir : rootlibexecdir)
libcrypt,
libopenssl,
libp11kit,
+ libfido2,
libpwquality],
install_rpath : rootlibexecdir,
install : true,
['pwquality'],
['libfdisk'],
['p11kit'],
+ ['libfido2'],
['AUDIT'],
['IMA'],
['AppArmor'],
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'],
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"
"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"
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í."
"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í."
[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 */
/* 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 */
},
};
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_;
(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
int socket_bind_to_ifindex(int fd, int ifindex) {
char ifname[IF_NAMESIZE + 1];
+ int r;
assert(fd >= 0);
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))
}
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);
if (!unit_name_is_valid(s, UNIT_NAME_INSTANCE))
return -EINVAL;
- *ret = s;
+ *ret = TAKE_PTR(s);
return 0;
}
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) */
"Unit\0"
"Automount\0"
"Install\0",
+ .private_section = "Automount",
.can_transient = true,
.can_fail = true,
"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);
}
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);
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.");
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,
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;
}
--- /dev/null
+/* 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
+}
--- /dev/null
+/* 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);
--- /dev/null
+/* 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
+}
--- /dev/null
+/* 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);
#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"
#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"
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;
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
!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) {
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)
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);
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;
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);
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");
} 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;
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;
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
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;
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;
}
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)
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;
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;
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;
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;
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);
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);
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);
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.");
" 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"
ARG_EXPORT_FORMAT,
ARG_AUTO_LOGIN,
ARG_PKCS11_TOKEN_URI,
+ ARG_FIDO2_DEVICE,
ARG_AND_RESIZE,
ARG_AND_CHANGE_PASSWORD,
};
{ "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 },
{}
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);
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;
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;
}
}
- 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)
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:
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;
int home_activate_cifs(
UserRecord *h,
- char ***pkcs11_decrypted_passwords,
+ PasswordCache *cache,
UserRecord **ret_home) {
_cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
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;
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);
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;
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;
int home_resize_directory(
UserRecord *h,
bool already_activated,
- char ***pkcs11_decrypted_passwords,
+ PasswordCache *cache,
HomeSetup *setup,
UserRecord **ret_home) {
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;
#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);
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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);
}
static int fscrypt_setup(
- char **pkcs11_decrypted_passwords,
+ const PasswordCache *cache,
char **password,
HomeSetup *setup,
void **ret_volume_key,
_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 */
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;
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;
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,
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;
assert(setup);
r = fscrypt_setup(
- pkcs11_decrypted_passwords,
+ cache,
h->password,
setup,
&volume_key,
#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);
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,
_cleanup_(erase_and_freep) void *vk = NULL;
sd_id128_t p;
size_t vks;
+ char **list;
int r;
assert(node);
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");
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,
_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;
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");
struct crypt_device *cd,
UserRecord *h,
const void *volume_key,
- char ***pkcs11_decrypted_passwords,
+ PasswordCache *cache,
UserRecord **ret_luks_home_record) {
int r, token;
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 */
UserRecord *h,
bool already_activated,
const char *force_image_path,
- char ***pkcs11_decrypted_passwords,
+ PasswordCache *cache,
HomeSetup *setup,
UserRecord **ret_luks_home) {
r = luks_open(setup->dm_name,
h->password,
- pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL,
+ cache,
&cd,
&found_luks_uuid,
&volume_key,
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;
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,
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;
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;
h,
false,
NULL,
- pkcs11_decrypted_passwords,
+ cache,
&setup,
&luks_home_record);
if (r < 0)
h,
&setup,
luks_home_record,
- pkcs11_decrypted_passwords,
+ cache,
&sfs,
&new_home);
if (r < 0)
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,
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 {
int home_create_luks(
UserRecord *h,
- char **pkcs11_decrypted_passwords,
+ PasswordCache *cache,
char **effective_passwords,
UserRecord **ret_home) {
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,
int home_resize_luks(
UserRecord *h,
bool already_activated,
- char ***pkcs11_decrypted_passwords,
+ PasswordCache *cache,
HomeSetup *setup,
UserRecord **ret_home) {
}
}
- 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;
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);
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");
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 {
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);
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");
#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;
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);
#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"
#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);
/* 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);
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) {
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) {
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();
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)
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
int home_prepare(
UserRecord *h,
bool already_activated,
- char ***pkcs11_decrypted_passwords,
+ PasswordCache *cache,
HomeSetup *setup,
UserRecord **ret_header_home) {
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:
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:
int root_fd,
UserRecord *header_home,
UserReconcileMode mode,
- char ***pkcs11_decrypted_passwords,
+ PasswordCache *cache,
UserRecord **ret_embedded_home,
UserRecord **ret_new_home) {
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 */
UserRecord *h,
HomeSetup *setup,
UserRecord *header_home,
- char ***pkcs11_decrypted_passwords,
+ PasswordCache *cache,
struct statfs *ret_statfs,
UserRecord **ret_new_home) {
/* 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;
}
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;
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;
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;
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;
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
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
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;
}
}
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;
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;
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:
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 */
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;
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;
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 */
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)));
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;
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;
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;
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;
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;
}
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);
/* 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;
* 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
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, \
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);
homework-mount.c
homework-mount.h
homework-pkcs11.h
+ homework-fido2.h
homework-quota.c
homework-quota.h
homework.c
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
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
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;
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;
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;
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;
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;
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
* -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);
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;
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);
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;
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;
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;
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;
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;
}
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);
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;
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);
#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;
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);
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);
{ { { 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);
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;
_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;
}
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,
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] = { };
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,
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;
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);
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;
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);
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];
}
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;
return test_fd[0];
}
-int icmp6_bind_router_advertisement(int index) {
-
+int icmp6_bind_router_advertisement(int ifindex) {
return -ENOSYS;
}
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),
#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"
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;
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;
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;
.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 },
[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 },
/* 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. */
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
#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"
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);
#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;
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_;
#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"
#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"
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)
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
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;
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);
unsigned routing_policy_rule_messages;
unsigned routing_policy_rule_remove_messages;
unsigned tc_messages;
+ unsigned sr_iov_messages;
unsigned enslaving;
Set *addresses;
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;
#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"
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)
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)
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
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
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
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
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
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
#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"
Route *route, *route_next;
FdbEntry *fdb, *fdb_next;
TrafficControl *tc;
+ SRIOV *sr_iov;
Iterator i;
assert(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;
}
filename, NETWORK_DIRS, dropin_dirname,
"Match\0"
"Link\0"
+ "SR-IOV\0"
"Network\0"
"Address\0"
"Neighbor\0"
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 &&
#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"
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;
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;
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;
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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);
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;
}
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",
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);
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",
#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;
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;
#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;
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");
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",
#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];
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",
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;
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);
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) {
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);
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);
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);
break;
} else {
dns_packet_rewind(p, pos);
- rr->unparseable = true;
- goto unparseable;
+ rr->unparsable = true;
+ goto unparsable;
}
}
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;
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);
/* 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);
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",
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:
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);
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;
/* 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;
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) {
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,
_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);
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);
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)
assert(m);
assert(slot_info);
assert(token_info);
- assert(search_uri);
token_label = pkcs11_token_label(token_info);
if (!token_label)
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. */
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;
}
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
_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)
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);
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",
.password_change_inactive_usec = UINT64_MAX,
.password_change_now = -1,
.pkcs11_protected_authentication_path_permitted = -1,
+ .fido2_user_presence_permitted = -1,
};
return h;
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;
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);
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);
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 },
{},
};
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;
}
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 },
{},
};
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 },
{},
};
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 },
{},
};
if (h->n_pkcs11_encrypted_key > 0)
return true;
+ if (h->n_fido2_hmac_salt > 0)
+ return true;
+
return !strv_isempty(h->hashed_password);
}
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;
char **hashed_password;
char **ssh_authorized_keys;
char **password;
- char **pkcs11_pin;
+ char **token_pin;
char *cifs_domain;
char *cifs_user_name;
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;
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. */
#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__);
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[]) {
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) {
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) {
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);
test_physical_memory_scale();
test_system_tasks_max();
test_system_tasks_max_scale();
+ test_foreach_pointer();
return 0;
}
#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"
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;
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"
"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;
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;
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;
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;
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) {
Multicast=
MACAddress=
Group=
+[SR-IOV]
+VirtualFunction=
+MACSpoofCheck=
+VLANId=
+VLANProtocol=
+QualityOfService=
+QueryReceiveSideScaling=
+Trust=
+LinkState=
+MACAddress=
[BridgeFDB]
VLANId=
MACAddress=
Parent=
Handle=
Rate=
+BurstBytes=
Burst=
+LimitBytes=
LimitSize=
MTUBytes=
MPUBytes=
Parent=
Handle=
PacketLimit=
+MemoryLimitBytes=
MemoryLimit=
Flows=
+QuantumBytes=
Quantum=
TargetSec=
IntervalSec=
Handle=
PacketLimit=
FlowLimit=
+QuantumBytes=
Quantum=
+InitialQuantumBytes=
InitialQuantum=
MaximumRate=
Buckets=
Parent=
Handle=
Bandwidth=
-Overhead=
+OverheadBytes=
[TrafficControlQueueingDiscipline]
Parent=
NetworkEmulatorDelaySec=
[BFIFO]
Parent=
Handle=
-LimitSize=
+LimitBytes=
[PFIFO]
Parent=
Handle=
Parent=
ClassId=
Weight=
-MaxPacketSize=
+MaxPacketBytes=
[DeficitRoundRobinScheduler]
Parent=
Handle=
[DeficitRoundRobinSchedulerClass]
Parent=
ClassId=
-Quantum=
+QuantumBytes=
[EnhancedTransmissionSelection]
Parent=
Handle=
[CAKE]
Parent=root
Handle=3a
-Overhead=128
+OverheadBytes=128
Bandwidth=500M
Handle=0032
PacketLimit=1000
FlowLimit=200
-Quantum=1500
-InitialQuantum=13000
+QuantumBytes=1500
+InitialQuantumBytes=13000
MaximumRate=1M
Buckets=512
OrphanMask=511
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
Parent=2:35
Handle=0035
Rate=1G
-Burst=5K
+BurstBytes=5000
LatencySec=70msec
PeakRate=100G
-MTUBytes=1M
+MTUBytes=1000000
[HierarchyTokenBucketClass]
Parent=root
[BFIFO]
Parent=2:3a
Handle=003a
-LimitSize=1M
+LimitBytes=1000000
[HierarchyTokenBucketClass]
Parent=root
[DeficitRoundRobinSchedulerClass]
Parent=root
ClassId=0002:0030
-Quantum=2000
+QuantumBytes=2000
Parent=root
ClassId=0002:0030
Weight=2
-MaxPacketSize=16000
+MaxPacketBytes=16000
[QuickFairQueueingClass]
Parent=root
ClassId=0002:0031
Weight=10
-MaxPacketSize=8000
+MaxPacketBytes=8000
--- /dev/null
+[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
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)
'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',
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')
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')
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',
[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
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