* 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 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 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 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 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 ` 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 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 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 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 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 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 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: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 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: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