11e8db6e2SBrian Feldman /* 21e8db6e2SBrian Feldman * Author: Tatu Ylonen <ylo@cs.hut.fi> 31e8db6e2SBrian Feldman * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 41e8db6e2SBrian Feldman * All rights reserved 51e8db6e2SBrian Feldman * Allocating a pseudo-terminal, and making it the controlling tty. 61e8db6e2SBrian Feldman * 71e8db6e2SBrian Feldman * As far as I am concerned, the code I have written for this software 81e8db6e2SBrian Feldman * can be used freely for any purpose. Any derived versions of this 91e8db6e2SBrian Feldman * software must be clearly marked as such, and if the derived work is 101e8db6e2SBrian Feldman * incompatible with the protocol description in the RFC file, it must be 111e8db6e2SBrian Feldman * called by a name other than "ssh" or "Secure Shell". 121e8db6e2SBrian Feldman */ 131e8db6e2SBrian Feldman 141e8db6e2SBrian Feldman #include "includes.h" 15af12a3e7SDag-Erling Smørgrav RCSID("$OpenBSD: sshpty.c,v 1.4 2001/12/19 07:18:56 deraadt Exp $"); 16ca3176e7SBrian Feldman RCSID("$FreeBSD$"); 171e8db6e2SBrian Feldman 18ca3176e7SBrian Feldman #include <libutil.h> 191e8db6e2SBrian Feldman #include "sshpty.h" 201e8db6e2SBrian Feldman #include "log.h" 211e8db6e2SBrian Feldman 221e8db6e2SBrian Feldman /* Pty allocated with _getpty gets broken if we do I_PUSH:es to it. */ 231e8db6e2SBrian Feldman #if defined(HAVE__GETPTY) || defined(HAVE_OPENPTY) 241e8db6e2SBrian Feldman #undef HAVE_DEV_PTMX 251e8db6e2SBrian Feldman #endif 261e8db6e2SBrian Feldman 271e8db6e2SBrian Feldman #ifndef O_NOCTTY 281e8db6e2SBrian Feldman #define O_NOCTTY 0 291e8db6e2SBrian Feldman #endif 301e8db6e2SBrian Feldman 311e8db6e2SBrian Feldman /* 321e8db6e2SBrian Feldman * Allocates and opens a pty. Returns 0 if no pty could be allocated, or 331e8db6e2SBrian Feldman * nonzero if a pty was successfully allocated. On success, open file 341e8db6e2SBrian Feldman * descriptors for the pty and tty sides and the name of the tty side are 351e8db6e2SBrian Feldman * returned (the buffer must be able to hold at least 64 characters). 361e8db6e2SBrian Feldman */ 371e8db6e2SBrian Feldman 381e8db6e2SBrian Feldman int 391e8db6e2SBrian Feldman pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, int namebuflen) 401e8db6e2SBrian Feldman { 411e8db6e2SBrian Feldman #if defined(HAVE_OPENPTY) || defined(BSD4_4) 421e8db6e2SBrian Feldman /* openpty(3) exists in OSF/1 and some other os'es */ 431e8db6e2SBrian Feldman char buf[64]; 441e8db6e2SBrian Feldman int i; 451e8db6e2SBrian Feldman 461e8db6e2SBrian Feldman i = openpty(ptyfd, ttyfd, buf, NULL, NULL); 471e8db6e2SBrian Feldman if (i < 0) { 481e8db6e2SBrian Feldman error("openpty: %.100s", strerror(errno)); 491e8db6e2SBrian Feldman return 0; 501e8db6e2SBrian Feldman } 511e8db6e2SBrian Feldman strlcpy(namebuf, buf, namebuflen); /* possible truncation */ 521e8db6e2SBrian Feldman return 1; 531e8db6e2SBrian Feldman #else /* HAVE_OPENPTY */ 541e8db6e2SBrian Feldman #ifdef HAVE__GETPTY 551e8db6e2SBrian Feldman /* 561e8db6e2SBrian Feldman * _getpty(3) exists in SGI Irix 4.x, 5.x & 6.x -- it generates more 571e8db6e2SBrian Feldman * pty's automagically when needed 581e8db6e2SBrian Feldman */ 591e8db6e2SBrian Feldman char *slave; 601e8db6e2SBrian Feldman 611e8db6e2SBrian Feldman slave = _getpty(ptyfd, O_RDWR, 0622, 0); 621e8db6e2SBrian Feldman if (slave == NULL) { 631e8db6e2SBrian Feldman error("_getpty: %.100s", strerror(errno)); 641e8db6e2SBrian Feldman return 0; 651e8db6e2SBrian Feldman } 661e8db6e2SBrian Feldman strlcpy(namebuf, slave, namebuflen); 671e8db6e2SBrian Feldman /* Open the slave side. */ 681e8db6e2SBrian Feldman *ttyfd = open(namebuf, O_RDWR | O_NOCTTY); 691e8db6e2SBrian Feldman if (*ttyfd < 0) { 701e8db6e2SBrian Feldman error("%.200s: %.100s", namebuf, strerror(errno)); 711e8db6e2SBrian Feldman close(*ptyfd); 721e8db6e2SBrian Feldman return 0; 731e8db6e2SBrian Feldman } 741e8db6e2SBrian Feldman return 1; 751e8db6e2SBrian Feldman #else /* HAVE__GETPTY */ 761e8db6e2SBrian Feldman #ifdef HAVE_DEV_PTMX 771e8db6e2SBrian Feldman /* 781e8db6e2SBrian Feldman * This code is used e.g. on Solaris 2.x. (Note that Solaris 2.3 791e8db6e2SBrian Feldman * also has bsd-style ptys, but they simply do not work.) 801e8db6e2SBrian Feldman */ 811e8db6e2SBrian Feldman int ptm; 821e8db6e2SBrian Feldman char *pts; 831e8db6e2SBrian Feldman 841e8db6e2SBrian Feldman ptm = open("/dev/ptmx", O_RDWR | O_NOCTTY); 851e8db6e2SBrian Feldman if (ptm < 0) { 861e8db6e2SBrian Feldman error("/dev/ptmx: %.100s", strerror(errno)); 871e8db6e2SBrian Feldman return 0; 881e8db6e2SBrian Feldman } 891e8db6e2SBrian Feldman if (grantpt(ptm) < 0) { 901e8db6e2SBrian Feldman error("grantpt: %.100s", strerror(errno)); 911e8db6e2SBrian Feldman return 0; 921e8db6e2SBrian Feldman } 931e8db6e2SBrian Feldman if (unlockpt(ptm) < 0) { 941e8db6e2SBrian Feldman error("unlockpt: %.100s", strerror(errno)); 951e8db6e2SBrian Feldman return 0; 961e8db6e2SBrian Feldman } 971e8db6e2SBrian Feldman pts = ptsname(ptm); 981e8db6e2SBrian Feldman if (pts == NULL) 991e8db6e2SBrian Feldman error("Slave pty side name could not be obtained."); 1001e8db6e2SBrian Feldman strlcpy(namebuf, pts, namebuflen); 1011e8db6e2SBrian Feldman *ptyfd = ptm; 1021e8db6e2SBrian Feldman 1031e8db6e2SBrian Feldman /* Open the slave side. */ 1041e8db6e2SBrian Feldman *ttyfd = open(namebuf, O_RDWR | O_NOCTTY); 1051e8db6e2SBrian Feldman if (*ttyfd < 0) { 1061e8db6e2SBrian Feldman error("%.100s: %.100s", namebuf, strerror(errno)); 1071e8db6e2SBrian Feldman close(*ptyfd); 1081e8db6e2SBrian Feldman return 0; 1091e8db6e2SBrian Feldman } 1101e8db6e2SBrian Feldman /* Push the appropriate streams modules, as described in Solaris pts(7). */ 1111e8db6e2SBrian Feldman if (ioctl(*ttyfd, I_PUSH, "ptem") < 0) 1121e8db6e2SBrian Feldman error("ioctl I_PUSH ptem: %.100s", strerror(errno)); 1131e8db6e2SBrian Feldman if (ioctl(*ttyfd, I_PUSH, "ldterm") < 0) 1141e8db6e2SBrian Feldman error("ioctl I_PUSH ldterm: %.100s", strerror(errno)); 1151e8db6e2SBrian Feldman if (ioctl(*ttyfd, I_PUSH, "ttcompat") < 0) 1161e8db6e2SBrian Feldman error("ioctl I_PUSH ttcompat: %.100s", strerror(errno)); 1171e8db6e2SBrian Feldman return 1; 1181e8db6e2SBrian Feldman #else /* HAVE_DEV_PTMX */ 1191e8db6e2SBrian Feldman #ifdef HAVE_DEV_PTS_AND_PTC 1201e8db6e2SBrian Feldman /* AIX-style pty code. */ 1211e8db6e2SBrian Feldman const char *name; 1221e8db6e2SBrian Feldman 1231e8db6e2SBrian Feldman *ptyfd = open("/dev/ptc", O_RDWR | O_NOCTTY); 1241e8db6e2SBrian Feldman if (*ptyfd < 0) { 1251e8db6e2SBrian Feldman error("Could not open /dev/ptc: %.100s", strerror(errno)); 1261e8db6e2SBrian Feldman return 0; 1271e8db6e2SBrian Feldman } 1281e8db6e2SBrian Feldman name = ttyname(*ptyfd); 1291e8db6e2SBrian Feldman if (!name) 1301e8db6e2SBrian Feldman fatal("Open of /dev/ptc returns device for which ttyname fails."); 1311e8db6e2SBrian Feldman strlcpy(namebuf, name, namebuflen); 1321e8db6e2SBrian Feldman *ttyfd = open(name, O_RDWR | O_NOCTTY); 1331e8db6e2SBrian Feldman if (*ttyfd < 0) { 1341e8db6e2SBrian Feldman error("Could not open pty slave side %.100s: %.100s", 1351e8db6e2SBrian Feldman name, strerror(errno)); 1361e8db6e2SBrian Feldman close(*ptyfd); 1371e8db6e2SBrian Feldman return 0; 1381e8db6e2SBrian Feldman } 1391e8db6e2SBrian Feldman return 1; 1401e8db6e2SBrian Feldman #else /* HAVE_DEV_PTS_AND_PTC */ 1411e8db6e2SBrian Feldman /* BSD-style pty code. */ 1421e8db6e2SBrian Feldman char buf[64]; 1431e8db6e2SBrian Feldman int i; 1441e8db6e2SBrian Feldman const char *ptymajors = "pqrstuvwxyzabcdefghijklmnoABCDEFGHIJKLMNOPQRSTUVWXYZ"; 1451e8db6e2SBrian Feldman const char *ptyminors = "0123456789abcdef"; 1461e8db6e2SBrian Feldman int num_minors = strlen(ptyminors); 1471e8db6e2SBrian Feldman int num_ptys = strlen(ptymajors) * num_minors; 1481e8db6e2SBrian Feldman 1491e8db6e2SBrian Feldman for (i = 0; i < num_ptys; i++) { 1501e8db6e2SBrian Feldman snprintf(buf, sizeof buf, "/dev/pty%c%c", ptymajors[i / num_minors], 1511e8db6e2SBrian Feldman ptyminors[i % num_minors]); 1521e8db6e2SBrian Feldman *ptyfd = open(buf, O_RDWR | O_NOCTTY); 1531e8db6e2SBrian Feldman if (*ptyfd < 0) 1541e8db6e2SBrian Feldman continue; 1551e8db6e2SBrian Feldman snprintf(namebuf, namebuflen, "/dev/tty%c%c", 1561e8db6e2SBrian Feldman ptymajors[i / num_minors], ptyminors[i % num_minors]); 1571e8db6e2SBrian Feldman 1581e8db6e2SBrian Feldman /* Open the slave side. */ 1591e8db6e2SBrian Feldman *ttyfd = open(namebuf, O_RDWR | O_NOCTTY); 1601e8db6e2SBrian Feldman if (*ttyfd < 0) { 1611e8db6e2SBrian Feldman error("%.100s: %.100s", namebuf, strerror(errno)); 1621e8db6e2SBrian Feldman close(*ptyfd); 1631e8db6e2SBrian Feldman return 0; 1641e8db6e2SBrian Feldman } 1651e8db6e2SBrian Feldman return 1; 1661e8db6e2SBrian Feldman } 1671e8db6e2SBrian Feldman return 0; 1681e8db6e2SBrian Feldman #endif /* HAVE_DEV_PTS_AND_PTC */ 1691e8db6e2SBrian Feldman #endif /* HAVE_DEV_PTMX */ 1701e8db6e2SBrian Feldman #endif /* HAVE__GETPTY */ 1711e8db6e2SBrian Feldman #endif /* HAVE_OPENPTY */ 1721e8db6e2SBrian Feldman } 1731e8db6e2SBrian Feldman 1741e8db6e2SBrian Feldman /* Releases the tty. Its ownership is returned to root, and permissions to 0666. */ 1751e8db6e2SBrian Feldman 1761e8db6e2SBrian Feldman void 1771e8db6e2SBrian Feldman pty_release(const char *ttyname) 1781e8db6e2SBrian Feldman { 1791e8db6e2SBrian Feldman if (chown(ttyname, (uid_t) 0, (gid_t) 0) < 0) 1801e8db6e2SBrian Feldman error("chown %.100s 0 0 failed: %.100s", ttyname, strerror(errno)); 1811e8db6e2SBrian Feldman if (chmod(ttyname, (mode_t) 0666) < 0) 1821e8db6e2SBrian Feldman error("chmod %.100s 0666 failed: %.100s", ttyname, strerror(errno)); 1831e8db6e2SBrian Feldman } 1841e8db6e2SBrian Feldman 1851e8db6e2SBrian Feldman /* Makes the tty the processes controlling tty and sets it to sane modes. */ 1861e8db6e2SBrian Feldman 1871e8db6e2SBrian Feldman void 1881e8db6e2SBrian Feldman pty_make_controlling_tty(int *ttyfd, const char *ttyname) 1891e8db6e2SBrian Feldman { 1901e8db6e2SBrian Feldman int fd; 1911e8db6e2SBrian Feldman 1921e8db6e2SBrian Feldman /* First disconnect from the old controlling tty. */ 1931e8db6e2SBrian Feldman #ifdef TIOCNOTTY 1941e8db6e2SBrian Feldman fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); 1951e8db6e2SBrian Feldman if (fd >= 0) { 1961e8db6e2SBrian Feldman (void) ioctl(fd, TIOCNOTTY, NULL); 1971e8db6e2SBrian Feldman close(fd); 1981e8db6e2SBrian Feldman } 1991e8db6e2SBrian Feldman #endif /* TIOCNOTTY */ 2001e8db6e2SBrian Feldman if (setsid() < 0) 2011e8db6e2SBrian Feldman error("setsid: %.100s", strerror(errno)); 2021e8db6e2SBrian Feldman 2031e8db6e2SBrian Feldman /* 2041e8db6e2SBrian Feldman * Verify that we are successfully disconnected from the controlling 2051e8db6e2SBrian Feldman * tty. 2061e8db6e2SBrian Feldman */ 2071e8db6e2SBrian Feldman fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); 2081e8db6e2SBrian Feldman if (fd >= 0) { 2091e8db6e2SBrian Feldman error("Failed to disconnect from controlling tty."); 2101e8db6e2SBrian Feldman close(fd); 2111e8db6e2SBrian Feldman } 2121e8db6e2SBrian Feldman /* Make it our controlling tty. */ 2131e8db6e2SBrian Feldman #ifdef TIOCSCTTY 2141e8db6e2SBrian Feldman debug("Setting controlling tty using TIOCSCTTY."); 2151e8db6e2SBrian Feldman if (ioctl(*ttyfd, TIOCSCTTY, NULL) < 0) 2161e8db6e2SBrian Feldman error("ioctl(TIOCSCTTY): %.100s", strerror(errno)); 2171e8db6e2SBrian Feldman #endif /* TIOCSCTTY */ 2181e8db6e2SBrian Feldman fd = open(ttyname, O_RDWR); 2191e8db6e2SBrian Feldman if (fd < 0) 2201e8db6e2SBrian Feldman error("%.100s: %.100s", ttyname, strerror(errno)); 2211e8db6e2SBrian Feldman else 2221e8db6e2SBrian Feldman close(fd); 2231e8db6e2SBrian Feldman 2241e8db6e2SBrian Feldman /* Verify that we now have a controlling tty. */ 2251e8db6e2SBrian Feldman fd = open(_PATH_TTY, O_WRONLY); 2261e8db6e2SBrian Feldman if (fd < 0) 2271e8db6e2SBrian Feldman error("open /dev/tty failed - could not set controlling tty: %.100s", 2281e8db6e2SBrian Feldman strerror(errno)); 2291e8db6e2SBrian Feldman else { 2301e8db6e2SBrian Feldman close(fd); 2311e8db6e2SBrian Feldman } 2321e8db6e2SBrian Feldman } 2331e8db6e2SBrian Feldman 2341e8db6e2SBrian Feldman /* Changes the window size associated with the pty. */ 2351e8db6e2SBrian Feldman 2361e8db6e2SBrian Feldman void 2371e8db6e2SBrian Feldman pty_change_window_size(int ptyfd, int row, int col, 2381e8db6e2SBrian Feldman int xpixel, int ypixel) 2391e8db6e2SBrian Feldman { 2401e8db6e2SBrian Feldman struct winsize w; 2411e8db6e2SBrian Feldman w.ws_row = row; 2421e8db6e2SBrian Feldman w.ws_col = col; 2431e8db6e2SBrian Feldman w.ws_xpixel = xpixel; 2441e8db6e2SBrian Feldman w.ws_ypixel = ypixel; 2451e8db6e2SBrian Feldman (void) ioctl(ptyfd, TIOCSWINSZ, &w); 2461e8db6e2SBrian Feldman } 2471e8db6e2SBrian Feldman 2481e8db6e2SBrian Feldman void 2491e8db6e2SBrian Feldman pty_setowner(struct passwd *pw, const char *ttyname) 2501e8db6e2SBrian Feldman { 2511e8db6e2SBrian Feldman struct group *grp; 2521e8db6e2SBrian Feldman gid_t gid; 2531e8db6e2SBrian Feldman mode_t mode; 2541e8db6e2SBrian Feldman struct stat st; 2551e8db6e2SBrian Feldman 2561e8db6e2SBrian Feldman /* Determine the group to make the owner of the tty. */ 2571e8db6e2SBrian Feldman grp = getgrnam("tty"); 2581e8db6e2SBrian Feldman if (grp) { 2591e8db6e2SBrian Feldman gid = grp->gr_gid; 2601e8db6e2SBrian Feldman mode = S_IRUSR | S_IWUSR | S_IWGRP; 2611e8db6e2SBrian Feldman } else { 2621e8db6e2SBrian Feldman gid = pw->pw_gid; 2631e8db6e2SBrian Feldman mode = S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH; 2641e8db6e2SBrian Feldman } 2651e8db6e2SBrian Feldman 2661e8db6e2SBrian Feldman /* 2671e8db6e2SBrian Feldman * Change owner and mode of the tty as required. 268af12a3e7SDag-Erling Smørgrav * Warn but continue if filesystem is read-only and the uids match/ 269af12a3e7SDag-Erling Smørgrav * tty is owned by root. 2701e8db6e2SBrian Feldman */ 2711e8db6e2SBrian Feldman if (stat(ttyname, &st)) 2721e8db6e2SBrian Feldman fatal("stat(%.100s) failed: %.100s", ttyname, 2731e8db6e2SBrian Feldman strerror(errno)); 2741e8db6e2SBrian Feldman 2751e8db6e2SBrian Feldman if (st.st_uid != pw->pw_uid || st.st_gid != gid) { 2761e8db6e2SBrian Feldman if (chown(ttyname, pw->pw_uid, gid) < 0) { 277af12a3e7SDag-Erling Smørgrav if (errno == EROFS && 278af12a3e7SDag-Erling Smørgrav (st.st_uid == pw->pw_uid || st.st_uid == 0)) 2791e8db6e2SBrian Feldman error("chown(%.100s, %d, %d) failed: %.100s", 2801e8db6e2SBrian Feldman ttyname, pw->pw_uid, gid, 2811e8db6e2SBrian Feldman strerror(errno)); 2821e8db6e2SBrian Feldman else 2831e8db6e2SBrian Feldman fatal("chown(%.100s, %d, %d) failed: %.100s", 2841e8db6e2SBrian Feldman ttyname, pw->pw_uid, gid, 2851e8db6e2SBrian Feldman strerror(errno)); 2861e8db6e2SBrian Feldman } 2871e8db6e2SBrian Feldman } 2881e8db6e2SBrian Feldman 2891e8db6e2SBrian Feldman if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) { 2901e8db6e2SBrian Feldman if (chmod(ttyname, mode) < 0) { 2911e8db6e2SBrian Feldman if (errno == EROFS && 2921e8db6e2SBrian Feldman (st.st_mode & (S_IRGRP | S_IROTH)) == 0) 2931e8db6e2SBrian Feldman error("chmod(%.100s, 0%o) failed: %.100s", 2941e8db6e2SBrian Feldman ttyname, mode, strerror(errno)); 2951e8db6e2SBrian Feldman else 2961e8db6e2SBrian Feldman fatal("chmod(%.100s, 0%o) failed: %.100s", 2971e8db6e2SBrian Feldman ttyname, mode, strerror(errno)); 2981e8db6e2SBrian Feldman } 2991e8db6e2SBrian Feldman } 3001e8db6e2SBrian Feldman } 301