aboutsummaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorAndy Lutomirski <luto@amacapital.net>2012-04-12 17:47:50 -0400
committerJames Morris <james.l.morris@oracle.com>2012-04-13 21:13:18 -0400
commit259e5e6c75a910f3b5e656151dc602f53f9d7548 (patch)
tree4405fdf68238f2e33f27b04e8c37c9e29a2493d8 /security
parent9ccf010f8172b699ea80178860e8ea228f7dce56 (diff)
Add PR_{GET,SET}_NO_NEW_PRIVS to prevent execve from granting privs
With this change, calling prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) disables privilege granting operations at execve-time. For example, a process will not be able to execute a setuid binary to change their uid or gid if this bit is set. The same is true for file capabilities. Additionally, LSM_UNSAFE_NO_NEW_PRIVS is defined to ensure that LSMs respect the requested behavior. To determine if the NO_NEW_PRIVS bit is set, a task may call prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0); It returns 1 if set and 0 if it is not set. If any of the arguments are non-zero, it will return -1 and set errno to -EINVAL. (PR_SET_NO_NEW_PRIVS behaves similarly.) This functionality is desired for the proposed seccomp filter patch series. By using PR_SET_NO_NEW_PRIVS, it allows a task to modify the system call behavior for itself and its child tasks without being able to impact the behavior of a more privileged task. Another potential use is making certain privileged operations unprivileged. For example, chroot may be considered "safe" if it cannot affect privileged tasks. Note, this patch causes execve to fail when PR_SET_NO_NEW_PRIVS is set and AppArmor is in use. It is fixed in a subsequent patch. Signed-off-by: Andy Lutomirski <luto@amacapital.net> Signed-off-by: Will Drewry <wad@chromium.org> Acked-by: Eric Paris <eparis@redhat.com> Acked-by: Kees Cook <keescook@chromium.org> v18: updated change desc v17: using new define values as per 3.4 Signed-off-by: James Morris <james.l.morris@oracle.com>
Diffstat (limited to 'security')
-rw-r--r--security/apparmor/domain.c4
-rw-r--r--security/commoncap.c7
-rw-r--r--security/selinux/hooks.c10
3 files changed, 18 insertions, 3 deletions
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index 6327685c101e..18c88d06e881 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -360,6 +360,10 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
360 if (bprm->cred_prepared) 360 if (bprm->cred_prepared)
361 return 0; 361 return 0;
362 362
363 /* XXX: no_new_privs is not usable with AppArmor yet */
364 if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)
365 return -EPERM;
366
363 cxt = bprm->cred->security; 367 cxt = bprm->cred->security;
364 BUG_ON(!cxt); 368 BUG_ON(!cxt);
365 369
diff --git a/security/commoncap.c b/security/commoncap.c
index 0cf4b53480a7..edd3918fac02 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -506,14 +506,17 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
506skip: 506skip:
507 507
508 /* Don't let someone trace a set[ug]id/setpcap binary with the revised 508 /* Don't let someone trace a set[ug]id/setpcap binary with the revised
509 * credentials unless they have the appropriate permit 509 * credentials unless they have the appropriate permit.
510 *
511 * In addition, if NO_NEW_PRIVS, then ensure we get no new privs.
510 */ 512 */
511 if ((new->euid != old->uid || 513 if ((new->euid != old->uid ||
512 new->egid != old->gid || 514 new->egid != old->gid ||
513 !cap_issubset(new->cap_permitted, old->cap_permitted)) && 515 !cap_issubset(new->cap_permitted, old->cap_permitted)) &&
514 bprm->unsafe & ~LSM_UNSAFE_PTRACE_CAP) { 516 bprm->unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
515 /* downgrade; they get no more than they had, and maybe less */ 517 /* downgrade; they get no more than they had, and maybe less */
516 if (!capable(CAP_SETUID)) { 518 if (!capable(CAP_SETUID) ||
519 (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)) {
517 new->euid = new->uid; 520 new->euid = new->uid;
518 new->egid = new->gid; 521 new->egid = new->gid;
519 } 522 }
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index d85b793c9321..0b06685787b9 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2016,6 +2016,13 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
2016 new_tsec->sid = old_tsec->exec_sid; 2016 new_tsec->sid = old_tsec->exec_sid;
2017 /* Reset exec SID on execve. */ 2017 /* Reset exec SID on execve. */
2018 new_tsec->exec_sid = 0; 2018 new_tsec->exec_sid = 0;
2019
2020 /*
2021 * Minimize confusion: if no_new_privs and a transition is
2022 * explicitly requested, then fail the exec.
2023 */
2024 if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)
2025 return -EPERM;
2019 } else { 2026 } else {
2020 /* Check for a default transition on this program. */ 2027 /* Check for a default transition on this program. */
2021 rc = security_transition_sid(old_tsec->sid, isec->sid, 2028 rc = security_transition_sid(old_tsec->sid, isec->sid,
@@ -2029,7 +2036,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
2029 ad.selinux_audit_data = &sad; 2036 ad.selinux_audit_data = &sad;
2030 ad.u.path = bprm->file->f_path; 2037 ad.u.path = bprm->file->f_path;
2031 2038
2032 if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) 2039 if ((bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) ||
2040 (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS))
2033 new_tsec->sid = old_tsec->sid; 2041 new_tsec->sid = old_tsec->sid;
2034 2042
2035 if (new_tsec->sid == old_tsec->sid) { 2043 if (new_tsec->sid == old_tsec->sid) {