1 /* 2 * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 of 7 * the License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it would be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * 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 the Free Software Foundation, 16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17 * 18 * Author: 19 * Alexey Kodanev <alexey.kodanev (at) oracle.com> 20 * 21 * Test checks following preconditions: 22 * 23 * Symlinks 24 * --------- 25 * Users who own sticky world-writable directory can't follow symlinks 26 * inside that directory if their don't own ones. All other users can follow. 27 * 28 * Hardlinks 29 * --------- 30 * Hard links restriction applies only to non-privileged users. Only 31 * non-privileged user can't create hard links to files if he isn't owner 32 * of the file or he doesn't have write access to the file. 33 */ 34 35 #define _GNU_SOURCE 36 #include <sys/types.h> 37 #include <sys/stat.h> 38 #include <pwd.h> 39 #include <unistd.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <signal.h> 44 45 #include "test.h" 46 #include "safe_macros.h" 47 48 char *TCID = "prot_hsymlinks"; 49 int TST_TOTAL = 396; 50 51 /* create 3 files and 1 dir in each base dir */ 52 #define MAX_FILES_CREATED 4 53 #define MAX_PATH 128 54 #define MAX_CMD_LEN 64 55 #define MAX_USER_NAME 16 56 57 enum { 58 ROOT = 0, 59 TEST_USER, 60 USERS_NUM 61 }; 62 63 #define BASE_DIR_NUM (USERS_NUM + 1) 64 /* 65 * max test files and directories 66 * that will be created during the test 67 * is't not include symlinks and hardlinks 68 * and base directories 69 */ 70 #define MAX_ENTITIES (MAX_FILES_CREATED * BASE_DIR_NUM) 71 72 struct dir_params { 73 char path[MAX_PATH]; 74 int world_writable; 75 int sticky; 76 int owner; 77 }; 78 79 static struct dir_params bdirs[BASE_DIR_NUM]; 80 81 static const char file_ext[] = ".hs"; 82 83 enum { 84 IS_FILE = 0, 85 IS_DIRECTORY, 86 }; 87 88 struct user_file { 89 char path[MAX_PATH]; 90 int is_dir; 91 }; 92 93 struct test_user { 94 char name[MAX_USER_NAME]; 95 struct user_file file[MAX_ENTITIES]; 96 int num; 97 }; 98 99 static struct test_user users[USERS_NUM]; 100 101 struct link_info { 102 char path[MAX_PATH]; 103 int owner; 104 int source_owner; 105 int in_world_write; 106 int in_sticky; 107 int is_dir; 108 int dir_owner; 109 }; 110 111 /* test flags */ 112 enum { 113 CANNOT_FOLLOW = -1, 114 CAN_FOLLOW = 0, 115 }; 116 117 enum { 118 CANNOT_CREATE = -1, 119 CAN_CREATE = 0, 120 }; 121 122 static char *tmp_user_name; 123 static char *default_user = "hsym"; 124 static int nflag; 125 static int skip_cleanup; 126 127 static const option_t options[] = { 128 {"u:", &nflag, &tmp_user_name}, /* -u #user name */ 129 {"s", &skip_cleanup, NULL}, 130 {NULL, NULL, NULL} 131 }; 132 /* full length of the test tmpdir path in /tmp */ 133 static size_t cwd_offset; 134 135 static const char hrdlink_proc_path[] = "/proc/sys/fs/protected_hardlinks"; 136 static const char symlink_proc_path[] = "/proc/sys/fs/protected_symlinks"; 137 138 static void help(void); 139 static void setup(int argc, char *argv[]); 140 static void cleanup(void); 141 142 static void test_user_cmd(const char *user_cmd); 143 144 static int disable_protected_slinks; 145 static int disable_protected_hlinks; 146 147 /* 148 * changes links restrictions 149 * @param value can be: 150 * 0 - restrictions is off 151 * 1 - restrictions is on 152 */ 153 static void switch_protected_slinks(int value); 154 static void switch_protected_hlinks(int value); 155 156 static int get_protected_slinks(void); 157 static int get_protected_hlinks(void); 158 159 static void create_link_path(char *buffer, int size, const char *path); 160 static int create_check_hlinks(const struct user_file *ufile, int owner); 161 static int create_check_slinks(const struct user_file *ufile, int owner); 162 static int check_symlink(const struct link_info *li); 163 static int try_open(const char *name, int mode); 164 /* try to open symlink in diff modes */ 165 static int try_symlink(const char *name); 166 167 static int test_run(void); 168 static void init_base_dirs(void); 169 static void init_files_dirs(void); 170 171 /* change effective user id and group id by name 172 * pass NULL to set root 173 */ 174 static void set_user(const char *name); 175 176 /* add new created files to user struct */ 177 static void ufiles_add(int usr, char *path, int type); 178 179 int main(int argc, char *argv[]) 180 { 181 setup(argc, argv); 182 183 test_run(); 184 185 cleanup(); 186 187 tst_exit(); 188 } 189 190 static void setup(int argc, char *argv[]) 191 { 192 tst_parse_opts(argc, argv, options, &help); 193 194 tst_require_root(); 195 196 if (tst_kvercmp(3, 7, 0) < 0) 197 tst_brkm(TCONF, NULL, 198 "Test must be run with kernel 3.7 or newer"); 199 200 if (eaccess("/etc/passwd", W_OK)) { 201 tst_brkm(TCONF, NULL, 202 "/etc/passwd is not accessible"); 203 } 204 205 /* initialize user names */ 206 strcpy(users[ROOT].name, "root"); 207 208 if (tmp_user_name == NULL) 209 tmp_user_name = default_user; 210 snprintf(users[TEST_USER].name, MAX_USER_NAME, "%s", tmp_user_name); 211 212 tst_sig(FORK, DEF_HANDLER, cleanup); 213 214 test_user_cmd("useradd"); 215 /* 216 * enable hardlinks and symlinks restrictions, 217 * it's not defualt but have to check 218 */ 219 if (!get_protected_hlinks()) { 220 switch_protected_hlinks(1); 221 disable_protected_hlinks = 1; 222 } 223 if (!get_protected_slinks()) { 224 switch_protected_slinks(1); 225 disable_protected_slinks = 1; 226 } 227 228 tst_tmpdir(); 229 230 init_base_dirs(); 231 232 init_files_dirs(); 233 } 234 235 static int test_run(void) 236 { 237 tst_resm(TINFO, " --- HARDLINKS AND SYMLINKS RESTRICTIONS TEST ---\n"); 238 239 int result_slink = 0, 240 result_hlink = 0, 241 usr, 242 file; 243 244 const struct user_file *ufile; 245 /* 246 * create symlinks and hardlinks from each user's files 247 * to each world writable directory 248 */ 249 for (usr = 0; usr < USERS_NUM; ++usr) { 250 /* get all users files and directories */ 251 for (file = 0; file < users[usr].num; ++file) { 252 ufile = &users[usr].file[file]; 253 result_slink |= create_check_slinks(ufile, usr); 254 result_hlink |= create_check_hlinks(ufile, usr); 255 } 256 } 257 258 /* final results */ 259 tst_resm(TINFO, "All test-cases have been completed, summary:\n" 260 " - symlinks test:\t%s\n" 261 " - hardlinks test:\t%s", 262 (result_slink == 1) ? "FAIL" : "PASS", 263 (result_hlink == 1) ? "FAIL" : "PASS"); 264 265 return result_slink | result_hlink; 266 } 267 268 static void cleanup(void) 269 { 270 /* call cleanup function only once */ 271 static int first_call = 1; 272 if (!first_call) 273 return; 274 first_call = 0; 275 276 set_user(NULL); 277 278 if (skip_cleanup) 279 return; 280 281 test_user_cmd("userdel -r"); 282 283 if (disable_protected_hlinks) { 284 tst_resm(TINFO, "Disable protected hardlinks mode back"); 285 switch_protected_hlinks(0); 286 } 287 if (disable_protected_slinks) { 288 tst_resm(TINFO, "Disable protected symlinks mode back"); 289 switch_protected_slinks(0); 290 } 291 292 tst_rmdir(); 293 } 294 295 static int get_protected_hlinks(void) 296 { 297 int value = 0; 298 SAFE_FILE_SCANF(cleanup, hrdlink_proc_path, "%d", &value); 299 return value; 300 } 301 302 static int get_protected_slinks(void) 303 { 304 int value = 0; 305 SAFE_FILE_SCANF(cleanup, symlink_proc_path, "%d", &value); 306 return value; 307 } 308 309 static void switch_protected_hlinks(int value) 310 { 311 SAFE_FILE_PRINTF(cleanup, hrdlink_proc_path, "%d", value == 1); 312 } 313 314 static void switch_protected_slinks(int value) 315 { 316 SAFE_FILE_PRINTF(cleanup, symlink_proc_path, "%d", value == 1); 317 } 318 319 static void test_user_cmd(const char *user_cmd) 320 { 321 char cmd[MAX_CMD_LEN]; 322 snprintf(cmd, MAX_CMD_LEN, "%s %s", user_cmd, users[TEST_USER].name); 323 if (system(cmd) != 0) { 324 tst_brkm(TBROK, cleanup, "Failed to run cmd: %s %s", 325 user_cmd, users[TEST_USER].name); 326 } 327 } 328 329 static void help(void) 330 { 331 printf(" -s Skip cleanup.\n"); 332 printf(" -u #user name : Define test user\n"); 333 } 334 335 static void create_sub_dir(const char *path, 336 struct dir_params *bdir, mode_t mode) 337 { 338 snprintf(bdir->path, MAX_PATH, "%s/tmp_%s", 339 path, users[bdir->owner].name); 340 SAFE_MKDIR(cleanup, bdir->path, mode); 341 342 if (bdir->sticky) 343 mode |= S_ISVTX; 344 chmod(bdir->path, mode); 345 } 346 347 static void init_base_dirs(void) 348 { 349 char *cwd = tst_get_tmpdir(); 350 cwd_offset = strlen(cwd); 351 352 mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO; 353 chmod(cwd, mode); 354 355 strcpy(bdirs[0].path, cwd); 356 free(cwd); 357 358 bdirs[0].sticky = 0; 359 bdirs[0].world_writable = 1; 360 361 /* create subdir for each user */ 362 int dir, usr; 363 for (usr = 0; usr < USERS_NUM; ++usr) { 364 set_user(users[usr].name); 365 dir = usr + 1; 366 bdirs[dir].sticky = 1; 367 bdirs[dir].world_writable = 1; 368 bdirs[dir].owner = usr; 369 370 create_sub_dir(bdirs[0].path, &bdirs[dir], mode); 371 } 372 } 373 374 static void init_files_dirs(void) 375 { 376 unsigned int dir, usr; 377 /* create all other dirs and files */ 378 for (dir = 0; dir < ARRAY_SIZE(bdirs); ++dir) { 379 for (usr = 0; usr < USERS_NUM; ++usr) { 380 set_user(users[usr].name); 381 char path[MAX_PATH]; 382 383 /* create file in the main directory */ 384 snprintf(path, MAX_PATH, "%s/%s%s", 385 bdirs[dir].path, users[usr].name, file_ext); 386 ufiles_add(usr, path, IS_FILE); 387 388 /* create file with S_IWOTH bit set */ 389 strcat(path, "_w"); 390 ufiles_add(usr, path, IS_FILE); 391 392 chmod(path, S_IRUSR | S_IRGRP | S_IWOTH | S_IROTH); 393 394 /* create sub directory */ 395 snprintf(path, MAX_PATH, "%s/%s", bdirs[dir].path, 396 users[usr].name); 397 ufiles_add(usr, path, IS_DIRECTORY); 398 399 /* create local file inside sub directory */ 400 snprintf(path + strlen(path), MAX_PATH - strlen(path), 401 "/local_%s%s", users[usr].name, file_ext); 402 ufiles_add(usr, path, IS_FILE); 403 } 404 } 405 } 406 407 static void ufiles_add(int usr, char *path, int type) 408 { 409 int file = users[usr].num; 410 411 if (file >= MAX_ENTITIES) 412 tst_brkm(TBROK, cleanup, "Unexpected number of files"); 413 414 struct user_file *ufile = &users[usr].file[file]; 415 416 if (type == IS_FILE) 417 SAFE_TOUCH(cleanup, path, 0644, NULL); 418 else 419 SAFE_MKDIR(cleanup, path, 0755); 420 421 strcpy(ufile->path, path); 422 423 ufile->is_dir = (type == IS_DIRECTORY); 424 ++users[usr].num; 425 } 426 427 static void create_link_path(char *buffer, int size, const char *path) 428 { 429 /* to make sure name is unique */ 430 static int count; 431 ++count; 432 433 /* construct link name */ 434 snprintf(buffer, size, "%s/link_%d", path, count); 435 } 436 437 static int create_check_slinks(const struct user_file *ufile, int owner) 438 { 439 int result = 0, usr; 440 unsigned int dir; 441 for (dir = 0; dir < ARRAY_SIZE(bdirs); ++dir) { 442 for (usr = 0; usr < USERS_NUM; ++usr) { 443 /* set user who will create symlink */ 444 set_user(users[usr].name); 445 446 struct link_info slink_info; 447 create_link_path(slink_info.path, MAX_PATH, 448 bdirs[dir].path); 449 450 slink_info.owner = usr; 451 slink_info.source_owner = owner; 452 slink_info.in_world_write = bdirs[dir].world_writable; 453 slink_info.in_sticky = bdirs[dir].sticky; 454 slink_info.dir_owner = bdirs[dir].owner; 455 456 if (symlink(ufile->path, slink_info.path) == -1) { 457 tst_brkm(TBROK, cleanup, 458 "Can't create symlink: %s", 459 slink_info.path); 460 } 461 result |= check_symlink(&slink_info); 462 } 463 } 464 return result; 465 } 466 467 static int create_check_hlinks(const struct user_file *ufile, int owner) 468 { 469 int result = 0, usr; 470 unsigned int dir; 471 for (dir = 0; dir < ARRAY_SIZE(bdirs); ++dir) { 472 for (usr = 0; usr < USERS_NUM; ++usr) { 473 /* can't create hardlink to directory */ 474 if (ufile->is_dir) 475 continue; 476 /* set user who will create hardlink */ 477 set_user(users[usr].name); 478 479 struct link_info hlink_info; 480 create_link_path(hlink_info.path, MAX_PATH, 481 bdirs[dir].path); 482 483 int can_write = try_open(ufile->path, O_WRONLY) == 0; 484 485 int tst_flag = (can_write || usr == owner || 486 usr == ROOT) ? CAN_CREATE : CANNOT_CREATE; 487 488 int fail; 489 fail = tst_flag != link(ufile->path, hlink_info.path); 490 491 result |= fail; 492 tst_resm((fail) ? TFAIL : TPASS, 493 "Expect: %s create hardlink '...%s' to '...%s', " 494 "owner '%s', curr.user '%s', w(%d)", 495 (tst_flag == CAN_CREATE) ? "can" : "can't", 496 ufile->path + cwd_offset, 497 hlink_info.path + cwd_offset, 498 users[owner].name, users[usr].name, 499 can_write); 500 } 501 } 502 return result; 503 } 504 505 static int check_symlink(const struct link_info *li) 506 { 507 int symlink_result = 0; 508 int usr; 509 for (usr = 0; usr < USERS_NUM; ++usr) { 510 set_user(users[usr].name); 511 int tst_flag = (usr == li->dir_owner && 512 li->in_world_write && li->in_sticky && 513 usr != li->owner) ? CANNOT_FOLLOW : CAN_FOLLOW; 514 515 int fail = tst_flag != try_symlink(li->path); 516 517 symlink_result |= fail; 518 519 tst_resm((fail) ? TFAIL : TPASS, 520 "Expect: %s follow symlink '...%s', " 521 "owner '%s', src.owner '%s', " 522 "curr.user '%s', dir.owner '%s'", 523 (tst_flag == CAN_FOLLOW) ? "can" : "can't", 524 li->path + cwd_offset, users[li->owner].name, 525 users[li->source_owner].name, users[usr].name, 526 users[li->dir_owner].name); 527 } 528 return symlink_result; 529 } 530 531 /* differenet modes to try in the test */ 532 static const int o_modes[] = { 533 O_RDONLY, 534 O_WRONLY, 535 O_RDWR, 536 O_RDONLY | O_NONBLOCK | O_DIRECTORY, 537 }; 538 539 static int try_symlink(const char *name) 540 { 541 unsigned int mode; 542 for (mode = 0; mode < ARRAY_SIZE(o_modes); ++mode) { 543 if (try_open(name, o_modes[mode]) != -1) 544 return CAN_FOLLOW; 545 } 546 547 return CANNOT_FOLLOW; 548 } 549 550 static int try_open(const char *name, int mode) 551 { 552 int fd = open(name, mode); 553 554 if (fd == -1) 555 return fd; 556 557 if (close(fd) == -1) 558 tst_brkm(TBROK, cleanup, "Can't close file: %s", name); 559 560 return 0; 561 } 562 563 static void set_user(const char *name) 564 { 565 uid_t user_id = 0; 566 gid_t user_gr = 0; 567 568 if (name != NULL) { 569 struct passwd *pswd = getpwnam(name); 570 571 if (pswd == 0) { 572 tst_brkm(TBROK, cleanup, 573 "Failed to find user '%s'", name); 574 } 575 user_id = pswd->pw_uid; 576 user_gr = pswd->pw_gid; 577 } 578 579 SAFE_SETEGID(cleanup, user_gr); 580 SAFE_SETEUID(cleanup, user_id); 581 } 582