Merge tag 'caps-pr-20250729' of git://git.kernel.org/pub/scm/linux/kernel/git/sergeh/linux

Pull capabilities update from Serge Hallyn:

 - Fix broken link in documentation in capability.h

 - Correct the permission check for unsafe exec

   During exec, different effective and real credentials were assumed to
   mean changed credentials, making it impossible in the no-new-privs
   case to keep different uid and euid

* tag 'caps-pr-20250729' of git://git.kernel.org/pub/scm/linux/kernel/git/sergeh/linux:
  uapi: fix broken link in linux/capability.h
  exec: Correct the permission check for unsafe exec
This commit is contained in:
Linus Torvalds
2025-07-31 11:19:54 -07:00
2 changed files with 11 additions and 14 deletions

View File

@@ -6,9 +6,10 @@
* Alexander Kjeldaas <astor@guardian.no>
* with help from Aleph1, Roland Buresund and Andrew Main.
*
* See here for the libcap library ("POSIX draft" compliance):
* See here for the libcap2 library (compliant with Section 25 of
* the withdrawn POSIX 1003.1e Draft 17):
*
* ftp://www.kernel.org/pub/linux/libs/security/linux-privs/kernel-2.6/
* https://www.kernel.org/pub/linux/libs/security/linux-privs/libcap2/
*/
#ifndef _UAPI_LINUX_CAPABILITY_H

View File

@@ -856,12 +856,6 @@ static void handle_privileged_root(struct linux_binprm *bprm, bool has_fcap,
#define __cap_full(field, cred) \
cap_issubset(CAP_FULL_SET, cred->cap_##field)
static inline bool __is_setuid(struct cred *new, const struct cred *old)
{ return !uid_eq(new->euid, old->uid); }
static inline bool __is_setgid(struct cred *new, const struct cred *old)
{ return !gid_eq(new->egid, old->gid); }
/*
* 1) Audit candidate if current->cap_effective is set
*
@@ -891,7 +885,7 @@ static inline bool nonroot_raised_pE(struct cred *new, const struct cred *old,
(root_privileged() &&
__is_suid(root, new) &&
!__cap_full(effective, new)) ||
(!__is_setuid(new, old) &&
(uid_eq(new->euid, old->euid) &&
((has_fcap &&
__cap_gained(permitted, new, old)) ||
__cap_gained(ambient, new, old))))
@@ -917,7 +911,7 @@ int cap_bprm_creds_from_file(struct linux_binprm *bprm, const struct file *file)
/* Process setpcap binaries and capabilities for uid 0 */
const struct cred *old = current_cred();
struct cred *new = bprm->cred;
bool effective = false, has_fcap = false, is_setid;
bool effective = false, has_fcap = false, id_changed;
int ret;
kuid_t root_uid;
@@ -941,9 +935,9 @@ int cap_bprm_creds_from_file(struct linux_binprm *bprm, const struct file *file)
*
* In addition, if NO_NEW_PRIVS, then ensure we get no new privs.
*/
is_setid = __is_setuid(new, old) || __is_setgid(new, old);
id_changed = !uid_eq(new->euid, old->euid) || !in_group_p(new->egid);
if ((is_setid || __cap_gained(permitted, new, old)) &&
if ((id_changed || __cap_gained(permitted, new, old)) &&
((bprm->unsafe & ~LSM_UNSAFE_PTRACE) ||
!ptracer_capable(current, new->user_ns))) {
/* downgrade; they get no more than they had, and maybe less */
@@ -960,7 +954,7 @@ int cap_bprm_creds_from_file(struct linux_binprm *bprm, const struct file *file)
new->sgid = new->fsgid = new->egid;
/* File caps or setid cancels ambient. */
if (has_fcap || is_setid)
if (has_fcap || id_changed)
cap_clear(new->cap_ambient);
/*
@@ -993,7 +987,9 @@ int cap_bprm_creds_from_file(struct linux_binprm *bprm, const struct file *file)
return -EPERM;
/* Check for privilege-elevated exec. */
if (is_setid ||
if (id_changed ||
!uid_eq(new->euid, old->uid) ||
!gid_eq(new->egid, old->gid) ||
(!__is_real(root_uid, new) &&
(effective ||
__cap_grew(permitted, ambient, new))))