TOMOYO: Support longer pathname.
Allow pathnames longer than 4000 bytes.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: James Morris <jmorris@namei.org>
diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c
index 1fd685a..153fa23 100644
--- a/security/tomoyo/realpath.c
+++ b/security/tomoyo/realpath.c
@@ -12,115 +12,60 @@
#include <linux/fs_struct.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 0 on success, -ENOMEM otherwise.
+ * 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.
*/
-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;
- }
- buflen -= 4;
- if (buflen <= 0)
- break;
- *buffer++ = '\\';
- *buffer++ = (c >> 6) + '0';
- *buffer++ = ((c >> 3) & 7) + '0';
- *buffer++ = (c & 7) + '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;
}
- return -ENOMEM;
-}
+ 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++;
-/**
- * tomoyo_realpath_from_path2 - Returns realpath(3) of the given dentry but ignores chroot'ed root.
- *
- * @path: Pointer to "struct path".
- * @newname: Pointer to buffer to return value in.
- * @newname_len: Size of @newname.
- *
- * Returns 0 on success, negative value 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.
- */
-int tomoyo_realpath_from_path2(struct path *path, char *newname,
- int newname_len)
-{
- int error = -ENOMEM;
- 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) {
- /* 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};
-
- spin_lock(&dcache_lock);
- /* go to whatever namespace root we are under */
- sp = __d_path(path, &ns_root, newname, newname_len);
- spin_unlock(&dcache_lock);
- /* Prepend "/proc" prefix if using internal proc vfs mount. */
- if (!IS_ERR(sp) && (path->mnt->mnt_flags & MNT_INTERNAL) &&
- (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) {
- sp -= 5;
- if (sp >= newname)
- memcpy(sp, "/proc", 5);
- else
- sp = ERR_PTR(-ENOMEM);
+ 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';
}
}
- 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;
- }
- }
- }
- if (error)
- tomoyo_warn_oom(__func__);
- return error;
+ return cp0;
}
/**
@@ -130,23 +75,90 @@
*
* 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(struct path *path)
{
- char *buf = kzalloc(sizeof(struct tomoyo_page_buffer), GFP_NOFS);
-
- BUILD_BUG_ON(TOMOYO_MAX_PATHNAME_LEN > PATH_MAX);
- BUILD_BUG_ON(sizeof(struct tomoyo_page_buffer)
- <= TOMOYO_MAX_PATHNAME_LEN - 1);
- if (!buf)
+ char *buf = NULL;
+ char *name = NULL;
+ unsigned int buf_len = PAGE_SIZE / 2;
+ struct dentry *dentry = path->dentry;
+ bool is_dir;
+ if (!dentry)
return NULL;
- if (tomoyo_realpath_from_path2(path, buf,
- TOMOYO_MAX_PATHNAME_LEN - 1) == 0)
- return buf;
+ 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:[\$]". */
+ 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 */
+ 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(pos) && (path->mnt->mnt_flags & MNT_INTERNAL) &&
+ (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) {
+ pos -= 5;
+ if (pos >= buf)
+ memcpy(pos, "/proc", 5);
+ else
+ pos = ERR_PTR(-ENOMEM);
+ }
+ if (IS_ERR(pos))
+ continue;
+ name = tomoyo_encode(pos);
+ break;
+ }
kfree(buf);
- return NULL;
+ 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 name;
}
/**