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 (tst_kvercmp(3, 7, 0) < 0)
197		tst_brkm(TCONF, NULL,
198			"Test must be run with kernel 3.7 or newer");
199
200	/* initialize user names */
201	strcpy(users[ROOT].name, "root");
202
203	if (tmp_user_name == NULL)
204		tmp_user_name = default_user;
205	snprintf(users[TEST_USER].name, MAX_USER_NAME, "%s", tmp_user_name);
206
207	tst_sig(FORK, DEF_HANDLER, cleanup);
208
209	test_user_cmd("useradd");
210	/*
211	 * enable hardlinks and symlinks restrictions,
212	 * it's not defualt but have to check
213	 */
214	if (!get_protected_hlinks()) {
215		switch_protected_hlinks(1);
216		disable_protected_hlinks = 1;
217	}
218	if (!get_protected_slinks()) {
219		switch_protected_slinks(1);
220		disable_protected_slinks = 1;
221	}
222
223	tst_tmpdir();
224
225	init_base_dirs();
226
227	init_files_dirs();
228}
229
230static int test_run(void)
231{
232	tst_resm(TINFO, " --- HARDLINKS AND SYMLINKS RESTRICTIONS TEST ---\n");
233
234	int	result_slink = 0,
235		result_hlink = 0,
236		usr,
237		file;
238
239	const struct user_file *ufile;
240	/*
241	 * create symlinks and hardlinks from each user's files
242	 * to each world writable directory
243	 */
244	for (usr = 0; usr < USERS_NUM; ++usr) {
245		/* get all users files and directories */
246		for (file = 0; file < users[usr].num; ++file) {
247			ufile = &users[usr].file[file];
248			result_slink |= create_check_slinks(ufile, usr);
249			result_hlink |= create_check_hlinks(ufile, usr);
250		}
251	}
252
253	/* final results */
254	tst_resm(TINFO, "All test-cases have been completed, summary:\n"
255		" - symlinks  test:\t%s\n"
256		" - hardlinks test:\t%s",
257		(result_slink == 1) ? "FAIL" : "PASS",
258		(result_hlink == 1) ? "FAIL" : "PASS");
259
260	return result_slink | result_hlink;
261}
262
263static void cleanup(void)
264{
265	/* call cleanup function only once */
266	static int first_call = 1;
267	if (!first_call)
268		return;
269	first_call = 0;
270
271	set_user(NULL);
272
273	if (skip_cleanup)
274		return;
275
276	test_user_cmd("userdel -r");
277
278	if (disable_protected_hlinks) {
279		tst_resm(TINFO, "Disable protected hardlinks mode back");
280		switch_protected_hlinks(0);
281	}
282	if (disable_protected_slinks) {
283		tst_resm(TINFO, "Disable protected symlinks mode back");
284		switch_protected_slinks(0);
285	}
286
287	tst_rmdir();
288}
289
290static int get_protected_hlinks(void)
291{
292	int value = 0;
293	SAFE_FILE_SCANF(cleanup, hrdlink_proc_path, "%d", &value);
294	return value;
295}
296
297static int get_protected_slinks(void)
298{
299	int value = 0;
300	SAFE_FILE_SCANF(cleanup, symlink_proc_path, "%d", &value);
301	return value;
302}
303
304static void switch_protected_hlinks(int value)
305{
306	SAFE_FILE_PRINTF(cleanup, hrdlink_proc_path, "%d", value == 1);
307}
308
309static void switch_protected_slinks(int value)
310{
311	SAFE_FILE_PRINTF(cleanup, symlink_proc_path, "%d", value == 1);
312}
313
314static void test_user_cmd(const char *user_cmd)
315{
316	char cmd[MAX_CMD_LEN];
317	snprintf(cmd, MAX_CMD_LEN, "%s %s", user_cmd, users[TEST_USER].name);
318	if (system(cmd) != 0) {
319		tst_brkm(TBROK, cleanup, "Failed to run cmd: %s %s",
320			user_cmd, users[TEST_USER].name);
321	}
322}
323
324static void help(void)
325{
326	printf("  -s      Skip cleanup.\n");
327	printf("  -u #user name : Define test user\n");
328}
329
330static void create_sub_dir(const char *path,
331	struct dir_params *bdir, mode_t mode)
332{
333	snprintf(bdir->path, MAX_PATH, "%s/tmp_%s",
334		path, users[bdir->owner].name);
335	SAFE_MKDIR(cleanup, bdir->path, mode);
336
337	if (bdir->sticky)
338		mode |= S_ISVTX;
339	chmod(bdir->path, mode);
340}
341
342static void init_base_dirs(void)
343{
344	char *cwd = tst_get_tmpdir();
345	cwd_offset = strlen(cwd);
346
347	mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO;
348	chmod(cwd, mode);
349
350	strcpy(bdirs[0].path, cwd);
351	free(cwd);
352
353	bdirs[0].sticky  = 0;
354	bdirs[0].world_writable = 1;
355
356	/* create subdir for each user */
357	int dir, usr;
358	for (usr = 0; usr < USERS_NUM; ++usr) {
359		set_user(users[usr].name);
360		dir = usr + 1;
361		bdirs[dir].sticky  = 1;
362		bdirs[dir].world_writable = 1;
363		bdirs[dir].owner = usr;
364
365		create_sub_dir(bdirs[0].path, &bdirs[dir], mode);
366	}
367}
368
369static void init_files_dirs(void)
370{
371	int dir, usr;
372	/* create all other dirs and files */
373	for (dir = 0; dir < ARRAY_SIZE(bdirs); ++dir) {
374		for (usr = 0; usr < USERS_NUM; ++usr) {
375			set_user(users[usr].name);
376			char path[MAX_PATH];
377
378			/* create file in the main directory */
379			snprintf(path, MAX_PATH, "%s/%s%s",
380				bdirs[dir].path, users[usr].name, file_ext);
381			ufiles_add(usr, path, IS_FILE);
382
383			/* create file with S_IWOTH bit set */
384			strcat(path, "_w");
385			ufiles_add(usr, path, IS_FILE);
386
387			chmod(path, S_IRUSR | S_IRGRP | S_IWOTH | S_IROTH);
388
389			/* create sub directory */
390			snprintf(path, MAX_PATH, "%s/%s", bdirs[dir].path,
391				users[usr].name);
392			ufiles_add(usr, path, IS_DIRECTORY);
393
394			/* create local file inside sub directory */
395			snprintf(path + strlen(path), MAX_PATH - strlen(path),
396				"/local_%s%s", users[usr].name, file_ext);
397			ufiles_add(usr, path, IS_FILE);
398		}
399	}
400}
401
402static void ufiles_add(int usr, char *path, int type)
403{
404	int file = users[usr].num;
405
406	if (file >= MAX_ENTITIES)
407		tst_brkm(TBROK, cleanup, "Unexpected number of files");
408
409	struct user_file *ufile = &users[usr].file[file];
410
411	if (type == IS_FILE)
412		SAFE_TOUCH(cleanup, path, 0644, NULL);
413	else
414		SAFE_MKDIR(cleanup, path, 0755);
415
416	strcpy(ufile->path, path);
417
418	ufile->is_dir = (type == IS_DIRECTORY);
419	++users[usr].num;
420}
421
422static void create_link_path(char *buffer, int size, const char *path)
423{
424	/* to make sure name is unique */
425	static int count;
426	++count;
427
428	/* construct link name */
429	snprintf(buffer, size, "%s/link_%d", path, count);
430}
431
432static int create_check_slinks(const struct user_file *ufile, int owner)
433{
434	int result = 0;
435	int dir, usr;
436	for (dir = 0; dir < ARRAY_SIZE(bdirs); ++dir) {
437		for (usr = 0; usr < USERS_NUM; ++usr) {
438			/* set user who will create symlink */
439			set_user(users[usr].name);
440
441			struct link_info slink_info;
442			create_link_path(slink_info.path, MAX_PATH,
443				bdirs[dir].path);
444
445			slink_info.owner = usr;
446			slink_info.source_owner = owner;
447			slink_info.in_world_write = bdirs[dir].world_writable;
448			slink_info.in_sticky = bdirs[dir].sticky;
449			slink_info.dir_owner = bdirs[dir].owner;
450
451			if (symlink(ufile->path, slink_info.path) == -1) {
452				tst_brkm(TBROK, cleanup,
453					"Can't create symlink: %s",
454					slink_info.path);
455			}
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;
465	int dir, usr;
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	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	if (close(fd) == -1)
553		tst_brkm(TBROK, cleanup, "Can't close file: %s", name);
554
555	return 0;
556}
557
558static 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