Blame sysdeps/unix/grantpt.c

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