310 lines · 7.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * security/tomoyo/realpath.c * * Copyright (C) 2005-2011  NTT DATA CORPORATION */ #include "common.h"#include <linux/magic.h>#include <linux/proc_fs.h> /** * tomoyo_encode2 - Encode binary string to ascii string. * * @str:     String in binary format. * @str_len: Size of @str in byte. * * 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. */char *tomoyo_encode2(const char *str, int str_len){	int i;	int len = 0;	const char *p = str;	char *cp;	char *cp0; 	if (!p)		return NULL;	for (i = 0; i < str_len; i++) {		const unsigned char c = p[i]; 		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;	for (i = 0; i < str_len; i++) {		const unsigned char c = p[i]; 		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_encode - 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. */char *tomoyo_encode(const char *str){	return str ? tomoyo_encode2(str, strlen(str)) : NULL;} /** * tomoyo_get_absolute_path - Get the path of a dentry but ignores chroot'ed root. * * @path:   Pointer to "struct path". * @buffer: Pointer to buffer to return value in. * @buflen: Sizeof @buffer. * * Returns the buffer on success, an error code otherwise. * * If dentry is a directory, trailing '/' is appended. */static char *tomoyo_get_absolute_path(const struct path *path, char * const buffer,				      const int buflen){	char *pos = ERR_PTR(-ENOMEM); 	if (buflen >= 256) {		/* go to whatever namespace root we are under */		pos = d_absolute_path(path, buffer, buflen - 1);		if (!IS_ERR(pos) && *pos == '/' && pos[1]) {			struct inode *inode = d_backing_inode(path->dentry); 			if (inode && S_ISDIR(inode->i_mode)) {				buffer[buflen - 2] = '/';				buffer[buflen - 1] = '\0';			}		}	}	return pos;} /** * tomoyo_get_dentry_path - Get the path of a dentry. * * @dentry: Pointer to "struct dentry". * @buffer: Pointer to buffer to return value in. * @buflen: Sizeof @buffer. * * Returns the buffer on success, an error code otherwise. * * If dentry is a directory, trailing '/' is appended. */static char *tomoyo_get_dentry_path(struct dentry *dentry, char * const buffer,				    const int buflen){	char *pos = ERR_PTR(-ENOMEM); 	if (buflen >= 256) {		pos = dentry_path_raw(dentry, buffer, buflen - 1);		if (!IS_ERR(pos) && *pos == '/' && pos[1]) {			struct inode *inode = d_backing_inode(dentry); 			if (inode && S_ISDIR(inode->i_mode)) {				buffer[buflen - 2] = '/';				buffer[buflen - 1] = '\0';			}		}	}	return pos;} /** * tomoyo_get_local_path - Get the path of a dentry. * * @dentry: Pointer to "struct dentry". * @buffer: Pointer to buffer to return value in. * @buflen: Sizeof @buffer. * * Returns the buffer on success, an error code otherwise. */static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer,				   const int buflen){	struct super_block *sb = dentry->d_sb;	char *pos = tomoyo_get_dentry_path(dentry, buffer, buflen); 	if (IS_ERR(pos))		return pos;	/* Convert from $PID to self if $PID is current thread. */	if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') {		char *ep;		const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10);		struct pid_namespace *proc_pidns = proc_pid_ns(sb); 		if (*ep == '/' && pid && pid ==		    task_tgid_nr_ns(current, proc_pidns)) {			pos = ep - 5;			if (pos < buffer)				goto out;			memmove(pos, "/self", 5);		}		goto prepend_filesystem_name;	}	/* Use filesystem name for unnamed devices. */	if (!MAJOR(sb->s_dev))		goto prepend_filesystem_name;	{		struct inode *inode = d_backing_inode(sb->s_root); 		/*		 * Use filesystem name if filesystem does not support rename()		 * operation.		 */		if (!inode->i_op->rename)			goto prepend_filesystem_name;	}	/* Prepend device name. */	{		char name[64];		int name_len;		const dev_t dev = sb->s_dev; 		name[sizeof(name) - 1] = '\0';		snprintf(name, sizeof(name) - 1, "dev(%u,%u):", MAJOR(dev),			 MINOR(dev));		name_len = strlen(name);		pos -= name_len;		if (pos < buffer)			goto out;		memmove(pos, name, name_len);		return pos;	}	/* Prepend filesystem name. */prepend_filesystem_name:	{		const char *name = sb->s_type->name;		const int name_len = strlen(name); 		pos -= name_len + 1;		if (pos < buffer)			goto out;		memmove(pos, name, name_len);		pos[name_len] = ':';	}	return pos;out:	return ERR_PTR(-ENOMEM);} /** * 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. * * 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. */char *tomoyo_realpath_from_path(const struct path *path){	char *buf = NULL;	char *name = NULL;	unsigned int buf_len = PAGE_SIZE / 2;	struct dentry *dentry = path->dentry;	struct super_block *sb = dentry->d_sb; 	while (1) {		char *pos;		struct inode *inode; 		buf_len <<= 1;		kfree(buf);		buf = kmalloc(buf_len, GFP_NOFS);		if (!buf)			break;		/* To make sure that pos is '\0' terminated. */		buf[buf_len - 1] = '\0';		/* For "pipe:[\$]" and "socket:[\$]". */		if (dentry->d_op && dentry->d_op->d_dname) {			pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1);			goto encode;		}		inode = d_backing_inode(sb->s_root);		/*		 * Get local name for filesystems without rename() operation		 */		if ((!inode->i_op->rename &&		     !(sb->s_type->fs_flags & FS_REQUIRES_DEV)))			pos = tomoyo_get_local_path(path->dentry, buf,						    buf_len - 1);		/* Get absolute name for the rest. */		else {			pos = tomoyo_get_absolute_path(path, buf, buf_len - 1);			/*			 * Fall back to local name if absolute name is not			 * available.			 */			if (pos == ERR_PTR(-EINVAL))				pos = tomoyo_get_local_path(path->dentry, buf,							    buf_len - 1);		}encode:		if (IS_ERR(pos))			continue;		name = tomoyo_encode(pos);		break;	}	kfree(buf);	if (!name)		tomoyo_warn_oom(__func__);	return name;} /** * tomoyo_realpath_nofollow - Get realpath of a pathname. * * @pathname: The pathname to solve. * * Returns the realpath of @pathname on success, NULL otherwise. */char *tomoyo_realpath_nofollow(const char *pathname){	struct path path; 	if (pathname && kern_path(pathname, 0, &path) == 0) {		char *buf = tomoyo_realpath_from_path(&path); 		path_put(&path);		return buf;	}	return NULL;}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t