Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c8c57e84 authored by Tetsuo Handa's avatar Tetsuo Handa Committed by James Morris
Browse files

TOMOYO: Support longer pathname.



Allow pathnames longer than 4000 bytes.

Signed-off-by: default avatarTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: default avatarJames Morris <jmorris@namei.org>
parent 9b244373
Loading
Loading
Loading
Loading
+2 −46
Original line number Diff line number Diff line
@@ -33,14 +33,7 @@ struct linux_binprm;
#define TOMOYO_HASH_BITS  8
#define TOMOYO_MAX_HASH (1u<<TOMOYO_HASH_BITS)

/*
 * This is the max length of a token.
 *
 * A token consists of only ASCII printable characters.
 * Non printable characters in a token is represented in \ooo style
 * octal string. Thus, \ itself is represented as \\.
 */
#define TOMOYO_MAX_PATHNAME_LEN 4000
#define TOMOYO_EXEC_TMPSIZE     4096

/* Profile number is an integer between 0 and 255. */
#define TOMOYO_MAX_PROFILES 256
@@ -167,17 +160,6 @@ enum tomoyo_securityfs_interface_index {

/********** Structure definitions. **********/

/*
 * tomoyo_page_buffer is a structure which is used for holding a pathname
 * obtained from "struct dentry" and "struct vfsmount" pair.
 * As of now, it is 4096 bytes. If users complain that 4096 bytes is too small
 * (because TOMOYO escapes non ASCII printable characters using \ooo format),
 * we will make the buffer larger.
 */
struct tomoyo_page_buffer {
	char buffer[4096];
};

/*
 * tomoyo_request_info is a structure which is used for holding
 *
@@ -231,28 +213,6 @@ struct tomoyo_name_entry {
	struct tomoyo_path_info entry;
};

/*
 * tomoyo_path_info_with_data is a structure which is used for holding a
 * pathname obtained from "struct dentry" and "struct vfsmount" pair.
 *
 * "struct tomoyo_path_info_with_data" consists of "struct tomoyo_path_info"
 * and buffer for the pathname, while "struct tomoyo_page_buffer" consists of
 * buffer for the pathname only.
 *
 * "struct tomoyo_path_info_with_data" is intended to allow TOMOYO to release
 * both "struct tomoyo_path_info" and buffer for the pathname by single kfree()
 * so that we don't need to return two pointers to the caller. If the caller
 * puts "struct tomoyo_path_info" on stack memory, we will be able to remove
 * "struct tomoyo_path_info_with_data".
 */
struct tomoyo_path_info_with_data {
	/* Keep "head" first, for this pointer is passed to kfree(). */
	struct tomoyo_path_info head;
	char barrier1[16]; /* Safeguard for overrun. */
	char body[TOMOYO_MAX_PATHNAME_LEN];
	char barrier2[16]; /* Safeguard for overrun. */
};

struct tomoyo_name_union {
	const struct tomoyo_path_info *filename;
	struct tomoyo_path_group *group;
@@ -827,11 +787,7 @@ void tomoyo_load_policy(const char *filename);
void tomoyo_put_number_union(struct tomoyo_number_union *ptr);

/* Convert binary string to ascii string. */
int tomoyo_encode(char *buffer, int buflen, const char *str);

/* Returns realpath(3) of the given pathname but ignores chroot'ed root. */
int tomoyo_realpath_from_path2(struct path *path, char *newname,
			       int newname_len);
char *tomoyo_encode(const char *str);

/*
 * Returns realpath(3) of the given pathname but ignores chroot'ed root.
+33 −39
Original line number Diff line number Diff line
@@ -676,47 +676,43 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
int tomoyo_find_next_domain(struct linux_binprm *bprm)
{
	struct tomoyo_request_info r;
	/*
	 * This function assumes that the size of buffer returned by
	 * tomoyo_realpath() = TOMOYO_MAX_PATHNAME_LEN.
	 */
	struct tomoyo_page_buffer *tmp = kzalloc(sizeof(*tmp), GFP_NOFS);
	char *tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
	struct tomoyo_domain_info *old_domain = tomoyo_domain();
	struct tomoyo_domain_info *domain = NULL;
	const char *old_domain_name = old_domain->domainname->name;
	const char *original_name = bprm->filename;
	char *new_domain_name = NULL;
	char *real_program_name = NULL;
	char *symlink_program_name = NULL;
	const u8 mode = tomoyo_check_flags(old_domain, TOMOYO_MAC_FOR_FILE);
	const bool is_enforce = (mode == TOMOYO_CONFIG_ENFORCING);
	int retval = -ENOMEM;
	struct tomoyo_path_info rn; /* real name */
	struct tomoyo_path_info sn; /* symlink name */
	bool need_kfree = false;
	struct tomoyo_path_info rn = { }; /* real name */
	struct tomoyo_path_info sn = { }; /* symlink name */
	struct tomoyo_path_info ln; /* last name */

	ln.name = tomoyo_get_last_name(old_domain);
	tomoyo_fill_path_info(&ln);
	tomoyo_init_request_info(&r, NULL);
	if (!tmp)
		goto out;

 retry:
	if (need_kfree) {
		kfree(rn.name);
		need_kfree = false;
	}
	/* Get tomoyo_realpath of program. */
	retval = -ENOENT;
	/* I hope tomoyo_realpath() won't fail with -ENOMEM. */
	real_program_name = tomoyo_realpath(original_name);
	if (!real_program_name)
	rn.name = tomoyo_realpath(original_name);
	if (!rn.name)
		goto out;
	tomoyo_fill_path_info(&rn);
	need_kfree = true;

	/* Get tomoyo_realpath of symbolic link. */
	symlink_program_name = tomoyo_realpath_nofollow(original_name);
	if (!symlink_program_name)
	sn.name = tomoyo_realpath_nofollow(original_name);
	if (!sn.name)
		goto out;

	rn.name = real_program_name;
	tomoyo_fill_path_info(&rn);
	sn.name = symlink_program_name;
	tomoyo_fill_path_info(&sn);
	ln.name = tomoyo_get_last_name(old_domain);
	tomoyo_fill_path_info(&ln);

	/* Check 'alias' directive. */
	if (tomoyo_pathcmp(&rn, &sn)) {
@@ -727,10 +723,10 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
			    tomoyo_pathcmp(&rn, ptr->original_name) ||
			    tomoyo_pathcmp(&sn, ptr->aliased_name))
				continue;
			memset(real_program_name, 0, TOMOYO_MAX_PATHNAME_LEN);
			strncpy(real_program_name, ptr->aliased_name->name,
				TOMOYO_MAX_PATHNAME_LEN - 1);
			tomoyo_fill_path_info(&rn);
			kfree(rn.name);
			need_kfree = false;
			/* This is OK because it is read only. */
			rn = *ptr->aliased_name;
			break;
		}
	}
@@ -742,11 +738,10 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
	if (retval < 0)
		goto out;

	new_domain_name = tmp->buffer;
	if (tomoyo_is_domain_initializer(old_domain->domainname, &rn, &ln)) {
		/* Transit to the child of tomoyo_kernel_domain domain. */
		snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1,
			 TOMOYO_ROOT_NAME " " "%s", real_program_name);
		snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1,
			 TOMOYO_ROOT_NAME " " "%s", rn.name);
	} else if (old_domain == &tomoyo_kernel_domain &&
		   !tomoyo_policy_loaded) {
		/*
@@ -760,29 +755,27 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
		domain = old_domain;
	} else {
		/* Normal domain transition. */
		snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1,
			 "%s %s", old_domain_name, real_program_name);
		snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1,
			 "%s %s", old_domain_name, rn.name);
	}
	if (domain || strlen(new_domain_name) >= TOMOYO_MAX_PATHNAME_LEN)
	if (domain || strlen(tmp) >= TOMOYO_EXEC_TMPSIZE - 10)
		goto done;
	domain = tomoyo_find_domain(new_domain_name);
	domain = tomoyo_find_domain(tmp);
	if (domain)
		goto done;
	if (is_enforce) {
		int error = tomoyo_supervisor(&r, "# wants to create domain\n"
					      "%s\n", new_domain_name);
					      "%s\n", tmp);
		if (error == TOMOYO_RETRY_REQUEST)
			goto retry;
		if (error < 0)
			goto done;
	}
	domain = tomoyo_find_or_assign_new_domain(new_domain_name,
						  old_domain->profile);
	domain = tomoyo_find_or_assign_new_domain(tmp, old_domain->profile);
 done:
	if (domain)
		goto out;
	printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n",
	       new_domain_name);
	printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n", tmp);
	if (is_enforce)
		retval = -EPERM;
	else
@@ -793,8 +786,9 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
	/* Update reference count on "struct tomoyo_domain_info". */
	atomic_inc(&domain->users);
	bprm->cred->security = domain;
	kfree(real_program_name);
	kfree(symlink_program_name);
	if (need_kfree)
		kfree(rn.name);
	kfree(sn.name);
	kfree(tmp);
	return retval;
}
+58 −78
Original line number Diff line number Diff line
@@ -148,6 +148,17 @@ const char *tomoyo_path_number2keyword(const u8 operation)
		? tomoyo_path_number_keyword[operation] : NULL;
}

static void tomoyo_add_slash(struct tomoyo_path_info *buf)
{
	if (buf->is_dir)
		return;
	/*
	 * This is OK because tomoyo_encode() reserves space for appending "/".
	 */
	strcat((char *) buf->name, "/");
	tomoyo_fill_path_info(buf);
}

/**
 * tomoyo_strendswith - Check whether the token ends with the given token.
 *
@@ -167,30 +178,21 @@ static bool tomoyo_strendswith(const char *name, const char *tail)
}

/**
 * tomoyo_get_path - Get realpath.
 * tomoyo_get_realpath - Get realpath.
 *
 * @buf:  Pointer to "struct tomoyo_path_info".
 * @path: Pointer to "struct path".
 *
 * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
 * Returns true on success, false otherwise.
 */
static struct tomoyo_path_info *tomoyo_get_path(struct path *path)
static bool tomoyo_get_realpath(struct tomoyo_path_info *buf, struct path *path)
{
	int error;
	struct tomoyo_path_info_with_data *buf = kzalloc(sizeof(*buf),
							 GFP_NOFS);

	if (!buf)
		return NULL;
	/* Reserve one byte for appending "/". */
	error = tomoyo_realpath_from_path2(path, buf->body,
					   sizeof(buf->body) - 2);
	if (!error) {
		buf->head.name = buf->body;
		tomoyo_fill_path_info(&buf->head);
		return &buf->head;
	buf->name = tomoyo_realpath_from_path(path);
	if (buf->name) {
		tomoyo_fill_path_info(buf);
		return true;
	}
	kfree(buf);
	return NULL;
        return false;
}

static int tomoyo_update_path2_acl(const u8 type, const char *filename1,
@@ -1259,26 +1261,20 @@ int tomoyo_path_number_perm(const u8 type, struct path *path,
{
	struct tomoyo_request_info r;
	int error = -ENOMEM;
	struct tomoyo_path_info *buf;
	struct tomoyo_path_info buf;
	int idx;

	if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED ||
	    !path->mnt || !path->dentry)
		return 0;
	idx = tomoyo_read_lock();
	buf = tomoyo_get_path(path);
	if (!buf)
	if (!tomoyo_get_realpath(&buf, path))
		goto out;
	if (type == TOMOYO_TYPE_MKDIR && !buf->is_dir) {
		/*
		 * tomoyo_get_path() reserves space for appending "/."
		 */
		strcat((char *) buf->name, "/");
		tomoyo_fill_path_info(buf);
	}
	error = tomoyo_path_number_perm2(&r, type, buf, number);
	if (type == TOMOYO_TYPE_MKDIR)
		tomoyo_add_slash(&buf);
	error = tomoyo_path_number_perm2(&r, type, &buf, number);
 out:
	kfree(buf);
	kfree(buf.name);
	tomoyo_read_unlock(idx);
	if (r.mode != TOMOYO_CONFIG_ENFORCING)
		error = 0;
@@ -1319,7 +1315,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
{
	const u8 acc_mode = ACC_MODE(flag);
	int error = -ENOMEM;
	struct tomoyo_path_info *buf;
	struct tomoyo_path_info buf;
	struct tomoyo_request_info r;
	int idx;

@@ -1335,8 +1331,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
		 */
		return 0;
	idx = tomoyo_read_lock();
	buf = tomoyo_get_path(path);
	if (!buf)
	if (!tomoyo_get_realpath(&buf, path))
		goto out;
	error = 0;
	/*
@@ -1346,15 +1341,15 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
	 */
	if ((acc_mode & MAY_WRITE) &&
	    ((flag & O_TRUNC) || !(flag & O_APPEND)) &&
	    (tomoyo_is_no_rewrite_file(buf))) {
		error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE, buf);
	    (tomoyo_is_no_rewrite_file(&buf))) {
		error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE, &buf);
	}
	if (!error)
		error = tomoyo_file_perm(&r, buf, acc_mode);
		error = tomoyo_file_perm(&r, &buf, acc_mode);
	if (!error && (flag & O_TRUNC))
		error = tomoyo_path_permission(&r, TOMOYO_TYPE_TRUNCATE, buf);
		error = tomoyo_path_permission(&r, TOMOYO_TYPE_TRUNCATE, &buf);
 out:
	kfree(buf);
	kfree(buf.name);
	tomoyo_read_unlock(idx);
	if (r.mode != TOMOYO_CONFIG_ENFORCING)
		error = 0;
@@ -1372,7 +1367,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
int tomoyo_path_perm(const u8 operation, struct path *path)
{
	int error = -ENOMEM;
	struct tomoyo_path_info *buf;
	struct tomoyo_path_info buf;
	struct tomoyo_request_info r;
	int idx;

@@ -1380,29 +1375,23 @@ int tomoyo_path_perm(const u8 operation, struct path *path)
	    !path->mnt)
		return 0;
	idx = tomoyo_read_lock();
	buf = tomoyo_get_path(path);
	if (!buf)
	if (!tomoyo_get_realpath(&buf, path))
		goto out;
	switch (operation) {
	case TOMOYO_TYPE_REWRITE:
		if (!tomoyo_is_no_rewrite_file(buf)) {
		if (!tomoyo_is_no_rewrite_file(&buf)) {
			error = 0;
			goto out;
		}
		break;
	case TOMOYO_TYPE_RMDIR:
	case TOMOYO_TYPE_CHROOT:
		if (!buf->is_dir) {
			/*
			 * tomoyo_get_path() reserves space for appending "/."
			 */
			strcat((char *) buf->name, "/");
			tomoyo_fill_path_info(buf);
		}
		tomoyo_add_slash(&buf);
		break;
	}
	error = tomoyo_path_permission(&r, operation, buf);
	error = tomoyo_path_permission(&r, operation, &buf);
 out:
	kfree(buf);
	kfree(buf.name);
	tomoyo_read_unlock(idx);
	if (r.mode != TOMOYO_CONFIG_ENFORCING)
		error = 0;
@@ -1465,7 +1454,7 @@ int tomoyo_path_number3_perm(const u8 operation, struct path *path,
{
	struct tomoyo_request_info r;
	int error = -ENOMEM;
	struct tomoyo_path_info *buf;
	struct tomoyo_path_info buf;
	int idx;

	if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED ||
@@ -1473,11 +1462,10 @@ int tomoyo_path_number3_perm(const u8 operation, struct path *path,
		return 0;
	idx = tomoyo_read_lock();
	error = -ENOMEM;
	buf = tomoyo_get_path(path);
	if (buf) {
		error = tomoyo_path_number3_perm2(&r, operation, buf, mode,
	if (tomoyo_get_realpath(&buf, path)) {
		error = tomoyo_path_number3_perm2(&r, operation, &buf, mode,
						  new_decode_dev(dev));
		kfree(buf);
		kfree(buf.name);
	}
	tomoyo_read_unlock(idx);
	if (r.mode != TOMOYO_CONFIG_ENFORCING)
@@ -1499,48 +1487,40 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1,
{
	int error = -ENOMEM;
	const char *msg;
	struct tomoyo_path_info *buf1;
	struct tomoyo_path_info *buf2;
	struct tomoyo_path_info buf1;
	struct tomoyo_path_info buf2;
	struct tomoyo_request_info r;
	int idx;

	if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED ||
	    !path1->mnt || !path2->mnt)
		return 0;
	buf1.name = NULL;
	buf2.name = NULL;
	idx = tomoyo_read_lock();
	buf1 = tomoyo_get_path(path1);
	buf2 = tomoyo_get_path(path2);
	if (!buf1 || !buf2)
	if (!tomoyo_get_realpath(&buf1, path1) ||
	    !tomoyo_get_realpath(&buf2, path2))
		goto out;
	{
		struct dentry *dentry = path1->dentry;
		if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) {
			/*
			 * tomoyo_get_path() reserves space for appending "/."
			 */
			if (!buf1->is_dir) {
				strcat((char *) buf1->name, "/");
				tomoyo_fill_path_info(buf1);
			}
			if (!buf2->is_dir) {
				strcat((char *) buf2->name, "/");
				tomoyo_fill_path_info(buf2);
			}
			tomoyo_add_slash(&buf1);
			tomoyo_add_slash(&buf2);
		}
	}
	do {
		error = tomoyo_path2_acl(&r, operation, buf1, buf2);
		error = tomoyo_path2_acl(&r, operation, &buf1, &buf2);
		if (!error)
			break;
		msg = tomoyo_path22keyword(operation);
		tomoyo_warn_log(&r, "%s %s %s", msg, buf1->name, buf2->name);
		tomoyo_warn_log(&r, "%s %s %s", msg, buf1.name, buf2.name);
		error = tomoyo_supervisor(&r, "allow_%s %s %s\n", msg,
					  tomoyo_file_pattern(buf1),
					  tomoyo_file_pattern(buf2));
					  tomoyo_file_pattern(&buf1),
					  tomoyo_file_pattern(&buf2));
        } while (error == TOMOYO_RETRY_REQUEST);
 out:
	kfree(buf1);
	kfree(buf2);
	kfree(buf1.name);
	kfree(buf2.name);
	tomoyo_read_unlock(idx);
	if (r.mode != TOMOYO_CONFIG_ENFORCING)
		error = 0;
+0 −1
Original line number Diff line number Diff line
@@ -153,7 +153,6 @@ void __init tomoyo_mm_init(void)
{
	int idx;

	BUILD_BUG_ON(TOMOYO_MAX_PATHNAME_LEN > PATH_MAX);
	for (idx = 0; idx < TOMOYO_MAX_HASH; idx++)
		INIT_LIST_HEAD(&tomoyo_name_list[idx]);
	INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
+2 −53
Original line number Diff line number Diff line
@@ -24,57 +24,6 @@
/* Allow to call 'mount --make-shared /dir'           */
#define TOMOYO_MOUNT_MAKE_SHARED_KEYWORD                 "--make-shared"

/**
 * tomoyo_encode2: Encode binary string to ascii string.
 *
 * @str: String in binary format.
 *
 * Returns pointer to @str in ascii format on success, NULL otherwise.
 *
 * This function uses kzalloc(), so caller must kfree() if this function
 * didn't return NULL.
 */
static char *tomoyo_encode2(const char *str)
{
	int len = 0;
	const char *p = str;
	char *cp;
	char *cp0;
	if (!p)
		return NULL;
	while (*p) {
		const unsigned char c = *p++;
		if (c == '\\')
			len += 2;
		else if (c > ' ' && c < 127)
			len++;
		else
			len += 4;
	}
	len++;
	/* Reserve space for appending "/". */
	cp = kzalloc(len + 10, GFP_NOFS);
	if (!cp)
		return NULL;
	cp0 = cp;
	p = str;
	while (*p) {
		const unsigned char c = *p++;
		if (c == '\\') {
			*cp++ = '\\';
			*cp++ = '\\';
		} else if (c > ' ' && c < 127) {
			*cp++ = c;
		} else {
			*cp++ = '\\';
			*cp++ = (c >> 6) + '0';
			*cp++ = ((c >> 3) & 7) + '0';
			*cp++ = (c & 7) + '0';
		}
	}
	return cp0;
}

/**
 * tomoyo_mount_acl2 - Check permission for mount() operation.
 *
@@ -104,7 +53,7 @@ static int tomoyo_mount_acl2(struct tomoyo_request_info *r, char *dev_name,
	int error = -ENOMEM;

	/* Get fstype. */
	requested_type = tomoyo_encode2(type);
	requested_type = tomoyo_encode(type);
	if (!requested_type)
		goto out;
	rtype.name = requested_type;
@@ -155,7 +104,7 @@ static int tomoyo_mount_acl2(struct tomoyo_request_info *r, char *dev_name,
		/* Map dev_name to "<NULL>" if no dev_name given. */
		if (!dev_name)
			dev_name = "<NULL>";
		requested_dev_name = tomoyo_encode2(dev_name);
		requested_dev_name = tomoyo_encode(dev_name);
		if (!requested_dev_name) {
			error = -ENOMEM;
			goto out;
Loading