1 /* 2 * Copyright (c) International Business Machines Corp., 2001 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 12 * the GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 */ 18 19 /* 20 * Test Name: lchown02 21 * 22 * Test Description: 23 * Verify that, 24 * 1) lchown(2) returns -1 and sets errno to EPERM if the effective user id 25 * of process does not match the owner of the file and the process is 26 * not super user. 27 * 2) lchown(2) returns -1 and sets errno to EACCES if search permission is 28 * denied on a component of the path prefix. 29 * 3) lchown(2) returns -1 and sets errno to EFAULT if pathname points 30 * outside user's accessible address space. 31 * 4) lchown(2) returns -1 and sets errno to ENAMETOOLONG if the pathname 32 * component is too long. 33 * 5) lchown(2) returns -1 and sets errno to ENOTDIR if the directory 34 * component in pathname is not a directory. 35 * 6) lchown(2) returns -1 and sets errno to ENOENT if the specified file 36 * does not exists. 37 * 38 * Expected Result: 39 * lchown() should fail with return value -1 and set expected errno. 40 * 41 * HISTORY 42 * 07/2001 Ported by Wayne Boyer 43 * 11/2010 Rewritten by Cyril Hrubis chrubis (at) suse.cz 44 */ 45 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <unistd.h> 49 #include <fcntl.h> 50 #include <errno.h> 51 #include <string.h> 52 #include <signal.h> 53 #include <grp.h> 54 #include <pwd.h> 55 #include <sys/types.h> 56 #include <sys/stat.h> 57 #include <sys/mman.h> 58 59 #include "test.h" 60 #include "safe_macros.h" 61 #include "compat_16.h" 62 63 #define TEST_USER "nobody" 64 #define MODE_RWX S_IRWXU | S_IRWXG | S_IRWXO 65 #define FILE_MODE S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH 66 #define DIR_TEMP "testdir_1" 67 #define TEST_FILE1 "tfile_1" 68 #define SFILE1 "sfile_1" 69 #define TEST_FILE2 "testdir_1/tfile_2" 70 #define SFILE2 "testdir_1/sfile_2" 71 #define TFILE3 "t_file" 72 #define SFILE3 "t_file/sfile" 73 74 TCID_DEFINE(lchown02); 75 int TST_TOTAL = 7; 76 77 static void setup_eperm(int pos); 78 static void setup_eacces(int pos); 79 static void setup_enotdir(int pos); 80 static void setup_longpath(int pos); 81 static void setup_efault(int pos); 82 static void setup_highaddress(int pos); 83 84 static char path[PATH_MAX + 2]; 85 86 struct test_case_t { 87 char *pathname; 88 char *desc; 89 int exp_errno; 90 void (*setup) (int pos); 91 }; 92 93 static struct test_case_t test_cases[] = { 94 {SFILE1, "Process is not owner/root", EPERM, setup_eperm}, 95 {SFILE2, "Search permission denied", EACCES, setup_eacces}, 96 {NULL, "Address beyond address space", EFAULT, setup_highaddress}, 97 {NULL, "Unaccessible address space", EFAULT, setup_efault}, 98 {path, "Pathname too long", ENAMETOOLONG, setup_longpath}, 99 {SFILE3, "Path contains regular file", ENOTDIR, setup_enotdir}, 100 {"", "Pathname is empty", ENOENT, NULL}, 101 {NULL, NULL, 0, NULL} 102 }; 103 104 static struct passwd *ltpuser; 105 106 static void setup(void); 107 static void cleanup(void); 108 109 int main(int argc, char *argv[]) 110 { 111 int lc; 112 uid_t user_id; 113 gid_t group_id; 114 int i; 115 116 tst_parse_opts(argc, argv, NULL, NULL); 117 118 setup(); 119 120 user_id = geteuid(); 121 UID16_CHECK(user_id, lchown, cleanup); 122 group_id = getegid(); 123 GID16_CHECK(group_id, lchown, cleanup); 124 125 for (lc = 0; TEST_LOOPING(lc); lc++) { 126 tst_count = 0; 127 128 for (i = 0; test_cases[i].desc != NULL; i++) { 129 char *file_name = test_cases[i].pathname; 130 char *test_desc = test_cases[i].desc; 131 132 /* 133 * Call lchown(2) to test different test conditions. 134 * verify that it fails with -1 return value and 135 * sets appropriate errno. 136 */ 137 TEST(LCHOWN(cleanup, file_name, user_id, group_id)); 138 139 /* Check return code from lchown(2) */ 140 if (TEST_RETURN == -1) { 141 if (TEST_ERRNO == test_cases[i].exp_errno) { 142 tst_resm(TPASS, 143 "lchown(2) fails, %s, errno:%d", 144 test_desc, TEST_ERRNO); 145 } else { 146 tst_resm(TFAIL, "lchown(2) fails, %s, " 147 "errno:%d, expected errno:%d", 148 test_desc, TEST_ERRNO, 149 test_cases[i].exp_errno); 150 } 151 } else { 152 tst_resm(TFAIL, "lchown(2) returned %ld, " 153 "expected -1, errno:%d", TEST_RETURN, 154 test_cases[i].exp_errno); 155 } 156 } 157 } 158 159 cleanup(); 160 tst_exit(); 161 } 162 163 static void setup(void) 164 { 165 int i; 166 167 tst_sig(FORK, DEF_HANDLER, cleanup); 168 169 tst_require_root(); 170 171 TEST_PAUSE; 172 173 /* change euid and gid to nobody */ 174 ltpuser = getpwnam(TEST_USER); 175 176 if (ltpuser == NULL) 177 tst_brkm(TBROK, cleanup, "getpwnam failed"); 178 179 if (setgid(ltpuser->pw_uid) == -1) 180 tst_resm(TBROK | TERRNO, "setgid failed"); 181 182 tst_tmpdir(); 183 184 for (i = 0; test_cases[i].desc != NULL; i++) 185 if (test_cases[i].setup != NULL) 186 test_cases[i].setup(i); 187 } 188 189 /* 190 * setup_eperm() - setup function for a test condition for which lchown(2) 191 * returns -1 and sets errno to EPERM. 192 * 193 * Create test file and symlink with uid 0. 194 */ 195 static void setup_eperm(int pos LTP_ATTRIBUTE_UNUSED) 196 { 197 int fd; 198 199 /* create a testfile */ 200 if ((fd = open(TEST_FILE1, O_RDWR | O_CREAT, 0666)) == -1) 201 tst_brkm(TBROK | TERRNO, cleanup, "open failed"); 202 203 SAFE_CLOSE(cleanup, fd); 204 205 /* become root once more */ 206 if (seteuid(0) == -1) 207 tst_resm(TBROK | TERRNO, "setuid(0) failed"); 208 209 /* create symling to testfile */ 210 SAFE_SYMLINK(cleanup, TEST_FILE1, SFILE1); 211 212 /* back to the user nobody */ 213 if (seteuid(ltpuser->pw_uid) == -1) 214 tst_resm(TBROK | TERRNO, "seteuid(%d) failed", ltpuser->pw_uid); 215 } 216 217 /* 218 * setup_eaccess() - setup function for a test condition for which lchown(2) 219 * returns -1 and sets errno to EACCES. 220 * 221 * Create a test directory under temporary directory and create a test file 222 * under this directory with mode "0666" permissions. 223 * Modify the mode permissions on test directory such that process will not 224 * have search permissions on test directory. 225 */ 226 static void setup_eacces(int pos LTP_ATTRIBUTE_UNUSED) 227 { 228 int fd; 229 230 /* create a test directory */ 231 SAFE_MKDIR(cleanup, DIR_TEMP, MODE_RWX); 232 233 /* create a file under test directory */ 234 if ((fd = open(TEST_FILE2, O_RDWR | O_CREAT, 0666)) == -1) 235 tst_brkm(TBROK | TERRNO, cleanup, "open failed"); 236 237 SAFE_CLOSE(cleanup, fd); 238 239 /* create a symlink of testfile */ 240 SAFE_SYMLINK(cleanup, TEST_FILE2, SFILE2); 241 242 /* modify mode permissions on test directory */ 243 SAFE_CHMOD(cleanup, DIR_TEMP, FILE_MODE); 244 } 245 246 /* 247 * setup_efault() -- setup for a test condition where lchown(2) returns -1 and 248 * sets errno to EFAULT. 249 * 250 * Create "bad address" by explicitly mmaping anonymous page that may not be 251 * accesed (see PROT_NONE). 252 */ 253 static void setup_efault(int pos) 254 { 255 char *bad_addr = 0; 256 257 bad_addr = mmap(NULL, 1, PROT_NONE, 258 MAP_PRIVATE_EXCEPT_UCLINUX | MAP_ANONYMOUS, -1, 0); 259 260 if (bad_addr == MAP_FAILED) 261 tst_brkm(TBROK | TERRNO, cleanup, "mmap failed"); 262 263 test_cases[pos].pathname = bad_addr; 264 } 265 266 /* 267 * setup_efault() -- setup for a test condition where lchown(2) returns -1 and 268 * sets errno to EFAULT. 269 * 270 * Use ltp function get_high_address() to compute high address. 271 */ 272 static void setup_highaddress(int pos) 273 { 274 test_cases[pos].pathname = get_high_address(); 275 } 276 277 /* 278 * setup_enotdir() - setup function for a test condition for which chown(2) 279 * returns -1 and sets errno to ENOTDIR. 280 * 281 * Create a regular file "t_file" to call lchown(2) on "t_file/sfile" later. 282 */ 283 static void setup_enotdir(int pos LTP_ATTRIBUTE_UNUSED) 284 { 285 int fd; 286 287 /* create a testfile under temporary directory */ 288 if ((fd = open(TFILE3, O_RDWR | O_CREAT, MODE_RWX)) == -1) { 289 tst_brkm(TBROK | TERRNO, cleanup, "open(2) %s failed", TFILE3); 290 } 291 292 SAFE_CLOSE(cleanup, fd); 293 } 294 295 /* 296 * longpath_setup() - setup to create a node with a name length exceeding 297 * the length of PATH_MAX. 298 */ 299 static void setup_longpath(int pos) 300 { 301 memset(test_cases[pos].pathname, 'a', PATH_MAX + 1); 302 test_cases[pos].pathname[PATH_MAX + 1] = '\0'; 303 } 304 305 static void cleanup(void) 306 { 307 if (seteuid(0) == -1) { 308 tst_resm(TINFO | TERRNO, 309 "seteuid(2) failed to set the effective uid to 0"); 310 } 311 312 tst_rmdir(); 313 } 314