Message ID | 20241108174559.470880-4-siddhesh@sourceware.org |
---|---|
State | New |
Headers | show |
Series | faccessat test improvements | expand |
On 08/11/24 14:45, Siddhesh Poyarekar wrote: > Add a new test tst-faccessat-setuid that iterates through real and > effective UID/GID combination and tests the faccessat() interface for > default and AT_EACCESS flags. > > Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org> LGTM, thanks. Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org> > --- > io/Makefile | 1 + > io/tst-faccessat-setuid.c | 163 ++++++++++++++++++++++++++++++++++++++ > 2 files changed, 164 insertions(+) > create mode 100644 io/tst-faccessat-setuid.c > > diff --git a/io/Makefile b/io/Makefile > index a8d575e9ce..f5813a81c7 100644 > --- a/io/Makefile > +++ b/io/Makefile > @@ -188,6 +188,7 @@ tests := \ > tst-closefrom \ > tst-copy_file_range \ > tst-faccessat \ > + tst-faccessat-setuid \ > tst-fchmod-errors \ > tst-fchmod-fuse \ > tst-fchmodat \ > diff --git a/io/tst-faccessat-setuid.c b/io/tst-faccessat-setuid.c > new file mode 100644 > index 0000000000..aab0261914 > --- /dev/null > +++ b/io/tst-faccessat-setuid.c > @@ -0,0 +1,163 @@ > +/* Smoke test for faccessat with different UID/GID combinations. Needs root > + access. > + Copyright The GNU Toolchain Authors. > + This file is part of the GNU C Library. > + > + The GNU C Library is free software; you can redistribute it and/or > + modify it under the terms of the GNU Lesser General Public > + License as published by the Free Software Foundation; either > + version 2.1 of the License, or (at your option) any later version. > + > + The GNU C Library is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public > + License along with the GNU C Library; if not, see > + <https://www.gnu.org/licenses/>. */ > + > +#include <errno.h> > +#include <fcntl.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <sys/types.h> > +#include <pwd.h> > + > +#include <support/check.h> > +#include <support/namespace.h> > +#include <support/support.h> > +#include <support/temp_file.h> > +#include <support/test-driver.h> > +#include <support/xdirent.h> > +#include <support/xunistd.h> > + > +#define SOMEFILE "some-file" > + > +static int dir_fd; > +uid_t users[3]; > +gid_t groups[3]; > + > +struct test_case > +{ > + int mode; > + uid_t uid; > + uid_t euid; > + gid_t gid; > + gid_t egid; > + int flags; > + bool succeeds; > +}; > + > +static void > +run_one_test_child (void *in) > +{ > + struct test_case *t = (struct test_case *) in; > + > + printf ("TEST: MODE=%s, UID=%d, EUID=%d, GID=%d, EGID=%d, FLAGS=%s: ", > + t->mode == R_OK ? "R_OK" : "W_OK", t->uid, t->euid, t->gid, t->egid, > + t->flags ? "AT_EACCESS" : "0"); > + > + if (setregid (t->gid, t->egid) != 0) > + FAIL_EXIT1 ("Could not change group: %m\n"); > + if (setreuid (t->uid, t->euid) != 0) > + FAIL_EXIT1 ("Could not change user: %m\n"); > + > + if (faccessat (dir_fd, SOMEFILE, t->mode, t->flags) != 0 && t->succeeds) > + FAIL_EXIT1 ("faccessat failed: %m\n"); > + > + if (!t->succeeds && errno != EACCES) > + FAIL_EXIT1 ("Unexpected faccessat failure: %m\n"); > + > + printf ("OK%s\n", !t->succeeds ? " (FAILED with EACCES)" : ""); > +} > + > +static void > +run_one_test (int mode, int u, int eu, int g, int eg, int flags, bool succeeds) > +{ > + struct test_case t = > + {mode, users[u], users[eu], groups[g], groups[eg], flags, succeeds}; > + support_isolate_in_subprocess (run_one_test_child, &t); > +} > + > +static int > +do_test (void) > +{ > + > + /* We need to start as root. */ > + if (getuid () != 0) > + FAIL_UNSUPPORTED ("Test needs to be run as root (UID 0)\n"); > + > + /* Collect 3 distinct users and groups to test with. */ > + struct passwd *ent = NULL; > + int count = 0; > + while ((ent = getpwent ()) != NULL && count < 3) > + { > + if (ent->pw_uid == 0 || ent->pw_gid == 0) > + continue; > + > + int i = count; > + bool skip = false; > + while (i > 0) > + if (groups[--i] == ent->pw_gid) > + skip = true; > + > + if (skip) > + continue; > + > + users[count] = ent->pw_uid; > + groups[count++] = ent->pw_gid; > + } > + > + if (count < 3) > + FAIL_UNSUPPORTED ("Not enough users in the system to do this test\n"); > + > + printf ("Testing with UID/GID:\n"); > + while (--count >= 0) > + printf (" UID: %d, GID: %d\n", users[count], groups[count]); > + printf ("\n"); > + > + char *tempdir = support_create_temp_directory ("tst-faccessat-setuid."); > + dir_fd = xopen (tempdir, O_RDONLY | O_DIRECTORY, 0); > + > + xfchmod (dir_fd, 0777); > + > + /* Now, create a file in it, which will be our test case. */ > + > + int fd = openat (dir_fd, SOMEFILE, O_CREAT|O_RDWR|O_EXCL, 0640); > + if (fd == -1) > + { > + if (errno == ENOSYS) > + FAIL_UNSUPPORTED ("*at functions not supported"); > + > + FAIL_EXIT1 ("file creation failed"); > + } > + xwrite (fd, "hello", 5); > + > + if (fchown (fd, users[0], groups[1]) == -1) > + FAIL_EXIT1 ("fchown failed: %m\n"); > + xclose (fd); > + > + char *somefile = xasprintf ("%s/" SOMEFILE, tempdir); > + add_temp_file (somefile); > + > + /* Finally, run through the combinations. */ > + for (int u = 0; u < 3; u++) > + for (int eu = 0; eu < 3; eu++) > + for (int g = 0; g < 3; g++) > + for (int eg = 0; eg < 3; eg++) > + { > + run_one_test (R_OK, u, eu, g, eg, 0, u == 0 || g == 1); > + run_one_test (W_OK, u, eu, g, eg, 0, u == 0); > + run_one_test (R_OK, u, eu, g, eg, AT_EACCESS, eu == 0 || eg == 1); > + run_one_test (W_OK, u, eu, g, eg, AT_EACCESS, eu == 0); > + } > + > + xclose (dir_fd); > + free (tempdir); > + free (somefile); > + > + return 0; > +} > +#include <support/test-driver.c>
diff --git a/io/Makefile b/io/Makefile index a8d575e9ce..f5813a81c7 100644 --- a/io/Makefile +++ b/io/Makefile @@ -188,6 +188,7 @@ tests := \ tst-closefrom \ tst-copy_file_range \ tst-faccessat \ + tst-faccessat-setuid \ tst-fchmod-errors \ tst-fchmod-fuse \ tst-fchmodat \ diff --git a/io/tst-faccessat-setuid.c b/io/tst-faccessat-setuid.c new file mode 100644 index 0000000000..aab0261914 --- /dev/null +++ b/io/tst-faccessat-setuid.c @@ -0,0 +1,163 @@ +/* Smoke test for faccessat with different UID/GID combinations. Needs root + access. + Copyright The GNU Toolchain Authors. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <pwd.h> + +#include <support/check.h> +#include <support/namespace.h> +#include <support/support.h> +#include <support/temp_file.h> +#include <support/test-driver.h> +#include <support/xdirent.h> +#include <support/xunistd.h> + +#define SOMEFILE "some-file" + +static int dir_fd; +uid_t users[3]; +gid_t groups[3]; + +struct test_case +{ + int mode; + uid_t uid; + uid_t euid; + gid_t gid; + gid_t egid; + int flags; + bool succeeds; +}; + +static void +run_one_test_child (void *in) +{ + struct test_case *t = (struct test_case *) in; + + printf ("TEST: MODE=%s, UID=%d, EUID=%d, GID=%d, EGID=%d, FLAGS=%s: ", + t->mode == R_OK ? "R_OK" : "W_OK", t->uid, t->euid, t->gid, t->egid, + t->flags ? "AT_EACCESS" : "0"); + + if (setregid (t->gid, t->egid) != 0) + FAIL_EXIT1 ("Could not change group: %m\n"); + if (setreuid (t->uid, t->euid) != 0) + FAIL_EXIT1 ("Could not change user: %m\n"); + + if (faccessat (dir_fd, SOMEFILE, t->mode, t->flags) != 0 && t->succeeds) + FAIL_EXIT1 ("faccessat failed: %m\n"); + + if (!t->succeeds && errno != EACCES) + FAIL_EXIT1 ("Unexpected faccessat failure: %m\n"); + + printf ("OK%s\n", !t->succeeds ? " (FAILED with EACCES)" : ""); +} + +static void +run_one_test (int mode, int u, int eu, int g, int eg, int flags, bool succeeds) +{ + struct test_case t = + {mode, users[u], users[eu], groups[g], groups[eg], flags, succeeds}; + support_isolate_in_subprocess (run_one_test_child, &t); +} + +static int +do_test (void) +{ + + /* We need to start as root. */ + if (getuid () != 0) + FAIL_UNSUPPORTED ("Test needs to be run as root (UID 0)\n"); + + /* Collect 3 distinct users and groups to test with. */ + struct passwd *ent = NULL; + int count = 0; + while ((ent = getpwent ()) != NULL && count < 3) + { + if (ent->pw_uid == 0 || ent->pw_gid == 0) + continue; + + int i = count; + bool skip = false; + while (i > 0) + if (groups[--i] == ent->pw_gid) + skip = true; + + if (skip) + continue; + + users[count] = ent->pw_uid; + groups[count++] = ent->pw_gid; + } + + if (count < 3) + FAIL_UNSUPPORTED ("Not enough users in the system to do this test\n"); + + printf ("Testing with UID/GID:\n"); + while (--count >= 0) + printf (" UID: %d, GID: %d\n", users[count], groups[count]); + printf ("\n"); + + char *tempdir = support_create_temp_directory ("tst-faccessat-setuid."); + dir_fd = xopen (tempdir, O_RDONLY | O_DIRECTORY, 0); + + xfchmod (dir_fd, 0777); + + /* Now, create a file in it, which will be our test case. */ + + int fd = openat (dir_fd, SOMEFILE, O_CREAT|O_RDWR|O_EXCL, 0640); + if (fd == -1) + { + if (errno == ENOSYS) + FAIL_UNSUPPORTED ("*at functions not supported"); + + FAIL_EXIT1 ("file creation failed"); + } + xwrite (fd, "hello", 5); + + if (fchown (fd, users[0], groups[1]) == -1) + FAIL_EXIT1 ("fchown failed: %m\n"); + xclose (fd); + + char *somefile = xasprintf ("%s/" SOMEFILE, tempdir); + add_temp_file (somefile); + + /* Finally, run through the combinations. */ + for (int u = 0; u < 3; u++) + for (int eu = 0; eu < 3; eu++) + for (int g = 0; g < 3; g++) + for (int eg = 0; eg < 3; eg++) + { + run_one_test (R_OK, u, eu, g, eg, 0, u == 0 || g == 1); + run_one_test (W_OK, u, eu, g, eg, 0, u == 0); + run_one_test (R_OK, u, eu, g, eg, AT_EACCESS, eu == 0 || eg == 1); + run_one_test (W_OK, u, eu, g, eg, AT_EACCESS, eu == 0); + } + + xclose (dir_fd); + free (tempdir); + free (somefile); + + return 0; +} +#include <support/test-driver.c>
Add a new test tst-faccessat-setuid that iterates through real and effective UID/GID combination and tests the faccessat() interface for default and AT_EACCESS flags. Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org> --- io/Makefile | 1 + io/tst-faccessat-setuid.c | 163 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 io/tst-faccessat-setuid.c