syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
PATCH 1/1 syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
--- 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
include/lapi/prctl.h +5 -0
@@ -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__ */
runtest/syscalls +1 -0
@@ -865,6 +865,7 @@ prctl02 prctl02 prctl03 prctl03 prctl04 prctl04 prctl05 prctl05 +prctl06 prctl06 pread01 pread01 pread01_64 pread01_64
testcases/kernel/syscalls/prctl/.gitignore +1 -0
@@ -3,3 +3,4 @@ /prctl03 /prctl04 /prctl05 +/prctl06
testcases/kernel/syscalls/prctl/Makefile +2 -0
@@ -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
testcases/kernel/syscalls/prctl/prctl06.c new file +117 -0
117 lines not on this page · raw message
testcases/kernel/syscalls/prctl/prctl06_execve.c new file +46 -0
46 lines not on this page · raw message
Signature · 1 line
2.18.1
PATCH 1/1 syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
Hi
Ping.:-)
Quoted 233 lines
Signed-off-by Yang Xu <[email protected]>--- 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.cinclude/lapi/prctl.h +5
-0@@ -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__ */ runtest/syscalls +1
-0@@ -865,6 +865,7 @@ prctl02 prctl02 prctl03 prctl03 prctl04 prctl04 prctl05 prctl05 +prctl06 prctl06 pread01 pread01 pread01_64 pread01_64testcases/kernel/syscalls/prctl/.gitignore +1
-0@@ -3,3 +3,4 @@ /prctl03 /prctl04 /prctl05 +/prctl06testcases/kernel/syscalls/prctl/Makefile +2
-0@@ -20,4 +20,6 @@ top_srcdir ?= ../../../.. include $(top_srcdir)/include/mk/testcases.mk +LDLIBS += $(CAP_LIBS) + include $(top_srcdir)/include/mk/generic_leaf_target.mktestcases/kernel/syscalls/prctl/prctl06.c new file +117
-0@@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +* Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. +* Author: Yang Xu <[email protected]> +* +* 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 , +};testcases/kernel/syscalls/prctl/prctl06_execve.c new file +46
-0@@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +* Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. +* Author: Yang Xu <[email protected]> +* +* 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 ; +}
PATCH 1/1 syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
In reply to Yang XuHi!
Quoted 4 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +4
-0+#define IPC_ENV_VAR "LTP_IPC_PATH" + +static void check_no_new_privs (int val) +{
testcases/kernel/syscalls/prctl/prctl06.c line 43
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +10
-0+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.
Quoted 11 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +11
-0+} + +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 ;
testcases/kernel/syscalls/prctl/prctl06.c line 64
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +10
-0+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.
Quoted 5 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +5
-0+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 ));
testcases/kernel/syscalls/prctl/prctl06.c line 79
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +10
-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.
testcases/kernel/syscalls/prctl/prctl06.c line 84
Quoted 4 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +4
-0+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.
Quoted 75 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +33
-0+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 , +};testcases/kernel/syscalls/prctl/prctl06_execve.c new file +36
-0@@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +* Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. +* Author: Yang Xu <[email protected]> +* +* 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);
testcases/kernel/syscalls/prctl/prctl06_execve.c line 46
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl06_execve.c new file +10
-0+/*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/.
Signature · 2 lines
Cyril Hrubis [email protected]
PATCH 1/1 syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
Hi Cyril
Sorry for the late reply.
Quoted 16 lines
Hi!
Quoted 4 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +4
-0+#define IPC_ENV_VAR "LTP_IPC_PATH" + +static void check_no_new_privs (int val) +{testcases/kernel/syscalls/prctl/prctl06.c line 43
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +10
-0+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.
Quoted 15 lines
Quoted 15 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +15
-0+} + +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 );
Quoted 9 lines
testcases/kernel/syscalls/prctl/prctl06.c line 64
Quoted 6 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +6
-0+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_brkwith TFAIL, the best you can do here is to use tst_ret(TFAIL ... ) then return; as you do below.
I got it.
Quoted 10 lines
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +10
-0+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" );
Quoted 9 lines
testcases/kernel/syscalls/prctl/prctl06.c line 79
Quoted 5 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +5
-0+ +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.
Quoted 9 lines
testcases/kernel/syscalls/prctl/prctl06.c line 84
Quoted 4 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +4
-0+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.
Quoted 92 lines
Quoted 75 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +33
-0+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 , +};testcases/kernel/syscalls/prctl/prctl06_execve.c new file +36
-0@@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +* Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. +* Author: Yang Xu<[email protected]> +* +* 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);testcases/kernel/syscalls/prctl/prctl06_execve.c line 46
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl06_execve.c new file +10
-0+/*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_PRIVSit has been designed expecially to avoid misuse of capabilities associated with a particular file.
Quoted 7 lines
So the check would have to do:
1. copy the
prctl06_execvebinary to a $PWD
2. chmod it withS_ISUID,S_ISGID
3. theprct06_execvewould be executed under user/group nobody
4. theprct06_execveitself 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.
Quoted 5 lines
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
PATCH 1/1 syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
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.
Signature · 2 lines
Cyril Hrubis [email protected]
PATCH RESEND 1/1 syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
--- 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
include/lapi/prctl.h +5 -0
@@ -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__ */
runtest/syscalls +1 -0
@@ -860,6 +860,7 @@ prctl02 prctl02 prctl03 prctl03 prctl04 prctl04 prctl05 prctl05 +prctl06 prctl06 pread01 pread01 pread01_64 pread01_64
testcases/kernel/syscalls/prctl/.gitignore +1 -0
@@ -3,3 +3,4 @@ /prctl03 /prctl04 /prctl05 +/prctl06
testcases/kernel/syscalls/prctl/prctl06.c new file +173 -0
173 lines not on this page · raw message
testcases/kernel/syscalls/prctl/prctl06_execve.c new file +65 -0
65 lines not on this page · raw message
Signature · 1 line
2.18.1
PATCH RESEND 1/1 syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
Hi!
testcases/kernel/syscalls/prctl/.gitignore line 6
Quoted 11 lines
pread01 pread01
pread01_64pread01_64testcases/kernel/syscalls/prctl/.gitignore +1
-0@@ -3,3 +3,4 @@ /prctl03 /prctl04 /prctl05 +/prctl06
Missing prctl06_execve
Quoted 39 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +33
-0@@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +* Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. +* Author: Yang Xu <[email protected]> +* +* 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"/"
testcases/kernel/syscalls/prctl/prctl06.c line 43
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +10
-0+#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.
testcases/kernel/syscalls/prctl/prctl06.c line 45
Quoted 2 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +2
-0+ pid =getpid (); +sprintf (path,"/proc/%d/status" , pid);
^ /proc/self/status ?
Quoted 28 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +28
-0+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 );
testcases/kernel/syscalls/prctl/prctl06.c line 85
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +10
-0+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...
testcases/kernel/syscalls/prctl/prctl06.c line 93
Quoted 8 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +8
-0+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
testcases/kernel/syscalls/prctl/prctl06.c line 106
Quoted 12 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +12
-0+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.
Quoted 23 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +23
-0+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" ); + } +} +
testcases/kernel/syscalls/prctl/prctl06.c line 139
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +10
-0+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.
Quoted 62 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +34
-0+} + +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 , +};testcases/kernel/syscalls/prctl/prctl06_execve.c new file +22
-0@@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +* Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. +* Author: Yang Xu <[email protected]> +* +* 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;
testcases/kernel/syscalls/prctl/prctl06_execve.c line 32
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl06_execve.c new file +10
-0+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
Quoted 20 lines
testcases/kernel/syscalls/prctl/prctl06_execve.c new file +20
-0+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
testcases/kernel/syscalls/prctl/prctl06_execve.c line 62
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl06_execve.c new file +10
-0+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.
testcases/kernel/syscalls/prctl/prctl06_execve.c line 65
Quoted 2 lines
testcases/kernel/syscalls/prctl/prctl06_execve.c new file +2
-0+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...
Signature · 2 lines
Cyril Hrubis [email protected]
PATCH RESEND 1/1 syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
Quoted 13 lines · Cyril Hrubis
Hi!
testcases/kernel/syscalls/prctl/.gitignore line 6
Quoted 11 lines
pread01 pread01
pread01_64pread01_64testcases/kernel/syscalls/prctl/.gitignore +1
-0@@ -3,3 +3,4 @@ /prctl03 /prctl04 /prctl05 +/prctl06Missing
prctl06_execve
OK.
Quoted 51 lines
Quoted 39 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +33
-0@@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +* Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. +* Author: Yang Xu<[email protected]> +* +* 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"/"testcases/kernel/syscalls/prctl/prctl06.c line 43
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +10
-0+#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:
Quoted 5 lines
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.
Quoted 5 lines
testcases/kernel/syscalls/prctl/prctl06.c line 45
Quoted 2 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +2
-0+ pid =getpid (); +sprintf (path,"/proc/%d/status" , pid);^ /proc/self/status ?
Yes.
Quoted 33 lines
Quoted 33 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +33
-0+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 )
Quoted 8 lines
testcases/kernel/syscalls/prctl/prctl06.c line 85
Quoted 5 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +5
-0+{ +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.
Quoted 11 lines
testcases/kernel/syscalls/prctl/prctl06.c line 93
Quoted 8 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +8
-0+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
Quoted 15 lines
testcases/kernel/syscalls/prctl/prctl06.c line 106
Quoted 12 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +12
-0+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 insetup()as well.
OK. I will move them into setup() .
Quoted 27 lines
Quoted 27 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +27
-0+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; +
Quoted 8 lines
testcases/kernel/syscalls/prctl/prctl06.c line 139
Quoted 6 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +6
-0+ 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.
Quoted 66 lines
Quoted 66 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +34
-0+} + +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 , +};testcases/kernel/syscalls/prctl/prctl06_execve.c new file +26
-0@@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +* Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. +* Author: Yang Xu<[email protected]> +* +* 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; +
Quoted 9 lines
testcases/kernel/syscalls/prctl/prctl06_execve.c line 32
Quoted 6 lines
testcases/kernel/syscalls/prctl/prctl06_execve.c new file +6
-0+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.
Quoted 24 lines
Quoted 24 lines
testcases/kernel/syscalls/prctl/prctl06_execve.c new file +24
-0+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));
Quoted 9 lines
testcases/kernel/syscalls/prctl/prctl06_execve.c line 62
Quoted 6 lines
testcases/kernel/syscalls/prctl/prctl06_execve.c new file +6
-0+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()andgetgid()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 )
Quoted 2 lines
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.
Quoted 8 lines
testcases/kernel/syscalls/prctl/prctl06_execve.c line 65
Quoted 2 lines
testcases/kernel/syscalls/prctl/prctl06_execve.c new file +2
-0+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.
PATCH RESEND 1/1 syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
Hi!
Quoted 5 lines
Quoted 3 lines
We are executing setuid binary that was created by root here so shouldn't we just check that
getuid()andgetgid()returns 0?I try it. whether we set or not set new privs, the
getuid()orgetgid()return nobody inprctl06_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().
Signature · 2 lines
Cyril Hrubis [email protected]
PATCH RESEND 1/1 syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
Quoted 10 lines
Hi!
Quoted 5 lines
Quoted 3 lines
We are executing setuid binary that was created by root here so shouldn't we just check that
getuid()andgetgid()returns 0?I try it. whether we set or not set new privs, the
getuid()orgetgid()return nobody inprctl06_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()andgetegid()will return 0 when the process was executed without theprctl().
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
PATCH v3 1/1 syscalls/prctl06: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
In reply to Cyril Hrubis--- 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
include/lapi/prctl.h +5 -0
@@ -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__ */
runtest/syscalls +1 -0
@@ -865,6 +865,7 @@ prctl02 prctl02 prctl03 prctl03 prctl04 prctl04 prctl05 prctl05 +prctl06 prctl06 pread01 pread01 pread01_64 pread01_64
testcases/kernel/syscalls/prctl/.gitignore +2 -0
@@ -3,3 +3,5 @@ /prctl03 /prctl04 /prctl05 +/prctl06 +/prctl06_execve
testcases/kernel/syscalls/prctl/prctl06.c new file +165 -0
165 lines not on this page · raw message
testcases/kernel/syscalls/prctl/prctl06_execve.c new file +52 -0
52 lines not on this page · raw message
Signature · 1 line
2.18.1
PATCH v3 1/1 syscalls/prctl06: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
Hi!
testcases/kernel/syscalls/prctl/prctl06.c line 45
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +10
-0+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?
Quoted 80 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +80
-0+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 )
testcases/kernel/syscalls/prctl/prctl06.c line 135
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +10
-0+{ +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.
Quoted 54 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +29
-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 , +};testcases/kernel/syscalls/prctl/prctl06_execve.c new file +19
-0@@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +* Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. +* Author: Yang Xu <[email protected]> +* +* 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) +{
testcases/kernel/syscalls/prctl/prctl06_execve.c line 29
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl06_execve.c new file +10
-0+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?
Quoted 12 lines
testcases/kernel/syscalls/prctl/prctl06_execve.c new file +12
-0+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 )
testcases/kernel/syscalls/prctl/prctl06_execve.c line 52
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl06_execve.c new file +10
-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.
Signature · 2 lines
Cyril Hrubis [email protected]
PATCH v3 1/1 syscalls/prctl06: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
Quoted 15 lines
Hi!
testcases/kernel/syscalls/prctl/prctl06.c line 45
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +10
-0+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.
Quoted 94 lines
Quoted 80 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +80
-0+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 )testcases/kernel/syscalls/prctl/prctl06.c line 135
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +10
-0+{ +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.
Quoted 2 lines
I guess that we should define
BIN_PATHand use it here and also for theexecve()above.
Yes .
Quoted 57 lines
Quoted 57 lines
testcases/kernel/syscalls/prctl/prctl06.c new file +29
-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 , +};testcases/kernel/syscalls/prctl/prctl06_execve.c new file +22
-0@@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +* Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. +* Author: Yang Xu<[email protected]> +* +* 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; +
Quoted 8 lines
testcases/kernel/syscalls/prctl/prctl06_execve.c line 29
Quoted 7 lines
testcases/kernel/syscalls/prctl/prctl06_execve.c new file +7
-0+ 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
Quoted 24 lines
Quoted 12 lines
testcases/kernel/syscalls/prctl/prctl06_execve.c new file +12
-0+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 )testcases/kernel/syscalls/prctl/prctl06_execve.c line 52
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl06_execve.c new file +10
-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.
PATCH 1/1 syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
In reply to Cyril HrubisQuoted 7 lines
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
PATCH v2 1/1 syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
In reply to Cyril Hrubis--- 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
include/lapi/prctl.h +5 -0
@@ -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__ */
runtest/syscalls +1 -0
@@ -860,6 +860,7 @@ prctl02 prctl02 prctl03 prctl03 prctl04 prctl04 prctl05 prctl05 +prctl06 prctl06 pread01 pread01 pread01_64 pread01_64
testcases/kernel/syscalls/prctl/.gitignore +1 -0
@@ -3,3 +3,4 @@ /prctl03 /prctl04 /prctl05 +/prctl06
testcases/kernel/syscalls/prctl/prctl06.c new file +173 -0
173 lines not on this page · raw message
testcases/kernel/syscalls/prctl/prctl06_execve.c new file +65 -0
65 lines not on this page · raw message
Signature · 1 line
2.18.1