Re: authentication / encryption key retention

From: David Howells
Date: Fri Aug 22 2003 - 12:41:40 EST



Linus Torvalds <torvalds@xxxxxxxx> wrote:

> > Each persona would then have an identity (UID) and a sequence of keys.

Sorry, I was using UID and user_struct interchangably.

> I don't know if UID helps much. What would you use it for?

It is also an access key and an identification token. It is used for some
local filesystems. It is also used to drive access control on /proc/<pid>/ and
for signal and ptrace targetting.

> This is what we do have UID's for, and changing that would break a lot of
> existing security-conscious programs potentially very badly. Another
> reason not to mix up the uid/key concepts.

I agree, but... each UID brings with it a set of keys, so they are linked at
least a little.

> You really want the "file open" part to be the thing that decides which
> key or bunch of keys are relevant to that file open.

Agreed. But there are some issues:

(1) Which key does it pick? There needs to be some ordering on the keys
attached to a process, and it must be possible for the process to either
rearrange these keys or to have a say in which key file->open() selects.

Remember, keys can come from three sources potentially:

(*) SUID programs
(*) UID default
(*) GID default
(*) credential passing
(*) manual subscription

Which one you want to apply to the opening of a file will vary.

(2) Key retirement. If file->open() selects a key that subsequently gets
retired, should the filesystem be able to select another key from the
original set of keyrings? Or should it thenceforth return an error?

I think an argument can be made for the filesystem being allowed to build
its own keyring in file->open() by filtering all the keys available to
the parent process. When all those keys have been retired, it then
returns an error.

UID, GID and groups would be kept in the keyring as three individual
keys, if the filesystem required them.

(3) Memory. You've only got so much. Do you want every open() call to go and
build a keyring containing a subset of a process's keys.

> And I don't think it would have anything to do with a persona: a file open
> will care about the particular keys needed for that
> connection/filesystem/file, not about "persons".

Except that the UID is also a key for some filesystems.

> - do _not_ mix up current uid/gid issues with key management. It will
> only cause untold pain for programs that expect to care only about
> uids.

Fair enough. It'd be, as you say, an untold pain otherwise.

> - For "group of key management", I do think you want to have your
> "persona", but not because you want to associate them directly with
> uid/gid issues, but because you would need to have some mechanism to
> pass many unrelated keys around, without having to pass around _all_
> your own keys.

How about this? Taking your suggestion for nested keyrings, a process has a
ring of private keyrings labelled uniquely within that process that define all
the keys it currently has access to:

Keyring "_process.11374"
Keyring "_uid.4043" # UID default keyring
Keyring "_gid.100" # GID default keyring
Keyring "_groups" # GROUPS keyring
Keyring "_leant.0" # keyring acquired from another program
Keyring "KDE-dhowells1" # user specified keyring
Keyring "AFS-dhowells" # user specified keyring

Anything that changes the process's UID or GID (such as setuid()) substitutes
alternative keyrings for "_uid*" and "_gid*", but without changing the
ordering or the keyrings.

The UNIX groups list could then be replaced with a keyring containing GIDs and
GID keyrings perhaps:

Keyring "_groups" # GROUPS keyring
Keyring "_gid.4043"
Key GID 4043 # undeletable key
Key DES "/dev/hda5" "2d189a4891c8218f9248904"
Key DES "/dev/hda6" "89052852098290423789042"
Key DES "/dev/hda7" "08983847892323987243987"
Keyring "_gid.4040"
Key GID 4040
Keyring "_gid.4041"
Key GID 4041
Keyring "_gid.4007"
Key GID 4007

And then if, say, ext3_file_open() filtered these for its own nefarious
purposes, you might end up with:

Keyring "_file"
Key UID 4043
Key GID 100
Key GID 4043
Key DES "/dev/hda5" "2d189a4891c8218f9248904"
Key GID 4040
Key GID 4041
Key GID 4007

Attached to file->f_keyring.

> And also, probably more importantly, it would also be the way to
> invalidate a bunch of keys

Of course, that may rule out filtering keyrings in file->open() to build a
private keyring for the FD.

> My personal favourite would actually be to allow "buckets of buckets of
> [buckets of] keys".

The big problem with this is dealing with them without recursive functions,
but I do like the idea.

> - it must _not_ be possible to read out a key just because you have
> access to it.

Agreed (from userspace at least). However, you might want to be able to see
the key descriptions if not the payload.

> - this means that once you get a key or a "bucket of keys", you can't
> just re-create it. The only thing you can do is to create another
> reference to it.

But you do need to be able to modify a bucket, at the very least by adding
things to it, retiring it or retiring keys from within it. Also you need to be
able to subscribe to it.

I think that each bucket will require an ACL, and possibly keys should have
ACLs too.

> So if you want to pass off the keys you got to some third party, together
> with a few new keys of your own, you'd really need to create a new
> "bucket" that contains a pointer to the old bucket along with the new
> keys.

The problem with that is you can't pass a subset, only a complete
set. Hmmm... I think it ought to be possible to filter on description, but
that the data should not ever return to userspace.

> And notice how you can invalidate an arbitrary bucket by just marking it
> "invalid"

I like that.

> What I really care about is:
>
> - the data structure/mechanism should be totally agnostic about the kind
> of keys you hide in there.

Yes.

> The only thing that should be contained in the keys/buckets (apart from
> the key blob itself) is enough meta-data that we can have an a sane "look
> up keys of type xxx" operation.

I think an ACL is a must (as I've said before). The ACL should provide the
following rights:

(*) Subscribe to Keyring
(*) Enumerate
(*) Add keys
(*) Retire keys
(*) Retire Keyring
(*) Change ACL

And should be able to use either UID or GID as required.

If key ACLs are also to be used, they should have the following rights
available:

(*) Use key
(*) Copy key to another keyring
(*) Retire key
(*) Read payload from userspace
(*) Change ACL

If keyrings are just special keys, then the two sets of rights can be merged.

Normally ACLs would be trivial - just give all rights to their owner, and
nothing to anybody else. In the case of UID or GID default rings, the owner is
UID or GID in question.

> - you do _not_ depend on "read the keys, duplicate them, write them out
> again as a new bunch" as a maintenance operation. That does _not_work_
> for keys that are supposed to be private. If I give somebody else my
> key, that does not mean that he can read it. He can use it, but he
> can't make a copy.

I wasn't advocating that userspace _should_ necessarily be allowed to read the
payload of a key. If at all possible, then it should strictly be under the
control of an ACL.

I also don't like the idea that you're suggesting, and wasn't suggesting it
myself.

> (And I'm carefully staying out of guessing what functions we want to have
> to iterate over all keys of a specific type. But it doesn't look
> impossible).

Okay... I'll have a look at implementing this.

Thanks,
David
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/