elf: Fix compile error with -DNDEBUG [BZ #18755]
[glibc.git] / sysdeps / unix / grantpt.c
blob226e7adb75b5e7a6e01f78b51e15cf42ba05eae8
1 /* Copyright (C) 1998-2023 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
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.
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.
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/>. */
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>
32 #include "pty-private.h"
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)
42 int rv;
43 char *buf = *pts;
45 for (;;)
47 char *new_buf;
49 if (buf_len)
51 rv = __ptsname_internal (fd, buf, buf_len, stp);
52 if (rv != 0)
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;
63 if (memchr (buf, '\0', buf_len))
64 /* We succeeded and the returned name fit in the buffer. */
65 break;
67 /* Try again with a longer buffer. */
68 buf_len += buf_len; /* Double it */
70 else
71 /* No initial buffer; start out by mallocing one. */
72 buf_len = 128; /* First time guess. */
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)
81 rv = -1;
82 __set_errno (ENOMEM);
83 break;
85 buf = new_buf;
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. */
93 return rv;
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)
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;
111 if (__glibc_unlikely (pts_name (fd, &buf, sizeof (_buf), &st)))
113 int save_errno = errno;
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;
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);
127 return -1;
130 /* Make sure that we own the device. */
131 uid_t uid = __getuid ();
132 if (st.st_uid != uid)
134 if (__chown (buf, uid, st.st_gid) < 0)
135 goto helper;
138 static int tty_gid = -1;
139 if (__glibc_unlikely (tty_gid == -1))
141 char *grtmpbuf;
142 struct group grbuf;
143 size_t grbuflen = __sysconf (_SC_GETGR_R_SIZE_MAX);
144 struct group *p;
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))
155 retval = -1;
156 goto cleanup;
158 grtmpbuf = sbuf.data;
159 __getgrnam_r (TTY_GROUP, &grbuf, grtmpbuf, grbuflen, &p);
160 if (p != NULL)
161 tty_gid = p->gr_gid;
163 scratch_buffer_free(&sbuf);
165 gid_t gid = tty_gid == -1 ? __getgid () : tty_gid;
167 #if HAVE_PT_CHOWN
168 /* Make sure the group of the device is that special group. */
169 if (st.st_gid != gid)
171 if (__chown (buf, uid, gid) < 0)
172 goto helper;
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. */
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
194 if ((st.st_mode & ACCESSPERMS) != mode)
196 if (__chmod (buf, mode) < 0)
197 goto helper;
200 retval = 0;
201 goto cleanup;
203 /* We have to use the helper program if it is available. */
204 helper:;
206 #if HAVE_PT_CHOWN
207 pid_t pid = __fork ();
208 if (pid == -1)
209 goto cleanup;
210 else if (pid == 0)
212 /* Disable core dumps. */
213 struct rlimit rl = { 0, 0 };
214 __setrlimit (RLIMIT_CORE, &rl);
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);
221 # ifdef CLOSE_ALL_FDS
222 CLOSE_ALL_FDS ();
223 # endif
225 execle (_PATH_PT_CHOWN, __basename (_PATH_PT_CHOWN), NULL, NULL);
226 _exit (FAIL_EXEC);
228 else
230 int w;
232 if (__waitpid (pid, &w, 0) == -1)
233 goto cleanup;
234 if (!WIFEXITED (w))
235 __set_errno (ENOEXEC);
236 else
237 switch (WEXITSTATUS (w))
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;
258 default:
259 assert(! "grantpt: internal error: invalid exit code from pt_chown");
262 #endif
264 cleanup:
265 if (buf != _buf)
266 free (buf);
268 return retval;
270 libc_hidden_def (grantpt)