Home | History | Annotate | Download | only in prot_hsymlinks
      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