File indexing completed on 2024-04-14 03:54:24

0001 /* kgrantpty - helper program for KPty. */
0002 
0003 /* This program is based on the glibc2.1 pt_chmod.
0004  * It was pulled out from there since both Linux
0005  * distributors and other OSes are not able to make
0006  * use of the glibc for different reasons.
0007  *
0008  * THIS IS A ROOT SUID PROGRAM
0009  *
0010  * Things work as following:
0011  *
0012  * In konsole we open a master pty. This can be opened
0013  * done by at most one process. Prior to opening the
0014  * master pty, the slave pty cannot be opened. Then, in
0015  * grantpty, we fork to this program. The trick is, that
0016  * the parameter is passes as a file handle, which cannot
0017  * be faked, so that we get a secure setuid root chmod/chown
0018  * with this program.
0019  *
0020  * We have to chown/chmod the slave pty to prevent eavesdroping.
0021  *
0022  * SPDX-FileCopyrightText: 1998 Zack Weinberg <zack@rabi.phys.columbia.edu>
0023  * SPDX-FileCopyrightText: 1999 Lars Doelle <lars.doelle@on-line.de>
0024  *
0025  * SPDX-License-Identifier: GPL-2.0-or-later
0026  */
0027 
0028 #include <config-pty.h>
0029 
0030 #include <cerrno>
0031 #include <grp.h>
0032 #include <stdio.h>
0033 #include <stdlib.h>
0034 #include <string.h>
0035 #include <sys/ioctl.h>
0036 #include <sys/stat.h>
0037 #include <sys/types.h>
0038 #include <unistd.h>
0039 #if HAVE_PTY_H
0040 #include <pty.h>
0041 #endif
0042 
0043 #include <sys/param.h>
0044 #if defined(__FreeBSD__)
0045 #define BSD_PTY_HACK
0046 #include <dirent.h>
0047 #include <paths.h>
0048 #endif
0049 
0050 #define TTY_GROUP "tty"
0051 
0052 int main(int argc, char *argv[])
0053 {
0054     struct stat st;
0055     struct group *p;
0056     gid_t gid;
0057     uid_t uid;
0058     mode_t mod;
0059     char *tty;
0060     int fd;
0061 #if !HAVE_PTSNAME && defined(TIOCGPTN)
0062     int ptyno;
0063     char ttyb[32];
0064 #endif
0065 
0066     /* check preconditions **************************************************/
0067     if (argc != 3 || (strcmp(argv[1], "--grant") && strcmp(argv[1], "--revoke"))) {
0068         printf(
0069             "usage: %s (--grant|--revoke) <file descriptor>\n"
0070             "%s is a helper for the KDE core libraries.\n"
0071             "It is not intended to be called from the command line.\n"
0072             "It needs to be installed setuid root to function.\n",
0073             argv[0],
0074             argv[0]);
0075         return 1; /* FAIL */
0076     }
0077 
0078     if (geteuid() != 0) {
0079         fprintf(stderr, "%s not installed setuid root\n", argv[0]);
0080         return 1; /* FAIL */
0081     }
0082 
0083     fd = atoi(argv[2]);
0084 
0085     /* get slave pty name from master pty file handle *********/
0086 #if HAVE_PTSNAME
0087     tty = ptsname(fd);
0088     if (!tty)
0089 #elif defined(TIOCGPTN)
0090     if (!ioctl(fd, TIOCGPTN, &ptyno)) {
0091         sprintf(ttyb, "/dev/pts/%d", ptyno);
0092         tty = ttyb;
0093     } else
0094 #endif
0095     {
0096         /* Check that fd is a valid master pseudo terminal.  */
0097         char *pty = ttyname(fd);
0098 
0099 #ifdef BSD_PTY_HACK
0100         if (pty == NULL) {
0101             /*
0102               Hack to make kgrantpty work on some versions of FreeBSD (and possibly
0103               other systems): ttyname(3) does not work with a file descriptor opened
0104               on a /dev/pty?? device.
0105 
0106               Instead, this code looks through all the devices in /dev for a device
0107               which has the same inode as our PTY_FILENO descriptor... if found, we
0108               have the name for our pty.
0109             */
0110 
0111             struct dirent *dirp;
0112             DIR *dp;
0113             struct stat dsb;
0114 
0115             if (fstat(fd, &dsb) != -1) {
0116                 if ((dp = opendir(_PATH_DEV)) != NULL) {
0117                     while ((dirp = readdir(dp))) {
0118                         if (dirp->d_fileno != dsb.st_ino) {
0119                             continue;
0120                         }
0121                         pty = malloc(sizeof(_PATH_DEV) + strlen(dirp->d_name));
0122                         if (pty) {
0123                             strcpy(pty, _PATH_DEV);
0124                             strcat(pty, dirp->d_name);
0125                         }
0126                         break;
0127                     }
0128 
0129                     (void)closedir(dp);
0130                 }
0131             }
0132         }
0133 #endif
0134 
0135         if (pty == NULL) {
0136             fprintf(stderr, "%s: cannot determine pty name.\n", argv[0]);
0137             return 1; /* FAIL */
0138         }
0139 
0140         /* matches /dev/pty?? */
0141         if (memcmp(pty, "/dev/pty", 8)) {
0142             fprintf(stderr, "%s: determined a strange pty name '%s'.\n", argv[0], pty);
0143             return 1; /* FAIL */
0144         }
0145 
0146         tty = malloc(strlen(pty) + 1);
0147         strcpy(tty, "/dev/tty");
0148         strcat(tty, pty + 8);
0149     }
0150 
0151     /* Check that the returned slave pseudo terminal is a character device.  */
0152     if (stat(tty, &st) < 0 || !S_ISCHR(st.st_mode)) {
0153         fprintf(stderr, "%s: found '%s' not to be a character device.\n", argv[0], tty);
0154         return 1; /* FAIL */
0155     }
0156 
0157     /* setup parameters for the operation ***********************************/
0158 
0159     if (!strcmp(argv[1], "--grant")) {
0160         uid = getuid();
0161         p = getgrnam(TTY_GROUP);
0162         if (!p) {
0163             p = getgrnam("wheel");
0164         }
0165         gid = p ? p->gr_gid : getgid();
0166         mod = S_IRUSR | S_IWUSR | S_IWGRP;
0167     } else {
0168         uid = 0;
0169         gid = st.st_gid == getgid() ? 0 : -1;
0170         mod = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
0171     }
0172 
0173     /* Perform the actual chown/chmod ************************************/
0174 
0175     if (chown(tty, uid, gid) < 0) {
0176         fprintf(stderr, "%s: cannot chown %s: %s\n", argv[0], tty, strerror(errno));
0177         return 1; /* FAIL */
0178     }
0179 
0180     if (chmod(tty, mod) < 0) {
0181         fprintf(stderr, "%s: cannot chmod %s: %s\n", argv[0], tty, strerror(errno));
0182         return 1; /* FAIL */
0183     }
0184 
0185     return 0; /* OK */
0186 }