• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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 
main(int argc,char * argv[])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 
setup(int argc,char * argv[])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 	/* fix for hsym user with umask 0077 */
231 	umask(0);
232 
233 	init_base_dirs();
234 
235 	init_files_dirs();
236 }
237 
test_run(void)238 static int test_run(void)
239 {
240 	tst_resm(TINFO, " --- HARDLINKS AND SYMLINKS RESTRICTIONS TEST ---");
241 
242 	int	result_slink = 0,
243 		result_hlink = 0,
244 		usr,
245 		file;
246 
247 	const struct user_file *ufile;
248 	/*
249 	 * create symlinks and hardlinks from each user's files
250 	 * to each world writable directory
251 	 */
252 	for (usr = 0; usr < USERS_NUM; ++usr) {
253 		/* get all users files and directories */
254 		for (file = 0; file < users[usr].num; ++file) {
255 			ufile = &users[usr].file[file];
256 			result_slink |= create_check_slinks(ufile, usr);
257 			result_hlink |= create_check_hlinks(ufile, usr);
258 		}
259 	}
260 
261 	/* final results */
262 	tst_resm(TINFO, "All test-cases have been completed, summary:"
263 		" - symlinks  test:\t%s"
264 		" - hardlinks test:\t%s",
265 		(result_slink == 1) ? "FAIL" : "PASS",
266 		(result_hlink == 1) ? "FAIL" : "PASS");
267 
268 	return result_slink | result_hlink;
269 }
270 
cleanup(void)271 static void cleanup(void)
272 {
273 	/* call cleanup function only once */
274 	static int first_call = 1;
275 	if (!first_call)
276 		return;
277 	first_call = 0;
278 
279 	set_user(NULL);
280 
281 	if (skip_cleanup)
282 		return;
283 
284 	test_user_cmd("userdel -r");
285 
286 	if (disable_protected_hlinks) {
287 		tst_resm(TINFO, "Disable protected hardlinks mode back");
288 		switch_protected_hlinks(0);
289 	}
290 	if (disable_protected_slinks) {
291 		tst_resm(TINFO, "Disable protected symlinks mode back");
292 		switch_protected_slinks(0);
293 	}
294 
295 	tst_rmdir();
296 }
297 
get_protected_hlinks(void)298 static int get_protected_hlinks(void)
299 {
300 	int value = 0;
301 	SAFE_FILE_SCANF(cleanup, hrdlink_proc_path, "%d", &value);
302 	return value;
303 }
304 
get_protected_slinks(void)305 static int get_protected_slinks(void)
306 {
307 	int value = 0;
308 	SAFE_FILE_SCANF(cleanup, symlink_proc_path, "%d", &value);
309 	return value;
310 }
311 
switch_protected_hlinks(int value)312 static void switch_protected_hlinks(int value)
313 {
314 	SAFE_FILE_PRINTF(cleanup, hrdlink_proc_path, "%d", value == 1);
315 }
316 
switch_protected_slinks(int value)317 static void switch_protected_slinks(int value)
318 {
319 	SAFE_FILE_PRINTF(cleanup, symlink_proc_path, "%d", value == 1);
320 }
321 
test_user_cmd(const char * user_cmd)322 static void test_user_cmd(const char *user_cmd)
323 {
324 	char cmd[MAX_CMD_LEN];
325 	snprintf(cmd, MAX_CMD_LEN, "%s %s", user_cmd, users[TEST_USER].name);
326 	if (system(cmd) != 0) {
327 		tst_brkm(TBROK, cleanup, "Failed to run cmd: %s %s",
328 			user_cmd, users[TEST_USER].name);
329 	}
330 }
331 
help(void)332 static void help(void)
333 {
334 	printf("  -s      Skip cleanup.\n");
335 	printf("  -u #user name : Define test user\n");
336 }
337 
create_sub_dir(const char * path,struct dir_params * bdir,mode_t mode)338 static void create_sub_dir(const char *path,
339 	struct dir_params *bdir, mode_t mode)
340 {
341 	snprintf(bdir->path, MAX_PATH, "%s/tmp_%s",
342 		path, users[bdir->owner].name);
343 	SAFE_MKDIR(cleanup, bdir->path, mode);
344 
345 	if (bdir->sticky)
346 		mode |= S_ISVTX;
347 	chmod(bdir->path, mode);
348 }
349 
init_base_dirs(void)350 static void init_base_dirs(void)
351 {
352 	char *cwd = tst_get_tmpdir();
353 	cwd_offset = strlen(cwd);
354 
355 	mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO;
356 	chmod(cwd, mode);
357 
358 	strcpy(bdirs[0].path, cwd);
359 	free(cwd);
360 
361 	bdirs[0].sticky  = 0;
362 	bdirs[0].world_writable = 1;
363 
364 	/* create subdir for each user */
365 	int dir, usr;
366 	for (usr = 0; usr < USERS_NUM; ++usr) {
367 		set_user(users[usr].name);
368 		dir = usr + 1;
369 		bdirs[dir].sticky  = 1;
370 		bdirs[dir].world_writable = 1;
371 		bdirs[dir].owner = usr;
372 
373 		create_sub_dir(bdirs[0].path, &bdirs[dir], mode);
374 	}
375 }
376 
init_files_dirs(void)377 static void init_files_dirs(void)
378 {
379 	unsigned int dir, usr;
380 	/* create all other dirs and files */
381 	for (dir = 0; dir < ARRAY_SIZE(bdirs); ++dir) {
382 		for (usr = 0; usr < USERS_NUM; ++usr) {
383 			set_user(users[usr].name);
384 			char path[MAX_PATH];
385 
386 			/* create file in the main directory */
387 			snprintf(path, MAX_PATH, "%s/%s%s",
388 				bdirs[dir].path, users[usr].name, file_ext);
389 			ufiles_add(usr, path, IS_FILE);
390 
391 			/* create file with S_IWOTH bit set */
392 			strcat(path, "_w");
393 			ufiles_add(usr, path, IS_FILE);
394 
395 			chmod(path, S_IRUSR | S_IRGRP | S_IWOTH | S_IROTH);
396 
397 			/* create sub directory */
398 			snprintf(path, MAX_PATH, "%s/%s", bdirs[dir].path,
399 				users[usr].name);
400 			ufiles_add(usr, path, IS_DIRECTORY);
401 
402 			/* create local file inside sub directory */
403 			snprintf(path + strlen(path), MAX_PATH - strlen(path),
404 				"/local_%s%s", users[usr].name, file_ext);
405 			ufiles_add(usr, path, IS_FILE);
406 		}
407 	}
408 }
409 
ufiles_add(int usr,char * path,int type)410 static void ufiles_add(int usr, char *path, int type)
411 {
412 	int file = users[usr].num;
413 
414 	if (file >= MAX_ENTITIES)
415 		tst_brkm(TBROK, cleanup, "Unexpected number of files");
416 
417 	struct user_file *ufile = &users[usr].file[file];
418 
419 	if (type == IS_FILE)
420 		SAFE_TOUCH(cleanup, path, 0644, NULL);
421 	else
422 		SAFE_MKDIR(cleanup, path, 0755);
423 
424 	strcpy(ufile->path, path);
425 
426 	ufile->is_dir = (type == IS_DIRECTORY);
427 	++users[usr].num;
428 }
429 
create_link_path(char * buffer,int size,const char * path)430 static void create_link_path(char *buffer, int size, const char *path)
431 {
432 	/* to make sure name is unique */
433 	static int count;
434 	++count;
435 
436 	/* construct link name */
437 	snprintf(buffer, size, "%s/link_%d", path, count);
438 }
439 
create_check_slinks(const struct user_file * ufile,int owner)440 static int create_check_slinks(const struct user_file *ufile, int owner)
441 {
442 	int result = 0, usr;
443 	unsigned int dir;
444 	for (dir = 0; dir < ARRAY_SIZE(bdirs); ++dir) {
445 		for (usr = 0; usr < USERS_NUM; ++usr) {
446 			/* set user who will create symlink */
447 			set_user(users[usr].name);
448 
449 			struct link_info slink_info;
450 			create_link_path(slink_info.path, MAX_PATH,
451 				bdirs[dir].path);
452 
453 			slink_info.owner = usr;
454 			slink_info.source_owner = owner;
455 			slink_info.in_world_write = bdirs[dir].world_writable;
456 			slink_info.in_sticky = bdirs[dir].sticky;
457 			slink_info.dir_owner = bdirs[dir].owner;
458 
459 			SAFE_SYMLINK(cleanup, ufile->path, slink_info.path);
460 			result |= check_symlink(&slink_info);
461 		}
462 	}
463 	return result;
464 }
465 
create_check_hlinks(const struct user_file * ufile,int owner)466 static int create_check_hlinks(const struct user_file *ufile, int owner)
467 {
468 	int result = 0, usr;
469 	unsigned int dir;
470 	for (dir = 0; dir < ARRAY_SIZE(bdirs); ++dir) {
471 		for (usr = 0; usr < USERS_NUM; ++usr) {
472 			/* can't create hardlink to directory */
473 			if (ufile->is_dir)
474 				continue;
475 			/* set user who will create hardlink */
476 			set_user(users[usr].name);
477 
478 			struct link_info hlink_info;
479 			create_link_path(hlink_info.path, MAX_PATH,
480 				bdirs[dir].path);
481 
482 			int can_write = try_open(ufile->path, O_WRONLY) == 0;
483 
484 			int tst_flag = (can_write || usr == owner ||
485 				usr == ROOT) ? CAN_CREATE : CANNOT_CREATE;
486 
487 			int fail;
488 			fail = tst_flag != link(ufile->path, hlink_info.path);
489 
490 			result |= fail;
491 			tst_resm((fail) ? TFAIL : TPASS,
492 				"Expect: %s create hardlink '...%s' to '...%s', "
493 				"owner '%s', curr.user '%s', w(%d)",
494 				(tst_flag == CAN_CREATE) ? "can" : "can't",
495 				ufile->path + cwd_offset,
496 				hlink_info.path + cwd_offset,
497 				users[owner].name, users[usr].name,
498 				can_write);
499 		}
500 	}
501 	return result;
502 }
503 
check_symlink(const struct link_info * li)504 static int check_symlink(const struct link_info *li)
505 {
506 	int symlink_result = 0;
507 	int usr;
508 	for (usr = 0; usr < USERS_NUM; ++usr) {
509 		set_user(users[usr].name);
510 		int tst_flag = (usr == li->dir_owner &&
511 			li->in_world_write && li->in_sticky &&
512 			usr != li->owner) ? CANNOT_FOLLOW : CAN_FOLLOW;
513 
514 		int fail = tst_flag != try_symlink(li->path);
515 
516 		symlink_result |= fail;
517 
518 		tst_resm((fail) ? TFAIL : TPASS,
519 			"Expect: %s follow symlink '...%s', "
520 			"owner '%s', src.owner '%s', "
521 			"curr.user '%s', dir.owner '%s'",
522 			(tst_flag == CAN_FOLLOW) ? "can" : "can't",
523 			li->path + cwd_offset, users[li->owner].name,
524 			users[li->source_owner].name, users[usr].name,
525 			users[li->dir_owner].name);
526 	}
527 	return symlink_result;
528 }
529 
530 /* differenet modes to try in the test */
531 static const int o_modes[] = {
532 	O_RDONLY,
533 	O_WRONLY,
534 	O_RDWR,
535 	O_RDONLY | O_NONBLOCK | O_DIRECTORY,
536 };
537 
try_symlink(const char * name)538 static int try_symlink(const char *name)
539 {
540 	unsigned int mode;
541 	for (mode = 0; mode < ARRAY_SIZE(o_modes); ++mode) {
542 		if (try_open(name, o_modes[mode]) != -1)
543 			return CAN_FOLLOW;
544 	}
545 
546 	return CANNOT_FOLLOW;
547 }
548 
try_open(const char * name,int mode)549 static int try_open(const char *name, int mode)
550 {
551 	int fd = open(name, mode);
552 
553 	if (fd == -1)
554 		return fd;
555 
556 	SAFE_CLOSE(cleanup, fd);
557 
558 	return 0;
559 }
560 
set_user(const char * name)561 static void set_user(const char *name)
562 {
563 	uid_t user_id = 0;
564 	gid_t user_gr = 0;
565 
566 	if (name != NULL) {
567 		struct passwd *pswd = getpwnam(name);
568 
569 		if (pswd == 0) {
570 			tst_brkm(TBROK, cleanup,
571 				"Failed to find user '%s'", name);
572 		}
573 		user_id = pswd->pw_uid;
574 		user_gr = pswd->pw_gid;
575 	}
576 
577 	SAFE_SETEGID(cleanup, user_gr);
578 	SAFE_SETEUID(cleanup, user_id);
579 }
580