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>