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