Skip to content

Commit 659c0ce

Browse files
WOnder93akpm00
authored andcommitted
kernel/sys.c: fix and improve control flow in __sys_setres[ug]id()
Linux Security Modules (LSMs) that implement the "capable" hook will usually emit an access denial message to the audit log whenever they "block" the current task from using the given capability based on their security policy. The occurrence of a denial is used as an indication that the given task has attempted an operation that requires the given access permission, so the callers of functions that perform LSM permission checks must take care to avoid calling them too early (before it is decided if the permission is actually needed to perform the requested operation). The __sys_setres[ug]id() functions violate this convention by first calling ns_capable_setid() and only then checking if the operation requires the capability or not. It means that any caller that has the capability granted by DAC (task's capability set) but not by MAC (LSMs) will generate a "denied" audit record, even if is doing an operation for which the capability is not required. Fix this by reordering the checks such that ns_capable_setid() is checked last and -EPERM is returned immediately if it returns false. While there, also do two small optimizations: * move the capability check before prepare_creds() and * bail out early in case of a no-op. Link: https://lkml.kernel.org/r/[email protected] Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Ondrej Mosnacek <[email protected]> Cc: Eric W. Biederman <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 2ff559f commit 659c0ce

File tree

1 file changed

+40
-29
lines changed

1 file changed

+40
-29
lines changed

kernel/sys.c

Lines changed: 40 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,7 @@ long __sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
664664
struct cred *new;
665665
int retval;
666666
kuid_t kruid, keuid, ksuid;
667+
bool ruid_new, euid_new, suid_new;
667668

668669
kruid = make_kuid(ns, ruid);
669670
keuid = make_kuid(ns, euid);
@@ -678,25 +679,29 @@ long __sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
678679
if ((suid != (uid_t) -1) && !uid_valid(ksuid))
679680
return -EINVAL;
680681

682+
old = current_cred();
683+
684+
/* check for no-op */
685+
if ((ruid == (uid_t) -1 || uid_eq(kruid, old->uid)) &&
686+
(euid == (uid_t) -1 || (uid_eq(keuid, old->euid) &&
687+
uid_eq(keuid, old->fsuid))) &&
688+
(suid == (uid_t) -1 || uid_eq(ksuid, old->suid)))
689+
return 0;
690+
691+
ruid_new = ruid != (uid_t) -1 && !uid_eq(kruid, old->uid) &&
692+
!uid_eq(kruid, old->euid) && !uid_eq(kruid, old->suid);
693+
euid_new = euid != (uid_t) -1 && !uid_eq(keuid, old->uid) &&
694+
!uid_eq(keuid, old->euid) && !uid_eq(keuid, old->suid);
695+
suid_new = suid != (uid_t) -1 && !uid_eq(ksuid, old->uid) &&
696+
!uid_eq(ksuid, old->euid) && !uid_eq(ksuid, old->suid);
697+
if ((ruid_new || euid_new || suid_new) &&
698+
!ns_capable_setid(old->user_ns, CAP_SETUID))
699+
return -EPERM;
700+
681701
new = prepare_creds();
682702
if (!new)
683703
return -ENOMEM;
684704

685-
old = current_cred();
686-
687-
retval = -EPERM;
688-
if (!ns_capable_setid(old->user_ns, CAP_SETUID)) {
689-
if (ruid != (uid_t) -1 && !uid_eq(kruid, old->uid) &&
690-
!uid_eq(kruid, old->euid) && !uid_eq(kruid, old->suid))
691-
goto error;
692-
if (euid != (uid_t) -1 && !uid_eq(keuid, old->uid) &&
693-
!uid_eq(keuid, old->euid) && !uid_eq(keuid, old->suid))
694-
goto error;
695-
if (suid != (uid_t) -1 && !uid_eq(ksuid, old->uid) &&
696-
!uid_eq(ksuid, old->euid) && !uid_eq(ksuid, old->suid))
697-
goto error;
698-
}
699-
700705
if (ruid != (uid_t) -1) {
701706
new->uid = kruid;
702707
if (!uid_eq(kruid, old->uid)) {
@@ -761,6 +766,7 @@ long __sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
761766
struct cred *new;
762767
int retval;
763768
kgid_t krgid, kegid, ksgid;
769+
bool rgid_new, egid_new, sgid_new;
764770

765771
krgid = make_kgid(ns, rgid);
766772
kegid = make_kgid(ns, egid);
@@ -773,23 +779,28 @@ long __sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
773779
if ((sgid != (gid_t) -1) && !gid_valid(ksgid))
774780
return -EINVAL;
775781

782+
old = current_cred();
783+
784+
/* check for no-op */
785+
if ((rgid == (gid_t) -1 || gid_eq(krgid, old->gid)) &&
786+
(egid == (gid_t) -1 || (gid_eq(kegid, old->egid) &&
787+
gid_eq(kegid, old->fsgid))) &&
788+
(sgid == (gid_t) -1 || gid_eq(ksgid, old->sgid)))
789+
return 0;
790+
791+
rgid_new = rgid != (gid_t) -1 && !gid_eq(krgid, old->gid) &&
792+
!gid_eq(krgid, old->egid) && !gid_eq(krgid, old->sgid);
793+
egid_new = egid != (gid_t) -1 && !gid_eq(kegid, old->gid) &&
794+
!gid_eq(kegid, old->egid) && !gid_eq(kegid, old->sgid);
795+
sgid_new = sgid != (gid_t) -1 && !gid_eq(ksgid, old->gid) &&
796+
!gid_eq(ksgid, old->egid) && !gid_eq(ksgid, old->sgid);
797+
if ((rgid_new || egid_new || sgid_new) &&
798+
!ns_capable_setid(old->user_ns, CAP_SETGID))
799+
return -EPERM;
800+
776801
new = prepare_creds();
777802
if (!new)
778803
return -ENOMEM;
779-
old = current_cred();
780-
781-
retval = -EPERM;
782-
if (!ns_capable_setid(old->user_ns, CAP_SETGID)) {
783-
if (rgid != (gid_t) -1 && !gid_eq(krgid, old->gid) &&
784-
!gid_eq(krgid, old->egid) && !gid_eq(krgid, old->sgid))
785-
goto error;
786-
if (egid != (gid_t) -1 && !gid_eq(kegid, old->gid) &&
787-
!gid_eq(kegid, old->egid) && !gid_eq(kegid, old->sgid))
788-
goto error;
789-
if (sgid != (gid_t) -1 && !gid_eq(ksgid, old->gid) &&
790-
!gid_eq(ksgid, old->egid) && !gid_eq(ksgid, old->sgid))
791-
goto error;
792-
}
793804

794805
if (rgid != (gid_t) -1)
795806
new->gid = krgid;

0 commit comments

Comments
 (0)