1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) International Business Machines Corp., 2001 4 * 07/2001 Ported by Wayne Boyer 5 * Copyright (c) 2019 SUSE LLC <mdoucha@suse.cz> 6 */ 7 8/* 9 * Check that the chdir() syscall returns correct value and error code 10 * in various situations when called with root privileges 11 */ 12 13#include <stdio.h> 14#include <stdlib.h> 15#include <sys/types.h> 16#include <unistd.h> 17#include <pwd.h> 18 19#include "tst_test.h" 20 21#define MNTPOINT "mntpoint" 22 23#define FILE_NAME "testfile" 24#define DIR_NAME "subdir" 25#define BLOCKED_NAME "keep_out" 26#define LINK_NAME1 "symloop" 27#define LINK_NAME2 "symloop2" 28#define TESTUSER "nobody" 29 30static char *workdir; 31static int skip_symlinks, skip_blocked; 32static struct passwd *ltpuser; 33 34static char *file_name; 35static char *blocked_name; 36static char *dir_name; 37static char *cwd_name; 38static char *parent_name; 39static char *root_name; 40static char *missing_name; 41static char *link_name; 42 43static struct test_case { 44 char **name; 45 int root_ret, root_err, nobody_ret, nobody_err; 46} testcase_list[] = { 47 {&file_name, -1, ENOTDIR, -1, ENOTDIR}, 48 {&blocked_name, 0, 0, -1, EACCES}, 49 {&dir_name, 0, 0, 0, 0}, 50 {&cwd_name, 0, 0, 0, 0}, 51 {&parent_name, 0, 0, 0, 0}, 52 {&root_name, 0, 0, 0, 0}, 53 {&missing_name, -1, ENOENT, -1, ENOENT}, 54 {&link_name, -1, ELOOP, -1, ELOOP}, 55}; 56 57static void setup(void) 58{ 59 char *cwd; 60 int fd; 61 struct stat statbuf; 62 63 umask(0); 64 65 cwd = SAFE_GETCWD(NULL, 0); 66 workdir = SAFE_MALLOC(strlen(cwd) + strlen(MNTPOINT) + 2); 67 sprintf(workdir, "%s/%s", cwd, MNTPOINT); 68 free(cwd); 69 SAFE_CHDIR(workdir); 70 71 SAFE_MKDIR(DIR_NAME, 0755); 72 SAFE_MKDIR(BLOCKED_NAME, 0644); 73 74 /* FAT and NTFS override file and directory permissions */ 75 SAFE_STAT(BLOCKED_NAME, &statbuf); 76 skip_blocked = statbuf.st_mode & 0111; 77 skip_symlinks = 0; 78 TEST(symlink(LINK_NAME1, LINK_NAME2)); 79 80 if (!TST_RET) 81 SAFE_SYMLINK(LINK_NAME2, LINK_NAME1); 82 else if (TST_RET == -1 && (TST_ERR == EPERM || TST_ERR == ENOSYS)) 83 skip_symlinks = 1; 84 else 85 tst_brk(TBROK | TTERRNO, "Cannot create symlinks"); 86 87 fd = SAFE_CREAT(FILE_NAME, 0644); 88 SAFE_CLOSE(fd); 89 90 if (!ltpuser) 91 ltpuser = SAFE_GETPWNAM(TESTUSER); 92} 93 94static void check_result(const char *user, const char *name, int retval, 95 int experr) 96{ 97 if (TST_RET != retval) { 98 tst_res(TFAIL | TTERRNO, 99 "%s: chdir(\"%s\") returned unexpected value %ld", 100 user, name, TST_RET); 101 return; 102 } 103 104 if (TST_RET != 0 && TST_ERR != experr) { 105 tst_res(TFAIL | TTERRNO, 106 "%s: chdir(\"%s\") returned unexpected error", user, 107 name); 108 return; 109 } 110 111 tst_res(TPASS | TTERRNO, "%s: chdir(\"%s\") returned correct value", 112 user, name); 113} 114 115static void run(unsigned int n) 116{ 117 struct test_case *tc = testcase_list + n; 118 119 tst_res(TINFO, "Testing '%s'", *tc->name); 120 121 if (tc->root_err == ELOOP && skip_symlinks) { 122 tst_res(TCONF, "Skipping symlink loop test, not supported"); 123 return; 124 } 125 126 /* Reset current directory to mountpoint */ 127 SAFE_CHDIR(workdir); 128 129 TEST(chdir(*tc->name)); 130 check_result("root", *tc->name, tc->root_ret, tc->root_err); 131 132 if (tc->nobody_err == EACCES && skip_blocked) { 133 tst_res(TCONF, "Skipping unprivileged permission test, " 134 "FS mangles dir mode"); 135 return; 136 } 137 138 SAFE_CHDIR(workdir); 139 SAFE_SETEUID(ltpuser->pw_uid); 140 TEST(chdir(*tc->name)); 141 SAFE_SETEUID(0); 142 check_result(TESTUSER, *tc->name, tc->nobody_ret, tc->nobody_err); 143} 144 145static void cleanup(void) 146{ 147 SAFE_CHDIR(".."); 148 free(workdir); 149} 150 151static struct tst_test test = { 152 .needs_root = 1, 153 .mount_device = 1, 154 .mntpoint = MNTPOINT, 155 .all_filesystems = 1, 156 .test = run, 157 .tcnt = ARRAY_SIZE(testcase_list), 158 .setup = setup, 159 .cleanup = cleanup, 160 .bufs = (struct tst_buffers []) { 161 {&file_name, .str = FILE_NAME}, 162 {&blocked_name, .str = BLOCKED_NAME}, 163 {&dir_name, .str = DIR_NAME}, 164 {&cwd_name, .str = "."}, 165 {&parent_name, .str = ".."}, 166 {&root_name, .str = "/"}, 167 {&missing_name, .str = "does_not_exist"}, 168 {&link_name, .str = LINK_NAME1}, 169 {} 170 } 171}; 172