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
48char *TCID = "prot_hsymlinks";
49int 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
57enum {
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
72struct dir_params {
73	char path[MAX_PATH];
74	int world_writable;
75	int sticky;
76	int owner;
77};
78
79static struct dir_params bdirs[BASE_DIR_NUM];
80
81static const char file_ext[] = ".hs";
82
83enum {
84	IS_FILE = 0,
85	IS_DIRECTORY,
86};
87
88struct user_file {
89	char path[MAX_PATH];
90	int is_dir;
91};
92
93struct test_user {
94	char name[MAX_USER_NAME];
95	struct user_file file[MAX_ENTITIES];
96	int num;
97};
98
99static struct test_user users[USERS_NUM];
100
101struct 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 */
112enum {
113	CANNOT_FOLLOW = -1,
114	CAN_FOLLOW = 0,
115};
116
117enum {
118	CANNOT_CREATE = -1,
119	CAN_CREATE = 0,
120};
121
122static char *tmp_user_name;
123static char *default_user = "hsym";
124static int nflag;
125static int skip_cleanup;
126
127static 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 */
133static size_t cwd_offset;
134
135static const char hrdlink_proc_path[]	= "/proc/sys/fs/protected_hardlinks";
136static const char symlink_proc_path[]	= "/proc/sys/fs/protected_symlinks";
137
138static void help(void);
139static void setup(int argc, char *argv[]);
140static void cleanup(void);
141
142static void test_user_cmd(const char *user_cmd);
143
144static int disable_protected_slinks;
145static 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 */
153static void switch_protected_slinks(int value);
154static void switch_protected_hlinks(int value);
155
156static int get_protected_slinks(void);
157static int get_protected_hlinks(void);
158
159static void create_link_path(char *buffer, int size, const char *path);
160static int create_check_hlinks(const struct user_file *ufile, int owner);
161static int create_check_slinks(const struct user_file *ufile, int owner);
162static int check_symlink(const struct link_info *li);
163static int try_open(const char *name, int mode);
164/* try to open symlink in diff modes */
165static int try_symlink(const char *name);
166
167static int test_run(void);
168static void init_base_dirs(void);
169static void init_files_dirs(void);
170
171/* change effective user id and group id by name
172 * pass NULL to set root
173 */
174static void set_user(const char *name);
175
176/* add new created files to user struct */
177static void ufiles_add(int usr, char *path, int type);
178
179int main(int argc, char *argv[])
180{
181	setup(argc, argv);
182
183	test_run();
184
185	cleanup();
186
187	tst_exit();
188}
189
190static void setup(int argc, char *argv[])
191{
192	tst_parse_opts(argc, argv, options, &help);
193
194	tst_require_root();
195
196	if (eaccess("/etc/passwd", W_OK)) {
197		tst_brkm(TCONF, NULL,
198			"/etc/passwd is not accessible");
199	}
200
201	/* initialize user names */
202	strcpy(users[ROOT].name, "root");
203
204	if (tmp_user_name == NULL)
205		tmp_user_name = default_user;
206	snprintf(users[TEST_USER].name, MAX_USER_NAME, "%s", tmp_user_name);
207
208	tst_sig(FORK, DEF_HANDLER, cleanup);
209
210	test_user_cmd("useradd");
211	/*
212	 * enable hardlinks and symlinks restrictions,
213	 * it's not defualt but have to check
214	 */
215	if (!get_protected_hlinks()) {
216		switch_protected_hlinks(1);
217		disable_protected_hlinks = 1;
218	}
219	if (!get_protected_slinks()) {
220		switch_protected_slinks(1);
221		disable_protected_slinks = 1;
222	}
223
224	tst_tmpdir();
225
226	/* fix for hsym user with umask 0077 */
227	umask(0);
228
229	init_base_dirs();
230
231	init_files_dirs();
232}
233
234static int test_run(void)
235{
236	tst_resm(TINFO, " --- HARDLINKS AND SYMLINKS RESTRICTIONS TEST ---");
237
238	int	result_slink = 0,
239		result_hlink = 0,
240		usr,
241		file;
242
243	const struct user_file *ufile;
244	/*
245	 * create symlinks and hardlinks from each user's files
246	 * to each world writable directory
247	 */
248	for (usr = 0; usr < USERS_NUM; ++usr) {
249		/* get all users files and directories */
250		for (file = 0; file < users[usr].num; ++file) {
251			ufile = &users[usr].file[file];
252			result_slink |= create_check_slinks(ufile, usr);
253			result_hlink |= create_check_hlinks(ufile, usr);
254		}
255	}
256
257	/* final results */
258	tst_resm(TINFO, "All test-cases have been completed, summary:"
259		" - symlinks  test:\t%s"
260		" - hardlinks test:\t%s",
261		(result_slink == 1) ? "FAIL" : "PASS",
262		(result_hlink == 1) ? "FAIL" : "PASS");
263
264	return result_slink | result_hlink;
265}
266
267static void cleanup(void)
268{
269	/* call cleanup function only once */
270	static int first_call = 1;
271	if (!first_call)
272		return;
273	first_call = 0;
274
275	set_user(NULL);
276
277	if (skip_cleanup)
278		return;
279
280	test_user_cmd("userdel -r");
281
282	if (disable_protected_hlinks) {
283		tst_resm(TINFO, "Disable protected hardlinks mode back");
284		switch_protected_hlinks(0);
285	}
286	if (disable_protected_slinks) {
287		tst_resm(TINFO, "Disable protected symlinks mode back");
288		switch_protected_slinks(0);
289	}
290
291	tst_rmdir();
292}
293
294static int get_protected_hlinks(void)
295{
296	int value = 0;
297	SAFE_FILE_SCANF(cleanup, hrdlink_proc_path, "%d", &value);
298	return value;
299}
300
301static int get_protected_slinks(void)
302{
303	int value = 0;
304	SAFE_FILE_SCANF(cleanup, symlink_proc_path, "%d", &value);
305	return value;
306}
307
308static void switch_protected_hlinks(int value)
309{
310	SAFE_FILE_PRINTF(cleanup, hrdlink_proc_path, "%d", value == 1);
311}
312
313static void switch_protected_slinks(int value)
314{
315	SAFE_FILE_PRINTF(cleanup, symlink_proc_path, "%d", value == 1);
316}
317
318static void test_user_cmd(const char *user_cmd)
319{
320	char cmd[MAX_CMD_LEN];
321	snprintf(cmd, MAX_CMD_LEN, "%s %s", user_cmd, users[TEST_USER].name);
322	if (system(cmd) != 0) {
323		tst_brkm(TBROK, cleanup, "Failed to run cmd: %s %s",
324			user_cmd, users[TEST_USER].name);
325	}
326}
327
328static void help(void)
329{
330	printf("  -s      Skip cleanup.\n");
331	printf("  -u #user name : Define test user\n");
332}
333
334static void create_sub_dir(const char *path,
335	struct dir_params *bdir, mode_t mode)
336{
337	snprintf(bdir->path, MAX_PATH, "%s/tmp_%s",
338		path, users[bdir->owner].name);
339	SAFE_MKDIR(cleanup, bdir->path, mode);
340
341	if (bdir->sticky)
342		mode |= S_ISVTX;
343	chmod(bdir->path, mode);
344}
345
346static void init_base_dirs(void)
347{
348	char *cwd = tst_get_tmpdir();
349	cwd_offset = strlen(cwd);
350
351	mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO;
352	chmod(cwd, mode);
353
354	strcpy(bdirs[0].path, cwd);
355	free(cwd);
356
357	bdirs[0].sticky  = 0;
358	bdirs[0].world_writable = 1;
359
360	/* create subdir for each user */
361	int dir, usr;
362	for (usr = 0; usr < USERS_NUM; ++usr) {
363		set_user(users[usr].name);
364		dir = usr + 1;
365		bdirs[dir].sticky  = 1;
366		bdirs[dir].world_writable = 1;
367		bdirs[dir].owner = usr;
368
369		create_sub_dir(bdirs[0].path, &bdirs[dir], mode);
370	}
371}
372
373static void init_files_dirs(void)
374{
375	unsigned int dir, usr;
376	/* create all other dirs and files */
377	for (dir = 0; dir < ARRAY_SIZE(bdirs); ++dir) {
378		for (usr = 0; usr < USERS_NUM; ++usr) {
379			set_user(users[usr].name);
380			char path[MAX_PATH];
381
382			/* create file in the main directory */
383			snprintf(path, MAX_PATH, "%s/%s%s",
384				bdirs[dir].path, users[usr].name, file_ext);
385			ufiles_add(usr, path, IS_FILE);
386
387			/* create file with S_IWOTH bit set */
388			strcat(path, "_w");
389			ufiles_add(usr, path, IS_FILE);
390
391			chmod(path, S_IRUSR | S_IRGRP | S_IWOTH | S_IROTH);
392
393			/* create sub directory */
394			snprintf(path, MAX_PATH, "%s/%s", bdirs[dir].path,
395				users[usr].name);
396			ufiles_add(usr, path, IS_DIRECTORY);
397
398			/* create local file inside sub directory */
399			snprintf(path + strlen(path), MAX_PATH - strlen(path),
400				"/local_%s%s", users[usr].name, file_ext);
401			ufiles_add(usr, path, IS_FILE);
402		}
403	}
404}
405
406static void ufiles_add(int usr, char *path, int type)
407{
408	int file = users[usr].num;
409
410	if (file >= MAX_ENTITIES)
411		tst_brkm(TBROK, cleanup, "Unexpected number of files");
412
413	struct user_file *ufile = &users[usr].file[file];
414
415	if (type == IS_FILE)
416		SAFE_TOUCH(cleanup, path, 0644, NULL);
417	else
418		SAFE_MKDIR(cleanup, path, 0755);
419
420	strcpy(ufile->path, path);
421
422	ufile->is_dir = (type == IS_DIRECTORY);
423	++users[usr].num;
424}
425
426static void create_link_path(char *buffer, int size, const char *path)
427{
428	/* to make sure name is unique */
429	static int count;
430	++count;
431
432	/* construct link name */
433	snprintf(buffer, size, "%s/link_%d", path, count);
434}
435
436static int create_check_slinks(const struct user_file *ufile, int owner)
437{
438	int result = 0, usr;
439	unsigned int dir;
440	for (dir = 0; dir < ARRAY_SIZE(bdirs); ++dir) {
441		for (usr = 0; usr < USERS_NUM; ++usr) {
442			/* set user who will create symlink */
443			set_user(users[usr].name);
444
445			struct link_info slink_info;
446			create_link_path(slink_info.path, MAX_PATH,
447				bdirs[dir].path);
448
449			slink_info.owner = usr;
450			slink_info.source_owner = owner;
451			slink_info.in_world_write = bdirs[dir].world_writable;
452			slink_info.in_sticky = bdirs[dir].sticky;
453			slink_info.dir_owner = bdirs[dir].owner;
454
455			SAFE_SYMLINK(cleanup, ufile->path, slink_info.path);
456			result |= check_symlink(&slink_info);
457		}
458	}
459	return result;
460}
461
462static int create_check_hlinks(const struct user_file *ufile, int owner)
463{
464	int result = 0, usr;
465	unsigned int dir;
466	for (dir = 0; dir < ARRAY_SIZE(bdirs); ++dir) {
467		for (usr = 0; usr < USERS_NUM; ++usr) {
468			/* can't create hardlink to directory */
469			if (ufile->is_dir)
470				continue;
471			/* set user who will create hardlink */
472			set_user(users[usr].name);
473
474			struct link_info hlink_info;
475			create_link_path(hlink_info.path, MAX_PATH,
476				bdirs[dir].path);
477
478			int can_write = try_open(ufile->path, O_WRONLY) == 0;
479
480			int tst_flag = (can_write || usr == owner ||
481				usr == ROOT) ? CAN_CREATE : CANNOT_CREATE;
482
483			int fail;
484			fail = tst_flag != link(ufile->path, hlink_info.path);
485
486			result |= fail;
487			tst_resm((fail) ? TFAIL : TPASS,
488				"Expect: %s create hardlink '...%s' to '...%s', "
489				"owner '%s', curr.user '%s', w(%d)",
490				(tst_flag == CAN_CREATE) ? "can" : "can't",
491				ufile->path + cwd_offset,
492				hlink_info.path + cwd_offset,
493				users[owner].name, users[usr].name,
494				can_write);
495		}
496	}
497	return result;
498}
499
500static int check_symlink(const struct link_info *li)
501{
502	int symlink_result = 0;
503	int usr;
504	for (usr = 0; usr < USERS_NUM; ++usr) {
505		set_user(users[usr].name);
506		int tst_flag = (usr == li->dir_owner &&
507			li->in_world_write && li->in_sticky &&
508			usr != li->owner) ? CANNOT_FOLLOW : CAN_FOLLOW;
509
510		int fail = tst_flag != try_symlink(li->path);
511
512		symlink_result |= fail;
513
514		tst_resm((fail) ? TFAIL : TPASS,
515			"Expect: %s follow symlink '...%s', "
516			"owner '%s', src.owner '%s', "
517			"curr.user '%s', dir.owner '%s'",
518			(tst_flag == CAN_FOLLOW) ? "can" : "can't",
519			li->path + cwd_offset, users[li->owner].name,
520			users[li->source_owner].name, users[usr].name,
521			users[li->dir_owner].name);
522	}
523	return symlink_result;
524}
525
526/* differenet modes to try in the test */
527static const int o_modes[] = {
528	O_RDONLY,
529	O_WRONLY,
530	O_RDWR,
531	O_RDONLY | O_NONBLOCK | O_DIRECTORY,
532};
533
534static int try_symlink(const char *name)
535{
536	unsigned int mode;
537	for (mode = 0; mode < ARRAY_SIZE(o_modes); ++mode) {
538		if (try_open(name, o_modes[mode]) != -1)
539			return CAN_FOLLOW;
540	}
541
542	return CANNOT_FOLLOW;
543}
544
545static int try_open(const char *name, int mode)
546{
547	int fd = open(name, mode);
548
549	if (fd == -1)
550		return fd;
551
552	SAFE_CLOSE(cleanup, fd);
553
554	return 0;
555}
556
557static void set_user(const char *name)
558{
559	uid_t user_id = 0;
560	gid_t user_gr = 0;
561
562	if (name != NULL) {
563		struct passwd *pswd = getpwnam(name);
564
565		if (pswd == 0) {
566			tst_brkm(TBROK, cleanup,
567				"Failed to find user '%s'", name);
568		}
569		user_id = pswd->pw_uid;
570		user_gr = pswd->pw_gid;
571	}
572
573	SAFE_SETEGID(cleanup, user_gr);
574	SAFE_SETEUID(cleanup, user_id);
575}
576