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 			SAFE_SYMLINK(cleanup, ufile->path, slink_info.path);
    457 			result |= check_symlink(&slink_info);
    458 		}
    459 	}
    460 	return result;
    461 }
    462 
    463 static int create_check_hlinks(const struct user_file *ufile, int owner)
    464 {
    465 	int result = 0, usr;
    466 	unsigned int dir;
    467 	for (dir = 0; dir < ARRAY_SIZE(bdirs); ++dir) {
    468 		for (usr = 0; usr < USERS_NUM; ++usr) {
    469 			/* can't create hardlink to directory */
    470 			if (ufile->is_dir)
    471 				continue;
    472 			/* set user who will create hardlink */
    473 			set_user(users[usr].name);
    474 
    475 			struct link_info hlink_info;
    476 			create_link_path(hlink_info.path, MAX_PATH,
    477 				bdirs[dir].path);
    478 
    479 			int can_write = try_open(ufile->path, O_WRONLY) == 0;
    480 
    481 			int tst_flag = (can_write || usr == owner ||
    482 				usr == ROOT) ? CAN_CREATE : CANNOT_CREATE;
    483 
    484 			int fail;
    485 			fail = tst_flag != link(ufile->path, hlink_info.path);
    486 
    487 			result |= fail;
    488 			tst_resm((fail) ? TFAIL : TPASS,
    489 				"Expect: %s create hardlink '...%s' to '...%s', "
    490 				"owner '%s', curr.user '%s', w(%d)",
    491 				(tst_flag == CAN_CREATE) ? "can" : "can't",
    492 				ufile->path + cwd_offset,
    493 				hlink_info.path + cwd_offset,
    494 				users[owner].name, users[usr].name,
    495 				can_write);
    496 		}
    497 	}
    498 	return result;
    499 }
    500 
    501 static int check_symlink(const struct link_info *li)
    502 {
    503 	int symlink_result = 0;
    504 	int usr;
    505 	for (usr = 0; usr < USERS_NUM; ++usr) {
    506 		set_user(users[usr].name);
    507 		int tst_flag = (usr == li->dir_owner &&
    508 			li->in_world_write && li->in_sticky &&
    509 			usr != li->owner) ? CANNOT_FOLLOW : CAN_FOLLOW;
    510 
    511 		int fail = tst_flag != try_symlink(li->path);
    512 
    513 		symlink_result |= fail;
    514 
    515 		tst_resm((fail) ? TFAIL : TPASS,
    516 			"Expect: %s follow symlink '...%s', "
    517 			"owner '%s', src.owner '%s', "
    518 			"curr.user '%s', dir.owner '%s'",
    519 			(tst_flag == CAN_FOLLOW) ? "can" : "can't",
    520 			li->path + cwd_offset, users[li->owner].name,
    521 			users[li->source_owner].name, users[usr].name,
    522 			users[li->dir_owner].name);
    523 	}
    524 	return symlink_result;
    525 }
    526 
    527 /* differenet modes to try in the test */
    528 static const int o_modes[] = {
    529 	O_RDONLY,
    530 	O_WRONLY,
    531 	O_RDWR,
    532 	O_RDONLY | O_NONBLOCK | O_DIRECTORY,
    533 };
    534 
    535 static int try_symlink(const char *name)
    536 {
    537 	unsigned int mode;
    538 	for (mode = 0; mode < ARRAY_SIZE(o_modes); ++mode) {
    539 		if (try_open(name, o_modes[mode]) != -1)
    540 			return CAN_FOLLOW;
    541 	}
    542 
    543 	return CANNOT_FOLLOW;
    544 }
    545 
    546 static int try_open(const char *name, int mode)
    547 {
    548 	int fd = open(name, mode);
    549 
    550 	if (fd == -1)
    551 		return fd;
    552 
    553 	SAFE_CLOSE(cleanup, fd);
    554 
    555 	return 0;
    556 }
    557 
    558 static void set_user(const char *name)
    559 {
    560 	uid_t user_id = 0;
    561 	gid_t user_gr = 0;
    562 
    563 	if (name != NULL) {
    564 		struct passwd *pswd = getpwnam(name);
    565 
    566 		if (pswd == 0) {
    567 			tst_brkm(TBROK, cleanup,
    568 				"Failed to find user '%s'", name);
    569 		}
    570 		user_id = pswd->pw_uid;
    571 		user_gr = pswd->pw_gid;
    572 	}
    573 
    574 	SAFE_SETEGID(cleanup, user_gr);
    575 	SAFE_SETEUID(cleanup, user_id);
    576 }
    577