linux/security/tomoyo/realpath.c
<<
>>
Prefs
   1/*
   2 * security/tomoyo/realpath.c
   3 *
   4 * Copyright (C) 2005-2011  NTT DATA CORPORATION
   5 */
   6
   7#include "common.h"
   8#include <linux/magic.h>
   9
  10/**
  11 * tomoyo_encode2 - Encode binary string to ascii string.
  12 *
  13 * @str:     String in binary format.
  14 * @str_len: Size of @str in byte.
  15 *
  16 * Returns pointer to @str in ascii format on success, NULL otherwise.
  17 *
  18 * This function uses kzalloc(), so caller must kfree() if this function
  19 * didn't return NULL.
  20 */
  21char *tomoyo_encode2(const char *str, int str_len)
  22{
  23        int i;
  24        int len = 0;
  25        const char *p = str;
  26        char *cp;
  27        char *cp0;
  28
  29        if (!p)
  30                return NULL;
  31        for (i = 0; i < str_len; i++) {
  32                const unsigned char c = p[i];
  33
  34                if (c == '\\')
  35                        len += 2;
  36                else if (c > ' ' && c < 127)
  37                        len++;
  38                else
  39                        len += 4;
  40        }
  41        len++;
  42        /* Reserve space for appending "/". */
  43        cp = kzalloc(len + 10, GFP_NOFS);
  44        if (!cp)
  45                return NULL;
  46        cp0 = cp;
  47        p = str;
  48        for (i = 0; i < str_len; i++) {
  49                const unsigned char c = p[i];
  50
  51                if (c == '\\') {
  52                        *cp++ = '\\';
  53                        *cp++ = '\\';
  54                } else if (c > ' ' && c < 127) {
  55                        *cp++ = c;
  56                } else {
  57                        *cp++ = '\\';
  58                        *cp++ = (c >> 6) + '0';
  59                        *cp++ = ((c >> 3) & 7) + '0';
  60                        *cp++ = (c & 7) + '0';
  61                }
  62        }
  63        return cp0;
  64}
  65
  66/**
  67 * tomoyo_encode - Encode binary string to ascii string.
  68 *
  69 * @str: String in binary format.
  70 *
  71 * Returns pointer to @str in ascii format on success, NULL otherwise.
  72 *
  73 * This function uses kzalloc(), so caller must kfree() if this function
  74 * didn't return NULL.
  75 */
  76char *tomoyo_encode(const char *str)
  77{
  78        return str ? tomoyo_encode2(str, strlen(str)) : NULL;
  79}
  80
  81/**
  82 * tomoyo_get_absolute_path - Get the path of a dentry but ignores chroot'ed root.
  83 *
  84 * @path:   Pointer to "struct path".
  85 * @buffer: Pointer to buffer to return value in.
  86 * @buflen: Sizeof @buffer.
  87 *
  88 * Returns the buffer on success, an error code otherwise.
  89 *
  90 * If dentry is a directory, trailing '/' is appended.
  91 */
  92static char *tomoyo_get_absolute_path(const struct path *path, char * const buffer,
  93                                      const int buflen)
  94{
  95        char *pos = ERR_PTR(-ENOMEM);
  96        if (buflen >= 256) {
  97                /* go to whatever namespace root we are under */
  98                pos = d_absolute_path(path, buffer, buflen - 1);
  99                if (!IS_ERR(pos) && *pos == '/' && pos[1]) {
 100                        struct inode *inode = d_backing_inode(path->dentry);
 101                        if (inode && S_ISDIR(inode->i_mode)) {
 102                                buffer[buflen - 2] = '/';
 103                                buffer[buflen - 1] = '\0';
 104                        }
 105                }
 106        }
 107        return pos;
 108}
 109
 110/**
 111 * tomoyo_get_dentry_path - Get the path of a dentry.
 112 *
 113 * @dentry: Pointer to "struct dentry".
 114 * @buffer: Pointer to buffer to return value in.
 115 * @buflen: Sizeof @buffer.
 116 *
 117 * Returns the buffer on success, an error code otherwise.
 118 *
 119 * If dentry is a directory, trailing '/' is appended.
 120 */
 121static char *tomoyo_get_dentry_path(struct dentry *dentry, char * const buffer,
 122                                    const int buflen)
 123{
 124        char *pos = ERR_PTR(-ENOMEM);
 125        if (buflen >= 256) {
 126                pos = dentry_path_raw(dentry, buffer, buflen - 1);
 127                if (!IS_ERR(pos) && *pos == '/' && pos[1]) {
 128                        struct inode *inode = d_backing_inode(dentry);
 129                        if (inode && S_ISDIR(inode->i_mode)) {
 130                                buffer[buflen - 2] = '/';
 131                                buffer[buflen - 1] = '\0';
 132                        }
 133                }
 134        }
 135        return pos;
 136}
 137
 138/**
 139 * tomoyo_get_local_path - Get the path of a dentry.
 140 *
 141 * @dentry: Pointer to "struct dentry".
 142 * @buffer: Pointer to buffer to return value in.
 143 * @buflen: Sizeof @buffer.
 144 *
 145 * Returns the buffer on success, an error code otherwise.
 146 */
 147static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer,
 148                                   const int buflen)
 149{
 150        struct super_block *sb = dentry->d_sb;
 151        char *pos = tomoyo_get_dentry_path(dentry, buffer, buflen);
 152        if (IS_ERR(pos))
 153                return pos;
 154        /* Convert from $PID to self if $PID is current thread. */
 155        if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') {
 156                char *ep;
 157                const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10);
 158                if (*ep == '/' && pid && pid ==
 159                    task_tgid_nr_ns(current, sb->s_fs_info)) {
 160                        pos = ep - 5;
 161                        if (pos < buffer)
 162                                goto out;
 163                        memmove(pos, "/self", 5);
 164                }
 165                goto prepend_filesystem_name;
 166        }
 167        /* Use filesystem name for unnamed devices. */
 168        if (!MAJOR(sb->s_dev))
 169                goto prepend_filesystem_name;
 170        {
 171                struct inode *inode = d_backing_inode(sb->s_root);
 172                /*
 173                 * Use filesystem name if filesystem does not support rename()
 174                 * operation.
 175                 */
 176                if (!inode->i_op->rename)
 177                        goto prepend_filesystem_name;
 178        }
 179        /* Prepend device name. */
 180        {
 181                char name[64];
 182                int name_len;
 183                const dev_t dev = sb->s_dev;
 184                name[sizeof(name) - 1] = '\0';
 185                snprintf(name, sizeof(name) - 1, "dev(%u,%u):", MAJOR(dev),
 186                         MINOR(dev));
 187                name_len = strlen(name);
 188                pos -= name_len;
 189                if (pos < buffer)
 190                        goto out;
 191                memmove(pos, name, name_len);
 192                return pos;
 193        }
 194        /* Prepend filesystem name. */
 195prepend_filesystem_name:
 196        {
 197                const char *name = sb->s_type->name;
 198                const int name_len = strlen(name);
 199                pos -= name_len + 1;
 200                if (pos < buffer)
 201                        goto out;
 202                memmove(pos, name, name_len);
 203                pos[name_len] = ':';
 204        }
 205        return pos;
 206out:
 207        return ERR_PTR(-ENOMEM);
 208}
 209
 210/**
 211 * tomoyo_get_socket_name - Get the name of a socket.
 212 *
 213 * @path:   Pointer to "struct path".
 214 * @buffer: Pointer to buffer to return value in.
 215 * @buflen: Sizeof @buffer.
 216 *
 217 * Returns the buffer.
 218 */
 219static char *tomoyo_get_socket_name(const struct path *path, char * const buffer,
 220                                    const int buflen)
 221{
 222        struct inode *inode = d_backing_inode(path->dentry);
 223        struct socket *sock = inode ? SOCKET_I(inode) : NULL;
 224        struct sock *sk = sock ? sock->sk : NULL;
 225        if (sk) {
 226                snprintf(buffer, buflen, "socket:[family=%u:type=%u:"
 227                         "protocol=%u]", sk->sk_family, sk->sk_type,
 228                         sk->sk_protocol);
 229        } else {
 230                snprintf(buffer, buflen, "socket:[unknown]");
 231        }
 232        return buffer;
 233}
 234
 235/**
 236 * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root.
 237 *
 238 * @path: Pointer to "struct path".
 239 *
 240 * Returns the realpath of the given @path on success, NULL otherwise.
 241 *
 242 * If dentry is a directory, trailing '/' is appended.
 243 * Characters out of 0x20 < c < 0x7F range are converted to
 244 * \ooo style octal string.
 245 * Character \ is converted to \\ string.
 246 *
 247 * These functions use kzalloc(), so the caller must call kfree()
 248 * if these functions didn't return NULL.
 249 */
 250char *tomoyo_realpath_from_path(const struct path *path)
 251{
 252        char *buf = NULL;
 253        char *name = NULL;
 254        unsigned int buf_len = PAGE_SIZE / 2;
 255        struct dentry *dentry = path->dentry;
 256        struct super_block *sb;
 257        if (!dentry)
 258                return NULL;
 259        sb = dentry->d_sb;
 260        while (1) {
 261                char *pos;
 262                struct inode *inode;
 263                buf_len <<= 1;
 264                kfree(buf);
 265                buf = kmalloc(buf_len, GFP_NOFS);
 266                if (!buf)
 267                        break;
 268                /* To make sure that pos is '\0' terminated. */
 269                buf[buf_len - 1] = '\0';
 270                /* Get better name for socket. */
 271                if (sb->s_magic == SOCKFS_MAGIC) {
 272                        pos = tomoyo_get_socket_name(path, buf, buf_len - 1);
 273                        goto encode;
 274                }
 275                /* For "pipe:[\$]". */
 276                if (dentry->d_op && dentry->d_op->d_dname) {
 277                        pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1);
 278                        goto encode;
 279                }
 280                inode = d_backing_inode(sb->s_root);
 281                /*
 282                 * Get local name for filesystems without rename() operation
 283                 * or dentry without vfsmount.
 284                 */
 285                if (!path->mnt ||
 286                    (!inode->i_op->rename))
 287                        pos = tomoyo_get_local_path(path->dentry, buf,
 288                                                    buf_len - 1);
 289                /* Get absolute name for the rest. */
 290                else {
 291                        pos = tomoyo_get_absolute_path(path, buf, buf_len - 1);
 292                        /*
 293                         * Fall back to local name if absolute name is not
 294                         * available.
 295                         */
 296                        if (pos == ERR_PTR(-EINVAL))
 297                                pos = tomoyo_get_local_path(path->dentry, buf,
 298                                                            buf_len - 1);
 299                }
 300encode:
 301                if (IS_ERR(pos))
 302                        continue;
 303                name = tomoyo_encode(pos);
 304                break;
 305        }
 306        kfree(buf);
 307        if (!name)
 308                tomoyo_warn_oom(__func__);
 309        return name;
 310}
 311
 312/**
 313 * tomoyo_realpath_nofollow - Get realpath of a pathname.
 314 *
 315 * @pathname: The pathname to solve.
 316 *
 317 * Returns the realpath of @pathname on success, NULL otherwise.
 318 */
 319char *tomoyo_realpath_nofollow(const char *pathname)
 320{
 321        struct path path;
 322
 323        if (pathname && kern_path(pathname, 0, &path) == 0) {
 324                char *buf = tomoyo_realpath_from_path(&path);
 325                path_put(&path);
 326                return buf;
 327        }
 328        return NULL;
 329}
 330