diff options
Diffstat (limited to 'security/tomoyo/realpath.c')
| -rw-r--r-- | security/tomoyo/realpath.c | 426 | 
1 files changed, 114 insertions, 312 deletions
diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index d1b96f01962..ed8ccd68010 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c @@ -1,174 +1,164 @@  /*   * security/tomoyo/realpath.c   * - * Get the canonicalized absolute pathnames. The basis for TOMOYO. - * - * Copyright (C) 2005-2009  NTT DATA CORPORATION - * - * Version: 2.2.0   2009/04/01 + * Pathname calculation functions for TOMOYO.   * + * Copyright (C) 2005-2010  NTT DATA CORPORATION   */  #include <linux/types.h>  #include <linux/mount.h>  #include <linux/mnt_namespace.h>  #include <linux/fs_struct.h> -#include <linux/hash.h>  #include <linux/magic.h>  #include <linux/slab.h> +#include <net/sock.h>  #include "common.h"  /**   * tomoyo_encode: Convert binary string to ascii string.   * - * @buffer:  Buffer for ASCII string. - * @buflen:  Size of @buffer. - * @str:     Binary string. + * @str: String in binary format. + * + * Returns pointer to @str in ascii format on success, NULL otherwise.   * - * Returns 0 on success, -ENOMEM otherwise. + * This function uses kzalloc(), so caller must kfree() if this function + * didn't return NULL.   */ -int tomoyo_encode(char *buffer, int buflen, const char *str) +char *tomoyo_encode(const char *str)  { -	while (1) { -		const unsigned char c = *(unsigned char *) str++; +	int len = 0; +	const char *p = str; +	char *cp; +	char *cp0; -		if (tomoyo_is_valid(c)) { -			if (--buflen <= 0) -				break; -			*buffer++ = (char) c; -			if (c != '\\') -				continue; -			if (--buflen <= 0) -				break; -			*buffer++ = (char) c; -			continue; -		} -		if (!c) { -			if (--buflen <= 0) -				break; -			*buffer = '\0'; -			return 0; +	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';  		} -		buflen -= 4; -		if (buflen <= 0) -			break; -		*buffer++ = '\\'; -		*buffer++ = (c >> 6) + '0'; -		*buffer++ = ((c >> 3) & 7) + '0'; -		*buffer++ = (c & 7) + '0';  	} -	return -ENOMEM; +	return cp0;  }  /** - * tomoyo_realpath_from_path2 - Returns realpath(3) of the given dentry but ignores chroot'ed root. + * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root.   * - * @path:        Pointer to "struct path". - * @newname:     Pointer to buffer to return value in. - * @newname_len: Size of @newname. + * @path: Pointer to "struct path".   * - * Returns 0 on success, negative value otherwise. + * Returns the realpath of the given @path on success, NULL otherwise.   *   * If dentry is a directory, trailing '/' is appended.   * Characters out of 0x20 < c < 0x7F range are converted to   * \ooo style octal string.   * Character \ is converted to \\ string. + * + * These functions use kzalloc(), so the caller must call kfree() + * if these functions didn't return NULL.   */ -int tomoyo_realpath_from_path2(struct path *path, char *newname, -			       int newname_len) +char *tomoyo_realpath_from_path(struct path *path)  { -	int error = -ENOMEM; +	char *buf = NULL; +	char *name = NULL; +	unsigned int buf_len = PAGE_SIZE / 2;  	struct dentry *dentry = path->dentry; -	char *sp; - -	if (!dentry || !path->mnt || !newname || newname_len <= 2048) -		return -EINVAL; -	if (dentry->d_op && dentry->d_op->d_dname) { +	bool is_dir; +	if (!dentry) +		return NULL; +	is_dir = dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode); +	while (1) { +		struct path ns_root = { .mnt = NULL, .dentry = NULL }; +		char *pos; +		buf_len <<= 1; +		kfree(buf); +		buf = kmalloc(buf_len, GFP_NOFS); +		if (!buf) +			break; +		/* Get better name for socket. */ +		if (dentry->d_sb && dentry->d_sb->s_magic == SOCKFS_MAGIC) { +			struct inode *inode = dentry->d_inode; +			struct socket *sock = inode ? SOCKET_I(inode) : NULL; +			struct sock *sk = sock ? sock->sk : NULL; +			if (sk) { +				snprintf(buf, buf_len - 1, "socket:[family=%u:" +					 "type=%u:protocol=%u]", sk->sk_family, +					 sk->sk_type, sk->sk_protocol); +			} else { +				snprintf(buf, buf_len - 1, "socket:[unknown]"); +			} +			name = tomoyo_encode(buf); +			break; +		}  		/* For "socket:[\$]" and "pipe:[\$]". */ -		static const int offset = 1536; -		sp = dentry->d_op->d_dname(dentry, newname + offset, -					   newname_len - offset); -	} else { -		struct path ns_root = {.mnt = NULL, .dentry = NULL}; - +		if (dentry->d_op && dentry->d_op->d_dname) { +			pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1); +			if (IS_ERR(pos)) +				continue; +			name = tomoyo_encode(pos); +			break; +		} +		/* If we don't have a vfsmount, we can't calculate. */ +		if (!path->mnt) +			break;  		spin_lock(&dcache_lock);  		/* go to whatever namespace root we are under */ -		sp = __d_path(path, &ns_root, newname, newname_len); +		pos = __d_path(path, &ns_root, buf, buf_len);  		spin_unlock(&dcache_lock);  		/* Prepend "/proc" prefix if using internal proc vfs mount. */ -		if (!IS_ERR(sp) && (path->mnt->mnt_flags & MNT_INTERNAL) && +		if (!IS_ERR(pos) && (path->mnt->mnt_flags & MNT_INTERNAL) &&  		    (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) { -			sp -= 5; -			if (sp >= newname) -				memcpy(sp, "/proc", 5); +			pos -= 5; +			if (pos >= buf) +				memcpy(pos, "/proc", 5);  			else -				sp = ERR_PTR(-ENOMEM); -		} -	} -	if (IS_ERR(sp)) -		error = PTR_ERR(sp); -	else -		error = tomoyo_encode(newname, sp - newname, sp); -	/* Append trailing '/' if dentry is a directory. */ -	if (!error && dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode) -	    && *newname) { -		sp = newname + strlen(newname); -		if (*(sp - 1) != '/') { -			if (sp < newname + newname_len - 4) { -				*sp++ = '/'; -				*sp = '\0'; -			} else { -				error = -ENOMEM; -			} +				pos = ERR_PTR(-ENOMEM);  		} +		if (IS_ERR(pos)) +			continue; +		name = tomoyo_encode(pos); +		break;  	} -	if (error) -		printk(KERN_WARNING "tomoyo_realpath: Pathname too long.\n"); -	return error; -} - -/** - * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root. - * - * @path: Pointer to "struct path". - * - * Returns the realpath of the given @path on success, NULL otherwise. - * - * These functions use kzalloc(), so the caller must call kfree() - * if these functions didn't return NULL. - */ -char *tomoyo_realpath_from_path(struct path *path) -{ -	char *buf = kzalloc(sizeof(struct tomoyo_page_buffer), GFP_NOFS); - -	BUILD_BUG_ON(sizeof(struct tomoyo_page_buffer) -		     <= TOMOYO_MAX_PATHNAME_LEN - 1); -	if (!buf) -		return NULL; -	if (tomoyo_realpath_from_path2(path, buf, -				       TOMOYO_MAX_PATHNAME_LEN - 1) == 0) -		return buf;  	kfree(buf); -	return NULL; -} - -/** - * tomoyo_realpath - Get realpath of a pathname. - * - * @pathname: The pathname to solve. - * - * Returns the realpath of @pathname on success, NULL otherwise. - */ -char *tomoyo_realpath(const char *pathname) -{ -	struct path path; - -	if (pathname && kern_path(pathname, LOOKUP_FOLLOW, &path) == 0) { -		char *buf = tomoyo_realpath_from_path(&path); -		path_put(&path); -		return buf; +	if (!name) +		tomoyo_warn_oom(__func__); +	else if (is_dir && *name) { +		/* Append trailing '/' if dentry is a directory. */ +		char *pos = name + strlen(name) - 1; +		if (*pos != '/') +			/* +			 * This is OK because tomoyo_encode() reserves space +			 * for appending "/". +			 */ +			*++pos = '/';  	} -	return NULL; +	return name;  }  /** @@ -189,191 +179,3 @@ char *tomoyo_realpath_nofollow(const char *pathname)  	}  	return NULL;  } - -/* Memory allocated for non-string data. */ -static atomic_t tomoyo_policy_memory_size; -/* Quota for holding policy. */ -static unsigned int tomoyo_quota_for_policy; - -/** - * tomoyo_memory_ok - Check memory quota. - * - * @ptr: Pointer to allocated memory. - * - * Returns true on success, false otherwise. - * - * Caller holds tomoyo_policy_lock. - * Memory pointed by @ptr will be zeroed on success. - */ -bool tomoyo_memory_ok(void *ptr) -{ -	int allocated_len = ptr ? ksize(ptr) : 0; -	atomic_add(allocated_len, &tomoyo_policy_memory_size); -	if (ptr && (!tomoyo_quota_for_policy || -		    atomic_read(&tomoyo_policy_memory_size) -		    <= tomoyo_quota_for_policy)) { -		memset(ptr, 0, allocated_len); -		return true; -	} -	printk(KERN_WARNING "ERROR: Out of memory " -	       "for tomoyo_alloc_element().\n"); -	if (!tomoyo_policy_loaded) -		panic("MAC Initialization failed.\n"); -	return false; -} - -/** - * tomoyo_commit_ok - Check memory quota. - * - * @data:   Data to copy from. - * @size:   Size in byte. - * - * Returns pointer to allocated memory on success, NULL otherwise. - */ -void *tomoyo_commit_ok(void *data, const unsigned int size) -{ -	void *ptr = kzalloc(size, GFP_NOFS); -	if (tomoyo_memory_ok(ptr)) { -		memmove(ptr, data, size); -		memset(data, 0, size); -		return ptr; -	} -	return NULL; -} - -/** - * tomoyo_memory_free - Free memory for elements. - * - * @ptr:  Pointer to allocated memory. - */ -void tomoyo_memory_free(void *ptr) -{ -	atomic_sub(ksize(ptr), &tomoyo_policy_memory_size); -	kfree(ptr); -} - -/* - * tomoyo_name_list is used for holding string data used by TOMOYO. - * Since same string data is likely used for multiple times (e.g. - * "/lib/libc-2.5.so"), TOMOYO shares string data in the form of - * "const struct tomoyo_path_info *". - */ -struct list_head tomoyo_name_list[TOMOYO_MAX_HASH]; - -/** - * tomoyo_get_name - Allocate permanent memory for string data. - * - * @name: The string to store into the permernent memory. - * - * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise. - */ -const struct tomoyo_path_info *tomoyo_get_name(const char *name) -{ -	struct tomoyo_name_entry *ptr; -	unsigned int hash; -	int len; -	int allocated_len; -	struct list_head *head; - -	if (!name) -		return NULL; -	len = strlen(name) + 1; -	hash = full_name_hash((const unsigned char *) name, len - 1); -	head = &tomoyo_name_list[hash_long(hash, TOMOYO_HASH_BITS)]; -	if (mutex_lock_interruptible(&tomoyo_policy_lock)) -		return NULL; -	list_for_each_entry(ptr, head, list) { -		if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name)) -			continue; -		atomic_inc(&ptr->users); -		goto out; -	} -	ptr = kzalloc(sizeof(*ptr) + len, GFP_NOFS); -	allocated_len = ptr ? ksize(ptr) : 0; -	if (!ptr || (tomoyo_quota_for_policy && -		     atomic_read(&tomoyo_policy_memory_size) + allocated_len -		     > tomoyo_quota_for_policy)) { -		kfree(ptr); -		printk(KERN_WARNING "ERROR: Out of memory " -		       "for tomoyo_get_name().\n"); -		if (!tomoyo_policy_loaded) -			panic("MAC Initialization failed.\n"); -		ptr = NULL; -		goto out; -	} -	atomic_add(allocated_len, &tomoyo_policy_memory_size); -	ptr->entry.name = ((char *) ptr) + sizeof(*ptr); -	memmove((char *) ptr->entry.name, name, len); -	atomic_set(&ptr->users, 1); -	tomoyo_fill_path_info(&ptr->entry); -	list_add_tail(&ptr->list, head); - out: -	mutex_unlock(&tomoyo_policy_lock); -	return ptr ? &ptr->entry : NULL; -} - -/** - * tomoyo_realpath_init - Initialize realpath related code. - */ -void __init tomoyo_realpath_init(void) -{ -	int i; - -	BUILD_BUG_ON(TOMOYO_MAX_PATHNAME_LEN > PATH_MAX); -	for (i = 0; i < TOMOYO_MAX_HASH; i++) -		INIT_LIST_HEAD(&tomoyo_name_list[i]); -	INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list); -	tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME); -	/* -	 * tomoyo_read_lock() is not needed because this function is -	 * called before the first "delete" request. -	 */ -	list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list); -	if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain) -		panic("Can't register tomoyo_kernel_domain"); -} - -/** - * tomoyo_read_memory_counter - Check for memory usage in bytes. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns memory usage. - */ -int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head) -{ -	if (!head->read_eof) { -		const unsigned int policy -			= atomic_read(&tomoyo_policy_memory_size); -		char buffer[64]; - -		memset(buffer, 0, sizeof(buffer)); -		if (tomoyo_quota_for_policy) -			snprintf(buffer, sizeof(buffer) - 1, -				 "   (Quota: %10u)", -				 tomoyo_quota_for_policy); -		else -			buffer[0] = '\0'; -		tomoyo_io_printf(head, "Policy:  %10u%s\n", policy, buffer); -		tomoyo_io_printf(head, "Total:   %10u\n", policy); -		head->read_eof = true; -	} -	return 0; -} - -/** - * tomoyo_write_memory_quota - Set memory quota. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns 0. - */ -int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head) -{ -	char *data = head->write_buf; -	unsigned int size; - -	if (sscanf(data, "Policy: %u", &size) == 1) -		tomoyo_quota_for_policy = size; -	return 0; -}  | 
