| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- // SPDX-License-Identifier: GPL-2.0
- #define _GNU_SOURCE
- #include <errno.h>
- #include <fcntl.h>
- #include <limits.h>
- #include <sched.h>
- #include <stdarg.h>
- #include <stdbool.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/mount.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <sys/vfs.h>
- #include <unistd.h>
- #ifndef MS_NOSYMFOLLOW
- # define MS_NOSYMFOLLOW 256 /* Do not follow symlinks */
- #endif
- #ifndef ST_NOSYMFOLLOW
- # define ST_NOSYMFOLLOW 0x2000 /* Do not follow symlinks */
- #endif
- #define DATA "/tmp/data"
- #define LINK "/tmp/symlink"
- #define TMP "/tmp"
- static void die(char *fmt, ...)
- {
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- exit(EXIT_FAILURE);
- }
- static void vmaybe_write_file(bool enoent_ok, char *filename, char *fmt,
- va_list ap)
- {
- ssize_t written;
- char buf[4096];
- int buf_len;
- int fd;
- buf_len = vsnprintf(buf, sizeof(buf), fmt, ap);
- if (buf_len < 0)
- die("vsnprintf failed: %s\n", strerror(errno));
- if (buf_len >= sizeof(buf))
- die("vsnprintf output truncated\n");
- fd = open(filename, O_WRONLY);
- if (fd < 0) {
- if ((errno == ENOENT) && enoent_ok)
- return;
- die("open of %s failed: %s\n", filename, strerror(errno));
- }
- written = write(fd, buf, buf_len);
- if (written != buf_len) {
- if (written >= 0) {
- die("short write to %s\n", filename);
- } else {
- die("write to %s failed: %s\n",
- filename, strerror(errno));
- }
- }
- if (close(fd) != 0)
- die("close of %s failed: %s\n", filename, strerror(errno));
- }
- static void maybe_write_file(char *filename, char *fmt, ...)
- {
- va_list ap;
- va_start(ap, fmt);
- vmaybe_write_file(true, filename, fmt, ap);
- va_end(ap);
- }
- static void write_file(char *filename, char *fmt, ...)
- {
- va_list ap;
- va_start(ap, fmt);
- vmaybe_write_file(false, filename, fmt, ap);
- va_end(ap);
- }
- static void create_and_enter_ns(void)
- {
- uid_t uid = getuid();
- gid_t gid = getgid();
- if (unshare(CLONE_NEWUSER) != 0)
- die("unshare(CLONE_NEWUSER) failed: %s\n", strerror(errno));
- maybe_write_file("/proc/self/setgroups", "deny");
- write_file("/proc/self/uid_map", "0 %d 1", uid);
- write_file("/proc/self/gid_map", "0 %d 1", gid);
- if (setgid(0) != 0)
- die("setgid(0) failed %s\n", strerror(errno));
- if (setuid(0) != 0)
- die("setuid(0) failed %s\n", strerror(errno));
- if (unshare(CLONE_NEWNS) != 0)
- die("unshare(CLONE_NEWNS) failed: %s\n", strerror(errno));
- }
- static void setup_symlink(void)
- {
- int data, err;
- data = creat(DATA, O_RDWR);
- if (data < 0)
- die("creat failed: %s\n", strerror(errno));
- err = symlink(DATA, LINK);
- if (err < 0)
- die("symlink failed: %s\n", strerror(errno));
- if (close(data) != 0)
- die("close of %s failed: %s\n", DATA, strerror(errno));
- }
- static void test_link_traversal(bool nosymfollow)
- {
- int link;
- link = open(LINK, 0, O_RDWR);
- if (nosymfollow) {
- if ((link != -1 || errno != ELOOP)) {
- die("link traversal unexpected result: %d, %s\n",
- link, strerror(errno));
- }
- } else {
- if (link < 0)
- die("link traversal failed: %s\n", strerror(errno));
- if (close(link) != 0)
- die("close of link failed: %s\n", strerror(errno));
- }
- }
- static void test_readlink(void)
- {
- char buf[4096];
- ssize_t ret;
- bzero(buf, sizeof(buf));
- ret = readlink(LINK, buf, sizeof(buf));
- if (ret < 0)
- die("readlink failed: %s\n", strerror(errno));
- if (strcmp(buf, DATA) != 0)
- die("readlink strcmp failed: '%s' '%s'\n", buf, DATA);
- }
- static void test_realpath(void)
- {
- char *path = realpath(LINK, NULL);
- if (!path)
- die("realpath failed: %s\n", strerror(errno));
- if (strcmp(path, DATA) != 0)
- die("realpath strcmp failed\n");
- free(path);
- }
- static void test_statfs(bool nosymfollow)
- {
- struct statfs buf;
- int ret;
- ret = statfs(TMP, &buf);
- if (ret)
- die("statfs failed: %s\n", strerror(errno));
- if (nosymfollow) {
- if ((buf.f_flags & ST_NOSYMFOLLOW) == 0)
- die("ST_NOSYMFOLLOW not set on %s\n", TMP);
- } else {
- if ((buf.f_flags & ST_NOSYMFOLLOW) != 0)
- die("ST_NOSYMFOLLOW set on %s\n", TMP);
- }
- }
- static void run_tests(bool nosymfollow)
- {
- test_link_traversal(nosymfollow);
- test_readlink();
- test_realpath();
- test_statfs(nosymfollow);
- }
- int main(int argc, char **argv)
- {
- create_and_enter_ns();
- if (mount("testing", TMP, "ramfs", 0, NULL) != 0)
- die("mount failed: %s\n", strerror(errno));
- setup_symlink();
- run_tests(false);
- if (mount("testing", TMP, "ramfs", MS_REMOUNT|MS_NOSYMFOLLOW, NULL) != 0)
- die("remount failed: %s\n", strerror(errno));
- run_tests(true);
- return EXIT_SUCCESS;
- }
|