tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SandboxBrokerRealpath.cpp (8416B)


      1 /*
      2 * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
      3 *
      4 * Redistribution and use in source and binary forms, with or without
      5 * modification, are permitted provided that the following conditions
      6 * are met:
      7 * 1. Redistributions of source code must retain the above copyright
      8 *    notice, this list of conditions and the following disclaimer.
      9 * 2. Redistributions in binary form must reproduce the above copyright
     10 *    notice, this list of conditions and the following disclaimer in the
     11 *    documentation and/or other materials provided with the distribution.
     12 * 3. The names of the authors may not be used to endorse or promote
     13 *    products derived from this software without specific prior written
     14 *    permission.
     15 *
     16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26 * SUCH DAMAGE.
     27 */
     28 
     29 /*
     30 * This is originally from:
     31 * android-n-mr2-preview-1-303-gccec0f4c1
     32 * libc/upstream-freebsd/lib/libc/stdlib/realpath.c
     33 */
     34 
     35 #if defined(LIBC_SCCS) && !defined(lint)
     36 static char sccsid[] = "@(#)realpath.c	8.1 (Berkeley) 2/16/94";
     37 #endif /* LIBC_SCCS and not lint */
     38 #include <sys/param.h>
     39 #include <sys/stat.h>
     40 
     41 #include <errno.h>
     42 #include <stdlib.h>
     43 #include <string.h>
     44 #include <unistd.h>
     45 
     46 #include "base/string_util.h"
     47 #include "SandboxBroker.h"
     48 
     49 // Original copy in, but not usable from here:
     50 // toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.cc
     51 static size_t my_strlcat(char* s1, const char* s2, size_t len) {
     52  size_t pos1 = 0;
     53 
     54  while (pos1 < len && s1[pos1] != '\0') pos1++;
     55 
     56  if (pos1 == len) return pos1;
     57 
     58  return pos1 + base::strlcpy(s1 + pos1, s2, len - pos1);
     59 }
     60 
     61 namespace mozilla {
     62 
     63 /*
     64 * Original: realpath
     65 * Find the real name of path, by removing all ".", ".." and symlink
     66 * components.  Returns (resolved) on success, or (NULL) on failure,
     67 * in which case the path which caused trouble is left in (resolved).
     68 * Changes:
     69 * Resolve relative paths, but don't allow backing out of a symlink
     70 * target. Fail with permission error if any dir is writable.
     71 */
     72 char* SandboxBroker::SymlinkPath(const Policy* policy,
     73                                 const char* __restrict path,
     74                                 char* __restrict resolved, int* perms) {
     75  struct stat sb;
     76  char *p, *q, *s;
     77  size_t left_len, resolved_len, backup_allowed;
     78  unsigned symlinks;
     79  int m, slen;
     80  char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX];
     81 
     82  if (*perms) {
     83    *perms = 0;
     84  }
     85  if (path == NULL) {
     86    errno = EINVAL;
     87    return (NULL);
     88  }
     89  if (path[0] == '\0') {
     90    errno = ENOENT;
     91    return (NULL);
     92  }
     93  if (resolved == NULL) {
     94    resolved = (char*)malloc(PATH_MAX);
     95    if (resolved == NULL) return (NULL);
     96    m = 1;
     97  } else
     98    m = 0;
     99  symlinks = 0;
    100  backup_allowed = PATH_MAX;
    101  if (path[0] == '/') {
    102    resolved[0] = '/';
    103    resolved[1] = '\0';
    104    if (path[1] == '\0') return (resolved);
    105    resolved_len = 1;
    106    left_len = base::strlcpy(left, path + 1, sizeof(left));
    107  } else {
    108    if (getcwd(resolved, PATH_MAX) == NULL) {
    109      if (m)
    110        free(resolved);
    111      else {
    112        resolved[0] = '.';
    113        resolved[1] = '\0';
    114      }
    115      return (NULL);
    116    }
    117    resolved_len = strlen(resolved);
    118    left_len = base::strlcpy(left, path, sizeof(left));
    119  }
    120  if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) {
    121    if (m) free(resolved);
    122    errno = ENAMETOOLONG;
    123    return (NULL);
    124  }
    125 
    126  /*
    127   * Iterate over path components in `left'.
    128   */
    129  while (left_len != 0) {
    130    /*
    131     * Extract the next path component and adjust `left'
    132     * and its length.
    133     */
    134    p = strchr(left, '/');
    135    s = p ? p : left + left_len;
    136    if (s - left >= (ssize_t)sizeof(next_token)) {
    137      if (m) free(resolved);
    138      errno = ENAMETOOLONG;
    139      return (NULL);
    140    }
    141    memcpy(next_token, left, s - left);
    142    next_token[s - left] = '\0';
    143    left_len -= s - left;
    144    if (p != NULL) memmove(left, s + 1, left_len + 1);
    145    if (resolved[resolved_len - 1] != '/') {
    146      if (resolved_len + 1 >= PATH_MAX) {
    147        if (m) free(resolved);
    148        errno = ENAMETOOLONG;
    149        return (NULL);
    150      }
    151      resolved[resolved_len++] = '/';
    152      resolved[resolved_len] = '\0';
    153    }
    154    if (next_token[0] == '\0') {
    155      /* Handle consequential slashes. */
    156      continue;
    157    } else if (strcmp(next_token, ".") == 0)
    158      continue;
    159    else if (strcmp(next_token, "..") == 0) {
    160      /*
    161       * Strip the last path component except when we have
    162       * single "/"
    163       */
    164      if (resolved_len > 1) {
    165        if (backup_allowed > 0) {
    166          resolved[resolved_len - 1] = '\0';
    167          q = strrchr(resolved, '/') + 1;
    168          *q = '\0';
    169          resolved_len = q - resolved;
    170          backup_allowed--;
    171        } else {
    172          // Backing out past a symlink target.
    173          // We don't allow this, because it can eliminate
    174          // permissions we accumulated while descending.
    175          if (m) free(resolved);
    176          errno = EPERM;
    177          return (NULL);
    178        }
    179      }
    180      continue;
    181    }
    182 
    183    /*
    184     * Append the next path component and lstat() it.
    185     */
    186    resolved_len = my_strlcat(resolved, next_token, PATH_MAX);
    187    backup_allowed++;
    188    if (resolved_len >= PATH_MAX) {
    189      if (m) free(resolved);
    190      errno = ENAMETOOLONG;
    191      return (NULL);
    192    }
    193    if (lstat(resolved, &sb) != 0) {
    194      if (m) free(resolved);
    195      return (NULL);
    196    }
    197    if (S_ISLNK(sb.st_mode)) {
    198      if (symlinks++ > MAXSYMLINKS) {
    199        if (m) free(resolved);
    200        errno = ELOOP;
    201        return (NULL);
    202      }
    203      /* Our changes start here:
    204       * It's a symlink, check for write permissions on the path where
    205       * it sits in, in which case we won't resolve and just error out. */
    206      int link_path_perms = policy->Lookup(resolved);
    207      if (link_path_perms & MAY_WRITE) {
    208        if (m) free(resolved);
    209        errno = EPERM;
    210        return (NULL);
    211      } else {
    212        /* Accumulate permissions so far */
    213        *perms |= link_path_perms;
    214      }
    215      /* Original symlink lookup code */
    216      slen = readlink(resolved, symlink, sizeof(symlink) - 1);
    217      if (slen < 0) {
    218        if (m) free(resolved);
    219        return (NULL);
    220      }
    221      symlink[slen] = '\0';
    222      if (symlink[0] == '/') {
    223        resolved[1] = 0;
    224        resolved_len = 1;
    225      } else if (resolved_len > 1) {
    226        /* Strip the last path component. */
    227        resolved[resolved_len - 1] = '\0';
    228        q = strrchr(resolved, '/') + 1;
    229        *q = '\0';
    230        resolved_len = q - resolved;
    231      }
    232 
    233      /*
    234       * If there are any path components left, then
    235       * append them to symlink. The result is placed
    236       * in `left'.
    237       */
    238      if (p != NULL) {
    239        if (symlink[slen - 1] != '/') {
    240          if (slen + 1 >= (ssize_t)sizeof(symlink)) {
    241            if (m) free(resolved);
    242            errno = ENAMETOOLONG;
    243            return (NULL);
    244          }
    245          symlink[slen] = '/';
    246          symlink[slen + 1] = 0;
    247        }
    248        left_len = my_strlcat(symlink, left, sizeof(symlink));
    249        if (left_len >= sizeof(left)) {
    250          if (m) free(resolved);
    251          errno = ENAMETOOLONG;
    252          return (NULL);
    253        }
    254      }
    255      left_len = base::strlcpy(left, symlink, sizeof(left));
    256      backup_allowed = 0;
    257    } else if (!S_ISDIR(sb.st_mode) && p != NULL) {
    258      if (m) free(resolved);
    259      errno = ENOTDIR;
    260      return (NULL);
    261    }
    262  }
    263 
    264  /*
    265   * Remove trailing slash except when the resolved pathname
    266   * is a single "/".
    267   */
    268  if (resolved_len > 1 && resolved[resolved_len - 1] == '/')
    269    resolved[resolved_len - 1] = '\0';
    270 
    271  /* Accumulate permissions. */
    272  *perms |= policy->Lookup(resolved);
    273 
    274  return (resolved);
    275 }
    276 
    277 }  // namespace mozilla