aboutsummaryrefslogtreecommitdiffstats
path: root/security/commoncap.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2008-11-13 18:39:24 -0500
committerJames Morris <jmorris@namei.org>2008-11-13 18:39:24 -0500
commita6f76f23d297f70e2a6b3ec607f7aeeea9e37e8d (patch)
tree8f95617996d0974507f176163459212a7def8b9a /security/commoncap.c
parentd84f4f992cbd76e8f39c488cf0c5d123843923b1 (diff)
CRED: Make execve() take advantage of copy-on-write credentials
Make execve() take advantage of copy-on-write credentials, allowing it to set up the credentials in advance, and then commit the whole lot after the point of no return. This patch and the preceding patches have been tested with the LTP SELinux testsuite. This patch makes several logical sets of alteration: (1) execve(). The credential bits from struct linux_binprm are, for the most part, replaced with a single credentials pointer (bprm->cred). This means that all the creds can be calculated in advance and then applied at the point of no return with no possibility of failure. I would like to replace bprm->cap_effective with: cap_isclear(bprm->cap_effective) but this seems impossible due to special behaviour for processes of pid 1 (they always retain their parent's capability masks where normally they'd be changed - see cap_bprm_set_creds()). The following sequence of events now happens: (a) At the start of do_execve, the current task's cred_exec_mutex is locked to prevent PTRACE_ATTACH from obsoleting the calculation of creds that we make. (a) prepare_exec_creds() is then called to make a copy of the current task's credentials and prepare it. This copy is then assigned to bprm->cred. This renders security_bprm_alloc() and security_bprm_free() unnecessary, and so they've been removed. (b) The determination of unsafe execution is now performed immediately after (a) rather than later on in the code. The result is stored in bprm->unsafe for future reference. (c) prepare_binprm() is called, possibly multiple times. (i) This applies the result of set[ug]id binaries to the new creds attached to bprm->cred. Personality bit clearance is recorded, but now deferred on the basis that the exec procedure may yet fail. (ii) This then calls the new security_bprm_set_creds(). This should calculate the new LSM and capability credentials into *bprm->cred. This folds together security_bprm_set() and parts of security_bprm_apply_creds() (these two have been removed). Anything that might fail must be done at this point. (iii) bprm->cred_prepared is set to 1. bprm->cred_prepared is 0 on the first pass of the security calculations, and 1 on all subsequent passes. This allows SELinux in (ii) to base its calculations only on the initial script and not on the interpreter. (d) flush_old_exec() is called to commit the task to execution. This performs the following steps with regard to credentials: (i) Clear pdeath_signal and set dumpable on certain circumstances that may not be covered by commit_creds(). (ii) Clear any bits in current->personality that were deferred from (c.i). (e) install_exec_creds() [compute_creds() as was] is called to install the new credentials. This performs the following steps with regard to credentials: (i) Calls security_bprm_committing_creds() to apply any security requirements, such as flushing unauthorised files in SELinux, that must be done before the credentials are changed. This is made up of bits of security_bprm_apply_creds() and security_bprm_post_apply_creds(), both of which have been removed. This function is not allowed to fail; anything that might fail must have been done in (c.ii). (ii) Calls commit_creds() to apply the new credentials in a single assignment (more or less). Possibly pdeath_signal and dumpable should be part of struct creds. (iii) Unlocks the task's cred_replace_mutex, thus allowing PTRACE_ATTACH to take place. (iv) Clears The bprm->cred pointer as the credentials it was holding are now immutable. (v) Calls security_bprm_committed_creds() to apply any security alterations that must be done after the creds have been changed. SELinux uses this to flush signals and signal handlers. (f) If an error occurs before (d.i), bprm_free() will call abort_creds() to destroy the proposed new credentials and will then unlock cred_replace_mutex. No changes to the credentials will have been made. (2) LSM interface. A number of functions have been changed, added or removed: (*) security_bprm_alloc(), ->bprm_alloc_security() (*) security_bprm_free(), ->bprm_free_security() Removed in favour of preparing new credentials and modifying those. (*) security_bprm_apply_creds(), ->bprm_apply_creds() (*) security_bprm_post_apply_creds(), ->bprm_post_apply_creds() Removed; split between security_bprm_set_creds(), security_bprm_committing_creds() and security_bprm_committed_creds(). (*) security_bprm_set(), ->bprm_set_security() Removed; folded into security_bprm_set_creds(). (*) security_bprm_set_creds(), ->bprm_set_creds() New. The new credentials in bprm->creds should be checked and set up as appropriate. bprm->cred_prepared is 0 on the first call, 1 on the second and subsequent calls. (*) security_bprm_committing_creds(), ->bprm_committing_creds() (*) security_bprm_committed_creds(), ->bprm_committed_creds() New. Apply the security effects of the new credentials. This includes closing unauthorised files in SELinux. This function may not fail. When the former is called, the creds haven't yet been applied to the process; when the latter is called, they have. The former may access bprm->cred, the latter may not. (3) SELinux. SELinux has a number of changes, in addition to those to support the LSM interface changes mentioned above: (a) The bprm_security_struct struct has been removed in favour of using the credentials-under-construction approach. (c) flush_unauthorized_files() now takes a cred pointer and passes it on to inode_has_perm(), file_has_perm() and dentry_open(). Signed-off-by: David Howells <dhowells@redhat.com> Acked-by: James Morris <jmorris@namei.org> Acked-by: Serge Hallyn <serue@us.ibm.com> Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/commoncap.c')
-rw-r--r--security/commoncap.c152
1 files changed, 76 insertions, 76 deletions
diff --git a/security/commoncap.c b/security/commoncap.c
index b5419273f92d..51dfa11e8e56 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -167,7 +167,7 @@ int cap_capset(struct cred *new,
167 167
168static inline void bprm_clear_caps(struct linux_binprm *bprm) 168static inline void bprm_clear_caps(struct linux_binprm *bprm)
169{ 169{
170 cap_clear(bprm->cap_post_exec_permitted); 170 cap_clear(bprm->cred->cap_permitted);
171 bprm->cap_effective = false; 171 bprm->cap_effective = false;
172} 172}
173 173
@@ -198,15 +198,15 @@ int cap_inode_killpriv(struct dentry *dentry)
198} 198}
199 199
200static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps, 200static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,
201 struct linux_binprm *bprm) 201 struct linux_binprm *bprm,
202 bool *effective)
202{ 203{
204 struct cred *new = bprm->cred;
203 unsigned i; 205 unsigned i;
204 int ret = 0; 206 int ret = 0;
205 207
206 if (caps->magic_etc & VFS_CAP_FLAGS_EFFECTIVE) 208 if (caps->magic_etc & VFS_CAP_FLAGS_EFFECTIVE)
207 bprm->cap_effective = true; 209 *effective = true;
208 else
209 bprm->cap_effective = false;
210 210
211 CAP_FOR_EACH_U32(i) { 211 CAP_FOR_EACH_U32(i) {
212 __u32 permitted = caps->permitted.cap[i]; 212 __u32 permitted = caps->permitted.cap[i];
@@ -215,16 +215,13 @@ static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,
215 /* 215 /*
216 * pP' = (X & fP) | (pI & fI) 216 * pP' = (X & fP) | (pI & fI)
217 */ 217 */
218 bprm->cap_post_exec_permitted.cap[i] = 218 new->cap_permitted.cap[i] =
219 (current->cred->cap_bset.cap[i] & permitted) | 219 (new->cap_bset.cap[i] & permitted) |
220 (current->cred->cap_inheritable.cap[i] & inheritable); 220 (new->cap_inheritable.cap[i] & inheritable);
221 221
222 if (permitted & ~bprm->cap_post_exec_permitted.cap[i]) { 222 if (permitted & ~new->cap_permitted.cap[i])
223 /* 223 /* insufficient to execute correctly */
224 * insufficient to execute correctly
225 */
226 ret = -EPERM; 224 ret = -EPERM;
227 }
228 } 225 }
229 226
230 /* 227 /*
@@ -232,7 +229,7 @@ static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,
232 * do not have enough capabilities, we return an error if they are 229 * do not have enough capabilities, we return an error if they are
233 * missing some "forced" (aka file-permitted) capabilities. 230 * missing some "forced" (aka file-permitted) capabilities.
234 */ 231 */
235 return bprm->cap_effective ? ret : 0; 232 return *effective ? ret : 0;
236} 233}
237 234
238int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps) 235int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps)
@@ -250,10 +247,9 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data
250 247
251 size = inode->i_op->getxattr((struct dentry *)dentry, XATTR_NAME_CAPS, &caps, 248 size = inode->i_op->getxattr((struct dentry *)dentry, XATTR_NAME_CAPS, &caps,
252 XATTR_CAPS_SZ); 249 XATTR_CAPS_SZ);
253 if (size == -ENODATA || size == -EOPNOTSUPP) { 250 if (size == -ENODATA || size == -EOPNOTSUPP)
254 /* no data, that's ok */ 251 /* no data, that's ok */
255 return -ENODATA; 252 return -ENODATA;
256 }
257 if (size < 0) 253 if (size < 0)
258 return size; 254 return size;
259 255
@@ -262,7 +258,7 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data
262 258
263 cpu_caps->magic_etc = magic_etc = le32_to_cpu(caps.magic_etc); 259 cpu_caps->magic_etc = magic_etc = le32_to_cpu(caps.magic_etc);
264 260
265 switch ((magic_etc & VFS_CAP_REVISION_MASK)) { 261 switch (magic_etc & VFS_CAP_REVISION_MASK) {
266 case VFS_CAP_REVISION_1: 262 case VFS_CAP_REVISION_1:
267 if (size != XATTR_CAPS_SZ_1) 263 if (size != XATTR_CAPS_SZ_1)
268 return -EINVAL; 264 return -EINVAL;
@@ -283,11 +279,12 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data
283 cpu_caps->permitted.cap[i] = le32_to_cpu(caps.data[i].permitted); 279 cpu_caps->permitted.cap[i] = le32_to_cpu(caps.data[i].permitted);
284 cpu_caps->inheritable.cap[i] = le32_to_cpu(caps.data[i].inheritable); 280 cpu_caps->inheritable.cap[i] = le32_to_cpu(caps.data[i].inheritable);
285 } 281 }
282
286 return 0; 283 return 0;
287} 284}
288 285
289/* Locate any VFS capabilities: */ 286/* Locate any VFS capabilities: */
290static int get_file_caps(struct linux_binprm *bprm) 287static int get_file_caps(struct linux_binprm *bprm, bool *effective)
291{ 288{
292 struct dentry *dentry; 289 struct dentry *dentry;
293 int rc = 0; 290 int rc = 0;
@@ -313,7 +310,10 @@ static int get_file_caps(struct linux_binprm *bprm)
313 goto out; 310 goto out;
314 } 311 }
315 312
316 rc = bprm_caps_from_vfs_caps(&vcaps, bprm); 313 rc = bprm_caps_from_vfs_caps(&vcaps, bprm, effective);
314 if (rc == -EINVAL)
315 printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n",
316 __func__, rc, bprm->filename);
317 317
318out: 318out:
319 dput(dentry); 319 dput(dentry);
@@ -334,18 +334,27 @@ int cap_inode_killpriv(struct dentry *dentry)
334 return 0; 334 return 0;
335} 335}
336 336
337static inline int get_file_caps(struct linux_binprm *bprm) 337static inline int get_file_caps(struct linux_binprm *bprm, bool *effective)
338{ 338{
339 bprm_clear_caps(bprm); 339 bprm_clear_caps(bprm);
340 return 0; 340 return 0;
341} 341}
342#endif 342#endif
343 343
344int cap_bprm_set_security (struct linux_binprm *bprm) 344/*
345 * set up the new credentials for an exec'd task
346 */
347int cap_bprm_set_creds(struct linux_binprm *bprm)
345{ 348{
349 const struct cred *old = current_cred();
350 struct cred *new = bprm->cred;
351 bool effective;
346 int ret; 352 int ret;
347 353
348 ret = get_file_caps(bprm); 354 effective = false;
355 ret = get_file_caps(bprm, &effective);
356 if (ret < 0)
357 return ret;
349 358
350 if (!issecure(SECURE_NOROOT)) { 359 if (!issecure(SECURE_NOROOT)) {
351 /* 360 /*
@@ -353,63 +362,47 @@ int cap_bprm_set_security (struct linux_binprm *bprm)
353 * executables under compatibility mode, we override the 362 * executables under compatibility mode, we override the
354 * capability sets for the file. 363 * capability sets for the file.
355 * 364 *
356 * If only the real uid is 0, we do not set the effective 365 * If only the real uid is 0, we do not set the effective bit.
357 * bit.
358 */ 366 */
359 if (bprm->e_uid == 0 || current_uid() == 0) { 367 if (new->euid == 0 || new->uid == 0) {
360 /* pP' = (cap_bset & ~0) | (pI & ~0) */ 368 /* pP' = (cap_bset & ~0) | (pI & ~0) */
361 bprm->cap_post_exec_permitted = cap_combine( 369 new->cap_permitted = cap_combine(old->cap_bset,
362 current->cred->cap_bset, 370 old->cap_inheritable);
363 current->cred->cap_inheritable);
364 bprm->cap_effective = (bprm->e_uid == 0);
365 ret = 0;
366 } 371 }
372 if (new->euid == 0)
373 effective = true;
367 } 374 }
368 375
369 return ret; 376 /* Don't let someone trace a set[ug]id/setpcap binary with the revised
370} 377 * credentials unless they have the appropriate permit
371 378 */
372int cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) 379 if ((new->euid != old->uid ||
373{ 380 new->egid != old->gid ||
374 const struct cred *old = current_cred(); 381 !cap_issubset(new->cap_permitted, old->cap_permitted)) &&
375 struct cred *new; 382 bprm->unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
376 383 /* downgrade; they get no more than they had, and maybe less */
377 new = prepare_creds(); 384 if (!capable(CAP_SETUID)) {
378 if (!new) 385 new->euid = new->uid;
379 return -ENOMEM; 386 new->egid = new->gid;
380
381 if (bprm->e_uid != old->uid || bprm->e_gid != old->gid ||
382 !cap_issubset(bprm->cap_post_exec_permitted,
383 old->cap_permitted)) {
384 set_dumpable(current->mm, suid_dumpable);
385 current->pdeath_signal = 0;
386
387 if (unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
388 if (!capable(CAP_SETUID)) {
389 bprm->e_uid = old->uid;
390 bprm->e_gid = old->gid;
391 }
392 if (cap_limit_ptraced_target()) {
393 bprm->cap_post_exec_permitted = cap_intersect(
394 bprm->cap_post_exec_permitted,
395 new->cap_permitted);
396 }
397 } 387 }
388 if (cap_limit_ptraced_target())
389 new->cap_permitted = cap_intersect(new->cap_permitted,
390 old->cap_permitted);
398 } 391 }
399 392
400 new->suid = new->euid = new->fsuid = bprm->e_uid; 393 new->suid = new->fsuid = new->euid;
401 new->sgid = new->egid = new->fsgid = bprm->e_gid; 394 new->sgid = new->fsgid = new->egid;
402 395
403 /* For init, we want to retain the capabilities set 396 /* For init, we want to retain the capabilities set in the initial
404 * in the init_task struct. Thus we skip the usual 397 * task. Thus we skip the usual capability rules
405 * capability rules */ 398 */
406 if (!is_global_init(current)) { 399 if (!is_global_init(current)) {
407 new->cap_permitted = bprm->cap_post_exec_permitted; 400 if (effective)
408 if (bprm->cap_effective) 401 new->cap_effective = new->cap_permitted;
409 new->cap_effective = bprm->cap_post_exec_permitted;
410 else 402 else
411 cap_clear(new->cap_effective); 403 cap_clear(new->cap_effective);
412 } 404 }
405 bprm->cap_effective = effective;
413 406
414 /* 407 /*
415 * Audit candidate if current->cap_effective is set 408 * Audit candidate if current->cap_effective is set
@@ -425,23 +418,31 @@ int cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
425 */ 418 */
426 if (!cap_isclear(new->cap_effective)) { 419 if (!cap_isclear(new->cap_effective)) {
427 if (!cap_issubset(CAP_FULL_SET, new->cap_effective) || 420 if (!cap_issubset(CAP_FULL_SET, new->cap_effective) ||
428 bprm->e_uid != 0 || new->uid != 0 || 421 new->euid != 0 || new->uid != 0 ||
429 issecure(SECURE_NOROOT)) 422 issecure(SECURE_NOROOT)) {
430 audit_log_bprm_fcaps(bprm, new, old); 423 ret = audit_log_bprm_fcaps(bprm, new, old);
424 if (ret < 0)
425 return ret;
426 }
431 } 427 }
432 428
433 new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS); 429 new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
434 return commit_creds(new); 430 return 0;
435} 431}
436 432
437int cap_bprm_secureexec (struct linux_binprm *bprm) 433/*
434 * determine whether a secure execution is required
435 * - the creds have been committed at this point, and are no longer available
436 * through bprm
437 */
438int cap_bprm_secureexec(struct linux_binprm *bprm)
438{ 439{
439 const struct cred *cred = current_cred(); 440 const struct cred *cred = current_cred();
440 441
441 if (cred->uid != 0) { 442 if (cred->uid != 0) {
442 if (bprm->cap_effective) 443 if (bprm->cap_effective)
443 return 1; 444 return 1;
444 if (!cap_isclear(bprm->cap_post_exec_permitted)) 445 if (!cap_isclear(cred->cap_permitted))
445 return 1; 446 return 1;
446 } 447 }
447 448
@@ -477,7 +478,7 @@ int cap_inode_removexattr(struct dentry *dentry, const char *name)
477} 478}
478 479
479/* moved from kernel/sys.c. */ 480/* moved from kernel/sys.c. */
480/* 481/*
481 * cap_emulate_setxuid() fixes the effective / permitted capabilities of 482 * cap_emulate_setxuid() fixes the effective / permitted capabilities of
482 * a process after a call to setuid, setreuid, or setresuid. 483 * a process after a call to setuid, setreuid, or setresuid.
483 * 484 *
@@ -491,10 +492,10 @@ int cap_inode_removexattr(struct dentry *dentry, const char *name)
491 * 3) When set*uiding _from_ euid != 0 _to_ euid == 0, the effective 492 * 3) When set*uiding _from_ euid != 0 _to_ euid == 0, the effective
492 * capabilities are set to the permitted capabilities. 493 * capabilities are set to the permitted capabilities.
493 * 494 *
494 * fsuid is handled elsewhere. fsuid == 0 and {r,e,s}uid!= 0 should 495 * fsuid is handled elsewhere. fsuid == 0 and {r,e,s}uid!= 0 should
495 * never happen. 496 * never happen.
496 * 497 *
497 * -astor 498 * -astor
498 * 499 *
499 * cevans - New behaviour, Oct '99 500 * cevans - New behaviour, Oct '99
500 * A process may, via prctl(), elect to keep its capabilities when it 501 * A process may, via prctl(), elect to keep its capabilities when it
@@ -751,4 +752,3 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages)
751 cap_sys_admin = 1; 752 cap_sys_admin = 1;
752 return __vm_enough_memory(mm, pages, cap_sys_admin); 753 return __vm_enough_memory(mm, pages, cap_sys_admin);
753} 754}
754