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