diff mbox series

[v4] syscalls/prctl06: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS

Message ID 1563255457-2336-1-git-send-email-xuyang2018.jy@cn.fujitsu.com
State Accepted
Headers show
Series [v4] syscalls/prctl06: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS | expand

Commit Message

Yang Xu July 16, 2019, 5:37 a.m. UTC
Signed-off-by: Yang Xu <xuyang2018.jy@cn.fujitsu.com>
---
 include/lapi/prctl.h                          |   5 +
 runtest/syscalls                              |   1 +
 testcases/kernel/syscalls/prctl/.gitignore    |   2 +
 testcases/kernel/syscalls/prctl/prctl06.c     | 115 ++++++++++++++++++
 testcases/kernel/syscalls/prctl/prctl06.h     |  64 ++++++++++
 .../kernel/syscalls/prctl/prctl06_execve.c    |  45 +++++++
 6 files changed, 232 insertions(+)
 create mode 100644 testcases/kernel/syscalls/prctl/prctl06.c
 create mode 100644 testcases/kernel/syscalls/prctl/prctl06.h
 create mode 100644 testcases/kernel/syscalls/prctl/prctl06_execve.c

Comments

Cyril Hrubis July 16, 2019, 9:09 a.m. UTC | #1
Hi!
Pushed with two changes, thanks.


diff --git a/testcases/kernel/syscalls/prctl/prctl06.c b/testcases/kernel/syscalls/prctl/prctl06.c
index eafbedfef..0e1274a2a 100644
--- a/testcases/kernel/syscalls/prctl/prctl06.c
+++ b/testcases/kernel/syscalls/prctl/prctl06.c
@@ -81,8 +81,8 @@ static void setup(void)
 
 	SAFE_CP(TESTBIN, TEST_REL_BIN_DIR);
 
-	SAFE_CHMOD(BIN_PATH, SUID_MODE);
 	SAFE_CHOWN(BIN_PATH, 0, 0);
+	SAFE_CHMOD(BIN_PATH, SUID_MODE);

 	TEST(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0));
 	if (TST_RET == 0) {

This is actually important fix, you have to set the SUID bits last since
they are cleared if you change owner of the file.


diff --git a/testcases/kernel/syscalls/prctl/prctl06.h b/testcases/kernel/syscalls/prctl/prctl06.h
index 72f9c4e5a..f5c66e809 100644
--- a/testcases/kernel/syscalls/prctl/prctl06.h
+++ b/testcases/kernel/syscalls/prctl/prctl06.h
@@ -25,12 +25,14 @@
 #define BIN_PATH           MNTPOINT"/"TESTBIN
 #define SUID_MODE          (S_ISUID|S_ISGID|S_IXUSR|S_IXGRP|S_IXOTH)
 
-static int flag = 1;
-
 void check_proc_field(int val, char *name)
 {
+	static int flag = 1;
 	int field = 0;
 
+	if (!flag)
+		return;
+
 	TEST(FILE_LINES_SCANF(PROC_STATUS, "NoNewPrivs:%d", &field));
 	if (TST_RET == 1) {
 		tst_res(TCONF,
@@ -57,8 +59,8 @@ void check_no_new_privs(int val, char *name)
 		tst_res(TFAIL,
 			"%s prctl(PR_GET_NO_NEW_PRIVS) expected %d got %ld",
 			name, val, TST_RET);
-	if (flag)
-		check_proc_field(val, name);
+
+	check_proc_field(val, name);
 }
 
 #endif

This is merely cosmetic and moves the flag to the function so that it's
not global.
Yang Xu July 16, 2019, 9:32 a.m. UTC | #2
> Hi!
> Pushed with two changes, thanks.
>
>
> diff --git a/testcases/kernel/syscalls/prctl/prctl06.c b/testcases/kernel/syscalls/prctl/prctl06.c
> index eafbedfef..0e1274a2a 100644
> --- a/testcases/kernel/syscalls/prctl/prctl06.c
> +++ b/testcases/kernel/syscalls/prctl/prctl06.c
> @@ -81,8 +81,8 @@ static void setup(void)
>
>   	SAFE_CP(TESTBIN, TEST_REL_BIN_DIR);
>
> -	SAFE_CHMOD(BIN_PATH, SUID_MODE);
>   	SAFE_CHOWN(BIN_PATH, 0, 0);
> +	SAFE_CHMOD(BIN_PATH, SUID_MODE);
>
>   	TEST(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0));
>   	if (TST_RET == 0) {
>
> This is actually important fix, you have to set the SUID bits last since
> they are cleared if you change owner of the file.
Hi Cyril

OK. I learn a lot from this case. Thanks for kindly reply.

>
> diff --git a/testcases/kernel/syscalls/prctl/prctl06.h b/testcases/kernel/syscalls/prctl/prctl06.h
> index 72f9c4e5a..f5c66e809 100644
> --- a/testcases/kernel/syscalls/prctl/prctl06.h
> +++ b/testcases/kernel/syscalls/prctl/prctl06.h
> @@ -25,12 +25,14 @@
>   #define BIN_PATH           MNTPOINT"/"TESTBIN
>   #define SUID_MODE          (S_ISUID|S_ISGID|S_IXUSR|S_IXGRP|S_IXOTH)
>
> -static int flag = 1;
> -
>   void check_proc_field(int val, char *name)
>   {
> +	static int flag = 1;
>   	int field = 0;
>
> +	if (!flag)
> +		return;
> +
>   	TEST(FILE_LINES_SCANF(PROC_STATUS, "NoNewPrivs:%d",&field));
>   	if (TST_RET == 1) {
>   		tst_res(TCONF,
> @@ -57,8 +59,8 @@ void check_no_new_privs(int val, char *name)
>   		tst_res(TFAIL,
>   			"%s prctl(PR_GET_NO_NEW_PRIVS) expected %d got %ld",
>   			name, val, TST_RET);
> -	if (flag)
> -		check_proc_field(val, name);
> +
> +	check_proc_field(val, name);
>   }
>
>   #endif
>
> This is merely cosmetic and moves the flag to the function so that it's
> not global.
Yes. static local variable is better.
diff mbox series

Patch

diff --git a/include/lapi/prctl.h b/include/lapi/prctl.h
index ad0b12bce..54b3da20f 100644
--- a/include/lapi/prctl.h
+++ b/include/lapi/prctl.h
@@ -24,4 +24,9 @@ 
 # define PR_GET_CHILD_SUBREAPER	37
 #endif
 
+#ifndef PR_SET_NO_NEW_PRIVS
+# define PR_SET_NO_NEW_PRIVS 38
+# define PR_GET_NO_NEW_PRIVS 39
+#endif
+
 #endif /* LAPI_PRCTL_H__ */
diff --git a/runtest/syscalls b/runtest/syscalls
index 42c200423..ef7af41b5 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -865,6 +865,7 @@  prctl02 prctl02
 prctl03 prctl03
 prctl04 prctl04
 prctl05 prctl05
+prctl06 prctl06
 
 pread01 pread01
 pread01_64 pread01_64
diff --git a/testcases/kernel/syscalls/prctl/.gitignore b/testcases/kernel/syscalls/prctl/.gitignore
index 9ecaf9854..ee994086f 100644
--- a/testcases/kernel/syscalls/prctl/.gitignore
+++ b/testcases/kernel/syscalls/prctl/.gitignore
@@ -3,3 +3,5 @@ 
 /prctl03
 /prctl04
 /prctl05
+/prctl06
+/prctl06_execve
diff --git a/testcases/kernel/syscalls/prctl/prctl06.c b/testcases/kernel/syscalls/prctl/prctl06.c
new file mode 100644
index 000000000..eafbedfef
--- /dev/null
+++ b/testcases/kernel/syscalls/prctl/prctl06.c
@@ -0,0 +1,115 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2019 FUJITSU LIMITED. All rights reserved.
+ * Author: Yang Xu <xuyang2018.jy@cn.fujitsu.com>
+ *
+ * Test PR_GET_NO_NEW_PRIVS and PR_SET_NO_NEW_PRIVS of prctl(2).
+ *
+ * 1)Return the value of the no_new_privs bit for the calling thread.
+ *  A value of 0 indicates the regular execve(2) behavior.  A value of
+ *  1 indicates execve(2) will operate in the privilege-restricting mode.
+ * 2)With no_new_privs set to 1, diables 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.
+ * 3)The setting of this bit is inherited by children created by fork(2),
+ *  and preserved across execve(2). We also check NoNewPrivs field in
+ *  /proc/self/status if it supports.
+ */
+
+#include "prctl06.h"
+
+static uid_t nobody_uid;
+static gid_t nobody_gid;
+
+static void do_prctl(void)
+{
+	char ipc_env_var[1024];
+	char *const argv[] = {BIN_PATH, "After execve, parent process", NULL};
+	char *const childargv[] = {BIN_PATH, "After execve, child process", NULL};
+	char *const envp[] = {ipc_env_var, NULL };
+	int childpid;
+
+	check_no_new_privs(0, "parent");
+
+	TEST(prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
+	if (TST_RET == -1) {
+		tst_res(TFAIL | TTERRNO, "prctl(PR_SET_NO_NEW_PRIVS) failed");
+		return;
+	}
+	tst_res(TPASS, "prctl(PR_SET_NO_NEW_PRIVS) succeeded");
+
+	SAFE_SETGID(nobody_gid);
+	SAFE_SETUID(nobody_uid);
+
+	sprintf(ipc_env_var, IPC_ENV_VAR "=%s", getenv(IPC_ENV_VAR));
+
+	childpid = SAFE_FORK();
+	if (childpid == 0) {
+		check_no_new_privs(1, "After fork, child process");
+		execve(BIN_PATH, childargv, envp);
+		tst_brk(TFAIL | TTERRNO,
+			"child process failed to execute prctl_execve");
+
+	} else {
+		tst_reap_children();
+		check_no_new_privs(1, "parent process");
+		execve(BIN_PATH, argv, envp);
+		tst_brk(TFAIL | TTERRNO,
+			"parent process failed to execute prctl_execve");
+	}
+}
+
+static void verify_prctl(void)
+{
+	int pid;
+
+	pid = SAFE_FORK();
+	if (pid == 0) {
+		do_prctl();
+		exit(0);
+	}
+}
+
+static void setup(void)
+{
+	struct passwd *pw;
+
+	pw = SAFE_GETPWNAM("nobody");
+	nobody_uid = pw->pw_uid;
+	nobody_gid = pw->pw_gid;
+
+	SAFE_CP(TESTBIN, TEST_REL_BIN_DIR);
+
+	SAFE_CHMOD(BIN_PATH, SUID_MODE);
+	SAFE_CHOWN(BIN_PATH, 0, 0);
+
+	TEST(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0));
+	if (TST_RET == 0) {
+		tst_res(TINFO, "kernel supports PR_GET/SET_NO_NEW_PRIVS");
+		return;
+	}
+
+	if (TST_ERR == EINVAL)
+		tst_brk(TCONF,
+			"kernel doesn't support PR_GET/SET_NO_NEW_PRIVS");
+
+	tst_brk(TBROK | TTERRNO,
+		"current environment doesn't permit PR_GET/SET_NO_NEW_PRIVS");
+}
+
+static const char *const resfile[] = {
+	TESTBIN,
+	NULL,
+};
+
+static struct tst_test test = {
+	.resource_files = resfile,
+	.setup = setup,
+	.test_all = verify_prctl,
+	.forks_child = 1,
+	.needs_root = 1,
+	.mount_device = 1,
+	.mntpoint = MNTPOINT,
+	.child_needs_reinit = 1,
+};
diff --git a/testcases/kernel/syscalls/prctl/prctl06.h b/testcases/kernel/syscalls/prctl/prctl06.h
new file mode 100644
index 000000000..72f9c4e5a
--- /dev/null
+++ b/testcases/kernel/syscalls/prctl/prctl06.h
@@ -0,0 +1,64 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2019 FUJITSU LIMITED. All rights reserved.
+ * Author: Yang Xu <xuyang2018.jy@cn.fujitsu.com>
+ */
+#ifndef PRCTL06_H
+#define PRCTL06_H
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/prctl.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/capability.h>
+#include <lapi/prctl.h>
+#include "tst_test.h"
+
+#define PROC_STATUS        "/proc/self/status"
+#define IPC_ENV_VAR        "LTP_IPC_PATH"
+#define MNTPOINT           "mntpoint"
+#define TESTBIN            "prctl06_execve"
+#define TEST_REL_BIN_DIR   MNTPOINT"/"
+#define BIN_PATH           MNTPOINT"/"TESTBIN
+#define SUID_MODE          (S_ISUID|S_ISGID|S_IXUSR|S_IXGRP|S_IXOTH)
+
+static int flag = 1;
+
+void check_proc_field(int val, char *name)
+{
+	int field = 0;
+
+	TEST(FILE_LINES_SCANF(PROC_STATUS, "NoNewPrivs:%d", &field));
+	if (TST_RET == 1) {
+		tst_res(TCONF,
+			"%s doesn't support NoNewPrivs field", PROC_STATUS);
+		flag = 0;
+		return;
+	}
+	if (val == field)
+		tst_res(TPASS, "%s %s NoNewPrivs field expected %d got %d",
+			name, PROC_STATUS, val, field);
+	else
+		tst_res(TFAIL, "%s %s NoNewPrivs field expected %d got %d",
+			name, PROC_STATUS, val, field);
+}
+
+void check_no_new_privs(int val, char *name)
+{
+	TEST(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0));
+	if (TST_RET == val)
+		tst_res(TPASS,
+			"%s prctl(PR_GET_NO_NEW_PRIVS) expected %d got %d",
+			name, val, val);
+	else
+		tst_res(TFAIL,
+			"%s prctl(PR_GET_NO_NEW_PRIVS) expected %d got %ld",
+			name, val, TST_RET);
+	if (flag)
+		check_proc_field(val, name);
+}
+
+#endif
diff --git a/testcases/kernel/syscalls/prctl/prctl06_execve.c b/testcases/kernel/syscalls/prctl/prctl06_execve.c
new file mode 100644
index 000000000..d1e60e6c2
--- /dev/null
+++ b/testcases/kernel/syscalls/prctl/prctl06_execve.c
@@ -0,0 +1,45 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2019 FUJITSU LIMITED. All rights reserved.
+ * Author: Yang Xu <xuyang2018.jy@cn.fujitsu.com>
+ *
+ * dummy program which is used by prctl06 testcase
+ */
+#define TST_NO_DEFAULT_MAIN
+#include "prctl06.h"
+
+
+int main(int argc, char **argv)
+{
+	struct passwd *pw;
+
+	pw = SAFE_GETPWNAM("nobody");
+
+	tst_reinit();
+	if (argc != 2)
+		tst_brk(TFAIL, "argc is %d, expected 2", argc);
+
+	check_no_new_privs(1, argv[1]);
+
+	TEST(getegid());
+	if (TST_RET == 0)
+		tst_res(TFAIL,
+			"%s getegid() returns 0 unexpectedly, it gains root privileges",
+			argv[1]);
+	if (TST_RET == pw->pw_gid)
+		tst_res(TPASS,
+			"%s getegid() returns nobody, it doesn't gain root privileges",
+			argv[1]);
+
+	TEST(geteuid());
+	if (TST_RET == 0)
+		tst_res(TFAIL,
+			"%s geteuid() returns 0 unexpectedly, it gains root privileges",
+			argv[1]);
+	if (TST_RET == pw->pw_uid)
+		tst_res(TPASS,
+			"%s geteuid() returns nobody, it doesn't gain root privileges",
+			argv[1]);
+
+	return 0;
+}