ubuntusystemdkeyring

Service user can't read user key with keyctl: keyctl_read_alloc: Permission denied


$ sudo -u backup_agent keyctl add user LUKS_PASSWORD "your-secure-password" @u
1013632990
$ sudo -u backup_agent keyctl list @u
1 key in keyring:
1013632990: --alswrv   110   110 user: LUKS_PASSWORD
$ sudo -u backup_agent keyctl read 1013632990
keyctl_read_alloc: Permission denied

I'm trying to create a key for a service user that it would use from a script later from a cron job. I can't understand why the last step above results in a permission denied error. Any ideas?


Solution

  • The key needs to be reachable from the process's "session keyring" (@s or @us).

    If you run keyctl describe <id> to see the full permissions on the newly added key, it'll show four sets of permissions: possessor, owner, group, other. By default they are:

    $ sudo keyctl describe 398779806
    398779806: alswrv-----v------------     0     0 user: foo
    

    So the permissions are alswrv for possessor, -----v for owner, ------ for group/other – which means that matching owner UID is insufficient to read the key. Full permissions are only granted if the key is "possessed" i.e. reachable from your process's @s or @us session keyring.

    The "user" keyring @u is not considered on its own – merely having UID 110 isn't enough; it actually has to be linked from the session keyring of the process doing the operation. If you run keyctl show without parameters (listing the session keyring), you'll usually see that it contains a link to your user keyring – shown as _uid.XXX – and that is what grants you "possession" of the keys in @u.

    $ keyctl show
    Session Keyring
    1046546095 --alswrv   2001   100  keyring: _ses
     348545926 --alswrv   2001 65534   \_ keyring: _uid.2001
    

    But the session keyring is inherited through fork and even through setuid, so sudo keyctl show will still show your original session containing a link to the original user's @u keyring; e.g. it is not _uid.0 but _uid.2001 in this example.

    $ sudo keyctl show
    Session Keyring
    1046546095 --alswrv   2001   100  keyring: _ses
     348545926 ---lswrv   2001 65534   \_ keyring: _uid.2001
    

    So in order to gain possession of the new key, you have to end up with a @s (either the same session or a brand new session) which has a link to the backup_agent's @u.

    This probably won't be a problem for cron jobs, as they already start from a fresh environment – going through PAM anew, and having pam_keyinit set up a fresh @s with the correct link to @u. Likewise, something like run0 (systemd's sudo alternative which asks init to spawn the shell indirectly) will get a new session keyring through PAM.

    $ run0 keyctl show
    Session Keyring
     363016313 --alswrv      0     0  keyring: _ses
     774763472 --alswrv      0 65534   \_ keyring: _uid.0
     398779806 --alswrv      0     0       \_ user: foo
    

    To make sudo work, however, you'd need either to: