Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

security: filesystem capabilities: fix fragile setuid fixup code

This commit includes a bugfix for the fragile setuid fixup code in the
case that filesystem capabilities are supported (in access()). The effect
of this fix is gated on filesystem capability support because changing
securebits is only supported when filesystem capabilities support is
configured.)

[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
Acked-by: Serge Hallyn <serue@us.ibm.com>
Acked-by: David Howells <dhowells@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Andrew G. Morgan and committed by
Linus Torvalds
086f7316 abbaeff3

+53 -22
+22 -15
fs/open.c
··· 16 16 #include <linux/namei.h> 17 17 #include <linux/backing-dev.h> 18 18 #include <linux/capability.h> 19 + #include <linux/securebits.h> 19 20 #include <linux/security.h> 20 21 #include <linux/mount.h> 21 22 #include <linux/vfs.h> ··· 426 425 { 427 426 struct nameidata nd; 428 427 int old_fsuid, old_fsgid; 429 - kernel_cap_t old_cap; 428 + kernel_cap_t uninitialized_var(old_cap); /* !SECURE_NO_SETUID_FIXUP */ 430 429 int res; 431 430 432 431 if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */ ··· 434 433 435 434 old_fsuid = current->fsuid; 436 435 old_fsgid = current->fsgid; 437 - old_cap = current->cap_effective; 438 436 439 437 current->fsuid = current->uid; 440 438 current->fsgid = current->gid; 441 439 442 - /* 443 - * Clear the capabilities if we switch to a non-root user 444 - * 445 - * FIXME: There is a race here against sys_capset. The 446 - * capabilities can change yet we will restore the old 447 - * value below. We should hold task_capabilities_lock, 448 - * but we cannot because user_path_walk can sleep. 449 - */ 450 - if (current->uid) 451 - cap_clear(current->cap_effective); 452 - else 453 - current->cap_effective = current->cap_permitted; 440 + if (!issecure(SECURE_NO_SETUID_FIXUP)) { 441 + /* 442 + * Clear the capabilities if we switch to a non-root user 443 + */ 444 + #ifndef CONFIG_SECURITY_FILE_CAPABILITIES 445 + /* 446 + * FIXME: There is a race here against sys_capset. The 447 + * capabilities can change yet we will restore the old 448 + * value below. We should hold task_capabilities_lock, 449 + * but we cannot because user_path_walk can sleep. 450 + */ 451 + #endif /* ndef CONFIG_SECURITY_FILE_CAPABILITIES */ 452 + if (current->uid) 453 + old_cap = cap_set_effective(__cap_empty_set); 454 + else 455 + old_cap = cap_set_effective(current->cap_permitted); 456 + } 454 457 455 458 res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd); 456 459 if (res) ··· 483 478 out: 484 479 current->fsuid = old_fsuid; 485 480 current->fsgid = old_fsgid; 486 - current->cap_effective = old_cap; 481 + 482 + if (!issecure(SECURE_NO_SETUID_FIXUP)) 483 + cap_set_effective(old_cap); 487 484 488 485 return res; 489 486 }
+2
include/linux/capability.h
··· 501 501 extern const kernel_cap_t __cap_full_set; 502 502 extern const kernel_cap_t __cap_init_eff_set; 503 503 504 + kernel_cap_t cap_set_effective(const kernel_cap_t pE_new); 505 + 504 506 int capable(int cap); 505 507 int __capable(struct task_struct *t, int cap); 506 508
+8 -7
include/linux/securebits.h
··· 7 7 inheritance of root-permissions and suid-root executable under 8 8 compatibility mode. We raise the effective and inheritable bitmasks 9 9 *of the executable file* if the effective uid of the new process is 10 - 0. If the real uid is 0, we raise the inheritable bitmask of the 10 + 0. If the real uid is 0, we raise the effective (legacy) bit of the 11 11 executable file. */ 12 12 #define SECURE_NOROOT 0 13 13 #define SECURE_NOROOT_LOCKED 1 /* make bit-0 immutable */ 14 14 15 - /* When set, setuid to/from uid 0 does not trigger capability-"fixes" 16 - to be compatible with old programs relying on set*uid to loose 17 - privileges. When unset, setuid doesn't change privileges. */ 15 + /* When set, setuid to/from uid 0 does not trigger capability-"fixup". 16 + When unset, to provide compatiblility with old programs relying on 17 + set*uid to gain/lose privilege, transitions to/from uid 0 cause 18 + capabilities to be gained/lost. */ 18 19 #define SECURE_NO_SETUID_FIXUP 2 19 20 #define SECURE_NO_SETUID_FIXUP_LOCKED 3 /* make bit-2 immutable */ 20 21 ··· 27 26 #define SECURE_KEEP_CAPS 4 28 27 #define SECURE_KEEP_CAPS_LOCKED 5 /* make bit-4 immutable */ 29 28 30 - /* Each securesetting is implemented using two bits. One bit specify 29 + /* Each securesetting is implemented using two bits. One bit specifies 31 30 whether the setting is on or off. The other bit specify whether the 32 - setting is fixed or not. A setting which is fixed cannot be changed 33 - from user-level. */ 31 + setting is locked or not. A setting which is locked cannot be 32 + changed from user-level. */ 34 33 #define issecure_mask(X) (1 << (X)) 35 34 #define issecure(X) (issecure_mask(X) & current->securebits) 36 35
+21
kernel/capability.c
··· 121 121 * uninteresting and/or not to be changed. 122 122 */ 123 123 124 + /* 125 + * Atomically modify the effective capabilities returning the original 126 + * value. No permission check is performed here - it is assumed that the 127 + * caller is permitted to set the desired effective capabilities. 128 + */ 129 + kernel_cap_t cap_set_effective(const kernel_cap_t pE_new) 130 + { 131 + kernel_cap_t pE_old; 132 + 133 + spin_lock(&task_capability_lock); 134 + 135 + pE_old = current->cap_effective; 136 + current->cap_effective = pE_new; 137 + 138 + spin_unlock(&task_capability_lock); 139 + 140 + return pE_old; 141 + } 142 + 143 + EXPORT_SYMBOL(cap_set_effective); 144 + 124 145 /** 125 146 * sys_capget - get the capabilities of a given process. 126 147 * @header: pointer to struct that contains capability version and