* In-kernel Authentication Tokens (PAGs)
@ 2004-06-12 2:37 Kyle Moffett
2004-06-12 3:13 ` Andy Lutomirski
` (2 more replies)
0 siblings, 3 replies; 40+ messages in thread
From: Kyle Moffett @ 2004-06-12 2:37 UTC (permalink / raw)
To: linux-kernel
I am working on a generic PAG subsystem for the kernel, something that
handles BLOB PAG data and could be used for OpenAFS, Coda, NFSv4, etc.
I have a patch, but it is not well tested yet. Here is an overview of
the
architecture:
Each process has a PAG, and each PAG has a parent PAG. Users are
allowed to make new PAGs associated with their UID and modify ones that
are already associated with their UID. Each PAG consists of a set of
tokens,
each uniquely identified by an integral "type" and a string "realm."
The
search for a token by any subsystem is done starting at the immediate
parent
and proceeds upward. Tokens are in kernel memory and so are not ever
swapped out.
Each PAG is represented in user-space as an integer. Here are the
sys-calls
that I propose:
sys_get_pag
sys_set_pag
These manipulate the PAG associated with a given PID.
sys_get_pag_parent
sys_set_pag_parent
These manipulate the parent PAG of a given PAG
sys_get_pag_uid
sys_set_pag_uid
These manipulate the UID which "owns" a PAG
sys_get_pag_token
sys_set_pag_token
These manipulate tokens within a specific PAG
sys_search_pag_token
This executes the search process as described above
Cheers,
Kyle Moffett
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-12 2:37 In-kernel Authentication Tokens (PAGs) Kyle Moffett
@ 2004-06-12 3:13 ` Andy Lutomirski
2004-06-12 4:57 ` Kyle Moffett
2004-06-12 3:15 ` Chris Wright
2004-06-12 22:51 ` Trond Myklebust
2 siblings, 1 reply; 40+ messages in thread
From: Andy Lutomirski @ 2004-06-12 3:13 UTC (permalink / raw)
To: Kyle Moffett; +Cc: Kernel Mailing List
Kyle Moffett wrote:
> I am working on a generic PAG subsystem for the kernel, something that
> handles BLOB PAG data and could be used for OpenAFS, Coda, NFSv4, etc.
> I have a patch, but it is not well tested yet. Here is an overview of the
> architecture:
>
> Each process has a PAG, and each PAG has a parent PAG. Users are
> allowed to make new PAGs associated with their UID and modify ones that
> are already associated with their UID. Each PAG consists of a set of
> tokens,
> each uniquely identified by an integral "type" and a string "realm." The
> search for a token by any subsystem is done starting at the immediate
> parent
> and proceeds upward. Tokens are in kernel memory and so are not ever
> swapped out.
>
> ...
I like the idea of having some kernel support for tokens.
But why PAGs? I imagine tokens as being independent objects without any
hierarchy. A token group is a set of tokens. The operations on tokens are:
read: read the raw value of the token
write: change the value of the token
execute: "use" the token (i.e. for VFS, pass over UNIX socket (to a
privileged process, I guess).
Which gives an interesting thought: there are "anonymous" and named tokens.
Anonymous ones are just fds. Named ones live in /cred/tokens.
/cred/tokens: a named token
/cred/groups/all: a magic group which has everything
/cred/groups/whatever: contains symlinks to tokens it can access
/proc/12345/tokengroup: symlink to my token group
To avoid information leaks, /cred/tokens would be readable and executable
only by root. You can only create symlinks to tokens you have access to.
And you have a syscall to select a token group.
AFS's pagsh (or whatever it's called) creates a new token group and selects it.
If you really need a hierarchy, then you could allow token groups to
contain other token groups, with the rule that the whole thing must be acyclic.
Now, if I only knew how to write filesystems...
--Andy
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-12 2:37 In-kernel Authentication Tokens (PAGs) Kyle Moffett
2004-06-12 3:13 ` Andy Lutomirski
@ 2004-06-12 3:15 ` Chris Wright
2004-06-12 4:48 ` Kyle Moffett
2004-06-12 22:51 ` Trond Myklebust
2 siblings, 1 reply; 40+ messages in thread
From: Chris Wright @ 2004-06-12 3:15 UTC (permalink / raw)
To: Kyle Moffett; +Cc: linux-kernel
* Kyle Moffett (mrmacman_g4@mac.com) wrote:
> I am working on a generic PAG subsystem for the kernel, something that
> handles BLOB PAG data and could be used for OpenAFS, Coda, NFSv4, etc.
> I have a patch, but it is not well tested yet. Here is an overview of
> the
> architecture:
>
> Each process has a PAG, and each PAG has a parent PAG. Users are
> allowed to make new PAGs associated with their UID and modify ones that
> are already associated with their UID. Each PAG consists of a set of
Hrm. Wouldn't it be possible that two processes with same uid have
authenticated in different domains, and as such shouldn't be allowed to
touch each other's PAGs? Or is this not allowed?
thanks,
-chris
--
Linux Security Modules http://lsm.immunix.org http://lsm.bkbits.net
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-12 3:15 ` Chris Wright
@ 2004-06-12 4:48 ` Kyle Moffett
2004-06-12 20:53 ` Chris Wright
0 siblings, 1 reply; 40+ messages in thread
From: Kyle Moffett @ 2004-06-12 4:48 UTC (permalink / raw)
To: Chris Wright; +Cc: linux-kernel
On Jun 11, 2004, at 23:15, Chris Wright wrote:
> Hrm. Wouldn't it be possible that two processes with same uid have
> authenticated in different domains, and as such shouldn't be allowed to
> touch each other's PAGs? Or is this not allowed?
Linux doesn't really support the idea that a process should not be able
to
affect another process in the same UID. There's too many things that
would break or become horribly insecure if we tried to assume that. For
example, just attach a debugger to a process that you want the keys of.
Then just insert a few system calls to retrieve the data, and leave.
Linux
assumes atomicity of a user/UID and it's not practical to change that.
Cheers,
Kyle Moffett
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-12 3:13 ` Andy Lutomirski
@ 2004-06-12 4:57 ` Kyle Moffett
2004-06-12 5:34 ` Andy Lutomirski
0 siblings, 1 reply; 40+ messages in thread
From: Kyle Moffett @ 2004-06-12 4:57 UTC (permalink / raw)
To: Andy Lutomirski; +Cc: Kernel Mailing List
On Jun 11, 2004, at 23:13, Andy Lutomirski wrote:
> I like the idea of having some kernel support for tokens.
>
> But why PAGs? I imagine tokens as being independent objects without
> any hierarchy. A token group is a set of tokens. The operations on
> tokens
> are:
>
> [...snip...]
>
> If you really need a hierarchy, then you could allow token groups to
> contain
> other token groups, with the rule that the whole thing must be acyclic.
I think my vocabulary here is confusing, what you refer to as a token
group, I refer to as a PAG. The idea for the hierarchy is that it is
frequently desirable to start a sub-shell with a temporarily different
set of tokens, or to mask out only a certain token without modifying
the rest. For example, let's say that I login at my school, which gets
Kerberos tokens and AFS tickets. Now I want to perform some Kerberos
administration activities, so I run "pagsh" or something to create a
new shell with a new PAG, one with a parent of the original PAG. When
I do filesystem access the search process finds the old AFS tokens, but
the kmoffett/admin@MY.HOST Kerberos TGT token hides the old
kmoffett@MY.HOST Kerberos TGT, without replacing it. That means that
all the old shells operate normally, nothing has changed for them, but
the new shell has the extra layer with the new Kerberos tickets, so I
can administrate Kerberos without changing my AFS tokens to admin ones.
Cheers,
Kyle Moffett
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-12 4:57 ` Kyle Moffett
@ 2004-06-12 5:34 ` Andy Lutomirski
2004-06-12 12:51 ` Kyle Moffett
0 siblings, 1 reply; 40+ messages in thread
From: Andy Lutomirski @ 2004-06-12 5:34 UTC (permalink / raw)
To: Kyle Moffett; +Cc: Kernel Mailing List
Kyle Moffett wrote:
> On Jun 11, 2004, at 23:13, Andy Lutomirski wrote:
>
>> I like the idea of having some kernel support for tokens.
>>
>> But why PAGs? I imagine tokens as being independent objects without
>> any hierarchy. A token group is a set of tokens. The operations on
>> tokens
>> are:
>>
>> [...snip...]
>>
>> If you really need a hierarchy, then you could allow token groups to
>> contain
>> other token groups, with the rule that the whole thing must be acyclic.
>
>
> I think my vocabulary here is confusing, what you refer to as a token
> group, I refer to as a PAG. The idea for the hierarchy is that it is
> frequently desirable to start a sub-shell with a temporarily different
> set of tokens, or to mask out only a certain token without modifying the
> rest.
Right. But I think it would be desirable to do other things -- for
example, a program might want to forward one token over to a daemon to do
some work. It doesn't make much sense here to have a hierarchial structure.
BTW, does AFS even have this hierarchy, or does pagsh just create a copy?
I can't find any manpage for it...
--Andy
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-12 5:34 ` Andy Lutomirski
@ 2004-06-12 12:51 ` Kyle Moffett
2004-06-12 15:37 ` Andy Lutomirski
0 siblings, 1 reply; 40+ messages in thread
From: Kyle Moffett @ 2004-06-12 12:51 UTC (permalink / raw)
To: Andy Lutomirski; +Cc: Kernel Mailing List
On Jun 12, 2004, at 01:34, Andy Lutomirski wrote:
> Right. But I think it would be desirable to do other things -- for
> example, a program might want to forward one token over to a daemon to
> do some work. It doesn't make much sense here to have a hierarchial
> structure.
So you disagree with the hierarchical structure because you believe
that there are other things that are more important that conflict with
it. I see no reason why both cannot be accommodated. For me, I would
really desire a hierarchical structure because it would make it very
simple to have a token set for the entire session and one for each
instance (shell), and ones for subshells where convenient.
You want to sent a token to some daemon over a UNIX socket? Just copy
the token data and write it out to the socket, the same as if you had
some external token store (Like in MIT Kerberos) and wanted to send the
token to somewhere without the environment variables. This system
would allow several existing token cache mechanisms to be converted to
this alternative store without much work at all.
Perhaps the syscalls should be changed to allow better protection
against race conditions when two processes are using token groups.
sys_tokgrp_open
Returns a tokgrp_handle associated with a token group id. Implies
that the tokgrp will not go away until this handle is closed
sys_tokgrp_pid_open
Returns a tokgrp_handle associated with the token group currently
controlled by a given PID.
sys_tokgrp_close
Releases a tokgrp_handle
sys_tokgrp_get{parent,uid,token}
sys_tokgrp_set{parent,uid,token}
These operate the same as earlier, except on tokgrp_handles instead of
tokgrp IDs.
Then perhaps we could arrange for a tokgrp_handle to be a special kind
of filehandle, and perhaps the set* and get* functions could be IOCTLs
or something. That would allow a tokgrp_handle to be passed around
between processes, although a suitably privileged process could just
run sys_tokgrp_pid_open on the PID of the other process. That way also
close-on-exec and such work as expected.
> BTW, does AFS even have this hierarchy, or does pagsh just create a
> copy? I can't find any manpage for it...
AFS in 2.4 has these magic high-numbered groups that it dynamically
allocates. The way a new set of tokens is created is by changing magic
groups to a new set. That whole system is just a massive hack and I'd
rather it stop at 2.4 I don't know about 2.6, I think they might be
ready for a beta, but I don't know how their auth tokens work.
Cheers,
Kyle Moffett
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-12 12:51 ` Kyle Moffett
@ 2004-06-12 15:37 ` Andy Lutomirski
2004-06-12 17:15 ` Kyle Moffett
0 siblings, 1 reply; 40+ messages in thread
From: Andy Lutomirski @ 2004-06-12 15:37 UTC (permalink / raw)
To: Kyle Moffett; +Cc: Kernel Mailing List
Kyle Moffett wrote:
> On Jun 12, 2004, at 01:34, Andy Lutomirski wrote:
>
>> Right. But I think it would be desirable to do other things -- for
>> example, a program might want to forward one token over to a daemon to
>> do some work. It doesn't make much sense here to have a hierarchial
>> structure.
>
>
> So you disagree with the hierarchical structure because you believe that
> there are other things that are more important that conflict with it. I
> see no reason why both cannot be accommodated. For me, I would really
> desire a hierarchical structure because it would make it very simple to
> have a token set for the entire session and one for each instance
> (shell), and ones for subshells where convenient.
OK.
>
> You want to sent a token to some daemon over a UNIX socket? Just copy
> the token data and write it out to the socket, the same as if you had
> some external token store (Like in MIT Kerberos) and wanted to send the
> token to somewhere without the environment variables. This system would
> allow several existing token cache mechanisms to be converted to this
> alternative store without much work at all.
Except I'd like non-root users to have tokens that they _cannot_ read, but
that they can still pass over unix sockets. I have no objection to also
allowing user-readable tokens.
This way I could have a server with, say, a Kerberos service token such
that a compromise of the server process does not compromise the service
token. (You still have a gotcha in that the kerberosd this would require
would, for performance reasons, want to handle only ticket-granting
traffic. Still, you just mark the TGT unreadable and the individual
session tickets readable, so that the damage of a compromise is limited to
a few hours until the sessions expire.)
Yes, this would be a _lot_ more work than just blindly porting Kerberos'
ticket cache, but it has security benefits.
And I really want a good token system in Linux -- that way the OpenAFS
people will stop bitching and I might be able to use it again.
--Andy
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-12 15:37 ` Andy Lutomirski
@ 2004-06-12 17:15 ` Kyle Moffett
0 siblings, 0 replies; 40+ messages in thread
From: Kyle Moffett @ 2004-06-12 17:15 UTC (permalink / raw)
To: Andy Lutomirski; +Cc: Kernel Mailing List
On Jun 12, 2004, at 11:37, Andy Lutomirski wrote:
> Kyle Moffett wrote:
>> You want to sent a token to some daemon over a UNIX socket? Just
>> copy the token data and write it out to the socket, the same as if
>> you had some external token store (Like in MIT Kerberos) and wanted
>> to send the token to somewhere without the environment variables.
>> This system would allow several existing token cache mechanisms to be
>> converted to this alternative store without much work at all.
>
> Except I'd like non-root users to have tokens that they _cannot_ read,
> but that they can still pass over unix sockets. I have no objection
> to also allowing user-readable tokens.
Ok, that makes a lot of sense, I really should have thought of that
earlier. So how about we add an extra flag to a token identifying
whether it's readable by someone with it's UID or only by someone with
CAP_LINUX_TOKGRP. How about the following interface:
The special files "/proc/tokgrp/<tokgrp-id>/group" represent token
groups. They cannot be read or written, but they utilize file modes
(I'm not sure how yet). Opening one of them prevents it from going
away until it is closed. There should be symlinks named
"/proc/<pid>/tokgrp" to the appropriate dir in "/proc/tokgrp/".
Perhaps there should be symlinks as "/proc/tokgrp/<tokgrp_id>/parent"
to the appropriate parent tokgrp directory. Those would not exist if
it did not have a parent.
IOCTLs:
tokgrp_{get,set}_pid: Manipulates the PID of a token group
tokgrp_{get,set}_parent: Manipulates the parent of a token group
Tokens are uniquely identified within a token group by an integral
"type" and a string "realm". A specific token is mapped to
"/proc/tokgrp/<id>/<typenum>/<realm>". They can be read or written
according to file modes, and the execute permission means the ability
to execute in-kernel operations on the token (depending on the type)
using IOCTLs. All tokens (As long as the process is in the same UID
and/or has CAP_LINUX_PAG) can be opened and the filehandles passed
around using UNIX sockets.
> This way I could have a server with, say, a Kerberos service token
> such that a compromise of the server process does not compromise the
> service token. (You still have a gotcha in that the kerberosd this
> would require would, for performance reasons, want to handle only
> ticket-granting traffic. Still, you just mark the TGT unreadable and
> the individual session tickets readable, so that the damage of a
> compromise is limited to a few hours until the sessions expire.)
Since we have in-kernel crypto we could even put some of the token
operations into the kernel and use those operations to work with the
ticket (decrypt, etc). That would mean that in certain cases we could
keep the tickets completely out of reach of exposed software.
> Yes, this would be a _lot_ more work than just blindly porting
> Kerberos' ticket cache, but it has security benefits.
Definitely
> And I really want a good token system in Linux -- that way the OpenAFS
> people will stop bitching and I might be able to use it again.
Yeah, I know. If we do it well enough and make it modular enough then
OpenAFS, Coda, NFSv4, Kerberos, OpenSSH (RSA keys) etc can all use the
same implementation. This could also be steps in the right direction
of making Linux more TCB-ish.
Cheers,
Kyle Moffett
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-12 4:48 ` Kyle Moffett
@ 2004-06-12 20:53 ` Chris Wright
2004-06-12 21:15 ` Kyle Moffett
0 siblings, 1 reply; 40+ messages in thread
From: Chris Wright @ 2004-06-12 20:53 UTC (permalink / raw)
To: Kyle Moffett; +Cc: Chris Wright, linux-kernel
* Kyle Moffett (mrmacman_g4@mac.com) wrote:
> On Jun 11, 2004, at 23:15, Chris Wright wrote:
> > Hrm. Wouldn't it be possible that two processes with same uid have
> > authenticated in different domains, and as such shouldn't be allowed to
> > touch each other's PAGs? Or is this not allowed?
>
> Linux doesn't really support the idea that a process should not be able
> to
> affect another process in the same UID. There's too many things that
Actually that's not the case. The UID is currently insufficient to
describe the security domain that a process is running in. The whole
of the LSM infrastructure is designed with this in mind. So somehting
like SELinux may enforce a security domain change (w/out a UID change)
across an execve() of pagsh. I was simply trying to ascertain if you
were storing this within task->user which I think would be wrong.
thanks,
-chris
--
Linux Security Modules http://lsm.immunix.org http://lsm.bkbits.net
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-12 20:53 ` Chris Wright
@ 2004-06-12 21:15 ` Kyle Moffett
2004-06-12 21:44 ` Chris Wright
0 siblings, 1 reply; 40+ messages in thread
From: Kyle Moffett @ 2004-06-12 21:15 UTC (permalink / raw)
To: Chris Wright; +Cc: linux-kernel
On Jun 12, 2004, at 16:53, Chris Wright wrote:
> Actually that's not the case. The UID is currently insufficient to
> describe the security domain that a process is running in. The whole
> of the LSM infrastructure is designed with this in mind. So somehting
> like SELinux may enforce a security domain change (w/out a UID change)
> across an execve() of pagsh. I was simply trying to ascertain if you
> were storing this within task->user which I think would be wrong.
Ahh, ok, I myself have no experience with LSM, so there will likely be
some
need to change my implementation. Currently the only field that I add
to
existing structures in the kernel is a pag field in the task_struct.
PAG
structures themselves need some way of determining if the calling task
is in the same "grouping" as a stored "grouping" within the PAG. My
implementation uses UIDs, but I would be very glad if you could tell me
what I should use instead. I need some kind of structure or pointer
that I
can embed within a PAG and a token, and some kind of equality test.
The other thing that needs to be implemented is some kind of limit that
restricts how many PAGs/tokens and how much memory can be allocated
in the kernel per user and per process. Do you have any information on
where would be the best place to store that information?
Cheers,
Kyle Moffett
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-12 21:15 ` Kyle Moffett
@ 2004-06-12 21:44 ` Chris Wright
2004-06-12 21:58 ` Kyle Moffett
0 siblings, 1 reply; 40+ messages in thread
From: Chris Wright @ 2004-06-12 21:44 UTC (permalink / raw)
To: Kyle Moffett; +Cc: Chris Wright, linux-kernel
* Kyle Moffett (mrmacman_g4@mac.com) wrote:
> On Jun 12, 2004, at 16:53, Chris Wright wrote:
> > Actually that's not the case. The UID is currently insufficient to
> > describe the security domain that a process is running in. The whole
> > of the LSM infrastructure is designed with this in mind. So somehting
> > like SELinux may enforce a security domain change (w/out a UID change)
> > across an execve() of pagsh. I was simply trying to ascertain if you
> > were storing this within task->user which I think would be wrong.
>
> Ahh, ok, I myself have no experience with LSM, so there will likely be
> some
> need to change my implementation. Currently the only field that I add
> to
> existing structures in the kernel is a pag field in the task_struct.
> PAG
> structures themselves need some way of determining if the calling task
> is in the same "grouping" as a stored "grouping" within the PAG. My
> implementation uses UIDs, but I would be very glad if you could tell me
> what I should use instead. I need some kind of structure or pointer
> that I
> can embed within a PAG and a token, and some kind of equality test.
Typically, objects that LSM cares about include a pointer to opaque
data (security blob) which describes the security domain for the object.
See task->security as an example. It's not clear to me if task->security
is sufficient and you only need a back pointer to the task or if each
PAG needs it's own security blob.
> The other thing that needs to be implemented is some kind of limit that
> restricts how many PAGs/tokens and how much memory can be allocated
> in the kernel per user and per process. Do you have any information on
> where would be the best place to store that information?
Sounds like an extension to rlimits. The counters could be stored in
->user to limit the userwide number of tokens.
thanks,
-chris
--
Linux Security Modules http://lsm.immunix.org http://lsm.bkbits.net
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-12 21:44 ` Chris Wright
@ 2004-06-12 21:58 ` Kyle Moffett
2004-06-12 22:51 ` Chris Wright
0 siblings, 1 reply; 40+ messages in thread
From: Kyle Moffett @ 2004-06-12 21:58 UTC (permalink / raw)
To: Chris Wright; +Cc: linux-kernel
On Jun 12, 2004, at 17:44, Chris Wright wrote:
> Typically, objects that LSM cares about include a pointer to opaque
> data (security blob) which describes the security domain for the
> object.
> See task->security as an example. It's not clear to me if
> task->security
> is sufficient and you only need a back pointer to the task or if each
> PAG needs it's own security blob.
Ahh, ok. In this case, a PAG is an independent object, not directly
associated with any specific task or other PAG, so therefore it will
likely
need its own security blob. Currently, in the creation of my PAG I just
copy over the UID from the calling task. If I was to convert it to use
LSM,
I guess I should just leave out the UID entirely, and just have a
pointer
to a security blob. What is the best way to portray the security blob
to
user-space, in terms of sys-calls? I need a way for user-space apps to
change the security context in a similar way as it is changed for a task
or process. Do you have a link to some documentation on the kernel
API for LSM? I basically need to copy the current task's security blob
into a new one and be able to compare two contexts to see if one
can affect the other. Thanks!
> Sounds like an extension to rlimits. The counters could be stored in
> ->user to limit the userwide number of tokens.
Ok, thank you very much, that's exactly what I need. I am somewhat
new to kernel development, so finding my way around the structs is
somewhat difficult. Is there a good guide somewhere on the net that
you can point me to?
Cheers,
Kyle Moffett
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-12 2:37 In-kernel Authentication Tokens (PAGs) Kyle Moffett
2004-06-12 3:13 ` Andy Lutomirski
2004-06-12 3:15 ` Chris Wright
@ 2004-06-12 22:51 ` Trond Myklebust
2004-06-12 23:33 ` Kyle Moffett
2 siblings, 1 reply; 40+ messages in thread
From: Trond Myklebust @ 2004-06-12 22:51 UTC (permalink / raw)
To: Kyle Moffett; +Cc: linux-kernel
På fr , 11/06/2004 klokka 22:37, skreiv Kyle Moffett:
> I am working on a generic PAG subsystem for the kernel,
It's been done.
See the thread on
http://marc.theaimsgroup.com/?l=linux-fsdevel&m=105290906118164&w=2
on why Linus vetoed the idea of PAGs for Linux.
Cheers,
Trond
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-12 21:58 ` Kyle Moffett
@ 2004-06-12 22:51 ` Chris Wright
2004-06-12 23:40 ` Kyle Moffett
0 siblings, 1 reply; 40+ messages in thread
From: Chris Wright @ 2004-06-12 22:51 UTC (permalink / raw)
To: Kyle Moffett; +Cc: Chris Wright, linux-kernel
* Kyle Moffett (mrmacman_g4@mac.com) wrote:
> On Jun 12, 2004, at 17:44, Chris Wright wrote:
> > Typically, objects that LSM cares about include a pointer to opaque
> > data (security blob) which describes the security domain for the
> > object.
> > See task->security as an example. It's not clear to me if
> > task->security
> > is sufficient and you only need a back pointer to the task or if each
> > PAG needs it's own security blob.
>
> Ahh, ok. In this case, a PAG is an independent object, not directly
> associated with any specific task or other PAG, so therefore it will
> likely
> need its own security blob. Currently, in the creation of my PAG I just
> copy over the UID from the calling task. If I was to convert it to use
> LSM,
> I guess I should just leave out the UID entirely, and just have a
> pointer
> to a security blob. What is the best way to portray the security blob
> to
> user-space, in terms of sys-calls? I need a way for user-space apps to
> change the security context in a similar way as it is changed for a task
> or process. Do you have a link to some documentation on the kernel
> API for LSM? I basically need to copy the current task's security blob
> into a new one and be able to compare two contexts to see if one
> can affect the other. Thanks!
I don't have a good feel for the PAG data structure, perhaps the
above would turn out as overkill. The security api is commented in
include/linux/security.h, take a gander there. The blobs are specific
to the security model, and I don't yet see a need to display them to
userspace for each PAG.
> > Sounds like an extension to rlimits. The counters could be stored in
> > ->user to limit the userwide number of tokens.
>
> Ok, thank you very much, that's exactly what I need. I am somewhat
> new to kernel development, so finding my way around the structs is
> somewhat difficult. Is there a good guide somewhere on the net that
> you can point me to?
Hmm, not really. The source is best.
thanks,
-chris
--
Linux Security Modules http://lsm.immunix.org http://lsm.bkbits.net
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-12 22:51 ` Trond Myklebust
@ 2004-06-12 23:33 ` Kyle Moffett
2004-06-12 23:58 ` Trond Myklebust
0 siblings, 1 reply; 40+ messages in thread
From: Kyle Moffett @ 2004-06-12 23:33 UTC (permalink / raw)
To: Trond Myklebust; +Cc: linux-kernel
On Jun 12, 2004, at 18:51, Trond Myklebust wrote:
> It's been done.
>
> See the thread on
>
> http://marc.theaimsgroup.com/?l=linux-fsdevel&m=105290906118164&w=2
>
> on why Linus vetoed the idea of PAGs for Linux.
He didn't veto PAGs, he only vetoed that particular implementation.
I've
read his criticisms before and I believe that my proposal neatly handles
most of them, which is why I'm posting it. Another nice feature of my
system
is that it does not break anything if it is completely ignored by some
tasks
and used by others. In fact, most processes shouldn't care, only stuff
that
actually needs to use or modify the tokens in user-space should care.
I really should change the term "PAG". My implementation does not
operate
at all like the posted one did. His implementation is tied in very
strongly with
the concept of a user, 1 user => 1 PAG. In my code the two are
completely
separate, a PAG is a generic object with a few properties: a security
context,
a list of tokens, and a parent. Linus' criticisms are:
- Designed for AFS
I designed this because I would like to see a ticket/token storage
mechanism
in the kernel. I would like this to be something that could be a
Kerberos V5
credentials cache, an RSA key storage agent (like ssh-agent), and a
generic
remote filesystem token storage.
- A token can only be on one PAG
True in my design, but in order to access the token you open() it,
which returns
a filehandle that can be passed to any other program over a UNIX socket.
This could be easily extended to allow the original user to
invalidate/close the
other user's filehandle. Also, the design allows each PAG to have a
parent,
so it forms a tree (Must be non-cyclic). This allows multiple
processes to share
a set of global tokens in a parent PAG, and each have their own sub-PAGs
that they can work in without disturbing the global token set. Linus
even
mentions that perhaps a list of PAGs could handle that issue, but with
that the
priority is unintuitive, whereas with a tree one can match the process
tree
much more accurately and without much effort. Even still, my design
makes it
simple to add the ability for a token to be in more than one PAG.
- pag_t is too small
This doesn't apply, because I don't map a PAG to a user, it stands on
its own,
in an allocation namespace separate from anything else.
- Desire for "group" PAGs.
I don't have any feature that allows for a "group" PAG, but I think
that should be
handled elsewhere. If I want special file access because I am in a
group, then
that should be handled by the filesystem based on my _user_ token. It
knows
who I am and can figure out my group list from that. If it is truly a
desire to have
all members share a token or set of tokens (I don't know of any
circumstances
where this would be desirable, please tell me if you have any
examples), then
each group could have a PAG tree associated with it, which would be
searched
*after* the PAG tree associated with a process when looking for tokens.
Cheers,
Kyle Moffett
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-12 22:51 ` Chris Wright
@ 2004-06-12 23:40 ` Kyle Moffett
0 siblings, 0 replies; 40+ messages in thread
From: Kyle Moffett @ 2004-06-12 23:40 UTC (permalink / raw)
To: Chris Wright; +Cc: linux-kernel
On Jun 12, 2004, at 18:51, Chris Wright wrote:
> I don't have a good feel for the PAG data structure, perhaps the
> above would turn out as overkill. The security api is commented in
> include/linux/security.h, take a gander there. The blobs are specific
> to the security model, and I don't yet see a need to display them to
> userspace for each PAG.
Ok, well perhaps what I actually need are some extra callbacks for
security modules then. A token-group (I'm changing the term because
PAG is confused with an older patch) needs to have an access spec
indicating who can read/modify the token-group as a whole and who
can read/modify each token within it. In my emails with Andy
Lutomirski we have thought about the idea of putting the token-groups
in /proc and running all access through that way. If we do that, then
it
would be very practical to just use file permissions and POSIX ACLs
to control access, in which case each token-group and token could
just use the same access-control mechanisms that are used for other
files in /proc. That would likely be the easiest path
>> Ok, thank you very much, that's exactly what I need. I am somewhat
>> new to kernel development, so finding my way around the structs is
>> somewhat difficult. Is there a good guide somewhere on the net that
>> you can point me to?
> Hmm, not really. The source is best.
Ok, well thanks for pointing out linux/security.h anyway.
Thanks very much for your help!
Cheers,
Kyle Moffett
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-12 23:33 ` Kyle Moffett
@ 2004-06-12 23:58 ` Trond Myklebust
2004-06-13 0:23 ` Kyle Moffett
0 siblings, 1 reply; 40+ messages in thread
From: Trond Myklebust @ 2004-06-12 23:58 UTC (permalink / raw)
To: Kyle Moffett; +Cc: linux-kernel
På lau , 12/06/2004 klokka 19:33, skreiv Kyle Moffett:
> - Designed for AFS
>
> I designed this because I would like to see a ticket/token storage
> mechanism
> in the kernel. I would like this to be something that could be a
> Kerberos V5
> credentials cache, an RSA key storage agent (like ssh-agent), and a
> generic
> remote filesystem token storage.
So this would be more like an in-kernel keyring then?
> - A token can only be on one PAG
>
> True in my design, but in order to access the token you open() it,
> which returns
> a filehandle that can be passed to any other program over a UNIX socket.
> This could be easily extended to allow the original user to
> invalidate/close the
> other user's filehandle. Also, the design allows each PAG to have a
> parent,
> so it forms a tree (Must be non-cyclic). This allows multiple
> processes to share
> a set of global tokens in a parent PAG, and each have their own sub-PAGs
> that they can work in without disturbing the global token set. Linus
> even
> mentions that perhaps a list of PAGs could handle that issue, but with
> that the
> priority is unintuitive, whereas with a tree one can match the process
> tree
> much more accurately and without much effort. Even still, my design
> makes it
> simple to add the ability for a token to be in more than one PAG.
How does it cope with lifetime issues such as token renewal and
invalidation?
Does it provide for notifiers in case of invalidation, renewal or token
expiration?
For NFSv4 for instance, this is important, since the client will want to
evict all state if the user's credential expires.
Cheers,
Trond
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-12 23:58 ` Trond Myklebust
@ 2004-06-13 0:23 ` Kyle Moffett
2004-06-15 6:38 ` Blair Strang
0 siblings, 1 reply; 40+ messages in thread
From: Kyle Moffett @ 2004-06-13 0:23 UTC (permalink / raw)
To: Trond Myklebust; +Cc: linux-kernel
On Jun 12, 2004, at 19:58, Trond Myklebust wrote:
> So this would be more like an in-kernel keyring then?
Yeah, that seems about right.
> How does it cope with lifetime issues such as token renewal and
> invalidation?
My design is token-type independent, it merely implements a generic
token BLOB handling facility that maps a process to a set of tokens.
The usage of tokens should be a completely separate issue. For
example, I auth to a Kerberos server and put the retrieved ticket into
the kernel token system. When that token is later used, the user
(NFSv4, in this case) must verify if the token has expired or not. A
variety of actions may be taken after that point, one of which may be
to just delete the token. If NFSv4 later tried to use the token handle
(Basically a filehandle), then it would get an error indicating that the
token was deleted, and could clean up. If NFSv4 was the first one
that discovered the token was expired, then _it_ could delete the
token and clean up the state.
> Does it provide for notifiers in case of invalidation, renewal or token
> expiration?
That is a token-type specific matter, and should be handled by a
module targeting a specific token type. All tokens have a type and a
service. The "type" is an integer, representing the format of the
token.
It could be TOKEN_RSA, or TOKEN_DSA, or TOKEN_KRB4, or
TOKEN_KRB5, or TOKEN_AES. The "service" is a string that ids
a token uniquely within its type and token group. A user program
could presumably create a token with any binary data and type arg
at all and just stick it into the kernel. It would deduct from the
user's
space and token limit, but it could be done and would work fine. If
some token type needs special kernel handling then that could be
a module with some custom IOCTLs on the token filehandle.
Cheers,
Kyle Moffett
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-13 0:23 ` Kyle Moffett
@ 2004-06-15 6:38 ` Blair Strang
2004-06-15 7:03 ` Trond Myklebust
0 siblings, 1 reply; 40+ messages in thread
From: Blair Strang @ 2004-06-15 6:38 UTC (permalink / raw)
To: Kyle Moffett; +Cc: lkml
Kyle Moffett <mrmacman_g4@mac.com> writes:
> On Jun 12, 2004, at 19:58, Trond Myklebust wrote:
> > So this would be more like an in-kernel keyring then?
> Yeah, that seems about right.
>
Hi,
Disclaimer: I haven't looked at the LSM stuff.
Surely the only logical reason to tag a process with extra security
information /in the kernel/ is because that information is going to be
used /by the kernel/. I can't think of a good reason to put a
generalised keystore in the kernel.
Distributed filesystem credentials are a sensible thing to associate
with a process because then they can be easily used by the filesystem
code. Keeping this information per-uid would be easier, but PAGs are
more general.
>From that point of view, AFS PAGs boil down to the idea of annotating
processes with authorisation tokens that can be easily used by the
kernel. IIRC, in AFS, they just tack on a couple of magic
supplementary GIDs. It is good if the "annotation" can be carried out
by a userspace process.
To be most useful for distributed filesystems, PAG ids should be
per-process and inherited across fork/exec.
This is somewhat different from the genereal idea of a keystore, or
capabilities ala http://www.cap-lore.com/CapTheory/.
So that you can see where I'm coming from (and tell me I'm wrong :)
here's my braindead idea of what generic PAG support would look like.
I would be very surprised if there were not some more generic
mechanism of which this is a subset. LSM?
=====
There needs to be some capability set to allow trusted processes to
grant a PAG id to a process. In a simple implementation, say,
CAP_PAG_ALLOCATE allows you to assign them. In some cases, the kernel
might attach a PAG id to a process.
Each process may have a list of PAG ids.
A PAG id list entry has two important parts, a pag_domain [which might
be PAG_DOMAIN_AFS or PAG_DOMAIN_CODA, whatever -- it just identifies
the kernel subsystem to which the PAG is meaningful] and a pag_id,
which is considered opaque.
You might also have CAP_PAG_GIVEAWAY to allow giveaways by processes
with PAG ids. I haven't thought that part through very well.
A pag-er (allocator) needs at least:
* some way to give a process a unique PAG id.
* some subsystem specific support to associate more data with that PAG id.
And the pag-ee needs at least:
* some (allocator specific) way to ask for a PAG id. this happens in
userspace
* some way to enumerate PAG ids.
* some way to get rid of PAG ids.
* perhaps some way to give PAG ids away.
Regards,
Blair.
--
Hello. Just walk along and try NOT to think about your INTESTINES
being almost FORTY YARDS LONG!! ; C-u M-x yow
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-15 6:38 ` Blair Strang
@ 2004-06-15 7:03 ` Trond Myklebust
2004-06-15 9:36 ` David Howells
0 siblings, 1 reply; 40+ messages in thread
From: Trond Myklebust @ 2004-06-15 7:03 UTC (permalink / raw)
To: Blair Strang; +Cc: Kyle Moffett, lkml
På ty , 15/06/2004 klokka 02:38, skreiv Blair Strang:
> Surely the only logical reason to tag a process with extra security
> information /in the kernel/ is because that information is going to be
> used /by the kernel/. I can't think of a good reason to put a
> generalised keystore in the kernel.
Here are three good reasons.
- You want the key lifetime to be the same as your process lifetime
- You want the key to be readable ONLY by that one process.
- The kernel wants to supports multiple security realms and mechanisms.
Not everybody is happy with just kerberosV credentials, and we already
have beta code for the SPKM mechanism in RPCSEC_GSS.
As for the AFS PAG idea: it's already been shot down. See the
linux-fsdevel thread I referred to earlier.
Cheers,
Trond
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-15 7:03 ` Trond Myklebust
@ 2004-06-15 9:36 ` David Howells
2004-06-15 19:00 ` Kyle Moffett
` (2 more replies)
0 siblings, 3 replies; 40+ messages in thread
From: David Howells @ 2004-06-15 9:36 UTC (permalink / raw)
To: Trond Myklebust; +Cc: Blair Strang, Kyle Moffett, lkml
[-- Attachment #1: Type: text/plain, Size: 1139 bytes --]
> > Surely the only logical reason to tag a process with extra security
> > information /in the kernel/ is because that information is going to be
> > used /by the kernel/. I can't think of a good reason to put a
> > generalised keystore in the kernel.
>
> Here are three good reasons.
>
> - You want the key lifetime to be the same as your process lifetime
> - You want the key to be readable ONLY by that one process.
> - The kernel wants to supports multiple security realms and mechanisms.
> Not everybody is happy with just kerberosV credentials, and we already
> have beta code for the SPKM mechanism in RPCSEC_GSS.
>
>
> As for the AFS PAG idea: it's already been shot down. See the
> linux-fsdevel thread I referred to earlier.
You might want to look at this patch. It's what I've come up with to support
kafs, but it's general, and should work for anything. It's been built along
Linus's guidelines, and has Linus's approval, contingent on something actually
using it fully.
You can use the session keyring number as a PAG ID if you wish.
I've a sample aklog program (key submission) should you be interested.
David
[-- Attachment #2: keys-266.diff --]
[-- Type: text/plain, Size: 47943 bytes --]
Signed-Off-By: David Howells <dhowells@redhat.com>
diff -uNr linux-2.6.6/fs/exec.c linux-2.6.6-keys/fs/exec.c
--- linux-2.6.6/fs/exec.c 2004-05-11 11:28:55.000000000 +0100
+++ linux-2.6.6-keys/fs/exec.c 2004-05-11 11:35:49.000000000 +0100
@@ -46,6 +46,7 @@
#include <linux/security.h>
#include <linux/syscalls.h>
#include <linux/rmap.h>
+#include <linux/key.h>
#include <asm/uaccess.h>
#include <asm/pgalloc.h>
@@ -848,8 +849,10 @@
flush_thread();
if (bprm->e_uid != current->euid || bprm->e_gid != current->egid ||
- permission(bprm->file->f_dentry->d_inode,MAY_READ, NULL))
+ permission(bprm->file->f_dentry->d_inode,MAY_READ, NULL)) {
+ suid_keys(current);
current->mm->dumpable = 0;
+ }
/* An exec changes our domain. We are no longer part of the thread
group */
@@ -939,6 +942,11 @@
void compute_creds(struct linux_binprm *bprm)
{
int unsafe;
+
+ if (bprm->e_uid != current->uid)
+ suid_keys(current);
+ exec_keys(current);
+
task_lock(current);
unsafe = unsafe_exec(current);
security_bprm_apply_creds(bprm, unsafe);
diff -uNr linux-2.6.6/include/linux/key.h linux-2.6.6-keys/include/linux/key.h
--- linux-2.6.6/include/linux/key.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.6-keys/include/linux/key.h 2004-05-11 11:32:06.000000000 +0100
@@ -0,0 +1,144 @@
+/* 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/list.h>
+#include <linux/rbtree.h>
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+
+#ifdef __KERNEL__
+#ifdef CONFIG_KEYS
+
+#define KEY_DEBUGGING
+
+typedef int32_t key_serial_t;
+
+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 and tickets
+ */
+struct key {
+ atomic_t usage;
+ key_serial_t serial; /* key serial number */
+ struct rb_node serial_node;
+ struct key_type *type; /* type of key */
+ rwlock_t lock; /* examination vs change lock */
+ struct rw_semaphore sem; /* change vs change sem */
+ unsigned short datalen; /* payload data length */
+ unsigned short 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 */
+ struct list_head link; /* link in types list */
+
+ int (*init)(struct key *key, const char *desc,
+ size_t datalen, const char *data);
+ int (*update)(struct key *key, const char *desc,
+ size_t datalen, const char *data);
+ int (*match)(const struct key *key, const void *desc);
+ void (*clear)(struct key *key);
+ void (*describe)(const struct key *key, struct seq_file *p);
+};
+
+extern int register_key_type(struct key_type *ktype);
+extern void unregister_key_type(struct key_type *ktype);
+
+extern void key_retire(struct key *key);
+extern void key_put(struct key *key);
+
+extern int add_process_key(int specifier,
+ const char *type,
+ const char *description,
+ int plen,
+ const void *payload);
+
+extern int keyring_search(struct key *keyring,
+ const struct key_type *type,
+ const char *description,
+ struct key **_key);
+
+#define SEARCH_KEYRING_THREAD 0x0001
+#define SEARCH_KEYRING_PROCESS 0x0002
+#define SEARCH_KEYRING_SESSION 0x0004
+#define SEARCH_KEYRING_UID 0x0008
+#define SEARCH_KEYRING_GID 0x0010
+#define SEARCH_KEYRING_ALL 0x001f
+
+extern int search_process_keyrings(unsigned search_mask,
+ const struct key_type *type,
+ const char *description,
+ struct key **_key);
+
+extern struct key root_user_keyring;
+extern int alloc_uid_keyring(struct user_struct *user);
+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);
+extern long get_process_keyring_ID(int specifier);
+extern long clear_process_keyring(int specifier);
+extern long new_session_keyring(void);
+extern long add_user_key(int specifier,
+ char __user *type,
+ char __user *description,
+ void __user *payload);
+
+#else /* CONFIG_KEYS */
+
+#define key_put(k) do { } while(0)
+#define alloc_uid_keyring(u) 0
+#define copy_keys(f,t) 0
+#define exit_keys(t) do { } while(0)
+#define suid_keys(t) 0
+#define exec_keys(t) 0
+#define get_process_keyring_ID(s) (-EINVAL)
+#define clear_process_keyring(s) (-EINVAL)
+#define new_session_keyring() (-EINVAL)
+#define add_user_key(s,t,d,p) (-EINVAL)
+
+#endif /* CONFIG_KEYS */
+#endif /* __KERNEL__ */
+#endif /* _LINUX_KEY_H */
diff -uNr linux-2.6.6/include/linux/prctl.h linux-2.6.6-keys/include/linux/prctl.h
--- linux-2.6.6/include/linux/prctl.h 2004-05-11 11:26:37.000000000 +0100
+++ linux-2.6.6-keys/include/linux/prctl.h 2004-05-11 11:32:06.000000000 +0100
@@ -43,5 +43,16 @@
# define PR_TIMING_TIMESTAMP 1 /* Accurate timestamp based
process timing */
+/* Manage a process's keyrings */
+#define PR_SPEC_THREAD_KEYRING 0 /* - specifier for thread-specific keyring */
+#define PR_SPEC_PROCESS_KEYRING 1 /* - specifier for process-specific keyring */
+#define PR_SPEC_SESSION_KEYRING 2 /* - specifier for session-specific keyring */
+#define PR_SPEC_USER_KEYRING 3 /* - specifier for UID-specific keyring */
+#define PR_SPEC_GROUP_KEYRING 4 /* - specifier for GID-specific keyring */
+
+#define PR_GET_KEYRING_ID 15 /* ask for specified keyring's ID */
+#define PR_CLEAR_KEYRING 16 /* clear contents of specified keyring */
+#define PR_NEW_SESSION_KEYRING 17 /* start a new session keyring */
+#define PR_ADD_NEW_KEY 18 /* add a key to specified keyring */
#endif /* _LINUX_PRCTL_H */
diff -uNr linux-2.6.6/include/linux/sched.h linux-2.6.6-keys/include/linux/sched.h
--- linux-2.6.6/include/linux/sched.h 2004-05-11 11:28:57.000000000 +0100
+++ linux-2.6.6-keys/include/linux/sched.h 2004-05-11 11:32:06.000000000 +0100
@@ -312,6 +312,10 @@
atomic_t processes; /* How many processes does this user have? */
atomic_t files; /* How many open files does this user have? */
+#ifdef CONFIG_KEYS
+ struct key *keyring; /* UID specific keyring */
+#endif
+
/* Hash table maintenance information */
struct list_head uidhash_list;
uid_t uid;
@@ -449,6 +453,11 @@
kernel_cap_t cap_effective, cap_inheritable, cap_permitted;
int keep_capabilities:1;
struct user_struct *user;
+#ifdef CONFIG_KEYS
+ 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 */
+#endif
/* limits */
struct rlimit rlim[RLIM_NLIMITS];
unsigned short used_math;
diff -uNr linux-2.6.6/kernel/exit.c linux-2.6.6-keys/kernel/exit.c
--- linux-2.6.6/kernel/exit.c 2004-05-11 11:28:57.000000000 +0100
+++ linux-2.6.6-keys/kernel/exit.c 2004-05-11 11:32:06.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>
@@ -790,6 +791,7 @@
__exit_fs(tsk);
exit_namespace(tsk);
exit_thread();
+ exit_keys(tsk);
if (tsk->signal->leader)
disassociate_ctty(1);
diff -uNr linux-2.6.6/kernel/fork.c linux-2.6.6-keys/kernel/fork.c
--- linux-2.6.6/kernel/fork.c 2004-05-11 11:28:57.000000000 +0100
+++ linux-2.6.6-keys/kernel/fork.c 2004-05-11 13:36:27.000000000 +0100
@@ -33,6 +33,7 @@
#include <linux/ptrace.h>
#include <linux/mount.h>
#include <linux/audit.h>
+#include <linux/key.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
@@ -972,8 +973,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;
@@ -1084,6 +1087,8 @@
bad_fork_cleanup_namespace:
exit_namespace(p);
+bad_fork_cleanup_keys:
+ exit_keys(p);
bad_fork_cleanup_mm:
exit_mm(p);
if (p->active_mm)
diff -uNr linux-2.6.6/kernel/sys.c linux-2.6.6-keys/kernel/sys.c
--- linux-2.6.6/kernel/sys.c 2004-05-11 11:28:57.000000000 +0100
+++ linux-2.6.6-keys/kernel/sys.c 2004-05-11 11:32:06.000000000 +0100
@@ -23,6 +23,7 @@
#include <linux/security.h>
#include <linux/dcookies.h>
#include <linux/suspend.h>
+#include <linux/key.h>
#include <asm/uaccess.h>
#include <asm/io.h>
@@ -1639,6 +1640,21 @@
}
current->keep_capabilities = arg2;
break;
+ case PR_GET_KEYRING_ID:
+ error = get_process_keyring_ID(arg2);
+ break;
+ case PR_CLEAR_KEYRING:
+ error = clear_process_keyring(arg2);
+ break;
+ case PR_NEW_SESSION_KEYRING:
+ error = new_session_keyring();
+ break;
+ case PR_ADD_NEW_KEY:
+ error = add_user_key(arg2,
+ (char __user *) arg3,
+ (char __user *) arg4,
+ (void __user *) arg5);
+ break;
default:
error = -EINVAL;
break;
diff -uNr linux-2.6.6/kernel/user.c linux-2.6.6-keys/kernel/user.c
--- linux-2.6.6/kernel/user.c 2004-05-11 11:27:00.000000000 +0100
+++ linux-2.6.6-keys/kernel/user.c 2004-05-11 11:32:06.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,10 @@
struct user_struct root_user = {
.__count = ATOMIC_INIT(1),
.processes = ATOMIC_INIT(1),
- .files = ATOMIC_INIT(0)
+ .files = ATOMIC_INIT(0),
+#ifdef CONFIG_KEYS
+ .keyring = &root_user_keyring,
+#endif
};
/*
@@ -73,6 +77,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 +103,11 @@
atomic_set(&new->processes, 0);
atomic_set(&new->files, 0);
+ if (alloc_uid_keyring(new) < 0) {
+ kmem_cache_free(uid_cachep, new);
+ return NULL;
+ }
+
/*
* Before adding this, check whether we raced
* on adding the same user already..
@@ -130,6 +140,7 @@
atomic_dec(&old_user->processes);
current->user = new_user;
free_uid(old_user);
+ suid_keys(current);
}
diff -uNr linux-2.6.6/security/Kconfig linux-2.6.6-keys/security/Kconfig
--- linux-2.6.6/security/Kconfig 2004-05-11 11:27:01.000000000 +0100
+++ linux-2.6.6-keys/security/Kconfig 2004-05-11 11:32:06.000000000 +0100
@@ -4,6 +4,23 @@
menu "Security options"
+config KEYS
+ bool "Enable access key retention support"
+ help
+ This option provides support for retaining authentication tokens and
+ access keys in the kernel.
+
+ It also includes provision of methods by which such keys might be
+ associated with a process so that network filesystems, encryption
+ support and the like can find them.
+
+ Furthermore, a special type of key is available that acts as keyring:
+ a searchable sequence of keys. Each process is equipped with access
+ to five standard keyrings: UID-specific, GID-specific, session,
+ process and thread.
+
+ If you are unsure as to whether this is required, answer N.
+
config SECURITY
bool "Enable different security models"
help
diff -uNr linux-2.6.6/security/keys/internal.h linux-2.6.6-keys/security/keys/internal.h
--- linux-2.6.6/security/keys/internal.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.6-keys/security/keys/internal.h 2004-05-11 11:32:06.000000000 +0100
@@ -0,0 +1,75 @@
+/* internal.h: authentication token and access key management internal defs
+ *
+ * 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 _INTERNAL_H
+#define _INTERNAL_H
+
+#include <linux/key.h>
+
+/*****************************************************************************/
+/*
+ * 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 struct rb_root key_serial_tree;
+extern struct semaphore key_serial_sem;
+extern struct list_head key_types_list;
+extern struct rw_semaphore key_types_sem;
+
+extern int key_alloc(struct key_type *type, struct key **_key);
+extern int keyring_alloc(struct key *source, struct key **_key);
+
+extern int __keyring_add_key(struct key *keyring, struct key *key);
+
+extern int keyring_set_name(struct key *keyring, const char *fmt, ...)
+__attribute__((format(printf, 2, 3)));
+
+
+#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
+
+#endif /* _INTERNAL_H */
diff -uNr linux-2.6.6/security/keys/key.c linux-2.6.6-keys/security/keys/key.c
--- linux-2.6.6/security/keys/key.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.6-keys/security/keys/key.c 2004-05-11 11:32:06.000000000 +0100
@@ -0,0 +1,714 @@
+/* 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/seq_file.h>
+#include <asm/errno.h>
+#include "internal.h"
+
+static kmem_cache_t *key_jar;
+static key_serial_t key_serial_next = 2;
+struct rb_root key_serial_tree;
+DECLARE_MUTEX(key_serial_sem);
+
+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_dead;
+
+static struct key_type key_type_keyring = {
+ .name = "keyring",
+ .link = { &key_type_dead.link, &key_types_list },
+ .match = keyring_match,
+ .clear = keyring_clear,
+ .describe = keyring_describe,
+};
+
+static struct key_type key_type_dead = {
+ .name = "dead",
+ .link = { &key_types_list, &key_type_keyring.link },
+ .match = NULL,
+ .clear = NULL,
+ .describe = NULL,
+};
+
+struct list_head key_types_list = {
+ .next = &key_type_keyring.link,
+ .prev = &key_type_dead.link,
+};
+DECLARE_RWSEM(key_types_sem);
+
+struct key root_user_keyring = {
+ .usage = ATOMIC_INIT(1),
+ .serial = 1,
+ .type = &key_type_keyring,
+ .lock = RW_LOCK_UNLOCKED,
+ .sem = __RWSEM_INITIALIZER(root_user_keyring.sem),
+#ifdef KEY_DEBUGGING
+ .magic = KEY_DEBUG_MAGIC,
+#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);
+ init_rwsem(&key->sem);
+ key->type = type;
+#ifdef KEY_DEBUGGING
+ key->magic = KEY_DEBUG_MAGIC;
+#endif
+
+ down(&key_serial_sem);
+
+ /* propose a serial number and try to insert it into the tree */
+ key->serial = key_serial_next;
+ if (key->serial < 2)
+ key->serial = 2;
+ key_serial_next = key->serial + 1;
+
+ 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 (;;) {
+ key->serial = key_serial_next;
+ if (key->serial < 2)
+ key->serial = 2;
+ key_serial_next = key->serial + 1;
+
+ 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);
+ up(&key_serial_sem);
+
+ *_key = key;
+ return 0;
+} /* end key_alloc() */
+
+/*****************************************************************************/
+/*
+ * dispose of a key
+ */
+void key_put(struct key *key)
+{
+ if (!key)
+ return;
+
+ key_validate(key);
+ down(&key_serial_sem);
+ if (atomic_dec_and_test(&key->usage)) {
+ key->flags |= KEY_FLAG_DEAD;
+ rb_erase(&key->serial_node, &key_serial_tree);
+
+ if (key->type->clear)
+ key->type->clear(key);
+#ifdef KEY_DEBUGGING
+ key->magic = KEY_DEBUG_MAGIC_X;
+#endif
+ kmem_cache_free(key_jar, key);
+ }
+
+ up(&key_serial_sem);
+
+} /* end key_put() */
+
+EXPORT_SYMBOL(key_put);
+
+/*****************************************************************************/
+/*
+ * retire a key from service
+ */
+void key_retire(struct key *key)
+{
+ key_validate(key);
+
+ down_write(&key->sem);
+ write_lock(&key->lock);
+ key->flags |= KEY_FLAG_RETIRED;
+ write_unlock(&key->lock);
+ up_write(&key->sem);
+} /* 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(source);
+
+ down_read(&source->sem);
+
+ /* duplicate the list of subscribed keys */
+ if (source->payload.subscriptions) {
+ const unsigned limit =
+ (PAGE_SIZE - sizeof(*klist)) / sizeof(struct key);
+
+ sklist = source->payload.subscriptions;
+ if (sklist && sklist->nkeys > 0) {
+ max = sklist->nkeys;
+ 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;
+
+ klist->maxkeys = max;
+ 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;
+ }
+ }
+
+ up_read(&source->sem);
+ return ret;
+
+ nomem:
+ up_read(&source->sem);
+ 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 char *description,
+ struct key **_key)
+{
+ struct {
+ struct key *keyring;
+ 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 ||
+ keyring->type != &key_type_dead)
+ 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, description)) {
+ if (key->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].kix = kix;
+ sp++;
+ goto descend;
+ }
+
+ next:
+ kix++;
+ }
+
+ retired:
+ read_unlock(&keyring->lock);
+
+ if (sp > 0) {
+ sp--;
+ keyring = stack[sp].keyring;
+ keylist = keyring->payload.subscriptions;
+ 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);
+
+/*****************************************************************************/
+/*
+ * see if a cycle will will be created by inserting acyclic tree B in acyclic
+ * tree A at the topmost level (ie: as a direct child of A)
+ * - since we are adding B to A at the top level, checking for cycles should
+ * just be a matter of seeing if node A is somewhere in tree B
+ */
+static int keyring_detect_cycle(struct key *A, struct key *B)
+{
+ struct {
+ struct key *subtree;
+ int kix;
+ } stack[KEYRING_SEARCH_MAX_DEPTH];
+
+ struct keyring_list *keylist;
+ struct key *subtree, *key;
+ int sp, kix, ret;
+
+ if (A == B)
+ return -EDEADLK;
+
+ subtree = B;
+ sp = 0;
+
+ descend:
+ read_lock(&subtree->lock);
+ if (subtree->flags & KEY_FLAG_RETIRED)
+ goto retired;
+
+ keylist = subtree->payload.subscriptions;
+ kix = 0;
+
+ ascend:
+ for (; kix < keylist->nkeys; kix++) {
+ key = keylist->keys[kix];
+
+ if (key == A)
+ goto cycle_detected;
+
+ if (key->type == &key_type_keyring) {
+ if (sp >= KEYRING_SEARCH_MAX_DEPTH)
+ goto too_deep;
+
+ stack[sp].subtree = subtree;
+ stack[sp].kix = kix;
+ sp++;
+ goto descend;
+ }
+ }
+
+ retired:
+ read_unlock(&subtree->lock);
+
+ if (sp > 0) {
+ sp--;
+ subtree = stack[sp].subtree;
+ keylist = subtree->payload.subscriptions;
+ kix = stack[sp].kix + 1;
+ goto ascend;
+ }
+
+ return 0;
+
+ too_deep:
+ ret = -ELOOP;
+ goto error;
+ cycle_detected:
+ ret = -EDEADLK;
+ error:
+ read_unlock(&subtree->lock);
+
+ for (; sp > 0; sp--)
+ read_unlock(&stack[sp].subtree->lock);
+ return ret;
+} /* end keyring_detect_cycle() */
+
+/*****************************************************************************/
+/*
+ * add a key to a keyring
+ */
+int __keyring_add_key(struct key *keyring, struct key *key)
+{
+ struct keyring_list *klist, *nklist;
+ unsigned max;
+ size_t size;
+ int ret;
+
+ if (keyring->flags & KEY_FLAG_RETIRED)
+ return -EINVAL;
+
+ /* check that we aren't going to create a cycle adding one keyring to
+ * another */
+ if (key->type == &key_type_keyring) {
+ ret = keyring_detect_cycle(keyring, key);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* add directly if sufficient slack space */
+ klist = keyring->payload.subscriptions;
+ if (klist && klist->nkeys < klist->maxkeys) {
+ atomic_inc(&key->usage);
+
+ write_lock(&keyring->lock);
+ klist->keys[klist->nkeys++] = key;
+ write_unlock(&keyring->lock);
+
+ return 0;
+ }
+
+ /* grow the key list */
+ max = 4;
+ if (klist)
+ max += klist->maxkeys;
+
+ size = sizeof(*klist) + sizeof(*key) * max;
+ if (size > PAGE_SIZE)
+ return -ENFILE;
+
+ nklist = kmalloc(size, GFP_KERNEL);
+ if (!nklist)
+ return -ENOMEM;
+ nklist->maxkeys = max;
+ nklist->nkeys = 0;
+
+ if (klist) {
+ nklist->nkeys = klist->nkeys;
+ memcpy(nklist->keys, klist->keys, sizeof(*key) * klist->nkeys);
+ }
+
+ atomic_inc(&key->usage);
+
+ write_lock(&keyring->lock);
+ keyring->payload.subscriptions = nklist;
+ nklist->keys[nklist->nkeys++] = key;
+ write_unlock(&keyring->lock);
+
+ if (klist)
+ kfree(klist);
+ return 0;
+
+} /* end __keyring_add_key() */
+
+/*****************************************************************************/
+/*
+ * add a key to a keyring
+ */
+int keyring_add_key(struct key *keyring, struct key *key)
+{
+ int ret;
+
+ key_validate(keyring);
+ key_validate(key);
+
+ down_write(&keyring->sem);
+ ret = __keyring_add_key(keyring, key);
+ up_write(&keyring->sem);
+
+ return ret;
+} /* 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);
+
+ if (keyring->type != &key_type_keyring)
+ return -EINVAL;
+
+ va_start(va, fmt);
+ len = vsnprintf(buf, 32, fmt, va);
+ va_end(va);
+
+ 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;
+
+ down_write(&keyring->sem);
+ write_lock(&keyring->lock);
+ keyring->description.ringname = kname;
+ write_unlock(&keyring->lock);
+ up_write(&keyring->sem);
+
+ return 0;
+} /* end keyring_set_name() */
+
+EXPORT_SYMBOL(keyring_set_name);
+
+/*****************************************************************************/
+/*
+ * match keyrings on their name
+ */
+static int keyring_match(const struct key *keyring, const void *description)
+{
+ struct keyring_name *kname;
+
+ kname = keyring->description.ringname;
+ if (kname)
+ if (strcmp(kname->name, description) == 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_puts(m, 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() */
+
+/*****************************************************************************/
+/*
+ * register a type of key
+ */
+int register_key_type(struct key_type *ktype)
+{
+ struct key_type *p;
+ int ret;
+
+ ret = -EEXIST;
+ down_write(&key_types_sem);
+
+ list_for_each_entry(p, &key_types_list, link) {
+ if (strcmp(p->name, ktype->name) == 0)
+ goto out;
+ }
+
+ list_add(&ktype->link, &key_types_list);
+ ret = 0;
+
+ out:
+ up_write(&key_types_sem);
+ return ret;
+} /* end register_key_type() */
+
+EXPORT_SYMBOL(register_key_type);
+
+/*****************************************************************************/
+/*
+ * unregister a type of key
+ */
+void unregister_key_type(struct key_type *ktype)
+{
+ struct rb_node *_n;
+ struct key *key;
+
+ down_write(&key_types_sem);
+
+ list_del_init(&ktype->link);
+
+ /* need to withdraw all keys of this type */
+ down(&key_serial_sem);
+
+ for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) {
+ key = rb_entry(_n, struct key, serial_node);
+
+ if (key->type != ktype)
+ continue;
+
+ write_lock(&key->lock);
+ key->type = &key_type_dead;
+ write_unlock(&key->lock);
+
+ /* nobody is going to look at description or payload anymore */
+ ktype->clear(key);
+ memset(&key->description, 0xbd, sizeof(key->description));
+ memset(&key->payload, 0xbd, sizeof(key->payload));
+ }
+
+ up(&key_serial_sem);
+ up_write(&key_types_sem);
+
+} /* end unregister_key_type() */
+
+EXPORT_SYMBOL(unregister_key_type);
+
+/*****************************************************************************/
+/*
+ * initialise the key management stuff
+ */
+static int __init key_init(void)
+{
+ 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");
+
+ 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);
diff -uNr linux-2.6.6/security/keys/Makefile linux-2.6.6-keys/security/keys/Makefile
--- linux-2.6.6/security/keys/Makefile 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.6-keys/security/keys/Makefile 2004-05-11 11:32:06.000000000 +0100
@@ -0,0 +1,9 @@
+#
+# Makefile for key management
+#
+
+obj-y := \
+ key.o \
+ process_keys.o
+
+obj-$(CONFIG_PROC_FS) += proc.o
diff -uNr linux-2.6.6/security/keys/proc.c linux-2.6.6-keys/security/keys/proc.c
--- linux-2.6.6/security/keys/proc.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.6-keys/security/keys/proc.c 2004-05-11 11:32:06.000000000 +0100
@@ -0,0 +1,120 @@
+/* proc.c: proc files for key database enumeration
+ *
+ * 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/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <asm/errno.h>
+#include "internal.h"
+
+static int proc_keys_open(struct inode *inode, struct file *file);
+static void *proc_keys_start(struct seq_file *p, loff_t *_pos);
+static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos);
+static void proc_keys_stop(struct seq_file *p, void *v);
+static int proc_keys_show(struct seq_file *m, void *v);
+
+static struct seq_operations proc_keys_ops = {
+ .start = proc_keys_start,
+ .next = proc_keys_next,
+ .stop = proc_keys_stop,
+ .show = proc_keys_show,
+};
+
+static struct file_operations proc_keys_fops = {
+ .open = proc_keys_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+/*****************************************************************************/
+/*
+ * implement "/proc/keys" to provides a list of the keys on the system
+ */
+static int proc_keys_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &proc_keys_ops);
+
+}
+
+static void *proc_keys_start(struct seq_file *p, loff_t *_pos)
+{
+ struct rb_node *_p;
+ loff_t pos = *_pos;
+
+ down(&key_serial_sem);
+
+ _p = rb_first(&key_serial_tree);
+ while (pos > 0 && _p) {
+ pos--;
+ _p = rb_next(_p);
+ }
+
+ return _p;
+}
+
+static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos)
+{
+ (*_pos)++;
+ return rb_next((struct rb_node *) v);
+}
+
+static void proc_keys_stop(struct seq_file *p, void *v)
+{
+ up(&key_serial_sem);
+}
+
+static int proc_keys_show(struct seq_file *m, void *v)
+{
+ struct rb_tree *_p = v;
+ struct key *key = rb_entry(_p, struct key, serial_node);
+
+ read_lock(&key->lock);
+
+ 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);
+
+ if (key->type->describe)
+ key->type->describe(key, m);
+ seq_putc(m, '\n');
+
+ read_unlock(&key->lock);
+
+ return 0;
+}
+
+/*****************************************************************************/
+/*
+ * declare the /proc files
+ */
+static int __init key_proc_init(void)
+{
+ struct proc_dir_entry *p;
+
+ p = create_proc_entry("keys", 0, NULL);
+ if (!p)
+ panic("Cannot create /proc/keys\n");
+
+ p->proc_fops = &proc_keys_fops;
+
+ return 0;
+} /* end key_proc_init() */
+
+__initcall(key_proc_init);
diff -uNr linux-2.6.6/security/keys/process_keys.c linux-2.6.6-keys/security/keys/process_keys.c
--- linux-2.6.6/security/keys/process_keys.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.6-keys/security/keys/process_keys.c 2004-05-11 11:32:06.000000000 +0100
@@ -0,0 +1,510 @@
+/* process_keys.c: management of process keyrings
+ *
+ * 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/prctl.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <asm/errno.h>
+#include <asm/uaccess.h>
+#include "internal.h"
+
+/*****************************************************************************/
+/*
+ * allocate a keyring to associate with a UID
+ */
+int alloc_uid_keyring(struct user_struct *user)
+{
+ struct key *keyring;
+ int ret;
+
+ ret = keyring_alloc(NULL, &keyring);
+ if (ret < 0)
+ return ret;
+
+ ret = keyring_set_name(keyring, "_uid.%u", user->uid);
+ if (ret < 0) {
+ key_put(keyring);
+ return ret;
+ }
+
+ user->keyring = keyring;
+ return 0;
+} /* end alloc_uid_keyring() */
+
+/*****************************************************************************/
+/*
+ * install a fresh thread keyring, discarding the old one
+ */
+static int install_thread_keyring(struct task_struct *tsk)
+{
+ struct key *keyring;
+ int ret;
+
+ ret = keyring_alloc(NULL, &keyring);
+ if (ret < 0)
+ return ret;
+
+ ret = keyring_set_name(keyring, "_tid.%d", tsk->pid);
+ if (ret < 0) {
+ key_put(keyring);
+ return ret;
+ }
+
+ key_put(xchg(&tsk->thread_keyring, keyring));
+
+ return 0;
+} /* end install_thread_keyring() */
+
+/*****************************************************************************/
+/*
+ * install a fresh process keyring, discarding the old one
+ */
+static int install_process_keyring(struct task_struct *tsk)
+{
+ struct key *keyring;
+ int ret;
+
+ ret = keyring_alloc(NULL, &keyring);
+ if (ret < 0)
+ return ret;
+
+ ret = keyring_set_name(keyring, "_pid.%d", tsk->tgid);
+ if (ret < 0) {
+ key_put(keyring);
+ return ret;
+ }
+
+ key_put(xchg(&tsk->process_keyring, keyring));
+
+ return 0;
+} /* end install_process_keyring() */
+
+/*****************************************************************************/
+/*
+ * install a fresh session keyring, discarding the old one
+ */
+static int install_session_keyring(struct task_struct *tsk)
+{
+ struct key *keyring;
+ int ret;
+
+ ret = keyring_alloc(NULL, &keyring);
+ if (ret < 0)
+ return ret;
+
+ ret = keyring_set_name(keyring, "_ses.%u", keyring->serial);
+ if (ret < 0) {
+ key_put(keyring);
+ return ret;
+ }
+
+ key_put(xchg(&tsk->session_keyring, keyring));
+
+ return 0;
+} /* end install_session_keyring() */
+
+/*****************************************************************************/
+/*
+ * select the keyring specified by the user, making sure it exists
+ */
+static int select_keyring(int specifier, struct key **_keyring)
+{
+ struct task_struct *tsk = current;
+ int ret;
+
+ switch (specifier) {
+ case PR_SPEC_THREAD_KEYRING:
+ if (!tsk->thread_keyring) {
+ ret = install_thread_keyring(tsk);
+ if (ret < 0)
+ return ret;
+ }
+ *_keyring = tsk->thread_keyring;
+ return 0;
+
+ case PR_SPEC_PROCESS_KEYRING:
+ if (!tsk->process_keyring) {
+ ret = install_process_keyring(tsk);
+ if (ret < 0)
+ return ret;
+ }
+ *_keyring = tsk->process_keyring;
+ return 0;
+
+ case PR_SPEC_SESSION_KEYRING:
+ if (!tsk->session_keyring) {
+ ret = install_session_keyring(tsk);
+ if (ret < 0)
+ return ret;
+ }
+ *_keyring = tsk->session_keyring;
+ return 0;
+
+ case PR_SPEC_USER_KEYRING:
+ *_keyring = tsk->user->keyring;
+ return 0;
+
+ case PR_SPEC_GROUP_KEYRING:
+ default:
+ return -EINVAL;
+ }
+} /* end select_keyring() */
+
+/*****************************************************************************/
+/*
+ * get the ID of the specified process keyring
+ * - implements prctl(PR_GET_KEYRING_ID)
+ */
+long get_process_keyring_ID(int specifier)
+{
+ struct key *keyring;
+ int ret;
+
+ ret = select_keyring(specifier, &keyring);
+
+ return ret < 0 ? ret : keyring->serial;
+
+} /* end get_process_keyring_ID() */
+
+/*****************************************************************************/
+/*
+ * clear the specified process keyring
+ * - implements prctl(PR_CLEAR_KEYRING)
+ */
+long clear_process_keyring(int specifier)
+{
+ struct keyring_list *klist;
+ struct task_struct *tsk = current;
+ struct key *keyring;
+ int loop;
+
+ switch (specifier) {
+ case PR_SPEC_THREAD_KEYRING:
+ if (!tsk->thread_keyring)
+ return 0;
+ keyring = tsk->thread_keyring;
+ break;
+
+ case PR_SPEC_PROCESS_KEYRING:
+ if (!tsk->process_keyring)
+ return 0;
+ keyring = tsk->process_keyring;
+ break;
+
+ case PR_SPEC_SESSION_KEYRING:
+ if (!tsk->session_keyring)
+ return 0;
+ keyring = tsk->session_keyring;
+ break;
+
+ case PR_SPEC_USER_KEYRING:
+ keyring = tsk->user->keyring;
+
+ case PR_SPEC_GROUP_KEYRING:
+ default:
+ return -EINVAL;
+ }
+
+ down_write(&keyring->sem);
+ klist = keyring->payload.subscriptions;
+ if (klist) {
+ write_lock(&keyring->lock);
+ keyring->payload.subscriptions = NULL;
+ write_unlock(&keyring->lock);
+ }
+ up_write(&keyring->sem);
+
+ if (klist) {
+ for (loop = klist->nkeys - 1; loop >= 0; loop--)
+ key_put(klist->keys[loop]);
+
+ kfree(klist);
+ }
+
+ return 0;
+} /* end clear_process_keyring() */
+
+/*****************************************************************************/
+/*
+ * install a new session keyring, discarding the old one
+ * - implements prctl(PR_NEW_SESSION_KEYRING)
+ */
+long new_session_keyring(void)
+{
+ struct task_struct *tsk = current;
+ int ret;
+
+ ret = install_session_keyring(tsk);
+ if (ret < 0)
+ return ret;
+
+ return tsk->session_keyring ? tsk->session_keyring->serial : 0;
+
+} /* end new_session_keyring() */
+
+/*****************************************************************************/
+/*
+ * add a new key to the specified process keyring
+ */
+int add_process_key(int specifier,
+ const char *type,
+ const char *description,
+ int plen,
+ const void *payload)
+{
+ struct keyring_list *klist;
+ struct key_type *ktype;
+ struct key *keyring, *key;
+ int loop, ret;
+
+ ret = select_keyring(specifier, &keyring);
+ if (ret < 0)
+ return ret;
+
+ down_read(&key_types_sem);
+
+ /* look up the type */
+ ret = -ENOENT;
+ list_for_each_entry(ktype, &key_types_list, link) {
+ if (strcmp(ktype->name, type) == 0)
+ goto found_type;
+ }
+ goto error;
+
+ found_type:
+ ret = -EINVAL;
+ if (!ktype->match)
+ goto error;
+
+ ret = -EISDIR;
+ if (!ktype->init)
+ goto error;
+
+ /* search for an existing key of the same type and description */
+ down_write(&keyring->sem);
+
+ klist = keyring->payload.subscriptions;
+ if (klist) {
+ for (loop = 0; loop < klist->nkeys; loop++) {
+ key = klist->keys[loop];
+ if (key->type == ktype &&
+ key->type->match(key, description) &&
+ !key->flags & KEY_FLAG_RETIRED)
+ goto update;
+ }
+ }
+
+ /* generate a new key and initialise it */
+ ret = key_alloc(ktype, &key);
+ if (ret < 0)
+ goto error2;
+
+ ret = ktype->init(key, description, plen, payload);
+ if (ret < 0)
+ goto error3;
+
+ ret = __keyring_add_key(keyring, key);
+
+ error3:
+ key_put(key);
+ error2:
+ up_write(&keyring->sem);
+ error:
+ up_read(&key_types_sem);
+ return ret;
+
+ /* update an existing key */
+ update:
+ ret = -EEXIST;
+ if (ktype->update)
+ ret = ktype->update(key, description, plen, payload);
+ goto error2;
+
+} /* end add_process_key() */
+
+EXPORT_SYMBOL(add_process_key);
+
+/*****************************************************************************/
+/*
+ * extract the description of a new key from userspace and add it as a key to
+ * one of the process's keyrings
+ * - implements prctl(PR_ADD_NEW_KEY)
+ */
+long add_user_key(int specifier,
+ char __user *_type,
+ char __user *_description,
+ void __user *_payload)
+{
+ char type[32], *description;
+ void *payload;
+ long dlen;
+ int ret, plen;
+
+ ret = strncpy_from_user(type, _type, sizeof(type) - 1);
+ if (ret < 0)
+ return ret;
+ type[31] = '\0';
+
+ dlen = strnlen_user(_description, PAGE_SIZE - 1);
+ if (dlen <= 0)
+ return -EFAULT;
+ if (dlen > PAGE_SIZE - 1)
+ return -EINVAL;
+
+ description = kmalloc(dlen, GFP_KERNEL);
+ if (!description)
+ return -ENOMEM;
+ ret = -EFAULT;
+ if (copy_from_user(description, _description, dlen) != 0)
+ goto error;
+
+ ret = get_user(plen, (uint16_t *) _payload);
+ if (ret < 0)
+ goto error;
+ _payload += 2;
+
+ ret = -EINVAL;
+ if (plen < 0 || plen > PAGE_SIZE)
+ goto error;
+
+ ret = -ENOMEM;
+ payload = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!payload)
+ goto error;
+
+ ret = -EFAULT;
+ if (copy_from_user(payload, _payload, plen) != 0)
+ goto error2;
+
+ ret = add_process_key(specifier, type, description, plen, payload);
+
+ error2:
+ free_page((unsigned long) payload);
+ error:
+ kfree(description);
+ return ret;
+} /* end add_user_key() */
+
+/*****************************************************************************/
+/*
+ * 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 {
+ tsk->process_keyring = NULL;
+ ret = install_process_keyring(tsk);
+ }
+ }
+
+ 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)
+{
+ return tsk->session_keyring ? install_session_keyring(tsk) : 0;
+
+} /* end suid_keys() */
+
+/*****************************************************************************/
+/*
+ * deal with execve()
+ */
+int exec_keys(struct task_struct *tsk)
+{
+ key_put(xchg(&tsk->thread_keyring, NULL));
+
+ if (!tsk->session_keyring)
+ if (install_session_keyring(tsk) < 0)
+ return -ENOMEM;
+
+ return install_process_keyring(tsk);
+
+} /* end exec_keys() */
+
+/*****************************************************************************/
+/*
+ * search selected process keyrings for the first matching key
+ */
+int search_process_keyrings(unsigned search_mask,
+ const struct key_type *type,
+ const char *description,
+ struct key **_key)
+{
+ struct task_struct *tsk = current;
+ int ret;
+
+ if (search_mask & SEARCH_KEYRING_THREAD && !tsk->thread_keyring) {
+ ret = keyring_search(tsk->thread_keyring,
+ type, description, _key);
+ if (ret == 0)
+ return ret;
+ }
+
+ if (search_mask & SEARCH_KEYRING_PROCESS && !tsk->process_keyring) {
+ ret = keyring_search(tsk->process_keyring,
+ type, description, _key);
+ if (ret == 0)
+ return ret;
+ }
+
+ if (search_mask & SEARCH_KEYRING_SESSION && !tsk->session_keyring) {
+ ret = keyring_search(tsk->session_keyring,
+ type, description, _key);
+ if (ret == 0)
+ return ret;
+ }
+
+ if (search_mask & SEARCH_KEYRING_UID && !tsk->user->keyring) {
+ ret = keyring_search(tsk->user->keyring,
+ type, description, _key);
+ if (ret == 0)
+ return ret;
+ }
+
+ return -ENOENT;
+} /* end search_process_keyrings() */
diff -uNr linux-2.6.6/security/Makefile linux-2.6.6-keys/security/Makefile
--- linux-2.6.6/security/Makefile 2004-05-11 11:27:01.000000000 +0100
+++ linux-2.6.6-keys/security/Makefile 2004-05-11 11:32:06.000000000 +0100
@@ -2,6 +2,7 @@
# Makefile for the kernel security code
#
+obj-$(CONFIG_KEYS) += keys/
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
# if we don't select a security model, use the default capabilities
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-15 9:36 ` David Howells
@ 2004-06-15 19:00 ` Kyle Moffett
2004-06-15 22:07 ` Chris Wright
2004-06-16 14:22 ` David Howells
2004-06-15 22:29 ` Chris Wright
2004-06-15 23:59 ` Kyle Moffett
2 siblings, 2 replies; 40+ messages in thread
From: Kyle Moffett @ 2004-06-15 19:00 UTC (permalink / raw)
To: David Howells; +Cc: Blair Strang, Trond Myklebust, lkml
On Jun 15, 2004, at 05:36, David Howells wrote:
> You might want to look at this patch. It's what I've come up with to
> support
> kafs, but it's general, and should work for anything. It's been built
> along
> Linus's guidelines, and has Linus's approval, contingent on something
> actually
> using it fully.
>
> You can use the session keyring number as a PAG ID if you wish.
>
> I've a sample aklog program (key submission) should you be interested.
One thing that I would very much like to have is the ability to create
a new
shell with a new keyring, such that I can still see and use the old
keyring,
but I can create new keys without modifying the old keyring, even to the
extent of masking out keys in the old keyring without modifying them for
other processes. From my brief glance at your patch, that's not a
feature
you have implemented. I would also like the ability to mark a key as
unreadable except by kernel threads or processes with CAP_KEYRING.
If I can pass key "handles" of some sort over UNIX sockets, then I can
also pass an unreadable key to a daemon process which uses it to
access my files until I revoke the key.
Cheers,
Kyle Moffett
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-15 19:00 ` Kyle Moffett
@ 2004-06-15 22:07 ` Chris Wright
2004-06-15 23:48 ` Kyle Moffett
2004-06-16 14:22 ` David Howells
1 sibling, 1 reply; 40+ messages in thread
From: Chris Wright @ 2004-06-15 22:07 UTC (permalink / raw)
To: Kyle Moffett; +Cc: David Howells, Blair Strang, Trond Myklebust, lkml
* Kyle Moffett (mrmacman_g4@mac.com) wrote:
> One thing that I would very much like to have is the ability to create
> a new
> shell with a new keyring, such that I can still see and use the old
> keyring,
> but I can create new keys without modifying the old keyring, even to the
> extent of masking out keys in the old keyring without modifying them for
> other processes. From my brief glance at your patch, that's not a
> feature you have implemented.
Sounds like a CLONE_KEYRING flag?
thanks,
-chris
--
Linux Security Modules http://lsm.immunix.org http://lsm.bkbits.net
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-15 9:36 ` David Howells
2004-06-15 19:00 ` Kyle Moffett
@ 2004-06-15 22:29 ` Chris Wright
2004-06-16 14:37 ` David Howells
2004-06-15 23:59 ` Kyle Moffett
2 siblings, 1 reply; 40+ messages in thread
From: Chris Wright @ 2004-06-15 22:29 UTC (permalink / raw)
To: David Howells; +Cc: Trond Myklebust, Blair Strang, Kyle Moffett, lkml
* David Howells (dhowells@redhat.com) wrote:
> You might want to look at this patch. It's what I've come up with to support
> kafs, but it's general, and should work for anything. It's been built along
> Linus's guidelines, and has Linus's approval, contingent on something actually
> using it fully.
>
> You can use the session keyring number as a PAG ID if you wish.
>
> I've a sample aklog program (key submission) should you be interested.
I'd be intereseted. BTW, I just took a brief look and had a quick
question.
> + if (bprm->e_uid != current->uid)
> + suid_keys(current);
> + exec_keys(current);
> +
would the security module be expected update/revoke keys if the thing changes
security domains on exec?
> task_lock(current);
> unsafe = unsafe_exec(current);
> security_bprm_apply_creds(bprm, unsafe);
thanks,
-chris
--
Linux Security Modules http://lsm.immunix.org http://lsm.bkbits.net
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-15 22:07 ` Chris Wright
@ 2004-06-15 23:48 ` Kyle Moffett
2004-06-16 0:01 ` Chris Wright
0 siblings, 1 reply; 40+ messages in thread
From: Kyle Moffett @ 2004-06-15 23:48 UTC (permalink / raw)
To: Chris Wright; +Cc: Blair Strang, Trond Myklebust, lkml, David Howells
On Jun 15, 2004, at 18:07, Chris Wright wrote:
> * Kyle Moffett (mrmacman_g4@mac.com) wrote:
>> One thing that I would very much like to have is the ability to create
>> a new
>> shell with a new keyring, such that I can still see and use the old
>> keyring,
>> but I can create new keys without modifying the old keyring, even to
>> the
>> extent of masking out keys in the old keyring without modifying them
>> for
>> other processes. From my brief glance at your patch, that's not a
>> feature you have implemented.
> Sounds like a CLONE_KEYRING flag?
I think the two concepts are unrelated. You should not be required
to create a new thread/process/task in order to give yourself a
separate key-ring, and it would be plain stupid to have one mode
of the clone() syscall that doesn't create a new task but instead
changes key-rings Take Apache and suexec PHP for example: it
would be very useful to be able to have a key-ring owned by the
root user that contains the AFS keys Apache uses to access files.
Then when it runs a suexec PHP script, it adds a new key-ring
owned by "someuser" to the process (without doing a clone()).
It does a seteuid("someuser"), then proceeds with the PHP code.
That gives the user's PHP its own key-ring context, and protects
the parent's key-ring. When done it removes "someuser"'s keys
and does seteuid(0).
Cheers,
Kyle Moffett
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-15 9:36 ` David Howells
2004-06-15 19:00 ` Kyle Moffett
2004-06-15 22:29 ` Chris Wright
@ 2004-06-15 23:59 ` Kyle Moffett
2004-06-16 14:49 ` David Howells
2 siblings, 1 reply; 40+ messages in thread
From: Kyle Moffett @ 2004-06-15 23:59 UTC (permalink / raw)
To: David Howells; +Cc: Blair Strang, Trond Myklebust, lkml
On Jun 15, 2004, at 05:36, David Howells wrote:
> You might want to look at this patch. It's what I've come up with to
> support
> kafs, but it's general, and should work for anything. It's been built
> along
> Linus's guidelines, and has Linus's approval, contingent on something
> actually
> using it fully.
One other thing that I'm not certain about in this patch is if there
is actually an important difference between "process" and
"session" key-rings. I believe that the "session" distinction
should be left up to user-space software like PAM to determine
which key-ring "session" a process should belong to. The user
and group key-rings are a good idea, so I guess the order with
which key-rings are checked for keys is:
Thread
Process
Session???
User
Group
Cheers,
Kyle Moffett
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-15 23:48 ` Kyle Moffett
@ 2004-06-16 0:01 ` Chris Wright
2004-06-16 0:06 ` Kyle Moffett
0 siblings, 1 reply; 40+ messages in thread
From: Chris Wright @ 2004-06-16 0:01 UTC (permalink / raw)
To: Kyle Moffett
Cc: Chris Wright, Blair Strang, Trond Myklebust, lkml, David Howells
* Kyle Moffett (mrmacman_g4@mac.com) wrote:
> On Jun 15, 2004, at 18:07, Chris Wright wrote:
> > * Kyle Moffett (mrmacman_g4@mac.com) wrote:
> >> One thing that I would very much like to have is the ability to create
> >> a new
> >> shell with a new keyring, such that I can still see and use the old
> >> keyring,
> >> but I can create new keys without modifying the old keyring, even to
> >> the
> >> extent of masking out keys in the old keyring without modifying them
> >> for
> >> other processes. From my brief glance at your patch, that's not a
> >> feature you have implemented.
> > Sounds like a CLONE_KEYRING flag?
>
> I think the two concepts are unrelated. You should not be required
> to create a new thread/process/task in order to give yourself a
Just commenting on your desire to "create a new shell with a new
keyring.." This had clone() implicit in it.
thanks,
-chris
--
Linux Security Modules http://lsm.immunix.org http://lsm.bkbits.net
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-16 0:01 ` Chris Wright
@ 2004-06-16 0:06 ` Kyle Moffett
0 siblings, 0 replies; 40+ messages in thread
From: Kyle Moffett @ 2004-06-16 0:06 UTC (permalink / raw)
To: Chris Wright; +Cc: Blair Strang, Trond Myklebust, lkml, David Howells
On Jun 15, 2004, at 20:01, Chris Wright wrote:
> Just commenting on your desire to "create a new shell with a new
> keyring.." This had clone() implicit in it.
Ah, it did indeed. Sorry for the confusion! :-D
Cheers,
Kyle Moffett
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-15 19:00 ` Kyle Moffett
2004-06-15 22:07 ` Chris Wright
@ 2004-06-16 14:22 ` David Howells
1 sibling, 0 replies; 40+ messages in thread
From: David Howells @ 2004-06-16 14:22 UTC (permalink / raw)
To: Kyle Moffett; +Cc: Blair Strang, Linus Torvalds, lkml
> One thing that I would very much like to have is the ability to create a new
> shell with a new keyring, such that I can still see and use the old keyring,
> but I can create new keys without modifying the old keyring, even to the
> extent of masking out keys in the old keyring without modifying them for
> other processes. From my brief glance at your patch, that's not a feature
> you have implemented.
Hmmm... What exactly are you wanting to do? Each task theoretically subscribes
to five keyrings (the group one isn't yet there) in this model; three of which
are transferred across a fork, and four across CLONE_THREAD.
The five keyrings are:
- Group (associated with primary GID)
- User (associated with UID)
- Session (voluntarily discarded)
- Process (shared between threads in a process)
- Thread (one per thread)
> I would also like the ability to mark a key as unreadable except by kernel
> threads or processes with CAP_KEYRING.
What do you mean by "unreadable"?
Currently, userspace can't see the data attached to a key. It can only see the
description, and only then through /proc/keys.
> If I can pass key "handles" of some sort over UNIX sockets, then I can also
> pass an unreadable key to a daemon process which uses it to access my files
> until I revoke the key.
I can see what you're getting at.
I think I need to create some more operations:
(*) Retire/Revoke key
(*) Add key to another keyring
(*) Remove key from keyring
(*) List keyring
(*) Describe key
(*) Read key (if not protected)
(*) Create keyring
I have pondered representing keyspace with some sort of filesystem interface
(using vfs ops to represent the operations), but that could require hardlinked
directories (keyrings) to pull off - either that or symlinks.
Also, there's the problem of security on the operations themselves. How do you
determine what a process is allowed to do?
Either I should only allow access to keys and keyrings to which a process is
subscribed, or I should attach UID/GID/MASK values to every key and keyring.
David
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-15 22:29 ` Chris Wright
@ 2004-06-16 14:37 ` David Howells
0 siblings, 0 replies; 40+ messages in thread
From: David Howells @ 2004-06-16 14:37 UTC (permalink / raw)
To: Chris Wright; +Cc: Blair Strang, Kyle Moffett, Linus Torvalds, lkml
[-- Attachment #1: Type: text/plain, Size: 874 bytes --]
> > I've a sample aklog program (key submission) should you be interested.
>
> I'd be intereseted. BTW, I just took a brief look and had a quick
> question.
Please see attached files.
key_afs.c Rudimentary kAFS filesystem token handling
afsutil.h }
kernel.c } aklog program
aklog.c }
> > + if (bprm->e_uid != current->uid)
> > + suid_keys(current);
> > + exec_keys(current);
> > +
>
> would the security module be expected update/revoke keys if the thing changes
> security domains on exec?
I don't know. Currently this patch replaces the old session keyring in favour
of a new empty one upon SUID exec. I suspect that depends on the policy set by
the administrator.
If you've a better suggestion than what I've done, feel free to make it.
> > task_lock(current);
> > unsafe = unsafe_exec(current);
> > security_bprm_apply_creds(bprm, unsafe);
David
[-- Attachment #2: key_afs.c --]
[-- Type: application/octet-stream, Size: 3084 bytes --]
/* key_afs.c: AFS filesystem keys
*
* 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/seq_file.h>
#include <asm/errno.h>
#include "cell.h"
#include "internal.h"
static int afs_key_init(struct key *key, const char *desc,
size_t datalen, const char *data);
static int afs_key_match(const struct key *key, const void *desc);
static void afs_key_clear(struct key *key);
static void afs_key_describe(const struct key *keyring, struct seq_file *m);
struct afs_key_data {
uint16_t session_key_size;
uint16_t ticket_size;
int32_t kvno;
time_t expiry;
uint8_t data[0];
};
/* AFS Kerberos ticket
* - the description must be the name of the cell to which applicable
* - the data is a struct afs_key_data
*/
struct key_type key_type_afs = {
.name = "afs",
.link = LIST_HEAD_INIT(key_type_afs.link),
.init = afs_key_init,
.match = afs_key_match,
.clear = afs_key_clear,
.describe = afs_key_describe,
};
static int afs_key_init(struct key *key, const char *desc,
size_t datalen, const char *data)
{
struct afs_key_data *keydata = (void *) data;
size_t dlen;
kenter("{%u},%s,%zu,{sk=%hu,tkt=%hu,v=%d,xp=%x}",
key->serial, desc, datalen,
keydata->session_key_size,
keydata->ticket_size,
keydata->kvno,
(int) keydata->expiry);
dlen = strlen(desc) + 1;
key->description.data = kmalloc(dlen, GFP_KERNEL);
if (!key->description.data) {
kleave(" = -ENOMEM");
return -ENOMEM;
}
memcpy(key->description.data, desc, dlen);
key->payload.data = kmalloc(datalen, GFP_KERNEL);
if (!key->payload.data) {
kleave(" = -ENOMEM");
return -ENOMEM;
}
key->datalen = datalen;
memcpy(key->payload.data, data, datalen);
kleave(" = 0");
return 0;
}
static int afs_key_match(const struct key *key, const void *desc)
{
if (!key->description.data)
return 0;
return strcmp(key->description.data, desc) == 0 ? 1 : 0;
}
static void afs_key_clear(struct key *key)
{
if (key->description.data)
kfree(key->description.data);
if (key->payload.data)
kfree(key->payload.data);
}
static void afs_key_describe(const struct key *key, struct seq_file *m)
{
struct afs_key_data *keydata;
if (!key->description.data) {
seq_puts(m, "[anon]");
return;
}
keydata = key->payload.data;
seq_printf(m, "%s => { s=%hu t=%hu v=%d x=%lx }",
(char *) key->description.data,
keydata->session_key_size,
keydata->ticket_size,
keydata->kvno,
(int) keydata->expiry - CURRENT_TIME.tv_sec);
}
int __init afs_key_register(void)
{
return register_key_type(&key_type_afs);
}
void __exit afs_key_unregister(void)
{
unregister_key_type(&key_type_afs);
}
[-- Attachment #3: afsutil.h --]
[-- Type: application/octet-stream, Size: 762 bytes --]
/* afsutil.h: AFS client utility library
*
* 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 _AFS_UTIL_H
#define _AFS_UTIL_H
#include <stdlib.h>
#include <stdint.h>
extern int afsutil_authorise_krb5(const char *cell,
const char *realm,
time_t expiry,
size_t session_key_size,
const void *session_key,
int ticket_kvno,
size_t ticket_size,
const void *ticket);
#endif /* _AFS_UTIL_H */
[-- Attachment #4: kernel.c --]
[-- Type: application/octet-stream, Size: 2527 bytes --]
/* kernel.c: routines for talking to the kernel
*
* 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 <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <alloca.h>
#include <sys/prctl.h>
/* Manage a process's keyrings */
#define PR_SPEC_THREAD_KEYRING 0 /* - specifier for thread-specific keyring */
#define PR_SPEC_PROCESS_KEYRING 1 /* - specifier for process-specific keyring */
#define PR_SPEC_SESSION_KEYRING 2 /* - specifier for session-specific keyring */
#define PR_SPEC_USER_KEYRING 3 /* - specifier for UID-specific keyring */
#define PR_SPEC_GROUP_KEYRING 4 /* - specifier for GID-specific keyring */
#define PR_GET_KEYRING_ID 15 /* ask for specified keyring's ID */
#define PR_CLEAR_KEYRING 16 /* clear contents of specified keyring */
#define PR_NEW_SESSION_KEYRING 17 /* start a new session keyring */
#define PR_ADD_NEW_KEY 18 /* add a key to specified keyring */
typedef int32_t key_serial_t;
struct afs_key_data {
uint16_t session_key_size;
uint16_t ticket_size;
int32_t kvno;
time_t expiry;
uint8_t data[0];
};
/*****************************************************************************/
/*
* pass authorisation information to the kernel indicating that we have a valid
* kerberos4 ticket
*/
int afsutil_authorise_krb5(const char *cell,
const char *realm,
time_t expiry,
size_t session_key_size,
const void *session_key,
int ticket_kvno,
size_t ticket_size,
const void *ticket)
{
struct afs_key_data *keydata;
uint16_t payload_size;
void *buffer;
int ret;
payload_size =
sizeof(struct afs_key_data) +
session_key_size +
ticket_size;
buffer = alloca(sizeof(uint16_t) + payload_size);
*(uint16_t *) buffer = payload_size;
keydata = buffer + sizeof(uint16_t);
keydata->session_key_size = session_key_size;
keydata->ticket_size = ticket_size;
keydata->kvno = ticket_kvno;
keydata->expiry = expiry;
memcpy(keydata->data, session_key, session_key_size);
memcpy(keydata->data + session_key_size, ticket, ticket_size);
if (prctl(PR_ADD_NEW_KEY, PR_SPEC_SESSION_KEYRING,
"afs", cell, buffer) < 0)
return -1;
return 0;
} /* end afsutil_authorise_krb5() */
[-- Attachment #5: aklog.c --]
[-- Type: application/octet-stream, Size: 5621 bytes --]
/* aklog.c: request a Kerberos ticket grant for an AFS cell
*
* 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 <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <krb5.h>
#include <com_err.h>
#include <kerberosIV/krb.h>
#include <afsutil.h>
extern int krb524_convert_creds_kdc(krb5_context, krb5_creds *, CREDENTIALS *);
char *realm = "CAMBRIDGE.REDHAT.COM"; //NULL;
char *cell = "cambridge.redhat.com"; //NULL;
int debug = 0;
krb5_principal princ;
krb5_context krb5context;
krb5_ccache tktcache;
static void format(void) __attribute__((noreturn));
static void format(void)
{
fprintf(stderr,
"aklog [-d] [-c] [<cell>] [-k <realm>]\n");
exit(2);
}
#define KRBERR(X,M) do { if ((X) != KSUCCESS) krberr((X),(M)); } while(0)
void krberr(errcode_t kerr, const char *where) __attribute__((noreturn));
void krberr(errcode_t kerr, const char *where)
{
fprintf(stderr, "%s: %s\n", where, error_message(kerr));
exit(1);
}
/*****************************************************************************/
/*
* parse the argument list
*/
void parse_args(char **argv)
{
if (!*argv)
return;
if (strcmp(argv[0], "-help") == 0)
format();
if (strcmp(argv[0], "-d") == 0) {
debug++;
argv++;
}
if (strcmp(argv[0], "-c") == 0)
argv++;
if (!*argv)
format();
if (argv[0][0] == '-')
format();
cell = *argv;
argv++;
if (!*argv)
return;
if (strcmp(argv[0], "-k") != 0)
format();
argv++;
if (!*argv)
return;
if (argv[0][0] == '-')
format();
realm = *argv;
argv++;
if (*argv)
format();
} /* end parse_args() */
/*****************************************************************************/
/*
* try to obtain a kerberos ticket
*/
int obtain_ticket(krb5_creds **creds, const char *service, const char *cell)
{
krb5_error_code kerr;
krb5_creds request;
/* set up a description of what we actually want */
memset(&request, 0, sizeof(request));
kerr = krb5_build_principal(krb5context, &request.server,
strlen(realm), realm,
service, cell,
NULL);
KRBERR(kerr, "failed to construct request");
request.client = princ;
request.times.endtime = 0;
request.keyblock.enctype = ENCTYPE_DES_CBC_CRC;
/* go and prod the Kerberos servers */
kerr = krb5_get_credentials(krb5context, 0, tktcache, &request, creds);
if (kerr != KSUCCESS && kerr != KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN)
KRBERR(kerr, "error talking to KDC");
return kerr == KSUCCESS;
} /* end obtain_ticket() */
/*****************************************************************************/
/*
*
*/
int main(int argc, char *argv[])
{
krb5_error_code kerr;
unsigned char *cp;
krb5_creds *creds5;
CREDENTIALS creds4;
time_t time;
char buf1[100];
int loop;
parse_args(argv + 1);
kerr = krb5_init_context(&krb5context);
KRBERR(kerr, "failed to initialise the Kerberos5 context");
/* may need to find the default realm */
if (!realm) {
kerr = krb5_get_default_realm(krb5context, &realm);
KRBERR(kerr, "failed to get the default realm");
}
/* may need to find the default AFS cell */
if (!cell) {
}
/* open the appropriate Kerberos ticket cache */
kerr = krb5_cc_default(krb5context, &tktcache);
KRBERR(kerr, "unable to resolve default cred cache");
kerr = krb5_cc_get_principal(krb5context, tktcache, &princ);
KRBERR(kerr, "unable to extract the principal from the cache");
/* ask the KDC to give us an AFS ticket */
if (!cell || !obtain_ticket(&creds5, "afs", cell)) {
if (!obtain_ticket(&creds5, "afs", NULL)) {
fprintf(stderr, "couldn't obtain AFS ticket\n");
exit(2);
}
}
kerr = krb5_cc_close(krb5context, tktcache);
KRBERR(kerr, "error closing cache");
/* ask the KDC to turn the Kerberos 5 ticket into a Kerberos 4
* ticket */
kerr = krb524_convert_creds_kdc(krb5context, creds5, &creds4);
KRBERR(kerr, "unable to convert to a kerberos V4 ticket");
/* dump the credential data obtained */
if (debug) {
printf("SERVICE : %s%s%s@%s\n",
creds4.service,
creds4.instance[0] ? "/" : "",
creds4.instance,
creds4.realm);
printf("PRINCIPAL: %s%s%s@%s\n",
creds4.pname,
creds4.pinst[0] ? "/" : "", creds4.pinst,
creds4.realm);
time = creds4.issue_date;
printf("ISSUED : %s", ctime_r(&time, buf1));
time = creds5->times.endtime;
printf("EXPIRES : %s", ctime_r(&time, buf1));
printf("SESSION : key=[");
cp = (unsigned char *) &creds4.session;
for (loop = 0; loop < sizeof(creds4.session); loop++)
printf("%02x", *cp++);
printf("]\n");
printf("TICKET : version %d, length %d:",
creds4.kvno, creds4.ticket_st.length);
cp = (unsigned char *) &creds4.ticket_st.dat;
for (loop = 0; loop < creds4.ticket_st.length; loop++) {
if (loop % (76 / 2) == 0)
printf("\n ");
printf("%02x", *cp++);
}
printf("\n");
}
/* pass the ticket to the AFS filesystem */
if (afsutil_authorise_krb5(cell,
realm,
creds5->times.endtime,
sizeof(creds4.session),
&creds4.session,
creds4.kvno,
creds4.ticket_st.length,
creds4.ticket_st.dat) < 0
) {
fprintf(stderr, "unable to pass token to kernel: %m\n");
exit(1);
}
krb5_free_creds(krb5context, creds5);
krb5_free_context(krb5context);
return 0;
} /* end main() */
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-15 23:59 ` Kyle Moffett
@ 2004-06-16 14:49 ` David Howells
2004-06-17 1:13 ` Kyle Moffett
0 siblings, 1 reply; 40+ messages in thread
From: David Howells @ 2004-06-16 14:49 UTC (permalink / raw)
To: Kyle Moffett; +Cc: Blair Strang, Linus Torvalds, lkml
> One other thing that I'm not certain about in this patch is if there
> is actually an important difference between "process" and
> "session" key-rings. I believe that the "session" distinction
> should be left up to user-space software like PAM to determine
> which key-ring "session" a process should belong to.
Well, userspace can decide that a process should begin a new session. I'd
envision this as a user gets a session keyring for each login, and so are able
to use these to hold different sets of credentials that don't interfere with
each other.
A UID keyring would be too pervasive - a key in there would affect _every_
process owned by that user - which might be undesirable.
A process keyring wouldn't be pervasive enough. You couldn't, for example, run
aklog in your shell to get you an AFS token attached to the session, use that
token several times by running programs and then quit the shell to dispose of
the token. Each process wanting the token would have to get itself a new token
by contacting the Kerberos server.
> The user and group key-rings are a good idea, so I guess the order with
> which key-rings are checked for keys is:
> Thread
> Process
> Session???
> User
> Group
That's about it, yes. Group keyrings don't currently actually exist.
David
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-16 14:49 ` David Howells
@ 2004-06-17 1:13 ` Kyle Moffett
2004-06-17 11:48 ` David Howells
0 siblings, 1 reply; 40+ messages in thread
From: Kyle Moffett @ 2004-06-17 1:13 UTC (permalink / raw)
To: David Howells; +Cc: Blair Strang, Linus Torvalds, lkml
On Jun 16, 2004, at 10:49, David Howells wrote:
> Well, userspace can decide that a process should begin a new session.
> I'd
> envision this as a user gets a session keyring for each login, and so
> are able
> to use these to hold different sets of credentials that don't
> interfere with
> each other.
>
> A UID keyring would be too pervasive - a key in there would affect
> _every_
> process owned by that user - which might be undesirable.
>
> A process keyring wouldn't be pervasive enough. You couldn't, for
> example, run
> aklog in your shell to get you an AFS token attached to the session,
> use that
> token several times by running programs and then quit the shell to
> dispose of
> the token. Each process wanting the token would have to get itself a
> new token
> by contacting the Kerberos server.
What if we leave the concept of a key-ring as completely general in
nature,
such that it can be associated with any object without needing to know
what
that object is. Then additional key-ring contexts could be created as
needed
for LSM modules or such.
I gave reasons earlier in this thread of why it is very useful to have
nested
key-rings, so perhaps we can give each key-ring a "parent" which
happens to
be an additional key-ring association. As long as we avoid cyclic
graphs, that
should give a great increase in flexibility without security problems.
Another complexity is the access control issue. I would rather not add
more
LSM hooks if we can avoid it, just to keep the complexity down, so I'm
thinking
that we could just represent all the information through the filesystem
and file
descriptors, with a couple of convenient IOCTLs. That way we could use
the
existing LSM hooks for filesystem access, and avoid giving sysadmins
another
system that they must start all over learning to secure.
I've looked at your patch, and it doesn't seem to be general enough to
allow
user-space to store arbitrary keys in the kernel, one of the features
that others
have expressed a desire for (see Andy Lutomirski's emails). With such a
system the most critical aspect to get first is the most flexible way
to manipulate
such keys without creating too much complexity.
It's essential to be able to tell the difference between different
types of keys,
especially if we want to let user-space use this to store other kinds
of keys. We
also need to be able to identify keys by "service" of some sort. I
think that
would probably be a key-type specific parameter, but for a Kerberos TGT
it
could be something like "krbtgt/MY.REALM@MY.REALM". With a type based
system we could even allow modules that implement additional
functionality
for certain types (IE something that uses CryptoAPI to do AES in-kernel
for
extra security).
Hmm, so going along with these ideas, how about this?
/proc/keyring/
MODE = 555
DESC = keyringfs, contains keyring metadata
<id>/
MODE = Access control for entire keyring
DESC = A keyring entry, referenced by number
opendir = Increments the ref count to make sure it won't go away.
parent => ../<id>
DESC = A symlink or hardlink to the parent keyring
<typeid>/
MODE = 555
DESC = A numerical "key-type" (KEYTYPE_KRB5)
<service>
MODE = Access control for a single key
DESC = The key, accessed as a file.
<typename> => <typeid>/
DESC = A symlink or hardlink to a type number from
a type name. These types could be registered
by modules that implement them.
There would be IOCTLs on the key-ring dir handles for getting the
key-ring
number, adding new keys, etc. On key handles there would be IOCTLs for
deleting the key, revoking access, etc. We'd also need a few syscalls
for
creating new key-rings.
Cheers,
Kyle Moffett
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-17 1:13 ` Kyle Moffett
@ 2004-06-17 11:48 ` David Howells
2004-06-17 19:06 ` Kyle Moffett
0 siblings, 1 reply; 40+ messages in thread
From: David Howells @ 2004-06-17 11:48 UTC (permalink / raw)
To: Kyle Moffett; +Cc: Blair Strang, Linus Torvalds, lkml
Kyle Moffett <mrmacman_g4@mac.com> wrote:
> What if we leave the concept of a key-ring as completely general in nature,
> such that it can be associated with any object without needing to know what
> that object is. Then additional key-ring contexts could be created as
> needed for LSM modules or such.
The main reason that there are attachment points for keyrings on UIDs,
processes, etc, is that there needs to be some way for things like open() to
find them without adding an equivalent syscalls that take a key as well.
In a way, you could look at it as these attachment points are like the PATH
variable - they describe a search path. You still need one of those.
LSM modules can always create keyrings and subscribe one of the five keyrings
a task has to them so that they become accessible.
> I gave reasons earlier in this thread of why it is very useful to have
> nested key-rings, so perhaps we can give each key-ring a "parent" which
> happens to be an additional key-ring association. As long as we avoid
> cyclic graphs, that should give a great increase in flexibility without
> security problems.
It supports nested keyrings - up to a certain depth anyway - and when you add
one keyring into another it checks to see you're not trying to create a cycle
(at least, you can from the kernel... you can't from userspace yet).
I'm not keen on the "parent" idea. I'm not sure what you are thinking of
exactly - a keyring can have multiple parents, and also this sounds like it
may cause a cycle.
> Another complexity is the access control issue. I would rather not add more
> LSM hooks if we can avoid it, just to keep the complexity down, so I'm
> thinking that we could just represent all the information through the
> filesystem and file descriptors, with a couple of convenient IOCTLs. That
> way we could use the existing LSM hooks for filesystem access, and avoid
> giving sysadmins another system that they must start all over learning to
> secure.
Using LSM hooks could represent a chicken vs egg scenario if you want to use
these keys to represent LSM security data (which you might reasonably want to
do).
> I've looked at your patch, and it doesn't seem to be general enough to allow
> user-space to store arbitrary keys in the kernel, one of the features that
> others have expressed a desire for (see Andy Lutomirski's emails). With
> such a system the most critical aspect to get first is the most flexible way
> to manipulate such keys without creating too much complexity.
It could be made such that arbitrary keys can be stored there (just another
keytype)... it's just that the keys eat into the kernel low memory region. It
may be better to permit such keys to be stored in highmem or swap somehow.
Plus, should there be a quota system?
> It's essential to be able to tell the difference between different types of
> keys, especially if we want to let user-space use this to store other kinds
> of keys. We also need to be able to identify keys by "service" of some sort.
Keys have a serial number, a type, a description and a payload. Currently the
types can't be arbitrary - a kernel driver or whatever must have registered
that type for it to be used. If that driver unregisters its key type, all keys
of that type are immediately withdrawn.
For instance, my kafs module registers an "afs" key type in which it can store
a Krb5 ticket. Having a type management system like this allows the type to
have operations to validate, match and pretty-print key descriptions. The
first one is the most important, I feel, because we can validate a key upon
addition.
It might be possible to make this more flexible. Have userspace upload a
"potential" key with arbitrary type, description and payload; and then have,
say, a kafs's file open routine locate a candidate key and render it into
parsed key form. Hmmm...
One thing I want to avoid is having to have the filesystem (or whatever)
validate the key every time it looks at it.
> I think that would probably be a key-type specific parameter, but for a
> Kerberos TGT it could be something like "krbtgt/MY.REALM@MY.REALM". With a
> type based system we could even allow modules that implement additional
> functionality for certain types (IE something that uses CryptoAPI to do AES
> in-kernel for extra security).
In this example, you might have something like:
type krbtgt
desc MY.REALM@MY.REALM
Or:
type user
desc krbtgt/MY.REALM@MY.REALM
But you'd have to provide the kernel with a type registration for the key type
in question.
> Hmm, so going along with these ideas, how about this?
> /proc/keyring/
> MODE = 555
> DESC = keyringfs, contains keyring metadata
I presume you mean keyringfs or keyfs mounted on /proc/keyring/ (or /proc/keys/).
> <id>/
> MODE = Access control for entire keyring
> DESC = A keyring entry, referenced by number
> opendir = Increments the ref count to make sure it won't go away.
>
> parent => ../<id>
> DESC = A symlink or hardlink to the parent keyring
>
> <typeid>/
> MODE = 555
> DESC = A numerical "key-type" (KEYTYPE_KRB5)
>
> <service>
> MODE = Access control for a single key
> DESC = The key, accessed as a file.
>
> <typename> => <typeid>/
> DESC = A symlink or hardlink to a type number from
> a type name. These types could be registered
> by modules that implement them.
>
> There would be IOCTLs on the key-ring dir handles for getting the key-ring
> number
Why? That's the filename of the keyring dir.
> adding new keys, etc.
Some of this could be done by link and rename.
> On key handles there would be IOCTLs for deleting the key,
unlink.
> revoking access, etc.
> We'd also need a few syscalls for creating new key-rings.
mkdir would be nice, but the key manager supplies the ID.
I think I'd make the filesystem look like:
/proc/keys/
types
keys/
<keyID>
<keyringID>/
<keyID>
<keyringID> => ../<keyringID> [symlink]
<keyID>
<keyID> [hardlink to keyID]
<keyringID>/
<keyID>
<keyID>
<keyringID>/
<keyringID>/
<keyID>
Each key would then have a UID, GID and umask which behave like for normal
files. Reading key files would then get you a summary of the key contents and
state. getxattr could be used also. The payload would only be accessible
through getxattr.
I might even permit the use of link() and rename(), provided the keys
filenames stay the same, and unlink().
However, I think I'd prefer to extend the syscall interface some more.
I've got four prctls:
(*) Get process subscribed keyring ID (choose which of the five).
(*) Clear process subscribed keyring ID (choose which of the five).
(*) Request new session keyring for this process.
(*) Add key to process subscribed keyring ID (choose which of the five).
I could then add some more syscalls:
(*) Add key to arbitrary keyring.
(*) Update key.
(*) Retire key.
(*) Get list of key IDs from keyring.
(*) Get type of key.
(*) Get description of key.
(*) Get payload of key.
(*) Link key to keyring.
(*) Unlink key from keyring.
All but the first two are trivially easy to do with a keyfs using standard
operations.
David
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-17 11:48 ` David Howells
@ 2004-06-17 19:06 ` Kyle Moffett
2004-06-23 12:29 ` David Howells
0 siblings, 1 reply; 40+ messages in thread
From: Kyle Moffett @ 2004-06-17 19:06 UTC (permalink / raw)
To: David Howells; +Cc: Blair Strang, Linus Torvalds, lkml
On Jun 17, 2004, at 07:48, David Howells wrote:
> The main reason that there are attachment points for keyrings on UIDs,
> processes, etc, is that there needs to be some way for things like
> open() to
> find them without adding an equivalent syscalls that take a key as
> well.
>
> In a way, you could look at it as these attachment points are like the
> PATH
> variable - they describe a search path. You still need one of those.
>
> LSM modules can always create keyrings and subscribe one of the five
> keyrings
> a task has to them so that they become accessible.
You are referring to the attachment point from the UID to key-ring or
process to
key-ring. I was referring to your method of telling the key-ring what
is attached
to it, though I could have misread your code. Of course a task_struct
should
have a key-ring pointer, but the key-ring shouldn't need to know what
points to
it, just how many things point to it (ref count).
> It supports nested keyrings - up to a certain depth anyway - and when
> you add
> one keyring into another it checks to see you're not trying to create
> a cycle
> (at least, you can from the kernel... you can't from userspace yet).
Ahh, that's basically what I was thinking about, I must have missed it
in your patch.
> I'm not keen on the "parent" idea. I'm not sure what you are thinking
> of
> exactly - a keyring can have multiple parents, and also this sounds
> like it
> may cause a cycle.
I see, we're going about this different ways. For me, the ideal search
path within
a single key-ring: keyring, keyring->parent, keyring->parent->parent,
etc. That
way we wouldn't even need a "session" key-ring in the kernel, a PAM
module
could join processes to the appropriate key-ring when you login. That
way if
I login several times on different console virtual terminals it can
share a key-ring
across all of them, but not when I login remotely.
> Using LSM hooks could represent a chicken vs egg scenario if you want
> to use
> these keys to represent LSM security data (which you might reasonably
> want to
> do).
It just means that LSM modules that want to use keys to hold LSM
security data
must be careful about their access to key-ring FS objects. Besides,
there's no
requirements that LSM use the user-space interfaces, we'd probably need
a
set of kernel-space functions that could ignore the access check if
requested,
and most LSM modules would just use unreadable/unwritable/unusable keys
and ignore the access checks.
> It could be made such that arbitrary keys can be stored there (just
> another
> keytype)... it's just that the keys eat into the kernel low memory
> region. It
> may be better to permit such keys to be stored in highmem or swap
> somehow.
Let's allow user programs to *request* (Could be overridden) that
certain keys
be swappable, and we could always allow them to be in highmem, as long
as
we can ensure that certain keys won't be swapped. There are advantages
to
not allowing keys to be swapped.
> Plus, should there be a quota system?
Definitely, by limiting the amount of memory allocated per security
context.
We should make sure that the extra key-ring struct data also counts, so
the
user can't just allocate thousands of empty keys.
> Keys have a serial number, a type, a description and a payload.
> Currently the
> types can't be arbitrary - a kernel driver or whatever must have
> registered
> that type for it to be used. If that driver unregisters its key type,
> all keys
> of that type are immediately withdrawn.
Are the serial numbers unique within a key-ring or within the entire
subsystem?
Perhaps we should allow user-space to "allocate" a key-type dynamically,
something >=1024, while <1024 is reserved for kernel-registered
key-types.
Key-types should be mappable name=>number and number=>name.
> For instance, my kafs module registers an "afs" key type in which it
> can store
> a Krb5 ticket. Having a type management system like this allows the
> type to
> have operations to validate, match and pretty-print key descriptions.
> The
> first one is the most important, I feel, because we can validate a key
> upon
> addition.
Kernel-registered key-types should definitely be validated by the
kernel.
> It might be possible to make this more flexible. Have userspace upload
> a
> "potential" key with arbitrary type, description and payload; and then
> have,
> say, a kafs's file open routine locate a candidate key and render it
> into
> parsed key form. Hmmm...
User-space should be required to parse the key as much as possible, so
that we can keep clutter out of kernel-space. But it's not an issue if
all AFS
ever does with the key is just perform mathematical operations on it and
it happens to be corrupted, as long as it doesn't break the kernel. If
we
allow user-space key-type allocations then we should allow arbitrary
data
to be stored there, up to quota.
> One thing I want to avoid is having to have the filesystem (or
> whatever)
> validate the key every time it looks at it.
No kidding. Once it's added (And validated if needed), we only need to
check it again if it's modified.
> In this example, you might have something like:
>
> type krbtgt
> desc MY.REALM@MY.REALM
>
> Or:
>
> type user
> desc krbtgt/MY.REALM@MY.REALM
>
> But you'd have to provide the kernel with a type registration for the
> key type
> in question.
Are the types numbers? That would seem simpler and allow differing
user-space and kernel-space key-type allocation. Then it would be:
type: KEYTYPE_KRB5 (1042 or some such user-space allocated number)
desc: "krbtgt/MY.REALM@MY.REALM"
>> Hmm, so going along with these ideas, how about this?
>> /proc/keyring/
>> MODE = 555
>> DESC = keyringfs, contains keyring metadata
> I presume you mean keyringfs or keyfs mounted on /proc/keyring/ (or
> /proc/keys/).
Of course
>> <id>/
>> MODE = Access control for entire keyring
>> DESC = A keyring entry, referenced by number
>> opendir = Increments the ref count to make sure it won't go away.
>>
>> parent => ../<id>
>> DESC = A symlink or hardlink to the parent keyring
>>
>> <typeid>/
>> MODE = 555
>> DESC = A numerical "key-type" (KEYTYPE_KRB5)
>>
>> <service>
>> MODE = Access control for a single key
>> DESC = The key, accessed as a file.
>>
>> <typename> => <typeid>/
>> DESC = A symlink or hardlink to a type number from
>> a type name. These types could be registered
>> by modules that implement them.
>>
>> There would be IOCTLs on the key-ring dir handles for getting the
>> key-ring
>> number
> Why? That's the filename of the keyring dir.
If you were passed a key-ring handle and told to do something with it,
how
would you find out what number the keyring is?
>> adding new keys, etc.
> Some of this could be done by link and rename.
Yeah, but carefully.
>> On key handles there would be IOCTLs for deleting the key,
> unlink.
Duh, I feel stupid now.
>> revoking access, etc.
>
>> We'd also need a few syscalls for creating new key-rings.
>
> mkdir would be nice, but the key manager supplies the ID.
How about a key-ring id directory "-1" that can be opendir()ed to
generate a new key-ring. Then once you have that you can just
use IOCTLs to find out what key-ring ID it has. That gives you a
directory file-handle and makes the retain count management a
little simpler. If they don't use the key-ring and just close the dir
handle then the retain count becomes zero and it disappears.
> I think I'd make the filesystem look like:
>
> /proc/keys/
> types
> keys/
> <keyID>
> <keyringID>/
> <keyID>
> <keyringID> => ../<keyringID> [symlink]
> <keyID>
> <keyID> [hardlink to keyID]
> <keyringID>/
> <keyID>
> <keyID>
> <keyringID>/
> <keyringID>/
> <keyID>
I think we are referring to the same ability here, you just referenced
it as "child"
key-rings, whereas I referenced them as "parent" key-rings. Both have
the same
behavior. I think I like the multiple inheritance idea better. What
we do need is
a way to make a
> Each key would then have a UID, GID and umask which behave like for
> normal
> files. Reading key files would then get you a summary of the key
> contents and
> state. getxattr could be used also. The payload would only be
> accessible
> through getxattr.
Yeah, but I think the payload should be accessible through read(),
write(), etc so
that cat can be used to get a dump of the keys, it makes it easier on
sysadmins
who are trying to debug things. Perhaps we could have two files for
each key in
a kernel-registered key-type:
"123" => summary of key, from kernel handler
"123-raw" => raw binary data of key, if accessible
User-mode key-types wouldn't have kernel functions, and so would only
have a
"123-raw" file.
> I might even permit the use of link() and rename(), provided the keys
> filenames stay the same, and unlink().
What if we have:
<keyring-id>/
0/ => These are sub-key-rings (type=0,desc=<key-ring ID string>)
<keyring-no>
<keyring-no>
<key-type>/
<key-desc>
<key-type>_raw/
<key-desc>
Or something like that. That way key filenames stay the same, are easy
to
locate and use in scripts, and give detailed information just in the
directories.
We can also store sub-key-rings that way. Here "unlink()" of a
directory could
be permitted. If it's a primary key-ring entry then we just remove it
from the
visible database but not from anything that references it (Like
unlink() on an
open file).
> However, I think I'd prefer to extend the syscall interface some more.
> I've got four prctls:
>
> (*) Get process subscribed keyring ID (choose which of the five).
>
> (*) Clear process subscribed keyring ID (choose which of the five).
>
> (*) Request new session keyring for this process.
>
> (*) Add key to process subscribed keyring ID (choose which of the
> five).
>
> I could then add some more syscalls:
>
> (*) Add key to arbitrary keyring.
>
> (*) Update key.
>
> (*) Retire key.
>
> (*) Get list of key IDs from keyring.
>
> (*) Get type of key.
>
> (*) Get description of key.
>
> (*) Get payload of key.
>
> (*) Link key to keyring.
>
> (*) Unlink key from keyring.
>
> All but the first two are trivially easy to do with a keyfs using
> standard
> operations.
Yeah. We ought to have equivalent IOCTLs so that mostly atomic updates
can be done to key-rings, possibly even setting up a mandatory flock()
for
key and key-ring file-handles. Opening a file-handle would be enough to
make sure it doesn't go away, but flocking it would protect against
other
kinds of operations.
As for the first two operations, looking up key-rings, we could just
add a
hard-link or soft-link /proc/<pid>/keyring/process/thread, though we'd
want
sys-calls as well, since the UID and group ones aren't mapped in proc.
I
don't expect those will be used as much, though.
Cheers,
Kyle Moffett
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-17 19:06 ` Kyle Moffett
@ 2004-06-23 12:29 ` David Howells
2004-06-23 21:03 ` Kyle Moffett
2004-06-29 17:07 ` Kyle Moffett
0 siblings, 2 replies; 40+ messages in thread
From: David Howells @ 2004-06-23 12:29 UTC (permalink / raw)
To: Kyle Moffett; +Cc: Blair Strang, lkml
Kyle Moffett <mrmacman_g4@mac.com> wrote:
> You are referring to the attachment point from the UID to key-ring or
> process to key-ring. I was referring to your method of telling the key-ring
> what is attached to it, though I could have misread your code. Of course a
> task_struct should have a key-ring pointer, but the key-ring shouldn't need
> to know what points to it, just how many things point to it (ref count).
A keyring doesn't know what points to it, only the keys that it holds. The key
part of the keyring keeps track of the refcount.
A keyring does have a name, though, but it is arbitrary and otherwise ignored.
> I see, we're going about this different ways. For me, the ideal search path
> within a single key-ring: keyring, keyring->parent, keyring->parent->parent,
> etc.
So you're thinking of a credential stack? That gets tricky when su is thrown
into the mix. Not that I'm saying my method is precisely simple then either.
Actually, you don't need the concept of a parent at all. If the process had a
current credential TOS pointer, you could push another keyring by adding the
TOS pointer as a child of the new keyring, and then redirecting the TOS
pointer. Basically, the old TOS becomes a child of the new TOS.
I've suggested a stack before, but it got rejected for various reasons.
> That way we wouldn't even need a "session" key-ring in the kernel, a PAM
> module could join processes to the appropriate key-ring when you login.
> That way if I login several times on different console virtual terminals it
> can share a key-ring across all of them, but not when I login remotely.
True. You would still have a "session" keyring, but it would be entirely
defined and governed by userspace (PAM) as to what it meant.
I sort of like that idea. The kernel could still pin keyrings for groups and
users, and PAM could bolt them together, so upon login PAM could create:
TOS
|
+--> Session keyring
|
+--> UID keyring
+--> GID keyring
+--> Supplementary Group keyring
+--> Supplementary Group keyring
+--> Supplementary Group keyring
And then a process or a thread that wanted its own private keys could stack a
new ring:
TOS
|
+--> Thread keyring
|
+--> Process keyring
|
+--> Session keyring
However, you have a number of problems to contend with:
(*) How do you handle setuid() and co?
(*) How do you handle setgid() and co?
(*) How do you handle setgroups()?
(*) How do you handle S_SUID?
This last could be handled in three ways: stack a new credential on the
front; have a second TOS pointer (similar to UIDs); or start a new stack.
If having a second TOS pointer, you could have setresuid() clear it if
setting all UIDs to non-zero.
(*) There needs to be a limit on recursion.
> Let's allow user programs to *request* (Could be overridden) that certain
> keys be swappable, and we could always allow them to be in highmem, as long
> as we can ensure that certain keys won't be swapped. There are advantages
> to not allowing keys to be swapped.
I'm not sure making keys swappable is necessarily easy.
> > Plus, should there be a quota system?
>
> Definitely, by limiting the amount of memory allocated per security context.
> We should make sure that the extra key-ring struct data also counts, so the
> user can't just allocate thousands of empty keys.
Put a counter in "struct user".
> Are the serial numbers unique within a key-ring or within the entire
> subsystem?
The latter.
> Perhaps we should allow user-space to "allocate" a key-type dynamically,
> something >=1024, while <1024 is reserved for kernel-registered
> key-types.
> Key-types should be mappable name=>number and number=>name.
Why?
> Are the types numbers? That would seem simpler and allow differing
> user-space and kernel-space key-type allocation. Then it would be:
> type: KEYTYPE_KRB5 (1042 or some such user-space allocated number)
> desc: "krbtgt/MY.REALM@MY.REALM"
No. The types are names. I suppose they could be made numeric too, but I don't
think there's a need for that. I could just decree that all userspace type
names begin with a '+' or something.
> > Some of this could be done by link and rename.
> Yeah, but carefully.
Actually, symlink() would probably be better. Though Al Viro might kill me for
abusing it:-)
> > mkdir would be nice, but the key manager supplies the ID.
> How about a key-ring id directory "-1" that can be opendir()ed to generate a
> new key-ring. Then once you have that you can just use IOCTLs to find out
> what key-ring ID it has. That gives you a directory file-handle and makes
> the retain count management a little simpler. If they don't use the
> key-ring and just close the dir handle then the retain count becomes zero
> and it disappears.
Let's try not to bend the VFS layer too far. Just add another syscall or
prctl() for that.
> I think we are referring to the same ability here, you just referenced it as
> "child" key-rings, whereas I referenced them as "parent" key-rings. Both
> have the same behavior. I think I like the multiple inheritance idea
> better. What we do need is a way to make a
Make a what?
> Yeah, but I think the payload should be accessible through read(), write(),
> etc so that cat can be used to get a dump of the keys, it makes it easier on
> sysadmins who are trying to debug things. Perhaps we could have two files
> for each key in a kernel-registered key-type:
Perhaps it'd be better to make each key a directory, whether or not it's a
keyring:
/proc/keys/
types
keys/
<keyID>/
type
state
description
payload
<keyringID>/
type
state
description
<keyID> => ../<keyID> [symlink]
> Or something like that. That way key filenames stay the same, are easy to
> locate and use in scripts, and give detailed information just in the
> directories.
> We can also store sub-key-rings that way. Here "unlink()" of a directory
> could be permitted.
I don't think you can unlink() a directory, and rmdir() might not work if it's
got contents.
Add a syscall or a prctl().
Perhaps we need a keyctl() syscall...
long keyctl(KEYCTL_NEW, keyid_t keyring,
char *type, char *desc, void *payload);
long keyctl(KEYCTL_UPDATE, keyid_t key,
char *type, void *desc, void *payload);
long keyctl(KEYCTL_LINK, keyid_t keyring, keyid_t key);
long keyctl(KEYCTL_UNLINK, keyid_t keyring, keyid_t key);
long keyctl(KEYCTL_CLEAR, keyid_t keyring);
long keyctl(KEYCTL_REVOKE, keyid_t key);
long keyctl(KEYCTL_FIND, keyid_t keyring, char *type, char *desc);
Some prctls()...
long prctl(PR_GET_KEYID, keyid_t keyring);
long prctl(PR_NEW_SESSION_KEYRING);
With some special keyIDs:
0xABCD0001 - This thread's keyring
0xABCD0002 - This process's keyring
0xABCD0003 - This session's keyring
0xABCD0004 - This UID's keyring
0xABCD0005 - This GID's keyring
New rlimits:
RLIMIT_KEYS - Amount of memory consumed by a user's keys.
And a new filesystem in which extant keys (including keyrings) can be viewed
to a greater or a lesser extent.
> Yeah. We ought to have equivalent IOCTLs so that mostly atomic updates can
> be done to key-rings, possibly even setting up a mandatory flock() for key
> and key-ring file-handles. Opening a file-handle would be enough to make
> sure it doesn't go away, but flocking it would protect against other kinds
> of operations.
We don't want to add ioctls if we can avoid it... And I don't think you want
to try mixing flock() in.
What you're suggesting makes filesystem key searching tricky... what happens
when it is running in softirq context and encounters a locked keyring?
> As for the first two operations, looking up key-rings, we could just add a
> hard-link or soft-link /proc/<pid>/keyring/process/thread, though we'd want
> sys-calls as well, since the UID and group ones aren't mapped in proc. I
> don't expect those will be used as much, though.
hard-link or soft-link to what? Keyrings are directories on another
filesystem, and we can only assume that it's mounted on /proc/keys. Besides,
you can't hard-link directories.
David
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-23 12:29 ` David Howells
@ 2004-06-23 21:03 ` Kyle Moffett
2004-06-29 17:07 ` Kyle Moffett
1 sibling, 0 replies; 40+ messages in thread
From: Kyle Moffett @ 2004-06-23 21:03 UTC (permalink / raw)
To: David Howells; +Cc: Blair Strang, lkml
On Jun 23, 2004, at 08:29, David Howells wrote:
> Kyle Moffett <mrmacman_g4@mac.com> wrote:
>> You are referring to the attachment point from the UID to key-ring or
>> process to key-ring. I was referring to your method of telling the
>> key-ring
>> what is attached to it, though I could have misread your code. Of
>> course a
>> task_struct should have a key-ring pointer, but the key-ring
>> shouldn't need
>> to know what points to it, just how many things point to it (ref
>> count).
>
> A keyring doesn't know what points to it, only the keys that it holds.
> The key
> part of the keyring keeps track of the refcount.
>
> A keyring does have a name, though, but it is arbitrary and otherwise
> ignored.
Ok, that makes sense, I just confused myself when I read your code.
Could we
just use numbers? Possibly a port of the PID allocator would make that
easier.
>> I see, we're going about this different ways. For me, the ideal
>> search path
>> within a single key-ring: keyring, keyring->parent,
>> keyring->parent->parent,
>> etc.
> So you're thinking of a credential stack? That gets tricky when su is
> thrown
> into the mix. Not that I'm saying my method is precisely simple then
> either.
>
> Actually, you don't need the concept of a parent at all. If the
> process had a
> current credential TOS pointer, you could push another keyring by
> adding the
> TOS pointer as a child of the new keyring, and then redirecting the TOS
> pointer. Basically, the old TOS becomes a child of the new TOS.
>
> I've suggested a stack before, but it got rejected for various reasons.
We've been looking at this from different perspectives. My search
system was
to have a special "parent" that is searched if a key cannot be found in
the
current key-ring. Your search systeam appears to be to have a bunch of
"children" that are just an extra collection of more keys that are
searched after
the key-ring.
>> That way we wouldn't even need a "session" key-ring in the kernel, a
>> PAM
>> module could join processes to the appropriate key-ring when you
>> login.
>> That way if I login several times on different console virtual
>> terminals it
>> can share a key-ring across all of them, but not when I login
>> remotely.
>>
> True. You would still have a "session" keyring, but it would be
> entirely
> defined and governed by userspace (PAM) as to what it meant.
>
> I sort of like that idea. The kernel could still pin keyrings for
> groups and
> users, and PAM could bolt them together, so upon login PAM could
> create:
>
> TOS
> |
> +--> Session keyring
> |
> +--> UID keyring
> +--> GID keyring
> +--> Supplementary Group keyring
> +--> Supplementary Group keyring
> +--> Supplementary Group keyring
>
> And then a process or a thread that wanted its own private keys could
> stack a
> new ring:
>
> TOS
> |
> +--> Thread keyring
> |
> +--> Process keyring
> |
> +--> Session keyring
I really like that idea, but perhaps it could be made more extendable.
Maybe
we could use a system like this:
Searching a key-ring involves searching its keys, then searching its
children,
(Child order is undefined). When a task begins a search operation it
searches
the following key-rings in this order:
Thread
Process
User
Primary Group
Secondary Group(s) (Undefined order)
The recommended way to join a process to a session is to change its
process
key-ring to something like the following:
Process
|
+---New empty process key-ring
|
+---"Session" key-ring
> However, you have a number of problems to contend with:
>
> (*) How do you handle setuid() and co?
By default the init process receives a NULL key-ring (No key-ring at
all). This
means that new processes spawned by init receive a NULL key-ring. These
are NULL key-ring pointers, not empty key-rings, so no data/keys are
shared.
If a process tries to create keys in a NULL key-ring, it will fail.
Then setuid(),
etc. merely change the uid, etc. If a daemon is explicitly given a
keyring at
startup it will retain that keyring. This preserves maximum
compatibility even
though there are no changes to libc.
> (*) How do you handle setgid() and co?
The same way as setuid().
> (*) How do you handle setgroups()?
The same way as setgid().
> (*) How do you handle S_SUID?
>
> This last could be handled in three ways: stack a new credential
> on the
> front; have a second TOS pointer (similar to UIDs); or start a
> new stack.
>
> If having a second TOS pointer, you could have setresuid() clear
> it if
> setting all UIDs to non-zero.
>
> (*) There needs to be a limit on recursion.
As long as we're careful to do all key-ring operations within an
interruptible task
context, and only use a locking iterative search, we don't need to care
about
tree depth. If the user creates too deep of a child structure, it just
gets credited to
their process time and user limits. Iterative searches eliminate the
stack usage
problems and make it simple to fit in a 4k stack limit.
>> Let's allow user programs to *request* (Could be overridden) that
>> certain
>> keys be swappable, and we could always allow them to be in highmem,
>> as long
>> as we can ensure that certain keys won't be swapped. There are
>> advantages
>> to not allowing keys to be swapped.
>
> I'm not sure making keys swappable is necessarily easy.
So in the initial implementation of the key-ring system all requests
for swappable
keys would be overridden to be not swappable. After all, it's only a
*request* :-D
> Put a counter in "struct user".
Possibly also per-process or per-thread limits. Maybe even per-group
limits, if we
want to go all the way. Those are relatively simple, though.
>> Are the serial numbers unique within a key-ring or within the entire
>> subsystem?
> The latter.
That makes it much easier to move keys around between key-rings, I
guess.
>> Are the types numbers? That would seem simpler and allow differing
>> user-space and kernel-space key-type allocation. Then it would be:
>> type: KEYTYPE_KRB5 (1042 or some such user-space allocated number)
>> desc: "krbtgt/MY.REALM@MY.REALM"
>
> No. The types are names. I suppose they could be made numeric too, but
> I don't
> think there's a need for that. I could just decree that all userspace
> type
> names begin with a '+' or something.
I suppose that makes sense. I think at one point I had a technical
reason for why
types should be numbers but it seems to have gone away. Oh well :-)
>>> Some of this could be done by link and rename.
>> Yeah, but carefully.
>
> Actually, symlink() would probably be better. Though Al Viro might
> kill me for
> abusing it:-)
We want to be careful to give processes a way to prevent race conditions
when accessing/modifying key-rings.
> Let's try not to bend the VFS layer too far. Just add another syscall
> or
> prctl() for that.
Yeah. Generally we want to give them a file or directory handle
instead of a
key-ring ID. That way we have a simple way to detect when they're done
using it.
> Perhaps it'd be better to make each key a directory, whether or not
> it's a
> keyring:
>
> /proc/keys/
> types
> keys/
> <keyID>/
> type
> state
> description
> payload
> <keyringID>/
> type
> state
> description
> <keyID> => ../<keyID> [symlink]
I like this idea. It's a simple shallow directory tree.
>> We can also store sub-key-rings that way. Here "unlink()" of a
>> directory
>> could be permitted.
>
> I don't think you can unlink() a directory, and rmdir() might not work
> if it's
> got contents.
Yes, but unlink would only be needed on the symlinks, which is what we
need. The keys and key-rings themselves would go away when all
references to them have been destroyed.
> With some special keyIDs:
>
> 0xABCD0001 - This thread's keyring
> 0xABCD0002 - This process's keyring
> 0xABCD0003 - This session's keyring
> 0xABCD0004 - This UID's keyring
> 0xABCD0005 - This GID's keyring
Why not just have separate keyctl calls to set thread, process, UID,
and GID
keyrings for specific threads/processes/UIDs/GIDs. That way a process
with
the appropriate capabilities can manipulate keys as needed. It also
frees
us from the need to worry about not allocating those particular IDs.
>> Yeah. We ought to have equivalent IOCTLs so that mostly atomic
>> updates can
>> be done to key-rings, possibly even setting up a mandatory flock()
>> for key
>> and key-ring file-handles. Opening a file-handle would be enough to
>> make
>> sure it doesn't go away, but flocking it would protect against other
>> kinds
>> of operations.
>
> We don't want to add ioctls if we can avoid it... And I don't think
> you want
> to try mixing flock() in.
>
> What you're suggesting makes filesystem key searching tricky... what
> happens
> when it is running in softirq context and encounters a locked keyring?
Is there anything that needs to run in softirq context that should be
accessing
key-rings there? Perhaps one condition on key-ring access would be to
require that it be done from interruptible task context. We could
re-implement
the flock operation for our particular key filesystem to be a mandatory
key lock.
That would prevent race conditions in priv'ed processes manipulating the
key-rings by allowing atomic modifications on a large scale.
> hard-link or soft-link to what? Keyrings are directories on another
> filesystem, and we can only assume that it's mounted on /proc/keys.
> Besides,
> you can't hard-link directories.
Ahh, sorry, I was thinking and got lost. Nevermind :-D
Perhaps we should add a few /proc/<pid>/keyring/{thread,process,...}
"files"
to allow the sysadmin to view what key-rings are currently used by a
particular
process.
Cheers,
Kyle Moffett
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-23 12:29 ` David Howells
2004-06-23 21:03 ` Kyle Moffett
@ 2004-06-29 17:07 ` Kyle Moffett
2004-07-07 18:54 ` John Bucy
1 sibling, 1 reply; 40+ messages in thread
From: Kyle Moffett @ 2004-06-29 17:07 UTC (permalink / raw)
To: David Howells; +Cc: Blair Strang, lkml
Ok, I have some free time this week to code up a patch. To start with
everything
will rely on being in process context just to make things simple.
Here's a short
summary of everything we've agreed upon so far:
1) A key-ring is an object independent of what uses it. It supports
operations
to read, modify, and search it.
2) When a key-ring is searched for a particular key by name and type,
first
it checks all keys of the specified type within said key-ring. If it
does not find it
there, then it proceeds with all sub-key-rings.
3) Keys and key-rings are allocated and referenced through an
independent
number space (Somewhat like the PID allocator)
4) Key and key-ring access should be controlled using permissions and
POSIX ACLs as though they were files. In this case, however, the
"execute"
permission bit is used to control in-kernel operations on the keys (EX:
AES
encrypting a block of data using CryptoAPI without being able to read
the key.
5) Key-rings should be automatically associated with the following
things.
This is also the search order when looking for a key.
Thread
Process
Process Group
Session
User
Group
6) User process with the appropriate privileges (Perhaps a new
capability
CAP_LINUX_KEYS or something) can modify/search/link/whatever any/all
keyrings. (I'm still not quite sure how much should be allowed.)
7) All algorithms working with keys and keyrings should be iterative,
not
recursive, and should be run it process context.
8) Graphs of nested key-rings must be non-circular.
9) All memory allocated for key-rings and keys should be counted from
various limits associated with users/processes/etc.
10) All kernel key-types should begin with a "+" character, and
user-space
cannot use an unregistered key-type that begins with a "+" character.
(This
means that all keys having a key-type beginning with a "+" character
have
been validated by kernel code.
11) A key-ring filesystem should be mounted on /proc/keys:
types
keys/
<keyID>/
control
type
description
state
<keyringID>/
control
type
description
state
<keyID> => ../<keyID> [symlink]
12) It should be impossible to look up keys by number unless said key
filesystem is mounted. The "control" entries in the key filesystem can
be
open()ed to get the kind of file handle that KETCTL manipulates.
13) A new syscall "keyctl":
KEYCTL_NEW_RING: Creates a new key-ring and returns its fd
Returns: int filedesc
Params:
char *desc
int *subkeys
long subkey_count
KEYCTL_NEW_KEY: Creates a new key and returns its fd
Returns: int filedesc
Params:
char *type
char *desc
void *data
KEYCTL_SHLOCK
KEYCTL_EXLOCK
KEYCTL_UNLOCK: A mandatory lock on the key/key-ring
Returns: int error
Params:
int filedesc
int flags
KEYCTL_TYPE: Retrieves the "type" field
Returns: long bytes_left
Params:
char *desc
long size
KEYCTL_DESC: Retrieves the "desc" field
Returns: long bytes_left
Params:
char *desc
long size
KEYCTL_READ: Retrieves the "data" field
Returns: long bytes_left
Params:
char *data
long size
KEYCTL_WRITE: Modifies the "data" field
Returns: int error
Params:
char *data
long size
KEYCTL_GET: Retrieves the current key fd
Returns: int filedesc
Params:
int type
KEY_THREAD
KEY_PROCESS
KEY_PGROUP
KEY_SESSION
KEY_USER
KEY_GROUP
KEYCTL_SET: Modifies the current key fd
Returns: int error
Params:
int type
<See above KEYCTL_GET_KEY>
int filedesc
KEYCTL_ENUM: Enumerates through all keys. NOTE: This must only be
called with a KEYCTL_SHLOCK, otherwise it might skip keys or repeat
keys.
Returns: int error
Params:
int keyring_fd
int *key_fd
KEYCTL_LOOKUP: Look for a key by type, but don't recurse
Returns: int filedesc
KEYCTL_SEARCH: Searches for a specific key by type
Returns: int filedesc
Params:
char *type
char *desc
KEYCTL_ADD: Adds a key to a key-ring
Returns: int error
Params:
int keyring_fd
int key_fd
KEYCTL_REMOVE: Removes a key from a key-ring
Returns: int error
Params:
int keyring_fd
int key_fd
KEYCTL_CLEAR: Removes all keys from a key-ring
Returns: int error
Params:
int keyring_fd
KEYCTL_REVOKE: Revokes a specific key file descriptor and any key
file descriptors it spawned. This should even apply to keyrings!
Returns: int error
Params:
int filedesc
I think that's just about everything. Let me know if I got something
wrong
or have another problem with the above.
Cheers,
Kyle Moffett
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-06-29 17:07 ` Kyle Moffett
@ 2004-07-07 18:54 ` John Bucy
2004-07-08 1:29 ` Kyle Moffett
0 siblings, 1 reply; 40+ messages in thread
From: John Bucy @ 2004-07-07 18:54 UTC (permalink / raw)
To: Kyle Moffett; +Cc: David Howells, Blair Strang, lkml
Speaking as a member of the AFS community, I'm thrilled to see this
coming along since PAGs are the major stumbling block for openafs in
2.6. I won't speak for Coda and NFSv4 but hopefully, this can help them
out as well.
The policy that a number of AFS people want is that (1) processes with
different UIDs can share the same keyring and that (2) a number of
processes with the same UID can opt not to share the same keyring. (1)
e.g. I have AFS creds (krb5 tickets) and want to run a setuid binary
with my creds. (2) e.g. I want to have a bunch of xterms some with
administrative rights and some with normal rights. Maybe I'm running
stuff out of cron or something under my UID that gets creds from a
ticket file, etc, and don't want it to interfere with my interactive use
of the machine.
>From my reading of the posts so far, it looks like (1) is no problem
since setuid() wouldn't touch the keyrings. I'm less sure about (2).
Does creating a new keyring (KEYCTL_NEW_RING) replace one of my existing
keyrings? Which one? To implement (2), do I need the ability to
explicitly zero-out some of my keyring associations?
john
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: In-kernel Authentication Tokens (PAGs)
2004-07-07 18:54 ` John Bucy
@ 2004-07-08 1:29 ` Kyle Moffett
0 siblings, 0 replies; 40+ messages in thread
From: Kyle Moffett @ 2004-07-08 1:29 UTC (permalink / raw)
To: John Bucy; +Cc: Blair Strang, lkml, David Howells
On Jul 07, 2004, at 14:54, John Bucy wrote:
> (1) processes with different UIDs can share the same keyring EX: I
> have AFS creds (krb5 tickets) and want to run a setuid binary with
> my creds.
This is relatively simple under our proposed system. Multiple
processes can be attached to a single keyring, and then the access
rights on the keyring control the processes control over it. Since
keys and keyrings are represented by file descriptors, there are two
ways to gain access to a key/keyring, via a duplicate of the file
descriptor (revokable), or (with sufficient privileges) opening a new
file descriptor using the keyringfs filesystem.
> (2) a number of processes with the same UID can opt not to share
> the same keyring. EX: I want to have a bunch of xterms some with
> administrative rights and some with normal rights.
This is equally easy. There are several attachment points for keys
and keyrings:
thread
process
process group
session
UID
GID
If you have two processes, and you want to create keys in one that
are inaccessible in the other, then just create a new keyring tied to
the "process" attachment point in the first process, and embed the
original process key-ring in it. Then you can add administrative
keys to the new keyring, without completely giving up the old one:
EX:
I am an AFS admin in CELL1, but not in CELL2, and have
different accounts in each. I have all my process' key-ring pointers
the same, a shared key-ring in which I have my user@CELL1 and
user@CELL2 keys. Later I decide to do admin work in CELL1,
except I still need access to some of my files in CELL2, so in one
of my shells, I run "key-sh" or something to get a new shell with a
local process key-ring (It has as a child the original shared ring).
Once I have that shell, I can add my CELL1 admin keys normally.
When AFS searches for CELL1 keys it finds them in the topmost
key-ring, stops, and uses the admin ones. When is searches for
my CELL2 keys, it goes down to the shared key-ring to find them.
Cheers,
Kyle Moffett
-----BEGIN GEEK CODE BLOCK-----
Version: 3.12
GCM/CS/IT/U d- s++: a17 C++++>$ UB/L/X/*++++(+)>$ P+++(++++)>$
L++++(+++) E W++(+) N+++(++) o? K? w--- O? M++ V? PS+() PE+(-) Y+
PGP+++ t+(+++) 5 X R? tv-(--) b++++(++) DI+ D+ G e->++++$ h!*()>++$ r
!y?(-)
------END GEEK CODE BLOCK------
^ permalink raw reply [flat|nested] 40+ messages in thread
end of thread, other threads:[~2004-07-08 1:29 UTC | newest]
Thread overview: 40+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-06-12 2:37 In-kernel Authentication Tokens (PAGs) Kyle Moffett
2004-06-12 3:13 ` Andy Lutomirski
2004-06-12 4:57 ` Kyle Moffett
2004-06-12 5:34 ` Andy Lutomirski
2004-06-12 12:51 ` Kyle Moffett
2004-06-12 15:37 ` Andy Lutomirski
2004-06-12 17:15 ` Kyle Moffett
2004-06-12 3:15 ` Chris Wright
2004-06-12 4:48 ` Kyle Moffett
2004-06-12 20:53 ` Chris Wright
2004-06-12 21:15 ` Kyle Moffett
2004-06-12 21:44 ` Chris Wright
2004-06-12 21:58 ` Kyle Moffett
2004-06-12 22:51 ` Chris Wright
2004-06-12 23:40 ` Kyle Moffett
2004-06-12 22:51 ` Trond Myklebust
2004-06-12 23:33 ` Kyle Moffett
2004-06-12 23:58 ` Trond Myklebust
2004-06-13 0:23 ` Kyle Moffett
2004-06-15 6:38 ` Blair Strang
2004-06-15 7:03 ` Trond Myklebust
2004-06-15 9:36 ` David Howells
2004-06-15 19:00 ` Kyle Moffett
2004-06-15 22:07 ` Chris Wright
2004-06-15 23:48 ` Kyle Moffett
2004-06-16 0:01 ` Chris Wright
2004-06-16 0:06 ` Kyle Moffett
2004-06-16 14:22 ` David Howells
2004-06-15 22:29 ` Chris Wright
2004-06-16 14:37 ` David Howells
2004-06-15 23:59 ` Kyle Moffett
2004-06-16 14:49 ` David Howells
2004-06-17 1:13 ` Kyle Moffett
2004-06-17 11:48 ` David Howells
2004-06-17 19:06 ` Kyle Moffett
2004-06-23 12:29 ` David Howells
2004-06-23 21:03 ` Kyle Moffett
2004-06-29 17:07 ` Kyle Moffett
2004-07-07 18:54 ` John Bucy
2004-07-08 1:29 ` Kyle Moffett
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox