Imported Upstream version 1.8.4p4
[debian/sudo] / src / ttyname.c
1 /*
2  * Copyright (c) 2012 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 #include <sys/types.h>
20 #include <sys/param.h>
21 #include <sys/stat.h>
22 #include <stdio.h>
23 #ifdef STDC_HEADERS
24 # include <stdlib.h>
25 # include <stddef.h>
26 #else
27 # ifdef HAVE_STDLIB_H
28 #  include <stdlib.h>
29 # endif
30 #endif /* STDC_HEADERS */
31 #ifdef HAVE_STRING_H
32 # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
33 #  include <memory.h>
34 # endif
35 # include <string.h>
36 #endif /* HAVE_STRING_H */
37 #ifdef HAVE_STRINGS_H
38 # include <strings.h>
39 #endif /* HAVE_STRINGS_H */
40 #ifdef HAVE_UNISTD_H
41 # include <unistd.h>
42 #endif /* HAVE_UNISTD_H */
43 #include <errno.h>
44 #include <fcntl.h>
45 #if defined(HAVE_STRUCT_KINFO_PROC_P_TDEV) || defined (HAVE_STRUCT_KINFO_PROC_KP_EPROC_E_TDEV) || defined(HAVE_STRUCT_KINFO_PROC2_P_TDEV)
46 # include <sys/sysctl.h>
47 #elif defined(HAVE_STRUCT_KINFO_PROC_KI_TDEV)
48 # include <sys/sysctl.h>
49 # include <sys/user.h>
50 #endif
51
52 #include "sudo.h"
53
54 /*
55  * How to access the tty device number in struct kinfo_proc.
56  */
57 #if defined(HAVE_STRUCT_KINFO_PROC2_P_TDEV)
58 # define SUDO_KERN_PROC         KERN_PROC2
59 # define sudo_kinfo_proc        kinfo_proc2
60 # define sudo_kp_tdev           p_tdev
61 # define sudo_kp_namelen        6
62 #elif defined(HAVE_STRUCT_KINFO_PROC_P_TDEV)
63 # define SUDO_KERN_PROC         KERN_PROC
64 # define sudo_kinfo_proc        kinfo_proc
65 # define sudo_kp_tdev           p_tdev
66 # define sudo_kp_namelen        6
67 #elif defined(HAVE_STRUCT_KINFO_PROC_KI_TDEV)
68 # define SUDO_KERN_PROC         KERN_PROC
69 # define sudo_kinfo_proc        kinfo_proc
70 # define sudo_kp_tdev           ki_tdev
71 # define sudo_kp_namelen        4
72 #elif defined(HAVE_STRUCT_KINFO_PROC_KP_EPROC_E_TDEV)
73 # define SUDO_KERN_PROC         KERN_PROC
74 # define sudo_kinfo_proc        kinfo_proc
75 # define sudo_kp_tdev           kp_eproc.e_tdev
76 # define sudo_kp_namelen        4
77 #endif
78
79 #ifdef sudo_kp_tdev
80 /*
81  * Return a string from ttyname() containing the tty to which the process is
82  * attached or NULL if there is no tty associated with the process (or its
83  * parent).  First tries sysctl using the current pid, then the parent's pid.
84  * Falls back on ttyname of std{in,out,err} if that fails.
85  */
86 char *
87 get_process_ttyname(void)
88 {
89     char *tty = NULL;
90     struct sudo_kinfo_proc *ki_proc = NULL;
91     size_t size = sizeof(*ki_proc);
92     int i, mib[6], rc;
93     debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
94
95     /*
96      * Lookup tty for this process and, failing that, our parent.
97      * Even if we redirect std{in,out,err} the kernel should still know.
98      */
99     for (i = 0; tty == NULL && i < 2; i++) {
100         mib[0] = CTL_KERN;
101         mib[1] = SUDO_KERN_PROC;
102         mib[2] = KERN_PROC_PID;
103         mib[3] = i ? (int)getppid() : (int)getpid();
104         mib[4] = sizeof(*ki_proc);
105         mib[5] = 1;
106         do {
107             size += size / 10;
108             ki_proc = erealloc(ki_proc, size);
109             rc = sysctl(mib, sudo_kp_namelen, ki_proc, &size, NULL, 0);
110         } while (rc == -1 && errno == ENOMEM);
111         if (rc != -1) {
112             char *dev = devname(ki_proc->sudo_kp_tdev, S_IFCHR);
113             /* Some versions of devname() return NULL, others do not. */
114             if (dev == NULL || *dev == '?' || *dev == '#') {
115                 sudo_debug_printf(SUDO_DEBUG_WARN,
116                     "unable to map device number %u to name",
117                     ki_proc->sudo_kp_tdev);
118             } else if (*dev != '/') {
119                 /* devname() doesn't use the /dev/ prefix, add one... */
120                 size_t len = sizeof(_PATH_DEV) + strlen(dev);
121                 tty = emalloc(len);
122                 strlcpy(tty, _PATH_DEV, len);
123                 strlcat(tty, dev, len);
124             } else {
125                 /* Should not happen but just in case... */
126                 tty = estrdup(dev);
127             }
128         } else {
129             sudo_debug_printf(SUDO_DEBUG_WARN,
130                 "unable to resolve tty via KERN_PROC: %s", strerror(errno));
131         }
132     }
133     efree(ki_proc);
134
135     /* If all else fails, fall back on ttyname(). */
136     if (tty == NULL) {
137         if ((tty = ttyname(STDIN_FILENO)) != NULL ||
138             (tty = ttyname(STDOUT_FILENO)) != NULL ||
139             (tty = ttyname(STDERR_FILENO)) != NULL)
140             tty = estrdup(tty);
141     }
142
143     debug_return_str(tty);
144 }
145 #else
146 /*
147  * Return a string from ttyname() containing the tty to which the process is
148  * attached or NULL if there is no tty associated with the process (or its
149  * parent).  First tries std{in,out,err} then falls back to the parent's /proc
150  * entry.  We could try following the parent all the way to pid 1 but
151  * /proc/%d/status is system-specific (text on Linux, a struct on Solaris).
152  */
153 char *
154 get_process_ttyname(void)
155 {
156     char path[PATH_MAX], *tty = NULL;
157     pid_t ppid;
158     int i, fd;
159     debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
160
161     if ((tty = ttyname(STDIN_FILENO)) == NULL &&
162         (tty = ttyname(STDOUT_FILENO)) == NULL &&
163         (tty = ttyname(STDERR_FILENO)) == NULL) {
164         /* No tty for child, check the parent via /proc. */
165         ppid = getppid();
166         for (i = STDIN_FILENO; i < STDERR_FILENO && tty == NULL; i++) {
167             snprintf(path, sizeof(path), "/proc/%d/fd/%d", (int)ppid, i);
168             fd = open(path, O_RDONLY|O_NOCTTY, 0);
169             if (fd != -1) {
170                 tty = ttyname(fd);
171                 close(fd);
172             }
173         }
174     }
175
176     debug_return_str(estrdup(tty));
177 }
178 #endif /* sudo_kp_tdev */