Imported Upstream version 1.8.7
[debian/sudo] / src / ttyname.c
1 /*
2  * Copyright (c) 2012-2013 Todd C. Miller <Todd.Miller@courtesan.com>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 #include <config.h>
18
19 /* Large files not supported by procfs.h */
20 #if defined(HAVE_PROCFS_H) || defined(HAVE_SYS_PROCFS_H)
21 # undef _FILE_OFFSET_BITS
22 # undef _LARGE_FILES
23 #endif
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #if defined(MAJOR_IN_MKDEV)
28 # include <sys/mkdev.h>
29 #elif defined(MAJOR_IN_SYSMACROS)
30 # include <sys/sysmacros.h>
31 #endif
32 #include <stdio.h>
33 #ifdef STDC_HEADERS
34 # include <stdlib.h>
35 # include <stddef.h>
36 #else
37 # ifdef HAVE_STDLIB_H
38 #  include <stdlib.h>
39 # endif
40 #endif /* STDC_HEADERS */
41 #ifdef HAVE_STRING_H
42 # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
43 #  include <memory.h>
44 # endif
45 # include <string.h>
46 #endif /* HAVE_STRING_H */
47 #ifdef HAVE_STRINGS_H
48 # include <strings.h>
49 #endif /* HAVE_STRINGS_H */
50 #ifdef HAVE_UNISTD_H
51 # include <unistd.h>
52 #endif /* HAVE_UNISTD_H */
53 #include <errno.h>
54 #include <fcntl.h>
55 #include <limits.h>
56 #ifdef HAVE_DIRENT_H
57 # include <dirent.h>
58 # define NAMLEN(dirent) strlen((dirent)->d_name)
59 #else
60 # define dirent direct
61 # define NAMLEN(dirent) (dirent)->d_namlen
62 # ifdef HAVE_SYS_NDIR_H
63 #  include <sys/ndir.h>
64 # endif
65 # ifdef HAVE_SYS_DIR_H
66 #  include <sys/dir.h>
67 # endif
68 # ifdef HAVE_NDIR_H
69 #  include <ndir.h>
70 # endif
71 #endif
72 #if defined(HAVE_STRUCT_KINFO_PROC_P_TDEV) || defined (HAVE_STRUCT_KINFO_PROC_KP_EPROC_E_TDEV) || defined(HAVE_STRUCT_KINFO_PROC2_P_TDEV)
73 # include <sys/param.h>
74 # include <sys/sysctl.h>
75 #elif defined(HAVE_STRUCT_KINFO_PROC_KI_TDEV)
76 # include <sys/param.h>
77 # include <sys/sysctl.h>
78 # include <sys/user.h>
79 #endif
80 #if defined(HAVE_PROCFS_H)
81 # include <procfs.h>
82 #elif defined(HAVE_SYS_PROCFS_H)
83 # include <sys/procfs.h>
84 #endif
85 #ifdef HAVE_PSTAT_GETPROC
86 # include <sys/param.h>
87 # include <sys/pstat.h>
88 #endif
89
90 #include "sudo.h"
91
92 /*
93  * How to access the tty device number in struct kinfo_proc.
94  */
95 #if defined(HAVE_STRUCT_KINFO_PROC2_P_TDEV)
96 # define SUDO_KERN_PROC         KERN_PROC2
97 # define sudo_kinfo_proc        kinfo_proc2
98 # define sudo_kp_tdev           p_tdev
99 # define sudo_kp_namelen        6
100 #elif defined(HAVE_STRUCT_KINFO_PROC_P_TDEV)
101 # define SUDO_KERN_PROC         KERN_PROC
102 # define sudo_kinfo_proc        kinfo_proc
103 # define sudo_kp_tdev           p_tdev
104 # define sudo_kp_namelen        6
105 #elif defined(HAVE_STRUCT_KINFO_PROC_KI_TDEV)
106 # define SUDO_KERN_PROC         KERN_PROC
107 # define sudo_kinfo_proc        kinfo_proc
108 # define sudo_kp_tdev           ki_tdev
109 # define sudo_kp_namelen        4
110 #elif defined(HAVE_STRUCT_KINFO_PROC_KP_EPROC_E_TDEV)
111 # define SUDO_KERN_PROC         KERN_PROC
112 # define sudo_kinfo_proc        kinfo_proc
113 # define sudo_kp_tdev           kp_eproc.e_tdev
114 # define sudo_kp_namelen        4
115 #endif
116
117 #if defined(sudo_kp_tdev)
118 /*
119  * Like ttyname() but uses a dev_t instead of an open fd.
120  * Caller is responsible for freeing the returned string.
121  * The BSD version uses devname()
122  */
123 static char *
124 sudo_ttyname_dev(dev_t tdev)
125 {
126     char *dev, *tty = NULL;
127     debug_decl(sudo_ttyname_dev, SUDO_DEBUG_UTIL)
128
129     /* Some versions of devname() return NULL on failure, others do not. */
130     dev = devname(tdev, S_IFCHR);
131     if (dev != NULL && *dev != '?' && *dev != '#') {
132         if (*dev != '/') {
133             /* devname() doesn't use the /dev/ prefix, add one... */
134             size_t len = sizeof(_PATH_DEV) + strlen(dev);
135             tty = emalloc(len);
136             strlcpy(tty, _PATH_DEV, len);
137             strlcat(tty, dev, len);
138         } else {
139             /* Should not happen but just in case... */
140             tty = estrdup(dev);
141         }
142     }
143     debug_return_str(tty);
144 }
145 #elif defined(HAVE__TTYNAME_DEV)
146 extern char *_ttyname_dev(dev_t rdev, char *buffer, size_t buflen);
147
148 /*
149  * Like ttyname() but uses a dev_t instead of an open fd.
150  * Caller is responsible for freeing the returned string.
151  * This version is just a wrapper around _ttyname_dev().
152  */
153 static char *
154 sudo_ttyname_dev(dev_t tdev)
155 {
156     char buf[TTYNAME_MAX], *tty;
157     debug_decl(sudo_ttyname_dev, SUDO_DEBUG_UTIL)
158
159     tty = _ttyname_dev(tdev, buf, sizeof(buf));
160
161     debug_return_str(estrdup(tty));
162 }
163 #elif defined(HAVE_STRUCT_PSINFO_PR_TTYDEV) || defined(HAVE_PSTAT_GETPROC) || defined(__linux__)
164 /*
165  * Devices to search before doing a breadth-first scan.
166  */
167 static char *search_devs[] = {
168     "/dev/console",
169     "/dev/wscons",
170     "/dev/pts/",
171     "/dev/vt/",
172     "/dev/term/",
173     "/dev/zcons/",
174     NULL
175 };
176
177 static char *ignore_devs[] = {
178     "/dev/fd/",
179     "/dev/stdin",
180     "/dev/stdout",
181     "/dev/stderr",
182     NULL
183 };
184
185 /*
186  * Do a breadth-first scan of dir looking for the specified device.
187  */
188 static char *
189 sudo_ttyname_scan(const char *dir, dev_t rdev, bool builtin)
190 {
191     DIR *d = NULL;
192     char pathbuf[PATH_MAX], **subdirs = NULL, *devname = NULL;
193     size_t sdlen, d_len, len, num_subdirs = 0, max_subdirs = 0;
194     struct dirent *dp;
195     struct stat sb;
196     int i;
197     debug_decl(sudo_ttyname_scan, SUDO_DEBUG_UTIL)
198
199     if (dir[0] == '\0' || (d = opendir(dir)) == NULL)
200         goto done;
201
202     sudo_debug_printf(SUDO_DEBUG_INFO, "scanning for dev %u in %s",
203         (unsigned int)rdev, dir);
204
205     sdlen = strlen(dir);
206     if (dir[sdlen - 1] == '/')
207         sdlen--;
208     if (sdlen + 1 >= sizeof(pathbuf)) {
209         errno = ENAMETOOLONG;
210         warning("%.*s/", (int)sdlen, dir);
211         goto done;
212     }
213     memcpy(pathbuf, dir, sdlen);
214     pathbuf[sdlen++] = '/';
215     pathbuf[sdlen] = '\0';
216
217     while ((dp = readdir(d)) != NULL) {
218         /* Skip anything starting with "." */
219         if (dp->d_name[0] == '.')
220             continue;
221
222         d_len = NAMLEN(dp);
223         if (sdlen + d_len >= sizeof(pathbuf))
224             continue;
225         memcpy(&pathbuf[sdlen], dp->d_name, d_len + 1); /* copy NUL too */
226         d_len += sdlen;
227
228         for (i = 0; ignore_devs[i] != NULL; i++) {
229             len = strlen(ignore_devs[i]);
230             if (ignore_devs[i][len - 1] == '/')
231                 len--;
232             if (d_len == len && strncmp(pathbuf, ignore_devs[i], len) == 0)
233                 break;
234         }
235         if (ignore_devs[i] != NULL)
236             continue;
237         if (!builtin) {
238             /* Skip entries in search_devs; we already checked them. */
239             for (i = 0; search_devs[i] != NULL; i++) {
240                 len = strlen(search_devs[i]);
241                 if (search_devs[i][len - 1] == '/')
242                     len--;
243                 if (d_len == len && strncmp(pathbuf, search_devs[i], len) == 0)
244                     break;
245             }
246             if (search_devs[i] != NULL)
247                 continue;
248         }
249 # if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(DTTOIF)
250         /*
251          * Convert dp->d_type to sb.st_mode to avoid a stat(2) if possible.
252          * We can't use it for links (since we want to follow them) or
253          * char devs (since we need st_rdev to compare the device number).
254          */
255         if (dp->d_type != DT_UNKNOWN && dp->d_type != DT_LNK && dp->d_type != DT_CHR)
256             sb.st_mode = DTTOIF(dp->d_type);
257         else
258 # endif
259         if (stat(pathbuf, &sb) == -1)
260             continue;
261         if (S_ISDIR(sb.st_mode)) {
262             if (!builtin) {
263                 /* Add to list of subdirs to search. */
264                 if (num_subdirs + 1 > max_subdirs) {
265                     max_subdirs += 64;
266                     subdirs = erealloc3(subdirs, max_subdirs, sizeof(char *));
267                 }
268                 subdirs[num_subdirs++] = estrdup(pathbuf);
269             }
270             continue;
271         }
272         if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev) {
273             devname = estrdup(pathbuf);
274             sudo_debug_printf(SUDO_DEBUG_INFO, "resolved dev %u as %s",
275                 (unsigned int)rdev, pathbuf);
276             goto done;
277         }
278     }
279
280     /* Search subdirs if we didn't find it in the root level. */
281     for (i = 0; devname == NULL && i < num_subdirs; i++)
282         devname = sudo_ttyname_scan(subdirs[i], rdev, false);
283
284 done:
285     if (d != NULL)
286         closedir(d);
287     for (i = 0; i < num_subdirs; i++)
288         efree(subdirs[i]);
289     efree(subdirs);
290     debug_return_str(devname);
291 }
292
293 /*
294  * Like ttyname() but uses a dev_t instead of an open fd.
295  * Caller is responsible for freeing the returned string.
296  * Generic version.
297  */
298 static char *
299 sudo_ttyname_dev(dev_t rdev)
300 {
301     struct stat sb;
302     size_t len;
303     char buf[PATH_MAX], **sd, *devname, *tty = NULL;
304     debug_decl(sudo_ttyname_dev, SUDO_DEBUG_UTIL)
305
306     /*
307      * First check search_devs for common tty devices.
308      */
309     for (sd = search_devs; tty == NULL && (devname = *sd) != NULL; sd++) {
310         len = strlen(devname);
311         if (devname[len - 1] == '/') {
312             if (strcmp(devname, "/dev/pts/") == 0) {
313                 /* Special case /dev/pts */
314                 (void)snprintf(buf, sizeof(buf), "%spts/%u", _PATH_DEV,
315                     (unsigned int)minor(rdev));
316                 if (stat(buf, &sb) == 0) {
317                     if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev)
318                         tty = estrdup(buf);
319                 }
320                 sudo_debug_printf(SUDO_DEBUG_INFO, "comparing dev %u to %s: %s",
321                     (unsigned int)rdev, buf, tty ? "yes" : "no");
322             } else {
323                 /* Traverse directory */
324                 tty = sudo_ttyname_scan(devname, rdev, true);
325             }
326         } else {
327             if (stat(devname, &sb) == 0) {
328                 if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev)
329                     tty = estrdup(devname);
330             }
331         }
332     }
333
334     /*
335      * Not found?  Do a breadth-first traversal of /dev/.
336      */
337     if (tty == NULL)
338         tty = sudo_ttyname_scan(_PATH_DEV, rdev, false);
339
340     debug_return_str(tty);
341 }
342 #endif
343
344 #if defined(sudo_kp_tdev)
345 /*
346  * Return a string from ttyname() containing the tty to which the process is
347  * attached or NULL if the process has no controlling tty.
348  */
349 char *
350 get_process_ttyname(void)
351 {
352     char *tty = NULL;
353     struct sudo_kinfo_proc *ki_proc = NULL;
354     size_t size = sizeof(*ki_proc);
355     int mib[6], rc;
356     debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
357
358     /*
359      * Lookup controlling tty for this process via sysctl.
360      * This will work even if std{in,out,err} are redirected.
361      */
362     mib[0] = CTL_KERN;
363     mib[1] = SUDO_KERN_PROC;
364     mib[2] = KERN_PROC_PID;
365     mib[3] = (int)getpid();
366     mib[4] = sizeof(*ki_proc);
367     mib[5] = 1;
368     do {
369         size += size / 10;
370         ki_proc = erealloc(ki_proc, size);
371         rc = sysctl(mib, sudo_kp_namelen, ki_proc, &size, NULL, 0);
372     } while (rc == -1 && errno == ENOMEM);
373     if (rc != -1) {
374         if (ki_proc->sudo_kp_tdev != (dev_t)-1) {
375             tty = sudo_ttyname_dev(ki_proc->sudo_kp_tdev);
376             if (tty == NULL) {
377                 sudo_debug_printf(SUDO_DEBUG_WARN,
378                     "unable to map device number %u to name",
379                     ki_proc->sudo_kp_tdev);
380             }
381         }
382     } else {
383         sudo_debug_printf(SUDO_DEBUG_WARN,
384             "unable to resolve tty via KERN_PROC: %s", strerror(errno));
385     }
386     efree(ki_proc);
387
388     debug_return_str(tty);
389 }
390 #elif defined(HAVE_STRUCT_PSINFO_PR_TTYDEV)
391 /*
392  * Return a string from ttyname() containing the tty to which the process is
393  * attached or NULL if the process has no controlling tty.
394  */
395 char *
396 get_process_ttyname(void)
397 {
398     char path[PATH_MAX], *tty = NULL;
399     struct psinfo psinfo;
400     ssize_t nread;
401     int fd;
402     debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
403
404     /* Try to determine the tty from pr_ttydev in /proc/pid/psinfo. */
405     snprintf(path, sizeof(path), "/proc/%u/psinfo", (unsigned int)getpid());
406     if ((fd = open(path, O_RDONLY, 0)) != -1) {
407         nread = read(fd, &psinfo, sizeof(psinfo));
408         close(fd);
409         if (nread == (ssize_t)sizeof(psinfo)) {
410             dev_t rdev = (dev_t)psinfo.pr_ttydev;
411 #if defined(_AIX) && defined(DEVNO64)
412             if (psinfo.pr_ttydev & DEVNO64)
413                 rdev = makedev(major64(psinfo.pr_ttydev), minor64(psinfo.pr_ttydev));
414 #endif
415             if (rdev != (dev_t)-1)
416                 tty = sudo_ttyname_dev(rdev);
417         }
418     }
419
420     debug_return_str(tty);
421 }
422 #elif defined(__linux__)
423 /*
424  * Return a string from ttyname() containing the tty to which the process is
425  * attached or NULL if the process has no controlling tty.
426  */
427 char *
428 get_process_ttyname(void)
429 {
430     char path[PATH_MAX], *line = NULL, *tty = NULL;
431     size_t linesize = 0;
432     ssize_t len;
433     FILE *fp;
434     debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
435
436     /* Try to determine the tty from tty_nr in /proc/pid/stat. */
437     snprintf(path, sizeof(path), "/proc/%u/stat", (unsigned int)getpid());
438     if ((fp = fopen(path, "r")) != NULL) {
439         len = getline(&line, &linesize, fp);
440         fclose(fp);
441         if (len != -1) {
442             /* Field 7 is the tty dev (0 if no tty) */
443             char *cp = line;
444             int field = 1;
445             while (*cp != '\0') {
446                 if (*cp++ == ' ') {
447                     if (++field == 7) {
448                         dev_t tdev = (dev_t)atoi(cp);
449                         if (tdev > 0)
450                             tty = sudo_ttyname_dev(tdev);
451                         break;
452                     }
453                 }
454             }
455         }
456         efree(line);
457     }
458
459     debug_return_str(tty);
460 }
461 #elif HAVE_PSTAT_GETPROC
462 /*
463  * Return a string from ttyname() containing the tty to which the process is
464  * attached or NULL if the process has no controlling tty.
465  */
466 char *
467 get_process_ttyname(void)
468 {
469     struct pst_status pstat;
470     char *tty = NULL;
471     debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
472
473     /* Try to determine the tty from psdev in struct pst_status. */
474     if (pstat_getproc(&pstat, sizeof(pstat), 0, (int)getpid()) != -1) {
475         if (pstat.pst_term.psd_major != -1 && pstat.pst_term.psd_minor != -1) {
476             tty = sudo_ttyname_dev(makedev(pstat.pst_term.psd_major,
477                 pstat.pst_term.psd_minor));
478         }
479     }
480     debug_return_str(tty);
481 }
482 #else
483 /*
484  * Return a string from ttyname() containing the tty to which the process is
485  * attached or NULL if the process has no controlling tty.
486  */
487 char *
488 get_process_ttyname(void)
489 {
490     char *tty;
491     debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
492
493     if ((tty = ttyname(STDIN_FILENO)) == NULL) {
494         if ((tty = ttyname(STDOUT_FILENO)) == NULL)
495             tty = ttyname(STDERR_FILENO);
496     }
497
498     debug_return_str(estrdup(tty));
499 }
500 #endif