ALT Linux kernel packages development
 help / color / mirror / Atom feed
* [d-kernel] [PATCH v6] AltHa: handle setcap binaries in the same way as setuid ones
@ 2022-05-30 21:31 Vladimir D. Seleznev
  2022-06-01 23:17 ` [d-kernel] [PATCH v6 1/2] " Vladimir D. Seleznev
  0 siblings, 1 reply; 5+ messages in thread
From: Vladimir D. Seleznev @ 2022-05-30 21:31 UTC (permalink / raw)
  To: devel-kernel

altha.nosuid facility controls what binaries can raise user privilleges.
Prior to this commit it only handled setuid binaries, but it was still
possible to raise privilleges via setcaps. Now it handles both setuid
and setcap binaries.

Signed-off-by: Vladimir D. Seleznev <vseleznv@altlinux.org>
---
 Documentation/admin-guide/LSM/AltHa.rst |  6 ++--
 security/altha/Kconfig                  |  2 +-
 security/altha/altha_lsm.c              | 47 ++++++++++++++++++++-----
 3 files changed, 43 insertions(+), 12 deletions(-)

diff --git a/Documentation/admin-guide/LSM/AltHa.rst b/Documentation/admin-guide/LSM/AltHa.rst
index be698709d3f0..beda40601c9e 100644
--- a/Documentation/admin-guide/LSM/AltHa.rst
+++ b/Documentation/admin-guide/LSM/AltHa.rst
@@ -3,7 +3,7 @@ AltHa
 ====
 
 AltHa is a Linux Security Module currently has three userspace hardening options:
-    * ignore SUID on binaries (with exceptions possible);
+    * ignore SUID and setcaps on binaries (with exceptions possible);
     * prevent running selected script interpreters in interactive mode;
     * disable open file unlinking in selected dirs.
     * enable kiosk mode
@@ -15,12 +15,12 @@ through sysctls in ``/proc/sys/kernel/altha``.
 
 NoSUID
 ============
-Modern Linux systems can be used with minimal (or even zero at least for OWL and ALT) usage of SUID programms, but in many cases in full-featured desktop or server systems there are plenty of them: uncounted and sometimes unnecessary. Privileged programms are always an attack surface, but mounting filesystems with ``nosuid`` flag doesn't provide enough granularity in SUID binaries management. This LSM module provides a single control point for all SUID binaries. When this submodule is enabled, SUID bits on all binaries except explicitly listed are system-wide ignored.
+Modern Linux systems can be used with minimal (or even zero at least for OWL and ALT) usage of SUID programms, but in many cases in full-featured desktop or server systems there are plenty of them: uncounted and sometimes unnecessary. Privileged programms are always an attack surface, but mounting filesystems with ``nosuid`` flag doesn't provide enough granularity in SUID binaries management. This LSM module provides a single control point for all SUID and setcap binaries. When this submodule is enabled, SUID and setcap bits on all binaries except explicitly listed are system-wide ignored.
 
 Sysctl parameters and defaults:
 
 * ``kernel.altha.nosuid.enabled = 0``, set to 1 to enable
-* ``kernel.altha.nosuid.exceptions =``, colon-separated list of enabled SUID binaries, for example: ``/bin/su:/usr/libexec/hasher-priv/hasher-priv``
+* ``kernel.altha.nosuid.exceptions =``, colon-separated list of enabled SUID and setcap binaries, for example: ``/bin/su:/usr/libexec/hasher-priv/hasher-priv``
 
 RestrScript
 ============
diff --git a/security/altha/Kconfig b/security/altha/Kconfig
index 4bafdef4e58e..cd1dd69cc48d 100644
--- a/security/altha/Kconfig
+++ b/security/altha/Kconfig
@@ -4,7 +4,7 @@ config SECURITY_ALTHA
 	default n
 	help
 	  Some hardening options:
-	    * ignore SUID on binaries (with exceptions possible);
+	    * ignore SUID and setcap on binaries (with exceptions possible);
 	    * prevent running selected script interprers in interactive move;
 	    * WxorX for filesystems (with exceptions possible);
 
diff --git a/security/altha/altha_lsm.c b/security/altha/altha_lsm.c
index c670ad7ed458..27cbc33d7b80 100644
--- a/security/altha/altha_lsm.c
+++ b/security/altha/altha_lsm.c
@@ -11,6 +11,7 @@
 
 #include <linux/lsm_hooks.h>
 #include <linux/cred.h>
+#include <linux/capability.h>
 #include <linux/sysctl.h>
 #include <linux/binfmts.h>
 #include <linux/file.h>
@@ -241,6 +242,7 @@ int is_olock_dir(struct inode *inode)
 static int altha_bprm_creds_from_file(struct linux_binprm *bprm, struct file * fi)
 {
 	struct altha_list_struct *node;
+	char *setuidcap_str = "setuid";
 	/* when it's not a shebang issued script interpreter */
 	if (rstrscript_enabled && bprm->executable == bprm->interpreter) {
 		char *path_p;
@@ -267,11 +269,37 @@ static int altha_bprm_creds_from_file(struct linux_binprm *bprm, struct file * f
 		up_read(&interpreters_sem);
 		kfree(path_buffer);
 	}
-	if (unlikely(nosuid_enabled &&
-		     !uid_eq(bprm->cred->uid, bprm->cred->euid))) {
+	if (nosuid_enabled) {
 		char *path_p;
 		char *path_buffer;
-		uid_t cur_uid;
+		int is_setuid = 0, is_setcap = 0;
+		uid_t cur_uid, cur_euid;
+
+		/*
+		 * While nosuid is supposed to prevent switching to superuser,
+		 * it does not check swtiching to a non-privileged user because
+		 * it is almost never used.
+		 */
+		is_setuid = !uid_eq(bprm->cred->uid, bprm->cred->euid);
+
+		if (!is_setuid) {
+			cur_euid = from_kuid(bprm->cred->user_ns, bprm->cred->euid);
+			/*
+			 * Check capabilities only for effectivly non-superuser
+			 * processes: superuser processess always have
+			 * capabilities, should keep them so these processes
+			 * continue working correctly.
+			 */
+			if (cur_euid != (uid_t) 0)
+				is_setcap = !(cap_isclear(bprm->cred->cap_permitted)
+						& cap_isclear(bprm->cred->cap_effective));
+
+			setuidcap_str = "setcap";
+		}
+
+		/* If no suid and no caps detected, exit. */
+		if (!is_setuid && !is_setcap)
+			return 0;
 
 		path_buffer = kmalloc(PATH_MAX, GFP_KERNEL);
 		if (!path_buffer)
@@ -283,8 +311,8 @@ static int altha_bprm_creds_from_file(struct linux_binprm *bprm, struct file * f
 		list_for_each_entry(node, &nosuid_exceptions_list, list) {
 			if (strcmp(path_p, node->spath) == 0) {
 				pr_notice_ratelimited
-				    ("AltHa/NoSUID: %s permitted to setuid from %d\n",
-				     bprm->filename, cur_uid);
+				    ("AltHa/NoSUID: %s permitted to %s from %d\n",
+				     bprm->filename, setuidcap_str, cur_uid);
 				up_read(&nosuid_exceptions_sem);
 				kfree(path_buffer);
 				return 0;
@@ -292,9 +320,12 @@ static int altha_bprm_creds_from_file(struct linux_binprm *bprm, struct file * f
 		}
 		up_read(&nosuid_exceptions_sem);
 		pr_notice_ratelimited
-		    ("AltHa/NoSUID: %s prevented to setuid from %d\n",
-		     bprm->filename, cur_uid);
-		bprm->cred->euid = bprm->cred->uid;
+		    ("AltHa/NoSUID: %s prevented to %s from %d\n",
+		     bprm->filename, setuidcap_str, cur_uid);
+		if (is_setuid)
+			bprm->cred->euid = bprm->cred->uid;
+		cap_clear(bprm->cred->cap_permitted);
+		cap_clear(bprm->cred->cap_effective);
 		kfree(path_buffer);
 	}
 	return 0;
-- 
2.33.3



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

* [d-kernel] [PATCH v6 1/2] AltHa: handle setcap binaries in the same way as setuid ones
  2022-05-30 21:31 [d-kernel] [PATCH v6] AltHa: handle setcap binaries in the same way as setuid ones Vladimir D. Seleznev
@ 2022-06-01 23:17 ` Vladimir D. Seleznev
  2022-06-01 23:17   ` [d-kernel] [PATCH v6 2/2] AltHa: add tests Vladimir D. Seleznev
  0 siblings, 1 reply; 5+ messages in thread
From: Vladimir D. Seleznev @ 2022-06-01 23:17 UTC (permalink / raw)
  To: devel-kernel

altha.nosuid facility controls what binaries can raise user privilleges.
Prior to this commit it only handled setuid binaries, but it was still
possible to raise privilleges via setcaps. Now it handles both setuid
and setcap binaries.

Signed-off-by: Vladimir D. Seleznev <vseleznv@altlinux.org>
---
 Documentation/admin-guide/LSM/AltHa.rst |  6 ++--
 security/altha/Kconfig                  |  2 +-
 security/altha/altha_lsm.c              | 47 ++++++++++++++++++++-----
 3 files changed, 43 insertions(+), 12 deletions(-)

diff --git a/Documentation/admin-guide/LSM/AltHa.rst b/Documentation/admin-guide/LSM/AltHa.rst
index be698709d3f0..beda40601c9e 100644
--- a/Documentation/admin-guide/LSM/AltHa.rst
+++ b/Documentation/admin-guide/LSM/AltHa.rst
@@ -3,7 +3,7 @@ AltHa
 ====
 
 AltHa is a Linux Security Module currently has three userspace hardening options:
-    * ignore SUID on binaries (with exceptions possible);
+    * ignore SUID and setcaps on binaries (with exceptions possible);
     * prevent running selected script interpreters in interactive mode;
     * disable open file unlinking in selected dirs.
     * enable kiosk mode
@@ -15,12 +15,12 @@ through sysctls in ``/proc/sys/kernel/altha``.
 
 NoSUID
 ============
-Modern Linux systems can be used with minimal (or even zero at least for OWL and ALT) usage of SUID programms, but in many cases in full-featured desktop or server systems there are plenty of them: uncounted and sometimes unnecessary. Privileged programms are always an attack surface, but mounting filesystems with ``nosuid`` flag doesn't provide enough granularity in SUID binaries management. This LSM module provides a single control point for all SUID binaries. When this submodule is enabled, SUID bits on all binaries except explicitly listed are system-wide ignored.
+Modern Linux systems can be used with minimal (or even zero at least for OWL and ALT) usage of SUID programms, but in many cases in full-featured desktop or server systems there are plenty of them: uncounted and sometimes unnecessary. Privileged programms are always an attack surface, but mounting filesystems with ``nosuid`` flag doesn't provide enough granularity in SUID binaries management. This LSM module provides a single control point for all SUID and setcap binaries. When this submodule is enabled, SUID and setcap bits on all binaries except explicitly listed are system-wide ignored.
 
 Sysctl parameters and defaults:
 
 * ``kernel.altha.nosuid.enabled = 0``, set to 1 to enable
-* ``kernel.altha.nosuid.exceptions =``, colon-separated list of enabled SUID binaries, for example: ``/bin/su:/usr/libexec/hasher-priv/hasher-priv``
+* ``kernel.altha.nosuid.exceptions =``, colon-separated list of enabled SUID and setcap binaries, for example: ``/bin/su:/usr/libexec/hasher-priv/hasher-priv``
 
 RestrScript
 ============
diff --git a/security/altha/Kconfig b/security/altha/Kconfig
index 4bafdef4e58e..cd1dd69cc48d 100644
--- a/security/altha/Kconfig
+++ b/security/altha/Kconfig
@@ -4,7 +4,7 @@ config SECURITY_ALTHA
 	default n
 	help
 	  Some hardening options:
-	    * ignore SUID on binaries (with exceptions possible);
+	    * ignore SUID and setcap on binaries (with exceptions possible);
 	    * prevent running selected script interprers in interactive move;
 	    * WxorX for filesystems (with exceptions possible);
 
diff --git a/security/altha/altha_lsm.c b/security/altha/altha_lsm.c
index c670ad7ed458..27cbc33d7b80 100644
--- a/security/altha/altha_lsm.c
+++ b/security/altha/altha_lsm.c
@@ -11,6 +11,7 @@
 
 #include <linux/lsm_hooks.h>
 #include <linux/cred.h>
+#include <linux/capability.h>
 #include <linux/sysctl.h>
 #include <linux/binfmts.h>
 #include <linux/file.h>
@@ -241,6 +242,7 @@ int is_olock_dir(struct inode *inode)
 static int altha_bprm_creds_from_file(struct linux_binprm *bprm, struct file * fi)
 {
 	struct altha_list_struct *node;
+	char *setuidcap_str = "setuid";
 	/* when it's not a shebang issued script interpreter */
 	if (rstrscript_enabled && bprm->executable == bprm->interpreter) {
 		char *path_p;
@@ -267,11 +269,37 @@ static int altha_bprm_creds_from_file(struct linux_binprm *bprm, struct file * f
 		up_read(&interpreters_sem);
 		kfree(path_buffer);
 	}
-	if (unlikely(nosuid_enabled &&
-		     !uid_eq(bprm->cred->uid, bprm->cred->euid))) {
+	if (nosuid_enabled) {
 		char *path_p;
 		char *path_buffer;
-		uid_t cur_uid;
+		int is_setuid = 0, is_setcap = 0;
+		uid_t cur_uid, cur_euid;
+
+		/*
+		 * While nosuid is supposed to prevent switching to superuser,
+		 * it does not check swtiching to a non-privileged user because
+		 * it is almost never used.
+		 */
+		is_setuid = !uid_eq(bprm->cred->uid, bprm->cred->euid);
+
+		if (!is_setuid) {
+			cur_euid = from_kuid(bprm->cred->user_ns, bprm->cred->euid);
+			/*
+			 * Check capabilities only for effectivly non-superuser
+			 * processes: superuser processess always have
+			 * capabilities, should keep them so these processes
+			 * continue working correctly.
+			 */
+			if (cur_euid != (uid_t) 0)
+				is_setcap = !(cap_isclear(bprm->cred->cap_permitted)
+						& cap_isclear(bprm->cred->cap_effective));
+
+			setuidcap_str = "setcap";
+		}
+
+		/* If no suid and no caps detected, exit. */
+		if (!is_setuid && !is_setcap)
+			return 0;
 
 		path_buffer = kmalloc(PATH_MAX, GFP_KERNEL);
 		if (!path_buffer)
@@ -283,8 +311,8 @@ static int altha_bprm_creds_from_file(struct linux_binprm *bprm, struct file * f
 		list_for_each_entry(node, &nosuid_exceptions_list, list) {
 			if (strcmp(path_p, node->spath) == 0) {
 				pr_notice_ratelimited
-				    ("AltHa/NoSUID: %s permitted to setuid from %d\n",
-				     bprm->filename, cur_uid);
+				    ("AltHa/NoSUID: %s permitted to %s from %d\n",
+				     bprm->filename, setuidcap_str, cur_uid);
 				up_read(&nosuid_exceptions_sem);
 				kfree(path_buffer);
 				return 0;
@@ -292,9 +320,12 @@ static int altha_bprm_creds_from_file(struct linux_binprm *bprm, struct file * f
 		}
 		up_read(&nosuid_exceptions_sem);
 		pr_notice_ratelimited
-		    ("AltHa/NoSUID: %s prevented to setuid from %d\n",
-		     bprm->filename, cur_uid);
-		bprm->cred->euid = bprm->cred->uid;
+		    ("AltHa/NoSUID: %s prevented to %s from %d\n",
+		     bprm->filename, setuidcap_str, cur_uid);
+		if (is_setuid)
+			bprm->cred->euid = bprm->cred->uid;
+		cap_clear(bprm->cred->cap_permitted);
+		cap_clear(bprm->cred->cap_effective);
 		kfree(path_buffer);
 	}
 	return 0;
-- 
2.33.3



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

* [d-kernel] [PATCH v6 2/2] AltHa: add tests
  2022-06-01 23:17 ` [d-kernel] [PATCH v6 1/2] " Vladimir D. Seleznev
@ 2022-06-01 23:17   ` Vladimir D. Seleznev
  2022-06-02 10:12     ` Alexey V. Vissarionov
  0 siblings, 1 reply; 5+ messages in thread
From: Vladimir D. Seleznev @ 2022-06-01 23:17 UTC (permalink / raw)
  To: devel-kernel

---
 security/altha/altha-test.sh | 114 +++++++++++++++++++++++++++++++++++
 1 file changed, 114 insertions(+)
 create mode 100755 security/altha/altha-test.sh

diff --git a/security/altha/altha-test.sh b/security/altha/altha-test.sh
new file mode 100755
index 000000000000..5de4c66b643b
--- /dev/null
+++ b/security/altha/altha-test.sh
@@ -0,0 +1,114 @@
+#!/bin/bash -efu
+# SPDX-License-Identifier: GPL-2.0
+#
+# AltHa test for nosuid feature
+
+sysctl -q kernel.altha.nosuid.enabled >/dev/null || {
+	echo >&2 "AltHa is not enabled, quitting"
+	exit 2
+}
+
+ret=0
+
+num_failed=0
+num_tests=0
+
+nosuid_enabled=kernel.altha.nosuid.enabled
+nosuid_exeptions=kernel.altha.nosuid.exceptions
+ID_CMD=/usr/bin/id
+NC_CMD=/usr/bin/nc
+
+tmpdir="$(mktemp -d)"
+cleanup()
+{
+	[ ! -f "$tmpdir/id_perms" ] ||
+		chmod "$(cat "$tmpdir/id_perms")" "$ID_CMD"
+
+	local caps
+	if [ -f "$tmpdir/nc_caps" ]; then
+		caps="$(cat "$tmpdir/nc_caps")"
+		setcap "${caps:--r}" "$NC_CMD"
+	fi
+
+	[ ! -f "$tmpdir/nosuid_enabled" ] ||
+		sysctl "$nosuid_enabled=$(cat "$tmpdir/nosuid_enabled")"
+
+	[ ! -f "$tmpdir/nosuid_exceptions" ] ||
+		sysctl "$nosuid_exeptions=$(cat "$tmpdir/nosuid_exceptions")"
+
+	rm -r "$tmpdir"
+	exit "$@"
+}
+trap 'cleanup $?' EXIT QUIT INT ERR
+
+save_altha_state()
+{
+	sysctl "$nosuid_enabled" |cut -f3 -d' ' > "$tmpdir/nosuid_enabled"
+	sysctl "$nosuid_exeptions" |cut -f3 -d' ' > "$tmpdir/nosuid_exceptions"
+}
+
+run_test()
+{
+	local test_cmd="$1"; shift
+	local test_cond="$1"; shift
+
+	while IFS=$'\t' read -r precond expres; do
+		num_tests=$((num_tests + 1))
+
+		eval "$precond"
+		eval "$test_cmd" >"$tmpdir/result" 2>&1 ||:
+
+		if [ "$(cat "$tmpdir/result")" != "$expres" ]; then
+			echo >&2 "$test_cmd FAILED with $precond"
+			echo >&2 "expected result: $expres"
+			echo >&2 "actual result: $(cat "$tmpdir/result")"
+			num_failed=$((num_failed + 1))
+		fi
+	done <"$test_cond"
+}
+
+check_setuid()
+{
+	# save id perm and make it setuid
+	stat -c '%a' "$ID_CMD" > "$tmpdir/id_perms"
+	chmod 4755 "$ID_CMD"
+
+	local nobody_uid
+	nobody_uid="$(grep -E '^\<nobody\>' /etc/passwd |cut -f3 -d:)"
+
+	cat <<EOF >"$tmpdir/setuid_test"
+sysctl $nosuid_enabled=0	0
+sysctl $nosuid_enabled=1	$nobody_uid
+sysctl $nosuid_exeptions=$ID_CMD	0
+EOF
+
+
+	run_test 'su nobody -s /bin/bash -c "id -u"' "$tmpdir/setuid_test"
+}
+
+check_setcap()
+{
+	getcap "$NC_CMD" |cut -d' ' -f3 > "$tmpdir/nc_caps"
+	setcap cap_net_bind_service,cap_net_admin+ep "$NC_CMD"
+
+	cat <<EOF >"$tmpdir/setcap_test"
+sysctl $nosuid_enabled=0
+sysctl $nosuid_enabled=1	nc: Permission denied
+sysctl $nosuid_exeptions=$NC_CMD
+EOF
+
+	run_test "timeout 1 "$NC_CMD" -l 80" "$tmpdir/setcap_test"
+}
+
+save_altha_state
+check_setuid
+check_setcap
+
+if [ "$num_failed" -ne 0 ]; then
+	echo >&2 "$num_failed of $num_tests tests FAILED"
+	ret=1
+else
+	echo >&2 "All $num_tests tests succeed"
+fi
+
+exit $ret
-- 
2.33.3



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

* Re: [d-kernel] [PATCH v6 2/2] AltHa: add tests
  2022-06-01 23:17   ` [d-kernel] [PATCH v6 2/2] AltHa: add tests Vladimir D. Seleznev
@ 2022-06-02 10:12     ` Alexey V. Vissarionov
  2022-06-02 10:39       ` Vladimir D. Seleznev
  0 siblings, 1 reply; 5+ messages in thread
From: Alexey V. Vissarionov @ 2022-06-02 10:12 UTC (permalink / raw)
  To: ALT Linux kernel packages development

On 2022-06-01 23:17:51 +0000, Vladimir D. Seleznev wrote:

 > run_test "timeout 1 "$NC_CMD" -l 80" "$tmpdir/setcap_test"

Now imagine *:80 is already bound by nginx or whatever :-)

I'd rather use port 9 ("discard" service), as bare `nc -l 9`
would do exactly that.


-- 
Alexey V. Vissarionov
gremlin ПРИ altlinux ТЧК org; +vii-cmiii-ccxxix-lxxix-xlii
GPG: 0D92F19E1C0DC36E27F61A29CD17E2B43D879005 @ hkp://keys.gnupg.net


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

* Re: [d-kernel] [PATCH v6 2/2] AltHa: add tests
  2022-06-02 10:12     ` Alexey V. Vissarionov
@ 2022-06-02 10:39       ` Vladimir D. Seleznev
  0 siblings, 0 replies; 5+ messages in thread
From: Vladimir D. Seleznev @ 2022-06-02 10:39 UTC (permalink / raw)
  To: ALT Linux kernel packages development

On Thu, Jun 02, 2022 at 01:12:11PM +0300, Alexey V. Vissarionov wrote:
> On 2022-06-01 23:17:51 +0000, Vladimir D. Seleznev wrote:
> 
>  > run_test "timeout 1 "$NC_CMD" -l 80" "$tmpdir/setcap_test"
> 
> Now imagine *:80 is already bound by nginx or whatever :-)
> 
> I'd rather use port 9 ("discard" service), as bare `nc -l 9`
> would do exactly that.

Nice advise, thank you!

-- 
   WBR,
   Vladimir D. Seleznev


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

end of thread, other threads:[~2022-06-02 10:39 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-05-30 21:31 [d-kernel] [PATCH v6] AltHa: handle setcap binaries in the same way as setuid ones Vladimir D. Seleznev
2022-06-01 23:17 ` [d-kernel] [PATCH v6 1/2] " Vladimir D. Seleznev
2022-06-01 23:17   ` [d-kernel] [PATCH v6 2/2] AltHa: add tests Vladimir D. Seleznev
2022-06-02 10:12     ` Alexey V. Vissarionov
2022-06-02 10:39       ` Vladimir D. Seleznev

ALT Linux kernel packages development

This inbox may be cloned and mirrored by anyone:

	git clone --mirror http://lore.altlinux.org/devel-kernel/0 devel-kernel/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 devel-kernel devel-kernel/ http://lore.altlinux.org/devel-kernel \
		devel-kernel@altlinux.org devel-kernel@altlinux.ru devel-kernel@altlinux.com
	public-inbox-index devel-kernel

Example config snippet for mirrors.
Newsgroup available over NNTP:
	nntp://lore.altlinux.org/org.altlinux.lists.devel-kernel


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git