All of lore.kernel.org
 help / color / mirror / Atom feed
* [LTP] [PATCH] syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
@ 2019-05-09 12:21 Yang Xu
  2019-05-22 10:18 ` xuyang
  2019-05-23 11:52 ` Cyril Hrubis
  0 siblings, 2 replies; 15+ messages in thread
From: Yang Xu @ 2019-05-09 12:21 UTC (permalink / raw)
  To: ltp

Signed-off-by: Yang Xu <xuyang2018.jy@cn.fujitsu.com>
---
 include/lapi/prctl.h                          |   5 +
 runtest/syscalls                              |   1 +
 testcases/kernel/syscalls/prctl/.gitignore    |   1 +
 testcases/kernel/syscalls/prctl/Makefile      |   2 +
 testcases/kernel/syscalls/prctl/prctl06.c     | 117 ++++++++++++++++++
 .../kernel/syscalls/prctl/prctl06_execve.c    |  46 +++++++
 6 files changed, 172 insertions(+)
 create mode 100644 testcases/kernel/syscalls/prctl/prctl06.c
 create mode 100644 testcases/kernel/syscalls/prctl/prctl06_execve.c

diff --git a/include/lapi/prctl.h b/include/lapi/prctl.h
index 91da9c2d6..663ce19e9 100644
--- a/include/lapi/prctl.h
+++ b/include/lapi/prctl.h
@@ -24,4 +24,9 @@
 # define PR_SET_SECCOMP  22
 #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 950615bef..a43bf5e4d 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..f52f6f665 100644
--- a/testcases/kernel/syscalls/prctl/.gitignore
+++ b/testcases/kernel/syscalls/prctl/.gitignore
@@ -3,3 +3,4 @@
 /prctl03
 /prctl04
 /prctl05
+/prctl06
diff --git a/testcases/kernel/syscalls/prctl/Makefile b/testcases/kernel/syscalls/prctl/Makefile
index bd617d806..99a9d42e7 100644
--- a/testcases/kernel/syscalls/prctl/Makefile
+++ b/testcases/kernel/syscalls/prctl/Makefile
@@ -20,4 +20,6 @@ top_srcdir		?= ../../../..
 
 include $(top_srcdir)/include/mk/testcases.mk
 
+LDLIBS			+= $(CAP_LIBS)
+
 include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/syscalls/prctl/prctl06.c b/testcases/kernel/syscalls/prctl/prctl06.c
new file mode 100644
index 000000000..ba5c2bcfb
--- /dev/null
+++ b/testcases/kernel/syscalls/prctl/prctl06.c
@@ -0,0 +1,117 @@
+// 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 (as the function result) 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, execve(2) promises not to grant privileges
+ *   to do anything that could not have been done without the execve(2)
+ *   call(for example, rendering the set-user-ID and set-group-ID mode bits)
+ * 3)The setting of this bit is inherited by children created by fork(2) and
+ *   clone(2), and preserved across execve(2).
+ */
+
+#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 IPC_ENV_VAR "LTP_IPC_PATH"
+
+static void check_no_new_privs(int val)
+{
+	TEST(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0));
+	if (TST_RET == val)
+		tst_res(TPASS,
+			"prctl(PR_GET_NO_NEW_PRIVS) got %d "
+			"when no_new_privs was %d", val, val);
+	else
+		tst_res(TFAIL | TTERRNO,
+			"prctl(PR_GET_NO_NEW_PRIVS) expected %d got %ld",
+			val, TST_RET);
+	return;
+}
+
+static void do_prctl(void)
+{
+	char path[4096];
+	char ipc_env_var[1024];
+	char *const argv[] = {"prctl06_execve", "parent process", NULL};
+	char *const childargv[] = {"prctl06_execve", "child process", NULL};
+	char *const envp[] = {"LTP_TEST_ENV_VAR=test", ipc_env_var, NULL };
+	cap_t caps = cap_init();
+	cap_value_t capList = CAP_SETGID;
+	unsigned int num_caps = 1;
+	int childpid;
+
+	cap_set_flag(caps, CAP_EFFECTIVE, num_caps, &capList, CAP_SET);
+	cap_set_flag(caps, CAP_INHERITABLE, num_caps, &capList, CAP_SET);
+	cap_set_flag(caps, CAP_PERMITTED, num_caps, &capList, CAP_SET);
+
+	if (cap_set_proc(caps))
+		tst_brk(TFAIL | TERRNO,
+			"cap_set_flag(CAP_SETGID) failed");
+	tst_res(TINFO, "cap_set_flag(CAP_SETGID) succeeded");
+
+	check_no_new_privs(0);
+
+	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");
+
+	check_no_new_privs(1);
+
+	if (tst_get_path("prctl06_execve", path, sizeof(path)))
+		tst_brk(TCONF, "Couldn't find prctl_execve in $PATH");
+
+	sprintf(ipc_env_var, IPC_ENV_VAR "=%s", getenv(IPC_ENV_VAR));
+	childpid = SAFE_FORK();
+	if (childpid == 0) {
+		check_no_new_privs(1);
+		execve(path, childargv, envp);
+		tst_brk(TFAIL | TERRNO,
+			"child process failed to execute prctl_execve");
+
+	} else {
+		tst_reap_children();
+		execve(path, argv, envp);
+		tst_brk(TFAIL | TERRNO,
+			"parent process failed to execute prctl_execve");
+	}
+	cap_free(caps);
+	return;
+}
+
+static void verify_prctl(void)
+{
+	int pid;
+
+	pid = SAFE_FORK();
+	if (pid == 0) {
+		do_prctl();
+		exit(0);
+	}
+	tst_reap_children();
+	return;
+}
+
+static struct tst_test test = {
+	.test_all = verify_prctl,
+	.forks_child = 1,
+	.needs_root = 1,
+	.child_needs_reinit = 1,
+};
diff --git a/testcases/kernel/syscalls/prctl/prctl06_execve.c b/testcases/kernel/syscalls/prctl/prctl06_execve.c
new file mode 100644
index 000000000..84c28551c
--- /dev/null
+++ b/testcases/kernel/syscalls/prctl/prctl06_execve.c
@@ -0,0 +1,46 @@
+// 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 <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pwd.h>
+#include "tst_test.h"
+
+int main(int argc, char **argv)
+{
+	struct passwd *pw;
+	uid_t nobody_uid;
+	gid_t nobody_gid;
+
+	tst_reinit();
+	if (argc != 2)
+		tst_brk(TFAIL, "argc is %d, expected 2", argc);
+
+	pw = SAFE_GETPWNAM("nobody");
+	nobody_uid = pw->pw_uid;
+	nobody_gid = pw->pw_gid;
+	/*positive check*/
+	TEST(setgid(nobody_gid));
+	if (TST_RET == -1)
+		tst_res(TFAIL | TTERRNO,
+			"%s setgid(%d) isn't permmit", argv[1], nobody_gid);
+	else
+		tst_res(TPASS, "%s setgid(%d) succeed expectedly",
+			argv[1], nobody_gid);
+	/*negative check*/
+	TEST(setuid(nobody_uid));
+	if (TST_RET == -1)
+		tst_res(TPASS | TTERRNO,
+			"%s setuid(%d) isn't permmit", argv[1], nobody_uid);
+	else
+		tst_res(TFAIL, " %s setuid(%d) succeed unexpectedly",
+			argv[1], nobody_gid);
+	return 0;
+}
-- 
2.18.1




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

* [LTP] [PATCH] syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
  2019-05-09 12:21 [LTP] [PATCH] syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS Yang Xu
@ 2019-05-22 10:18 ` xuyang
  2019-05-23 11:52 ` Cyril Hrubis
  1 sibling, 0 replies; 15+ messages in thread
From: xuyang @ 2019-05-22 10:18 UTC (permalink / raw)
  To: ltp

Hi
Ping.:-)
> Signed-off-by: Yang Xu <xuyang2018.jy@cn.fujitsu.com>
> ---
>  include/lapi/prctl.h                          |   5 +
>  runtest/syscalls                              |   1 +
>  testcases/kernel/syscalls/prctl/.gitignore    |   1 +
>  testcases/kernel/syscalls/prctl/Makefile      |   2 +
>  testcases/kernel/syscalls/prctl/prctl06.c     | 117 ++++++++++++++++++
>  .../kernel/syscalls/prctl/prctl06_execve.c    |  46 +++++++
>  6 files changed, 172 insertions(+)
>  create mode 100644 testcases/kernel/syscalls/prctl/prctl06.c
>  create mode 100644 testcases/kernel/syscalls/prctl/prctl06_execve.c
>
> diff --git a/include/lapi/prctl.h b/include/lapi/prctl.h
> index 91da9c2d6..663ce19e9 100644
> --- a/include/lapi/prctl.h
> +++ b/include/lapi/prctl.h
> @@ -24,4 +24,9 @@
>  # define PR_SET_SECCOMP  22
>  #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 950615bef..a43bf5e4d 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..f52f6f665 100644
> --- a/testcases/kernel/syscalls/prctl/.gitignore
> +++ b/testcases/kernel/syscalls/prctl/.gitignore
> @@ -3,3 +3,4 @@
>  /prctl03
>  /prctl04
>  /prctl05
> +/prctl06
> diff --git a/testcases/kernel/syscalls/prctl/Makefile b/testcases/kernel/syscalls/prctl/Makefile
> index bd617d806..99a9d42e7 100644
> --- a/testcases/kernel/syscalls/prctl/Makefile
> +++ b/testcases/kernel/syscalls/prctl/Makefile
> @@ -20,4 +20,6 @@ top_srcdir		?= ../../../..
>  
>  include $(top_srcdir)/include/mk/testcases.mk
>  
> +LDLIBS			+= $(CAP_LIBS)
> +
>  include $(top_srcdir)/include/mk/generic_leaf_target.mk
> diff --git a/testcases/kernel/syscalls/prctl/prctl06.c b/testcases/kernel/syscalls/prctl/prctl06.c
> new file mode 100644
> index 000000000..ba5c2bcfb
> --- /dev/null
> +++ b/testcases/kernel/syscalls/prctl/prctl06.c
> @@ -0,0 +1,117 @@
> +// 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 (as the function result) 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, execve(2) promises not to grant privileges
> + *   to do anything that could not have been done without the execve(2)
> + *   call(for example, rendering the set-user-ID and set-group-ID mode bits)
> + * 3)The setting of this bit is inherited by children created by fork(2) and
> + *   clone(2), and preserved across execve(2).
> + */
> +
> +#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 IPC_ENV_VAR "LTP_IPC_PATH"
> +
> +static void check_no_new_privs(int val)
> +{
> +	TEST(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0));
> +	if (TST_RET == val)
> +		tst_res(TPASS,
> +			"prctl(PR_GET_NO_NEW_PRIVS) got %d "
> +			"when no_new_privs was %d", val, val);
> +	else
> +		tst_res(TFAIL | TTERRNO,
> +			"prctl(PR_GET_NO_NEW_PRIVS) expected %d got %ld",
> +			val, TST_RET);
> +	return;
> +}
> +
> +static void do_prctl(void)
> +{
> +	char path[4096];
> +	char ipc_env_var[1024];
> +	char *const argv[] = {"prctl06_execve", "parent process", NULL};
> +	char *const childargv[] = {"prctl06_execve", "child process", NULL};
> +	char *const envp[] = {"LTP_TEST_ENV_VAR=test", ipc_env_var, NULL };
> +	cap_t caps = cap_init();
> +	cap_value_t capList = CAP_SETGID;
> +	unsigned int num_caps = 1;
> +	int childpid;
> +
> +	cap_set_flag(caps, CAP_EFFECTIVE, num_caps, &capList, CAP_SET);
> +	cap_set_flag(caps, CAP_INHERITABLE, num_caps, &capList, CAP_SET);
> +	cap_set_flag(caps, CAP_PERMITTED, num_caps, &capList, CAP_SET);
> +
> +	if (cap_set_proc(caps))
> +		tst_brk(TFAIL | TERRNO,
> +			"cap_set_flag(CAP_SETGID) failed");
> +	tst_res(TINFO, "cap_set_flag(CAP_SETGID) succeeded");
> +
> +	check_no_new_privs(0);
> +
> +	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");
> +
> +	check_no_new_privs(1);
> +
> +	if (tst_get_path("prctl06_execve", path, sizeof(path)))
> +		tst_brk(TCONF, "Couldn't find prctl_execve in $PATH");
> +
> +	sprintf(ipc_env_var, IPC_ENV_VAR "=%s", getenv(IPC_ENV_VAR));
> +	childpid = SAFE_FORK();
> +	if (childpid == 0) {
> +		check_no_new_privs(1);
> +		execve(path, childargv, envp);
> +		tst_brk(TFAIL | TERRNO,
> +			"child process failed to execute prctl_execve");
> +
> +	} else {
> +		tst_reap_children();
> +		execve(path, argv, envp);
> +		tst_brk(TFAIL | TERRNO,
> +			"parent process failed to execute prctl_execve");
> +	}
> +	cap_free(caps);
> +	return;
> +}
> +
> +static void verify_prctl(void)
> +{
> +	int pid;
> +
> +	pid = SAFE_FORK();
> +	if (pid == 0) {
> +		do_prctl();
> +		exit(0);
> +	}
> +	tst_reap_children();
> +	return;
> +}
> +
> +static struct tst_test test = {
> +	.test_all = verify_prctl,
> +	.forks_child = 1,
> +	.needs_root = 1,
> +	.child_needs_reinit = 1,
> +};
> diff --git a/testcases/kernel/syscalls/prctl/prctl06_execve.c b/testcases/kernel/syscalls/prctl/prctl06_execve.c
> new file mode 100644
> index 000000000..84c28551c
> --- /dev/null
> +++ b/testcases/kernel/syscalls/prctl/prctl06_execve.c
> @@ -0,0 +1,46 @@
> +// 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 <stdlib.h>
> +#include <sys/types.h>
> +#include <unistd.h>
> +#include <errno.h>
> +#include <pwd.h>
> +#include "tst_test.h"
> +
> +int main(int argc, char **argv)
> +{
> +	struct passwd *pw;
> +	uid_t nobody_uid;
> +	gid_t nobody_gid;
> +
> +	tst_reinit();
> +	if (argc != 2)
> +		tst_brk(TFAIL, "argc is %d, expected 2", argc);
> +
> +	pw = SAFE_GETPWNAM("nobody");
> +	nobody_uid = pw->pw_uid;
> +	nobody_gid = pw->pw_gid;
> +	/*positive check*/
> +	TEST(setgid(nobody_gid));
> +	if (TST_RET == -1)
> +		tst_res(TFAIL | TTERRNO,
> +			"%s setgid(%d) isn't permmit", argv[1], nobody_gid);
> +	else
> +		tst_res(TPASS, "%s setgid(%d) succeed expectedly",
> +			argv[1], nobody_gid);
> +	/*negative check*/
> +	TEST(setuid(nobody_uid));
> +	if (TST_RET == -1)
> +		tst_res(TPASS | TTERRNO,
> +			"%s setuid(%d) isn't permmit", argv[1], nobody_uid);
> +	else
> +		tst_res(TFAIL, " %s setuid(%d) succeed unexpectedly",
> +			argv[1], nobody_gid);
> +	return 0;
> +}




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

* [LTP] [PATCH] syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
  2019-05-09 12:21 [LTP] [PATCH] syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS Yang Xu
  2019-05-22 10:18 ` xuyang
@ 2019-05-23 11:52 ` Cyril Hrubis
  2019-06-14 10:32   ` Yang Xu
  2019-06-19 10:58   ` [LTP] [PATCH v2] " Yang Xu
  1 sibling, 2 replies; 15+ messages in thread
From: Cyril Hrubis @ 2019-05-23 11:52 UTC (permalink / raw)
  To: ltp

Hi!
> +#define IPC_ENV_VAR "LTP_IPC_PATH"
> +
> +static void check_no_new_privs(int val)
> +{
> +	TEST(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0));
> +	if (TST_RET == val)
> +		tst_res(TPASS,
> +			"prctl(PR_GET_NO_NEW_PRIVS) got %d "
> +			"when no_new_privs was %d", val, val);
> +	else
> +		tst_res(TFAIL | TTERRNO,
> +			"prctl(PR_GET_NO_NEW_PRIVS) expected %d got %ld",
> +			val, TST_RET);
> +	return;

This return is useless.

> +}
> +
> +static void do_prctl(void)
> +{
> +	char path[4096];
> +	char ipc_env_var[1024];
> +	char *const argv[] = {"prctl06_execve", "parent process", NULL};
> +	char *const childargv[] = {"prctl06_execve", "child process", NULL};
> +	char *const envp[] = {"LTP_TEST_ENV_VAR=test", ipc_env_var, NULL };
> +	cap_t caps = cap_init();
> +	cap_value_t capList = CAP_SETGID;
> +	unsigned int num_caps = 1;
> +	int childpid;
> +
> +	cap_set_flag(caps, CAP_EFFECTIVE, num_caps, &capList, CAP_SET);
> +	cap_set_flag(caps, CAP_INHERITABLE, num_caps, &capList, CAP_SET);
> +	cap_set_flag(caps, CAP_PERMITTED, num_caps, &capList, CAP_SET);
> +
> +	if (cap_set_proc(caps))
> +		tst_brk(TFAIL | TERRNO,
> +			"cap_set_flag(CAP_SETGID) failed");

You cannot use tst_brk with TFAIL, the best you can do here is to use
tst_ret(TFAIL ... ) then return; as you do below.

> +	tst_res(TINFO, "cap_set_flag(CAP_SETGID) succeeded");
> +
> +	check_no_new_privs(0);
> +
> +	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");
> +
> +	check_no_new_privs(1);
> +
> +	if (tst_get_path("prctl06_execve", path, sizeof(path)))
> +		tst_brk(TCONF, "Couldn't find prctl_execve in $PATH");

If the path to the binary is in $PATH you don't have to execute the
binary by an absolute path, passing it's name to execve() should
suffice.

> +	sprintf(ipc_env_var, IPC_ENV_VAR "=%s", getenv(IPC_ENV_VAR));
> +	childpid = SAFE_FORK();
> +	if (childpid == 0) {
> +		check_no_new_privs(1);

		Maybe we can pass a "child" string here and "parent"
		string in the cases above so that we can print if the
		check was done in child/parent in the tst_res() inside
		of this function.

> +		execve(path, childargv, envp);
> +		tst_brk(TFAIL | TERRNO,
> +			"child process failed to execute prctl_execve");
> +
> +	} else {
> +		tst_reap_children();
> +		execve(path, argv, envp);
> +		tst_brk(TFAIL | TERRNO,
> +			"parent process failed to execute prctl_execve");
> +	}
> +	cap_free(caps);
> +	return;
> +}
> +
> +static void verify_prctl(void)
> +{
> +	int pid;
> +
> +	pid = SAFE_FORK();
> +	if (pid == 0) {
> +		do_prctl();
> +		exit(0);
> +	}
> +	tst_reap_children();
> +	return;
> +}
> +
> +static struct tst_test test = {
> +	.test_all = verify_prctl,
> +	.forks_child = 1,
> +	.needs_root = 1,
> +	.child_needs_reinit = 1,
> +};
> diff --git a/testcases/kernel/syscalls/prctl/prctl06_execve.c b/testcases/kernel/syscalls/prctl/prctl06_execve.c
> new file mode 100644
> index 000000000..84c28551c
> --- /dev/null
> +++ b/testcases/kernel/syscalls/prctl/prctl06_execve.c
> @@ -0,0 +1,46 @@
> +// 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 <stdlib.h>
> +#include <sys/types.h>
> +#include <unistd.h>
> +#include <errno.h>
> +#include <pwd.h>
> +#include "tst_test.h"
> +
> +int main(int argc, char **argv)
> +{
> +	struct passwd *pw;
> +	uid_t nobody_uid;
> +	gid_t nobody_gid;
> +
> +	tst_reinit();
> +	if (argc != 2)
> +		tst_brk(TFAIL, "argc is %d, expected 2", argc);
> +
> +	pw = SAFE_GETPWNAM("nobody");
> +	nobody_uid = pw->pw_uid;
> +	nobody_gid = pw->pw_gid;
> +	/*positive check*/
> +	TEST(setgid(nobody_gid));
> +	if (TST_RET == -1)
> +		tst_res(TFAIL | TTERRNO,
> +			"%s setgid(%d) isn't permmit", argv[1], nobody_gid);
> +	else
> +		tst_res(TPASS, "%s setgid(%d) succeed expectedly",
> +			argv[1], nobody_gid);
> +	/*negative check*/
> +	TEST(setuid(nobody_uid));
> +	if (TST_RET == -1)
> +		tst_res(TPASS | TTERRNO,
> +			"%s setuid(%d) isn't permmit", argv[1], nobody_uid);
> +	else
> +		tst_res(TFAIL, " %s setuid(%d) succeed unexpectedly",
> +			argv[1], nobody_gid);
> +	return 0;
> +}

I do not think that this is actually testing the prctl in question. Here
we actually check that capabilities were inherited over fork() + exec().

As far as I understand the PR_SET_NO_NEW_PRIVS it has been designed
expecially to avoid misuse of capabilities associated with a particular
file.

So the check would have to do:

1. copy the prctl06_execve binary to a $PWD
2. chmod it with S_ISUID, S_ISGID
3. the prct06_execve would be executed under user/group nobody
4. the prct06_execve itself would check if it gained root priviledges

And we can do the same for capablities as well.

I guess that we would have to format a device and mount it with support
for capabilities for the test, since as far as I can tell you cannot
usually do neither of set-usr-id or add capabilites to files in /tmp/.

-- 
Cyril Hrubis
chrubis@suse.cz

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

* [LTP] [PATCH] syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
  2019-05-23 11:52 ` Cyril Hrubis
@ 2019-06-14 10:32   ` Yang Xu
  2019-07-09 10:53     ` Cyril Hrubis
  2019-06-19 10:58   ` [LTP] [PATCH v2] " Yang Xu
  1 sibling, 1 reply; 15+ messages in thread
From: Yang Xu @ 2019-06-14 10:32 UTC (permalink / raw)
  To: ltp

Hi Cyril
   Sorry for the late reply.

> Hi!
>> +#define IPC_ENV_VAR "LTP_IPC_PATH"
>> +
>> +static void check_no_new_privs(int val)
>> +{
>> +	TEST(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0));
>> +	if (TST_RET == val)
>> +		tst_res(TPASS,
>> +			"prctl(PR_GET_NO_NEW_PRIVS) got %d "
>> +			"when no_new_privs was %d", val, val);
>> +	else
>> +		tst_res(TFAIL | TTERRNO,
>> +			"prctl(PR_GET_NO_NEW_PRIVS) expected %d got %ld",
>> +			val, TST_RET);
>> +	return;
> This return is useless.
OK. I will remove it and add check for /proc/[pid]/status for NoNewPrivs.

>> +}
>> +
>> +static void do_prctl(void)
>> +{
>> +	char path[4096];
>> +	char ipc_env_var[1024];
>> +	char *const argv[] = {"prctl06_execve", "parent process", NULL};
>> +	char *const childargv[] = {"prctl06_execve", "child process", NULL};
>> +	char *const envp[] = {"LTP_TEST_ENV_VAR=test", ipc_env_var, NULL };
>> +	cap_t caps = cap_init();
>> +	cap_value_t capList = CAP_SETGID;
>> +	unsigned int num_caps = 1;
>> +	int childpid;
>> +
>> +	cap_set_flag(caps, CAP_EFFECTIVE, num_caps,&capList, CAP_SET);
>> +	cap_set_flag(caps, CAP_INHERITABLE, num_caps,&capList, CAP_SET);
>> +	cap_set_flag(caps, CAP_PERMITTED, num_caps,&capList, CAP_SET);
>> +
>> +	if (cap_set_proc(caps))
>> +		tst_brk(TFAIL | TERRNO,
>> +			"cap_set_flag(CAP_SETGID) failed");
> You cannot use tst_brk with TFAIL, the best you can do here is to use
> tst_ret(TFAIL ... ) then return; as you do below.
>
I got it.

>> +	tst_res(TINFO, "cap_set_flag(CAP_SETGID) succeeded");
>> +
>> +	check_no_new_privs(0);
>> +
>> +	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");
>> +
>> +	check_no_new_privs(1);
>> +
>> +	if (tst_get_path("prctl06_execve", path, sizeof(path)))
>> +		tst_brk(TCONF, "Couldn't find prctl_execve in $PATH");
> If the path to the binary is in $PATH you don't have to execute the
> binary by an absolute path, passing it's name to execve() should
> suffice.
>
  It sounds reasonable. I will pass binary_name directly.

>> +	sprintf(ipc_env_var, IPC_ENV_VAR "=%s", getenv(IPC_ENV_VAR));
>> +	childpid = SAFE_FORK();
>> +	if (childpid == 0) {
>> +		check_no_new_privs(1);
> 		Maybe we can pass a "child" string here and "parent"
> 		string in the cases above so that we can print if the
> 		check was done in child/parent in the tst_res() inside
> 		of this function.
>
Ok. Pass a "child" or "parent" string can make it more clear for user.

>> +		execve(path, childargv, envp);
>> +		tst_brk(TFAIL | TERRNO,
>> +			"child process failed to execute prctl_execve");
>> +
>> +	} else {
>> +		tst_reap_children();
>> +		execve(path, argv, envp);
>> +		tst_brk(TFAIL | TERRNO,
>> +			"parent process failed to execute prctl_execve");
>> +	}
>> +	cap_free(caps);
>> +	return;
>> +}
>> +
>> +static void verify_prctl(void)
>> +{
>> +	int pid;
>> +
>> +	pid = SAFE_FORK();
>> +	if (pid == 0) {
>> +		do_prctl();
>> +		exit(0);
>> +	}
>> +	tst_reap_children();
>> +	return;
>> +}
>> +
>> +static struct tst_test test = {
>> +	.test_all = verify_prctl,
>> +	.forks_child = 1,
>> +	.needs_root = 1,
>> +	.child_needs_reinit = 1,
>> +};
>> diff --git a/testcases/kernel/syscalls/prctl/prctl06_execve.c b/testcases/kernel/syscalls/prctl/prctl06_execve.c
>> new file mode 100644
>> index 000000000..84c28551c
>> --- /dev/null
>> +++ b/testcases/kernel/syscalls/prctl/prctl06_execve.c
>> @@ -0,0 +1,46 @@
>> +// 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<stdlib.h>
>> +#include<sys/types.h>
>> +#include<unistd.h>
>> +#include<errno.h>
>> +#include<pwd.h>
>> +#include "tst_test.h"
>> +
>> +int main(int argc, char **argv)
>> +{
>> +	struct passwd *pw;
>> +	uid_t nobody_uid;
>> +	gid_t nobody_gid;
>> +
>> +	tst_reinit();
>> +	if (argc != 2)
>> +		tst_brk(TFAIL, "argc is %d, expected 2", argc);
>> +
>> +	pw = SAFE_GETPWNAM("nobody");
>> +	nobody_uid = pw->pw_uid;
>> +	nobody_gid = pw->pw_gid;
>> +	/*positive check*/
>> +	TEST(setgid(nobody_gid));
>> +	if (TST_RET == -1)
>> +		tst_res(TFAIL | TTERRNO,
>> +			"%s setgid(%d) isn't permmit", argv[1], nobody_gid);
>> +	else
>> +		tst_res(TPASS, "%s setgid(%d) succeed expectedly",
>> +			argv[1], nobody_gid);
>> +	/*negative check*/
>> +	TEST(setuid(nobody_uid));
>> +	if (TST_RET == -1)
>> +		tst_res(TPASS | TTERRNO,
>> +			"%s setuid(%d) isn't permmit", argv[1], nobody_uid);
>> +	else
>> +		tst_res(TFAIL, " %s setuid(%d) succeed unexpectedly",
>> +			argv[1], nobody_gid);
>> +	return 0;
>> +}
> I do not think that this is actually testing the prctl in question. Here
> we actually check that capabilities were inherited over fork() + exec().
>
> As far as I understand the PR_SET_NO_NEW_PRIVS it has been designed
> expecially to avoid misuse of capabilities associated with a particular
> file.
>
> So the check would have to do:
>
> 1. copy the prctl06_execve binary to a $PWD
> 2. chmod it with S_ISUID, S_ISGID
> 3. the prct06_execve would be executed under user/group nobody
> 4. the prct06_execve itself would check if it gained root priviledges
>
I have seen documention about PR_SET_NO_NEW_PRIVS.  I think your way is right. But I have reset this capabilities
and only keep CAP_SETGID, so when I set no_new_privs to 1, prctl06_execve inherits CAP_SETGID but not gain CAP_SETUID.


> And we can do the same for capablities as well.
>
> I guess that we would have to format a device and mount it with support
> for capabilities for the test, since as far as I can tell you cannot
> usually do neither of set-usr-id or add capabilites to files in /tmp/.
Yes. In /tmp, these make no sense. I will follow by your advise.

Thanks
Yang Xu





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

* [LTP] [PATCH v2] syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
  2019-05-23 11:52 ` Cyril Hrubis
  2019-06-14 10:32   ` Yang Xu
@ 2019-06-19 10:58   ` Yang Xu
  1 sibling, 0 replies; 15+ messages in thread
From: Yang Xu @ 2019-06-19 10:58 UTC (permalink / raw)
  To: ltp

Signed-off-by: Yang Xu <xuyang2018.jy@cn.fujitsu.com>
---
 include/lapi/prctl.h                          |   5 +
 runtest/syscalls                              |   1 +
 testcases/kernel/syscalls/prctl/.gitignore    |   1 +
 testcases/kernel/syscalls/prctl/prctl06.c     | 173 ++++++++++++++++++
 .../kernel/syscalls/prctl/prctl06_execve.c    |  65 +++++++
 5 files changed, 245 insertions(+)
 create mode 100644 testcases/kernel/syscalls/prctl/prctl06.c
 create mode 100644 testcases/kernel/syscalls/prctl/prctl06_execve.c

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 702d6a8c7..a9cad6748 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -860,6 +860,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..f52f6f665 100644
--- a/testcases/kernel/syscalls/prctl/.gitignore
+++ b/testcases/kernel/syscalls/prctl/.gitignore
@@ -3,3 +3,4 @@
 /prctl03
 /prctl04
 /prctl05
+/prctl06
diff --git a/testcases/kernel/syscalls/prctl/prctl06.c b/testcases/kernel/syscalls/prctl/prctl06.c
new file mode 100644
index 000000000..9dd82a241
--- /dev/null
+++ b/testcases/kernel/syscalls/prctl/prctl06.c
@@ -0,0 +1,173 @@
+// 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).
+ *  We also check NoNewPrivs field in /proc/[pid]/status if it supports.
+ */
+
+#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 IPC_ENV_VAR        "LTP_IPC_PATH"
+#define MNTPOINT           "mntpoint"
+#define TESTBIN            "prctl06_execve"
+#define TEST_REL_BIN_DIR   MNTPOINT"/"
+#define SUID_MODE          (S_ISUID|S_ISGID|S_IXUSR|S_IXGRP|S_IXOTH)
+
+static int flag = 1;
+static char CapEff[20];
+
+static void check_proc_field(int val, char *name)
+{
+	char path[50];
+	pid_t pid;
+	int field = 0;
+
+	pid = getpid();
+	sprintf(path, "/proc/%d/status", pid);
+
+	TEST(FILE_LINES_SCANF(path, "NoNewPrivs:%d", &field));
+	if (TST_RET == 1) {
+		tst_res(TCONF,
+			"/proc/[pid]/status doesn't support NoNewPrivs field");
+		flag = 0;
+		return;
+	}
+	if (val == field)
+		tst_res(TPASS, "%s %s NoNewPrivs field expected %d got %d",
+			name, path, val, field);
+	else
+		tst_res(TFAIL, "%s %s NoNewPrivs field expected %d got %d",
+			name, path, val, field);
+
+	SAFE_FILE_LINES_SCANF(path, "CapEff:%s", CapEff);
+}
+
+static 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);
+}
+
+static void do_prctl(void)
+{
+	char ipc_env_var[1024];
+	char *const argv[] = {"prctl06_execve", "parent process", CapEff, NULL};
+	char *const childargv[] = {"prctl06_execve", "child process", CapEff, NULL};
+	char *const envp[] = {"LTP_TEST_ENV_VAR=test", ipc_env_var, NULL };
+	int childpid;
+	struct passwd *pw;
+	uid_t nobody_uid;
+	gid_t nobody_gid;
+
+	pw = SAFE_GETPWNAM("nobody");
+	nobody_uid = pw->pw_uid;
+	nobody_gid = pw->pw_gid;
+
+	check_no_new_privs(0, "parent");
+	tst_res(TINFO,
+		"parent process CapEff %s when no new privs was 0", CapEff);
+
+	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_CHMOD("prctl06_execve", SUID_MODE);
+	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, "child");
+		execve("prctl06_execve", childargv, envp);
+		tst_brk(TFAIL | TTERRNO,
+			"child process failed to execute prctl_execve");
+
+	} else {
+		tst_reap_children();
+		check_no_new_privs(1, "parent");
+		tst_res(TINFO,
+			"parent process CapEff %s when no new privs was 1", CapEff);
+		execve("prctl06_execve", 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);
+	}
+	tst_reap_children();
+}
+
+static void setup(void)
+{
+	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");
+		SAFE_CP(TESTBIN, TEST_REL_BIN_DIR);
+		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_execve.c b/testcases/kernel/syscalls/prctl/prctl06_execve.c
new file mode 100644
index 000000000..6b256afae
--- /dev/null
+++ b/testcases/kernel/syscalls/prctl/prctl06_execve.c
@@ -0,0 +1,65 @@
+// 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 <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include "tst_test.h"
+
+int main(int argc, char **argv)
+{
+	struct passwd *pw;
+	uid_t unknown_uid;
+	gid_t unknown_gid;
+	char path[50];
+	char CapEff[20];
+	pid_t pid;
+
+	tst_reinit();
+	if (argc != 3)
+		tst_brk(TFAIL, "argc is %d, expected 3", argc);
+
+	pid = getpid();
+	sprintf(path, "/proc/%d/status", pid);
+	SAFE_FILE_LINES_SCANF(path, "CapEff:%s", CapEff);
+
+	if (strncmp(CapEff, argv[2], sizeof(CapEff)))
+		tst_res(TFAIL,
+			"%s gains root privileges, current CapEff %s, expect %s",
+			argv[1], CapEff, argv[2]);
+	else
+		tst_res(TPASS,
+			"%s doesn't gain root privileges, CapEff %s",
+			argv[1], CapEff);
+
+	pw = SAFE_GETPWNAM("nobody");
+	unknown_uid = pw->pw_uid + 1;
+	unknown_gid = pw->pw_gid + 1;
+
+	TEST(setgid(unknown_gid));
+	if (TST_RET == -1)
+		tst_res(TPASS,
+			"%s setgid(%d) isn't permmit", argv[1], unknown_gid);
+	else
+		tst_res(TFAIL, "%s setgid(%d) succeed expectedly",
+			argv[1], unknown_gid);
+
+	TEST(setuid(unknown_uid));
+	if (TST_RET == -1)
+		tst_res(TPASS,
+			"%s setuid(%d) isn't permmit", argv[1], unknown_uid);
+	else
+		tst_res(TFAIL, " %s setuid(%d) succeed unexpectedly",
+			argv[1], unknown_gid);
+
+	return 0;
+}
-- 
2.18.1




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

* [LTP] [PATCH RESEND] syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
  2019-07-09 10:53     ` Cyril Hrubis
@ 2019-07-05 22:48       ` Yang Xu
  2019-07-10 10:52         ` Cyril Hrubis
  2019-07-10  9:42       ` [LTP] [PATCH] syscalls/prctl06.c: " Yang Xu
  1 sibling, 1 reply; 15+ messages in thread
From: Yang Xu @ 2019-07-05 22:48 UTC (permalink / raw)
  To: ltp

Signed-off-by: Yang Xu <xuyang2018.jy@cn.fujitsu.com>
---
 include/lapi/prctl.h                          |   5 +
 runtest/syscalls                              |   1 +
 testcases/kernel/syscalls/prctl/.gitignore    |   1 +
 testcases/kernel/syscalls/prctl/prctl06.c     | 173 ++++++++++++++++++
 .../kernel/syscalls/prctl/prctl06_execve.c    |  65 +++++++
 5 files changed, 245 insertions(+)
 create mode 100644 testcases/kernel/syscalls/prctl/prctl06.c
 create mode 100644 testcases/kernel/syscalls/prctl/prctl06_execve.c

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 702d6a8c7..a9cad6748 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -860,6 +860,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..f52f6f665 100644
--- a/testcases/kernel/syscalls/prctl/.gitignore
+++ b/testcases/kernel/syscalls/prctl/.gitignore
@@ -3,3 +3,4 @@
 /prctl03
 /prctl04
 /prctl05
+/prctl06
diff --git a/testcases/kernel/syscalls/prctl/prctl06.c b/testcases/kernel/syscalls/prctl/prctl06.c
new file mode 100644
index 000000000..9dd82a241
--- /dev/null
+++ b/testcases/kernel/syscalls/prctl/prctl06.c
@@ -0,0 +1,173 @@
+// 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).
+ *  We also check NoNewPrivs field in /proc/[pid]/status if it supports.
+ */
+
+#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 IPC_ENV_VAR        "LTP_IPC_PATH"
+#define MNTPOINT           "mntpoint"
+#define TESTBIN            "prctl06_execve"
+#define TEST_REL_BIN_DIR   MNTPOINT"/"
+#define SUID_MODE          (S_ISUID|S_ISGID|S_IXUSR|S_IXGRP|S_IXOTH)
+
+static int flag = 1;
+static char CapEff[20];
+
+static void check_proc_field(int val, char *name)
+{
+	char path[50];
+	pid_t pid;
+	int field = 0;
+
+	pid = getpid();
+	sprintf(path, "/proc/%d/status", pid);
+
+	TEST(FILE_LINES_SCANF(path, "NoNewPrivs:%d", &field));
+	if (TST_RET == 1) {
+		tst_res(TCONF,
+			"/proc/[pid]/status doesn't support NoNewPrivs field");
+		flag = 0;
+		return;
+	}
+	if (val == field)
+		tst_res(TPASS, "%s %s NoNewPrivs field expected %d got %d",
+			name, path, val, field);
+	else
+		tst_res(TFAIL, "%s %s NoNewPrivs field expected %d got %d",
+			name, path, val, field);
+
+	SAFE_FILE_LINES_SCANF(path, "CapEff:%s", CapEff);
+}
+
+static 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);
+}
+
+static void do_prctl(void)
+{
+	char ipc_env_var[1024];
+	char *const argv[] = {"prctl06_execve", "parent process", CapEff, NULL};
+	char *const childargv[] = {"prctl06_execve", "child process", CapEff, NULL};
+	char *const envp[] = {"LTP_TEST_ENV_VAR=test", ipc_env_var, NULL };
+	int childpid;
+	struct passwd *pw;
+	uid_t nobody_uid;
+	gid_t nobody_gid;
+
+	pw = SAFE_GETPWNAM("nobody");
+	nobody_uid = pw->pw_uid;
+	nobody_gid = pw->pw_gid;
+
+	check_no_new_privs(0, "parent");
+	tst_res(TINFO,
+		"parent process CapEff %s when no new privs was 0", CapEff);
+
+	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_CHMOD("prctl06_execve", SUID_MODE);
+	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, "child");
+		execve("prctl06_execve", childargv, envp);
+		tst_brk(TFAIL | TTERRNO,
+			"child process failed to execute prctl_execve");
+
+	} else {
+		tst_reap_children();
+		check_no_new_privs(1, "parent");
+		tst_res(TINFO,
+			"parent process CapEff %s when no new privs was 1", CapEff);
+		execve("prctl06_execve", 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);
+	}
+	tst_reap_children();
+}
+
+static void setup(void)
+{
+	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");
+		SAFE_CP(TESTBIN, TEST_REL_BIN_DIR);
+		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_execve.c b/testcases/kernel/syscalls/prctl/prctl06_execve.c
new file mode 100644
index 000000000..6b256afae
--- /dev/null
+++ b/testcases/kernel/syscalls/prctl/prctl06_execve.c
@@ -0,0 +1,65 @@
+// 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 <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include "tst_test.h"
+
+int main(int argc, char **argv)
+{
+	struct passwd *pw;
+	uid_t unknown_uid;
+	gid_t unknown_gid;
+	char path[50];
+	char CapEff[20];
+	pid_t pid;
+
+	tst_reinit();
+	if (argc != 3)
+		tst_brk(TFAIL, "argc is %d, expected 3", argc);
+
+	pid = getpid();
+	sprintf(path, "/proc/%d/status", pid);
+	SAFE_FILE_LINES_SCANF(path, "CapEff:%s", CapEff);
+
+	if (strncmp(CapEff, argv[2], sizeof(CapEff)))
+		tst_res(TFAIL,
+			"%s gains root privileges, current CapEff %s, expect %s",
+			argv[1], CapEff, argv[2]);
+	else
+		tst_res(TPASS,
+			"%s doesn't gain root privileges, CapEff %s",
+			argv[1], CapEff);
+
+	pw = SAFE_GETPWNAM("nobody");
+	unknown_uid = pw->pw_uid + 1;
+	unknown_gid = pw->pw_gid + 1;
+
+	TEST(setgid(unknown_gid));
+	if (TST_RET == -1)
+		tst_res(TPASS,
+			"%s setgid(%d) isn't permmit", argv[1], unknown_gid);
+	else
+		tst_res(TFAIL, "%s setgid(%d) succeed expectedly",
+			argv[1], unknown_gid);
+
+	TEST(setuid(unknown_uid));
+	if (TST_RET == -1)
+		tst_res(TPASS,
+			"%s setuid(%d) isn't permmit", argv[1], unknown_uid);
+	else
+		tst_res(TFAIL, " %s setuid(%d) succeed unexpectedly",
+			argv[1], unknown_gid);
+
+	return 0;
+}
-- 
2.18.1




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

* [LTP] [PATCH] syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
  2019-06-14 10:32   ` Yang Xu
@ 2019-07-09 10:53     ` Cyril Hrubis
  2019-07-05 22:48       ` [LTP] [PATCH RESEND] " Yang Xu
  2019-07-10  9:42       ` [LTP] [PATCH] syscalls/prctl06.c: " Yang Xu
  0 siblings, 2 replies; 15+ messages in thread
From: Cyril Hrubis @ 2019-07-09 10:53 UTC (permalink / raw)
  To: ltp

Hi!
Can you pretty please resend the prctl06 v2 patch?

I can see it in the patchwork at
http://patchwork.ozlabs.org/patch/1118628/
but that email haven't made it to my mailbox.

-- 
Cyril Hrubis
chrubis@suse.cz

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

* [LTP] [PATCH] syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
  2019-07-09 10:53     ` Cyril Hrubis
  2019-07-05 22:48       ` [LTP] [PATCH RESEND] " Yang Xu
@ 2019-07-10  9:42       ` Yang Xu
  1 sibling, 0 replies; 15+ messages in thread
From: Yang Xu @ 2019-07-10  9:42 UTC (permalink / raw)
  To: ltp

> Hi!
> Can you pretty please resend the prctl06 v2 patch?
>
> I can see it in the patchwork at
> http://patchwork.ozlabs.org/patch/1118628/
> but that email haven't made it to my mailbox.
>
Hi cyril

I have resent it again.

ps:I sent it but email date is 2019/07/06 6:48 (my virtual machine date is incorrect I don't notice it )

Thanks
Yang Xu




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

* [LTP] [PATCH RESEND] syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
  2019-07-05 22:48       ` [LTP] [PATCH RESEND] " Yang Xu
@ 2019-07-10 10:52         ` Cyril Hrubis
  2019-07-11  7:57           ` Yang Xu
  0 siblings, 1 reply; 15+ messages in thread
From: Cyril Hrubis @ 2019-07-10 10:52 UTC (permalink / raw)
  To: ltp

Hi!
>  pread01 pread01
>  pread01_64 pread01_64
> diff --git a/testcases/kernel/syscalls/prctl/.gitignore b/testcases/kernel/syscalls/prctl/.gitignore
> index 9ecaf9854..f52f6f665 100644
> --- a/testcases/kernel/syscalls/prctl/.gitignore
> +++ b/testcases/kernel/syscalls/prctl/.gitignore
> @@ -3,3 +3,4 @@
>  /prctl03
>  /prctl04
>  /prctl05
> +/prctl06

Missing prctl06_execve

> diff --git a/testcases/kernel/syscalls/prctl/prctl06.c b/testcases/kernel/syscalls/prctl/prctl06.c
> new file mode 100644
> index 000000000..9dd82a241
> --- /dev/null
> +++ b/testcases/kernel/syscalls/prctl/prctl06.c
> @@ -0,0 +1,173 @@
> +// 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).
> + *  We also check NoNewPrivs field in /proc/[pid]/status if it supports.
> + */
> +
> +#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 IPC_ENV_VAR        "LTP_IPC_PATH"
> +#define MNTPOINT           "mntpoint"
> +#define TESTBIN            "prctl06_execve"
> +#define TEST_REL_BIN_DIR   MNTPOINT"/"
> +#define SUID_MODE          (S_ISUID|S_ISGID|S_IXUSR|S_IXGRP|S_IXOTH)
> +
> +static int flag = 1;
> +static char CapEff[20];
> +
> +static void check_proc_field(int val, char *name)
> +{
> +	char path[50];
> +	pid_t pid;
> +	int field = 0;

Also it would be a bit cleaner if we do:

	if (flag)
		return;

here and called the function unconditionaly down below.

> +	pid = getpid();
> +	sprintf(path, "/proc/%d/status", pid);
                       ^
		       /proc/self/status ?

> +	TEST(FILE_LINES_SCANF(path, "NoNewPrivs:%d", &field));
> +	if (TST_RET == 1) {
> +		tst_res(TCONF,
> +			"/proc/[pid]/status doesn't support NoNewPrivs field");
> +		flag = 0;
> +		return;
> +	}
> +	if (val == field)
> +		tst_res(TPASS, "%s %s NoNewPrivs field expected %d got %d",
> +			name, path, val, field);
> +	else
> +		tst_res(TFAIL, "%s %s NoNewPrivs field expected %d got %d",
> +			name, path, val, field);
> +
> +	SAFE_FILE_LINES_SCANF(path, "CapEff:%s", CapEff);
> +}
> +
> +static 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);
> +}
> +
> +static void do_prctl(void)
> +{
> +	char ipc_env_var[1024];
> +	char *const argv[] = {"prctl06_execve", "parent process", CapEff, NULL};
> +	char *const childargv[] = {"prctl06_execve", "child process", CapEff, NULL};
> +	char *const envp[] = {"LTP_TEST_ENV_VAR=test", ipc_env_var, NULL };
                                ^
				This is not really needed here. We use
				that only in the execve tests...
> +	int childpid;
> +	struct passwd *pw;
> +	uid_t nobody_uid;
> +	gid_t nobody_gid;
> +
> +	pw = SAFE_GETPWNAM("nobody");
> +	nobody_uid = pw->pw_uid;
> +	nobody_gid = pw->pw_gid;
          ^
	  This can be done once in test setup

> +	check_no_new_privs(0, "parent");
> +	tst_res(TINFO,
> +		"parent process CapEff %s when no new privs was 0", CapEff);
> +
> +	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_CHMOD("prctl06_execve", SUID_MODE);
         ^
	 This can be done in setup() as well.

> +	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, "child");
> +		execve("prctl06_execve", childargv, envp);
> +		tst_brk(TFAIL | TTERRNO,
> +			"child process failed to execute prctl_execve");
> +
> +	} else {
> +		tst_reap_children();
> +		check_no_new_privs(1, "parent");
> +		tst_res(TINFO,
> +			"parent process CapEff %s when no new privs was 1", CapEff);
> +		execve("prctl06_execve", 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);
> +	}
> +	tst_reap_children();

No need to reap children here if you do exit(0) the library will reap
them for you.

> +}
> +
> +static void setup(void)
> +{
> +	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");
> +		SAFE_CP(TESTBIN, TEST_REL_BIN_DIR);
> +		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_execve.c b/testcases/kernel/syscalls/prctl/prctl06_execve.c
> new file mode 100644
> index 000000000..6b256afae
> --- /dev/null
> +++ b/testcases/kernel/syscalls/prctl/prctl06_execve.c
> @@ -0,0 +1,65 @@
> +// 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 <stdlib.h>
> +#include <sys/types.h>
> +#include <unistd.h>
> +#include <errno.h>
> +#include <pwd.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include "tst_test.h"
> +
> +int main(int argc, char **argv)
> +{
> +	struct passwd *pw;
> +	uid_t unknown_uid;
> +	gid_t unknown_gid;
> +	char path[50];
> +	char CapEff[20];
> +	pid_t pid;
> +
> +	tst_reinit();
> +	if (argc != 3)
> +		tst_brk(TFAIL, "argc is %d, expected 3", argc);
> +
> +	pid = getpid();
> +	sprintf(path, "/proc/%d/status", pid);
                        ^
			/proc/self/status

> +	SAFE_FILE_LINES_SCANF(path, "CapEff:%s", CapEff);
> +
> +	if (strncmp(CapEff, argv[2], sizeof(CapEff)))
> +		tst_res(TFAIL,
> +			"%s gains root privileges, current CapEff %s, expect %s",
> +			argv[1], CapEff, argv[2]);
> +	else
> +		tst_res(TPASS,
> +			"%s doesn't gain root privileges, CapEff %s",
> +			argv[1], CapEff);
> +
> +	pw = SAFE_GETPWNAM("nobody");
> +	unknown_uid = pw->pw_uid + 1;
> +	unknown_gid = pw->pw_gid + 1;
> +
> +	TEST(setgid(unknown_gid));
> +	if (TST_RET == -1)
> +		tst_res(TPASS,
> +			"%s setgid(%d) isn't permmit", argv[1], unknown_gid);
> +	else
> +		tst_res(TFAIL, "%s setgid(%d) succeed expectedly",
> +			argv[1], unknown_gid);
> +
> +	TEST(setuid(unknown_uid));
> +	if (TST_RET == -1)
> +		tst_res(TPASS,
> +			"%s setuid(%d) isn't permmit", argv[1], unknown_uid);
> +	else
> +		tst_res(TFAIL, " %s setuid(%d) succeed unexpectedly",
> +			argv[1], unknown_gid);

We are executing setuid binary that was created by root here so
shouldn't we just check that getuid() and getgid() returns 0?

I guess that we can also chown the file to uid=0 and gid=0 once it has
been copied to be 100% sure that the ids are correct in the test setup.

> +	return 0;
> +}

Otherwise the test looks very good.


Also I guess that we need another test that would set the prctl value
and check that it cannot be unset. If you are going to do that please do
that in a separate file, this one is complex enough...

-- 
Cyril Hrubis
chrubis@suse.cz

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

* [LTP] [PATCH RESEND] syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
  2019-07-10 10:52         ` Cyril Hrubis
@ 2019-07-11  7:57           ` Yang Xu
  2019-07-11 11:34             ` Cyril Hrubis
  0 siblings, 1 reply; 15+ messages in thread
From: Yang Xu @ 2019-07-11  7:57 UTC (permalink / raw)
  To: ltp

on 2019/07/10 18:52, Cyril Hrubis wrote:

> Hi!
>>   pread01 pread01
>>   pread01_64 pread01_64
>> diff --git a/testcases/kernel/syscalls/prctl/.gitignore b/testcases/kernel/syscalls/prctl/.gitignore
>> index 9ecaf9854..f52f6f665 100644
>> --- a/testcases/kernel/syscalls/prctl/.gitignore
>> +++ b/testcases/kernel/syscalls/prctl/.gitignore
>> @@ -3,3 +3,4 @@
>>   /prctl03
>>   /prctl04
>>   /prctl05
>> +/prctl06
> Missing prctl06_execve
OK.

>> diff --git a/testcases/kernel/syscalls/prctl/prctl06.c b/testcases/kernel/syscalls/prctl/prctl06.c
>> new file mode 100644
>> index 000000000..9dd82a241
>> --- /dev/null
>> +++ b/testcases/kernel/syscalls/prctl/prctl06.c
>> @@ -0,0 +1,173 @@
>> +// 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).
>> + *  We also check NoNewPrivs field in /proc/[pid]/status if it supports.
>> + */
>> +
>> +#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 IPC_ENV_VAR        "LTP_IPC_PATH"
>> +#define MNTPOINT           "mntpoint"
>> +#define TESTBIN            "prctl06_execve"
>> +#define TEST_REL_BIN_DIR   MNTPOINT"/"
>> +#define SUID_MODE          (S_ISUID|S_ISGID|S_IXUSR|S_IXGRP|S_IXOTH)
>> +
>> +static int flag = 1;
>> +static char CapEff[20];
>> +
>> +static void check_proc_field(int val, char *name)
>> +{
>> +	char path[50];
>> +	pid_t pid;
>> +	int field = 0;
> Also it would be a bit cleaner if we do:
>
> 	if (flag)
> 		return;
>
> here and called the function unconditionaly down below.
>
Call check_proc_field()  when flag is 1 in check_no_new_privs(). So we have avoideded this situation.

>> +	pid = getpid();
>> +	sprintf(path, "/proc/%d/status", pid);
>                         ^
> 		       /proc/self/status ?
>
Yes.

>> +	TEST(FILE_LINES_SCANF(path, "NoNewPrivs:%d",&field));
>> +	if (TST_RET == 1) {
>> +		tst_res(TCONF,
>> +			"/proc/[pid]/status doesn't support NoNewPrivs field");
>> +		flag = 0;
>> +		return;
>> +	}
>> +	if (val == field)
>> +		tst_res(TPASS, "%s %s NoNewPrivs field expected %d got %d",
>> +			name, path, val, field);
>> +	else
>> +		tst_res(TFAIL, "%s %s NoNewPrivs field expected %d got %d",
>> +			name, path, val, field);
>> +
>> +	SAFE_FILE_LINES_SCANF(path, "CapEff:%s", CapEff);
>> +}
>> +
>> +static 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);
>> +}
>> +
>> +static void do_prctl(void)
>> +{
>> +	char ipc_env_var[1024];
>> +	char *const argv[] = {"prctl06_execve", "parent process", CapEff, NULL};
>> +	char *const childargv[] = {"prctl06_execve", "child process", CapEff, NULL};
>> +	char *const envp[] = {"LTP_TEST_ENV_VAR=test", ipc_env_var, NULL };
>                                  ^
> 				This is not really needed here. We use
> 				that only in the execve tests...
OK. I got it.
>> +	int childpid;
>> +	struct passwd *pw;
>> +	uid_t nobody_uid;
>> +	gid_t nobody_gid;
>> +
>> +	pw = SAFE_GETPWNAM("nobody");
>> +	nobody_uid = pw->pw_uid;
>> +	nobody_gid = pw->pw_gid;
>            ^
> 	  This can be done once in test setup
>
>> +	check_no_new_privs(0, "parent");
>> +	tst_res(TINFO,
>> +		"parent process CapEff %s when no new privs was 0", CapEff);
>> +
>> +	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_CHMOD("prctl06_execve", SUID_MODE);
>           ^
> 	 This can be done in setup() as well.
>
OK. I will move them into setup() .

>> +	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, "child");
>> +		execve("prctl06_execve", childargv, envp);
>> +		tst_brk(TFAIL | TTERRNO,
>> +			"child process failed to execute prctl_execve");
>> +
>> +	} else {
>> +		tst_reap_children();
>> +		check_no_new_privs(1, "parent");
>> +		tst_res(TINFO,
>> +			"parent process CapEff %s when no new privs was 1", CapEff);
>> +		execve("prctl06_execve", 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);
>> +	}
>> +	tst_reap_children();
> No need to reap children here if you do exit(0) the library will reap
> them for you.
Yes.

>> +}
>> +
>> +static void setup(void)
>> +{
>> +	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");
>> +		SAFE_CP(TESTBIN, TEST_REL_BIN_DIR);
>> +		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_execve.c b/testcases/kernel/syscalls/prctl/prctl06_execve.c
>> new file mode 100644
>> index 000000000..6b256afae
>> --- /dev/null
>> +++ b/testcases/kernel/syscalls/prctl/prctl06_execve.c
>> @@ -0,0 +1,65 @@
>> +// 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<stdlib.h>
>> +#include<sys/types.h>
>> +#include<unistd.h>
>> +#include<errno.h>
>> +#include<pwd.h>
>> +#include<stdio.h>
>> +#include<string.h>
>> +#include "tst_test.h"
>> +
>> +int main(int argc, char **argv)
>> +{
>> +	struct passwd *pw;
>> +	uid_t unknown_uid;
>> +	gid_t unknown_gid;
>> +	char path[50];
>> +	char CapEff[20];
>> +	pid_t pid;
>> +
>> +	tst_reinit();
>> +	if (argc != 3)
>> +		tst_brk(TFAIL, "argc is %d, expected 3", argc);
>> +
>> +	pid = getpid();
>> +	sprintf(path, "/proc/%d/status", pid);
>                          ^
> 			/proc/self/status
>
Yes.
>> +	SAFE_FILE_LINES_SCANF(path, "CapEff:%s", CapEff);
>> +
>> +	if (strncmp(CapEff, argv[2], sizeof(CapEff)))
>> +		tst_res(TFAIL,
>> +			"%s gains root privileges, current CapEff %s, expect %s",
>> +			argv[1], CapEff, argv[2]);
>> +	else
>> +		tst_res(TPASS,
>> +			"%s doesn't gain root privileges, CapEff %s",
>> +			argv[1], CapEff);
>> +
>> +	pw = SAFE_GETPWNAM("nobody");
>> +	unknown_uid = pw->pw_uid + 1;
>> +	unknown_gid = pw->pw_gid + 1;
>> +
>> +	TEST(setgid(unknown_gid));
>> +	if (TST_RET == -1)
>> +		tst_res(TPASS,
>> +			"%s setgid(%d) isn't permmit", argv[1], unknown_gid);
>> +	else
>> +		tst_res(TFAIL, "%s setgid(%d) succeed expectedly",
>> +			argv[1], unknown_gid);
>> +
>> +	TEST(setuid(unknown_uid));
>> +	if (TST_RET == -1)
>> +		tst_res(TPASS,
>> +			"%s setuid(%d) isn't permmit", argv[1], unknown_uid);
>> +	else
>> +		tst_res(TFAIL, " %s setuid(%d) succeed unexpectedly",
>> +			argv[1], unknown_gid);
> We are executing setuid binary that was created by root here so
> shouldn't we just check that getuid() and getgid() returns 0?
>
I try it.  whether we set or not set new privs, the getuid() or getgid() return nobody in
prctl06_execve.  Or, I misunderstand your advise?

Also, my design is that
1. copy the prctl06_execve binary to a $PWD
2. chmod it with S_ISUID, S_ISGID ,prctl06_exeve can be excuted under nobody
3. prctl06 SETGID and SETUID as nobody, set no_new_privs to 1 and excute prctl06_execve
4. the prct06_execve itself would check if it gained root priviledges(check proc/self/status capeff
and excute setuid and setgid action )


> I guess that we can also chown the file to uid=0 and gid=0 once it has
> been copied to be 100% sure that the ids are correct in the test setup.
Yes.
>> +	return 0;
>> +}
> Otherwise the test looks very good.
>
>
> Also I guess that we need another test that would set the prctl value
> and check that it cannot be unset. If you are going to do that please do
> that in a separate file, this one is complex enough...
OK. I will add this in a separate file.





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

* [LTP] [PATCH RESEND] syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
  2019-07-11  7:57           ` Yang Xu
@ 2019-07-11 11:34             ` Cyril Hrubis
  2019-07-12  4:34               ` Yang Xu
  2019-07-12  4:53               ` [LTP] [PATCH v3] syscalls/prctl06: " Yang Xu
  0 siblings, 2 replies; 15+ messages in thread
From: Cyril Hrubis @ 2019-07-11 11:34 UTC (permalink / raw)
  To: ltp

Hi!
> > We are executing setuid binary that was created by root here so
> > shouldn't we just check that getuid() and getgid() returns 0?
> >
> I try it.  whether we set or not set new privs, the getuid() or getgid() return nobody in
> prctl06_execve.  Or, I misunderstand your advise?

Looking closely into the manuals the setuid and setgid bits are supposed
to set the effective ids, so I guess that the geteuid() and getegid()
will return 0 when the process was executed without the prctl().

-- 
Cyril Hrubis
chrubis@suse.cz

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

* [LTP] [PATCH RESEND] syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
  2019-07-11 11:34             ` Cyril Hrubis
@ 2019-07-12  4:34               ` Yang Xu
  2019-07-12  4:53               ` [LTP] [PATCH v3] syscalls/prctl06: " Yang Xu
  1 sibling, 0 replies; 15+ messages in thread
From: Yang Xu @ 2019-07-12  4:34 UTC (permalink / raw)
  To: ltp

> Hi!
>>> We are executing setuid binary that was created by root here so
>>> shouldn't we just check that getuid() and getgid() returns 0?
>>>
>> I try it.  whether we set or not set new privs, the getuid() or getgid() return nobody in
>> prctl06_execve.  Or, I misunderstand your advise?
> Looking closely into the manuals the setuid and setgid bits are supposed
> to set the effective ids, so I guess that the geteuid() and getegid()
> will return 0 when the process was executed without the prctl().
>

Hi Cyril

Yes. I will remove capeff and setuid,setgid check in my v3 patch.  For the prctl value
and check that it cannot be unset, I think it is a error test as manpage said
"prctl() fails with EINVAL when options is PR_SET_NO_NEW_PRIVS&  arg2 is not equal to 1 or arg3, arg4, or arg5 is nonzero."

I will add it into prctl02.c.  Also, there are many error conditions for prctl. So I think when my prctl07.c are merged into

ltp, I will increase prctl02.c together.

Thanks
Yang Xu




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

* [LTP] [PATCH v3] syscalls/prctl06: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
  2019-07-11 11:34             ` Cyril Hrubis
  2019-07-12  4:34               ` Yang Xu
@ 2019-07-12  4:53               ` Yang Xu
  2019-07-15 15:49                 ` Cyril Hrubis
  1 sibling, 1 reply; 15+ messages in thread
From: Yang Xu @ 2019-07-12  4:53 UTC (permalink / raw)
  To: ltp

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     | 165 ++++++++++++++++++
 .../kernel/syscalls/prctl/prctl06_execve.c    |  52 ++++++
 5 files changed, 225 insertions(+)
 create mode 100644 testcases/kernel/syscalls/prctl/prctl06.c
 create mode 100644 testcases/kernel/syscalls/prctl/prctl06_execve.c

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..71160b1c1
--- /dev/null
+++ b/testcases/kernel/syscalls/prctl/prctl06.c
@@ -0,0 +1,165 @@
+// 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).
+ *  We also check NoNewPrivs field in /proc/[pid]/status if it supports.
+ */
+
+#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 IPC_ENV_VAR        "LTP_IPC_PATH"
+#define MNTPOINT           "mntpoint"
+#define TESTBIN            "prctl06_execve"
+#define TEST_REL_BIN_DIR   MNTPOINT"/"
+#define SUID_MODE          (S_ISUID|S_ISGID|S_IXUSR|S_IXGRP|S_IXOTH)
+
+static uid_t nobody_uid;
+static gid_t nobody_gid;
+static int flag = 1;
+
+static void check_proc_field(int val, char *name)
+{
+	int field = 0;
+	char path[50];
+
+	strcpy(path, "/proc/self/status");
+	TEST(FILE_LINES_SCANF(path, "NoNewPrivs:%d", &field));
+	if (TST_RET == 1) {
+		tst_res(TCONF,
+			"%s doesn't support NoNewPrivs field", path);
+		flag = 0;
+		return;
+	}
+	if (val == field)
+		tst_res(TPASS, "%s %s NoNewPrivs field expected %d got %d",
+			name, path, val, field);
+	else
+		tst_res(TFAIL, "%s %s NoNewPrivs field expected %d got %d",
+			name, path, val, field);
+}
+
+static 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);
+}
+
+static void do_prctl(void)
+{
+	char ipc_env_var[1024];
+	char *const argv[] = {"prctl06_execve", "parent process", NULL};
+	char *const childargv[] = {"prctl06_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, "child");
+		execve("prctl06_execve", childargv, envp);
+		tst_brk(TFAIL | TTERRNO,
+			"child process failed to execute prctl_execve");
+
+	} else {
+		tst_reap_children();
+		check_no_new_privs(1, "parent");
+		execve("prctl06_execve", 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("prctl06_execve", SUID_MODE);
+	SAFE_CHOWN("prctl06_execve", 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_execve.c b/testcases/kernel/syscalls/prctl/prctl06_execve.c
new file mode 100644
index 000000000..92e218020
--- /dev/null
+++ b/testcases/kernel/syscalls/prctl/prctl06_execve.c
@@ -0,0 +1,52 @@
+// 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 <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include "tst_test.h"
+
+int main(int argc, char **argv)
+{
+	char path[50];
+	struct passwd *pw;
+
+	pw = SAFE_GETPWNAM("nobody");
+
+	tst_reinit();
+	if (argc != 2)
+		tst_brk(TFAIL, "argc is %d, expected 2", argc);
+
+	strcpy(path, "/proc/self/status");
+
+	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;
+}
-- 
2.18.1




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

* [LTP] [PATCH v3] syscalls/prctl06: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
  2019-07-12  4:53               ` [LTP] [PATCH v3] syscalls/prctl06: " Yang Xu
@ 2019-07-15 15:49                 ` Cyril Hrubis
  2019-07-16  5:32                   ` Yang Xu
  0 siblings, 1 reply; 15+ messages in thread
From: Cyril Hrubis @ 2019-07-15 15:49 UTC (permalink / raw)
  To: ltp

Hi!
> +static uid_t nobody_uid;
> +static gid_t nobody_gid;
> +static int flag = 1;
> +
> +static void check_proc_field(int val, char *name)
> +{
> +	int field = 0;
> +	char path[50];
> +
> +	strcpy(path, "/proc/self/status");

Can't we just define PROC_STATUS "/proc/self/status" and use that
instead of copying bytes around at runtime?

> +	TEST(FILE_LINES_SCANF(path, "NoNewPrivs:%d", &field));
> +	if (TST_RET == 1) {
> +		tst_res(TCONF,
> +			"%s doesn't support NoNewPrivs field", path);
> +		flag = 0;
> +		return;
> +	}
> +	if (val == field)
> +		tst_res(TPASS, "%s %s NoNewPrivs field expected %d got %d",
> +			name, path, val, field);
> +	else
> +		tst_res(TFAIL, "%s %s NoNewPrivs field expected %d got %d",
> +			name, path, val, field);
> +}
> +
> +static 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);
> +}
> +
> +static void do_prctl(void)
> +{
> +	char ipc_env_var[1024];
> +	char *const argv[] = {"prctl06_execve", "parent process", NULL};
> +	char *const childargv[] = {"prctl06_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, "child");
> +		execve("prctl06_execve", childargv, envp);
> +		tst_brk(TFAIL | TTERRNO,
> +			"child process failed to execute prctl_execve");
> +
> +	} else {
> +		tst_reap_children();
> +		check_no_new_privs(1, "parent");
> +		execve("prctl06_execve", 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("prctl06_execve", SUID_MODE);
> +	SAFE_CHOWN("prctl06_execve", 0, 0);

You are actually changing and executing the wrong file here. You are
working directly with the file in the test temporary directory and not
with the file that has been copied over to the mount point.

I guess that we should define BIN_PATH and use it here and also for the
execve() above.

> +	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_execve.c b/testcases/kernel/syscalls/prctl/prctl06_execve.c
> new file mode 100644
> index 000000000..92e218020
> --- /dev/null
> +++ b/testcases/kernel/syscalls/prctl/prctl06_execve.c
> @@ -0,0 +1,52 @@
> +// 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 <stdlib.h>
> +#include <sys/types.h>
> +#include <unistd.h>
> +#include <errno.h>
> +#include <pwd.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include "tst_test.h"
> +
> +int main(int argc, char **argv)
> +{
> +	char path[50];
> +	struct passwd *pw;
> +
> +	pw = SAFE_GETPWNAM("nobody");
> +
> +	tst_reinit();
> +	if (argc != 2)
> +		tst_brk(TFAIL, "argc is %d, expected 2", argc);
> +
> +	strcpy(path, "/proc/self/status");

Here as well, can we use a macro instead?

> +	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;
> +}

The rest looks good.

-- 
Cyril Hrubis
chrubis@suse.cz

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

* [LTP] [PATCH v3] syscalls/prctl06: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
  2019-07-15 15:49                 ` Cyril Hrubis
@ 2019-07-16  5:32                   ` Yang Xu
  0 siblings, 0 replies; 15+ messages in thread
From: Yang Xu @ 2019-07-16  5:32 UTC (permalink / raw)
  To: ltp


> Hi!
>> +static uid_t nobody_uid;
>> +static gid_t nobody_gid;
>> +static int flag = 1;
>> +
>> +static void check_proc_field(int val, char *name)
>> +{
>> +	int field = 0;
>> +	char path[50];
>> +
>> +	strcpy(path, "/proc/self/status");
> Can't we just define PROC_STATUS "/proc/self/status" and use that
> instead of copying bytes around at runtime?
>
Hi

Yes. I move PROC_STATUS and check functions into prctl06.h because we also need to check field after execve.
Please see my v4 patch.

>> +	TEST(FILE_LINES_SCANF(path, "NoNewPrivs:%d",&field));
>> +	if (TST_RET == 1) {
>> +		tst_res(TCONF,
>> +			"%s doesn't support NoNewPrivs field", path);
>> +		flag = 0;
>> +		return;
>> +	}
>> +	if (val == field)
>> +		tst_res(TPASS, "%s %s NoNewPrivs field expected %d got %d",
>> +			name, path, val, field);
>> +	else
>> +		tst_res(TFAIL, "%s %s NoNewPrivs field expected %d got %d",
>> +			name, path, val, field);
>> +}
>> +
>> +static 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);
>> +}
>> +
>> +static void do_prctl(void)
>> +{
>> +	char ipc_env_var[1024];
>> +	char *const argv[] = {"prctl06_execve", "parent process", NULL};
>> +	char *const childargv[] = {"prctl06_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, "child");
>> +		execve("prctl06_execve", childargv, envp);
>> +		tst_brk(TFAIL | TTERRNO,
>> +			"child process failed to execute prctl_execve");
>> +
>> +	} else {
>> +		tst_reap_children();
>> +		check_no_new_privs(1, "parent");
>> +		execve("prctl06_execve", 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("prctl06_execve", SUID_MODE);
>> +	SAFE_CHOWN("prctl06_execve", 0, 0);
> You are actually changing and executing the wrong file here. You are
> working directly with the file in the test temporary directory and not
> with the file that has been copied over to the mount point.
>
> I guess that we should define BIN_PATH and use it here and also for the
> execve() above.
Yes .

>> +	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_execve.c b/testcases/kernel/syscalls/prctl/prctl06_execve.c
>> new file mode 100644
>> index 000000000..92e218020
>> --- /dev/null
>> +++ b/testcases/kernel/syscalls/prctl/prctl06_execve.c
>> @@ -0,0 +1,52 @@
>> +// 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<stdlib.h>
>> +#include<sys/types.h>
>> +#include<unistd.h>
>> +#include<errno.h>
>> +#include<pwd.h>
>> +#include<stdio.h>
>> +#include<string.h>
>> +#include "tst_test.h"
>> +
>> +int main(int argc, char **argv)
>> +{
>> +	char path[50];
>> +	struct passwd *pw;
>> +
>> +	pw = SAFE_GETPWNAM("nobody");
>> +
>> +	tst_reinit();
>> +	if (argc != 2)
>> +		tst_brk(TFAIL, "argc is %d, expected 2", argc);
>> +
>> +	strcpy(path, "/proc/self/status");
> Here as well, can we use a macro instead?
Yes

>> +	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;
>> +}
> The rest looks good.
>




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

end of thread, other threads:[~2019-07-16  5:32 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-05-09 12:21 [LTP] [PATCH] syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS Yang Xu
2019-05-22 10:18 ` xuyang
2019-05-23 11:52 ` Cyril Hrubis
2019-06-14 10:32   ` Yang Xu
2019-07-09 10:53     ` Cyril Hrubis
2019-07-05 22:48       ` [LTP] [PATCH RESEND] " Yang Xu
2019-07-10 10:52         ` Cyril Hrubis
2019-07-11  7:57           ` Yang Xu
2019-07-11 11:34             ` Cyril Hrubis
2019-07-12  4:34               ` Yang Xu
2019-07-12  4:53               ` [LTP] [PATCH v3] syscalls/prctl06: " Yang Xu
2019-07-15 15:49                 ` Cyril Hrubis
2019-07-16  5:32                   ` Yang Xu
2019-07-10  9:42       ` [LTP] [PATCH] syscalls/prctl06.c: " Yang Xu
2019-06-19 10:58   ` [LTP] [PATCH v2] " Yang Xu

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.