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