For discussion around this see: https://pagure.io/fedora-workstation/issue/82
Recovery keys for homed are very similar to regular passwords, except
that they are exclusively generated by the computer, and not chosen by
the user. The idea is that they are printed or otherwise stored
externally and not what users type in every day.
Taking inspiration from Windows and MacOS this uses 256bit keys. We
format them in 64 yubikey modhex characters, in groups of 8 chars
separated by dashes.
Why yubikey modhex? modhex only uses characters that are are located at
the same place in western keyboard designs. This should reduce the
chance for incorrect inputs for a major chunk of our users, though
certainly not all. This is particular relevant during early boot and
recovery situations, where there's a good chance the keyboard mapping is
not correctly set up.
We return BUS_ERROR_NO_SUCH_UNIT a.k.a. org.freedesktop.systemd1.NoSuchUnit
in various places. In #16813:
Aug 22 06:14:48 core sudo[2769199]: pam_systemd_home(sudo:account): Failed to query user record: Unit dbus-org.freedesktop.home1.service not found.
Aug 22 06:14:48 core dbus-daemon[5311]: [system] Activation via systemd failed for unit 'dbus-org.freedesktop.home1.service': Unit dbus-org.freedesktop.home1.service not found.
Aug 22 06:14:48 core dbus-daemon[5311]: [system] Activating via systemd: service name='org.freedesktop.home1' unit='dbus-org.freedesktop.home1.service' requested by ':1.6564' (uid=0 pid=2769199 comm="sudo su ")
This particular error comes from bus_unit_validate_load_state() in pid1:
case UNIT_NOT_FOUND:
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not found.", u->id);
It seems possible that we should return a different error, but it doesn't really
matter: if we change pid1 to return a different error, we still need to handle
BUS_ERROR_NO_SUCH_UNIT as in this patch to handle pid1 with current code.
Apparently both Fedora and suse default to btrfs now, it should hence be
good enough for us too.
This enables a bunch of really nice things for us, most importanly we
can resize home directories freely (i.e. both grow *and* shrink) while
online. It also allows us to add nice subvolume based home directory
snapshotting later on.
Also, whenever we mention the three supported types, alaways mention
them in alphabetical order, which is also our new order of preference.
Also, let's move the glue for this to src/shared/ so that we later can
reuse this in sysemd-firstboot.
Given that libpwquality is a more a leaf dependency, let's make it
runtime optional, so that downstream distros can downgrade their package
deps from Required to Recommended.
The cryptsetup context pins the loop device even after deactivation.
Let's explicitly release the context to make sure the subsequent
loopback device detaching works cleanly.
This how this works on Linux: when atomically creating a file we need to
fully populate it under a temporary name and then when we are fully
done, sync it and the directory it is contained in, before renaming it
to the final name.
../src/home/homectl-pkcs11.c:19:13: warning: ‘pkcs11_callback_data_release’ defined but not used [-Wunused-function]
19 | static void pkcs11_callback_data_release(struct pkcs11_callback_data *data) {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
This is mostly cosmetic, but let's reorder the destructors so that
we do the final sd_notify() call before we run the destructor for
the manager object.
There's some highly specific PKCS#11 code in homectl.c. Let's split that
out, since it is easily isolatable, to make homectl.c a bit more
readable.
No funcional changes, just some moving around and renaming two functions
to make them more suitably named when exported.
When updating a home directory we might update the record first, then
resize the image and finally synchronize the passwords to the storage
layers. These are three individually authenticated operations. Since
each might require touching a FIDO2 or PKCS#11 key we should say what we
are doing. Hence do so.
Usually we are pretty quiet with what we do, and let's stick to that.
Hence show this information only if we actually do more than one thing.
If we only update (and do not resize/sync passwords) then let's be quiet
as usual, as the command line then sufficiently clarifies what we are
doing.
After all, when creating we might need interaction with the security
token too, and our initial attempt to create the user will fail, since
we do not allow interactive auth on the security token, so that we then
can print a log message and retry with interactive auth then enabled.
We'd like to use it for FIDO2 tokens too, and the concept is entirely
generic, hence let's just reuse the field, but rename it. Read the old
name for compatibility, and treat the old name and the new name as
identical for most purposes.
Presently, CLI utilities such as systemctl will check whether they have a tty
attached or not to decide whether to parse /proc/cmdline or EFI variable
SystemdOptions looking for systemd.log_* entries.
But this check will be misleading if these tools are being launched by a
daemon, such as a monitoring daemon or automation service that runs in
background.
Make log handling of CLI tools uniform by never checking /proc/cmdline or EFI
variables to determine the logging level.
Furthermore, introduce a new log_setup_cli() shortcut to set up common options
used by most command-line utilities.
This is a follow-up for 9f83091e3c.
Instead of reading the mtime off the configuration files after reading,
let's do so before reading, but with the fd we read the data from. This
is not only cleaner (as it allows us to save one stat()), but also has
the benefit that we'll detect changes that happen while we read the
files.
This also reworks unit file drop-ins to use the common code for
determining drop-in mtime, instead of reading system clock for that.
homed maintains two or three copies of the user's identity record per
home directory: one on the host, one inside the LUKS header, and one
embedded in the home directory.
Previously we'd insist that if a user logs in they have to authenticate
against all three, as a safety feature. This broke logging into
unfixated records however, since in that case the host version is
synthetic and thus does not carry any authentication data.
Let's hence losen the strictness here: accept authentication against
host records that carry no auth data. This should be safe as we know
after all that the second/third record will catch invalid accesses.
Fixes: #15178
We generally return > 1 if any of the actions we are doing is instantly
complete and == 0 when we started doing it asynchronously (by forking
off homework), in our functions that execute operations on homes.
Fix a mix-up where the test for this was reversed in
home_dispatch_release() and home_dispatch_lock_all().
Fixes: #15684
This variable is read by the module and can be used instead of the
suspend= PAM module parameter.
It is also set for the session itself to make debugging easy.
We might pin a home through authentication and a different one through a
session, all from the same PAM context, like sudo does. Hence also store
the referencing fd keyed by the user name.
Since acquiring user records involves plenty of IPC we try to cache user
records in the PAM context between our various hooks. Previously we'd
just cache whatever we acquired, and use it from the on, forever until
the context is destroyed.
This is problematic however, since some programs (notably sudo) use the
same PAM context for multiple different operations. Specifically, sudo
first authenticates the originating user before creating a session for
the destination user, all with the same PAM context. Thankfully, there
was a safety check for this case in place that re-validated that the
cached user record actually matched our current idea of the user to
operate on, but this just meant the hook would fail entirely.
Let's rework this: let's key the cache by the user name, so that we do
not confused by the changing of the user name during the context's
lifecycle and always, strictly use the cached user record of the user we
operate on.
Essentially this just means we now include the user name in the PAM data
field.
Secondly, this gets rid of the extra PAM data field that indicates
whether a user record is from homed or something else. To simplify
things we instead just cache the user record twice: once for consumption
by pam_systemd_home (which only wants homed records) and once shared by
pam_systemd and pam_systemd_home (and whoever else wants it). The cache
entries simply have different field names.
These arguments contain UserRecord structures serialized to JSON,
however only the "secret" part of it, not a whole user record. We do
this since the secret part is conceptually part of the user record and
in some contexts we need a user record in full with both secret and
non-secret part, and in others just the secret and in other just the
non-secret part, but we want to keep this in memory in the same logic.
Hence, let's rename the arguments where we expect a user record
consisting only of the secret part to "secret".
Previously pam_systemd_home.so was relying on `PAM_PROMPT_ECHO_OFF` to
display error messages to the user and also display the next prompt.
`PAM_PROMPT_ECHO_OFF` was never meant as a way to convey information to
the user, and following the example set in pam_unix.so you can see that
it's meant to _only_ display the prompt. Details about why the
authentication failed should be done in a `PAM_ERROR_MSG` before
displaying a short prompt as per usual using `PAM_PROMPT_ECHO_OFF`.
Let's make the logic a bit smarter: if we detect that /home is
encrypted, let's avoid double encryption and prefer plain
directory/subvolumes instead of our regular luks images.
Also, allow configuration go storage/file system via an env var passed
to homework. In a later commit, let's then change homed to initialize
that env var from a config file setting, when invoking homework.
Make use of the new user_record_build_image_path() helper the previous
commit added to share some code.
Also, let's make sure we update all parsed-out fields with the new data
from the binding, so that the parsed-out fields are definitely
up-to-date.
Let's make debugging a bit easier: when invoking homed from the build
tree it's now possible to make sure homed invokes the build tree's
homework binary by setting an env var.
We always need to make them unions with a "struct cmsghdr" in them, so
that things properly aligned. Otherwise we might end up at an unaligned
address and the counting goes all wrong, possibly making the kernel
refuse our buffers.
Also, let's make sure we initialize the control buffers to zero when
sending, but leave them uninitialized when reading.
Both the alignment and the initialization thing is mentioned in the
cmsg(3) man page.
Apparently unpriv clients expect to be able to auth via PAM. Kinda
sucks. But it is what it is. Hence open this up.
This shouldn't be too bad in effect since clients after all need to
provide security creds for unlocking the home dir, in order to misuse
this.
Fixes: #15072
Let's be extra careful whenever we return from recvmsg() and see
MSG_CTRUNC set. This generally means we ran into a programming error, as
we didn't size the control buffer large enough. It's an error condition
we should at least log about, or propagate up. Hence do that.
This is particularly important when receiving fds, since for those the
control data can be of any size. In particular on stream sockets that's
nasty, because if we miss an fd because of control data truncation we
cannot recover, we might not even realize that we are one off.
(Also, when failing early, if there's any chance the socket might be
AF_UNIX let's close all received fds, all the time. We got this right
most of the time, but there were a few cases missing. God, UNIX is hard
to use)