linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* authentication / encryption key retention
@ 2003-08-22 15:25 David Howells
  2003-08-22 16:19 ` Linus Torvalds
  0 siblings, 1 reply; 10+ messages in thread
From: David Howells @ 2003-08-22 15:25 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: linux-kernel


Hi Linus,

I'm looking at implementing authentication token and encryption key retention
for the 2.7 kernel again.

I can implement keyrings and sequences of keyrings, but the problem I'm not
sure about it how to attach them to a process. Are you willing to advise me on
my ideas?

I think the best way is to have a stack of "personae" attached to a
process. Each persona would then have an identity (UID) and a sequence of keys.

Personae would be acquired by way of SUID programs, AF_UNIX credential passing,
and directly by way of syscalls.

The top persona on the stack would define what the process looked like to other
processes - controlling access to the process by signal and ptrace.

One of the personae on the stack would be elected to be the "effective" one -
the one that gets attached to newly opened files, and is used to specify access
to other processes for signal and ptrace.

Syscalls would be provided to reorder and modify the contents of the persona
stack.

Each struct file would have a pointer to the persona that contains the identity
and keys governing access to the underlying data.

Syscalls would also be provided to subscribe a persona to new keyrings (persona
would be shared as long as possible, but would be forked on modification).

Each Keyring would contain a set of distinguishable keys of various types, eg:
UNIX groups list, kerberos TGT for a particular domain, encryption key, process
access key. Each keyring would have an ACL that governs which UIDs/GIDs are
allowed to subscribe to it or modify it. Whilst keyrings would be visible
through sysfs, they would not be persistent once no longer in use.


However, all this makes for a few problems:

 (1) setuid()

     This can be made to manipulate the personae stack in various ways, but
     it'd be hard to get it'd work exactly as before.

     This is especially true with respect to GIDs. I think I'd have to attach
     a GID to each persona, however this would mean these UID manipulating
     syscalls would _also_ manipulate GIDs.

 (2) setreuid(), setresuid()

     As with setuid(), except that these would then manipulate multiple
     personae simultaneously, which probably won't be a problem - except if the
     "effective" UID is not a member of the stack...

     The top two entries on the stack can be viewed as real (TOS) and saved
     (2OS) UIDs.

 (3) setgid()

     GIDs would no longer be independent of UIDs, so this would change the GID
     on both the TOS and the effective persona.

 (4) setregid(), setresgid()

     These give the worst problems. I could duplicate personae to make sure I
     can store all the GIDs if they are all different, but that then has
     consequences for setre*uid().

 (5) setfsuid()

     This would then select the first persona on the stack that matches the
     UID, but what if there is no match? Should it then create a new persona
     with a different UID?

What I don't know is how attached we are to the current interface.

David

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: authentication / encryption key retention
  2003-08-22 15:25 authentication / encryption key retention David Howells
@ 2003-08-22 16:19 ` Linus Torvalds
  2003-08-22 17:39   ` David Howells
  0 siblings, 1 reply; 10+ messages in thread
From: Linus Torvalds @ 2003-08-22 16:19 UTC (permalink / raw)
  To: David Howells; +Cc: linux-kernel


On Fri, 22 Aug 2003, David Howells wrote:
> 
> I think the best way is to have a stack of "personae" attached to a
> process. Each persona would then have an identity (UID) and a sequence of keys.

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

The UID is already used to index into the "struct user" thing, and that
can (and probably should) contain a set of keys associated with that user.  
That implies that we _already_ have a UID -> set-of-key translation,
without actually mixing the UID with the keys themselves.

> The top persona on the stack would define what the process looked like to other
> processes - controlling access to the process by signal and ptrace.

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.

> One of the personae on the stack would be elected to be the "effective" one -
> the one that gets attached to newly opened files, and is used to specify access
> to other processes for signal and ptrace.

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

While a person will want to have keys associated with multiple _different_ 
filesystems or connections. 

So there is no 1:1 relationship there.

So my suggestion boils down to:

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

   So uid/group changes would happen exactly like they do now: and they 
   would result only in _purely_ uid/gid-related security elevation. 

   Of course, since we can have uid/gid-specific key bunches (ie the 
   "struct user" thing), when you have a setuid program that switches to 
   another user, you may implicitly get access to that users keys. But 
   that is only a direct result of the setuid itself, and doesn't change 
   the security model of setuid. It's 100% equivalent.

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

   And also, probably more importantly, it would also be the way to
   invalidate a bunch of keys (ie the security deamon that gave you a 
   particular bucket because you identified yourself to it can invalidate
   the whole bucket without actually having to invalidate each key 
   separately).

   So it would be just a "bucket of keys" you got from somewhere. Maybe 
   you get a couple of buckets as part of your login, and maybe you have 
   to do some strong authentication to a security server to get another
   "bucket".

   But "two buckets" would not make "one larger bucket".

My personal favourite would actually be to allow "buckets of buckets of 
[buckets of] keys". The reason you may want this is:

 - it must _not_ be possible to read out a key just because you have 
   access to it. In networked filesystems, keys don't have any specific 
   relevance (they're just random bits), but in many other cases they _do_ 
   have special relevance (ie they could be somebody's private key that he
   gave you temporary access to).

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

(disallowing recursive buckets is an issue, but is pretty trivial)

So the data structures could be something really trivial, like

	struct key_bucket {
		int type;	/* bucket or individual key */
		atomic_t count;	/* reference count */
		int valid;
		char *name;	/* identifier of creator of this bucket */
		union {
			struct key *key;
			struct bucket *bucket;
		}
	};

	struct key {
		int type;
		atomic_t count;
		const char *description;
		const char *blob;
	};

	struct bucket {
		int entries;
		struct key_bucket *list[];
	};

and now "struct key_bucket" can be just a single key, or it can be a 
bucket of single keys, or it can be a bucket of mixed keys/buckets.

(Yeah, the contents of "struct key" are totally made-up. The "blob" is the 
content-dependent part of the key, but they will obviusly depend on what 
kind of key it is, so you need some higher-level description of the key so 
that the _users_ of the key can search for a key they are interested in.)

And notice how you can invalidate an arbitrary bucket by just marking it
"invalid" - that doesn't invalidate any of the keys contained within, but
it just means that the key_bucket cannot be searched/looked up any more,
and thus it's effectively disabled.

What I really care about is:

 - the data structure/mechanism should be totally agnostic about the kind 
   of keys you hide in there. 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. 

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

With the straw-man bucket-of-buckets proposals, you can now endow _any_ 
data structure with any random collection of keys by just adding a

	struct key_bucket *keys;

entry to it.

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

		Linus


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: authentication / encryption key retention
  2003-08-22 16:19 ` Linus Torvalds
@ 2003-08-22 17:39   ` David Howells
  2003-08-22 18:38     ` Linus Torvalds
  0 siblings, 1 reply; 10+ messages in thread
From: David Howells @ 2003-08-22 17:39 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: linux-kernel


Linus Torvalds <torvalds@osdl.org> 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

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: authentication / encryption key retention
  2003-08-22 17:39   ` David Howells
@ 2003-08-22 18:38     ` Linus Torvalds
  2003-08-26 10:12       ` David Howells
  2003-08-27 15:31       ` [PATCH/RFC] " David Howells
  0 siblings, 2 replies; 10+ messages in thread
From: Linus Torvalds @ 2003-08-22 18:38 UTC (permalink / raw)
  To: David Howells; +Cc: linux-kernel


On Fri, 22 Aug 2003, David Howells wrote:
> 
> > 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.

I would think we can do this by just having a well-known search order. For
example, if we just end up with a "tree of buckets", we just make the
search order be depth-first, and you can this pick and choose which
buckets you access first by just ordering how you add a new bucket.

But I think it ends up being a secondary issue, because _most_ of the time 
you're likely to have just one key that "fits the lock".

So I wouldn't worry about it _too_ much, at least until we have code that 
works and is used by multiple subsystems.

>  (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 you have to return an error in the general case. You may have done 
actions with the key that are hard to just move over to another one.

Again, if that ends up being a real problem, maybe the solution is just 
"don't do that then". Or maybe the solution ends up being to have 
infrastructure to switch keys on the fly. 

But don't overdesign. Start from a small set of basic requirments, and 
make a simple implementation. Go from there.

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

No, usually it would just search the keyring, and increment the reference
count for the key it finds (or the whole bucket. Or a sub-bucket. Some
subsystems might want to just say "I want it all" - it's the only sane
thing to do if the subsystem really decides that it wants to be able to
switch keys around on failure, I suspect).

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

Yes, but those filesystems will NOT use the "extended key" functionality 
at all.

It would be silly to make existing ext2 users use the new keys. They'll 
just use "uid" and "gid" like they always have been.

Purists go "hey, that's having two totally different mechanisms for the 
same thing - security". But sane people go "why complicate the issue 
unnecessarily".

> 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

I'd actually suggest keeping the uid/gid keyrings off the "process 
keyring".

So each process would have a a few different keyrings:

 - the process-local one: "current->keyring"

 - the UID-local one: either "current->fsuid->keyring" (ie maintaining a 
   "fsuid" structure pointer over fsuid changes) or just having a hash 
   function from "uid" -> "keyring"

 - the group-local one: "current->group->keyring" or "gid" -> "keyring" 
   hash function.

and they'd be all separate (they could have overlap, of course, but the
difference is that this makes it very easy to change groups and uid's:  
such changes would _not_ touch the "process-local" keys at all).

So part of the search order would be the order these are used in looking 
stuff up. My "obvious" order would be to do process-local first, then 
user-local, and finally group-local.

But some parts of the kernel might look at only the process-local keys 
("does this process have rights to do that?") or look at other localities 
("does this socket have the keys to do that?") entirely.

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

Yes. You definitely want to have an extended "id" program that allows you 
to see the descriptions and types.

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

No, you can certainly pass subsets too, by just having a "create a new 
bucket that contains these other buckets" (which you got from the lookup 
operation). You never see what the actual keys are, but each bucket and 
each key do have an ID field, so you can create buckets by reference that 
way.

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

Who not just make ACL's part of the "bucket of keys", but make them
basically "conditional keys" (in the "type field", the same way we
distinguish between buckets and keys). A conditional key is a key in
itself, but it's a requirement thing: you can do this operation on the
tree of keys only if you also have access to the non-conditional version
of this key.

I think it would be a mistake to have _another_ level of keys/ACL's just
to keep track of what keys you can modify. "Quid custodiet ipsos
custodies?". Where would it end?

Having the tree-of-keys have branches that can be conditional on another 
key solves the problem. It would be a "you can only add this keyring to 
your tree if you already have that other key" kind of thing. All 
self-contained within the architecture.

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

Well, that was how the previous key management approach worked. I just 
wanted to make clear that this was one reason I considered it unusable.

		Linus



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: authentication / encryption key retention
  2003-08-22 18:38     ` Linus Torvalds
@ 2003-08-26 10:12       ` David Howells
  2003-08-26 15:30         ` Alan Cox
                           ` (2 more replies)
  2003-08-27 15:31       ` [PATCH/RFC] " David Howells
  1 sibling, 3 replies; 10+ messages in thread
From: David Howells @ 2003-08-26 10:12 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: linux-kernel


So each process should have three keyrings:

	- Process level (inherited over fork/exec, but not SUID)
	- UID level (fixed to whichever UID a process currently is)
	- GID level (fixed to whichever GID a process currently is)

So the search would proceed through the above list top-down, depth first (each
key can itself be another keyring).

But what about:

	- setuid() and related UID syscalls?
	- setgid() and related GID syscalls?
	- SGID programs?
	- The GROUPS list? Each GID there could have a keyring...

I think changes to UID should cause the process level keyring to be exchanged
for a new empty one, but changes in GID/GROUPS should not. Also I think GROUPS
list specified keyrings can be ignored for now.

> No, usually it would just search the keyring, and increment the reference
> count for the key it finds

If your subset is but a single pre-existing key (where this includes a keyring
as a specialisation of a key), then yes; but if the filesystem is permitted to
select multiple keys from different sources, then no.

I think for the moment, however, we can assume there will be a single key.

> Yes, but those filesystems will NOT use the "extended key" functionality 
> at all.

Fair enough.

> Purists go "hey, that's having two totally different mechanisms for the 
> same thing - security". But sane people go "why complicate the issue 
> unnecessarily".

Because not doing so complicates things in other ways. But don't lets worry
about that.

> But some parts of the kernel might look at only the process-local keys 
> ("does this process have rights to do that?")

Why? If a process has access to appropriate UID-level or GID-level keys, then
surely it has the rights to do "that", even if it hasn't copied them into its
process level keyring...

> > 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.
> 
> No, you can certainly pass subsets too, by just having a "create a new 
> bucket that contains these other buckets" (which you got from the lookup 
> operation). You never see what the actual keys are, but each bucket and 
> each key do have an ID field, so you can create buckets by reference that 
> way.

Hmmm... if I'm using these keys for AFS, say, then I don't want to have to
search a complete tree of keyrings every time I perform secure operation
(every RxRPC operation possibly). I would like to have the applicable keys
immediately to hand - and only those keys - with as little structure as
possible.

> > I think an ACL is a must (as I've said before). The ACL should provide the
> > following rights:
> 
> Who not just make ACL's part of the "bucket of keys", but make them
> basically "conditional keys" (in the "type field", the same way we
> distinguish between buckets and keys). A conditional key is a key in
> itself, but it's a requirement thing: you can do this operation on the
> tree of keys only if you also have access to the non-conditional version
> of this key.

Um. I'm not sure that what you describe is of much use... I'm not sure I
understand correctly what your suggestion is. Can you give an example please?

> I think it would be a mistake to have _another_ level of keys/ACL's just
> to keep track of what keys you can modify. "Quid custodiet ipsos
> custodies?". Where would it end?

Exactly there. The ACL would contain a right to (dis)allow modifications to
itself.

Actually, it's more a problem of controlling subscription to arbitrarily
subscribable "named" keys.

For example, say that a process (owned by dwmw2) says "give me access to the
keyring named 'dhowells-session-1275'", then what's to prohibit it from
gaining access to all my keys _unless_ I can add an ACL?

Of course, access control could also be performed UID/GID and mode_t settings,
but that's just a very simple ACL.

> Having the tree-of-keys have branches that can be conditional on another 
> key solves the problem. It would be a "you can only add this keyring to 
> your tree if you already have that other key" kind of thing. All 
> self-contained within the architecture.

But how do you get that other key in the first place?

This sounds a lot more complicated than it needs to be, and slow... To perform
subscription, you then have to search the process's three keyrings for a match
for every conditional key in the target tree...

David

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: authentication / encryption key retention
  2003-08-26 10:12       ` David Howells
@ 2003-08-26 15:30         ` Alan Cox
  2003-08-26 16:07         ` David Howells
  2003-08-26 16:26         ` Stephen Smalley
  2 siblings, 0 replies; 10+ messages in thread
From: Alan Cox @ 2003-08-26 15:30 UTC (permalink / raw)
  To: David Howells; +Cc: Linus Torvalds, Linux Kernel Mailing List

On Maw, 2003-08-26 at 11:12, David Howells wrote:
> If your subset is but a single pre-existing key (where this includes a keyring
> as a specialisation of a key), then yes; but if the filesystem is permitted to
> select multiple keys from different sources, then no.
> 
> I think for the moment, however, we can assume there will be a single key.

How is the key choice different to say selinux roles ?


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: authentication / encryption key retention
  2003-08-26 10:12       ` David Howells
  2003-08-26 15:30         ` Alan Cox
@ 2003-08-26 16:07         ` David Howells
  2003-08-26 16:26           ` Valdis.Kletnieks
  2003-08-26 16:26         ` Stephen Smalley
  2 siblings, 1 reply; 10+ messages in thread
From: David Howells @ 2003-08-26 16:07 UTC (permalink / raw)
  To: Alan Cox; +Cc: Linus Torvalds, Linux Kernel Mailing List


> On Maw, 2003-08-26 at 11:12, David Howells wrote:
> > If your subset is but a single pre-existing key (where this includes a
> > keyring as a specialisation of a key), then yes; but if the filesystem is
> > permitted to select multiple keys from different sources, then no.
> > 
> > I think for the moment, however, we can assume there will be a single key.
> 
> How is the key choice different to say selinux roles ?

I'm not sure. After a quick overview of SE Linux, it doesn't seem that it
would be able to support this.

David

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: authentication / encryption key retention
  2003-08-26 10:12       ` David Howells
  2003-08-26 15:30         ` Alan Cox
  2003-08-26 16:07         ` David Howells
@ 2003-08-26 16:26         ` Stephen Smalley
  2 siblings, 0 replies; 10+ messages in thread
From: Stephen Smalley @ 2003-08-26 16:26 UTC (permalink / raw)
  To: David Howells; +Cc: Linus Torvalds, lkml

On Tue, 2003-08-26 at 06:12, David Howells wrote:
> > But some parts of the kernel might look at only the process-local keys 
> > ("does this process have rights to do that?")
> 
> Why? If a process has access to appropriate UID-level or GID-level keys, then
> surely it has the rights to do "that", even if it hasn't copied them into its
> process level keyring...

Because not every process that runs under your identity should have your
full set of rights.  You should be able to confine a given process based
on more than just the associated user identity, e.g. the role and
clearance in which the user is operating, the function and
trustworthiness of the program that the process is executing, etc. 
Otherwise, you have no protection against flawed or malicious programs
executed from any of your sessions.

-- 
Stephen Smalley <sds@epoch.ncsc.mil>
National Security Agency


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: authentication / encryption key retention
  2003-08-26 16:07         ` David Howells
@ 2003-08-26 16:26           ` Valdis.Kletnieks
  0 siblings, 0 replies; 10+ messages in thread
From: Valdis.Kletnieks @ 2003-08-26 16:26 UTC (permalink / raw)
  To: David Howells; +Cc: Alan Cox, Linus Torvalds, Linux Kernel Mailing List

[-- Attachment #1: Type: text/plain, Size: 598 bytes --]

On Tue, 26 Aug 2003 17:07:03 BST, David Howells said:
> > <Alan Cox said, full attribution lost>
> > > On Maw, 2003-08-26 at 11:12, David Howells wrote:
>  > > I think for the moment, however, we can assume there will be a single key.
> > How is the key choice different to say selinux roles ?
> 
> I'm not sure. After a quick overview of SE Linux, it doesn't seem that it
> would be able to support this.

Well, selinux *does* support the concept of a "role" - Alan's question was
whether it's safe to assume that  "a single key" is a safe assumption, or
if it's reasonable to have multiple keys.

[-- Attachment #2: Type: application/pgp-signature, Size: 226 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH/RFC] authentication / encryption key retention
  2003-08-22 18:38     ` Linus Torvalds
  2003-08-26 10:12       ` David Howells
@ 2003-08-27 15:31       ` David Howells
  1 sibling, 0 replies; 10+ messages in thread
From: David Howells @ 2003-08-27 15:31 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: linux-kernel


Hi Linus,

Here's an incomplete patch that introduces basic key retention services into
the kernel.

What it does:

	- manages key creation / destruction
	- manages keyrings as special keys
	- creates a default UID keyring attached to user_struct
	- permits a per-thread keyring that isn't inherited across fork, clone
	  or execve
	- creates a per-process keyring that is inherited across clone +
	  CLONE_THREAD, but is not inherited across fork, clone or execve
	- creates a "per-session" keyring that is inherited across fork, clone
	  and exec (non-SUID). this is also cleared when switch_uid() is
	  called.
	- permits the set of system keys to be viewed in /proc/keys
	- assigns every key a unique ID

What it doesn't yet do or hasn't yet been tested:

	- GID default keyrings
	- key addition and retirement
	- key searching
	- keyring subscription

David


diff -uNr linux-2.6.0-test4/fs/exec.c linux-2.6.0-test4-keys/fs/exec.c
--- linux-2.6.0-test4/fs/exec.c	2003-08-26 14:04:44.000000000 +0100
+++ linux-2.6.0-test4-keys/fs/exec.c	2003-08-27 15:00:11.000000000 +0100
@@ -45,6 +45,7 @@
 #include <linux/mount.h>
 #include <linux/security.h>
 #include <linux/rmap-locking.h>
+#include <linux/key.h>
 
 #include <asm/uaccess.h>
 #include <asm/pgalloc.h>
@@ -889,10 +890,15 @@
 
 void compute_creds(struct linux_binprm *bprm) 
 {
+	if (bprm->e_uid != current->uid)
+		suid_keys(current);
+	exec_keys(current);
+
 	task_lock(current);
 	if (bprm->e_uid != current->uid || bprm->e_gid != current->gid) {
                 current->mm->dumpable = 0;
-		
+		suid_keys(current);
+
 		if (must_not_trace_exec(current)
 		    || atomic_read(&current->fs->count) > 1
 		    || atomic_read(&current->files->count) > 1
diff -uNr linux-2.6.0-test4/include/linux/key.h linux-2.6.0-test4-keys/include/linux/key.h
--- linux-2.6.0-test4/include/linux/key.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.0-test4-keys/include/linux/key.h	2003-08-27 15:35:49.000000000 +0100
@@ -0,0 +1,123 @@
+/* key.h: authentication token and access key management
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_KEY_H
+#define _LINUX_KEY_H
+
+#include <linux/types.h>
+#include <linux/rbtree.h>
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+
+#define KEY_DEBUGGING
+
+struct seq_file;
+
+struct key;
+struct key_type;
+struct keyring_list;
+struct keyring_name;
+
+/*****************************************************************************/
+/*
+ * authentication token / access credential / keyring
+ * - types of key include:
+ *   - keyrings
+ *   - disk encryption IDs
+ *   - Kerberos TGTs
+ */
+struct key {
+	atomic_t		usage;
+	unsigned		serial;		/* key serial number (0 if retired) */
+	struct rb_node		serial_node;
+	struct key_type		*type;		/* type of key */
+	rwlock_t		lock;
+	unsigned		flags;
+#define KEY_FLAG_DEAD		0x00000001	/* set if key is deleted */
+#define KEY_FLAG_RETIRED	0x00000002	/* set if key is retired from service */
+#define KEY_FLAG_PUBLIC_KEYRING	0x00000004	/* set if publicly subscribable keyring */
+
+#ifdef KEY_DEBUGGING
+	unsigned		magic;
+#define KEY_DEBUG_MAGIC		0x18273645u
+#define KEY_DEBUG_MAGIC_X	0xf8e9dacbu
+#endif
+
+	union {
+		struct keyring_name	*ringname;
+		void			*data;
+	} description;
+
+	union {
+		void			*data;
+		struct keyring_list	*subscriptions;
+	} payload;
+};
+
+struct key_type {
+	const char		*name;		/* name of type */
+
+	int (*match)(const struct key *key, const void *criterion);
+	void (*clear)(struct key *key);
+	void (*describe)(const struct key *key, struct seq_file *p);
+};
+
+extern int key_alloc(struct key_type *type, struct key **_key);
+extern void key_retire(struct key *key);
+extern void key_put(struct key *key);
+
+extern int keyring_alloc(struct key *source, struct key **_key);
+
+/*****************************************************************************/
+/*
+ * list of subscribed keys
+ * - when plumbing the depths of the key tree, there's a hard limit set on the
+ *   depth to deal with cycles in the tree
+ */
+struct keyring_list {
+	unsigned		maxkeys;	/* max keys this list can hold */
+	unsigned		nkeys;		/* number of keys this list currently holds */
+	struct key		*keys[0];
+};
+
+#define KEYRING_SEARCH_MAX_DEPTH 6
+
+/*****************************************************************************/
+/*
+ * name of keyring
+ * - publicly available keyrings must be labelled uniquely
+ */
+struct keyring_name {
+	struct key		*keyring;	/* keyring key */
+	struct rb_node		name_node;	/* node in tree of public names */
+	char			name[0];	/* label for this keyring */
+};
+
+extern int keyring_search(struct key *keyring,
+			  const struct key_type *type,
+			  const void *criterion,
+			  struct key **_key);
+
+extern int keyring_filter(struct key *source,
+			  int (*func)(struct key *key, void *criterion),
+			  void *criterion,
+			  struct key **_subset);
+
+extern int keyring_set_name(struct key *keyring, const char *fmt, ...)
+__attribute__((format(printf, 2, 3)));
+
+extern struct key root_user_keyring;
+extern int copy_keys(unsigned long clone_flags, struct task_struct *tsk);
+extern void exit_keys(struct task_struct *tsk);
+extern int suid_keys(struct task_struct *tsk);
+extern int exec_keys(struct task_struct *tsk);
+
+#endif /* _LINUX_KEY_H */
diff -uNr linux-2.6.0-test4/include/linux/sched.h linux-2.6.0-test4-keys/include/linux/sched.h
--- linux-2.6.0-test4/include/linux/sched.h	2003-08-26 14:04:47.000000000 +0100
+++ linux-2.6.0-test4-keys/include/linux/sched.h	2003-08-27 13:05:43.000000000 +0100
@@ -290,6 +290,8 @@
 	atomic_t processes;	/* How many processes does this user have? */
 	atomic_t files;		/* How many open files does this user have? */
 
+	struct key *keyring;	/* UID specific keyring */
+
 	/* Hash table maintenance information */
 	struct list_head uidhash_list;
 	uid_t uid;
@@ -402,6 +404,9 @@
 	kernel_cap_t   cap_effective, cap_inheritable, cap_permitted;
 	int keep_capabilities:1;
 	struct user_struct *user;
+	struct key *session_keyring;	/* keyring inherited over fork */
+	struct key *process_keyring;	/* keyring private to this process (CLONE_THREAD) */
+	struct key *thread_keyring;	/* keyring private to this thread */
 /* limits */
 	struct rlimit rlim[RLIM_NLIMITS];
 	unsigned short used_math;
diff -uNr linux-2.6.0-test4/kernel/exit.c linux-2.6.0-test4-keys/kernel/exit.c
--- linux-2.6.0-test4/kernel/exit.c	2003-08-26 13:27:21.000000000 +0100
+++ linux-2.6.0-test4-keys/kernel/exit.c	2003-08-27 13:09:01.000000000 +0100
@@ -22,6 +22,7 @@
 #include <linux/profile.h>
 #include <linux/mount.h>
 #include <linux/proc_fs.h>
+#include <linux/key.h>
 
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
@@ -711,6 +712,7 @@
 	exit_namespace(tsk);
 	exit_itimers(tsk);
 	exit_thread();
+	exit_keys(tsk);
 
 	if (tsk->leader)
 		disassociate_ctty(1);
diff -uNr linux-2.6.0-test4/kernel/fork.c linux-2.6.0-test4-keys/kernel/fork.c
--- linux-2.6.0-test4/kernel/fork.c	2003-08-26 14:04:48.000000000 +0100
+++ linux-2.6.0-test4-keys/kernel/fork.c	2003-08-27 13:08:54.000000000 +0100
@@ -30,6 +30,7 @@
 #include <linux/futex.h>
 #include <linux/ptrace.h>
 #include <linux/mount.h>
+#include <linux/key.h>
 
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>
@@ -884,8 +885,10 @@
 		goto bad_fork_cleanup_sighand;
 	if ((retval = copy_mm(clone_flags, p)))
 		goto bad_fork_cleanup_signal;
-	if ((retval = copy_namespace(clone_flags, p)))
+	if ((retval = copy_keys(clone_flags, p)))
 		goto bad_fork_cleanup_mm;
+	if ((retval = copy_namespace(clone_flags, p)))
+		goto bad_fork_cleanup_keys;
 	retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
 	if (retval)
 		goto bad_fork_cleanup_namespace;
@@ -1021,6 +1024,8 @@
 
 bad_fork_cleanup_namespace:
 	exit_namespace(p);
+bad_fork_cleanup_keys:
+	exit_keys(p);
 bad_fork_cleanup_mm:
 	exit_mm(p);
 bad_fork_cleanup_signal:
diff -uNr linux-2.6.0-test4/kernel/key.c linux-2.6.0-test4-keys/kernel/key.c
--- linux-2.6.0-test4/kernel/key.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.0-test4-keys/kernel/key.c	2003-08-27 16:12:59.000000000 +0100
@@ -0,0 +1,754 @@
+/* key.c: authentication token and access key management
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/key.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <asm/errno.h>
+
+static kmem_cache_t	*key_jar;
+static struct rb_root	key_serial_tree;
+static unsigned		key_serial_next = 2;
+static spinlock_t	key_serial_lock = SPIN_LOCK_UNLOCKED;
+
+static struct rb_root	keyring_name_tree;
+static rwlock_t		keyring_name_lock = RW_LOCK_UNLOCKED;
+
+static int keyring_match(const struct key *key, const void *criterion);
+static void keyring_clear(struct key *key);
+static void keyring_describe(const struct key *keyring, struct seq_file *m);
+
+static struct key_type key_type_keyring = {
+	.name		= "keyring",
+	.match		= keyring_match,
+	.clear		= keyring_clear,
+	.describe	= keyring_describe,
+};
+
+struct key root_user_keyring = {
+	.usage			= ATOMIC_INIT(1),
+	.serial			= 1,
+	.type			= &key_type_keyring,
+	.lock			= RW_LOCK_UNLOCKED,
+#ifdef KEY_DEBUGGING
+	.magic			= KEY_DEBUG_MAGIC,
+#endif
+};
+
+#ifdef CONFIG_PROC_FS
+static int key_proc_open(struct inode *inode, struct file *file);
+static void *key_proc_start(struct seq_file *p, loff_t *_pos);
+static void *key_proc_next(struct seq_file *p, void *v, loff_t *_pos);
+static void key_proc_stop(struct seq_file *p, void *v);
+static int key_proc_show(struct seq_file *m, void *v);
+
+static struct seq_operations key_proc_ops = {
+	.start	= key_proc_start,
+	.next	= key_proc_next,
+	.stop	= key_proc_stop,
+	.show	= key_proc_show,
+};
+
+static struct file_operations key_proc_fops = {
+	.open		= key_proc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+#endif
+
+#ifdef KEY_DEBUGGING
+static void __key_validate(const struct key *key)
+{
+	printk("__key_validate: key %p {%08x} should be {%08x}\n",
+	       key, key->magic, KEY_DEBUG_MAGIC);
+	BUG();
+}
+static inline void key_validate(const struct key *key)
+{
+	if (key && key->magic != KEY_DEBUG_MAGIC)
+		__key_validate(key);
+}
+#else
+static inline void key_validate(const struct key *key) {}
+#endif
+
+/*****************************************************************************/
+/*
+ * allocate a key of the specified type
+ * - the key is provided with a unique serial number
+ */
+int key_alloc(struct key_type *type, struct key **_key)
+{
+	struct rb_node *parent, **p;
+	struct key *key, *xkey;
+
+	*_key = NULL;
+
+	key = kmem_cache_alloc(key_jar, SLAB_KERNEL);
+	if (!key)
+		return -ENOMEM;
+
+	memset(key, 0, sizeof(*key));
+
+	atomic_set(&key->usage, 1);
+	rwlock_init(&key->lock);
+	key->type = type;
+#ifdef KEY_DEBUGGING
+	key->magic = KEY_DEBUG_MAGIC;
+#endif
+
+	spin_lock(&key_serial_lock);
+
+	/* propose a serial number and try to insert it into the tree */
+	if (key_serial_next < 1)
+		key_serial_next = 1;
+	key->serial = key_serial_next++;
+
+	parent = NULL;
+	p = &key_serial_tree.rb_node;
+
+	while (*p) {
+		parent = *p;
+		xkey = rb_entry(parent, struct key, serial_node);
+
+		if (key->serial < xkey->serial)
+			p = &(*p)->rb_left;
+		else if (key->serial > xkey->serial)
+			p = &(*p)->rb_right;
+		else
+			goto serial_exists;
+	}
+	goto insert_here;
+
+	/* we found a key with the proposed serial number - walk the tree from
+	 * that point looking for the next unused serial number */
+ serial_exists:
+	for (;;) {
+		if (key_serial_next < 1)
+			key_serial_next = 1;
+		key->serial = key_serial_next++;
+
+		if (!parent->rb_parent)
+			p = &key_serial_tree.rb_node;
+		else if (parent->rb_parent->rb_left == parent)
+			p = &parent->rb_parent->rb_left;
+		else
+			p = &parent->rb_parent->rb_right;
+
+		parent = rb_next(parent);
+		if (!parent)
+			break;
+
+		xkey = rb_entry(parent, struct key, serial_node);
+		if (key->serial < xkey->serial)
+			goto insert_here;
+	}
+
+ insert_here:
+	rb_link_node(&key->serial_node, parent, p);
+	rb_insert_color(&key->serial_node, &key_serial_tree);
+	spin_unlock(&key_serial_lock);
+
+	*_key = key;
+	return 0;
+} /* end key_alloc() */
+
+EXPORT_SYMBOL(key_alloc);
+
+/*****************************************************************************/
+/*
+ * dispose of a key
+ */
+void key_put(struct key *key)
+{
+	if (!key)
+		return;
+
+	key_validate(key);
+	if (atomic_dec_and_lock(&key->usage, &key_serial_lock)) {
+		key->flags |= KEY_FLAG_DEAD;
+		rb_erase(&key->serial_node, &key_serial_tree);
+		spin_unlock(&key_serial_lock);
+
+		key->type->clear(key);
+#ifdef KEY_DEBUGGING
+		key->magic = KEY_DEBUG_MAGIC_X;
+#endif
+		kmem_cache_free(key_jar, key);
+	}
+
+} /* end key_put() */
+
+EXPORT_SYMBOL(key_put);
+
+/*****************************************************************************/
+/*
+ * retire a key from service
+ */
+void key_retire(struct key *key)
+{
+	key_validate(key);
+	write_lock(&key->lock);
+	key->flags |= KEY_FLAG_RETIRED;
+	write_unlock(&key->lock);
+} /* end key_retire() */
+
+EXPORT_SYMBOL(key_retire);
+
+/*****************************************************************************/
+/*
+ * allocate or duplicate a keyring
+ * - the new keyring does not get a name attached, even if duplicated
+ */
+int keyring_alloc(struct key *source, struct key **_key)
+{
+	struct keyring_list *sklist, *klist;
+	struct key *keyring;
+	unsigned max;
+	size_t size;
+	int ret, loop;
+
+	ret = key_alloc(&key_type_keyring, _key);
+	if (ret < 0 || !source)
+		return ret;
+
+	keyring = *_key;
+	key_validate(keyring);
+
+	/* duplicate the list of subscribed keys */
+	if (source->payload.subscriptions) {
+		const unsigned limit =
+			(PAGE_SIZE - sizeof(*klist)) / sizeof(struct key);
+
+		max = 0;
+		klist = NULL;
+
+		read_lock(&source->lock);
+
+		/* we need to take care here as some other process may
+		 * resize the list under us when we drop the spinlock
+		 * to perform the allocation
+		 */
+		while (sklist = source->payload.subscriptions,
+		       sklist && sklist->nkeys > 0 && max < sklist->nkeys
+		       ) {
+			max = sklist->nkeys;
+			read_unlock(&source->lock);
+
+			BUG_ON(max > limit);
+
+			max = (max + 3) & ~3;
+			if (max > limit)
+				max = limit;
+
+			size = sizeof(*klist) + sizeof(struct key) * max;
+			klist = kmalloc(size, GFP_KERNEL);
+			if (!klist)
+				goto nomem;
+			memset(klist, 0, size);
+			klist->maxkeys = max;
+
+			read_lock(&source->lock);
+		}
+
+		if (sklist && sklist->nkeys) {
+			klist->nkeys = sklist->nkeys;
+			memcpy(klist->keys, sklist->keys,
+			       sklist->nkeys * sizeof(struct key));
+
+			for (loop = klist->nkeys - 1; loop >= 0; loop--)
+				atomic_inc(&klist->keys[loop]->usage);
+
+			keyring->payload.subscriptions = klist;
+		}
+		else if (klist) {
+			kfree(klist);
+		}
+
+		read_unlock(&source->lock);
+	}
+
+	return ret;
+
+ nomem:
+	key_put(keyring);
+	*_key = NULL;
+	return -ENOMEM;
+} /* end keyring_alloc() */
+
+EXPORT_SYMBOL(keyring_alloc);
+
+/*****************************************************************************/
+/*
+ * search the supplied keyring tree for a key that matches the criterion
+ * - perform a depth-first search up to the prescribed limit
+ */
+int keyring_search(struct key *keyring,
+		   const struct key_type *type,
+		   const void *criterion,
+		   struct key **_key)
+{
+	struct {
+		struct key *keyring;
+		struct keyring_list *keylist;
+		int kix;
+	} stack[KEYRING_SEARCH_MAX_DEPTH];
+
+	struct keyring_list *keylist;
+	struct key *key;
+	int sp, psp, kix;
+
+	key_validate(keyring);
+
+	*_key = NULL;
+
+	if (keyring->type != &key_type_keyring)
+		return -EINVAL;
+
+	sp = 0;
+
+ descend:
+	read_lock(&keyring->lock);
+	if (keyring->flags & KEY_FLAG_RETIRED)
+		goto retired;
+
+	keylist = keyring->payload.subscriptions;
+	kix = 0;
+
+ ascend:
+	while (kix < keylist->nkeys) {
+		key = keylist->keys[kix];
+
+		if (key->type == type && key->type->match(key, criterion)) {
+			if (keyring->flags & KEY_FLAG_RETIRED)
+				goto next;
+			atomic_inc(&key->usage);
+			goto found;
+		}
+
+		if (key->type == &key_type_keyring) {
+			if (sp >= KEYRING_SEARCH_MAX_DEPTH)
+				goto next;
+
+			/* evade loops in the keyring tree */
+			for (psp = 0; psp < sp; psp++)
+				if (stack[psp].keyring == keyring)
+					goto next;
+
+			stack[sp].keyring = keyring;
+			stack[sp].keylist = keylist;
+			stack[sp].kix = kix;
+			sp++;
+			goto descend;
+		}
+
+	next:
+		kix++;
+	}
+
+ retired:
+	read_unlock(&keyring->lock);
+
+	if (sp > 0) {
+		sp--;
+		keyring = stack[sp].keyring;
+		keylist = stack[sp].keylist;
+		kix = stack[sp].kix + 1;
+		goto ascend;
+	}
+
+	return -ENOENT;
+
+ found:
+	read_unlock(&keyring->lock);
+
+	for (; sp > 0; sp--)
+		read_unlock(&stack[sp].keyring->lock);
+
+	key_validate(key);
+
+	*_key = key;
+	return 0;
+} /* end keyring_search() */
+
+EXPORT_SYMBOL(keyring_search);
+
+/*****************************************************************************/
+/*
+ * add a key to a keyring
+ */
+int keyring_add_key(struct key *keyring, struct key *key)
+{
+	struct keyring_list *klist, *xklist;
+	unsigned max;
+	size_t size;
+
+	key_validate(keyring);
+	key_validate(key);
+
+	max = 0;
+	xklist = NULL;
+	write_lock(&keyring->lock);
+
+	if (keyring->flags & KEY_FLAG_RETIRED)
+		goto retired;
+
+	klist = keyring->payload.subscriptions;
+	if (klist) {
+		if (klist->maxkeys > klist->nkeys) {
+			atomic_inc(&key->usage);
+			klist->keys[klist->nkeys++] = key;
+			write_unlock(&keyring->lock);
+			return 0;
+		}
+
+		max = klist->maxkeys;
+	}
+
+	write_unlock(&keyring->lock);
+
+	/* try to grow the key list */
+ again:
+	max += 4;
+	size = sizeof(*klist) + sizeof(*key) * max;
+	if (size > PAGE_SIZE)
+		return -ENFILE;
+
+	xklist = kmalloc(size, GFP_KERNEL);
+	if (!xklist)
+		return -ENOMEM;
+	memset(xklist, 0, size);
+	xklist->maxkeys = max;
+
+	write_lock(&keyring->lock);
+
+	if (keyring->flags & KEY_FLAG_RETIRED)
+		goto retired;
+
+	klist = keyring->payload.subscriptions;
+	if (klist) {
+		if (max <= klist->nkeys)
+			goto keyring_unexpectedly_resized;
+
+		xklist->nkeys = klist->nkeys;
+		memcpy(xklist->keys, klist->keys, sizeof(*key) * klist->nkeys);
+	}
+
+	atomic_inc(&key->usage);
+	xklist->keys[xklist->nkeys++] = key;
+	keyring->payload.subscriptions = xklist;
+	write_unlock(&keyring->lock);
+
+	if (klist)
+		kfree(klist);
+	return 0;
+
+	/* some other process changed the number of keys in the list */
+ keyring_unexpectedly_resized:
+	max = klist->maxkeys;
+	write_unlock(&keyring->lock);
+	kfree(xklist);
+	goto again;
+
+ retired:
+	write_unlock(&keyring->lock);
+	if (xklist)
+		kfree(xklist);
+	return -EINVAL;
+
+} /* end keyring_add_key() */
+
+EXPORT_SYMBOL(keyring_add_key);
+
+/*****************************************************************************/
+/*
+ * set the name of a keyring
+ */
+int keyring_set_name(struct key *keyring, const char *fmt, ...)
+{
+	struct keyring_name *kname;
+	va_list va;
+	size_t len;
+	char buf[32];
+
+	key_validate(keyring);
+
+	va_start(va, fmt);
+	vsnprintf(buf, 32, fmt, va);
+	va_end(va);
+	len = strlen(buf);
+
+	kname = kmalloc(sizeof(*kname) + len + 1, GFP_KERNEL);
+	if (!kname)
+		return -ENOMEM;
+	memset(kname, 0, sizeof(*kname));
+	memcpy(kname->name, buf, len + 1);
+	kname->keyring = keyring;
+	keyring->description.ringname = kname;
+
+	return 0;
+} /* end keyring_set_name() */
+
+EXPORT_SYMBOL(keyring_set_name);
+
+/*****************************************************************************/
+/*
+ * keyrings aren't currently matchable
+ */
+static int keyring_match(const struct key *keyring, const void *criterion)
+{
+	struct keyring_name *kname;
+
+	kname = keyring->description.ringname;
+	if (kname)
+		if (strcmp(kname->name, criterion) == 0)
+			return 1;
+
+	return 0;
+} /* end keyring_match() */
+
+/*****************************************************************************/
+/*
+ * dispose of the data dangling from the corpse of a keyring
+ */
+static void keyring_clear(struct key *keyring)
+{
+	struct keyring_name *kname;
+	struct keyring_list *klist;
+	int loop;
+
+	kname = keyring->description.ringname;
+	if (kname) {
+		if (keyring->flags & KEY_FLAG_PUBLIC_KEYRING) {
+			write_lock(&keyring_name_lock);
+			rb_erase(&kname->name_node, &keyring_name_tree);
+			write_unlock(&keyring_name_lock);
+		}
+
+		kfree(kname);
+	}
+
+	klist = keyring->payload.subscriptions;
+	if (klist) {
+		for (loop = klist->nkeys - 1; loop >= 0; loop--)
+			key_put(klist->keys[loop]);
+		kfree(klist);
+	}
+
+} /* end keyring_clear() */
+
+/*****************************************************************************/
+/*
+ * describe the keyring
+ */
+static void keyring_describe(const struct key *keyring, struct seq_file *m)
+{
+	struct keyring_name *kname;
+	struct keyring_list *klist;
+
+	kname = keyring->description.ringname;
+	if (kname) {
+		seq_printf(m, "%s", kname->name);
+	}
+	else {
+		seq_puts(m, "[anon]");
+	}
+
+	klist = keyring->payload.subscriptions;
+	if (klist)
+		seq_printf(m, ": %u/%u", klist->nkeys, klist->maxkeys);
+	else
+		seq_puts(m, ": empty");
+
+} /* end keyring_describe() */
+
+/*****************************************************************************/
+/*
+ * initialise the key management stuff
+ */
+static int __init key_init(void)
+{
+	struct proc_dir_entry *p;
+
+	key_jar = kmem_cache_create("key_jar", sizeof(struct key),
+				    0,
+				    SLAB_HWCACHE_ALIGN, NULL, NULL);
+	if (!key_jar)
+		panic("Cannot create key jar\n");
+
+	p = create_proc_entry("keys", 0, NULL);
+	if (!p)
+		panic("Cannot create /proc/keys\n");
+
+	p->proc_fops = &key_proc_fops;
+
+	key_validate(&root_user_keyring);
+	rb_link_node(&root_user_keyring.serial_node, NULL, &key_serial_tree.rb_node);
+	rb_insert_color(&root_user_keyring.serial_node, &key_serial_tree);
+	keyring_set_name(&root_user_keyring, "_uid.0");
+
+	return 0;
+} /* end key_init() */
+
+subsys_initcall(key_init);
+
+/*****************************************************************************/
+/*
+ * copy the keys for fork
+ */
+int copy_keys(unsigned long clone_flags, struct task_struct *tsk)
+{
+	int ret = 0;
+
+	key_validate(tsk->session_keyring);
+	key_validate(tsk->process_keyring);
+	key_validate(tsk->thread_keyring);
+
+	if (tsk->session_keyring)
+		atomic_inc(&tsk->session_keyring->usage);
+
+	if (tsk->process_keyring) {
+		if (clone_flags & CLONE_THREAD) {
+			atomic_inc(&tsk->process_keyring->usage);
+		}
+		else {
+			ret = keyring_alloc(NULL, &tsk->process_keyring);
+			if (ret == 0)
+				ret = keyring_set_name(tsk->process_keyring,
+						       "_pid.%d", tsk->tgid);
+		}
+	}
+
+	tsk->thread_keyring = NULL;
+
+	return ret;
+} /* end copy_keys() */
+
+/*****************************************************************************/
+/*
+ * dispose of keys upon exit
+ */
+void exit_keys(struct task_struct *tsk)
+{
+	key_put(tsk->session_keyring);
+	key_put(tsk->process_keyring);
+	key_put(tsk->thread_keyring);
+
+} /* end exit_keys() */
+
+/*****************************************************************************/
+/*
+ * deal with SUID programs and setuid()/setreuid()/setresuid()
+ */
+int suid_keys(struct task_struct *tsk)
+{
+	struct key *keyring = xchg(&tsk->session_keyring, NULL);
+	if (keyring) {
+		key_put(keyring);
+
+		if (keyring_alloc(NULL, &tsk->session_keyring) < 0)
+			return -ENOMEM;
+		return keyring_set_name(tsk->session_keyring,
+					"_ses.%u",
+					tsk->session_keyring->serial);
+	}
+	
+	return 0;
+} /* end suid_keys() */
+
+/*****************************************************************************/
+/*
+ * deal with execve()
+ */
+int exec_keys(struct task_struct *tsk)
+{
+	key_put(xchg(&tsk->process_keyring, NULL));
+	key_put(xchg(&tsk->thread_keyring, NULL));
+
+	if (!tsk->session_keyring) {
+		if (keyring_alloc(NULL, &tsk->session_keyring) < 0)
+			return -ENOMEM;
+		if (keyring_set_name(tsk->session_keyring,
+				     "_ses.%u", tsk->session_keyring->serial
+				     ) < 0)
+			return -ENOMEM;
+	}
+
+	if (keyring_alloc(NULL, &tsk->process_keyring) < 0)
+		return -ENOMEM;
+
+	return keyring_set_name(tsk->process_keyring, "_pid.%d", tsk->tgid);
+	
+} /* end exec_keys() */
+
+/*****************************************************************************/
+/*
+ * implement "/proc/keys" to provides a list of the keys on the system
+ */
+#ifdef CONFIG_PROC_FS
+static int key_proc_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &key_proc_ops);
+
+}
+
+static void *key_proc_start(struct seq_file *p, loff_t *_pos)
+{
+	struct rb_node *_p;
+	loff_t pos = *_pos;
+
+	spin_lock(&key_serial_lock);
+
+	_p = rb_first(&key_serial_tree);
+	while (pos > 0 && _p) {
+		pos--;
+		_p = rb_next(_p);
+	}
+
+	return _p;
+}
+
+static void *key_proc_next(struct seq_file *p, void *v, loff_t *_pos)
+{
+	(*_pos)++;
+	return rb_next((struct rb_node *) v);
+}
+
+static void key_proc_stop(struct seq_file *p, void *v)
+{
+	spin_unlock(&key_serial_lock);
+}
+
+static int key_proc_show(struct seq_file *m, void *v)
+{
+	struct rb_tree *_p = v;
+	const struct key *key = rb_entry(_p, struct key, serial_node);
+
+	seq_printf(m, "%08x %c%c%c %5d %-9.9s ",
+		   key->serial,
+		   key->flags & KEY_FLAG_PUBLIC_KEYRING	? 'p' : '-',
+		   key->flags & KEY_FLAG_RETIRED	? 'r' : '-',
+		   key->flags & KEY_FLAG_DEAD		? 'd' : '-',
+		   atomic_read(&key->usage),
+		   key->type->name);
+
+	key->type->describe(key, m);
+	seq_putc(m, '\n');
+
+	return 0;
+}
+#endif /* CONFIG_PROC_FS */
diff -uNr linux-2.6.0-test4/kernel/Makefile linux-2.6.0-test4-keys/kernel/Makefile
--- linux-2.6.0-test4/kernel/Makefile	2003-08-26 14:04:48.000000000 +0100
+++ linux-2.6.0-test4-keys/kernel/Makefile	2003-08-26 18:09:44.000000000 +0100
@@ -6,7 +6,7 @@
 	    exit.o itimer.o time.o softirq.o resource.o \
 	    sysctl.o capability.o ptrace.o timer.o user.o \
 	    signal.o sys.o kmod.o workqueue.o pid.o \
-	    rcupdate.o intermodule.o extable.o params.o posix-timers.o
+	    rcupdate.o intermodule.o extable.o params.o posix-timers.o key.o
 
 obj-$(CONFIG_FUTEX) += futex.o
 obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
diff -uNr linux-2.6.0-test4/kernel/user.c linux-2.6.0-test4-keys/kernel/user.c
--- linux-2.6.0-test4/kernel/user.c	2003-08-26 13:27:21.000000000 +0100
+++ linux-2.6.0-test4-keys/kernel/user.c	2003-08-27 16:18:26.000000000 +0100
@@ -12,6 +12,7 @@
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/bitops.h>
+#include <linux/key.h>
 
 /*
  * UID task count cache, to get fast user lookup in "alloc_uid"
@@ -30,7 +31,8 @@
 struct user_struct root_user = {
 	.__count	= ATOMIC_INIT(1),
 	.processes	= ATOMIC_INIT(1),
-	.files		= ATOMIC_INIT(0)
+	.files		= ATOMIC_INIT(0),
+	.keyring	= &root_user_keyring,
 };
 
 /*
@@ -73,6 +75,7 @@
 {
 	if (up && atomic_dec_and_lock(&up->__count, &uidhash_lock)) {
 		uid_hash_remove(up);
+		key_put(up->keyring);
 		kmem_cache_free(uid_cachep, up);
 		spin_unlock(&uidhash_lock);
 	}
@@ -98,6 +101,17 @@
 		atomic_set(&new->processes, 0);
 		atomic_set(&new->files, 0);
 
+		if (keyring_alloc(NULL, &new->keyring) < 0) {
+			kmem_cache_free(uid_cachep, up);
+			return NULL;
+		}
+
+		if (keyring_set_name(new->keyring, "_uid.%u", uid) < 0) {
+			key_put(new->keyring);
+			kmem_cache_free(uid_cachep, up);
+			return NULL;
+		}
+
 		/*
 		 * Before adding this, check whether we raced
 		 * on adding the same user already..
@@ -130,6 +144,7 @@
 	atomic_dec(&old_user->processes);
 	current->user = new_user;
 	free_uid(old_user);
+	suid_keys(current);
 }
 
 


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2003-08-27 15:33 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2003-08-22 15:25 authentication / encryption key retention David Howells
2003-08-22 16:19 ` Linus Torvalds
2003-08-22 17:39   ` David Howells
2003-08-22 18:38     ` Linus Torvalds
2003-08-26 10:12       ` David Howells
2003-08-26 15:30         ` Alan Cox
2003-08-26 16:07         ` David Howells
2003-08-26 16:26           ` Valdis.Kletnieks
2003-08-26 16:26         ` Stephen Smalley
2003-08-27 15:31       ` [PATCH/RFC] " David Howells

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).