]> Pileus Git - ~andy/linux/blobdiff - security/tomoyo/realpath.c
TOMOYO: Use common code for policy reading.
[~andy/linux] / security / tomoyo / realpath.c
index 1fd685a94ad1243c0c162df511cb5d61e010e789..ed8ccd680102b53d98f9b98cb9a3aa05c8143090 100644 (file)
 #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;
+       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)
-               tomoyo_warn_oom(__func__);
-       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(TOMOYO_MAX_PATHNAME_LEN > PATH_MAX);
-       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;
 }
 
 /**