(root)/
glibc-2.38/
elf/
chroot_canon.c
       1  /* Return the canonical absolute name of a given file inside chroot.
       2     Copyright (C) 1996-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     This program is free software; you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published
       7     by the Free Software Foundation; version 2 of the License, or
       8     (at your option) any later version.
       9  
      10     This program is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program; if not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <stdlib.h>
      19  #include <string.h>
      20  #include <unistd.h>
      21  #include <limits.h>
      22  #include <sys/stat.h>
      23  #include <errno.h>
      24  #include <stddef.h>
      25  #include <stdint.h>
      26  
      27  #include <eloop-threshold.h>
      28  #include <ldconfig.h>
      29  
      30  #ifndef PATH_MAX
      31  #define PATH_MAX 1024
      32  #endif
      33  
      34  /* Return the canonical absolute name of file NAME as if chroot(CHROOT) was
      35     done first.  A canonical name does not contain any `.', `..' components
      36     nor any repeated path separators ('/') or symlinks.  All path components
      37     must exist and NAME must be absolute filename.  The result is malloc'd.
      38     The returned name includes the CHROOT prefix.  */
      39  
      40  char *
      41  chroot_canon (const char *chroot, const char *name)
      42  {
      43    char *rpath;
      44    char *dest;
      45    char *extra_buf = NULL;
      46    char *rpath_root;
      47    const char *start;
      48    const char *end;
      49    const char *rpath_limit;
      50    int num_links = 0;
      51    size_t chroot_len = strlen (chroot);
      52  
      53    if (chroot_len < 1)
      54      {
      55        __set_errno (EINVAL);
      56        return NULL;
      57      }
      58  
      59    rpath = xmalloc (chroot_len + PATH_MAX);
      60  
      61    rpath_limit = rpath + chroot_len + PATH_MAX;
      62  
      63    rpath_root = (char *) mempcpy (rpath, chroot, chroot_len) - 1;
      64    if (*rpath_root != '/')
      65      *++rpath_root = '/';
      66    dest = rpath_root + 1;
      67  
      68    for (start = end = name; *start; start = end)
      69      {
      70        struct stat st;
      71  
      72        /* Skip sequence of multiple path-separators.  */
      73        while (*start == '/')
      74  	++start;
      75  
      76        /* Find end of path component.  */
      77        for (end = start; *end && *end != '/'; ++end)
      78  	/* Nothing.  */;
      79  
      80        if (end - start == 0)
      81  	break;
      82        else if (end - start == 1 && start[0] == '.')
      83  	/* nothing */;
      84        else if (end - start == 2 && start[0] == '.' && start[1] == '.')
      85  	{
      86  	  /* Back up to previous component, ignore if at root already.  */
      87  	  if (dest > rpath_root + 1)
      88  	    while ((--dest)[-1] != '/');
      89  	}
      90        else
      91  	{
      92  	  size_t new_size;
      93  
      94  	  if (dest[-1] != '/')
      95  	    *dest++ = '/';
      96  
      97  	  if (dest + (end - start) >= rpath_limit)
      98  	    {
      99  	      ptrdiff_t dest_offset = dest - rpath;
     100  	      char *new_rpath;
     101  
     102  	      new_size = rpath_limit - rpath;
     103  	      if (end - start + 1 > PATH_MAX)
     104  		new_size += end - start + 1;
     105  	      else
     106  		new_size += PATH_MAX;
     107  	      new_rpath = (char *) xrealloc (rpath, new_size);
     108  	      rpath = new_rpath;
     109  	      rpath_limit = rpath + new_size;
     110  
     111  	      dest = rpath + dest_offset;
     112  	    }
     113  
     114  	  dest = mempcpy (dest, start, end - start);
     115  	  *dest = '\0';
     116  
     117  	  if (lstat (rpath, &st) < 0)
     118  	    {
     119  	      if (*end == '\0')
     120  		goto done;
     121  	      goto error;
     122  	    }
     123  
     124  	  if (S_ISLNK (st.st_mode))
     125  	    {
     126  	      char *buf = alloca (PATH_MAX);
     127  	      size_t len;
     128  
     129  	      if (++num_links > __eloop_threshold ())
     130  		{
     131  		  __set_errno (ELOOP);
     132  		  goto error;
     133  		}
     134  
     135  	      ssize_t n = readlink (rpath, buf, PATH_MAX - 1);
     136  	      if (n < 0)
     137  		{
     138  		  if (*end == '\0')
     139  		    goto done;
     140  		  goto error;
     141  		}
     142  	      buf[n] = '\0';
     143  
     144  	      if (!extra_buf)
     145  		extra_buf = alloca (PATH_MAX);
     146  
     147  	      len = strlen (end);
     148  	      if (len >= PATH_MAX - n)
     149  		{
     150  		  __set_errno (ENAMETOOLONG);
     151  		  goto error;
     152  		}
     153  
     154  	      /* Careful here, end may be a pointer into extra_buf... */
     155  	      memmove (&extra_buf[n], end, len + 1);
     156  	      name = end = memcpy (extra_buf, buf, n);
     157  
     158  	      if (buf[0] == '/')
     159  		dest = rpath_root + 1;	/* It's an absolute symlink */
     160  	      else
     161  		/* Back up to previous component, ignore if at root already: */
     162  		if (dest > rpath_root + 1)
     163  		  while ((--dest)[-1] != '/');
     164  	    }
     165  	}
     166      }
     167   done:
     168    if (dest > rpath_root + 1 && dest[-1] == '/')
     169      --dest;
     170    *dest = '\0';
     171  
     172    return rpath;
     173  
     174   error:
     175    free (rpath);
     176    return NULL;
     177  }