authentication / encryption key retention
Subsystems (6)
David Howells
maintainerPATCH 1/1 authentication / encryption key retention
Hi Linus,
Here's a patch to the kernel that can provide authentication token and encryption key retention at various levels. Could you have a look at it and see if it's along the lines of what you want?
Note that it uses the prctl() syscall to propose keys to the kernel.
One thing I think it needs is some way of limiting the number of extant keys a user is permitted to have.
I can now use it to retain AFS keys obtained from Kerberos.
David
fs/exec.c +7 -1
@@ -45,6 +45,7 @@#include <linux/mount.h> #include <linux/security.h> #include <linux/rmap-locking.h> +#include <linux/key.h> #include <asm/uaccess.h> #include <asm/pgalloc.h>
@@ -889,10 +890,15 @@void compute_creds (struct linux_binprm *bprm) { +if (bprm->e_uid != current->uid) +suid_keys (current); +exec_keys (current); +task_lock (current);if (bprm->e_uid != current->uid || bprm->e_gid != current->gid) { current->mm->dumpable =0 ; - +suid_keys (current); +if (must_not_trace_exec (current) ||atomic_read (¤t->fs->count) >1 || atomic_read(¤t->files->count) > 1
include/linux/key.h +144 -0
144 lines not on this page · raw message
include/linux/prctl.h +11 -0
@@ -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 */
include/linux/sched.h +9 -0
@@ -290,6 +290,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;
@@ -402,6 +406,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;
kernel/exit.c +2 -0
@@ -22,6 +22,7 @@#include <linux/profile.h> #include <linux/mount.h> #include <linux/proc_fs.h> +#include <linux/key.h> #include <asm/uaccess.h> #include <asm/pgtable.h>
@@ -711,6 +712,7 @@exit_namespace (tsk);exit_itimers (tsk);exit_thread (); +exit_keys (tsk);if (tsk->leader)disassociate_ctty (1 );
kernel/fork.c +6 -1
@@ -30,6 +30,7 @@#include <linux/futex.h> #include <linux/ptrace.h> #include <linux/mount.h> +#include <linux/key.h> #include <asm/pgtable.h> #include <asm/pgalloc.h>
@@ -884,8 +885,10 @@ goto bad_fork_cleanup_sighand;if ((retval =copy_mm (clone_flags, p))) goto bad_fork_cleanup_signal; -if ((retval =copy_namespace (clone_flags, p))) +if ((retval =copy_keys (clone_flags, p))) goto bad_fork_cleanup_mm; +if ((retval =copy_namespace (clone_flags, p))) + goto bad_fork_cleanup_keys; retval =copy_thread (0 , clone_flags, stack_start, stack_size, p, regs);if (retval) goto bad_fork_cleanup_namespace;
@@ -1021,6 +1024,8 @@ bad_fork_cleanup_namespace:exit_namespace (p); +bad_fork_cleanup_keys: +exit_keys (p); bad_fork_cleanup_mm:exit_mm (p); bad_fork_cleanup_signal:
kernel/sys.c +16 -0
@@ -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>
@@ -1417,6 +1418,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 ;
kernel/user.c +12 -1
@@ -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, up); +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); }
security/Kconfig +12 -0
@@ -4,6 +4,18 @@ 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. + + If you are unsure how to answer this question, answer N. + config SECURITY bool "Enable different security models" help
security/keys/internal.h +75 -0
75 lines not on this page · raw message
security/keys/key.c +595 -0
595 lines not on this page · raw message
security/keys/Makefile +9 -0
@@ -0,0 +1,9 @@ +# +# Makefile for key management +# + +obj-y := \ + key.o \ + process_keys.o + +obj-$(CONFIG_PROC_FS) += proc.o
security/keys/proc.c +119 -0
119 lines not on this page · raw message
security/keys/process_keys.c +491 -0
491 lines not on this page · raw message
security/Makefile +1 -0
@@ -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
Hi David,
some suggestions to improve your patch. First: It is very elegant.
kernel/user.c line 107
Quoted 6 lines
kernel/user.c +2
-0@@ -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, up);
this should be 'new' instead of 'up'
Quoted 12 lines
kernel/user.c +4
-0+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); }
kernel/user.c
Quoted 10 lines
kernel/user.c +9
-0+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.
Maybe the keyword "keyring" should be put here somehow, since many security people know this concept by exactly this name.
Quoted 49 lines
linux-2.6.0-test4/security/keys/key.c1970-01-01 01:00:00.000000000 +0100
+++linux-2.6.0-test4-keys/security/keys/key.c2003-08-29 11:07:39.000000000 +0100security/keys/key.c +47
-0+spin_lock (&key_serial_lock); + +/* 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;
security/keys/key.c line 139
Quoted 10 lines
security/keys/key.c +10
-0+ } + + insert_here: +rb_link_node (&key->serial_node, parent, p); +rb_insert_color (&key->serial_node, &key_serial_tree); +spin_unlock (&key_serial_lock); + + *_key = key; +return 0 ; +}/* end key_alloc() */
Maybe this allocator should be separated. It will be useful outside this function.
Quoted 10 lines
security/keys/key.c +8
-0+void key_retire (struct key *key) +{ + +/* duplicate the list of subscribed keys */ +if (source->payload.subscriptions) { +/* prevent the source keyring from being altered */ +down_read (&source->sem); +
[...]
security/keys/key.c line 238
Quoted 3 lines
security/keys/key.c +3
-0+ +up_read (&source->sem); + }
You should take the semaphore always to prevent the tiny race, where you allocate the first subscription, while you check this.
Quoted 63 lines
security/keys/key.c +63
-0+int keyring_search (struct key *keyring, +const struct key_type *type, +const char *description, +struct key **_key) +{ +struct { +struct key *keyring; +struct keyring_list *keylist; +int kix; + } stack[KEYRING_SEARCH_MAX_DEPTH ]; + +struct keyring_list *keylist; +struct key *key; +int sp, psp, kix; + +key_validate (keyring); + + *_key =NULL ; + +if (keyring->type != &key_type_keyring) +return -EINVAL ; + + sp =0 ; + + descend: +read_lock (&keyring->lock); +if (keyring->flags &KEY_FLAG_RETIRED ) + goto retired; + + keylist = keyring->payload.subscriptions; + kix =0 ; + + ascend: +while (kix < keylist->nkeys) { + key = keylist->keys[kix]; + +if (key->type == type && key->type->match (key, 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].keylist = keylist; + stack[sp].kix = kix; + sp++; + goto descend; + } + + next: + kix++; + } +
security/keys/key.c line 328
Quoted 10 lines
security/keys/key.c +10
-0+ retired: +read_unlock (&keyring->lock); + +if (sp >0 ) { + sp--; + keyring = stack[sp].keyring; + keylist = stack[sp].keylist; + kix = stack[sp].kix +1 ; + goto ascend; + }
Why do you do the iteration without taking the spinlock here? That whole function looks a little hackish and is difficult to follow. I would at least like to see
for (; kix < keyring->payload.subscriptions->nkeys; kix++) {
key = keyring->payload.subscriptions->keys[kix];
}Now the goto next becomes continue.
You also never need to store keylist on the stack, because of the identity stack[sp].keyring->payload.subscriptions == stack[sp].keylist
This should reduce stack usage or allow deeper nesting.
Loop detection should better be done on insert, not on search. With the above code you allow loops, but ignore them, right?
Quoted 4 lines
security/keys/key.c +4
-0+int keyring_set_name (struct key *keyring,const char *fmt, ...) +{ +struct keyring_name *kname; +va_list va;
security/keys/key.c line 440
Quoted 10 lines
security/keys/key.c +10
-0+size_t len; +char buf[32 ]; + +key_validate (keyring); + +if (keyring->type != &key_type_keyring) +return -EINVAL ; + +va_start (va, fmt); +vsnprintf (buf,32 , fmt, va);
you should check the return value of vsnprintf here end fail for ret >= sizeof(buf)
security/keys/key.c line 442
Quoted 2 lines
security/keys/key.c +2
-0+va_end (va); + len =strlen (buf);
Then this becomes unnecessary.
security/keys/key.c line 519
Quoted 12 lines
+/************************************************************************* ****/ +/*
+ * describe the keyring
+ */
+static voidkeyring_describe(const struct key *keyring, struct seq_file *m)+{security/keys/key.c +6
-0+struct keyring_name *kname; +struct keyring_list *klist; + + kname = keyring->description.ringname; +if (kname) { +seq_printf (m,"%s" , kname->name);
What about seq_puts(kname->name) here?
Quoted 7 lines
security/keys/process_keys.c +7
-0+static int install_session_keyring (struct task_struct *tsk) +{ +struct key *keyring; +int ret; + + ret =keyring_alloc (NULL , &keyring); +if (ret <0 )
security/keys/process_keys.c line 103
Quoted 10 lines
security/keys/process_keys.c +10
-0+return ret; + + ret =keyring_set_name (keyring,"_ses.%u" , keyring->serial); +if (ret <0 ) +return ret; + +key_put (xchg (&tsk->session_keyring, keyring)); + +return 0 ; +}/* end install_session_keyring() */
Why is this one implemented differently from the others above?
security/keys/process_keys.c line 205
Quoted 7 lines
security/keys/process_keys.c +7
-0+long clear_process_keyring (int specifier) +{ + +case PR_SPEC_GROUP_KEYRING : +default : +return -EINVAL ; + }
Not implemented, yet? ;-)
Quoted 6 lines
+/************************************************************************* ****/ +/*
+ * install a new session keyring, discarding the old one
+ * - implementsprctl(PR_NEW_SESSION_KEYRING)
+ */security/keys/process_keys.c +1
-0+long new_session_keyring (void )
security/keys/process_keys.c line 241
Quoted 10 lines
security/keys/process_keys.c +10
-0+{ +int ret; + + ret =install_session_keyring (current); +if (ret <0 ) +return ret; + +return current->session_keyring ? current->session_keyring->serial :0 ; + +}/* end new_session_keyring() */
You evaluate current three times here. Evaluating it is expensive. You might consider using a variable holding current instead.
Quoted 5 lines
security/keys/process_keys.c +5
-0+long add_user_key (int specifier, +char __user *_type, +char __user *_description, +void __user *_payload) +{
[...]
security/keys/process_keys.c line 355
Quoted 1 line
security/keys/process_keys.c +1
-0+ ret =get_user (plen, (uint16_t *) _payload);
Why this cast?
Quoted 15 lines
security/keys/process_keys.c +15
-0+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); + }
security/keys/process_keys.c line 410
Quoted 10 lines
security/keys/process_keys.c +10
-0+else { + tsk->process_keyring =NULL ; + ret =install_process_keyring (tsk); + } + } + + tsk->thread_keyring =NULL ; + +return ret; +}/* end copy_keys() */
What about the per thread keyring?
Is there an atomic_inc(tsk->thread_keyring) missing?
So all in all, it looks like a very nice and clean patch.
I'm looking forward to have this included.
Regards
Ingo Oeser
David Howells
maintainerHi Ingo,
Quoted 1 line
some suggestions to improve your patch. First: It is very elegant.
Thanks:-)
Quoted 2 lines
Review
Quoted 1 line
(quoted diff) +1
-0+ kmem_cache_free(uid_cachep, up);this should be 'new' instead of 'up'
Fixed.
Quoted 2 lines
Maybe the keyword "keyring" should be put here somehow, since many security people know this concept by exactly this name.
Done:
+ 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.
Quoted 2 lines
Maybe this allocator should be separated. It will be useful outside this function.
By "this allocator" do you mean the algorithm that generates a unique serial number?
Quoted 2 lines
You should take the semaphore always to prevent the tiny race, where you allocate the first subscription, while you check this.
I don't think there's a race there. We do a spot check, and if there's nothing there at that time, so be it. True, the subscriber may be inside the semaphore, preparatory to adding a ring, but they haven't done so yet, so it isn't much of a problem.
However, I'll make the change anyway... it's probably not going to be called much, so it shouldn't hurt.
Quoted 4 lines
Quoted 4 lines
+ retired:
(quoted diff) +3
-0+ read_unlock(&keyring->lock); + + if (sp > 0) {
Quoted 8 lines
Review
Quoted 6 lines
(quoted diff) +6
-0+ sp--; + keyring = stack[sp].keyring; + keylist = stack[sp].keylist; + kix = stack[sp].kix + 1; + goto ascend; + }Why do you do the iteration without taking the spinlock here?
What do you mean? I read_lock() every node as I deal with it. All the nodes stored on the stack are still read_locked().
And do you mean iteration or recursion?
Quoted 7 lines
That whole function looks a little hackish and is difficult to follow. I would at least like to see
for (; kix < keyring->payload.subscriptions->nkeys; kix++) { key = keyring->payload.subscriptions->keys[kix]; }Now the goto next becomes continue.
Erm... what about the recursion element? The set of keyrings is a graph not a one dimensional list (some of the keys may actually be keyrings themselves).
Quoted 2 lines
You also never need to store keylist on the stack, because of the identity stack[sp].keyring->payload.subscriptions == stack[sp].keylist
Good point. Fixed.
Quoted 1 line
This should reduce stack usage or allow deeper nesting.
True. But 6 levels is probably enough.
Quoted 2 lines
Loop detection should better be done on insert, not on search. With the above code you allow loops, but ignore them, right?
Yes, loop detection is a pain whenever it is done, especially as loops can cause recursive deadlocks in keyring_search().
I'm not sure what'd be the best way to avoid loop detection. What if the user sets up three keyrings and then subscribes A to B, B to C and C to A? On the other hand what if they subscribe A to B, B to C and A to C?
I think the only way to do it would be to add in the new subscription and then walk the new graph in its entirety, marking keyrings as you go down and then unmarking them as you rise back up, and giving an error if we try to walk over a node that's already marked.
The problem again is evading recursive deadlock - particularly against other algorithms also recursively walking the graph at the same time.
My initial thought was just to say that a user can only subscribe keyrings to one of the five process keyrings, and those can't be subscribed elsewhere.
Quoted 4 lines
Review
Quoted 1 line
(quoted diff) +1
-0+ vsnprintf(buf, 32, fmt, va);you should check the return value of vsnprintf here end fail for ret >= sizeof(buf)
But that can't happen. The kernel's vsnprintf() returns the number of chars written into the buffer (exclusive of the NUL char), and so if its return value is >= sizeof(buf) then your stack is stuffed:
[lib/vsprintf.c]
int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
{
...
/* the trailing null byte doesn't count towards the total
* ++str;
*/
return str-buf;
}However, I've changed things to eliminate the strlen() call and use the result of vsnprintf() instead.
Quoted 3 lines
Review
Quoted 1 line
(quoted diff) +1
-0+ seq_printf(m, "%s", kname->name);What about
seq_puts(kname->name)here?
Done. It used to say something else as well, but no longer...
Quoted 4 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+static int install_session_keyring(struct task_struct *tsk) +{...
Why is this one implemented differently from the others above?
At one point the function did something else as well, but that has been taken out. I've normalised the process & thread versions to be the same, and I've made them release the newly allocated keyring if the naming function fails.
Quoted 3 lines
Review
Quoted 1 line
(quoted diff) +1
-0+ case PR_SPEC_GROUP_KEYRING:...
Not implemented, yet? ;-)
No. I don't want to start mucking around adding "struct group" to match "struct user" just yet. I'll consider that after Linus adds this key management stuff to the kernel.
I'd also like to add "login session" and "process" / "thread group" structs too.
Quoted 4 lines
Quoted 1 line
+long
new_session_keyring(void)...
You evaluate current three times here. Evaluating it is expensive. You might consider using a variable holding current instead.
It's not that expensive, at least not on the x86 arch. It's probably more expensive to save an extra value on the stack so as to have the value to hand. Besides, the get_current() function probably ought to be __attribute__((const)).
Quoted 3 lines
Review
Quoted 1 line
(quoted diff) +1
-0+ ret = get_user(plen, (uint16_t *) _payload);Why this cast?
get_user() looks at the pointer type to determine the fetch size. _payload is void *.
Quoted 4 lines
Quoted 1 line
+int
copy_keys(unsigned long clone_flags, struct task_struct *tsk)...
What about the per thread keyring?
Is there anatomic_inc(tsk->thread_keyring)missing?
No. Note the following:
tsk->thread_keyring = NULL;
Quoted 1 line
So all in all, it looks like a very nice and clean patch.
I've attached a revised patch:-)
Quoted 1 line
I'm looking forward to have this included.
So am I.
Thanks for your comments,
David
fs/exec.c +7 -1
@@ -45,6 +45,7 @@#include <linux/mount.h> #include <linux/security.h> #include <linux/rmap-locking.h> +#include <linux/key.h> #include <asm/uaccess.h> #include <asm/pgalloc.h>
@@ -889,10 +890,15 @@void compute_creds (struct linux_binprm *bprm) { +if (bprm->e_uid != current->uid) +suid_keys (current); +exec_keys (current); +task_lock (current);if (bprm->e_uid != current->uid || bprm->e_gid != current->gid) { current->mm->dumpable =0 ; - +suid_keys (current); +if (must_not_trace_exec (current) ||atomic_read (¤t->fs->count) >1 || atomic_read(¤t->files->count) > 1
include/linux/key.h +144 -0
144 lines not on this page · raw message
include/linux/prctl.h +11 -0
@@ -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 */
include/linux/sched.h +9 -0
@@ -290,6 +290,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;
@@ -402,6 +406,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;
kernel/exit.c +2 -0
@@ -22,6 +22,7 @@#include <linux/profile.h> #include <linux/mount.h> #include <linux/proc_fs.h> +#include <linux/key.h> #include <asm/uaccess.h> #include <asm/pgtable.h>
@@ -711,6 +712,7 @@exit_namespace (tsk);exit_itimers (tsk);exit_thread (); +exit_keys (tsk);if (tsk->leader)disassociate_ctty (1 );
kernel/fork.c +6 -1
@@ -30,6 +30,7 @@#include <linux/futex.h> #include <linux/ptrace.h> #include <linux/mount.h> +#include <linux/key.h> #include <asm/pgtable.h> #include <asm/pgalloc.h>
@@ -884,8 +885,10 @@ goto bad_fork_cleanup_sighand;if ((retval =copy_mm (clone_flags, p))) goto bad_fork_cleanup_signal; -if ((retval =copy_namespace (clone_flags, p))) +if ((retval =copy_keys (clone_flags, p))) goto bad_fork_cleanup_mm; +if ((retval =copy_namespace (clone_flags, p))) + goto bad_fork_cleanup_keys; retval =copy_thread (0 , clone_flags, stack_start, stack_size, p, regs);if (retval) goto bad_fork_cleanup_namespace;
@@ -1021,6 +1024,8 @@ bad_fork_cleanup_namespace:exit_namespace (p); +bad_fork_cleanup_keys: +exit_keys (p); bad_fork_cleanup_mm:exit_mm (p); bad_fork_cleanup_signal:
kernel/sys.c +16 -0
@@ -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>
@@ -1417,6 +1418,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 ;
kernel/user.c +12 -1
@@ -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); }
security/Kconfig +17 -0
@@ -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
security/keys/internal.h +75 -0
75 lines not on this page · raw message
security/keys/key.c +590 -0
590 lines not on this page · raw message
security/keys/Makefile +9 -0
@@ -0,0 +1,9 @@ +# +# Makefile for key management +# + +obj-y := \ + key.o \ + process_keys.o + +obj-$(CONFIG_PROC_FS) += proc.o
security/keys/proc.c +119 -0
119 lines not on this page · raw message
security/keys/process_keys.c +506 -0
506 lines not on this page · raw message
security/Makefile +1 -0
@@ -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
David Howells
maintainerHi Ingo,
Quoted 5 lines
Quoted 2 lines
Loop detection should better be done on insert, not on search. With the above code you allow loops, but ignore them, right?
Yes, loop detection is a pain whenever it is done, especially as loops can cause recursive deadlocks in
keyring_search().
I've produced a new patch with loop detection added in (keyring_add_key() should now return -EDEADLK if you try to create a loop).
I've also got u-space tools to allow some keyring manipulation and also to submit AFS Kerberos keys for retention if you want a look.
David
fs/exec.c +7 -1
@@ -45,6 +45,7 @@#include <linux/mount.h> #include <linux/security.h> #include <linux/rmap-locking.h> +#include <linux/key.h> #include <asm/uaccess.h> #include <asm/pgalloc.h>
@@ -889,10 +890,15 @@void compute_creds (struct linux_binprm *bprm) { +if (bprm->e_uid != current->uid) +suid_keys (current); +exec_keys (current); +task_lock (current);if (bprm->e_uid != current->uid || bprm->e_gid != current->gid) { current->mm->dumpable =0 ; - +suid_keys (current); +if (must_not_trace_exec (current) ||atomic_read (¤t->fs->count) >1 || atomic_read(¤t->files->count) > 1
include/linux/key.h +144 -0
144 lines not on this page · raw message
include/linux/prctl.h +11 -0
@@ -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 */
include/linux/sched.h +9 -0
@@ -290,6 +290,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;
@@ -403,6 +407,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;
kernel/exit.c +2 -0
@@ -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>
@@ -713,6 +714,7 @@exit_namespace (tsk);exit_itimers (tsk);exit_thread (); +exit_keys (tsk);if (tsk->leader)disassociate_ctty (1 );
kernel/fork.c +6 -1
@@ -30,6 +30,7 @@#include <linux/futex.h> #include <linux/ptrace.h> #include <linux/mount.h> +#include <linux/key.h> #include <asm/pgtable.h> #include <asm/pgalloc.h>
@@ -885,8 +886,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;
@@ -1022,6 +1025,8 @@ bad_fork_cleanup_namespace:exit_namespace (p); +bad_fork_cleanup_keys: +exit_keys (p); bad_fork_cleanup_mm:exit_mm (p); bad_fork_cleanup_signal:
kernel/sys.c +16 -0
@@ -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>
@@ -1423,6 +1424,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 ;
kernel/user.c +12 -1
@@ -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); }
security/Kconfig +17 -0
@@ -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
security/keys/internal.h +75 -0
75 lines not on this page · raw message
security/keys/key.c +714 -0
714 lines not on this page · raw message
security/keys/Makefile +9 -0
@@ -0,0 +1,9 @@ +# +# Makefile for key management +# + +obj-y := \ + key.o \ + process_keys.o + +obj-$(CONFIG_PROC_FS) += proc.o
security/keys/proc.c +120 -0
120 lines not on this page · raw message
security/keys/process_keys.c +510 -0
510 lines not on this page · raw message
security/Makefile +1 -0
@@ -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