#939357 `decrypt_keyctl` fails when the user-keyring(7) isn't attached to the calling process

Package:
sudo
Source:
sudo
Description:
Provide limited super user privileges to specific users
Submitter:
Sebastian Mohr
Date:
2022-03-14 14:27:04 UTC
Severity:
normal
Tags:
#939357#5
Date:
2019-09-03 22:18:57 UTC
From:
To:
Dear Maintainer,

when configuring this encrypted machine running debian stable with keyscript
"decryt_keyctl", the invocation of "cryptdisks_start data{0,1}_crypt" when
logged in via ssh as normal user and "sudo -i" to root fails with the message:

| [....] Starting crypto disk...data0_crypt (starting)...Caching passphrase for data0_crypt:  *********************
| keyctl_set_timeout: Permission denied
| Error setting timeout on key (824927206), removing
| Nothing to read on input.
| Caching passphrase for data0_crypt:

After some searching, I found a link to bug #758788, especially message #45 and #50 [1], which
described seemingly the same bug:

| > On the first invocation (for cont1_crypt), I got this dialog:
| >
| > root@marislae:~# cryptdisks_start cont1_crypt
| > [[....] Starting crypto disk...[info] cont1_crypt (starting)...
| > Caching passphrase for /cont1:  ******
| > keyctl_set_timeout: Permission denied
| > Error setting timeout on key (2524288), removing
| > Caching passphrase for /cont1:  ******
| > keyctl_set_timeout: Permission denied
| > Error setting timeout on key (612589418), removing
| >
| > [Here I pressed <ctrl-C> to stop the attempts]
| >
| > Caching passphrase  for /cont1:  Erreur de lecture de la phrase secrète.
| >
| >
| > I was running the commands from root. I initially logged in to the
| > computer from SSH to a regular user, than did "sudo -i" to get root
| > access if this matters. As I suspected this may be a problem, I allowed
| > root direct SSH access and tried again, login directly to root account,
| > this time it worked:
|
| Interesting, never saw these kind of problems before. I'm testing on a
| Laptop with Ubuntu 14.04 installed and use 'sudo -i' a lot. Indeed I
| used it for testing the commands above as well. For me it worked. But
| let's keep that aside. It's another issue and out of scope for this
| bugreport ;)

But I haven't found another link to that aspect of that bug; the mentioned
bug was closed without looking at that facette further.

Another link I stumbled upon was one of Matthew Gerret describing a workaround
with the kernel keyring and sudo [2].

I adapted a copy of decrypt_keyctl to his suggestions and afterwards I could
decrypt the multiple cryptdisks. Here is the patch:
--- 8< --- 8< --- 8< ---
--- decrypt_keyctl 2019-06-10 14:51:15.000000000 +0200 +++ decrypt_keyctl_debug 2019-09-03 23:33:51.311240116 +0200 @@ -43,8 +43,13 @@ keyctl unlink "$KID_" @u KID_="" fi - KID_="$(printf "%s" "$KEY_" | keyctl padd user "$ID_" @u)" + KID_="$(printf "%s" "$KEY_" | keyctl padd user "$ID_" @s)" [ -n "$KID_" ] || die "Error adding passphrase to kernel keyring" + if ! ( keyctl setperm "$KID_" 0x3f3f0000 && keyctl link "$KID_" @u && keyctl unlink "$KID_" @s ); then + keyctl unlink "$KID_" @u + keyctl unlink "$KID_" @s + die "Error transferring permissions on key ($KID_), removing" + fi if ! keyctl timeout "$KID_" "$TIMEOUT_"; then keyctl unlink "$KID_" @u die "Error setting timeout on key ($KID_), removing"
--- 8< --- 8< --- 8< --- I don't know whether there are further security implications or something like that, nor did I do extensive testing. The error handling was also added in a quick way without testing it further (I also don't know how to provoke that error). I just wanted to let you know that and how I fixed a problem not only I had. Regards Sebastian [1]: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=758788#50 [2]: https://mjg59.dreamwidth.org/37333.html
#939357#10
Date:
2019-09-05 00:03:34 UTC
From:
To:
Hi Sebastian,

Thanks for the detailed report!  I was able to reproduce this in a fresh
Buster netinstall, taking SSH sessions and sudo(8)'s ‘-i’ flag out of
the picture.  This is what I get right after a reboot:

    user@kvm-31992:~$ sudo keyctl show @u
    Keyring
      99804791 --alswrv      0 65534  keyring: _uid.0
    user@kvm-31992:~$ sudo keyctl show @s
    Keyring
    1021507217 --alswrv   1000  1000  keyring: _ses
     872133423 ---lswrv   1000 65534   \_ keyring: _uid.1000
    user@kvm-31992:~$ sudo keyctl show @us
    Keyring
     133933438 --alswrv      0 65534  keyring: _uid_ses.0
      99804791 --alswrv      0 65534   \_ keyring: _uid.0

Keys attached to root's user-keyring(7) can't be read.

    user@kvm-31992:~$ sudo keyctl add user "foo" "bar" @u
    875128385
    user@kvm-31992:~$ sudo keyctl show @us
    Keyring
     133933438 --alswrv      0 65534  keyring: _uid_ses.0
      99804791 --alswrv      0 65534   \_ keyring: _uid.0
     875128385 --alswrv      0     0       \_ user: foo
    user@kvm-31992:~$ sudo keyctl print 875128385
    keyctl_read_alloc: Permission denied

So LUKS2 volumes can't be open using token keyrings:

    user@kvm-31992:~$ sudo dd if=/dev/zero of=/tmp/disk.img bs=1M count=64 status=none
    user@kvm-31992:~$ sudo cryptsetup luksFormat -q \
        --pbkdf-force-iterations 4 --pbkdf-memory 32 /tmp/disk.img <<<bar
    user@kvm-31992:~$ sudo cryptsetup token add --key-description foo -S 0 /tmp/disk.img
    user@kvm-31992:~$ sudo cryptsetup luksOpen --test-passphrase --debug \
        --token-only /tmp/disk.img
    […]
    # keyring_get_passphrase failed (error -126)
    # Token 0 (luks2-keyring) open failed with -22.

And indeed decrypt_keyctl chokes as well:

    user@kvm-31992:~$ sudo CRYPTTAB_NAME=foo CRYPTTAB_KEY=bar CRYPTTAB_TRIED=0 \
        /lib/cryptsetup/scripts/decrypt_keyctl
    Caching passphrase for foo:  ***
    keyctl_set_timeout: Permission denied
    Error setting timeout on key (163348240), removing

AFAICT this is because the caller doesn't possess %user:foo, cf.
https://manpages.debian.org/buster/manpages/keyrings.7.en.html#Possession

A workaround is to do these operations in a new session-keyring(7), and
link root's user-keyring(7) to it.

    user@kvm-31992:~$ sudo keyctl session
    Joined session keyring: 777897511
    root@kvm-31992:/home/user# keyctl link @u @s
    root@kvm-31992:/home/user# keyctl show
    Session Keyring
     777897511 --alswrv      0     0  keyring: _ses
      99804791 --alswrv      0 65534   \_ keyring: _uid.0
     875128385 --alswrv      0     0       \_ user: foo
    root@kvm-31992:/home/user# keyctl print 875128385
    bar
    root@kvm-31992:/home/user# cryptsetup luksOpen --test-passphrase -v \
        --token-only /tmp/disk.img
    Key slot 0 unlocked.
    Command successful.
    root@kvm-31992:/home/user# CRYPTTAB_NAME=foo CRYPTTAB_KEY=bar CRYPTTAB_TRIED=0 \
        /lib/cryptsetup/scripts/decrypt_keyctl >/dev/null
    Caching passphrase for foo:  ***
    root@kvm-31992:/home/user# exit

I suppose /etc/pam.d/sudo could use pam_keyinit(8) to initialize a new
session instead of doing the above dance.  Anyway cryptsetup calls
request_key("user",,NULL,0) at the moment, so unlocking LUKS2 devices
via keyring tokens will only works in sessions where the user-keyring(7)
is linked to the session-keyring(7).

AFAICT blindly using session-keyring(7) as it might open a window during
which any program in the user session is able to read the payload:

    user@kvm-31992:~$ sudo keyctl add user "foo" "foo" @s
    137160032
    user@kvm-31992:~$ keyctl show @s
    Keyring
     461059914 --alswrv   1000  1000  keyring: _ses
     641525861 --alswrv   1000 65534   \_ keyring: _uid.1000
     137160032 --alswrv      0     0   \_ user: foo
    user@kvm-31992:~$ keyctl print 137160032
    foo

(While the key is owned by the privileged user, it's possessed by the
caller.  It might even be possible to poison the session-keyring(7)
by creating %user:foo with non-root ownership.)  I think the best place
to solve that is in /etc/pam.d/sudo itself, but failing that I suppose
it's acceptable to have the payload readable by root (perhaps only as
fallback, when the caller doesn't have enough right on the key it's
adding).

Perhaps keyctl(1) could provide a wrapper using thread-keyring(7) as
temporary keyring, like the attached PoC.  Or we could even rewrite
decrypt_keyctl in C, or merge it with askpass.  In fact one of our goals
for this release cycle is to better integrate crypttab(5) with LUKS2
native keyring support, and that probably goes via adding keyring
support in askpass.

Cheers,

#939357#17
Date:
2019-09-05 00:36:35 UTC
From:
To:
Control: reassign -1 sudo 1.8.27-1
Control: affects -1 cryptsetup
Control: merge -1 906752

Of course I forgot the attachment :-P  That said I'm not sure that
using a temporary keyring and changing ownership is the way to go, it
adds complexity and not having a reachable user-keyring(7) might cause
other problems.

I was about to reassign that to sudo but noticed there is already a bug
open: https://bugs.debian.org/906752