aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/cred.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2008-11-13 18:39:23 -0500
committerJames Morris <jmorris@namei.org>2008-11-13 18:39:23 -0500
commitd84f4f992cbd76e8f39c488cf0c5d123843923b1 (patch)
treefc4a0349c42995715b93d0f7a3c78e9ea9b3f36e /kernel/cred.c
parent745ca2475a6ac596e3d8d37c2759c0fbe2586227 (diff)
CRED: Inaugurate COW credentials
Inaugurate copy-on-write credentials management. This uses RCU to manage the credentials pointer in the task_struct with respect to accesses by other tasks. A process may only modify its own credentials, and so does not need locking to access or modify its own credentials. A mutex (cred_replace_mutex) is added to the task_struct to control the effect of PTRACE_ATTACHED on credential calculations, particularly with respect to execve(). With this patch, the contents of an active credentials struct may not be changed directly; rather a new set of credentials must be prepared, modified and committed using something like the following sequence of events: struct cred *new = prepare_creds(); int ret = blah(new); if (ret < 0) { abort_creds(new); return ret; } return commit_creds(new); There are some exceptions to this rule: the keyrings pointed to by the active credentials may be instantiated - keyrings violate the COW rule as managing COW keyrings is tricky, given that it is possible for a task to directly alter the keys in a keyring in use by another task. To help enforce this, various pointers to sets of credentials, such as those in the task_struct, are declared const. The purpose of this is compile-time discouragement of altering credentials through those pointers. Once a set of credentials has been made public through one of these pointers, it may not be modified, except under special circumstances: (1) Its reference count may incremented and decremented. (2) The keyrings to which it points may be modified, but not replaced. The only safe way to modify anything else is to create a replacement and commit using the functions described in Documentation/credentials.txt (which will be added by a later patch). This patch and the preceding patches have been tested with the LTP SELinux testsuite. This patch makes several logical sets of alteration: (1) execve(). This now prepares and commits credentials in various places in the security code rather than altering the current creds directly. (2) Temporary credential overrides. do_coredump() and sys_faccessat() now prepare their own credentials and temporarily override the ones currently on the acting thread, whilst preventing interference from other threads by holding cred_replace_mutex on the thread being dumped. This will be replaced in a future patch by something that hands down the credentials directly to the functions being called, rather than altering the task's objective credentials. (3) LSM interface. A number of functions have been changed, added or removed: (*) security_capset_check(), ->capset_check() (*) security_capset_set(), ->capset_set() Removed in favour of security_capset(). (*) security_capset(), ->capset() New. This is passed a pointer to the new creds, a pointer to the old creds and the proposed capability sets. It should fill in the new creds or return an error. All pointers, barring the pointer to the new creds, are now const. (*) security_bprm_apply_creds(), ->bprm_apply_creds() Changed; now returns a value, which will cause the process to be killed if it's an error. (*) security_task_alloc(), ->task_alloc_security() Removed in favour of security_prepare_creds(). (*) security_cred_free(), ->cred_free() New. Free security data attached to cred->security. (*) security_prepare_creds(), ->cred_prepare() New. Duplicate any security data attached to cred->security. (*) security_commit_creds(), ->cred_commit() New. Apply any security effects for the upcoming installation of new security by commit_creds(). (*) security_task_post_setuid(), ->task_post_setuid() Removed in favour of security_task_fix_setuid(). (*) security_task_fix_setuid(), ->task_fix_setuid() Fix up the proposed new credentials for setuid(). This is used by cap_set_fix_setuid() to implicitly adjust capabilities in line with setuid() changes. Changes are made to the new credentials, rather than the task itself as in security_task_post_setuid(). (*) security_task_reparent_to_init(), ->task_reparent_to_init() Removed. Instead the task being reparented to init is referred directly to init's credentials. NOTE! This results in the loss of some state: SELinux's osid no longer records the sid of the thread that forked it. (*) security_key_alloc(), ->key_alloc() (*) security_key_permission(), ->key_permission() Changed. These now take cred pointers rather than task pointers to refer to the security context. (4) sys_capset(). This has been simplified and uses less locking. The LSM functions it calls have been merged. (5) reparent_to_kthreadd(). This gives the current thread the same credentials as init by simply using commit_thread() to point that way. (6) __sigqueue_alloc() and switch_uid() __sigqueue_alloc() can't stop the target task from changing its creds beneath it, so this function gets a reference to the currently applicable user_struct which it then passes into the sigqueue struct it returns if successful. switch_uid() is now called from commit_creds(), and possibly should be folded into that. commit_creds() should take care of protecting __sigqueue_alloc(). (7) [sg]et[ug]id() and co and [sg]et_current_groups. The set functions now all use prepare_creds(), commit_creds() and abort_creds() to build and check a new set of credentials before applying it. security_task_set[ug]id() is called inside the prepared section. This guarantees that nothing else will affect the creds until we've finished. The calling of set_dumpable() has been moved into commit_creds(). Much of the functionality of set_user() has been moved into commit_creds(). The get functions all simply access the data directly. (8) security_task_prctl() and cap_task_prctl(). security_task_prctl() has been modified to return -ENOSYS if it doesn't want to handle a function, or otherwise return the return value directly rather than through an argument. Additionally, cap_task_prctl() now prepares a new set of credentials, even if it doesn't end up using it. (9) Keyrings. A number of changes have been made to the keyrings code: (a) switch_uid_keyring(), copy_keys(), exit_keys() and suid_keys() have all been dropped and built in to the credentials functions directly. They may want separating out again later. (b) key_alloc() and search_process_keyrings() now take a cred pointer rather than a task pointer to specify the security context. (c) copy_creds() gives a new thread within the same thread group a new thread keyring if its parent had one, otherwise it discards the thread keyring. (d) The authorisation key now points directly to the credentials to extend the search into rather pointing to the task that carries them. (e) Installing thread, process or session keyrings causes a new set of credentials to be created, even though it's not strictly necessary for process or session keyrings (they're shared). (10) Usermode helper. The usermode helper code now carries a cred struct pointer in its subprocess_info struct instead of a new session keyring pointer. This set of credentials is derived from init_cred and installed on the new process after it has been cloned. call_usermodehelper_setup() allocates the new credentials and call_usermodehelper_freeinfo() discards them if they haven't been used. A special cred function (prepare_usermodeinfo_creds()) is provided specifically for call_usermodehelper_setup() to call. call_usermodehelper_setkeys() adjusts the credentials to sport the supplied keyring as the new session keyring. (11) SELinux. SELinux has a number of changes, in addition to those to support the LSM interface changes mentioned above: (a) selinux_setprocattr() no longer does its check for whether the current ptracer can access processes with the new SID inside the lock that covers getting the ptracer's SID. Whilst this lock ensures that the check is done with the ptracer pinned, the result is only valid until the lock is released, so there's no point doing it inside the lock. (12) is_single_threaded(). This function has been extracted from selinux_setprocattr() and put into a file of its own in the lib/ directory as join_session_keyring() now wants to use it too. The code in SELinux just checked to see whether a task shared mm_structs with other tasks (CLONE_VM), but that isn't good enough. We really want to know if they're part of the same thread group (CLONE_THREAD). (13) nfsd. The NFS server daemon now has to use the COW credentials to set the credentials it is going to use. It really needs to pass the credentials down to the functions it calls, but it can't do that until other patches in this series have been applied. Signed-off-by: David Howells <dhowells@redhat.com> Acked-by: James Morris <jmorris@namei.org> Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'kernel/cred.c')
-rw-r--r--kernel/cred.c321
1 files changed, 284 insertions, 37 deletions
diff --git a/kernel/cred.c b/kernel/cred.c
index ac73e3617684..cb6b5eda978d 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -15,6 +15,10 @@
15#include <linux/keyctl.h> 15#include <linux/keyctl.h>
16#include <linux/init_task.h> 16#include <linux/init_task.h>
17#include <linux/security.h> 17#include <linux/security.h>
18#include <linux/cn_proc.h>
19#include "cred-internals.h"
20
21static struct kmem_cache *cred_jar;
18 22
19/* 23/*
20 * The common credentials for the initial task's thread group 24 * The common credentials for the initial task's thread group
@@ -64,7 +68,7 @@ static void release_tgcred_rcu(struct rcu_head *rcu)
64/* 68/*
65 * Release a set of thread group credentials. 69 * Release a set of thread group credentials.
66 */ 70 */
67static void release_tgcred(struct cred *cred) 71void release_tgcred(struct cred *cred)
68{ 72{
69#ifdef CONFIG_KEYS 73#ifdef CONFIG_KEYS
70 struct thread_group_cred *tgcred = cred->tgcred; 74 struct thread_group_cred *tgcred = cred->tgcred;
@@ -81,79 +85,322 @@ static void put_cred_rcu(struct rcu_head *rcu)
81{ 85{
82 struct cred *cred = container_of(rcu, struct cred, rcu); 86 struct cred *cred = container_of(rcu, struct cred, rcu);
83 87
84 BUG_ON(atomic_read(&cred->usage) != 0); 88 if (atomic_read(&cred->usage) != 0)
89 panic("CRED: put_cred_rcu() sees %p with usage %d\n",
90 cred, atomic_read(&cred->usage));
85 91
92 security_cred_free(cred);
86 key_put(cred->thread_keyring); 93 key_put(cred->thread_keyring);
87 key_put(cred->request_key_auth); 94 key_put(cred->request_key_auth);
88 release_tgcred(cred); 95 release_tgcred(cred);
89 put_group_info(cred->group_info); 96 put_group_info(cred->group_info);
90 free_uid(cred->user); 97 free_uid(cred->user);
91 security_cred_free(cred); 98 kmem_cache_free(cred_jar, cred);
92 kfree(cred);
93} 99}
94 100
95/** 101/**
96 * __put_cred - Destroy a set of credentials 102 * __put_cred - Destroy a set of credentials
97 * @sec: The record to release 103 * @cred: The record to release
98 * 104 *
99 * Destroy a set of credentials on which no references remain. 105 * Destroy a set of credentials on which no references remain.
100 */ 106 */
101void __put_cred(struct cred *cred) 107void __put_cred(struct cred *cred)
102{ 108{
109 BUG_ON(atomic_read(&cred->usage) != 0);
110
103 call_rcu(&cred->rcu, put_cred_rcu); 111 call_rcu(&cred->rcu, put_cred_rcu);
104} 112}
105EXPORT_SYMBOL(__put_cred); 113EXPORT_SYMBOL(__put_cred);
106 114
115/**
116 * prepare_creds - Prepare a new set of credentials for modification
117 *
118 * Prepare a new set of task credentials for modification. A task's creds
119 * shouldn't generally be modified directly, therefore this function is used to
120 * prepare a new copy, which the caller then modifies and then commits by
121 * calling commit_creds().
122 *
123 * Returns a pointer to the new creds-to-be if successful, NULL otherwise.
124 *
125 * Call commit_creds() or abort_creds() to clean up.
126 */
127struct cred *prepare_creds(void)
128{
129 struct task_struct *task = current;
130 const struct cred *old;
131 struct cred *new;
132
133 BUG_ON(atomic_read(&task->cred->usage) < 1);
134
135 new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
136 if (!new)
137 return NULL;
138
139 old = task->cred;
140 memcpy(new, old, sizeof(struct cred));
141
142 atomic_set(&new->usage, 1);
143 get_group_info(new->group_info);
144 get_uid(new->user);
145
146#ifdef CONFIG_KEYS
147 key_get(new->thread_keyring);
148 key_get(new->request_key_auth);
149 atomic_inc(&new->tgcred->usage);
150#endif
151
152#ifdef CONFIG_SECURITY
153 new->security = NULL;
154#endif
155
156 if (security_prepare_creds(new, old, GFP_KERNEL) < 0)
157 goto error;
158 return new;
159
160error:
161 abort_creds(new);
162 return NULL;
163}
164EXPORT_SYMBOL(prepare_creds);
165
166/*
167 * prepare new credentials for the usermode helper dispatcher
168 */
169struct cred *prepare_usermodehelper_creds(void)
170{
171#ifdef CONFIG_KEYS
172 struct thread_group_cred *tgcred = NULL;
173#endif
174 struct cred *new;
175
176#ifdef CONFIG_KEYS
177 tgcred = kzalloc(sizeof(*new->tgcred), GFP_ATOMIC);
178 if (!tgcred)
179 return NULL;
180#endif
181
182 new = kmem_cache_alloc(cred_jar, GFP_ATOMIC);
183 if (!new)
184 return NULL;
185
186 memcpy(new, &init_cred, sizeof(struct cred));
187
188 atomic_set(&new->usage, 1);
189 get_group_info(new->group_info);
190 get_uid(new->user);
191
192#ifdef CONFIG_KEYS
193 new->thread_keyring = NULL;
194 new->request_key_auth = NULL;
195 new->jit_keyring = KEY_REQKEY_DEFL_DEFAULT;
196
197 atomic_set(&tgcred->usage, 1);
198 spin_lock_init(&tgcred->lock);
199 new->tgcred = tgcred;
200#endif
201
202#ifdef CONFIG_SECURITY
203 new->security = NULL;
204#endif
205 if (security_prepare_creds(new, &init_cred, GFP_ATOMIC) < 0)
206 goto error;
207
208 BUG_ON(atomic_read(&new->usage) != 1);
209 return new;
210
211error:
212 put_cred(new);
213 return NULL;
214}
215
107/* 216/*
108 * Copy credentials for the new process created by fork() 217 * Copy credentials for the new process created by fork()
218 *
219 * We share if we can, but under some circumstances we have to generate a new
220 * set.
109 */ 221 */
110int copy_creds(struct task_struct *p, unsigned long clone_flags) 222int copy_creds(struct task_struct *p, unsigned long clone_flags)
111{ 223{
112 struct cred *pcred; 224#ifdef CONFIG_KEYS
113 int ret; 225 struct thread_group_cred *tgcred;
226#endif
227 struct cred *new;
228
229 mutex_init(&p->cred_exec_mutex);
114 230
115 pcred = kmemdup(p->cred, sizeof(*p->cred), GFP_KERNEL); 231 if (
116 if (!pcred) 232#ifdef CONFIG_KEYS
233 !p->cred->thread_keyring &&
234#endif
235 clone_flags & CLONE_THREAD
236 ) {
237 get_cred(p->cred);
238 atomic_inc(&p->cred->user->processes);
239 return 0;
240 }
241
242 new = prepare_creds();
243 if (!new)
117 return -ENOMEM; 244 return -ENOMEM;
118 245
119#ifdef CONFIG_KEYS 246#ifdef CONFIG_KEYS
120 if (clone_flags & CLONE_THREAD) { 247 /* new threads get their own thread keyrings if their parent already
121 atomic_inc(&pcred->tgcred->usage); 248 * had one */
122 } else { 249 if (new->thread_keyring) {
123 pcred->tgcred = kmalloc(sizeof(struct cred), GFP_KERNEL); 250 key_put(new->thread_keyring);
124 if (!pcred->tgcred) { 251 new->thread_keyring = NULL;
125 kfree(pcred); 252 if (clone_flags & CLONE_THREAD)
253 install_thread_keyring_to_cred(new);
254 }
255
256 /* we share the process and session keyrings between all the threads in
257 * a process - this is slightly icky as we violate COW credentials a
258 * bit */
259 if (!(clone_flags & CLONE_THREAD)) {
260 tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL);
261 if (!tgcred) {
262 put_cred(new);
126 return -ENOMEM; 263 return -ENOMEM;
127 } 264 }
128 atomic_set(&pcred->tgcred->usage, 1); 265 atomic_set(&tgcred->usage, 1);
129 spin_lock_init(&pcred->tgcred->lock); 266 spin_lock_init(&tgcred->lock);
130 pcred->tgcred->process_keyring = NULL; 267 tgcred->process_keyring = NULL;
131 pcred->tgcred->session_keyring = 268 tgcred->session_keyring = key_get(new->tgcred->session_keyring);
132 key_get(p->cred->tgcred->session_keyring); 269
270 release_tgcred(new);
271 new->tgcred = tgcred;
133 } 272 }
134#endif 273#endif
135 274
136#ifdef CONFIG_SECURITY 275 atomic_inc(&new->user->processes);
137 pcred->security = NULL; 276 p->cred = new;
138#endif 277 return 0;
278}
139 279
140 ret = security_cred_alloc(pcred); 280/**
141 if (ret < 0) { 281 * commit_creds - Install new credentials upon the current task
142 release_tgcred(pcred); 282 * @new: The credentials to be assigned
143 kfree(pcred); 283 *
144 return ret; 284 * Install a new set of credentials to the current task, using RCU to replace
285 * the old set.
286 *
287 * This function eats the caller's reference to the new credentials.
288 *
289 * Always returns 0 thus allowing this function to be tail-called at the end
290 * of, say, sys_setgid().
291 */
292int commit_creds(struct cred *new)
293{
294 struct task_struct *task = current;
295 const struct cred *old;
296
297 BUG_ON(atomic_read(&new->usage) < 1);
298 BUG_ON(atomic_read(&task->cred->usage) < 1);
299
300 old = task->cred;
301 security_commit_creds(new, old);
302
303 /* dumpability changes */
304 if (old->euid != new->euid ||
305 old->egid != new->egid ||
306 old->fsuid != new->fsuid ||
307 old->fsgid != new->fsgid ||
308 !cap_issubset(new->cap_permitted, old->cap_permitted)) {
309 set_dumpable(task->mm, suid_dumpable);
310 task->pdeath_signal = 0;
311 smp_wmb();
145 } 312 }
146 313
147 atomic_set(&pcred->usage, 1); 314 /* alter the thread keyring */
148 get_group_info(pcred->group_info); 315 if (new->fsuid != old->fsuid)
149 get_uid(pcred->user); 316 key_fsuid_changed(task);
150 key_get(pcred->thread_keyring); 317 if (new->fsgid != old->fsgid)
151 key_get(pcred->request_key_auth); 318 key_fsgid_changed(task);
319
320 /* do it
321 * - What if a process setreuid()'s and this brings the
322 * new uid over his NPROC rlimit? We can check this now
323 * cheaply with the new uid cache, so if it matters
324 * we should be checking for it. -DaveM
325 */
326 if (new->user != old->user)
327 atomic_inc(&new->user->processes);
328 rcu_assign_pointer(task->cred, new);
329 if (new->user != old->user)
330 atomic_dec(&old->user->processes);
331
332 sched_switch_user(task);
333
334 /* send notifications */
335 if (new->uid != old->uid ||
336 new->euid != old->euid ||
337 new->suid != old->suid ||
338 new->fsuid != old->fsuid)
339 proc_id_connector(task, PROC_EVENT_UID);
152 340
153 atomic_inc(&pcred->user->processes); 341 if (new->gid != old->gid ||
342 new->egid != old->egid ||
343 new->sgid != old->sgid ||
344 new->fsgid != old->fsgid)
345 proc_id_connector(task, PROC_EVENT_GID);
154 346
155 /* RCU assignment is unneeded here as no-one can have accessed this 347 put_cred(old);
156 * pointer yet, barring us */
157 p->cred = pcred;
158 return 0; 348 return 0;
159} 349}
350EXPORT_SYMBOL(commit_creds);
351
352/**
353 * abort_creds - Discard a set of credentials and unlock the current task
354 * @new: The credentials that were going to be applied
355 *
356 * Discard a set of credentials that were under construction and unlock the
357 * current task.
358 */
359void abort_creds(struct cred *new)
360{
361 BUG_ON(atomic_read(&new->usage) < 1);
362 put_cred(new);
363}
364EXPORT_SYMBOL(abort_creds);
365
366/**
367 * override_creds - Temporarily override the current process's credentials
368 * @new: The credentials to be assigned
369 *
370 * Install a set of temporary override credentials on the current process,
371 * returning the old set for later reversion.
372 */
373const struct cred *override_creds(const struct cred *new)
374{
375 const struct cred *old = current->cred;
376
377 rcu_assign_pointer(current->cred, get_cred(new));
378 return old;
379}
380EXPORT_SYMBOL(override_creds);
381
382/**
383 * revert_creds - Revert a temporary credentials override
384 * @old: The credentials to be restored
385 *
386 * Revert a temporary set of override credentials to an old set, discarding the
387 * override set.
388 */
389void revert_creds(const struct cred *old)
390{
391 const struct cred *override = current->cred;
392
393 rcu_assign_pointer(current->cred, old);
394 put_cred(override);
395}
396EXPORT_SYMBOL(revert_creds);
397
398/*
399 * initialise the credentials stuff
400 */
401void __init cred_init(void)
402{
403 /* allocate a slab in which we can store credentials */
404 cred_jar = kmem_cache_create("cred_jar", sizeof(struct cred),
405 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
406}