Blame elf/chroot_canon.c

Packit Service 82fcde
/* Return the canonical absolute name of a given file inside chroot.
Packit Service 82fcde
   Copyright (C) 1996-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
Packit Service 82fcde
   This program is free software; you can redistribute it and/or modify
Packit Service 82fcde
   it under the terms of the GNU General Public License as published
Packit Service 82fcde
   by the Free Software Foundation; version 2 of the License, or
Packit Service 82fcde
   (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   This program is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 82fcde
   GNU General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU General Public License
Packit Service 82fcde
   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include <stdlib.h>
Packit Service 82fcde
#include <string.h>
Packit Service 82fcde
#include <unistd.h>
Packit Service 82fcde
#include <limits.h>
Packit Service 82fcde
#include <sys/stat.h>
Packit Service 82fcde
#include <errno.h>
Packit Service 82fcde
#include <stddef.h>
Packit Service 82fcde
#include <stdint.h>
Packit Service 82fcde
Packit Service 82fcde
#include <eloop-threshold.h>
Packit Service 82fcde
#include <ldconfig.h>
Packit Service 82fcde
Packit Service 82fcde
#ifndef PATH_MAX
Packit Service 82fcde
#define PATH_MAX 1024
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
/* Return the canonical absolute name of file NAME as if chroot(CHROOT) was
Packit Service 82fcde
   done first.  A canonical name does not contain any `.', `..' components
Packit Service 82fcde
   nor any repeated path separators ('/') or symlinks.  All path components
Packit Service 82fcde
   must exist and NAME must be absolute filename.  The result is malloc'd.
Packit Service 82fcde
   The returned name includes the CHROOT prefix.  */
Packit Service 82fcde
Packit Service 82fcde
char *
Packit Service 82fcde
chroot_canon (const char *chroot, const char *name)
Packit Service 82fcde
{
Packit Service 82fcde
  char *rpath;
Packit Service 82fcde
  char *dest;
Packit Service 82fcde
  char *extra_buf = NULL;
Packit Service 82fcde
  char *rpath_root;
Packit Service 82fcde
  const char *start;
Packit Service 82fcde
  const char *end;
Packit Service 82fcde
  const char *rpath_limit;
Packit Service 82fcde
  int num_links = 0;
Packit Service 82fcde
  size_t chroot_len = strlen (chroot);
Packit Service 82fcde
Packit Service 82fcde
  if (chroot_len < 1)
Packit Service 82fcde
    {
Packit Service 82fcde
      __set_errno (EINVAL);
Packit Service 82fcde
      return NULL;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  rpath = xmalloc (chroot_len + PATH_MAX);
Packit Service 82fcde
Packit Service 82fcde
  rpath_limit = rpath + chroot_len + PATH_MAX;
Packit Service 82fcde
Packit Service 82fcde
  rpath_root = (char *) mempcpy (rpath, chroot, chroot_len) - 1;
Packit Service 82fcde
  if (*rpath_root != '/')
Packit Service 82fcde
    *++rpath_root = '/';
Packit Service 82fcde
  dest = rpath_root + 1;
Packit Service 82fcde
Packit Service 82fcde
  for (start = end = name; *start; start = end)
Packit Service 82fcde
    {
Packit Service 82fcde
      struct stat64 st;
Packit Service 82fcde
Packit Service 82fcde
      /* Skip sequence of multiple path-separators.  */
Packit Service 82fcde
      while (*start == '/')
Packit Service 82fcde
	++start;
Packit Service 82fcde
Packit Service 82fcde
      /* Find end of path component.  */
Packit Service 82fcde
      for (end = start; *end && *end != '/'; ++end)
Packit Service 82fcde
	/* Nothing.  */;
Packit Service 82fcde
Packit Service 82fcde
      if (end - start == 0)
Packit Service 82fcde
	break;
Packit Service 82fcde
      else if (end - start == 1 && start[0] == '.')
Packit Service 82fcde
	/* nothing */;
Packit Service 82fcde
      else if (end - start == 2 && start[0] == '.' && start[1] == '.')
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* Back up to previous component, ignore if at root already.  */
Packit Service 82fcde
	  if (dest > rpath_root + 1)
Packit Service 82fcde
	    while ((--dest)[-1] != '/');
Packit Service 82fcde
	}
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  size_t new_size;
Packit Service 82fcde
Packit Service 82fcde
	  if (dest[-1] != '/')
Packit Service 82fcde
	    *dest++ = '/';
Packit Service 82fcde
Packit Service 82fcde
	  if (dest + (end - start) >= rpath_limit)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      ptrdiff_t dest_offset = dest - rpath;
Packit Service 82fcde
	      char *new_rpath;
Packit Service 82fcde
Packit Service 82fcde
	      new_size = rpath_limit - rpath;
Packit Service 82fcde
	      if (end - start + 1 > PATH_MAX)
Packit Service 82fcde
		new_size += end - start + 1;
Packit Service 82fcde
	      else
Packit Service 82fcde
		new_size += PATH_MAX;
Packit Service 82fcde
	      new_rpath = (char *) xrealloc (rpath, new_size);
Packit Service 82fcde
	      rpath = new_rpath;
Packit Service 82fcde
	      rpath_limit = rpath + new_size;
Packit Service 82fcde
Packit Service 82fcde
	      dest = rpath + dest_offset;
Packit Service 82fcde
	    }
Packit Service 82fcde
Packit Service 82fcde
	  dest = mempcpy (dest, start, end - start);
Packit Service 82fcde
	  *dest = '\0';
Packit Service 82fcde
Packit Service 82fcde
	  if (lstat64 (rpath, &st) < 0)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      if (*end == '\0')
Packit Service 82fcde
		goto done;
Packit Service 82fcde
	      goto error;
Packit Service 82fcde
	    }
Packit Service 82fcde
Packit Service 82fcde
	  if (S_ISLNK (st.st_mode))
Packit Service 82fcde
	    {
Packit Service 82fcde
	      char *buf = alloca (PATH_MAX);
Packit Service 82fcde
	      size_t len;
Packit Service 82fcde
Packit Service 82fcde
	      if (++num_links > __eloop_threshold ())
Packit Service 82fcde
		{
Packit Service 82fcde
		  __set_errno (ELOOP);
Packit Service 82fcde
		  goto error;
Packit Service 82fcde
		}
Packit Service 82fcde
Packit Service 82fcde
	      ssize_t n = readlink (rpath, buf, PATH_MAX - 1);
Packit Service 82fcde
	      if (n < 0)
Packit Service 82fcde
		{
Packit Service 82fcde
		  if (*end == '\0')
Packit Service 82fcde
		    goto done;
Packit Service 82fcde
		  goto error;
Packit Service 82fcde
		}
Packit Service 82fcde
	      buf[n] = '\0';
Packit Service 82fcde
Packit Service 82fcde
	      if (!extra_buf)
Packit Service 82fcde
		extra_buf = alloca (PATH_MAX);
Packit Service 82fcde
Packit Service 82fcde
	      len = strlen (end);
Packit Service 82fcde
	      if (len >= PATH_MAX - n)
Packit Service 82fcde
		{
Packit Service 82fcde
		  __set_errno (ENAMETOOLONG);
Packit Service 82fcde
		  goto error;
Packit Service 82fcde
		}
Packit Service 82fcde
Packit Service 82fcde
	      /* Careful here, end may be a pointer into extra_buf... */
Packit Service 82fcde
	      memmove (&extra_buf[n], end, len + 1);
Packit Service 82fcde
	      name = end = memcpy (extra_buf, buf, n);
Packit Service 82fcde
Packit Service 82fcde
	      if (buf[0] == '/')
Packit Service 82fcde
		dest = rpath_root + 1;	/* It's an absolute symlink */
Packit Service 82fcde
	      else
Packit Service 82fcde
		/* Back up to previous component, ignore if at root already: */
Packit Service 82fcde
		if (dest > rpath_root + 1)
Packit Service 82fcde
		  while ((--dest)[-1] != '/');
Packit Service 82fcde
	    }
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
 done:
Packit Service 82fcde
  if (dest > rpath_root + 1 && dest[-1] == '/')
Packit Service 82fcde
    --dest;
Packit Service 82fcde
  *dest = '\0';
Packit Service 82fcde
Packit Service 82fcde
  return rpath;
Packit Service 82fcde
Packit Service 82fcde
 error:
Packit Service 82fcde
  free (rpath);
Packit Service 82fcde
  return NULL;
Packit Service 82fcde
}