read_all: Drop privileges
PATCH 1/1 read_all: Drop privileges
The LTP is usually run as root, which allows read_all_dev to read files which are usually protected from being read at random. This patch introduces the -p switch to read_all which is used to drop privileges (switch to the nobody user) for the read_all_dev test.
If -p is set, but the current user does not have the capabilities to change the uid and gid, then the test will continue under the current user. This allows the most common scenarios to work as expected, but may cause difficulties for someone running the LTP under a semi-privileged user.
--- runtest/fs | 2 +- testcases/kernel/fs/read_all/read_all.c | 26 +++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-)
runtest/fs +1 -1
@@ -69,7 +69,7 @@ fs_di fs_di -d $TMPDIR # Was not sure why it should reside in runtest/crashme and won´t get tested ever proc01 proc01 -m 128 -read_all_dev read_all -d /dev -e '/dev/watchdog?(0)' -q -r 10 +read_all_dev read_all -d /dev -p -q -r 10 read_all_proc read_all -d /proc -q -r 10 read_all_sys read_all -d /sys -q -r 10
testcases/kernel/fs/read_all/read_all.c +25 -1
@@ -50,6 +50,7 @@#include <fnmatch.h> #include <semaphore.h> #include <ctype.h> +#include <pwd.h> #include "tst_test.h"
@@ -88,6 +89,7 @@ static long worker_count;static char *str_max_workers;static long max_workers =15 ;static struct worker *workers; +static char *drop_privs;static struct tst_option options[] = { {"v" , &verbose,
@@ -104,6 +106,8 @@ static struct tst_option options[] = { "-w count Set the worker count limit, the default is 15."}, {"W:" , &str_worker_count, "-W count Override the worker count. Ignores (-w) and the processor count."}, + {"p", &drop_privs, + "-p Drop privileges; switch to the nobody user."}, {NULL, NULL, NULL} };
@@ -247,6 +251,24 @@ static int worker_run(struct worker *self)return 0 ; } +static void maybe_drop_privs (void ) +{ +struct passwd *nobody; + +if (!drop_privs) +return ; + + nobody =SAFE_GETPWNAM ("nobody" ); + +TEST (setgid (nobody->pw_gid)); +if (TEST_RETURN <0 &&TEST_ERRNO !=EPERM ) +tst_res (TBROK |TTERRNO ,"Failed to use nobody gid" ); + +TEST (setuid (nobody->pw_uid)); +if (TEST_RETURN <0 &&TEST_ERRNO !=EPERM ) +tst_res (TBROK |TTERRNO ,"Failed to use nobody uid" ); +} +static void spawn_workers (void ) {int i;
@@ -257,8 +279,10 @@ static void spawn_workers(void)for (i =0 ; i < worker_count; i++) { wa[i].q =queue_init (); wa[i].pid =SAFE_FORK (); -if (!wa[i].pid) +if (!wa[i].pid) { +maybe_drop_privs ();exit (worker_run (wa + i)); + } } }
Signature · 1 line
2.16.3
PATCH 1/1 read_all: Drop privileges
Hi!
testcases/kernel/fs/read_all/read_all.c static int worker_run(struct worker *self) · line 265
Quoted 12 lines
testcases/kernel/fs/read_all/read_all.c +12
-0+static void maybe_drop_privs (void ) +{ +struct passwd *nobody; + +if (!drop_privs) +return ; + + nobody =SAFE_GETPWNAM ("nobody" ); + +TEST (setgid (nobody->pw_gid)); +if (TEST_RETURN <0 &&TEST_ERRNO !=EPERM ) +tst_res (TBROK |TTERRNO ,"Failed to use nobody gid" );
^
Shouldn't this be tst_brk()?
testcases/kernel/fs/read_all/read_all.c static int worker_run(struct worker *self) · line 269
Quoted 3 lines
testcases/kernel/fs/read_all/read_all.c +3
-0+TEST (setuid (nobody->pw_uid)); +if (TEST_RETURN <0 &&TEST_ERRNO !=EPERM ) +tst_res (TBROK |TTERRNO ,"Failed to use nobody uid" );
^
And here as well?
Otherwise it looks fine.
Signature · 2 lines
Cyril Hrubis [email protected]
PATCH 1/1 read_all: Drop privileges
Hello,
Quoted 16 lines · Cyril Hrubis
Hi!
testcases/kernel/fs/read_all/read_all.c static int worker_run(struct worker *self) · line 265
Quoted 12 lines
testcases/kernel/fs/read_all/read_all.c +12
-0+static void maybe_drop_privs (void ) +{ +struct passwd *nobody; + +if (!drop_privs) +return ; + + nobody =SAFE_GETPWNAM ("nobody" ); + +TEST (setgid (nobody->pw_gid)); +if (TEST_RETURN <0 &&TEST_ERRNO !=EPERM ) +tst_res (TBROK |TTERRNO ,"Failed to use nobody gid" );^
Shouldn't this betst_brk()?
Quoted 7 lines
testcases/kernel/fs/read_all/read_all.c static int worker_run(struct worker *self) · line 269
Quoted 3 lines
testcases/kernel/fs/read_all/read_all.c +3
-0+TEST (setuid (nobody->pw_uid)); +if (TEST_RETURN <0 &&TEST_ERRNO !=EPERM ) +tst_res (TBROK |TTERRNO ,"Failed to use nobody uid" );^
And here as well?Otherwise it looks fine.
Well spotted, yes it should.
Signature · 2 lines
Thank you, Richard.
PATCH 1/1 read_all: Drop privileges
Hi!
Quoted 3 lines
Quoted 1 line
Otherwise it looks fine.
Well spotted, yes it should.
I will push it with that change then, thanks.
Signature · 2 lines
Cyril Hrubis [email protected]
PATCH 1/1 read_all: Drop privileges
Hi Richard, Cyril,
Quoted 6 lines · Cyril Hrubis
Hi!
Quoted 3 lines
Quoted 1 line
Otherwise it looks fine.
Well spotted, yes it should.
I will push it with that change then, thanks.
I can confirm that with the update, the system no longer generates aborts due to /dev/port access.
Thanks for the quick turn around.
Punit
PATCH 1/1 read_all: Drop privileges
Hello,
Quoted 9 lines · Punit Agrawal
Hi Richard, Cyril,
Quoted 5 lines · Cyril Hrubis
Hi!
Quoted 3 lines
Quoted 1 line
Otherwise it looks fine.
Well spotted, yes it should.
Quoted 8 lines
Quoted 1 line
I will push it with that change then, thanks.
I can confirm that with the update, the system no longer generates aborts due to
/dev/portaccess.Thanks for the quick turn around.
Punit
Great, thanks for testing it.
Signature · 2 lines
Thank you, Richard.
PATCH 1/1 read_all: Drop privileges
In reply to Cyril HrubisHi Richard, Cyril,
Quoted 6 lines · Cyril Hrubis
Hi!
Quoted 3 lines
Quoted 1 line
Otherwise it looks fine.
Well spotted, yes it should.
I will push it with that change then, thanks.
I can confirm that with the change the system no longer generates aborts while reading nodes in /dev.
Thanks for pushing this through before the release.
Punit
PATCH 1/1 read_all: Drop privileges
In reply to Richard PalethorpeHi Richard,
If the permission of /dev/watchdog was 0600(default permission on RHEL7), we could not read /dev/watchdog as nobody user and returned EACCES as expected.
If the permission of /dev/watchdog was 0660(default permission on RHEL6), Reading /dev/watchdog as nobody user failed, but still led to system reboot.
I think reading /dev/watchdog as nobody user should get EACCES even if the permission is 0660, but i am not sure whether this is a watchdog bug in kernel or not.
Thanks,
Xiao Yang
Quoted 29 lines · Richard Palethorpe
Hello,
Quoted 16 lines · Cyril Hrubis
Hi!
testcases/kernel/fs/read_all/read_all.c static int worker_run(struct worker *self) · line 265
Quoted 12 lines
testcases/kernel/fs/read_all/read_all.c +12
-0+static void maybe_drop_privs (void ) +{ +struct passwd *nobody; + +if (!drop_privs) +return ; + + nobody =SAFE_GETPWNAM ("nobody" ); + +TEST (setgid (nobody->pw_gid)); +if (TEST_RETURN <0 &&TEST_ERRNO !=EPERM ) +tst_res (TBROK |TTERRNO ,"Failed to use nobody gid" );^
Shouldn't this betst_brk()?Quoted 7 lines
testcases/kernel/fs/read_all/read_all.c static int worker_run(struct worker *self) · line 269
Quoted 3 lines
testcases/kernel/fs/read_all/read_all.c +3
-0+TEST (setuid (nobody->pw_uid)); +if (TEST_RETURN <0 &&TEST_ERRNO !=EPERM ) +tst_res (TBROK |TTERRNO ,"Failed to use nobody uid" );^
And here as well?Otherwise it looks fine.
Well spotted, yes it should.
PATCH 1/1 read_all: Drop privileges
Hi!
Quoted 2 lines
If the permission of
/dev/watchdogwas 0660(default permission on RHEL6), Reading/dev/watchdogas nobody user failed, but still led to system reboot.
If unprivileged user can reboot the system it's a bug.
Signature · 2 lines
Cyril Hrubis [email protected]
PATCH 1/1 read_all: Drop privileges
Quoted 4 lines · Cyril Hrubis
Hi!
Quoted 2 lines
If the permission of
/dev/watchdogwas 0660(default permission on RHEL6), Reading/dev/watchdogas nobody user failed, but still led to system reboot.If unprivileged user can reboot the system it's a bug.
Hi Cyril,
Sorry, it seems a bug in open(2) instead of watchdog.
You can reproduce the issue by running the following test.c:
----------------------------------------------------------------------------------------------------------
#include <errno.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pwd.h>
#include <unistd.h>
#include <stdlib.h>
static void switch_privs(void)
{
struct passwd *nobody;
int ret;
nobody = getpwnam("nobody");
if (nobody == NULL) {
printf("getpwnam(nobody) failed with errno %d\n", errno);
exit(1);
}
ret = setgid(nobody->pw_gid);
if (ret < 0) {
printf("Failed to use nobody gid with errno %d\n", errno);
exit(1);
}
ret = setuid(nobody->pw_uid);
if (ret < 0) {
printf("Failed to use nobody uid with errno %d\n", errno);
exit(1);
}
}int main(void)
{
int fd;
umask(0);
fd = open("testfile", O_RDWR | O_CREAT, 0660);
if (fd < 0) {
printf("open(testfile) failed with errno %d\n", errno);
return 1;
}
close(fd);
switch_privs();
fd = open("testfile", O_RDWR);
if (fd < 0) {
printf("open(testfile) failed with errno %d\n", errno);
return 1;
}
printf("open(testfile) succeeded unexpectedly\n");
close(fd);
}
------------------------------------------------------------------------------------------------------------
# gcc -o test test.c
# ./test
open(testfile) succeeded unexpectedlyWe created a test file with 0660 mode as root user, and opened the test file as nobody user switched by setuid() and setgid(). Running this test got success rather than EACCES. Do you think this is a bug or i misunderstand the permissions of file?
Thanks,
Xiao Yang
PATCH 1/1 read_all: Drop privileges
Hi!
Quoted 1 line
Sorry, it seems a bug in open(2) instead of watchdog.
Looks like the list of supplementary groups is at fault here.
On my system I do have in /etc/group:
root:x:0:root
Which means that among other groups root has root suplementary group set when logged in.
Which means that even when a program sets it's user and group ids to nobody the root still stays in the list of supplementary groups, which then is matched for files with root group ownership and hence we can stil open the file.
Adding setgroups(0, NULL); to switch_privs() in your program "fixes" the behavior and we get EPERM as expected. And I guess that we should patch the read_all to do the same, which should fix your problem. I will apply the fix.
Signature · 2 lines
Cyril Hrubis [email protected]
PATCH 1/1 read_all: Drop privileges
Quoted 16 lines · Cyril Hrubis
Hi!
Quoted 1 line
Sorry, it seems a bug in open(2) instead of watchdog.
Looks like the list of supplementary groups is at fault here.
On my system I do have in
/etc/group:root:x:0:root
Which means that among other groups root has root suplementary group set when logged in.
Which means that even when a program sets it's user and group ids to nobody the root still stays in the list of supplementary groups, which then is matched for files with root group ownership and hence we can stil open the file.
Quoted 4 lines
Adding
setgroups(0, NULL); toswitch_privs()in your program "fixes" the behavior and we get EPERM as expected. And I guess that we should patch theread_allto do the same, which should fix your problem. I will apply the fix.
Hi Cyril,
Thanks for your detailed explanation.
I will send the fix patch as you suggested.
Thanks,
Xiao Yang
PATCH 1/1 fs/read_all: Clear suplementary groups before droping privileges
In reply to Cyril HrubisCurrent user(e.g. root) has its own suplementary group set when logged in. Which means that even when a program sets it's user and group ids to nobody the current group still stays in the list of supplementary groups, which then is matched for files with the current group ownership and hence we can still access the file.
For example, if /dev/watchdog has root group ownership and rw group permissions, running read_all_dev can still open /dev/watchdog and reboot system even after switching user and group ids from root to nobody.
We need to clear suplementary groups before droping privileges and keep the same rule as commit 1f011e5 if current user doesn't have the capabilities to clear suplementary groups.
--- testcases/kernel/fs/read_all/read_all.c | 6 ++++++ 1 file changed, 6 insertions(+)
testcases/kernel/fs/read_all/read_all.c +6 -0
@@ -258,6 +258,12 @@ static void maybe_drop_privs(void)if (!drop_privs)return ; +TEST (setgroups (0 ,NULL )); +if (TEST_RETURN <0 &&TEST_ERRNO !=EPERM ) { +tst_brk (TBROK |TTERRNO , +"Failed to clear suplementary group set" ); + } + nobody =SAFE_GETPWNAM ("nobody" );TEST (setgid (nobody->pw_gid));
Signature · 1 line
1.8.3.1
PATCH 1/1 fs/read_all: Clear suplementary groups before droping privileges
Hello,
Quoted 25 lines · Xiao Yang
Current user(e.g. root) has its own suplementary group set when logged in. Which means that even when a program sets it's user and group ids to nobody the current group still stays in the list of supplementary groups, which then is matched for files with the current group ownership and hence we can still access the file.
For example, if
/dev/watchdoghas root group ownership and rw group permissions, runningread_all_devcan still open/dev/watchdogand reboot system even after switching user and group ids from root to nobody.We need to clear suplementary groups before droping privileges and keep the same rule as commit 1f011e5 if current user doesn't have the capabilities to clear suplementary groups.
Signed-off-by Xiao Yang <[email protected]>--- testcases/kernel/fs/read_all/read_all.c | 6 ++++++ 1 file changed, 6 insertions(+)testcases/kernel/fs/read_all/read_all.c +0
-0@@ -258,6 +258,12 @@ static void maybe_drop_privs(void)if (!drop_privs)return ;
testcases/kernel/fs/read_all/read_all.c static void maybe_drop_privs(void) · line 269
Quoted 10 lines
testcases/kernel/fs/read_all/read_all.c +6
-0+TEST (setgroups (0 ,NULL )); +if (TEST_RETURN <0 &&TEST_ERRNO !=EPERM ) { +tst_brk (TBROK |TTERRNO , +"Failed to clear suplementary group set" ); + } + nobody =SAFE_GETPWNAM ("nobody" );TEST (setgid (nobody->pw_gid));
LGTM!
Signature · 2 lines
Thank you, Richard.
PATCH 1/1 fs/read_all: Clear suplementary groups before droping privileges
Hi!
Quoted 1 line
LGTM!
We may as well think about safe groups for the program, looking at my /dev/ it may sense to add auxiliary groups disk and tty so that we tests these devices as well.
Signature · 2 lines
Cyril Hrubis [email protected]
PATCH 1/1 fs/read_all: Clear suplementary groups before droping privileges
In reply to Xiao YangHi!
I've added #include <grp.h> to the patch so that we get definition for the setgrp() call and pushed, thanks.
Signature · 2 lines
Cyril Hrubis [email protected]
PATCH v2 1/1 read_all: Drop privileges
In reply to Cyril HrubisThe LTP is usually run as root, which allows read_all_dev to read files which are usually protected from being read at random. This patch introduces the -p switch to read_all which is used to drop privileges (switch to the nobody user) for the read_all_dev test.
If -p is set, but the current user does not have the capabilities to change the uid and gid, then the test will continue under the current user. This allows the most common scenarios to work as expected, but may cause difficulties for someone running the LTP under a semi-privileged user.
--- runtest/fs | 2 +- testcases/kernel/fs/read_all/read_all.c | 26 +++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-)
runtest/fs +1 -1
@@ -69,7 +69,7 @@ fs_di fs_di -d $TMPDIR # Was not sure why it should reside in runtest/crashme and won´t get tested ever proc01 proc01 -m 128 -read_all_dev read_all -d /dev -e '/dev/watchdog?(0)' -q -r 10 +read_all_dev read_all -d /dev -p -q -r 10 read_all_proc read_all -d /proc -q -r 10 read_all_sys read_all -d /sys -q -r 10
testcases/kernel/fs/read_all/read_all.c +25 -1
@@ -50,6 +50,7 @@#include <fnmatch.h> #include <semaphore.h> #include <ctype.h> +#include <pwd.h> #include "tst_test.h"
@@ -88,6 +89,7 @@ static long worker_count;static char *str_max_workers;static long max_workers =15 ;static struct worker *workers; +static char *drop_privs;static struct tst_option options[] = { {"v" , &verbose,
@@ -104,6 +106,8 @@ static struct tst_option options[] = { "-w count Set the worker count limit, the default is 15."}, {"W:" , &str_worker_count, "-W count Override the worker count. Ignores (-w) and the processor count."}, + {"p", &drop_privs, + "-p Drop privileges; switch to the nobody user."}, {NULL, NULL, NULL} };
@@ -247,6 +251,24 @@ static int worker_run(struct worker *self)return 0 ; } +static void maybe_drop_privs (void ) +{ +struct passwd *nobody; + +if (!drop_privs) +return ; + + nobody =SAFE_GETPWNAM ("nobody" ); + +TEST (setgid (nobody->pw_gid)); +if (TEST_RETURN <0 &&TEST_ERRNO !=EPERM ) +tst_brk (TBROK |TTERRNO ,"Failed to use nobody gid" ); + +TEST (setuid (nobody->pw_uid)); +if (TEST_RETURN <0 &&TEST_ERRNO !=EPERM ) +tst_brk (TBROK |TTERRNO ,"Failed to use nobody uid" ); +} +static void spawn_workers (void ) {int i;
@@ -257,8 +279,10 @@ static void spawn_workers(void)for (i =0 ; i < worker_count; i++) { wa[i].q =queue_init (); wa[i].pid =SAFE_FORK (); -if (!wa[i].pid) +if (!wa[i].pid) { +maybe_drop_privs ();exit (worker_run (wa + i)); + } } }
Signature · 1 line
2.16.3