diff mbox series

[v2,3/3] io: Add setuid tests for faccessat

Message ID 20241108174559.470880-4-siddhesh@sourceware.org
State New
Headers show
Series faccessat test improvements | expand

Commit Message

Siddhesh Poyarekar Nov. 8, 2024, 5:45 p.m. UTC
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

Comments

Adhemerval Zanella Nov. 11, 2024, 4:05 p.m. UTC | #1
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 mbox series

Patch

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>