linux-security-module.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2] kernel/sys.c: fix and improve control flow in __sys_setres[ug]id()
@ 2023-02-17 16:21 Ondrej Mosnacek
  2023-03-13 13:15 ` Ondrej Mosnacek
  2023-03-13 18:29 ` Andrew Morton
  0 siblings, 2 replies; 4+ messages in thread
From: Ondrej Mosnacek @ 2023-02-17 16:21 UTC (permalink / raw)
  To: Eric W. Biederman, Andrew Morton
  Cc: linux-security-module, selinux, linux-kernel

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.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
---

v2: improve commit message

 kernel/sys.c | 69 ++++++++++++++++++++++++++++++----------------------
 1 file changed, 40 insertions(+), 29 deletions(-)

diff --git a/kernel/sys.c b/kernel/sys.c
index 5fd54bf0e8867..6fd88686cd06f 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -664,6 +664,7 @@ long __sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
 	struct cred *new;
 	int retval;
 	kuid_t kruid, keuid, ksuid;
+	bool ruid_new, euid_new, suid_new;
 
 	kruid = make_kuid(ns, ruid);
 	keuid = make_kuid(ns, euid);
@@ -678,25 +679,29 @@ long __sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
 	if ((suid != (uid_t) -1) && !uid_valid(ksuid))
 		return -EINVAL;
 
+	old = current_cred();
+
+	/* check for no-op */
+	if ((ruid == (uid_t) -1 || uid_eq(kruid, old->uid)) &&
+	    (euid == (uid_t) -1 || (uid_eq(keuid, old->euid) &&
+				    uid_eq(keuid, old->fsuid))) &&
+	    (suid == (uid_t) -1 || uid_eq(ksuid, old->suid)))
+		return 0;
+
+	ruid_new = ruid != (uid_t) -1        && !uid_eq(kruid, old->uid) &&
+		   !uid_eq(kruid, old->euid) && !uid_eq(kruid, old->suid);
+	euid_new = euid != (uid_t) -1        && !uid_eq(keuid, old->uid) &&
+		   !uid_eq(keuid, old->euid) && !uid_eq(keuid, old->suid);
+	suid_new = suid != (uid_t) -1        && !uid_eq(ksuid, old->uid) &&
+		   !uid_eq(ksuid, old->euid) && !uid_eq(ksuid, old->suid);
+	if ((ruid_new || euid_new || suid_new) &&
+	    !ns_capable_setid(old->user_ns, CAP_SETUID))
+		return -EPERM;
+
 	new = prepare_creds();
 	if (!new)
 		return -ENOMEM;
 
-	old = current_cred();
-
-	retval = -EPERM;
-	if (!ns_capable_setid(old->user_ns, CAP_SETUID)) {
-		if (ruid != (uid_t) -1        && !uid_eq(kruid, old->uid) &&
-		    !uid_eq(kruid, old->euid) && !uid_eq(kruid, old->suid))
-			goto error;
-		if (euid != (uid_t) -1        && !uid_eq(keuid, old->uid) &&
-		    !uid_eq(keuid, old->euid) && !uid_eq(keuid, old->suid))
-			goto error;
-		if (suid != (uid_t) -1        && !uid_eq(ksuid, old->uid) &&
-		    !uid_eq(ksuid, old->euid) && !uid_eq(ksuid, old->suid))
-			goto error;
-	}
-
 	if (ruid != (uid_t) -1) {
 		new->uid = kruid;
 		if (!uid_eq(kruid, old->uid)) {
@@ -761,6 +766,7 @@ long __sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
 	struct cred *new;
 	int retval;
 	kgid_t krgid, kegid, ksgid;
+	bool rgid_new, egid_new, sgid_new;
 
 	krgid = make_kgid(ns, rgid);
 	kegid = make_kgid(ns, egid);
@@ -773,23 +779,28 @@ long __sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
 	if ((sgid != (gid_t) -1) && !gid_valid(ksgid))
 		return -EINVAL;
 
+	old = current_cred();
+
+	/* check for no-op */
+	if ((rgid == (gid_t) -1 || gid_eq(krgid, old->gid)) &&
+	    (egid == (gid_t) -1 || (gid_eq(kegid, old->egid) &&
+				    gid_eq(kegid, old->fsgid))) &&
+	    (sgid == (gid_t) -1 || gid_eq(ksgid, old->sgid)))
+		return 0;
+
+	rgid_new = rgid != (gid_t) -1        && !gid_eq(krgid, old->gid) &&
+		   !gid_eq(krgid, old->egid) && !gid_eq(krgid, old->sgid);
+	egid_new = egid != (gid_t) -1        && !gid_eq(kegid, old->gid) &&
+		   !gid_eq(kegid, old->egid) && !gid_eq(kegid, old->sgid);
+	sgid_new = sgid != (gid_t) -1        && !gid_eq(ksgid, old->gid) &&
+		   !gid_eq(ksgid, old->egid) && !gid_eq(ksgid, old->sgid);
+	if ((rgid_new || egid_new || sgid_new) &&
+	    !ns_capable_setid(old->user_ns, CAP_SETGID))
+		return -EPERM;
+
 	new = prepare_creds();
 	if (!new)
 		return -ENOMEM;
-	old = current_cred();
-
-	retval = -EPERM;
-	if (!ns_capable_setid(old->user_ns, CAP_SETGID)) {
-		if (rgid != (gid_t) -1        && !gid_eq(krgid, old->gid) &&
-		    !gid_eq(krgid, old->egid) && !gid_eq(krgid, old->sgid))
-			goto error;
-		if (egid != (gid_t) -1        && !gid_eq(kegid, old->gid) &&
-		    !gid_eq(kegid, old->egid) && !gid_eq(kegid, old->sgid))
-			goto error;
-		if (sgid != (gid_t) -1        && !gid_eq(ksgid, old->gid) &&
-		    !gid_eq(ksgid, old->egid) && !gid_eq(ksgid, old->sgid))
-			goto error;
-	}
 
 	if (rgid != (gid_t) -1)
 		new->gid = krgid;
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH v2] kernel/sys.c: fix and improve control flow in __sys_setres[ug]id()
  2023-02-17 16:21 [PATCH v2] kernel/sys.c: fix and improve control flow in __sys_setres[ug]id() Ondrej Mosnacek
@ 2023-03-13 13:15 ` Ondrej Mosnacek
  2023-03-13 18:29 ` Andrew Morton
  1 sibling, 0 replies; 4+ messages in thread
From: Ondrej Mosnacek @ 2023-03-13 13:15 UTC (permalink / raw)
  To: Eric W. Biederman, Andrew Morton
  Cc: linux-security-module, selinux, linux-kernel

On Fri, Feb 17, 2023 at 5:21 PM Ondrej Mosnacek <omosnace@redhat.com> wrote:
>
> 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.
>
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
> ---
>
> v2: improve commit message
>
>  kernel/sys.c | 69 ++++++++++++++++++++++++++++++----------------------
>  1 file changed, 40 insertions(+), 29 deletions(-)

Ping?

-- 
Ondrej Mosnacek
Senior Software Engineer, Linux Security - SELinux kernel
Red Hat, Inc.


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v2] kernel/sys.c: fix and improve control flow in __sys_setres[ug]id()
  2023-02-17 16:21 [PATCH v2] kernel/sys.c: fix and improve control flow in __sys_setres[ug]id() Ondrej Mosnacek
  2023-03-13 13:15 ` Ondrej Mosnacek
@ 2023-03-13 18:29 ` Andrew Morton
  2023-03-14  9:16   ` Ondrej Mosnacek
  1 sibling, 1 reply; 4+ messages in thread
From: Andrew Morton @ 2023-03-13 18:29 UTC (permalink / raw)
  To: Ondrej Mosnacek
  Cc: Eric W. Biederman, linux-security-module, selinux, linux-kernel

On Fri, 17 Feb 2023 17:21:54 +0100 Ondrej Mosnacek <omosnace@redhat.com> wrote:

> 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.
> 
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")

Looks and sounds good to me, so I queued it up for some testing.  I'd
ask that someone more familiar with this code perform review, please.

I assume that you believe that a -stable backport is desirable?  I'll
add a cc:stable to the patch for now.


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v2] kernel/sys.c: fix and improve control flow in __sys_setres[ug]id()
  2023-03-13 18:29 ` Andrew Morton
@ 2023-03-14  9:16   ` Ondrej Mosnacek
  0 siblings, 0 replies; 4+ messages in thread
From: Ondrej Mosnacek @ 2023-03-14  9:16 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Eric W. Biederman, linux-security-module, selinux, linux-kernel

On Mon, Mar 13, 2023 at 7:29 PM Andrew Morton <akpm@linux-foundation.org> wrote:
>
> On Fri, 17 Feb 2023 17:21:54 +0100 Ondrej Mosnacek <omosnace@redhat.com> wrote:
>
> > 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.
> >
> > Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
>
> Looks and sounds good to me, so I queued it up for some testing.  I'd
> ask that someone more familiar with this code perform review, please.
>
> I assume that you believe that a -stable backport is desirable?  I'll
> add a cc:stable to the patch for now.

Yes, it's a minor bug, but we hit it while testing on Fedora and it's
better for us to have the fix in stable kernels than adding a
workaround elsewhere.

Thanks,

-- 
Ondrej Mosnacek
Senior Software Engineer, Linux Security - SELinux kernel
Red Hat, Inc.


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2023-03-14  9:19 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-02-17 16:21 [PATCH v2] kernel/sys.c: fix and improve control flow in __sys_setres[ug]id() Ondrej Mosnacek
2023-03-13 13:15 ` Ondrej Mosnacek
2023-03-13 18:29 ` Andrew Morton
2023-03-14  9:16   ` Ondrej Mosnacek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).