File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / sudo / src / ttyname.c
Revision 1.1.1.4 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 16:12:55 2014 UTC (10 years ago) by misho
Branches: sudo, MAIN
CVS tags: v1_8_10p3_0, v1_8_10p3, HEAD
sudo v 1.8.10p3

    1: /*
    2:  * Copyright (c) 2012-2014 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:     unsigned 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 ((dev_t)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: 	    char *ep = line;
  445: 	    const char *errstr;
  446: 	    int field = 0;
  447: 	    while (*++ep != '\0') {
  448: 		if (*ep == ' ') {
  449: 		    *ep = '\0';
  450: 		    if (++field == 7) {
  451: 			dev_t tdev = strtonum(cp, INT_MIN, INT_MAX, &errstr);
  452: 			if (errstr) {
  453: 			    sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
  454: 				"%s: tty device %s: %s", path, cp, errstr);
  455: 			}
  456: 			if (tdev > 0)
  457: 			    tty = sudo_ttyname_dev(tdev);
  458: 			break;
  459: 		    }
  460: 		    cp = ep + 1;
  461: 		}
  462: 	    }
  463: 	}
  464: 	efree(line);
  465:     }
  466: 
  467:     debug_return_str(tty);
  468: }
  469: #elif defined(HAVE_PSTAT_GETPROC)
  470: /*
  471:  * Return a string from ttyname() containing the tty to which the process is
  472:  * attached or NULL if the process has no controlling tty.
  473:  */
  474: char *
  475: get_process_ttyname(void)
  476: {
  477:     struct pst_status pstat;
  478:     char *tty = NULL;
  479:     int rc;
  480:     debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
  481: 
  482:     /*
  483:      * Determine the tty from psdev in struct pst_status.
  484:      * We may get EOVERFLOW if the whole thing doesn't fit but that is OK.
  485:      */
  486:     rc = pstat_getproc(&pstat, sizeof(pstat), (size_t)0, (int)getpid());
  487:     if (rc != -1 || errno == EOVERFLOW) {
  488: 	if (pstat.pst_term.psd_major != -1 && pstat.pst_term.psd_minor != -1) {
  489: 	    tty = sudo_ttyname_dev(makedev(pstat.pst_term.psd_major,
  490: 		pstat.pst_term.psd_minor));
  491: 	}
  492:     }
  493:     debug_return_str(tty);
  494: }
  495: #else
  496: /*
  497:  * Return a string from ttyname() containing the tty to which the process is
  498:  * attached or NULL if the process has no controlling tty.
  499:  */
  500: char *
  501: get_process_ttyname(void)
  502: {
  503:     char *tty;
  504:     debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
  505: 
  506:     if ((tty = ttyname(STDIN_FILENO)) == NULL) {
  507: 	if ((tty = ttyname(STDOUT_FILENO)) == NULL)
  508: 	    tty = ttyname(STDERR_FILENO);
  509:     }
  510: 
  511:     debug_return_str(estrdup(tty));
  512: }
  513: #endif

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>