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