Annotation of embedaddon/sudo/src/ttyname.c, revision 1.1.1.2

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 psinfo psinfo;
                    402:     ssize_t nread;
                    403:     int i, fd;
                    404:     debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
                    405: 
                    406:     /* Try to determine the tty from pr_ttydev in /proc/pid/psinfo. */
                    407:     for (i = 0; tty == NULL && i < 2; i++) {
                    408:        (void)snprintf(path, sizeof(path), "/proc/%u/psinfo",
                    409:            i ? (unsigned int)getppid() : (unsigned int)getpid());
                    410:        if ((fd = open(path, O_RDONLY, 0)) == -1)
                    411:            continue;
                    412:        nread = read(fd, &psinfo, sizeof(psinfo));
                    413:        close(fd);
                    414:        if (nread == (ssize_t)sizeof(psinfo) && psinfo.pr_ttydev != (dev_t)-1) {
                    415:            tty = sudo_ttyname_dev(psinfo.pr_ttydev);
                    416:        }
                    417:     }
                    418: 
                    419:     /* If all else fails, fall back on ttyname(). */
                    420:     if (tty == NULL) {
                    421:        if ((tty = ttyname(STDIN_FILENO)) != NULL ||
                    422:            (tty = ttyname(STDOUT_FILENO)) != NULL ||
                    423:            (tty = ttyname(STDERR_FILENO)) != NULL)
                    424:            tty = estrdup(tty);
                    425:     }
                    426: 
                    427:     debug_return_str(tty);
                    428: }
                    429: #elif defined(__linux__)
                    430: /*
                    431:  * Return a string from ttyname() containing the tty to which the process is
                    432:  * attached or NULL if there is no tty associated with the process (or its
                    433:  * parent).  First tries field 7 in /proc/pid/stat, then /proc/ppid/stat.
                    434:  * Falls back on ttyname of std{in,out,err} if that fails.
                    435:  */
                    436: char *
                    437: get_process_ttyname(void)
                    438: {
                    439:     char *line = NULL, *tty = NULL;
                    440:     size_t linesize = 0;
                    441:     ssize_t len;
                    442:     int i;
                    443:     debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
                    444: 
                    445:     /* Try to determine the tty from pr_ttydev in /proc/pid/psinfo. */
                    446:     for (i = 0; tty == NULL && i < 2; i++) {
                    447:        FILE *fp;
                    448:        char path[PATH_MAX];
                    449:        (void)snprintf(path, sizeof(path), "/proc/%u/stat",
                    450:            i ? (unsigned int)getppid() : (unsigned int)getpid());
                    451:        if ((fp = fopen(path, "r")) == NULL)
                    452:            continue;
                    453:        len = getline(&line, &linesize, fp);
                    454:        fclose(fp);
                    455:        if (len != -1) {
                    456:            /* Field 7 is the tty dev (0 if no tty) */
                    457:            char *cp = line;
                    458:            int field = 1;
                    459:            while (*cp != '\0') {
                    460:                if (*cp++ == ' ') {
                    461:                    if (++field == 7) {
                    462:                        dev_t tdev = (dev_t)atoi(cp);
                    463:                        if (tdev > 0)
                    464:                            tty = sudo_ttyname_dev(tdev);
                    465:                        break;
                    466:                    }
                    467:                }
                    468:            }
                    469:        }
                    470:     }
                    471:     efree(line);
                    472: 
                    473:     /* If all else fails, fall back on ttyname(). */
                    474:     if (tty == NULL) {
                    475:        if ((tty = ttyname(STDIN_FILENO)) != NULL ||
                    476:            (tty = ttyname(STDOUT_FILENO)) != NULL ||
                    477:            (tty = ttyname(STDERR_FILENO)) != NULL)
                    478:            tty = estrdup(tty);
                    479:     }
                    480: 
                    481:     debug_return_str(tty);
                    482: }
                    483: #else
                    484: /*
                    485:  * Return a string from ttyname() containing the tty to which the process is
                    486:  * attached or NULL if there is no tty associated with the process.
                    487:  * parent).
                    488:  */
                    489: char *
                    490: get_process_ttyname(void)
                    491: {
                    492:     char *tty;
                    493:     debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
                    494: 
                    495:     if ((tty = ttyname(STDIN_FILENO)) == NULL) {
                    496:        if ((tty = ttyname(STDOUT_FILENO)) == NULL)
                    497:            tty = ttyname(STDERR_FILENO);
                    498:     }
                    499: 
                    500:     debug_return_str(estrdup(tty));
                    501: }
                    502: #endif

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