public inbox for ltp@lists.linux.it
 help / color / mirror / Atom feed
From: chrubis@suse.cz
To: Alexey Kodanev <alexey.kodanev@oracle.com>
Cc: vasily.isaenko@oracle.com, ltp-list@lists.sourceforge.net
Subject: Re: [LTP] [PATCH] New core test of hardlinks and symlinks restrictions added in Linux 3.6. Disabled by default in Linux 3.7.
Date: Mon, 6 May 2013 15:47:22 +0200	[thread overview]
Message-ID: <20130506134721.GE4388@rei> (raw)
In-Reply-To: <1367833278-18177-1-git-send-email-alexey.kodanev@oracle.com>

Hi!
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <pwd.h>
> +#include <unistd.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <signal.h>
> +
> +#include "test.h"
> +#include "usctest.h"
> +#include "safe_macros.h"
> +
> +char *TCID = "prot_hsymlinks";
> +int TST_TOTAL = 396;
> +
> +/* create 3 files and 1 dir in each base dir */
> +#define MAX_FILES_CREATED	4
> +#define MAX_PATH		128
> +#define MAX_CMD_LEN		64
> +#define MAX_USER_NAME		16
> +
> +enum {
> +	ROOT = 0,
> +	TEST_USER,
> +	USERS_NUM
> +};
> +
> +#define BASE_DIR_NUM		(USERS_NUM+1)
> +/*
> + * max test files and directories
> + * that will be created during the test
> + * is't not include symlinks and hardlinks
> + * and base directories
> + */
> +#define MAX_ENTITIES		(MAX_FILES_CREATED*BASE_DIR_NUM)
> +
> +struct dir_params {
> +	char path[MAX_PATH];
> +	int world_writable;
> +	int sticky;
> +	int owner;
> +};
> +
> +static struct dir_params bdirs[BASE_DIR_NUM];
> +
> +static const char *file_postfix = ".hs";

Although postfix is synonym for suffix it is also mailer daenom, name as
file_suffix or file_ext (as for extension) would be better.

> +enum {
> +	IS_FILE = 0,
> +	IS_DIRECTORY,
> +};
> +
> +struct user_file {
> +	char path[MAX_PATH];
> +	int is_dir;
> +};
> +
> +struct test_user {
> +	char name[MAX_USER_NAME];
> +	struct user_file file[MAX_ENTITIES];
> +	int num;
> +};
> +
> +static struct test_user users[USERS_NUM];
> +
> +struct link_info {
> +	char path[MAX_PATH];
> +	int owner;
> +	int source_owner;
> +	int in_world_write;
> +	int in_sticky;
> +	int is_dir;
> +	int dir_owner;
> +};
> +
> +/* test flags */
> +enum {
> +	CANNOT_FOLLOW = -1,
> +	CAN_FOLLOW = 0,
> +};
> +
> +enum {
> +	CANNOT_CREATE = -1,
> +	CAN_CREATE = 0,
> +};
> +
> +static char *tmp_user_name;
> +static char *default_user = "hsym";
> +static int nflag;
> +static int skip_cleanup;
> +
> +option_t options[] = {
> +	{"u:", &nflag, &tmp_user_name},	/* -u #user name */
> +	{"s", &skip_cleanup, NULL},
> +	{NULL, NULL, NULL}
> +};
> +/* full length of the test tmpdir path in /tmp */
> +static int cwd_offset;
> +
> +static const char *hrdlink_proc_path	= "/proc/sys/fs/protected_hardlinks";
> +static const char *symlink_proc_path	= "/proc/sys/fs/protected_symlinks";
> +
> +static void help(void);
> +static void setup(int ac, char **av);
> +static void cleanup(void);
> +
> +static void create_test_user(void);
> +static void delete_test_user(void);
> +
> +static int disable_protected_slinks;
> +static int disable_protected_hlinks;
> +
> +/*
> + * changes links restrictions
> + * @param value can be:
> + * 0 - restrictions is off
> + * 1 - restrictions is on
> + */
> +static void switch_protected_slinks(int value);
> +static void switch_protected_hlinks(int value);
> +
> +static int get_protected_slinks(void);
> +static int get_protected_hlinks(void);
> +
> +static void create_link_path(char *buffer, int size, const char *path);
> +static int create_check_hlinks(const struct user_file *ufile, int owner_idx);
> +static int create_check_slinks(const struct user_file *ufile, int owner_idx);
> +static int check_symlink(const struct link_info *li);
> +static int try_fopen(const char *name, int mode);
> +/* try to open symlink in diff modes */
> +static int try_symlink(const char *name);
> +
> +static int test_run(void);
> +static void init_base_dirs(void);
> +static void init_files_dirs(void);
> +
> +/* change effective user id and group id by name
> + * pass NULL to set root
> + */
> +static void set_user(const char *name);
> +
> +/* add new created files to user struct */
> +static void ufiles_add(int user_idx, char *path, int type);
> +
> +int main(int ac, char **av)
> +{
> +	setup(ac, av);
> +
> +	test_run();
> +
> +	cleanup();
> +
> +	tst_exit();
> +}
> +
> +static void setup(int ac, char **av)
> +{
> +	char *msg;
> +	msg = parse_opts(ac, av, options, &help);
> +	if (msg != NULL)
> +		tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
> +
> +	tst_require_root(NULL);
> +
> +	if (tst_kvercmp(3, 7, 0) < 0) {
> +		tst_brkm(TCONF, &cleanup,
> +		"Test must be run with kernel 3.7 or newer");
> +	}
> +
> +	/* initialize user names */
> +	strcpy(users[ROOT].name, "root");
> +
> +	if (tmp_user_name == NULL)
> +		tmp_user_name = default_user;
> +	snprintf(users[TEST_USER].name, MAX_USER_NAME, "%s", tmp_user_name);
> +
> +	tst_sig(FORK, DEF_HANDLER, &cleanup);
> +
> +	create_test_user();
> +	/*
> +	 * enable hardlinks and symlinks restrictions,
> +	 * it's not defualt but have to check
> +	 */
> +	if (!get_protected_hlinks()) {
> +	    switch_protected_hlinks(1);
> +	    disable_protected_hlinks = 1;
> +	}
> +	if (!get_protected_slinks()) {
> +	    switch_protected_slinks(1);
> +	    disable_protected_slinks = 1;

Use tabs for indentation. And look for lines over 80 chars. Please use
checkpatch.pl from linux kernel sources to check the patch next time.

> +	}
> +
> +	init_base_dirs();
> +
> +	init_files_dirs();
> +}
> +
> +static int test_run(void)
> +{
> +	tst_resm(TINFO, " --- HARDLINKS AND SYMLINKS RESTRICTIONS TEST ---\n");
> +
> +	int	result_slink = 0,
> +		result_hlink = 0,
> +		owner_idx,
> +		file_idx;
> +
> +	const struct user_file *ufile;
> +	/*
> +	 * create symlinks and hardlinks from each user's files
> +	 * to each world writable directory
> +	 */
> +	for (owner_idx = 0; owner_idx < USERS_NUM; ++owner_idx) {
> +		/* get all users files and directories */
> +		for (file_idx = 0; file_idx < users[owner_idx].num; ++file_idx) {
> +			ufile = &users[owner_idx].file[file_idx];
> +			result_slink |= create_check_slinks(ufile, owner_idx);
> +			result_hlink |= create_check_hlinks(ufile, owner_idx);
> +		}
> +	}
> +
> +	/* final results */
> +	tst_resm(TINFO, "All test-cases have been completed, summary:\n"
> +		" - symlinks  test:\t%s\n"
> +		" - hardlinks test:\t%s",
> +		(result_slink == 1) ? "FAIL" : "PASS",
> +		(result_hlink == 1) ? "FAIL" : "PASS");
> +
> +	return result_slink | result_hlink;
> +}
> +
> +static void cleanup(void)
> +{
> +	set_user(NULL);
> +
> +	if (skip_cleanup)
> +		return;
> +
> +	delete_test_user();
> +
> +	if (disable_protected_hlinks) {
> +		tst_resm(TINFO, "Disable protected hardlinks mode back");
> +		switch_protected_hlinks(0);
> +	}
> +	if (disable_protected_slinks) {
> +		tst_resm(TINFO, "Disable protected symlinks mode back");
> +		switch_protected_slinks(0);
> +	}
> +
> +	tst_rmdir();
> +	TEST_CLEANUP;
> +}
> +
> +static int get_protected_hlinks(void)
> +{
> +	int value = 0;
> +	SAFE_FILE_SCANF(&cleanup, hrdlink_proc_path, "%d", &value);
> +	return value;
> +}
> +
> +static int get_protected_slinks(void)
> +{
> +	int value = 0;
> +	SAFE_FILE_SCANF(&cleanup, symlink_proc_path, "%d", &value);
> +	return value;
> +}
> +
> +static void switch_protected_hlinks(int value)
> +{
> +	SAFE_FILE_PRINTF(&cleanup, hrdlink_proc_path, "%d", value == 1);
> +}
> +
> +static void switch_protected_slinks(int value)
> +{
> +	SAFE_FILE_PRINTF(&cleanup, symlink_proc_path, "%d", value == 1);
> +}
> +
> +static void create_test_user(void)
> +{
> +	char cmd[MAX_CMD_LEN];
> +	snprintf(cmd, MAX_CMD_LEN, "%s %s", "useradd", users[TEST_USER].name);

You can change the format string to "useradd %s" ;)

> +	if (system(cmd) != 0) {
> +		tst_brkm(TBROK, &cleanup, "Can't create new user: %s",
> +			users[TEST_USER].name);
> +	}
> +}
> +
> +static void delete_test_user(void)
> +{
> +	char cmd[MAX_CMD_LEN];
> +	snprintf(cmd, MAX_CMD_LEN, "%s %s", "userdel -r", users[TEST_USER].name);

Here as well.

> +	if (system(cmd) != 0) {
> +		tst_brkm(TBROK, &cleanup, "Can't delete user: %s",
> +			users[TEST_USER].name);

You are calling this function from cleanup right? So if this fails the
cleanup function will be called which would again get to this point and
call cleanup etc...

> +	}
> +}
> +
> +static void help(void)
> +{
> +	printf("  -s      Skip cleanup.\n");
> +	printf("  -u #user name : Define test user\n");
> +}
> +
> +static void create_sub_dir(const char *path,
> +	struct dir_params *bdir, mode_t mode)
> +{
> +	snprintf(bdir->path, MAX_PATH, "%s/tmp_%s", path, users[bdir->owner].name);
> +	SAFE_MKDIR(&cleanup, bdir->path, mode);
> +
> +	if (bdir->sticky)
> +	    mode |= S_ISVTX;
> +	chmod(bdir->path, mode);
> +}
> +
> +static void init_base_dirs(void)
> +{
> +	memset(&bdirs, 0, sizeof(bdirs));

Again bdirs is global variable and as such is initialized to zero
automatically.

> +	/* create main dir, world-writable */
> +	tst_tmpdir();

It is customary to call this function in setup, so that everyone knows
the test creates temporary directory.

> +	char *cwd = get_tst_tmpdir();
> +	cwd_offset = strlen(cwd);
> +
> +	mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO;
> +	chmod(cwd, mode);
> +
> +	struct dir_params *bdir = bdirs;
> +	strcpy(bdir->path, cwd);
> +	free(cwd);

Do you really need the global path to the tst tmp dir? The call to
tst_tmpdir() changes your CWD to it, so at least the subdirs could be
created with local paths.

> +	bdir->sticky  = 0;
> +	bdir->world_writable = 1;
> +
> +	/* create subdir for each user */
> +	++bdir;
> +	int idx;
> +	for (idx = 0; idx < USERS_NUM; ++idx, ++bdir) {
> +		set_user(users[idx].name);
> +
> +		bdir->sticky  = 1;
> +		bdir->world_writable = 1;
> +		bdir->owner = idx;
> +
> +		create_sub_dir(bdirs[0].path, bdir, mode);
> +	}
> +}
> +
> +static void init_files_dirs(void)
> +{
> +	int i, owner_idx;
> +	/* create all other dirs and files */
> +	const struct dir_params *bdir = bdirs;
> +	for (i = 0; i < ARRAY_SIZE(bdirs); ++i, ++bdir) {
> +		for (owner_idx = 0; owner_idx < USERS_NUM; ++owner_idx) {
> +			set_user(users[owner_idx].name);
> +			char path[MAX_PATH];
> +
> +			/* create file in the main directory */
> +			snprintf(path, MAX_PATH, "%s/%s%s",
> +				bdir->path, users[owner_idx].name, file_postfix);
> +			ufiles_add(owner_idx, path, IS_FILE);
> +
> +			/* create file with S_IWOTH bit set */
> +			strcat(path, "_w");
> +			ufiles_add(owner_idx, path, IS_FILE);
> +
> +			chmod(path, S_IRUSR | S_IRGRP | S_IWOTH | S_IROTH);
> +
> +			/* create sub directory */
> +			snprintf(path, MAX_PATH, "%s/%s", bdir->path,
> +				users[owner_idx].name);
> +			ufiles_add(owner_idx, path, IS_DIRECTORY);
> +
> +			/* create local file inside sub directory */
> +			snprintf(path + strlen(path), MAX_PATH - strlen(path),
> +				"/local_%s%s", users[owner_idx].name, file_postfix);
> +			ufiles_add(owner_idx, path, IS_FILE);
> +		}
> +	}
> +}
> +
> +static void ufiles_add(int user_idx, char *path, int type)
> +{
> +	int file_idx = users[user_idx].num;
> +
> +	if (file_idx >= MAX_ENTITIES)
> +		tst_brkm(TBROK, &cleanup, "Unexpected number of files");
> +
> +	struct user_file *ufile = &users[user_idx].file[file_idx];
> +
> +	if (type == IS_FILE)
> +		SAFE_CREAT(&cleanup, path, 0644);
> +	else
> +		SAFE_MKDIR(&cleanup, path, 0755);
> +
> +	strcpy(ufile->path, path);
> +
> +	ufile->is_dir = (type == IS_DIRECTORY);
> +	++users[user_idx].num;
> +}
> +
> +static void create_link_path(char *buffer, int size, const char *path)
> +{
> +	/* to make sure name is unique */
> +	static int count;
> +	++count;
> +
> +	/* construct link name */
> +	snprintf(buffer, size, "%s/link_%d", path, count);
> +}
> +
> +static int create_check_slinks(const struct user_file *ufile, int owner_idx)
> +{
> +	int result = 0;
> +	int i, user_idx;
> +
> +	const struct dir_params *bdir = bdirs;
> +	for (i = 0; i < ARRAY_SIZE(bdirs); ++i, ++bdir) {
> +		for (user_idx = 0; user_idx < USERS_NUM; ++user_idx) {
> +			/* set user who will create symlink */
> +			set_user(users[user_idx].name);
> +
> +			struct link_info slink_info;
> +			create_link_path(slink_info.path, MAX_PATH, bdir->path);
> +
> +			slink_info.owner = user_idx;
> +			slink_info.source_owner = owner_idx;
> +			slink_info.in_world_write = bdir->world_writable;
> +			slink_info.in_sticky = bdir->sticky;
> +			slink_info.dir_owner = bdir->owner;
> +
> +			if (symlink(ufile->path, slink_info.path) == -1) {
> +				tst_brkm(TBROK, &cleanup, "Can't create symlink: %s",
> +					slink_info.path);
> +			}
> +			result |= check_symlink(&slink_info);
> +		}
> +	}
> +	return result;
> +}
> +
> +static int create_check_hlinks(const struct user_file *ufile, int owner_idx)
> +{
> +	int result = 0;
> +	int i, user_idx;
> +	const struct dir_params *bdir = bdirs;
> +	for (i = 0; i < ARRAY_SIZE(bdirs); ++i, ++bdir) {
> +		for (user_idx = 0; user_idx < USERS_NUM; ++user_idx) {
> +			/* can't create hardlink to directory */
> +			if (ufile->is_dir)
> +				continue;
> +			/* set user who will create hardlink */
> +			set_user(users[user_idx].name);
> +
> +			struct link_info hlink_info;
> +			create_link_path(hlink_info.path, MAX_PATH, bdir->path);
> +
> +			int can_write = try_fopen(ufile->path, O_WRONLY) == 0;
> +
> +			int tst_flag = (can_write || user_idx == owner_idx ||
> +				user_idx == ROOT) ? CAN_CREATE : CANNOT_CREATE;
> +
> +			int fail = tst_flag != link(ufile->path, hlink_info.path);
> +
> +			result |= fail;
> +			tst_resm((fail) ? TFAIL : TPASS,
> +				"Expect: %s create hardlink \"...%s\" to \"...%s\", "
> +				"owner \"%s\", curr.user \"%s\", w(%d)",
> +				(tst_flag == CAN_CREATE) ? "can" : "can't",
> +				ufile->path + cwd_offset,
> +				hlink_info.path + cwd_offset,
> +				users[owner_idx].name, users[user_idx].name,
> +				can_write);
> +		}
> +	}
> +	return result;
> +}
> +
> +static int check_symlink(const struct link_info *li)
> +{
> +	int symlink_result = 0;
> +	int user_idx;
> +	for (user_idx = 0; user_idx < USERS_NUM; ++user_idx) {
> +		set_user(users[user_idx].name);
> +		int tst_flag = (user_idx == li->dir_owner &&
> +			li->in_world_write && li->in_sticky &&
> +			user_idx != li->owner) ? CANNOT_FOLLOW : CAN_FOLLOW;
> +
> +		int fail = tst_flag != try_symlink(li->path);
> +
> +		symlink_result |= fail;
> +
> +		tst_resm((fail) ? TFAIL : TPASS,
> +			"Expect: %s follow symlink \"...%s\", "
> +			"owner \"%s\", src.owner \"%s\", "
> +			"curr.user \"%s\", dir.owner \"%s\"",
> +			(tst_flag == CAN_FOLLOW) ? "can" : "can't",
> +			li->path + cwd_offset, users[li->owner].name,
> +			users[li->source_owner].name, users[user_idx].name,
> +			users[li->dir_owner].name);
> +	}

If you use simple quotes you don't have to escape then in the format
string such as: "Failed to find usere '%s'"

> +	return symlink_result;
> +}
> +
> +/* differenet modes to try in the test */
> +static const int o_modes[] = {
> +	O_RDONLY,
> +	O_WRONLY,
> +	O_RDWR,
> +	O_RDONLY | O_NONBLOCK | O_DIRECTORY,
> +};
> +
> +static int try_symlink(const char *name)
> +{
> +	int i;
> +	for (i = 0; i < ARRAY_SIZE(o_modes); ++i) {
> +		if (try_fopen(name, o_modes[i]) != -1)
> +			return CAN_FOLLOW;
> +	}
> +
> +	return CANNOT_FOLLOW;
> +}
> +
> +static int try_fopen(const char *name, int mode)
> +{
> +	int fd = open(name, mode);
> +
> +	if (fd == -1)
> +		return fd;
> +
> +	if (close(fd) == -1)
> +		tst_brkm(TBROK, &cleanup, "Can't close file: %s", name);
> +
> +	return 0;
> +}

This function name is confusing, it has fopen in name but calls open.

> +static void set_user(const char *name)
> +{
> +	uid_t user_id = 0;
> +	gid_t user_gr = 0;
> +
> +	if (name != NULL) {
> +		struct passwd *pswd = getpwnam(name);
> +
> +		if (pswd == 0)
> +			tst_brkm(TBROK, &cleanup, "Failed to find user \"%s\"", name);
> +		user_id = pswd->pw_uid;
> +		user_gr = pswd->pw_gid;
> +	}
> +
> +	SAFE_SETEGID(&cleanup, user_gr);
> +	SAFE_SETEUID(&cleanup, user_id);
> +}

-- 
Cyril Hrubis
chrubis@suse.cz

------------------------------------------------------------------------------
Introducing AppDynamics Lite, a free troubleshooting tool for Java/.NET
Get 100% visibility into your production application - at no cost.
Code-level diagnostics for performance bottlenecks with <2% overhead
Download for free and get started troubleshooting in minutes.
http://p.sf.net/sfu/appdyn_d2d_ap1
_______________________________________________
Ltp-list mailing list
Ltp-list@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/ltp-list

  reply	other threads:[~2013-05-06 13:46 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-05-06  9:41 [LTP] [PATCH] New core test of hardlinks and symlinks restrictions added in Linux 3.6. Disabled by default in Linux 3.7 Alexey Kodanev
2013-05-06 13:47 ` chrubis [this message]
     [not found]   ` <5187D15B.7080106@oracle.com>
2013-05-06 16:42     ` chrubis
2013-05-06 15:55 ` Mike Frysinger
  -- strict thread matches above, loose matches on Subject: below --
2013-05-07  8:40 Alexey Kodanev
2013-05-07 14:29 ` chrubis
2013-04-30  6:27 Alexey Kodanev
2013-04-30 12:24 ` chrubis
2013-04-30 17:24 ` Mike Frysinger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20130506134721.GE4388@rei \
    --to=chrubis@suse.cz \
    --cc=alexey.kodanev@oracle.com \
    --cc=ltp-list@lists.sourceforge.net \
    --cc=vasily.isaenko@oracle.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox