(root)/
glibc-2.38/
sysdeps/
unix/
grantpt.c
       1  /* Copyright (C) 1998-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <assert.h>
      19  #include <errno.h>
      20  #include <fcntl.h>
      21  #include <grp.h>
      22  #include <limits.h>
      23  #include <scratch_buffer.h>
      24  #include <stdlib.h>
      25  #include <string.h>
      26  #include <sys/resource.h>
      27  #include <sys/stat.h>
      28  #include <sys/types.h>
      29  #include <sys/wait.h>
      30  #include <unistd.h>
      31  
      32  #include "pty-private.h"
      33  
      34  
      35  /* Return the result of ptsname_r in the buffer pointed to by PTS,
      36     which should be of length BUF_LEN.  If it is too long to fit in
      37     this buffer, a sufficiently long buffer is allocated using malloc,
      38     and returned in PTS.  0 is returned upon success, -1 otherwise.  */
      39  static int
      40  pts_name (int fd, char **pts, size_t buf_len, struct stat64 *stp)
      41  {
      42    int rv;
      43    char *buf = *pts;
      44  
      45    for (;;)
      46      {
      47        char *new_buf;
      48  
      49        if (buf_len)
      50  	{
      51  	  rv = __ptsname_internal (fd, buf, buf_len, stp);
      52  	  if (rv != 0)
      53  	    {
      54  	      if (rv == ENOTTY)
      55  		/* ptsname_r returns with ENOTTY to indicate
      56  		   a descriptor not referring to a pty master.
      57  		   For this condition, grantpt must return EINVAL.  */
      58  		rv = EINVAL;
      59  	      errno = rv;	/* Not necessarily set by __ptsname_r.  */
      60  	      break;
      61  	    }
      62  
      63  	  if (memchr (buf, '\0', buf_len))
      64  	    /* We succeeded and the returned name fit in the buffer.  */
      65  	    break;
      66  
      67  	  /* Try again with a longer buffer.  */
      68  	  buf_len += buf_len;	/* Double it */
      69  	}
      70        else
      71  	/* No initial buffer; start out by mallocing one.  */
      72  	buf_len = 128;		/* First time guess.  */
      73  
      74        if (buf != *pts)
      75  	/* We've already malloced another buffer at least once.  */
      76  	new_buf = (char *) realloc (buf, buf_len);
      77        else
      78  	new_buf = (char *) malloc (buf_len);
      79        if (! new_buf)
      80  	{
      81  	  rv = -1;
      82  	  __set_errno (ENOMEM);
      83  	  break;
      84  	}
      85        buf = new_buf;
      86      }
      87  
      88    if (rv == 0)
      89      *pts = buf;		/* Return buffer to the user.  */
      90    else if (buf != *pts)
      91      free (buf);		/* Free what we malloced when returning an error.  */
      92  
      93    return rv;
      94  }
      95  
      96  /* Change the ownership and access permission of the slave pseudo
      97     terminal associated with the master pseudo terminal specified
      98     by FD.  */
      99  int
     100  grantpt (int fd)
     101  {
     102    int retval = -1;
     103  #ifdef PATH_MAX
     104    char _buf[PATH_MAX];
     105  #else
     106    char _buf[512];
     107  #endif
     108    char *buf = _buf;
     109    struct stat64 st;
     110  
     111    if (__glibc_unlikely (pts_name (fd, &buf, sizeof (_buf), &st)))
     112      {
     113        int save_errno = errno;
     114  
     115        /* Check, if the file descriptor is valid.  pts_name returns the
     116  	 wrong errno number, so we cannot use that.  */
     117        if (__libc_fcntl (fd, F_GETFD) == -1 && errno == EBADF)
     118  	return -1;
     119  
     120         /* If the filedescriptor is no TTY, grantpt has to set errno
     121  	  to EINVAL.  */
     122         if (save_errno == ENOTTY)
     123  	 __set_errno (EINVAL);
     124         else
     125  	 __set_errno (save_errno);
     126  
     127         return -1;
     128      }
     129  
     130    /* Make sure that we own the device.  */
     131    uid_t uid = __getuid ();
     132    if (st.st_uid != uid)
     133      {
     134        if (__chown (buf, uid, st.st_gid) < 0)
     135  	goto helper;
     136      }
     137  
     138    static int tty_gid = -1;
     139    if (__glibc_unlikely (tty_gid == -1))
     140      {
     141        char *grtmpbuf;
     142        struct group grbuf;
     143        size_t grbuflen = __sysconf (_SC_GETGR_R_SIZE_MAX);
     144        struct group *p;
     145  
     146        /* Get the group ID of the special `tty' group.  */
     147        if (grbuflen == (size_t) -1L)
     148  	/* `sysconf' does not support _SC_GETGR_R_SIZE_MAX.
     149  	   Try a moderate value.  */
     150  	grbuflen = 1024;
     151        struct scratch_buffer sbuf;
     152        scratch_buffer_init (&sbuf);
     153        if (!scratch_buffer_set_array_size (&sbuf, 1, grbuflen))
     154  	{
     155  	  retval = -1;
     156  	  goto cleanup;
     157  	}
     158        grtmpbuf = sbuf.data;
     159        __getgrnam_r (TTY_GROUP, &grbuf, grtmpbuf, grbuflen, &p);
     160        if (p != NULL)
     161  	tty_gid = p->gr_gid;
     162  
     163        scratch_buffer_free(&sbuf);
     164      }
     165    gid_t gid = tty_gid == -1 ? __getgid () : tty_gid;
     166  
     167  #if HAVE_PT_CHOWN
     168    /* Make sure the group of the device is that special group.  */
     169    if (st.st_gid != gid)
     170      {
     171        if (__chown (buf, uid, gid) < 0)
     172  	goto helper;
     173      }
     174  
     175    /* Make sure the permission mode is set to readable and writable by
     176       the owner, and writable by the group.  */
     177    mode_t mode = S_IRUSR|S_IWUSR|S_IWGRP;
     178  #else
     179    /* When built without pt_chown, we have delegated the creation of the
     180       pty node with the right group and permission mode to the kernel, and
     181       non-root users are unlikely to be able to change it. Therefore let's
     182       consider that POSIX enforcement is the responsibility of the whole
     183       system and not only the GNU libc. Thus accept different group or
     184       permission mode.  */
     185  
     186    /* Make sure the permission is set to readable and writable by the
     187       owner.  For security reasons, make it writable by the group only
     188       when originally writable and when the group of the device is that
     189       special group.  */
     190    mode_t mode = S_IRUSR|S_IWUSR
     191  	        |((st.st_gid == gid) ? (st.st_mode & S_IWGRP) : 0);
     192  #endif
     193  
     194    if ((st.st_mode & ACCESSPERMS) != mode)
     195      {
     196        if (__chmod (buf, mode) < 0)
     197  	goto helper;
     198      }
     199  
     200    retval = 0;
     201    goto cleanup;
     202  
     203    /* We have to use the helper program if it is available.  */
     204   helper:;
     205  
     206  #if HAVE_PT_CHOWN
     207    pid_t pid = __fork ();
     208    if (pid == -1)
     209      goto cleanup;
     210    else if (pid == 0)
     211      {
     212        /* Disable core dumps.  */
     213        struct rlimit rl = { 0, 0 };
     214        __setrlimit (RLIMIT_CORE, &rl);
     215  
     216        /* We pass the master pseudo terminal as file descriptor PTY_FILENO.  */
     217        if (fd != PTY_FILENO)
     218  	if (__dup2 (fd, PTY_FILENO) < 0)
     219  	  _exit (FAIL_EBADF);
     220  
     221  # ifdef CLOSE_ALL_FDS
     222        CLOSE_ALL_FDS ();
     223  # endif
     224  
     225        execle (_PATH_PT_CHOWN, __basename (_PATH_PT_CHOWN), NULL, NULL);
     226        _exit (FAIL_EXEC);
     227      }
     228    else
     229      {
     230        int w;
     231  
     232        if (__waitpid (pid, &w, 0) == -1)
     233  	goto cleanup;
     234        if (!WIFEXITED (w))
     235  	__set_errno (ENOEXEC);
     236        else
     237  	switch (WEXITSTATUS (w))
     238  	  {
     239  	  case 0:
     240  	    retval = 0;
     241  	    break;
     242  	  case FAIL_EBADF:
     243  	    __set_errno (EBADF);
     244  	    break;
     245  	  case FAIL_EINVAL:
     246  	    __set_errno (EINVAL);
     247  	    break;
     248  	  case FAIL_EACCES:
     249  	    __set_errno (EACCES);
     250  	    break;
     251  	  case FAIL_EXEC:
     252  	    __set_errno (ENOEXEC);
     253  	    break;
     254  	  case FAIL_ENOMEM:
     255  	    __set_errno (ENOMEM);
     256  	    break;
     257  
     258  	  default:
     259  	    assert(! "grantpt: internal error: invalid exit code from pt_chown");
     260  	  }
     261      }
     262  #endif
     263  
     264   cleanup:
     265    if (buf != _buf)
     266      free (buf);
     267  
     268    return retval;
     269  }
     270  libc_hidden_def (grantpt)