Blame sysdeps/unix/grantpt.c

Packit Service 82fcde
/* Copyright (C) 1998-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
   Contributed by Zack Weinberg <zack@rabi.phys.columbia.edu>, 1998.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library 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 GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include <assert.h>
Packit Service 82fcde
#include <errno.h>
Packit Service 82fcde
#include <fcntl.h>
Packit Service 82fcde
#include <grp.h>
Packit Service 82fcde
#include <limits.h>
Packit Service 82fcde
#include <stdlib.h>
Packit Service 82fcde
#include <string.h>
Packit Service 82fcde
#include <sys/resource.h>
Packit Service 82fcde
#include <sys/stat.h>
Packit Service 82fcde
#include <sys/types.h>
Packit Service 82fcde
#include <sys/wait.h>
Packit Service 82fcde
#include <unistd.h>
Packit Service 82fcde
Packit Service 82fcde
#include "pty-private.h"
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Return the result of ptsname_r in the buffer pointed to by PTS,
Packit Service 82fcde
   which should be of length BUF_LEN.  If it is too long to fit in
Packit Service 82fcde
   this buffer, a sufficiently long buffer is allocated using malloc,
Packit Service 82fcde
   and returned in PTS.  0 is returned upon success, -1 otherwise.  */
Packit Service 82fcde
static int
Packit Service 82fcde
pts_name (int fd, char **pts, size_t buf_len, struct stat64 *stp)
Packit Service 82fcde
{
Packit Service 82fcde
  int rv;
Packit Service 82fcde
  char *buf = *pts;
Packit Service 82fcde
Packit Service 82fcde
  for (;;)
Packit Service 82fcde
    {
Packit Service 82fcde
      char *new_buf;
Packit Service 82fcde
Packit Service 82fcde
      if (buf_len)
Packit Service 82fcde
	{
Packit Service 82fcde
	  rv = __ptsname_internal (fd, buf, buf_len, stp);
Packit Service 82fcde
	  if (rv != 0)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      if (rv == ENOTTY)
Packit Service 82fcde
		/* ptsname_r returns with ENOTTY to indicate
Packit Service 82fcde
		   a descriptor not referring to a pty master.
Packit Service 82fcde
		   For this condition, grantpt must return EINVAL.  */
Packit Service 82fcde
		rv = EINVAL;
Packit Service 82fcde
	      errno = rv;	/* Not necessarily set by __ptsname_r.  */
Packit Service 82fcde
	      break;
Packit Service 82fcde
	    }
Packit Service 82fcde
Packit Service 82fcde
	  if (memchr (buf, '\0', buf_len))
Packit Service 82fcde
	    /* We succeeded and the returned name fit in the buffer.  */
Packit Service 82fcde
	    break;
Packit Service 82fcde
Packit Service 82fcde
	  /* Try again with a longer buffer.  */
Packit Service 82fcde
	  buf_len += buf_len;	/* Double it */
Packit Service 82fcde
	}
Packit Service 82fcde
      else
Packit Service 82fcde
	/* No initial buffer; start out by mallocing one.  */
Packit Service 82fcde
	buf_len = 128;		/* First time guess.  */
Packit Service 82fcde
Packit Service 82fcde
      if (buf != *pts)
Packit Service 82fcde
	/* We've already malloced another buffer at least once.  */
Packit Service 82fcde
	new_buf = (char *) realloc (buf, buf_len);
Packit Service 82fcde
      else
Packit Service 82fcde
	new_buf = (char *) malloc (buf_len);
Packit Service 82fcde
      if (! new_buf)
Packit Service 82fcde
	{
Packit Service 82fcde
	  rv = -1;
Packit Service 82fcde
	  __set_errno (ENOMEM);
Packit Service 82fcde
	  break;
Packit Service 82fcde
	}
Packit Service 82fcde
      buf = new_buf;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  if (rv == 0)
Packit Service 82fcde
    *pts = buf;		/* Return buffer to the user.  */
Packit Service 82fcde
  else if (buf != *pts)
Packit Service 82fcde
    free (buf);		/* Free what we malloced when returning an error.  */
Packit Service 82fcde
Packit Service 82fcde
  return rv;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Change the ownership and access permission of the slave pseudo
Packit Service 82fcde
   terminal associated with the master pseudo terminal specified
Packit Service 82fcde
   by FD.  */
Packit Service 82fcde
int
Packit Service 82fcde
grantpt (int fd)
Packit Service 82fcde
{
Packit Service 82fcde
  int retval = -1;
Packit Service 82fcde
#ifdef PATH_MAX
Packit Service 82fcde
  char _buf[PATH_MAX];
Packit Service 82fcde
#else
Packit Service 82fcde
  char _buf[512];
Packit Service 82fcde
#endif
Packit Service 82fcde
  char *buf = _buf;
Packit Service 82fcde
  struct stat64 st;
Packit Service 82fcde
Packit Service 82fcde
  if (__glibc_unlikely (pts_name (fd, &buf, sizeof (_buf), &st)))
Packit Service 82fcde
    {
Packit Service 82fcde
      int save_errno = errno;
Packit Service 82fcde
Packit Service 82fcde
      /* Check, if the file descriptor is valid.  pts_name returns the
Packit Service 82fcde
	 wrong errno number, so we cannot use that.  */
Packit Service 82fcde
      if (__libc_fcntl (fd, F_GETFD) == -1 && errno == EBADF)
Packit Service 82fcde
	return -1;
Packit Service 82fcde
Packit Service 82fcde
       /* If the filedescriptor is no TTY, grantpt has to set errno
Packit Service 82fcde
	  to EINVAL.  */
Packit Service 82fcde
       if (save_errno == ENOTTY)
Packit Service 82fcde
	 __set_errno (EINVAL);
Packit Service 82fcde
       else
Packit Service 82fcde
	 __set_errno (save_errno);
Packit Service 82fcde
Packit Service 82fcde
       return -1;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* Make sure that we own the device.  */
Packit Service 82fcde
  uid_t uid = __getuid ();
Packit Service 82fcde
  if (st.st_uid != uid)
Packit Service 82fcde
    {
Packit Service 82fcde
      if (__chown (buf, uid, st.st_gid) < 0)
Packit Service 82fcde
	goto helper;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  static int tty_gid = -1;
Packit Service 82fcde
  if (__glibc_unlikely (tty_gid == -1))
Packit Service 82fcde
    {
Packit Service 82fcde
      char *grtmpbuf;
Packit Service 82fcde
      struct group grbuf;
Packit Service 82fcde
      size_t grbuflen = __sysconf (_SC_GETGR_R_SIZE_MAX);
Packit Service 82fcde
      struct group *p;
Packit Service 82fcde
Packit Service 82fcde
      /* Get the group ID of the special `tty' group.  */
Packit Service 82fcde
      if (grbuflen == (size_t) -1L)
Packit Service 82fcde
	/* `sysconf' does not support _SC_GETGR_R_SIZE_MAX.
Packit Service 82fcde
	   Try a moderate value.  */
Packit Service 82fcde
	grbuflen = 1024;
Packit Service 82fcde
      grtmpbuf = (char *) __alloca (grbuflen);
Packit Service 82fcde
      __getgrnam_r (TTY_GROUP, &grbuf, grtmpbuf, grbuflen, &p);
Packit Service 82fcde
      if (p != NULL)
Packit Service 82fcde
	tty_gid = p->gr_gid;
Packit Service 82fcde
    }
Packit Service 82fcde
  gid_t gid = tty_gid == -1 ? __getgid () : tty_gid;
Packit Service 82fcde
Packit Service 82fcde
#if HAVE_PT_CHOWN
Packit Service 82fcde
  /* Make sure the group of the device is that special group.  */
Packit Service 82fcde
  if (st.st_gid != gid)
Packit Service 82fcde
    {
Packit Service 82fcde
      if (__chown (buf, uid, gid) < 0)
Packit Service 82fcde
	goto helper;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* Make sure the permission mode is set to readable and writable by
Packit Service 82fcde
     the owner, and writable by the group.  */
Packit Service 82fcde
  mode_t mode = S_IRUSR|S_IWUSR|S_IWGRP;
Packit Service 82fcde
#else
Packit Service 82fcde
  /* When built without pt_chown, we have delegated the creation of the
Packit Service 82fcde
     pty node with the right group and permission mode to the kernel, and
Packit Service 82fcde
     non-root users are unlikely to be able to change it. Therefore let's
Packit Service 82fcde
     consider that POSIX enforcement is the responsibility of the whole
Packit Service 82fcde
     system and not only the GNU libc. Thus accept different group or
Packit Service 82fcde
     permission mode.  */
Packit Service 82fcde
Packit Service 82fcde
  /* Make sure the permission is set to readable and writable by the
Packit Service 82fcde
     owner.  For security reasons, make it writable by the group only
Packit Service 82fcde
     when originally writable and when the group of the device is that
Packit Service 82fcde
     special group.  */
Packit Service 82fcde
  mode_t mode = S_IRUSR|S_IWUSR|
Packit Service 82fcde
	        ((st.st_gid == gid) ? (st.st_mode & S_IWGRP) : 0);
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
  if ((st.st_mode & ACCESSPERMS) != mode)
Packit Service 82fcde
    {
Packit Service 82fcde
      if (__chmod (buf, mode) < 0)
Packit Service 82fcde
	goto helper;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  retval = 0;
Packit Service 82fcde
  goto cleanup;
Packit Service 82fcde
Packit Service 82fcde
  /* We have to use the helper program if it is available.  */
Packit Service 82fcde
 helper:;
Packit Service 82fcde
Packit Service 82fcde
#if HAVE_PT_CHOWN
Packit Service 82fcde
  pid_t pid = __fork ();
Packit Service 82fcde
  if (pid == -1)
Packit Service 82fcde
    goto cleanup;
Packit Service 82fcde
  else if (pid == 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Disable core dumps.  */
Packit Service 82fcde
      struct rlimit rl = { 0, 0 };
Packit Service 82fcde
      __setrlimit (RLIMIT_CORE, &rl);
Packit Service 82fcde
Packit Service 82fcde
      /* We pass the master pseudo terminal as file descriptor PTY_FILENO.  */
Packit Service 82fcde
      if (fd != PTY_FILENO)
Packit Service 82fcde
	if (__dup2 (fd, PTY_FILENO) < 0)
Packit Service 82fcde
	  _exit (FAIL_EBADF);
Packit Service 82fcde
Packit Service 82fcde
# ifdef CLOSE_ALL_FDS
Packit Service 82fcde
      CLOSE_ALL_FDS ();
Packit Service 82fcde
# endif
Packit Service 82fcde
Packit Service 82fcde
      execle (_PATH_PT_CHOWN, __basename (_PATH_PT_CHOWN), NULL, NULL);
Packit Service 82fcde
      _exit (FAIL_EXEC);
Packit Service 82fcde
    }
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      int w;
Packit Service 82fcde
Packit Service 82fcde
      if (__waitpid (pid, &w, 0) == -1)
Packit Service 82fcde
	goto cleanup;
Packit Service 82fcde
      if (!WIFEXITED (w))
Packit Service 82fcde
	__set_errno (ENOEXEC);
Packit Service 82fcde
      else
Packit Service 82fcde
	switch (WEXITSTATUS (w))
Packit Service 82fcde
	  {
Packit Service 82fcde
	  case 0:
Packit Service 82fcde
	    retval = 0;
Packit Service 82fcde
	    break;
Packit Service 82fcde
	  case FAIL_EBADF:
Packit Service 82fcde
	    __set_errno (EBADF);
Packit Service 82fcde
	    break;
Packit Service 82fcde
	  case FAIL_EINVAL:
Packit Service 82fcde
	    __set_errno (EINVAL);
Packit Service 82fcde
	    break;
Packit Service 82fcde
	  case FAIL_EACCES:
Packit Service 82fcde
	    __set_errno (EACCES);
Packit Service 82fcde
	    break;
Packit Service 82fcde
	  case FAIL_EXEC:
Packit Service 82fcde
	    __set_errno (ENOEXEC);
Packit Service 82fcde
	    break;
Packit Service 82fcde
	  case FAIL_ENOMEM:
Packit Service 82fcde
	    __set_errno (ENOMEM);
Packit Service 82fcde
	    break;
Packit Service 82fcde
Packit Service 82fcde
	  default:
Packit Service 82fcde
	    assert(! "grantpt: internal error: invalid exit code from pt_chown");
Packit Service 82fcde
	  }
Packit Service 82fcde
    }
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
 cleanup:
Packit Service 82fcde
  if (buf != _buf)
Packit Service 82fcde
    free (buf);
Packit Service 82fcde
Packit Service 82fcde
  return retval;
Packit Service 82fcde
}