diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/auditsc.c | 42 | ||||
-rw-r--r-- | kernel/capability.c | 78 | ||||
-rw-r--r-- | kernel/cred-internals.h | 21 | ||||
-rw-r--r-- | kernel/cred.c | 321 | ||||
-rw-r--r-- | kernel/exit.c | 9 | ||||
-rw-r--r-- | kernel/fork.c | 7 | ||||
-rw-r--r-- | kernel/kmod.c | 30 | ||||
-rw-r--r-- | kernel/ptrace.c | 9 | ||||
-rw-r--r-- | kernel/signal.c | 10 | ||||
-rw-r--r-- | kernel/sys.c | 450 | ||||
-rw-r--r-- | kernel/user.c | 37 | ||||
-rw-r--r-- | kernel/user_namespace.c | 12 |
12 files changed, 652 insertions, 374 deletions
diff --git a/kernel/auditsc.c b/kernel/auditsc.c index ae8ef88ade3f..bc1e2d854bf6 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c | |||
@@ -2546,18 +2546,17 @@ int __audit_signal_info(int sig, struct task_struct *t) | |||
2546 | 2546 | ||
2547 | /** | 2547 | /** |
2548 | * __audit_log_bprm_fcaps - store information about a loading bprm and relevant fcaps | 2548 | * __audit_log_bprm_fcaps - store information about a loading bprm and relevant fcaps |
2549 | * @bprm pointer to the bprm being processed | 2549 | * @bprm: pointer to the bprm being processed |
2550 | * @caps the caps read from the disk | 2550 | * @new: the proposed new credentials |
2551 | * @old: the old credentials | ||
2551 | * | 2552 | * |
2552 | * Simply check if the proc already has the caps given by the file and if not | 2553 | * Simply check if the proc already has the caps given by the file and if not |
2553 | * store the priv escalation info for later auditing at the end of the syscall | 2554 | * store the priv escalation info for later auditing at the end of the syscall |
2554 | * | 2555 | * |
2555 | * this can fail and we don't care. See the note in audit.h for | ||
2556 | * audit_log_bprm_fcaps() for my explaination.... | ||
2557 | * | ||
2558 | * -Eric | 2556 | * -Eric |
2559 | */ | 2557 | */ |
2560 | void __audit_log_bprm_fcaps(struct linux_binprm *bprm, kernel_cap_t *pP, kernel_cap_t *pE) | 2558 | int __audit_log_bprm_fcaps(struct linux_binprm *bprm, |
2559 | const struct cred *new, const struct cred *old) | ||
2561 | { | 2560 | { |
2562 | struct audit_aux_data_bprm_fcaps *ax; | 2561 | struct audit_aux_data_bprm_fcaps *ax; |
2563 | struct audit_context *context = current->audit_context; | 2562 | struct audit_context *context = current->audit_context; |
@@ -2566,7 +2565,7 @@ void __audit_log_bprm_fcaps(struct linux_binprm *bprm, kernel_cap_t *pP, kernel_ | |||
2566 | 2565 | ||
2567 | ax = kmalloc(sizeof(*ax), GFP_KERNEL); | 2566 | ax = kmalloc(sizeof(*ax), GFP_KERNEL); |
2568 | if (!ax) | 2567 | if (!ax) |
2569 | return; | 2568 | return -ENOMEM; |
2570 | 2569 | ||
2571 | ax->d.type = AUDIT_BPRM_FCAPS; | 2570 | ax->d.type = AUDIT_BPRM_FCAPS; |
2572 | ax->d.next = context->aux; | 2571 | ax->d.next = context->aux; |
@@ -2581,26 +2580,27 @@ void __audit_log_bprm_fcaps(struct linux_binprm *bprm, kernel_cap_t *pP, kernel_ | |||
2581 | ax->fcap.fE = !!(vcaps.magic_etc & VFS_CAP_FLAGS_EFFECTIVE); | 2580 | ax->fcap.fE = !!(vcaps.magic_etc & VFS_CAP_FLAGS_EFFECTIVE); |
2582 | ax->fcap_ver = (vcaps.magic_etc & VFS_CAP_REVISION_MASK) >> VFS_CAP_REVISION_SHIFT; | 2581 | ax->fcap_ver = (vcaps.magic_etc & VFS_CAP_REVISION_MASK) >> VFS_CAP_REVISION_SHIFT; |
2583 | 2582 | ||
2584 | ax->old_pcap.permitted = *pP; | 2583 | ax->old_pcap.permitted = old->cap_permitted; |
2585 | ax->old_pcap.inheritable = current->cred->cap_inheritable; | 2584 | ax->old_pcap.inheritable = old->cap_inheritable; |
2586 | ax->old_pcap.effective = *pE; | 2585 | ax->old_pcap.effective = old->cap_effective; |
2587 | 2586 | ||
2588 | ax->new_pcap.permitted = current->cred->cap_permitted; | 2587 | ax->new_pcap.permitted = new->cap_permitted; |
2589 | ax->new_pcap.inheritable = current->cred->cap_inheritable; | 2588 | ax->new_pcap.inheritable = new->cap_inheritable; |
2590 | ax->new_pcap.effective = current->cred->cap_effective; | 2589 | ax->new_pcap.effective = new->cap_effective; |
2590 | return 0; | ||
2591 | } | 2591 | } |
2592 | 2592 | ||
2593 | /** | 2593 | /** |
2594 | * __audit_log_capset - store information about the arguments to the capset syscall | 2594 | * __audit_log_capset - store information about the arguments to the capset syscall |
2595 | * @pid target pid of the capset call | 2595 | * @pid: target pid of the capset call |
2596 | * @eff effective cap set | 2596 | * @new: the new credentials |
2597 | * @inh inheritible cap set | 2597 | * @old: the old (current) credentials |
2598 | * @perm permited cap set | ||
2599 | * | 2598 | * |
2600 | * Record the aguments userspace sent to sys_capset for later printing by the | 2599 | * Record the aguments userspace sent to sys_capset for later printing by the |
2601 | * audit system if applicable | 2600 | * audit system if applicable |
2602 | */ | 2601 | */ |
2603 | int __audit_log_capset(pid_t pid, kernel_cap_t *eff, kernel_cap_t *inh, kernel_cap_t *perm) | 2602 | int __audit_log_capset(pid_t pid, |
2603 | const struct cred *new, const struct cred *old) | ||
2604 | { | 2604 | { |
2605 | struct audit_aux_data_capset *ax; | 2605 | struct audit_aux_data_capset *ax; |
2606 | struct audit_context *context = current->audit_context; | 2606 | struct audit_context *context = current->audit_context; |
@@ -2617,9 +2617,9 @@ int __audit_log_capset(pid_t pid, kernel_cap_t *eff, kernel_cap_t *inh, kernel_c | |||
2617 | context->aux = (void *)ax; | 2617 | context->aux = (void *)ax; |
2618 | 2618 | ||
2619 | ax->pid = pid; | 2619 | ax->pid = pid; |
2620 | ax->cap.effective = *eff; | 2620 | ax->cap.effective = new->cap_effective; |
2621 | ax->cap.inheritable = *eff; | 2621 | ax->cap.inheritable = new->cap_effective; |
2622 | ax->cap.permitted = *perm; | 2622 | ax->cap.permitted = new->cap_permitted; |
2623 | 2623 | ||
2624 | return 0; | 2624 | return 0; |
2625 | } | 2625 | } |
diff --git a/kernel/capability.c b/kernel/capability.c index a404b980b1bd..36b4b4daebec 100644 --- a/kernel/capability.c +++ b/kernel/capability.c | |||
@@ -15,12 +15,7 @@ | |||
15 | #include <linux/syscalls.h> | 15 | #include <linux/syscalls.h> |
16 | #include <linux/pid_namespace.h> | 16 | #include <linux/pid_namespace.h> |
17 | #include <asm/uaccess.h> | 17 | #include <asm/uaccess.h> |
18 | 18 | #include "cred-internals.h" | |
19 | /* | ||
20 | * This lock protects task->cap_* for all tasks including current. | ||
21 | * Locking rule: acquire this prior to tasklist_lock. | ||
22 | */ | ||
23 | static DEFINE_SPINLOCK(task_capability_lock); | ||
24 | 19 | ||
25 | /* | 20 | /* |
26 | * Leveraged for setting/resetting capabilities | 21 | * Leveraged for setting/resetting capabilities |
@@ -128,12 +123,11 @@ static int cap_validate_magic(cap_user_header_t header, unsigned *tocopy) | |||
128 | } | 123 | } |
129 | 124 | ||
130 | /* | 125 | /* |
131 | * If we have configured with filesystem capability support, then the | 126 | * The only thing that can change the capabilities of the current |
132 | * only thing that can change the capabilities of the current process | 127 | * process is the current process. As such, we can't be in this code |
133 | * is the current process. As such, we can't be in this code at the | 128 | * at the same time as we are in the process of setting capabilities |
134 | * same time as we are in the process of setting capabilities in this | 129 | * in this process. The net result is that we can limit our use of |
135 | * process. The net result is that we can limit our use of locks to | 130 | * locks to when we are reading the caps of another process. |
136 | * when we are reading the caps of another process. | ||
137 | */ | 131 | */ |
138 | static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp, | 132 | static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp, |
139 | kernel_cap_t *pIp, kernel_cap_t *pPp) | 133 | kernel_cap_t *pIp, kernel_cap_t *pPp) |
@@ -143,7 +137,6 @@ static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp, | |||
143 | if (pid && (pid != task_pid_vnr(current))) { | 137 | if (pid && (pid != task_pid_vnr(current))) { |
144 | struct task_struct *target; | 138 | struct task_struct *target; |
145 | 139 | ||
146 | spin_lock(&task_capability_lock); | ||
147 | read_lock(&tasklist_lock); | 140 | read_lock(&tasklist_lock); |
148 | 141 | ||
149 | target = find_task_by_vpid(pid); | 142 | target = find_task_by_vpid(pid); |
@@ -153,34 +146,12 @@ static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp, | |||
153 | ret = security_capget(target, pEp, pIp, pPp); | 146 | ret = security_capget(target, pEp, pIp, pPp); |
154 | 147 | ||
155 | read_unlock(&tasklist_lock); | 148 | read_unlock(&tasklist_lock); |
156 | spin_unlock(&task_capability_lock); | ||
157 | } else | 149 | } else |
158 | ret = security_capget(current, pEp, pIp, pPp); | 150 | ret = security_capget(current, pEp, pIp, pPp); |
159 | 151 | ||
160 | return ret; | 152 | return ret; |
161 | } | 153 | } |
162 | 154 | ||
163 | /* | ||
164 | * Atomically modify the effective capabilities returning the original | ||
165 | * value. No permission check is performed here - it is assumed that the | ||
166 | * caller is permitted to set the desired effective capabilities. | ||
167 | */ | ||
168 | kernel_cap_t cap_set_effective(const kernel_cap_t pE_new) | ||
169 | { | ||
170 | kernel_cap_t pE_old; | ||
171 | |||
172 | spin_lock(&task_capability_lock); | ||
173 | |||
174 | pE_old = current->cred->cap_effective; | ||
175 | current->cred->cap_effective = pE_new; | ||
176 | |||
177 | spin_unlock(&task_capability_lock); | ||
178 | |||
179 | return pE_old; | ||
180 | } | ||
181 | |||
182 | EXPORT_SYMBOL(cap_set_effective); | ||
183 | |||
184 | /** | 155 | /** |
185 | * sys_capget - get the capabilities of a given process. | 156 | * sys_capget - get the capabilities of a given process. |
186 | * @header: pointer to struct that contains capability version and | 157 | * @header: pointer to struct that contains capability version and |
@@ -208,7 +179,6 @@ asmlinkage long sys_capget(cap_user_header_t header, cap_user_data_t dataptr) | |||
208 | return -EINVAL; | 179 | return -EINVAL; |
209 | 180 | ||
210 | ret = cap_get_target_pid(pid, &pE, &pI, &pP); | 181 | ret = cap_get_target_pid(pid, &pE, &pI, &pP); |
211 | |||
212 | if (!ret) { | 182 | if (!ret) { |
213 | struct __user_cap_data_struct kdata[_KERNEL_CAPABILITY_U32S]; | 183 | struct __user_cap_data_struct kdata[_KERNEL_CAPABILITY_U32S]; |
214 | unsigned i; | 184 | unsigned i; |
@@ -270,6 +240,7 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data) | |||
270 | struct __user_cap_data_struct kdata[_KERNEL_CAPABILITY_U32S]; | 240 | struct __user_cap_data_struct kdata[_KERNEL_CAPABILITY_U32S]; |
271 | unsigned i, tocopy; | 241 | unsigned i, tocopy; |
272 | kernel_cap_t inheritable, permitted, effective; | 242 | kernel_cap_t inheritable, permitted, effective; |
243 | struct cred *new; | ||
273 | int ret; | 244 | int ret; |
274 | pid_t pid; | 245 | pid_t pid; |
275 | 246 | ||
@@ -284,8 +255,8 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data) | |||
284 | if (pid != 0 && pid != task_pid_vnr(current)) | 255 | if (pid != 0 && pid != task_pid_vnr(current)) |
285 | return -EPERM; | 256 | return -EPERM; |
286 | 257 | ||
287 | if (copy_from_user(&kdata, data, tocopy | 258 | if (copy_from_user(&kdata, data, |
288 | * sizeof(struct __user_cap_data_struct))) | 259 | tocopy * sizeof(struct __user_cap_data_struct))) |
289 | return -EFAULT; | 260 | return -EFAULT; |
290 | 261 | ||
291 | for (i = 0; i < tocopy; i++) { | 262 | for (i = 0; i < tocopy; i++) { |
@@ -300,24 +271,23 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data) | |||
300 | i++; | 271 | i++; |
301 | } | 272 | } |
302 | 273 | ||
303 | ret = audit_log_capset(pid, &effective, &inheritable, &permitted); | 274 | new = prepare_creds(); |
304 | if (ret) | 275 | if (!new) |
276 | return -ENOMEM; | ||
277 | |||
278 | ret = security_capset(new, current_cred(), | ||
279 | &effective, &inheritable, &permitted); | ||
280 | if (ret < 0) | ||
281 | goto error; | ||
282 | |||
283 | ret = audit_log_capset(pid, new, current_cred()); | ||
284 | if (ret < 0) | ||
305 | return ret; | 285 | return ret; |
306 | 286 | ||
307 | /* This lock is required even when filesystem capability support is | 287 | return commit_creds(new); |
308 | * configured - it protects the sys_capget() call from returning | 288 | |
309 | * incorrect data in the case that the targeted process is not the | 289 | error: |
310 | * current one. | 290 | abort_creds(new); |
311 | */ | ||
312 | spin_lock(&task_capability_lock); | ||
313 | |||
314 | ret = security_capset_check(&effective, &inheritable, &permitted); | ||
315 | /* Having verified that the proposed changes are legal, we now put them | ||
316 | * into effect. | ||
317 | */ | ||
318 | if (!ret) | ||
319 | security_capset_set(&effective, &inheritable, &permitted); | ||
320 | spin_unlock(&task_capability_lock); | ||
321 | return ret; | 291 | return ret; |
322 | } | 292 | } |
323 | 293 | ||
diff --git a/kernel/cred-internals.h b/kernel/cred-internals.h new file mode 100644 index 000000000000..2dc4fc2d0bf1 --- /dev/null +++ b/kernel/cred-internals.h | |||
@@ -0,0 +1,21 @@ | |||
1 | /* Internal credentials stuff | ||
2 | * | ||
3 | * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public Licence | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the Licence, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | /* | ||
13 | * user.c | ||
14 | */ | ||
15 | static inline void sched_switch_user(struct task_struct *p) | ||
16 | { | ||
17 | #ifdef CONFIG_USER_SCHED | ||
18 | sched_move_task(p); | ||
19 | #endif /* CONFIG_USER_SCHED */ | ||
20 | } | ||
21 | |||
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 | |||
21 | static 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 | */ |
67 | static void release_tgcred(struct cred *cred) | 71 | void 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 | */ |
101 | void __put_cred(struct cred *cred) | 107 | void __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 | } |
105 | EXPORT_SYMBOL(__put_cred); | 113 | EXPORT_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 | */ | ||
127 | struct 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 | |||
160 | error: | ||
161 | abort_creds(new); | ||
162 | return NULL; | ||
163 | } | ||
164 | EXPORT_SYMBOL(prepare_creds); | ||
165 | |||
166 | /* | ||
167 | * prepare new credentials for the usermode helper dispatcher | ||
168 | */ | ||
169 | struct 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 | |||
211 | error: | ||
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 | */ |
110 | int copy_creds(struct task_struct *p, unsigned long clone_flags) | 222 | int 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 | */ | ||
292 | int 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 | } |
350 | EXPORT_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 | */ | ||
359 | void abort_creds(struct cred *new) | ||
360 | { | ||
361 | BUG_ON(atomic_read(&new->usage) < 1); | ||
362 | put_cred(new); | ||
363 | } | ||
364 | EXPORT_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 | */ | ||
373 | const 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 | } | ||
380 | EXPORT_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 | */ | ||
389 | void 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 | } | ||
396 | EXPORT_SYMBOL(revert_creds); | ||
397 | |||
398 | /* | ||
399 | * initialise the credentials stuff | ||
400 | */ | ||
401 | void __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 | } | ||
diff --git a/kernel/exit.c b/kernel/exit.c index bbc22530f2c1..c0711da15486 100644 --- a/kernel/exit.c +++ b/kernel/exit.c | |||
@@ -47,12 +47,14 @@ | |||
47 | #include <linux/blkdev.h> | 47 | #include <linux/blkdev.h> |
48 | #include <linux/task_io_accounting_ops.h> | 48 | #include <linux/task_io_accounting_ops.h> |
49 | #include <linux/tracehook.h> | 49 | #include <linux/tracehook.h> |
50 | #include <linux/init_task.h> | ||
50 | #include <trace/sched.h> | 51 | #include <trace/sched.h> |
51 | 52 | ||
52 | #include <asm/uaccess.h> | 53 | #include <asm/uaccess.h> |
53 | #include <asm/unistd.h> | 54 | #include <asm/unistd.h> |
54 | #include <asm/pgtable.h> | 55 | #include <asm/pgtable.h> |
55 | #include <asm/mmu_context.h> | 56 | #include <asm/mmu_context.h> |
57 | #include "cred-internals.h" | ||
56 | 58 | ||
57 | static void exit_mm(struct task_struct * tsk); | 59 | static void exit_mm(struct task_struct * tsk); |
58 | 60 | ||
@@ -338,12 +340,12 @@ static void reparent_to_kthreadd(void) | |||
338 | /* cpus_allowed? */ | 340 | /* cpus_allowed? */ |
339 | /* rt_priority? */ | 341 | /* rt_priority? */ |
340 | /* signals? */ | 342 | /* signals? */ |
341 | security_task_reparent_to_init(current); | ||
342 | memcpy(current->signal->rlim, init_task.signal->rlim, | 343 | memcpy(current->signal->rlim, init_task.signal->rlim, |
343 | sizeof(current->signal->rlim)); | 344 | sizeof(current->signal->rlim)); |
344 | atomic_inc(&(INIT_USER->__count)); | 345 | |
346 | atomic_inc(&init_cred.usage); | ||
347 | commit_creds(&init_cred); | ||
345 | write_unlock_irq(&tasklist_lock); | 348 | write_unlock_irq(&tasklist_lock); |
346 | switch_uid(INIT_USER); | ||
347 | } | 349 | } |
348 | 350 | ||
349 | void __set_special_pids(struct pid *pid) | 351 | void __set_special_pids(struct pid *pid) |
@@ -1085,7 +1087,6 @@ NORET_TYPE void do_exit(long code) | |||
1085 | check_stack_usage(); | 1087 | check_stack_usage(); |
1086 | exit_thread(); | 1088 | exit_thread(); |
1087 | cgroup_exit(tsk, 1); | 1089 | cgroup_exit(tsk, 1); |
1088 | exit_keys(tsk); | ||
1089 | 1090 | ||
1090 | if (group_dead && tsk->signal->leader) | 1091 | if (group_dead && tsk->signal->leader) |
1091 | disassociate_ctty(1); | 1092 | disassociate_ctty(1); |
diff --git a/kernel/fork.c b/kernel/fork.c index ded1972672a3..82a7948a664e 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -1084,10 +1084,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
1084 | goto bad_fork_cleanup_sighand; | 1084 | goto bad_fork_cleanup_sighand; |
1085 | if ((retval = copy_mm(clone_flags, p))) | 1085 | if ((retval = copy_mm(clone_flags, p))) |
1086 | goto bad_fork_cleanup_signal; | 1086 | goto bad_fork_cleanup_signal; |
1087 | if ((retval = copy_keys(clone_flags, p))) | ||
1088 | goto bad_fork_cleanup_mm; | ||
1089 | if ((retval = copy_namespaces(clone_flags, p))) | 1087 | if ((retval = copy_namespaces(clone_flags, p))) |
1090 | goto bad_fork_cleanup_keys; | 1088 | goto bad_fork_cleanup_mm; |
1091 | if ((retval = copy_io(clone_flags, p))) | 1089 | if ((retval = copy_io(clone_flags, p))) |
1092 | goto bad_fork_cleanup_namespaces; | 1090 | goto bad_fork_cleanup_namespaces; |
1093 | retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs); | 1091 | retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs); |
@@ -1252,8 +1250,6 @@ bad_fork_cleanup_io: | |||
1252 | put_io_context(p->io_context); | 1250 | put_io_context(p->io_context); |
1253 | bad_fork_cleanup_namespaces: | 1251 | bad_fork_cleanup_namespaces: |
1254 | exit_task_namespaces(p); | 1252 | exit_task_namespaces(p); |
1255 | bad_fork_cleanup_keys: | ||
1256 | exit_keys(p); | ||
1257 | bad_fork_cleanup_mm: | 1253 | bad_fork_cleanup_mm: |
1258 | if (p->mm) | 1254 | if (p->mm) |
1259 | mmput(p->mm); | 1255 | mmput(p->mm); |
@@ -1281,6 +1277,7 @@ bad_fork_cleanup_cgroup: | |||
1281 | bad_fork_cleanup_put_domain: | 1277 | bad_fork_cleanup_put_domain: |
1282 | module_put(task_thread_info(p)->exec_domain->module); | 1278 | module_put(task_thread_info(p)->exec_domain->module); |
1283 | bad_fork_cleanup_count: | 1279 | bad_fork_cleanup_count: |
1280 | atomic_dec(&p->cred->user->processes); | ||
1284 | put_cred(p->cred); | 1281 | put_cred(p->cred); |
1285 | bad_fork_free: | 1282 | bad_fork_free: |
1286 | free_task(p); | 1283 | free_task(p); |
diff --git a/kernel/kmod.c b/kernel/kmod.c index f044f8f57703..b46dbb908669 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c | |||
@@ -118,10 +118,10 @@ EXPORT_SYMBOL(request_module); | |||
118 | struct subprocess_info { | 118 | struct subprocess_info { |
119 | struct work_struct work; | 119 | struct work_struct work; |
120 | struct completion *complete; | 120 | struct completion *complete; |
121 | struct cred *cred; | ||
121 | char *path; | 122 | char *path; |
122 | char **argv; | 123 | char **argv; |
123 | char **envp; | 124 | char **envp; |
124 | struct key *ring; | ||
125 | enum umh_wait wait; | 125 | enum umh_wait wait; |
126 | int retval; | 126 | int retval; |
127 | struct file *stdin; | 127 | struct file *stdin; |
@@ -134,19 +134,20 @@ struct subprocess_info { | |||
134 | static int ____call_usermodehelper(void *data) | 134 | static int ____call_usermodehelper(void *data) |
135 | { | 135 | { |
136 | struct subprocess_info *sub_info = data; | 136 | struct subprocess_info *sub_info = data; |
137 | struct key *new_session, *old_session; | ||
138 | int retval; | 137 | int retval; |
139 | 138 | ||
140 | /* Unblock all signals and set the session keyring. */ | 139 | BUG_ON(atomic_read(&sub_info->cred->usage) != 1); |
141 | new_session = key_get(sub_info->ring); | 140 | |
141 | /* Unblock all signals */ | ||
142 | spin_lock_irq(¤t->sighand->siglock); | 142 | spin_lock_irq(¤t->sighand->siglock); |
143 | old_session = __install_session_keyring(new_session); | ||
144 | flush_signal_handlers(current, 1); | 143 | flush_signal_handlers(current, 1); |
145 | sigemptyset(¤t->blocked); | 144 | sigemptyset(¤t->blocked); |
146 | recalc_sigpending(); | 145 | recalc_sigpending(); |
147 | spin_unlock_irq(¤t->sighand->siglock); | 146 | spin_unlock_irq(¤t->sighand->siglock); |
148 | 147 | ||
149 | key_put(old_session); | 148 | /* Install the credentials */ |
149 | commit_creds(sub_info->cred); | ||
150 | sub_info->cred = NULL; | ||
150 | 151 | ||
151 | /* Install input pipe when needed */ | 152 | /* Install input pipe when needed */ |
152 | if (sub_info->stdin) { | 153 | if (sub_info->stdin) { |
@@ -185,6 +186,8 @@ void call_usermodehelper_freeinfo(struct subprocess_info *info) | |||
185 | { | 186 | { |
186 | if (info->cleanup) | 187 | if (info->cleanup) |
187 | (*info->cleanup)(info->argv, info->envp); | 188 | (*info->cleanup)(info->argv, info->envp); |
189 | if (info->cred) | ||
190 | put_cred(info->cred); | ||
188 | kfree(info); | 191 | kfree(info); |
189 | } | 192 | } |
190 | EXPORT_SYMBOL(call_usermodehelper_freeinfo); | 193 | EXPORT_SYMBOL(call_usermodehelper_freeinfo); |
@@ -240,6 +243,8 @@ static void __call_usermodehelper(struct work_struct *work) | |||
240 | pid_t pid; | 243 | pid_t pid; |
241 | enum umh_wait wait = sub_info->wait; | 244 | enum umh_wait wait = sub_info->wait; |
242 | 245 | ||
246 | BUG_ON(atomic_read(&sub_info->cred->usage) != 1); | ||
247 | |||
243 | /* CLONE_VFORK: wait until the usermode helper has execve'd | 248 | /* CLONE_VFORK: wait until the usermode helper has execve'd |
244 | * successfully We need the data structures to stay around | 249 | * successfully We need the data structures to stay around |
245 | * until that is done. */ | 250 | * until that is done. */ |
@@ -362,6 +367,9 @@ struct subprocess_info *call_usermodehelper_setup(char *path, char **argv, | |||
362 | sub_info->path = path; | 367 | sub_info->path = path; |
363 | sub_info->argv = argv; | 368 | sub_info->argv = argv; |
364 | sub_info->envp = envp; | 369 | sub_info->envp = envp; |
370 | sub_info->cred = prepare_usermodehelper_creds(); | ||
371 | if (!sub_info->cred) | ||
372 | return NULL; | ||
365 | 373 | ||
366 | out: | 374 | out: |
367 | return sub_info; | 375 | return sub_info; |
@@ -376,7 +384,13 @@ EXPORT_SYMBOL(call_usermodehelper_setup); | |||
376 | void call_usermodehelper_setkeys(struct subprocess_info *info, | 384 | void call_usermodehelper_setkeys(struct subprocess_info *info, |
377 | struct key *session_keyring) | 385 | struct key *session_keyring) |
378 | { | 386 | { |
379 | info->ring = session_keyring; | 387 | #ifdef CONFIG_KEYS |
388 | struct thread_group_cred *tgcred = info->cred->tgcred; | ||
389 | key_put(tgcred->session_keyring); | ||
390 | tgcred->session_keyring = key_get(session_keyring); | ||
391 | #else | ||
392 | BUG(); | ||
393 | #endif | ||
380 | } | 394 | } |
381 | EXPORT_SYMBOL(call_usermodehelper_setkeys); | 395 | EXPORT_SYMBOL(call_usermodehelper_setkeys); |
382 | 396 | ||
@@ -444,6 +458,8 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, | |||
444 | DECLARE_COMPLETION_ONSTACK(done); | 458 | DECLARE_COMPLETION_ONSTACK(done); |
445 | int retval = 0; | 459 | int retval = 0; |
446 | 460 | ||
461 | BUG_ON(atomic_read(&sub_info->cred->usage) != 1); | ||
462 | |||
447 | helper_lock(); | 463 | helper_lock(); |
448 | if (sub_info->path[0] == '\0') | 464 | if (sub_info->path[0] == '\0') |
449 | goto out; | 465 | goto out; |
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index b9d5f4e4f6a4..f764b8806955 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
@@ -171,6 +171,14 @@ int ptrace_attach(struct task_struct *task) | |||
171 | if (same_thread_group(task, current)) | 171 | if (same_thread_group(task, current)) |
172 | goto out; | 172 | goto out; |
173 | 173 | ||
174 | /* Protect exec's credential calculations against our interference; | ||
175 | * SUID, SGID and LSM creds get determined differently under ptrace. | ||
176 | */ | ||
177 | retval = mutex_lock_interruptible(¤t->cred_exec_mutex); | ||
178 | if (retval < 0) | ||
179 | goto out; | ||
180 | |||
181 | retval = -EPERM; | ||
174 | repeat: | 182 | repeat: |
175 | /* | 183 | /* |
176 | * Nasty, nasty. | 184 | * Nasty, nasty. |
@@ -210,6 +218,7 @@ repeat: | |||
210 | bad: | 218 | bad: |
211 | write_unlock_irqrestore(&tasklist_lock, flags); | 219 | write_unlock_irqrestore(&tasklist_lock, flags); |
212 | task_unlock(task); | 220 | task_unlock(task); |
221 | mutex_unlock(¤t->cred_exec_mutex); | ||
213 | out: | 222 | out: |
214 | return retval; | 223 | return retval; |
215 | } | 224 | } |
diff --git a/kernel/signal.c b/kernel/signal.c index 84989124bafb..2a64304ed54b 100644 --- a/kernel/signal.c +++ b/kernel/signal.c | |||
@@ -180,7 +180,7 @@ int next_signal(struct sigpending *pending, sigset_t *mask) | |||
180 | /* | 180 | /* |
181 | * allocate a new signal queue record | 181 | * allocate a new signal queue record |
182 | * - this may be called without locks if and only if t == current, otherwise an | 182 | * - this may be called without locks if and only if t == current, otherwise an |
183 | * appopriate lock must be held to protect t's user_struct | 183 | * appopriate lock must be held to stop the target task from exiting |
184 | */ | 184 | */ |
185 | static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags, | 185 | static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags, |
186 | int override_rlimit) | 186 | int override_rlimit) |
@@ -194,7 +194,7 @@ static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags, | |||
194 | * caller must be holding the RCU readlock (by way of a spinlock) and | 194 | * caller must be holding the RCU readlock (by way of a spinlock) and |
195 | * we use RCU protection here | 195 | * we use RCU protection here |
196 | */ | 196 | */ |
197 | user = __task_cred(t)->user; | 197 | user = get_uid(__task_cred(t)->user); |
198 | atomic_inc(&user->sigpending); | 198 | atomic_inc(&user->sigpending); |
199 | if (override_rlimit || | 199 | if (override_rlimit || |
200 | atomic_read(&user->sigpending) <= | 200 | atomic_read(&user->sigpending) <= |
@@ -202,12 +202,14 @@ static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags, | |||
202 | q = kmem_cache_alloc(sigqueue_cachep, flags); | 202 | q = kmem_cache_alloc(sigqueue_cachep, flags); |
203 | if (unlikely(q == NULL)) { | 203 | if (unlikely(q == NULL)) { |
204 | atomic_dec(&user->sigpending); | 204 | atomic_dec(&user->sigpending); |
205 | free_uid(user); | ||
205 | } else { | 206 | } else { |
206 | INIT_LIST_HEAD(&q->list); | 207 | INIT_LIST_HEAD(&q->list); |
207 | q->flags = 0; | 208 | q->flags = 0; |
208 | q->user = get_uid(user); | 209 | q->user = user; |
209 | } | 210 | } |
210 | return(q); | 211 | |
212 | return q; | ||
211 | } | 213 | } |
212 | 214 | ||
213 | static void __sigqueue_free(struct sigqueue *q) | 215 | static void __sigqueue_free(struct sigqueue *q) |
diff --git a/kernel/sys.c b/kernel/sys.c index ccc9eb736d35..ab735040468a 100644 --- a/kernel/sys.c +++ b/kernel/sys.c | |||
@@ -180,7 +180,7 @@ asmlinkage long sys_setpriority(int which, int who, int niceval) | |||
180 | } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); | 180 | } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); |
181 | break; | 181 | break; |
182 | case PRIO_USER: | 182 | case PRIO_USER: |
183 | user = cred->user; | 183 | user = (struct user_struct *) cred->user; |
184 | if (!who) | 184 | if (!who) |
185 | who = cred->uid; | 185 | who = cred->uid; |
186 | else if ((who != cred->uid) && | 186 | else if ((who != cred->uid) && |
@@ -479,47 +479,48 @@ void ctrl_alt_del(void) | |||
479 | */ | 479 | */ |
480 | asmlinkage long sys_setregid(gid_t rgid, gid_t egid) | 480 | asmlinkage long sys_setregid(gid_t rgid, gid_t egid) |
481 | { | 481 | { |
482 | struct cred *cred = current->cred; | 482 | const struct cred *old; |
483 | int old_rgid = cred->gid; | 483 | struct cred *new; |
484 | int old_egid = cred->egid; | ||
485 | int new_rgid = old_rgid; | ||
486 | int new_egid = old_egid; | ||
487 | int retval; | 484 | int retval; |
488 | 485 | ||
486 | new = prepare_creds(); | ||
487 | if (!new) | ||
488 | return -ENOMEM; | ||
489 | old = current_cred(); | ||
490 | |||
489 | retval = security_task_setgid(rgid, egid, (gid_t)-1, LSM_SETID_RE); | 491 | retval = security_task_setgid(rgid, egid, (gid_t)-1, LSM_SETID_RE); |
490 | if (retval) | 492 | if (retval) |
491 | return retval; | 493 | goto error; |
492 | 494 | ||
495 | retval = -EPERM; | ||
493 | if (rgid != (gid_t) -1) { | 496 | if (rgid != (gid_t) -1) { |
494 | if ((old_rgid == rgid) || | 497 | if (old->gid == rgid || |
495 | (cred->egid == rgid) || | 498 | old->egid == rgid || |
496 | capable(CAP_SETGID)) | 499 | capable(CAP_SETGID)) |
497 | new_rgid = rgid; | 500 | new->gid = rgid; |
498 | else | 501 | else |
499 | return -EPERM; | 502 | goto error; |
500 | } | 503 | } |
501 | if (egid != (gid_t) -1) { | 504 | if (egid != (gid_t) -1) { |
502 | if ((old_rgid == egid) || | 505 | if (old->gid == egid || |
503 | (cred->egid == egid) || | 506 | old->egid == egid || |
504 | (cred->sgid == egid) || | 507 | old->sgid == egid || |
505 | capable(CAP_SETGID)) | 508 | capable(CAP_SETGID)) |
506 | new_egid = egid; | 509 | new->egid = egid; |
507 | else | 510 | else |
508 | return -EPERM; | 511 | goto error; |
509 | } | ||
510 | if (new_egid != old_egid) { | ||
511 | set_dumpable(current->mm, suid_dumpable); | ||
512 | smp_wmb(); | ||
513 | } | 512 | } |
513 | |||
514 | if (rgid != (gid_t) -1 || | 514 | if (rgid != (gid_t) -1 || |
515 | (egid != (gid_t) -1 && egid != old_rgid)) | 515 | (egid != (gid_t) -1 && egid != old->gid)) |
516 | cred->sgid = new_egid; | 516 | new->sgid = new->egid; |
517 | cred->fsgid = new_egid; | 517 | new->fsgid = new->egid; |
518 | cred->egid = new_egid; | 518 | |
519 | cred->gid = new_rgid; | 519 | return commit_creds(new); |
520 | key_fsgid_changed(current); | 520 | |
521 | proc_id_connector(current, PROC_EVENT_GID); | 521 | error: |
522 | return 0; | 522 | abort_creds(new); |
523 | return retval; | ||
523 | } | 524 | } |
524 | 525 | ||
525 | /* | 526 | /* |
@@ -529,40 +530,42 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid) | |||
529 | */ | 530 | */ |
530 | asmlinkage long sys_setgid(gid_t gid) | 531 | asmlinkage long sys_setgid(gid_t gid) |
531 | { | 532 | { |
532 | struct cred *cred = current->cred; | 533 | const struct cred *old; |
533 | int old_egid = cred->egid; | 534 | struct cred *new; |
534 | int retval; | 535 | int retval; |
535 | 536 | ||
537 | new = prepare_creds(); | ||
538 | if (!new) | ||
539 | return -ENOMEM; | ||
540 | old = current_cred(); | ||
541 | |||
536 | retval = security_task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_ID); | 542 | retval = security_task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_ID); |
537 | if (retval) | 543 | if (retval) |
538 | return retval; | 544 | goto error; |
539 | 545 | ||
540 | if (capable(CAP_SETGID)) { | 546 | retval = -EPERM; |
541 | if (old_egid != gid) { | 547 | if (capable(CAP_SETGID)) |
542 | set_dumpable(current->mm, suid_dumpable); | 548 | new->gid = new->egid = new->sgid = new->fsgid = gid; |
543 | smp_wmb(); | 549 | else if (gid == old->gid || gid == old->sgid) |
544 | } | 550 | new->egid = new->fsgid = gid; |
545 | cred->gid = cred->egid = cred->sgid = cred->fsgid = gid; | ||
546 | } else if ((gid == cred->gid) || (gid == cred->sgid)) { | ||
547 | if (old_egid != gid) { | ||
548 | set_dumpable(current->mm, suid_dumpable); | ||
549 | smp_wmb(); | ||
550 | } | ||
551 | cred->egid = cred->fsgid = gid; | ||
552 | } | ||
553 | else | 551 | else |
554 | return -EPERM; | 552 | goto error; |
555 | 553 | ||
556 | key_fsgid_changed(current); | 554 | return commit_creds(new); |
557 | proc_id_connector(current, PROC_EVENT_GID); | 555 | |
558 | return 0; | 556 | error: |
557 | abort_creds(new); | ||
558 | return retval; | ||
559 | } | 559 | } |
560 | 560 | ||
561 | static int set_user(uid_t new_ruid, int dumpclear) | 561 | /* |
562 | * change the user struct in a credentials set to match the new UID | ||
563 | */ | ||
564 | static int set_user(struct cred *new) | ||
562 | { | 565 | { |
563 | struct user_struct *new_user; | 566 | struct user_struct *new_user; |
564 | 567 | ||
565 | new_user = alloc_uid(current->nsproxy->user_ns, new_ruid); | 568 | new_user = alloc_uid(current->nsproxy->user_ns, new->uid); |
566 | if (!new_user) | 569 | if (!new_user) |
567 | return -EAGAIN; | 570 | return -EAGAIN; |
568 | 571 | ||
@@ -573,13 +576,8 @@ static int set_user(uid_t new_ruid, int dumpclear) | |||
573 | return -EAGAIN; | 576 | return -EAGAIN; |
574 | } | 577 | } |
575 | 578 | ||
576 | switch_uid(new_user); | 579 | free_uid(new->user); |
577 | 580 | new->user = new_user; | |
578 | if (dumpclear) { | ||
579 | set_dumpable(current->mm, suid_dumpable); | ||
580 | smp_wmb(); | ||
581 | } | ||
582 | current->cred->uid = new_ruid; | ||
583 | return 0; | 581 | return 0; |
584 | } | 582 | } |
585 | 583 | ||
@@ -600,55 +598,56 @@ static int set_user(uid_t new_ruid, int dumpclear) | |||
600 | */ | 598 | */ |
601 | asmlinkage long sys_setreuid(uid_t ruid, uid_t euid) | 599 | asmlinkage long sys_setreuid(uid_t ruid, uid_t euid) |
602 | { | 600 | { |
603 | struct cred *cred = current->cred; | 601 | const struct cred *old; |
604 | int old_ruid, old_euid, old_suid, new_ruid, new_euid; | 602 | struct cred *new; |
605 | int retval; | 603 | int retval; |
606 | 604 | ||
605 | new = prepare_creds(); | ||
606 | if (!new) | ||
607 | return -ENOMEM; | ||
608 | old = current_cred(); | ||
609 | |||
607 | retval = security_task_setuid(ruid, euid, (uid_t)-1, LSM_SETID_RE); | 610 | retval = security_task_setuid(ruid, euid, (uid_t)-1, LSM_SETID_RE); |
608 | if (retval) | 611 | if (retval) |
609 | return retval; | 612 | goto error; |
610 | |||
611 | new_ruid = old_ruid = cred->uid; | ||
612 | new_euid = old_euid = cred->euid; | ||
613 | old_suid = cred->suid; | ||
614 | 613 | ||
614 | retval = -EPERM; | ||
615 | if (ruid != (uid_t) -1) { | 615 | if (ruid != (uid_t) -1) { |
616 | new_ruid = ruid; | 616 | new->uid = ruid; |
617 | if ((old_ruid != ruid) && | 617 | if (old->uid != ruid && |
618 | (cred->euid != ruid) && | 618 | old->euid != ruid && |
619 | !capable(CAP_SETUID)) | 619 | !capable(CAP_SETUID)) |
620 | return -EPERM; | 620 | goto error; |
621 | } | 621 | } |
622 | 622 | ||
623 | if (euid != (uid_t) -1) { | 623 | if (euid != (uid_t) -1) { |
624 | new_euid = euid; | 624 | new->euid = euid; |
625 | if ((old_ruid != euid) && | 625 | if (old->uid != euid && |
626 | (cred->euid != euid) && | 626 | old->euid != euid && |
627 | (cred->suid != euid) && | 627 | old->suid != euid && |
628 | !capable(CAP_SETUID)) | 628 | !capable(CAP_SETUID)) |
629 | return -EPERM; | 629 | goto error; |
630 | } | 630 | } |
631 | 631 | ||
632 | if (new_ruid != old_ruid && set_user(new_ruid, new_euid != old_euid) < 0) | 632 | retval = -EAGAIN; |
633 | return -EAGAIN; | 633 | if (new->uid != old->uid && set_user(new) < 0) |
634 | goto error; | ||
634 | 635 | ||
635 | if (new_euid != old_euid) { | ||
636 | set_dumpable(current->mm, suid_dumpable); | ||
637 | smp_wmb(); | ||
638 | } | ||
639 | cred->fsuid = cred->euid = new_euid; | ||
640 | if (ruid != (uid_t) -1 || | 636 | if (ruid != (uid_t) -1 || |
641 | (euid != (uid_t) -1 && euid != old_ruid)) | 637 | (euid != (uid_t) -1 && euid != old->uid)) |
642 | cred->suid = cred->euid; | 638 | new->suid = new->euid; |
643 | cred->fsuid = cred->euid; | 639 | new->fsuid = new->euid; |
644 | |||
645 | key_fsuid_changed(current); | ||
646 | proc_id_connector(current, PROC_EVENT_UID); | ||
647 | 640 | ||
648 | return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RE); | 641 | retval = security_task_fix_setuid(new, old, LSM_SETID_RE); |
649 | } | 642 | if (retval < 0) |
643 | goto error; | ||
650 | 644 | ||
645 | return commit_creds(new); | ||
651 | 646 | ||
647 | error: | ||
648 | abort_creds(new); | ||
649 | return retval; | ||
650 | } | ||
652 | 651 | ||
653 | /* | 652 | /* |
654 | * setuid() is implemented like SysV with SAVED_IDS | 653 | * setuid() is implemented like SysV with SAVED_IDS |
@@ -663,37 +662,41 @@ asmlinkage long sys_setreuid(uid_t ruid, uid_t euid) | |||
663 | */ | 662 | */ |
664 | asmlinkage long sys_setuid(uid_t uid) | 663 | asmlinkage long sys_setuid(uid_t uid) |
665 | { | 664 | { |
666 | struct cred *cred = current->cred; | 665 | const struct cred *old; |
667 | int old_euid = cred->euid; | 666 | struct cred *new; |
668 | int old_ruid, old_suid, new_suid; | ||
669 | int retval; | 667 | int retval; |
670 | 668 | ||
669 | new = prepare_creds(); | ||
670 | if (!new) | ||
671 | return -ENOMEM; | ||
672 | old = current_cred(); | ||
673 | |||
671 | retval = security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_ID); | 674 | retval = security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_ID); |
672 | if (retval) | 675 | if (retval) |
673 | return retval; | 676 | goto error; |
674 | 677 | ||
675 | old_ruid = cred->uid; | 678 | retval = -EPERM; |
676 | old_suid = cred->suid; | ||
677 | new_suid = old_suid; | ||
678 | |||
679 | if (capable(CAP_SETUID)) { | 679 | if (capable(CAP_SETUID)) { |
680 | if (uid != old_ruid && set_user(uid, old_euid != uid) < 0) | 680 | new->suid = new->uid = uid; |
681 | return -EAGAIN; | 681 | if (uid != old->uid && set_user(new) < 0) { |
682 | new_suid = uid; | 682 | retval = -EAGAIN; |
683 | } else if ((uid != cred->uid) && (uid != new_suid)) | 683 | goto error; |
684 | return -EPERM; | 684 | } |
685 | 685 | } else if (uid != old->uid && uid != new->suid) { | |
686 | if (old_euid != uid) { | 686 | goto error; |
687 | set_dumpable(current->mm, suid_dumpable); | ||
688 | smp_wmb(); | ||
689 | } | 687 | } |
690 | cred->fsuid = cred->euid = uid; | ||
691 | cred->suid = new_suid; | ||
692 | 688 | ||
693 | key_fsuid_changed(current); | 689 | new->fsuid = new->euid = uid; |
694 | proc_id_connector(current, PROC_EVENT_UID); | 690 | |
691 | retval = security_task_fix_setuid(new, old, LSM_SETID_ID); | ||
692 | if (retval < 0) | ||
693 | goto error; | ||
694 | |||
695 | return commit_creds(new); | ||
695 | 696 | ||
696 | return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_ID); | 697 | error: |
698 | abort_creds(new); | ||
699 | return retval; | ||
697 | } | 700 | } |
698 | 701 | ||
699 | 702 | ||
@@ -703,47 +706,53 @@ asmlinkage long sys_setuid(uid_t uid) | |||
703 | */ | 706 | */ |
704 | asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid) | 707 | asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid) |
705 | { | 708 | { |
706 | struct cred *cred = current->cred; | 709 | const struct cred *old; |
707 | int old_ruid = cred->uid; | 710 | struct cred *new; |
708 | int old_euid = cred->euid; | ||
709 | int old_suid = cred->suid; | ||
710 | int retval; | 711 | int retval; |
711 | 712 | ||
713 | new = prepare_creds(); | ||
714 | if (!new) | ||
715 | return -ENOMEM; | ||
716 | |||
712 | retval = security_task_setuid(ruid, euid, suid, LSM_SETID_RES); | 717 | retval = security_task_setuid(ruid, euid, suid, LSM_SETID_RES); |
713 | if (retval) | 718 | if (retval) |
714 | return retval; | 719 | goto error; |
720 | old = current_cred(); | ||
715 | 721 | ||
722 | retval = -EPERM; | ||
716 | if (!capable(CAP_SETUID)) { | 723 | if (!capable(CAP_SETUID)) { |
717 | if ((ruid != (uid_t) -1) && (ruid != cred->uid) && | 724 | if (ruid != (uid_t) -1 && ruid != old->uid && |
718 | (ruid != cred->euid) && (ruid != cred->suid)) | 725 | ruid != old->euid && ruid != old->suid) |
719 | return -EPERM; | 726 | goto error; |
720 | if ((euid != (uid_t) -1) && (euid != cred->uid) && | 727 | if (euid != (uid_t) -1 && euid != old->uid && |
721 | (euid != cred->euid) && (euid != cred->suid)) | 728 | euid != old->euid && euid != old->suid) |
722 | return -EPERM; | 729 | goto error; |
723 | if ((suid != (uid_t) -1) && (suid != cred->uid) && | 730 | if (suid != (uid_t) -1 && suid != old->uid && |
724 | (suid != cred->euid) && (suid != cred->suid)) | 731 | suid != old->euid && suid != old->suid) |
725 | return -EPERM; | 732 | goto error; |
726 | } | 733 | } |
734 | |||
735 | retval = -EAGAIN; | ||
727 | if (ruid != (uid_t) -1) { | 736 | if (ruid != (uid_t) -1) { |
728 | if (ruid != cred->uid && | 737 | new->uid = ruid; |
729 | set_user(ruid, euid != cred->euid) < 0) | 738 | if (ruid != old->uid && set_user(new) < 0) |
730 | return -EAGAIN; | 739 | goto error; |
731 | } | 740 | } |
732 | if (euid != (uid_t) -1) { | 741 | if (euid != (uid_t) -1) |
733 | if (euid != cred->euid) { | 742 | new->euid = euid; |
734 | set_dumpable(current->mm, suid_dumpable); | ||
735 | smp_wmb(); | ||
736 | } | ||
737 | cred->euid = euid; | ||
738 | } | ||
739 | cred->fsuid = cred->euid; | ||
740 | if (suid != (uid_t) -1) | 743 | if (suid != (uid_t) -1) |
741 | cred->suid = suid; | 744 | new->suid = suid; |
745 | new->fsuid = new->euid; | ||
742 | 746 | ||
743 | key_fsuid_changed(current); | 747 | retval = security_task_fix_setuid(new, old, LSM_SETID_RES); |
744 | proc_id_connector(current, PROC_EVENT_UID); | 748 | if (retval < 0) |
749 | goto error; | ||
745 | 750 | ||
746 | return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RES); | 751 | return commit_creds(new); |
752 | |||
753 | error: | ||
754 | abort_creds(new); | ||
755 | return retval; | ||
747 | } | 756 | } |
748 | 757 | ||
749 | asmlinkage long sys_getresuid(uid_t __user *ruid, uid_t __user *euid, uid_t __user *suid) | 758 | asmlinkage long sys_getresuid(uid_t __user *ruid, uid_t __user *euid, uid_t __user *suid) |
@@ -763,40 +772,45 @@ asmlinkage long sys_getresuid(uid_t __user *ruid, uid_t __user *euid, uid_t __us | |||
763 | */ | 772 | */ |
764 | asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid) | 773 | asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid) |
765 | { | 774 | { |
766 | struct cred *cred = current->cred; | 775 | const struct cred *old; |
776 | struct cred *new; | ||
767 | int retval; | 777 | int retval; |
768 | 778 | ||
779 | new = prepare_creds(); | ||
780 | if (!new) | ||
781 | return -ENOMEM; | ||
782 | old = current_cred(); | ||
783 | |||
769 | retval = security_task_setgid(rgid, egid, sgid, LSM_SETID_RES); | 784 | retval = security_task_setgid(rgid, egid, sgid, LSM_SETID_RES); |
770 | if (retval) | 785 | if (retval) |
771 | return retval; | 786 | goto error; |
772 | 787 | ||
788 | retval = -EPERM; | ||
773 | if (!capable(CAP_SETGID)) { | 789 | if (!capable(CAP_SETGID)) { |
774 | if ((rgid != (gid_t) -1) && (rgid != cred->gid) && | 790 | if (rgid != (gid_t) -1 && rgid != old->gid && |
775 | (rgid != cred->egid) && (rgid != cred->sgid)) | 791 | rgid != old->egid && rgid != old->sgid) |
776 | return -EPERM; | 792 | goto error; |
777 | if ((egid != (gid_t) -1) && (egid != cred->gid) && | 793 | if (egid != (gid_t) -1 && egid != old->gid && |
778 | (egid != cred->egid) && (egid != cred->sgid)) | 794 | egid != old->egid && egid != old->sgid) |
779 | return -EPERM; | 795 | goto error; |
780 | if ((sgid != (gid_t) -1) && (sgid != cred->gid) && | 796 | if (sgid != (gid_t) -1 && sgid != old->gid && |
781 | (sgid != cred->egid) && (sgid != cred->sgid)) | 797 | sgid != old->egid && sgid != old->sgid) |
782 | return -EPERM; | 798 | goto error; |
783 | } | 799 | } |
784 | if (egid != (gid_t) -1) { | 800 | |
785 | if (egid != cred->egid) { | ||
786 | set_dumpable(current->mm, suid_dumpable); | ||
787 | smp_wmb(); | ||
788 | } | ||
789 | cred->egid = egid; | ||
790 | } | ||
791 | cred->fsgid = cred->egid; | ||
792 | if (rgid != (gid_t) -1) | 801 | if (rgid != (gid_t) -1) |
793 | cred->gid = rgid; | 802 | new->gid = rgid; |
803 | if (egid != (gid_t) -1) | ||
804 | new->egid = egid; | ||
794 | if (sgid != (gid_t) -1) | 805 | if (sgid != (gid_t) -1) |
795 | cred->sgid = sgid; | 806 | new->sgid = sgid; |
807 | new->fsgid = new->egid; | ||
796 | 808 | ||
797 | key_fsgid_changed(current); | 809 | return commit_creds(new); |
798 | proc_id_connector(current, PROC_EVENT_GID); | 810 | |
799 | return 0; | 811 | error: |
812 | abort_creds(new); | ||
813 | return retval; | ||
800 | } | 814 | } |
801 | 815 | ||
802 | asmlinkage long sys_getresgid(gid_t __user *rgid, gid_t __user *egid, gid_t __user *sgid) | 816 | asmlinkage long sys_getresgid(gid_t __user *rgid, gid_t __user *egid, gid_t __user *sgid) |
@@ -820,28 +834,35 @@ asmlinkage long sys_getresgid(gid_t __user *rgid, gid_t __user *egid, gid_t __us | |||
820 | */ | 834 | */ |
821 | asmlinkage long sys_setfsuid(uid_t uid) | 835 | asmlinkage long sys_setfsuid(uid_t uid) |
822 | { | 836 | { |
823 | struct cred *cred = current->cred; | 837 | const struct cred *old; |
824 | int old_fsuid; | 838 | struct cred *new; |
839 | uid_t old_fsuid; | ||
840 | |||
841 | new = prepare_creds(); | ||
842 | if (!new) | ||
843 | return current_fsuid(); | ||
844 | old = current_cred(); | ||
845 | old_fsuid = old->fsuid; | ||
825 | 846 | ||
826 | old_fsuid = cred->fsuid; | 847 | if (security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS) < 0) |
827 | if (security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS)) | 848 | goto error; |
828 | return old_fsuid; | ||
829 | 849 | ||
830 | if (uid == cred->uid || uid == cred->euid || | 850 | if (uid == old->uid || uid == old->euid || |
831 | uid == cred->suid || uid == cred->fsuid || | 851 | uid == old->suid || uid == old->fsuid || |
832 | capable(CAP_SETUID)) { | 852 | capable(CAP_SETUID)) { |
833 | if (uid != old_fsuid) { | 853 | if (uid != old_fsuid) { |
834 | set_dumpable(current->mm, suid_dumpable); | 854 | new->fsuid = uid; |
835 | smp_wmb(); | 855 | if (security_task_fix_setuid(new, old, LSM_SETID_FS) == 0) |
856 | goto change_okay; | ||
836 | } | 857 | } |
837 | cred->fsuid = uid; | ||
838 | } | 858 | } |
839 | 859 | ||
840 | key_fsuid_changed(current); | 860 | error: |
841 | proc_id_connector(current, PROC_EVENT_UID); | 861 | abort_creds(new); |
842 | 862 | return old_fsuid; | |
843 | security_task_post_setuid(old_fsuid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS); | ||
844 | 863 | ||
864 | change_okay: | ||
865 | commit_creds(new); | ||
845 | return old_fsuid; | 866 | return old_fsuid; |
846 | } | 867 | } |
847 | 868 | ||
@@ -850,24 +871,34 @@ asmlinkage long sys_setfsuid(uid_t uid) | |||
850 | */ | 871 | */ |
851 | asmlinkage long sys_setfsgid(gid_t gid) | 872 | asmlinkage long sys_setfsgid(gid_t gid) |
852 | { | 873 | { |
853 | struct cred *cred = current->cred; | 874 | const struct cred *old; |
854 | int old_fsgid; | 875 | struct cred *new; |
876 | gid_t old_fsgid; | ||
877 | |||
878 | new = prepare_creds(); | ||
879 | if (!new) | ||
880 | return current_fsgid(); | ||
881 | old = current_cred(); | ||
882 | old_fsgid = old->fsgid; | ||
855 | 883 | ||
856 | old_fsgid = cred->fsgid; | ||
857 | if (security_task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_FS)) | 884 | if (security_task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_FS)) |
858 | return old_fsgid; | 885 | goto error; |
859 | 886 | ||
860 | if (gid == cred->gid || gid == cred->egid || | 887 | if (gid == old->gid || gid == old->egid || |
861 | gid == cred->sgid || gid == cred->fsgid || | 888 | gid == old->sgid || gid == old->fsgid || |
862 | capable(CAP_SETGID)) { | 889 | capable(CAP_SETGID)) { |
863 | if (gid != old_fsgid) { | 890 | if (gid != old_fsgid) { |
864 | set_dumpable(current->mm, suid_dumpable); | 891 | new->fsgid = gid; |
865 | smp_wmb(); | 892 | goto change_okay; |
866 | } | 893 | } |
867 | cred->fsgid = gid; | ||
868 | key_fsgid_changed(current); | ||
869 | proc_id_connector(current, PROC_EVENT_GID); | ||
870 | } | 894 | } |
895 | |||
896 | error: | ||
897 | abort_creds(new); | ||
898 | return old_fsgid; | ||
899 | |||
900 | change_okay: | ||
901 | commit_creds(new); | ||
871 | return old_fsgid; | 902 | return old_fsgid; |
872 | } | 903 | } |
873 | 904 | ||
@@ -1136,7 +1167,7 @@ EXPORT_SYMBOL(groups_free); | |||
1136 | 1167 | ||
1137 | /* export the group_info to a user-space array */ | 1168 | /* export the group_info to a user-space array */ |
1138 | static int groups_to_user(gid_t __user *grouplist, | 1169 | static int groups_to_user(gid_t __user *grouplist, |
1139 | struct group_info *group_info) | 1170 | const struct group_info *group_info) |
1140 | { | 1171 | { |
1141 | int i; | 1172 | int i; |
1142 | unsigned int count = group_info->ngroups; | 1173 | unsigned int count = group_info->ngroups; |
@@ -1227,31 +1258,25 @@ int groups_search(const struct group_info *group_info, gid_t grp) | |||
1227 | } | 1258 | } |
1228 | 1259 | ||
1229 | /** | 1260 | /** |
1230 | * set_groups - Change a group subscription in a security record | 1261 | * set_groups - Change a group subscription in a set of credentials |
1231 | * @sec: The security record to alter | 1262 | * @new: The newly prepared set of credentials to alter |
1232 | * @group_info: The group list to impose | 1263 | * @group_info: The group list to install |
1233 | * | 1264 | * |
1234 | * Validate a group subscription and, if valid, impose it upon a task security | 1265 | * Validate a group subscription and, if valid, insert it into a set |
1235 | * record. | 1266 | * of credentials. |
1236 | */ | 1267 | */ |
1237 | int set_groups(struct cred *cred, struct group_info *group_info) | 1268 | int set_groups(struct cred *new, struct group_info *group_info) |
1238 | { | 1269 | { |
1239 | int retval; | 1270 | int retval; |
1240 | struct group_info *old_info; | ||
1241 | 1271 | ||
1242 | retval = security_task_setgroups(group_info); | 1272 | retval = security_task_setgroups(group_info); |
1243 | if (retval) | 1273 | if (retval) |
1244 | return retval; | 1274 | return retval; |
1245 | 1275 | ||
1276 | put_group_info(new->group_info); | ||
1246 | groups_sort(group_info); | 1277 | groups_sort(group_info); |
1247 | get_group_info(group_info); | 1278 | get_group_info(group_info); |
1248 | 1279 | new->group_info = group_info; | |
1249 | spin_lock(&cred->lock); | ||
1250 | old_info = cred->group_info; | ||
1251 | cred->group_info = group_info; | ||
1252 | spin_unlock(&cred->lock); | ||
1253 | |||
1254 | put_group_info(old_info); | ||
1255 | return 0; | 1280 | return 0; |
1256 | } | 1281 | } |
1257 | 1282 | ||
@@ -1266,7 +1291,20 @@ EXPORT_SYMBOL(set_groups); | |||
1266 | */ | 1291 | */ |
1267 | int set_current_groups(struct group_info *group_info) | 1292 | int set_current_groups(struct group_info *group_info) |
1268 | { | 1293 | { |
1269 | return set_groups(current->cred, group_info); | 1294 | struct cred *new; |
1295 | int ret; | ||
1296 | |||
1297 | new = prepare_creds(); | ||
1298 | if (!new) | ||
1299 | return -ENOMEM; | ||
1300 | |||
1301 | ret = set_groups(new, group_info); | ||
1302 | if (ret < 0) { | ||
1303 | abort_creds(new); | ||
1304 | return ret; | ||
1305 | } | ||
1306 | |||
1307 | return commit_creds(new); | ||
1270 | } | 1308 | } |
1271 | 1309 | ||
1272 | EXPORT_SYMBOL(set_current_groups); | 1310 | EXPORT_SYMBOL(set_current_groups); |
@@ -1666,9 +1704,11 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3, | |||
1666 | unsigned char comm[sizeof(me->comm)]; | 1704 | unsigned char comm[sizeof(me->comm)]; |
1667 | long error; | 1705 | long error; |
1668 | 1706 | ||
1669 | if (security_task_prctl(option, arg2, arg3, arg4, arg5, &error)) | 1707 | error = security_task_prctl(option, arg2, arg3, arg4, arg5); |
1708 | if (error != -ENOSYS) | ||
1670 | return error; | 1709 | return error; |
1671 | 1710 | ||
1711 | error = 0; | ||
1672 | switch (option) { | 1712 | switch (option) { |
1673 | case PR_SET_PDEATHSIG: | 1713 | case PR_SET_PDEATHSIG: |
1674 | if (!valid_signal(arg2)) { | 1714 | if (!valid_signal(arg2)) { |
diff --git a/kernel/user.c b/kernel/user.c index 104d22ac84d5..d476307dd4b0 100644 --- a/kernel/user.c +++ b/kernel/user.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/interrupt.h> | 16 | #include <linux/interrupt.h> |
17 | #include <linux/module.h> | 17 | #include <linux/module.h> |
18 | #include <linux/user_namespace.h> | 18 | #include <linux/user_namespace.h> |
19 | #include "cred-internals.h" | ||
19 | 20 | ||
20 | struct user_namespace init_user_ns = { | 21 | struct user_namespace init_user_ns = { |
21 | .kref = { | 22 | .kref = { |
@@ -104,16 +105,10 @@ static int sched_create_user(struct user_struct *up) | |||
104 | return rc; | 105 | return rc; |
105 | } | 106 | } |
106 | 107 | ||
107 | static void sched_switch_user(struct task_struct *p) | ||
108 | { | ||
109 | sched_move_task(p); | ||
110 | } | ||
111 | |||
112 | #else /* CONFIG_USER_SCHED */ | 108 | #else /* CONFIG_USER_SCHED */ |
113 | 109 | ||
114 | static void sched_destroy_user(struct user_struct *up) { } | 110 | static void sched_destroy_user(struct user_struct *up) { } |
115 | static int sched_create_user(struct user_struct *up) { return 0; } | 111 | static int sched_create_user(struct user_struct *up) { return 0; } |
116 | static void sched_switch_user(struct task_struct *p) { } | ||
117 | 112 | ||
118 | #endif /* CONFIG_USER_SCHED */ | 113 | #endif /* CONFIG_USER_SCHED */ |
119 | 114 | ||
@@ -448,36 +443,6 @@ out_unlock: | |||
448 | return NULL; | 443 | return NULL; |
449 | } | 444 | } |
450 | 445 | ||
451 | void switch_uid(struct user_struct *new_user) | ||
452 | { | ||
453 | struct user_struct *old_user; | ||
454 | |||
455 | /* What if a process setreuid()'s and this brings the | ||
456 | * new uid over his NPROC rlimit? We can check this now | ||
457 | * cheaply with the new uid cache, so if it matters | ||
458 | * we should be checking for it. -DaveM | ||
459 | */ | ||
460 | old_user = current->cred->user; | ||
461 | atomic_inc(&new_user->processes); | ||
462 | atomic_dec(&old_user->processes); | ||
463 | switch_uid_keyring(new_user); | ||
464 | current->cred->user = new_user; | ||
465 | sched_switch_user(current); | ||
466 | |||
467 | /* | ||
468 | * We need to synchronize with __sigqueue_alloc() | ||
469 | * doing a get_uid(p->user).. If that saw the old | ||
470 | * user value, we need to wait until it has exited | ||
471 | * its critical region before we can free the old | ||
472 | * structure. | ||
473 | */ | ||
474 | smp_mb(); | ||
475 | spin_unlock_wait(¤t->sighand->siglock); | ||
476 | |||
477 | free_uid(old_user); | ||
478 | suid_keys(current); | ||
479 | } | ||
480 | |||
481 | #ifdef CONFIG_USER_NS | 446 | #ifdef CONFIG_USER_NS |
482 | void release_uids(struct user_namespace *ns) | 447 | void release_uids(struct user_namespace *ns) |
483 | { | 448 | { |
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index f82730adea00..0d9c51d67333 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c | |||
@@ -19,6 +19,7 @@ static struct user_namespace *clone_user_ns(struct user_namespace *old_ns) | |||
19 | { | 19 | { |
20 | struct user_namespace *ns; | 20 | struct user_namespace *ns; |
21 | struct user_struct *new_user; | 21 | struct user_struct *new_user; |
22 | struct cred *new; | ||
22 | int n; | 23 | int n; |
23 | 24 | ||
24 | ns = kmalloc(sizeof(struct user_namespace), GFP_KERNEL); | 25 | ns = kmalloc(sizeof(struct user_namespace), GFP_KERNEL); |
@@ -45,7 +46,16 @@ static struct user_namespace *clone_user_ns(struct user_namespace *old_ns) | |||
45 | return ERR_PTR(-ENOMEM); | 46 | return ERR_PTR(-ENOMEM); |
46 | } | 47 | } |
47 | 48 | ||
48 | switch_uid(new_user); | 49 | /* Install the new user */ |
50 | new = prepare_creds(); | ||
51 | if (!new) { | ||
52 | free_uid(new_user); | ||
53 | free_uid(ns->root_user); | ||
54 | kfree(ns); | ||
55 | } | ||
56 | free_uid(new->user); | ||
57 | new->user = new_user; | ||
58 | commit_creds(new); | ||
49 | return ns; | 59 | return ns; |
50 | } | 60 | } |
51 | 61 | ||