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
  98        if (buflen >= 256) {
  99                /* go to whatever namespace root we are under */
 100                pos = d_absolute_path(path, buffer, buflen - 1);
 101                if (!IS_ERR(pos) && *pos == '/' && pos[1]) {
 102                        struct inode *inode = d_backing_inode(path->dentry);
 103
 104                        if (inode && S_ISDIR(inode->i_mode)) {
 105                                buffer[buflen - 2] = '/';
 106                                buffer[buflen - 1] = '\0';
 107                        }
 108                }
 109        }
 110        return pos;
 111}
 112
 113/**
 114 * tomoyo_get_dentry_path - Get the path of a dentry.
 115 *
 116 * @dentry: Pointer to "struct dentry".
 117 * @buffer: Pointer to buffer to return value in.
 118 * @buflen: Sizeof @buffer.
 119 *
 120 * Returns the buffer on success, an error code otherwise.
 121 *
 122 * If dentry is a directory, trailing '/' is appended.
 123 */
 124static char *tomoyo_get_dentry_path(struct dentry *dentry, char * const buffer,
 125                                    const int buflen)
 126{
 127        char *pos = ERR_PTR(-ENOMEM);
 128
 129        if (buflen >= 256) {
 130                pos = dentry_path_raw(dentry, buffer, buflen - 1);
 131                if (!IS_ERR(pos) && *pos == '/' && pos[1]) {
 132                        struct inode *inode = d_backing_inode(dentry);
 133
 134                        if (inode && S_ISDIR(inode->i_mode)) {
 135                                buffer[buflen - 2] = '/';
 136                                buffer[buflen - 1] = '\0';
 137                        }
 138                }
 139        }
 140        return pos;
 141}
 142
 143/**
 144 * tomoyo_get_local_path - Get the path of a dentry.
 145 *
 146 * @dentry: Pointer to "struct dentry".
 147 * @buffer: Pointer to buffer to return value in.
 148 * @buflen: Sizeof @buffer.
 149 *
 150 * Returns the buffer on success, an error code otherwise.
 151 */
 152static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer,
 153                                   const int buflen)
 154{
 155        struct super_block *sb = dentry->d_sb;
 156        char *pos = tomoyo_get_dentry_path(dentry, buffer, buflen);
 157
 158        if (IS_ERR(pos))
 159                return pos;
 160        /* Convert from $PID to self if $PID is current thread. */
 161        if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') {
 162                char *ep;
 163                const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10);
 164
 165                if (*ep == '/' && pid && pid ==
 166                    task_tgid_nr_ns(current, sb->s_fs_info)) {
 167                        pos = ep - 5;
 168                        if (pos < buffer)
 169                                goto out;
 170                        memmove(pos, "/self", 5);
 171                }
 172                goto prepend_filesystem_name;
 173        }
 174        /* Use filesystem name for unnamed devices. */
 175        if (!MAJOR(sb->s_dev))
 176                goto prepend_filesystem_name;
 177        {
 178                struct inode *inode = d_backing_inode(sb->s_root);
 179
 180                /*
 181                 * Use filesystem name if filesystem does not support rename()
 182                 * operation.
 183                 */
 184                if (!inode->i_op->rename)
 185                        goto prepend_filesystem_name;
 186        }
 187        /* Prepend device name. */
 188        {
 189                char name[64];
 190                int name_len;
 191                const dev_t dev = sb->s_dev;
 192
 193                name[sizeof(name) - 1] = '\0';
 194                snprintf(name, sizeof(name) - 1, "dev(%u,%u):", MAJOR(dev),
 195                         MINOR(dev));
 196                name_len = strlen(name);
 197                pos -= name_len;
 198                if (pos < buffer)
 199                        goto out;
 200                memmove(pos, name, name_len);
 201                return pos;
 202        }
 203        /* Prepend filesystem name. */
 204prepend_filesystem_name:
 205        {
 206                const char *name = sb->s_type->name;
 207                const int name_len = strlen(name);
 208
 209                pos -= name_len + 1;
 210                if (pos < buffer)
 211                        goto out;
 212                memmove(pos, name, name_len);
 213                pos[name_len] = ':';
 214        }
 215        return pos;
 216out:
 217        return ERR_PTR(-ENOMEM);
 218}
 219
 220/**
 221 * tomoyo_get_socket_name - Get the name of a socket.
 222 *
 223 * @path:   Pointer to "struct path".
 224 * @buffer: Pointer to buffer to return value in.
 225 * @buflen: Sizeof @buffer.
 226 *
 227 * Returns the buffer.
 228 */
 229static char *tomoyo_get_socket_name(const struct path *path, char * const buffer,
 230                                    const int buflen)
 231{
 232        struct inode *inode = d_backing_inode(path->dentry);
 233        struct socket *sock = inode ? SOCKET_I(inode) : NULL;
 234        struct sock *sk = sock ? sock->sk : NULL;
 235
 236        if (sk) {
 237                snprintf(buffer, buflen, "socket:[family=%u:type=%u:protocol=%u]",
 238                         sk->sk_family, sk->sk_type, sk->sk_protocol);
 239        } else {
 240                snprintf(buffer, buflen, "socket:[unknown]");
 241        }
 242        return buffer;
 243}
 244
 245/**
 246 * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root.
 247 *
 248 * @path: Pointer to "struct path".
 249 *
 250 * Returns the realpath of the given @path on success, NULL otherwise.
 251 *
 252 * If dentry is a directory, trailing '/' is appended.
 253 * Characters out of 0x20 < c < 0x7F range are converted to
 254 * \ooo style octal string.
 255 * Character \ is converted to \\ string.
 256 *
 257 * These functions use kzalloc(), so the caller must call kfree()
 258 * if these functions didn't return NULL.
 259 */
 260char *tomoyo_realpath_from_path(const struct path *path)
 261{
 262        char *buf = NULL;
 263        char *name = NULL;
 264        unsigned int buf_len = PAGE_SIZE / 2;
 265        struct dentry *dentry = path->dentry;
 266        struct super_block *sb;
 267
 268        if (!dentry)
 269                return NULL;
 270        sb = dentry->d_sb;
 271        while (1) {
 272                char *pos;
 273                struct inode *inode;
 274
 275                buf_len <<= 1;
 276                kfree(buf);
 277                buf = kmalloc(buf_len, GFP_NOFS);
 278                if (!buf)
 279                        break;
 280                /* To make sure that pos is '\0' terminated. */
 281                buf[buf_len - 1] = '\0';
 282                /* Get better name for socket. */
 283                if (sb->s_magic == SOCKFS_MAGIC) {
 284                        pos = tomoyo_get_socket_name(path, buf, buf_len - 1);
 285                        goto encode;
 286                }
 287                /* For "pipe:[\$]". */
 288                if (dentry->d_op && dentry->d_op->d_dname) {
 289                        pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1);
 290                        goto encode;
 291                }
 292                inode = d_backing_inode(sb->s_root);
 293                /*
 294                 * Get local name for filesystems without rename() operation
 295                 * or dentry without vfsmount.
 296                 */
 297                if (!path->mnt ||
 298                    (!inode->i_op->rename &&
 299                     !(sb->s_type->fs_flags & FS_REQUIRES_DEV)))
 300                        pos = tomoyo_get_local_path(path->dentry, buf,
 301                                                    buf_len - 1);
 302                /* Get absolute name for the rest. */
 303                else {
 304                        pos = tomoyo_get_absolute_path(path, buf, buf_len - 1);
 305                        /*
 306                         * Fall back to local name if absolute name is not
 307                         * available.
 308                         */
 309                        if (pos == ERR_PTR(-EINVAL))
 310                                pos = tomoyo_get_local_path(path->dentry, buf,
 311                                                            buf_len - 1);
 312                }
 313encode:
 314                if (IS_ERR(pos))
 315                        continue;
 316                name = tomoyo_encode(pos);
 317                break;
 318        }
 319        kfree(buf);
 320        if (!name)
 321                tomoyo_warn_oom(__func__);
 322        return name;
 323}
 324
 325/**
 326 * tomoyo_realpath_nofollow - Get realpath of a pathname.
 327 *
 328 * @pathname: The pathname to solve.
 329 *
 330 * Returns the realpath of @pathname on success, NULL otherwise.
 331 */
 332char *tomoyo_realpath_nofollow(const char *pathname)
 333{
 334        struct path path;
 335
 336        if (pathname && kern_path(pathname, 0, &path) == 0) {
 337                char *buf = tomoyo_realpath_from_path(&path);
 338
 339                path_put(&path);
 340                return buf;
 341        }
 342        return NULL;
 343}
 344