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