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

1.1       misho       1: /*
1.1.1.3 ! misho       2:  * Copyright (c) 2012-2013 Todd C. Miller <Todd.Miller@courtesan.com>
1.1       misho       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)
1.1.1.3 ! misho      73: # include <sys/param.h>
1.1       misho      74: # include <sys/sysctl.h>
                     75: #elif defined(HAVE_STRUCT_KINFO_PROC_KI_TDEV)
1.1.1.3 ! misho      76: # include <sys/param.h>
1.1       misho      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
1.1.1.3 ! misho      85: #ifdef HAVE_PSTAT_GETPROC
        !            86: # include <sys/param.h>
        !            87: # include <sys/pstat.h>
        !            88: #endif
1.1       misho      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: }
1.1.1.3 ! misho     163: #elif defined(HAVE_STRUCT_PSINFO_PR_TTYDEV) || defined(HAVE_PSTAT_GETPROC) || defined(__linux__)
1.1       misho     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:  */
1.1.1.3 ! misho     188: static char *
        !           189: sudo_ttyname_scan(const char *dir, dev_t rdev, bool builtin)
1.1       misho     190: {
1.1.1.3 ! misho     191:     DIR *d = NULL;
1.1       misho     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:     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: 
1.1.1.3 ! misho     202:     sudo_debug_printf(SUDO_DEBUG_INFO, "scanning for dev %u in %s",
        !           203:        (unsigned int)rdev, dir);
        !           204: 
1.1       misho     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)
1.1.1.3 ! misho     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)
1.1       misho     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);
1.1.1.3 ! misho     274:            sudo_debug_printf(SUDO_DEBUG_INFO, "resolved dev %u as %s",
        !           275:                (unsigned int)rdev, pathbuf);
        !           276:            goto done;
1.1       misho     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:
1.1.1.3 ! misho     285:     if (d != NULL)
        !           286:        closedir(d);
1.1       misho     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:     /*
1.1.1.3 ! misho     307:      * First check search_devs for common tty devices.
1.1       misho     308:      */
1.1.1.3 ! misho     309:     for (sd = search_devs; tty == NULL && (devname = *sd) != NULL; sd++) {
1.1       misho     310:        len = strlen(devname);
                    311:        if (devname[len - 1] == '/') {
                    312:            if (strcmp(devname, "/dev/pts/") == 0) {
1.1.1.3 ! misho     313:                /* Special case /dev/pts */
1.1       misho     314:                (void)snprintf(buf, sizeof(buf), "%spts/%u", _PATH_DEV,
                    315:                    (unsigned int)minor(rdev));
                    316:                if (stat(buf, &sb) == 0) {
1.1.1.3 ! misho     317:                    if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev)
1.1       misho     318:                        tty = estrdup(buf);
                    319:                }
1.1.1.3 ! misho     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);
1.1       misho     325:            }
                    326:        } else {
                    327:            if (stat(devname, &sb) == 0) {
1.1.1.3 ! misho     328:                if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev)
1.1       misho     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
1.1.1.3 ! misho     347:  * attached or NULL if the process has no controlling tty.
1.1       misho     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);
1.1.1.3 ! misho     355:     int mib[6], rc;
1.1       misho     356:     debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
                    357: 
                    358:     /*
1.1.1.3 ! misho     359:      * Lookup controlling tty for this process via sysctl.
        !           360:      * This will work even if std{in,out,err} are redirected.
1.1       misho     361:      */
1.1.1.3 ! misho     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 (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);
1.1       misho     380:            }
                    381:        }
1.1.1.3 ! misho     382:     } else {
        !           383:        sudo_debug_printf(SUDO_DEBUG_WARN,
        !           384:            "unable to resolve tty via KERN_PROC: %s", strerror(errno));
1.1       misho     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
1.1.1.3 ! misho     393:  * attached or NULL if the process has no controlling tty.
1.1       misho     394:  */
                    395: char *
                    396: get_process_ttyname(void)
                    397: {
                    398:     char path[PATH_MAX], *tty = NULL;
                    399:     struct psinfo psinfo;
                    400:     ssize_t nread;
1.1.1.3 ! misho     401:     int fd;
1.1       misho     402:     debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
                    403: 
                    404:     /* Try to determine the tty from pr_ttydev in /proc/pid/psinfo. */
1.1.1.3 ! misho     405:     snprintf(path, sizeof(path), "/proc/%u/psinfo", (unsigned int)getpid());
        !           406:     if ((fd = open(path, O_RDONLY, 0)) != -1) {
1.1       misho     407:        nread = read(fd, &psinfo, sizeof(psinfo));
                    408:        close(fd);
1.1.1.3 ! misho     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);
1.1       misho     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
1.1.1.3 ! misho     425:  * attached or NULL if the process has no controlling tty.
1.1       misho     426:  */
                    427: char *
                    428: get_process_ttyname(void)
                    429: {
1.1.1.3 ! misho     430:     char path[PATH_MAX], *line = NULL, *tty = NULL;
1.1       misho     431:     size_t linesize = 0;
                    432:     ssize_t len;
1.1.1.3 ! misho     433:     FILE *fp;
1.1       misho     434:     debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
                    435: 
1.1.1.3 ! misho     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) {
1.1       misho     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:            int field = 1;
                    445:            while (*cp != '\0') {
                    446:                if (*cp++ == ' ') {
                    447:                    if (++field == 7) {
                    448:                        dev_t tdev = (dev_t)atoi(cp);
                    449:                        if (tdev > 0)
                    450:                            tty = sudo_ttyname_dev(tdev);
                    451:                        break;
                    452:                    }
                    453:                }
                    454:            }
                    455:        }
1.1.1.3 ! misho     456:        efree(line);
1.1       misho     457:     }
                    458: 
1.1.1.3 ! misho     459:     debug_return_str(tty);
        !           460: }
        !           461: #elif HAVE_PSTAT_GETPROC
        !           462: /*
        !           463:  * Return a string from ttyname() containing the tty to which the process is
        !           464:  * attached or NULL if the process has no controlling tty.
        !           465:  */
        !           466: char *
        !           467: get_process_ttyname(void)
        !           468: {
        !           469:     struct pst_status pstat;
        !           470:     char *tty = NULL;
        !           471:     debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
1.1       misho     472: 
1.1.1.3 ! misho     473:     /* Try to determine the tty from psdev in struct pst_status. */
        !           474:     if (pstat_getproc(&pstat, sizeof(pstat), 0, (int)getpid()) != -1) {
        !           475:        if (pstat.pst_term.psd_major != -1 && pstat.pst_term.psd_minor != -1) {
        !           476:            tty = sudo_ttyname_dev(makedev(pstat.pst_term.psd_major,
        !           477:                pstat.pst_term.psd_minor));
        !           478:        }
        !           479:     }
1.1       misho     480:     debug_return_str(tty);
                    481: }
                    482: #else
                    483: /*
                    484:  * Return a string from ttyname() containing the tty to which the process is
1.1.1.3 ! misho     485:  * attached or NULL if the process has no controlling tty.
1.1       misho     486:  */
                    487: char *
                    488: get_process_ttyname(void)
                    489: {
                    490:     char *tty;
                    491:     debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
                    492: 
                    493:     if ((tty = ttyname(STDIN_FILENO)) == NULL) {
                    494:        if ((tty = ttyname(STDOUT_FILENO)) == NULL)
                    495:            tty = ttyname(STDERR_FILENO);
                    496:     }
                    497: 
                    498:     debug_return_str(estrdup(tty));
                    499: }
                    500: #endif

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