Blame elf/chroot_canon.c

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