linux/tools/testing/selftests/mount/nosymfollow-test.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#define _GNU_SOURCE
   3#include <errno.h>
   4#include <fcntl.h>
   5#include <limits.h>
   6#include <sched.h>
   7#include <stdarg.h>
   8#include <stdbool.h>
   9#include <stdio.h>
  10#include <stdlib.h>
  11#include <string.h>
  12#include <sys/mount.h>
  13#include <sys/stat.h>
  14#include <sys/types.h>
  15#include <sys/vfs.h>
  16#include <unistd.h>
  17
  18#ifndef MS_NOSYMFOLLOW
  19# define MS_NOSYMFOLLOW 256     /* Do not follow symlinks */
  20#endif
  21
  22#ifndef ST_NOSYMFOLLOW
  23# define ST_NOSYMFOLLOW 0x2000  /* Do not follow symlinks */
  24#endif
  25
  26#define DATA "/tmp/data"
  27#define LINK "/tmp/symlink"
  28#define TMP  "/tmp"
  29
  30static void die(char *fmt, ...)
  31{
  32        va_list ap;
  33
  34        va_start(ap, fmt);
  35        vfprintf(stderr, fmt, ap);
  36        va_end(ap);
  37        exit(EXIT_FAILURE);
  38}
  39
  40static void vmaybe_write_file(bool enoent_ok, char *filename, char *fmt,
  41                va_list ap)
  42{
  43        ssize_t written;
  44        char buf[4096];
  45        int buf_len;
  46        int fd;
  47
  48        buf_len = vsnprintf(buf, sizeof(buf), fmt, ap);
  49        if (buf_len < 0)
  50                die("vsnprintf failed: %s\n", strerror(errno));
  51
  52        if (buf_len >= sizeof(buf))
  53                die("vsnprintf output truncated\n");
  54
  55        fd = open(filename, O_WRONLY);
  56        if (fd < 0) {
  57                if ((errno == ENOENT) && enoent_ok)
  58                        return;
  59                die("open of %s failed: %s\n", filename, strerror(errno));
  60        }
  61
  62        written = write(fd, buf, buf_len);
  63        if (written != buf_len) {
  64                if (written >= 0) {
  65                        die("short write to %s\n", filename);
  66                } else {
  67                        die("write to %s failed: %s\n",
  68                                filename, strerror(errno));
  69                }
  70        }
  71
  72        if (close(fd) != 0)
  73                die("close of %s failed: %s\n", filename, strerror(errno));
  74}
  75
  76static void maybe_write_file(char *filename, char *fmt, ...)
  77{
  78        va_list ap;
  79
  80        va_start(ap, fmt);
  81        vmaybe_write_file(true, filename, fmt, ap);
  82        va_end(ap);
  83}
  84
  85static void write_file(char *filename, char *fmt, ...)
  86{
  87        va_list ap;
  88
  89        va_start(ap, fmt);
  90        vmaybe_write_file(false, filename, fmt, ap);
  91        va_end(ap);
  92}
  93
  94static void create_and_enter_ns(void)
  95{
  96        uid_t uid = getuid();
  97        gid_t gid = getgid();
  98
  99        if (unshare(CLONE_NEWUSER) != 0)
 100                die("unshare(CLONE_NEWUSER) failed: %s\n", strerror(errno));
 101
 102        maybe_write_file("/proc/self/setgroups", "deny");
 103        write_file("/proc/self/uid_map", "0 %d 1", uid);
 104        write_file("/proc/self/gid_map", "0 %d 1", gid);
 105
 106        if (setgid(0) != 0)
 107                die("setgid(0) failed %s\n", strerror(errno));
 108        if (setuid(0) != 0)
 109                die("setuid(0) failed %s\n", strerror(errno));
 110
 111        if (unshare(CLONE_NEWNS) != 0)
 112                die("unshare(CLONE_NEWNS) failed: %s\n", strerror(errno));
 113}
 114
 115static void setup_symlink(void)
 116{
 117        int data, err;
 118
 119        data = creat(DATA, O_RDWR);
 120        if (data < 0)
 121                die("creat failed: %s\n", strerror(errno));
 122
 123        err = symlink(DATA, LINK);
 124        if (err < 0)
 125                die("symlink failed: %s\n", strerror(errno));
 126
 127        if (close(data) != 0)
 128                die("close of %s failed: %s\n", DATA, strerror(errno));
 129}
 130
 131static void test_link_traversal(bool nosymfollow)
 132{
 133        int link;
 134
 135        link = open(LINK, 0, O_RDWR);
 136        if (nosymfollow) {
 137                if ((link != -1 || errno != ELOOP)) {
 138                        die("link traversal unexpected result: %d, %s\n",
 139                                        link, strerror(errno));
 140                }
 141        } else {
 142                if (link < 0)
 143                        die("link traversal failed: %s\n", strerror(errno));
 144
 145                if (close(link) != 0)
 146                        die("close of link failed: %s\n", strerror(errno));
 147        }
 148}
 149
 150static void test_readlink(void)
 151{
 152        char buf[4096];
 153        ssize_t ret;
 154
 155        bzero(buf, sizeof(buf));
 156
 157        ret = readlink(LINK, buf, sizeof(buf));
 158        if (ret < 0)
 159                die("readlink failed: %s\n", strerror(errno));
 160        if (strcmp(buf, DATA) != 0)
 161                die("readlink strcmp failed: '%s' '%s'\n", buf, DATA);
 162}
 163
 164static void test_realpath(void)
 165{
 166        char *path = realpath(LINK, NULL);
 167
 168        if (!path)
 169                die("realpath failed: %s\n", strerror(errno));
 170        if (strcmp(path, DATA) != 0)
 171                die("realpath strcmp failed\n");
 172
 173        free(path);
 174}
 175
 176static void test_statfs(bool nosymfollow)
 177{
 178        struct statfs buf;
 179        int ret;
 180
 181        ret = statfs(TMP, &buf);
 182        if (ret)
 183                die("statfs failed: %s\n", strerror(errno));
 184
 185        if (nosymfollow) {
 186                if ((buf.f_flags & ST_NOSYMFOLLOW) == 0)
 187                        die("ST_NOSYMFOLLOW not set on %s\n", TMP);
 188        } else {
 189                if ((buf.f_flags & ST_NOSYMFOLLOW) != 0)
 190                        die("ST_NOSYMFOLLOW set on %s\n", TMP);
 191        }
 192}
 193
 194static void run_tests(bool nosymfollow)
 195{
 196        test_link_traversal(nosymfollow);
 197        test_readlink();
 198        test_realpath();
 199        test_statfs(nosymfollow);
 200}
 201
 202int main(int argc, char **argv)
 203{
 204        create_and_enter_ns();
 205
 206        if (mount("testing", TMP, "ramfs", 0, NULL) != 0)
 207                die("mount failed: %s\n", strerror(errno));
 208
 209        setup_symlink();
 210        run_tests(false);
 211
 212        if (mount("testing", TMP, "ramfs", MS_REMOUNT|MS_NOSYMFOLLOW, NULL) != 0)
 213                die("remount failed: %s\n", strerror(errno));
 214
 215        run_tests(true);
 216
 217        return EXIT_SUCCESS;
 218}
 219