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

LSM: add SafeSetID module that gates setid calls

This change ensures that the set*uid family of syscalls in kernel/sys.c
(setreuid, setuid, setresuid, setfsuid) all call ns_capable_common with
the CAP_OPT_INSETID flag, so capability checks in the security_capable
hook can know whether they are being called from within a set*uid
syscall. This change is a no-op by itself, but is needed for the
proposed SafeSetID LSM.

Signed-off-by: Micah Morton <mortonm@chromium.org>
Acked-by: Kees Cook <keescook@chromium.org>
Signed-off-by: James Morris <james.morris@microsoft.com>

authored by

Micah Morton and committed by
James Morris
40852275 4b425641

+29 -5
+5
include/linux/capability.h
··· 209 209 extern bool capable(int cap); 210 210 extern bool ns_capable(struct user_namespace *ns, int cap); 211 211 extern bool ns_capable_noaudit(struct user_namespace *ns, int cap); 212 + extern bool ns_capable_setid(struct user_namespace *ns, int cap); 212 213 #else 213 214 static inline bool has_capability(struct task_struct *t, int cap) 214 215 { ··· 238 237 return true; 239 238 } 240 239 static inline bool ns_capable_noaudit(struct user_namespace *ns, int cap) 240 + { 241 + return true; 242 + } 243 + static inline bool ns_capable_setid(struct user_namespace *ns, int cap) 241 244 { 242 245 return true; 243 246 }
+19
kernel/capability.c
··· 416 416 EXPORT_SYMBOL(ns_capable_noaudit); 417 417 418 418 /** 419 + * ns_capable_setid - Determine if the current task has a superior capability 420 + * in effect, while signalling that this check is being done from within a 421 + * setid syscall. 422 + * @ns: The usernamespace we want the capability in 423 + * @cap: The capability to be tested for 424 + * 425 + * Return true if the current task has the given superior capability currently 426 + * available for use, false if not. 427 + * 428 + * This sets PF_SUPERPRIV on the task if the capability is available on the 429 + * assumption that it's about to be used. 430 + */ 431 + bool ns_capable_setid(struct user_namespace *ns, int cap) 432 + { 433 + return ns_capable_common(ns, cap, CAP_OPT_INSETID); 434 + } 435 + EXPORT_SYMBOL(ns_capable_setid); 436 + 437 + /** 419 438 * capable - Determine if the current task has a superior capability in effect 420 439 * @cap: The capability to be tested for 421 440 *
+5 -5
kernel/sys.c
··· 516 516 new->uid = kruid; 517 517 if (!uid_eq(old->uid, kruid) && 518 518 !uid_eq(old->euid, kruid) && 519 - !ns_capable(old->user_ns, CAP_SETUID)) 519 + !ns_capable_setid(old->user_ns, CAP_SETUID)) 520 520 goto error; 521 521 } 522 522 ··· 525 525 if (!uid_eq(old->uid, keuid) && 526 526 !uid_eq(old->euid, keuid) && 527 527 !uid_eq(old->suid, keuid) && 528 - !ns_capable(old->user_ns, CAP_SETUID)) 528 + !ns_capable_setid(old->user_ns, CAP_SETUID)) 529 529 goto error; 530 530 } 531 531 ··· 584 584 old = current_cred(); 585 585 586 586 retval = -EPERM; 587 - if (ns_capable(old->user_ns, CAP_SETUID)) { 587 + if (ns_capable_setid(old->user_ns, CAP_SETUID)) { 588 588 new->suid = new->uid = kuid; 589 589 if (!uid_eq(kuid, old->uid)) { 590 590 retval = set_user(new); ··· 646 646 old = current_cred(); 647 647 648 648 retval = -EPERM; 649 - if (!ns_capable(old->user_ns, CAP_SETUID)) { 649 + if (!ns_capable_setid(old->user_ns, CAP_SETUID)) { 650 650 if (ruid != (uid_t) -1 && !uid_eq(kruid, old->uid) && 651 651 !uid_eq(kruid, old->euid) && !uid_eq(kruid, old->suid)) 652 652 goto error; ··· 814 814 815 815 if (uid_eq(kuid, old->uid) || uid_eq(kuid, old->euid) || 816 816 uid_eq(kuid, old->suid) || uid_eq(kuid, old->fsuid) || 817 - ns_capable(old->user_ns, CAP_SETUID)) { 817 + ns_capable_setid(old->user_ns, CAP_SETUID)) { 818 818 if (!uid_eq(kuid, old->fsuid)) { 819 819 new->fsuid = kuid; 820 820 if (security_task_fix_setuid(new, old, LSM_SETID_FS) == 0)