Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/src/ttyname.c
1532 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 2012-2024 Todd C. Miller <[email protected]>
5
*
6
* Permission to use, copy, modify, and distribute this software for any
7
* purpose with or without fee is hereby granted, provided that the above
8
* copyright notice and this permission notice appear in all copies.
9
*
10
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
*/
18
19
#include <config.h>
20
21
/* Large files may not be supported by procfs.h on Solaris. */
22
#if defined(HAVE_STRUCT_PSINFO_PR_TTYDEV)
23
# undef _FILE_OFFSET_BITS
24
# undef _LARGE_FILES
25
#endif
26
27
#include <sys/types.h>
28
#include <sys/stat.h>
29
#if defined(MAJOR_IN_MKDEV)
30
# include <sys/mkdev.h>
31
#elif defined(MAJOR_IN_SYSMACROS)
32
# include <sys/sysmacros.h>
33
#endif
34
#include <stdio.h>
35
#include <stdlib.h>
36
#include <string.h>
37
#include <unistd.h>
38
#include <errno.h>
39
#include <fcntl.h>
40
#include <limits.h>
41
#include <dirent.h>
42
#if defined(HAVE_KINFO_PROC2_NETBSD) || defined (HAVE_KINFO_PROC_OPENBSD) || defined(HAVE_KINFO_PROC_44BSD)
43
# include <sys/sysctl.h>
44
#elif defined(HAVE_KINFO_PROC_FREEBSD) || defined(HAVE_KINFO_PROC_DFLY)
45
# include <sys/param.h>
46
# include <sys/sysctl.h>
47
# include <sys/user.h>
48
#endif
49
#if defined(HAVE_PROCFS_H)
50
# include <procfs.h>
51
#elif defined(HAVE_SYS_PROCFS_H)
52
# include <sys/procfs.h>
53
#endif
54
#ifdef HAVE_PSTAT_GETPROC
55
# include <sys/pstat.h>
56
#endif
57
58
#include <sudo.h>
59
60
/*
61
* How to access the tty device number in struct kinfo_proc.
62
*/
63
#if defined(HAVE_KINFO_PROC2_NETBSD)
64
# define SUDO_KERN_PROC KERN_PROC2
65
# define sudo_kinfo_proc kinfo_proc2
66
# define sudo_kp_tdev p_tdev
67
# define sudo_kp_namelen 6
68
#elif defined(HAVE_KINFO_PROC_OPENBSD)
69
# define SUDO_KERN_PROC KERN_PROC
70
# define sudo_kinfo_proc kinfo_proc
71
# define sudo_kp_tdev p_tdev
72
# define sudo_kp_namelen 6
73
#elif defined(HAVE_KINFO_PROC_FREEBSD)
74
# define SUDO_KERN_PROC KERN_PROC
75
# define sudo_kinfo_proc kinfo_proc
76
# define sudo_kp_tdev ki_tdev
77
# define sudo_kp_namelen 4
78
#elif defined(HAVE_KINFO_PROC_DFLY)
79
# define SUDO_KERN_PROC KERN_PROC
80
# define sudo_kinfo_proc kinfo_proc
81
# define sudo_kp_tdev kp_tdev
82
# define sudo_kp_namelen 4
83
#elif defined(HAVE_KINFO_PROC_44BSD)
84
# define SUDO_KERN_PROC KERN_PROC
85
# define sudo_kinfo_proc kinfo_proc
86
# define sudo_kp_tdev kp_eproc.e_tdev
87
# define sudo_kp_namelen 4
88
#endif
89
90
#if defined(sudo_kp_tdev)
91
/*
92
* Look up terminal device that the process is attached to and
93
* fill in its name, if available. Sets name to the empty string
94
* if the device number cannot be mapped to a device name.
95
* Returns the tty device number on success and -1 on failure, setting errno.
96
*/
97
dev_t
98
get_process_ttyname(char *name, size_t namelen)
99
{
100
struct sudo_kinfo_proc *ki_proc = NULL;
101
size_t size = sizeof(*ki_proc);
102
int mib[6], rc, serrno = errno;
103
dev_t ttydev = NODEV;
104
debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL);
105
106
/*
107
* Lookup controlling tty for this process via sysctl.
108
* This will work even if std{in,out,err} are redirected.
109
*/
110
mib[0] = CTL_KERN;
111
mib[1] = SUDO_KERN_PROC;
112
mib[2] = KERN_PROC_PID;
113
mib[3] = (int)getpid();
114
mib[4] = sizeof(*ki_proc);
115
mib[5] = 1;
116
for (;;) {
117
struct sudo_kinfo_proc *kp;
118
119
size += size / 10;
120
if ((kp = realloc(ki_proc, size)) == NULL) {
121
rc = -1;
122
break; /* really out of memory. */
123
}
124
ki_proc = kp;
125
rc = sysctl(mib, sudo_kp_namelen, ki_proc, &size, NULL, 0);
126
if (rc != -1 || errno != ENOMEM)
127
break;
128
}
129
errno = ENOENT;
130
if (rc != -1) {
131
if ((dev_t)ki_proc->sudo_kp_tdev != NODEV) {
132
errno = serrno;
133
ttydev = (dev_t)ki_proc->sudo_kp_tdev;
134
if (sudo_ttyname_dev(ttydev, name, namelen) == NULL) {
135
sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
136
"unable to find terminal name for device %u, %u",
137
(unsigned int)major(ttydev), (unsigned int)minor(ttydev));
138
if (namelen != 0)
139
*name = '\0';
140
}
141
}
142
} else {
143
sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
144
"unable to resolve tty via KERN_PROC");
145
}
146
free(ki_proc);
147
148
debug_return_dev_t(ttydev);
149
}
150
#elif defined(HAVE_STRUCT_PSINFO_PR_TTYDEV)
151
/*
152
* Look up terminal device that the process is attached to and
153
* fill in its name, if available. Sets name to the empty string
154
* if the device number cannot be mapped to a device name.
155
* Returns the tty device number on success and -1 on failure, setting errno.
156
*/
157
dev_t
158
get_process_ttyname(char *name, size_t namelen)
159
{
160
dev_t ttydev = NODEV;
161
struct psinfo psinfo;
162
char path[PATH_MAX];
163
ssize_t nread;
164
int fd, serrno = errno;
165
debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL);
166
167
/* Try to determine the tty from pr_ttydev in /proc/pid/psinfo. */
168
(void)snprintf(path, sizeof(path), "/proc/%u/psinfo", (unsigned int)getpid());
169
if ((fd = open(path, O_RDONLY, 0)) != -1) {
170
nread = read(fd, &psinfo, sizeof(psinfo));
171
close(fd);
172
if (nread == (ssize_t)sizeof(psinfo)) {
173
ttydev = (dev_t)psinfo.pr_ttydev;
174
#if defined(_AIX) && defined(DEVNO64)
175
if ((psinfo.pr_ttydev & DEVNO64) && sizeof(dev_t) == 4)
176
ttydev = makedev(major64(psinfo.pr_ttydev), minor64(psinfo.pr_ttydev));
177
#endif
178
/* On AIX, pr_ttydev is 0 (not -1) when no terminal is present. */
179
if (ttydev != 0 && ttydev != NODEV) {
180
errno = serrno;
181
if (sudo_ttyname_dev(ttydev, name, namelen) == NULL) {
182
sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
183
"unable to find terminal name for device %u, %u",
184
(unsigned int)major(ttydev), (unsigned int)minor(ttydev));
185
if (namelen != 0)
186
*name = '\0';
187
}
188
goto done;
189
}
190
ttydev = NODEV;
191
}
192
} else {
193
struct stat sb;
194
int i;
195
196
/* Missing /proc/pid/psinfo file. */
197
for (i = STDIN_FILENO; i <= STDERR_FILENO; i++) {
198
if (sudo_isatty(i, &sb)) {
199
ttydev = sb.st_rdev;
200
if (sudo_ttyname_dev(ttydev, name, namelen) == NULL) {
201
sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
202
"unable to find terminal name for device %u, %u",
203
(unsigned int)major(ttydev), (unsigned int)minor(ttydev));
204
if (namelen != 0)
205
*name = '\0';
206
}
207
goto done;
208
}
209
}
210
}
211
errno = ENOENT;
212
213
done:
214
if (ttydev == NODEV)
215
sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
216
"unable to resolve tty via %s", path);
217
218
debug_return_dev_t(ttydev);
219
}
220
#elif defined(__linux__)
221
/*
222
* Look up terminal device that the process is attached to and
223
* fill in its name, if available. Sets name to the empty string
224
* if the device number cannot be mapped to a device name.
225
* Returns the tty device number on success and -1 on failure, setting errno.
226
*/
227
dev_t
228
get_process_ttyname(char *name, size_t namelen)
229
{
230
const char path[] = "/proc/self/stat";
231
dev_t ttydev = NODEV;
232
char *cp, buf[1024];
233
int serrno = errno;
234
pid_t ppid = 0;
235
ssize_t nread;
236
int fd;
237
debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL);
238
239
/*
240
* Try to determine the tty from tty_nr in /proc/self/stat.
241
* Ignore /proc/self/stat if it contains embedded NUL bytes.
242
*/
243
if ((fd = open(path, O_RDONLY | O_NOFOLLOW)) != -1) {
244
cp = buf;
245
while ((nread = read(fd, cp, sizeof(buf) - (size_t)(cp - buf))) != 0) {
246
if (nread < 0) {
247
if (errno == EAGAIN || errno == EINTR)
248
continue;
249
break;
250
}
251
cp += nread;
252
if (cp >= buf + sizeof(buf))
253
break;
254
}
255
if (nread == 0 && memchr(buf, '\0', (size_t)(cp - buf)) == NULL) {
256
/*
257
* Field 7 is the tty dev (0 if no tty).
258
* Since the process name at field 2 "(comm)" may include
259
* whitespace (including newlines), start at the last ')' found.
260
*/
261
*cp = '\0';
262
cp = strrchr(buf, ')');
263
if (cp != NULL) {
264
char *ep = cp;
265
const char *errstr;
266
int field = 1;
267
268
while (*++ep != '\0') {
269
if (*ep == ' ') {
270
*ep = '\0';
271
field++;
272
if (field == 7) {
273
int tty_nr = (int)sudo_strtonum(cp, INT_MIN,
274
INT_MAX, &errstr);
275
if (errstr) {
276
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
277
"%s: tty device %s: %s", path, cp, errstr);
278
}
279
if (tty_nr != 0) {
280
/*
281
* Avoid sign extension when assigning tdev.
282
* tty_nr in /proc/self/stat is printed as a
283
* signed int but the actual device number is an
284
* unsigned int and dev_t is unsigned long long.
285
*/
286
ttydev = (unsigned int)tty_nr;
287
errno = serrno;
288
if (sudo_ttyname_dev(ttydev, name, namelen) == NULL) {
289
sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
290
"unable to find terminal name for device %u, %u",
291
(unsigned int)major(ttydev), (unsigned int)minor(ttydev));
292
if (namelen != 0)
293
*name = '\0';
294
}
295
goto done;
296
}
297
break;
298
}
299
if (field == 4) {
300
ppid =
301
(int)sudo_strtonum(cp, INT_MIN, INT_MAX, NULL);
302
}
303
cp = ep + 1;
304
}
305
}
306
}
307
}
308
}
309
if (ppid == 0) {
310
struct stat sb;
311
int i;
312
313
/* No parent pid found, /proc/self/stat is missing or corrupt. */
314
for (i = STDIN_FILENO; i <= STDERR_FILENO; i++) {
315
if (sudo_isatty(i, &sb)) {
316
ttydev = sb.st_rdev;
317
if (sudo_ttyname_dev(sb.st_rdev, name, namelen) == NULL) {
318
sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
319
"unable to find terminal name for device %u, %u",
320
(unsigned int)major(ttydev), (unsigned int)minor(ttydev));
321
if (namelen != 0)
322
*name = '\0';
323
}
324
goto done;
325
}
326
}
327
}
328
errno = ENOENT;
329
330
done:
331
if (fd != -1)
332
close(fd);
333
if (ttydev == NODEV)
334
sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
335
"unable to resolve tty via %s", path);
336
337
debug_return_dev_t(ttydev);
338
}
339
#elif defined(HAVE_PSTAT_GETPROC)
340
/*
341
* Look up terminal device that the process is attached to and
342
* fill in its name, if available. Sets name to the empty string
343
* if the device number cannot be mapped to a device name.
344
* Returns the tty device number on success and -1 on failure, setting errno.
345
*/
346
dev_t
347
get_process_ttyname(char *name, size_t namelen)
348
{
349
dev_t ttydev = NODEV;
350
int rc, serrno = errno;
351
struct pst_status pst;
352
debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL);
353
354
/*
355
* Determine the tty from psdev in struct pst_status.
356
* EOVERFLOW is not a fatal error for the fields we use.
357
* See the "EOVERFLOW Error" section of pstat_getvminfo(3).
358
*/
359
rc = pstat_getproc(&pst, sizeof(pst), 0, getpid());
360
if (rc != -1 || errno == EOVERFLOW) {
361
if (pst.pst_term.psd_major != -1 && pst.pst_term.psd_minor != -1) {
362
errno = serrno;
363
ttydev = makedev(pst.pst_term.psd_major, pst.pst_term.psd_minor);
364
if (sudo_ttyname_dev(ttydev, name, namelen) == NULL) {
365
sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
366
"unable to find terminal name for device %u, %u",
367
(unsigned int)pst.pst_term.psd_major,
368
(unsigned int)pst.pst_term.psd_minor);
369
if (namelen != 0)
370
*name = '\0';
371
}
372
goto done;
373
}
374
}
375
sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
376
"unable to resolve tty via pstat");
377
errno = ENOENT;
378
379
done:
380
381
debug_return_dev_t(ttydev);
382
}
383
#else
384
/*
385
* Look up terminal device that the process is attached to and fill in name.
386
* Returns the tty device number on success and -1 on failure, setting errno.
387
*/
388
dev_t
389
get_process_ttyname(char *name, size_t namelen)
390
{
391
struct stat sb;
392
char *tty;
393
int i;
394
debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL);
395
396
for (i = STDIN_FILENO; i <= STDERR_FILENO; i++) {
397
/* Only call ttyname() on a character special device. */
398
if (fstat(i, &sb) == -1 || !S_ISCHR(sb.st_mode))
399
continue;
400
if ((tty = ttyname(i)) == NULL)
401
continue;
402
403
if (strlcpy(name, tty, namelen) >= namelen) {
404
errno = ENAMETOOLONG;
405
sudo_debug_printf(
406
SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
407
"unable to store tty from ttyname");
408
debug_return_dev_t(-1);
409
}
410
debug_return_dev_t(sb.st_rdev);
411
}
412
413
sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
414
"unable to resolve tty via ttyname");
415
errno = ENOENT;
416
debug_return_dev_t(NODEV);
417
}
418
#endif
419
420