synalice
synalice

Reputation: 358

Share keys between sessions of kernel keyring

With Linux Kernel Keyring (keyctl or keyutils) how can I share a key not only between sessions but also between users?

I want a key to be created once and then accessible for everyone on the system no matter the session, UID or GID. This is required because systemd may launch a service as a separate session and different user.

Upvotes: -1

Views: 222

Answers (1)

FrankH.
FrankH.

Reputation: 18217

This is possible, but not in itself a good idea. Let me illustrate a little. So you can "make a key public" by this:

$ keyctl add user "mykey" "mysecretdata" @u
680243147
$ keyctl setperm 680243147 0x3f3f3f3f

For the sake of briefness ... I've done to that key what a "chmod 777" on a file would do ... basically, give everything to everyone - not great.

Then a "foreign" session like:

$ keyctl new_session "my new session ring"
1012519756
$ keyctl show
Session Keyring
1012519756 --alswrv   1000  1000  keyring: my new session ring
$ keyctl describe 680243147
680243147: alswrvalswrvalswrvalswrv  1000  1000 user: mykey
$ keyctl link 680243147 @s
$ keyctl show
Session Keyring
1012519756 --alswrv   1000  1000  keyring: my new session ring
 680243147 --alswrv   1000  1000   \_ user: mykey

will see it. Well, with the above, anyone who knows the key number can immediately access it (even without linking it to a ring of theirs first, so yes you can keyctl pipe 680243147 without the link in this example).

The devil is in the detail - namely, in the key permissions. These are:

  • 'a' - set attributes
  • 'l' - link/unlink the key (add / remove it to/from a keyring)
  • 's' - search for the key
  • 'w' - write the key (update the contents)
  • 'r' - read the key (use it)
  • 'v' - get (view) attributes

And then, these are grouped by possessor (anyone who holds the key in a keyring), owner, (owning) group, and other (anyone else).

The most tricky part in that sense is that you cannot be an "exclusive" owner; if you, say, allow attribute modifications or writing for possessors only, then the ability of anyone to link your key to a ring of theirs "transfers". If you restrict writes to the owner, then any process running under the same UID can update the key. If you give rights to "other" you have no longer any control who can see/use it.

Additionally ... you need to find a way to "pass the magic number" onwards.

So the idea of you "managing" access to key material that way is basically "here's a key use it folks !", and that fits pretty much only the usecase of "making a pubkey available".

But yea, if you want only that, change attributes on a key to be at least 'srv' (search, read, view attrs) for everyone including "others", and all you then need is a way to pass the key ID to your users. Make it non-writeable / non-attr-modifyable to everyone and you have a "global shared readonly key". It'll go away when the last user has gone.

But there's another option for sharing key material - using request_key(2). With that, you can do much more elaborate key management; in a way, it's a "proxy service" to the kernel keyring. The way it works is:

  1. the client app does request_key("user", "mydesiredkey", "some auth token goes here", KEY_SPEC_SESSION_KEYRING) (simplified)
  2. the kernel spawns a userspace helper process, /sbin/request-key to "validate" the request

For "usual" keyutils, this is a small shim program, request-key(8) which can be made to pass-on the request to another program of your choosing, see request-key.conf(5). That program can then decide what to do with the request. I can then either populate the key material (give the requesting client the "secret"), or reject the request. What it does "in the meantime" (connect out to some other service to "check", for example) is all up to the implementation. From my point of view (having written such code), no "great" sample application for this exists to illustrate several usecases and/or ways to "authenticate" request-key(2) usage.

Your specific usecase could be "served" by request_key; you would, as part of your systemd service install, also provide a small shell script, /usr/sbin/my-service-secret-reader that tests whether your service runs, then queries the "shared key content" and prints it to stdout; then also add a /etc/request-key.d/myservicekey.conf config file with a handler line:

create user service-xxx-sharedsecret "some auth token goes here" |/usr/sbin/my-service-secret-reader

The advantage of this is that each requestor gets their own (deep) copy of the key. I.e. the key itself isn't shared, and unless you "request" it first, you can't have it. Have your script fail (exit with an error) if the service isn't running, and instead the key requestor gets an error. And clients needn't know any "magic" locations where to get the shared secret - only type/name.

In practice ... if you use it exactly like this example the authentication to access (a known cleartext "password") is still rather weak. One can come up with much more elaborate schemes (think of something like proxying request-key(2) to some sort of vault service), but to even speculate about a possible design, I'd need much much more details on your usecase and requirements.

Upvotes: 1

Related Questions