Control: retitle -1 `decrypt_keyctl` fails when the user-keyring(7) isn't 
attached to the calling process

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).

On Wed, 04 Sep 2019 at 00:18:57 +0200, Sebastian Mohr wrote:
> I don't know whether there are further security implications or
> something like that,

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,
-- 
Guilhem.

Attachment: signature.asc
Description: PGP signature

Reply via email to