1*ce74bacaSMatthew Dillon /* $OpenBSD: sshpty.c,v 1.31 2016/11/29 03:54:50 dtucker Exp $ */ 218de8d7fSPeter Avalos /* 318de8d7fSPeter Avalos * Author: Tatu Ylonen <ylo@cs.hut.fi> 418de8d7fSPeter Avalos * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 518de8d7fSPeter Avalos * All rights reserved 618de8d7fSPeter Avalos * Allocating a pseudo-terminal, and making it the controlling tty. 718de8d7fSPeter Avalos * 818de8d7fSPeter Avalos * As far as I am concerned, the code I have written for this software 918de8d7fSPeter Avalos * can be used freely for any purpose. Any derived versions of this 1018de8d7fSPeter Avalos * software must be clearly marked as such, and if the derived work is 1118de8d7fSPeter Avalos * incompatible with the protocol description in the RFC file, it must be 1218de8d7fSPeter Avalos * called by a name other than "ssh" or "Secure Shell". 1318de8d7fSPeter Avalos */ 1418de8d7fSPeter Avalos 1518de8d7fSPeter Avalos #include "includes.h" 1618de8d7fSPeter Avalos 1718de8d7fSPeter Avalos #include <sys/types.h> 1818de8d7fSPeter Avalos #include <sys/ioctl.h> 1918de8d7fSPeter Avalos #include <sys/stat.h> 2018de8d7fSPeter Avalos #include <signal.h> 2118de8d7fSPeter Avalos 2218de8d7fSPeter Avalos #include <errno.h> 2318de8d7fSPeter Avalos #include <fcntl.h> 2418de8d7fSPeter Avalos #include <grp.h> 2518de8d7fSPeter Avalos #ifdef HAVE_PATHS_H 2618de8d7fSPeter Avalos # include <paths.h> 2718de8d7fSPeter Avalos #endif 2818de8d7fSPeter Avalos #include <pwd.h> 2918de8d7fSPeter Avalos #include <stdarg.h> 3018de8d7fSPeter Avalos #include <string.h> 3118de8d7fSPeter Avalos #include <termios.h> 3218de8d7fSPeter Avalos #ifdef HAVE_UTIL_H 3318de8d7fSPeter Avalos # include <util.h> 3418de8d7fSPeter Avalos #endif 3518de8d7fSPeter Avalos #include <unistd.h> 3618de8d7fSPeter Avalos 3718de8d7fSPeter Avalos #include "sshpty.h" 3818de8d7fSPeter Avalos #include "log.h" 3918de8d7fSPeter Avalos #include "misc.h" 4018de8d7fSPeter Avalos 4118de8d7fSPeter Avalos #ifdef HAVE_PTY_H 4218de8d7fSPeter Avalos # include <pty.h> 4318de8d7fSPeter Avalos #endif 4418de8d7fSPeter Avalos 4518de8d7fSPeter Avalos #ifndef O_NOCTTY 4618de8d7fSPeter Avalos #define O_NOCTTY 0 4718de8d7fSPeter Avalos #endif 4818de8d7fSPeter Avalos 49cb5eb4f1SPeter Avalos #ifdef __APPLE__ 50cb5eb4f1SPeter Avalos # include <AvailabilityMacros.h> 51cb5eb4f1SPeter Avalos # if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) 52cb5eb4f1SPeter Avalos # define __APPLE_PRIVPTY__ 53cb5eb4f1SPeter Avalos # endif 54cb5eb4f1SPeter Avalos #endif 55cb5eb4f1SPeter Avalos 5618de8d7fSPeter Avalos /* 5718de8d7fSPeter Avalos * Allocates and opens a pty. Returns 0 if no pty could be allocated, or 5818de8d7fSPeter Avalos * nonzero if a pty was successfully allocated. On success, open file 5918de8d7fSPeter Avalos * descriptors for the pty and tty sides and the name of the tty side are 6018de8d7fSPeter Avalos * returned (the buffer must be able to hold at least 64 characters). 6118de8d7fSPeter Avalos */ 6218de8d7fSPeter Avalos 6318de8d7fSPeter Avalos int 6418de8d7fSPeter Avalos pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, size_t namebuflen) 6518de8d7fSPeter Avalos { 6618de8d7fSPeter Avalos /* openpty(3) exists in OSF/1 and some other os'es */ 6718de8d7fSPeter Avalos char *name; 6818de8d7fSPeter Avalos int i; 6918de8d7fSPeter Avalos 7018de8d7fSPeter Avalos i = openpty(ptyfd, ttyfd, NULL, NULL, NULL); 7118de8d7fSPeter Avalos if (i < 0) { 7218de8d7fSPeter Avalos error("openpty: %.100s", strerror(errno)); 7318de8d7fSPeter Avalos return 0; 7418de8d7fSPeter Avalos } 7518de8d7fSPeter Avalos name = ttyname(*ttyfd); 7618de8d7fSPeter Avalos if (!name) 7718de8d7fSPeter Avalos fatal("openpty returns device for which ttyname fails."); 7818de8d7fSPeter Avalos 7918de8d7fSPeter Avalos strlcpy(namebuf, name, namebuflen); /* possible truncation */ 8018de8d7fSPeter Avalos return 1; 8118de8d7fSPeter Avalos } 8218de8d7fSPeter Avalos 8318de8d7fSPeter Avalos /* Releases the tty. Its ownership is returned to root, and permissions to 0666. */ 8418de8d7fSPeter Avalos 8518de8d7fSPeter Avalos void 8618de8d7fSPeter Avalos pty_release(const char *tty) 8718de8d7fSPeter Avalos { 88e9778795SPeter Avalos #if !defined(__APPLE_PRIVPTY__) && !defined(HAVE_OPENPTY) 8918de8d7fSPeter Avalos if (chown(tty, (uid_t) 0, (gid_t) 0) < 0) 9018de8d7fSPeter Avalos error("chown %.100s 0 0 failed: %.100s", tty, strerror(errno)); 9118de8d7fSPeter Avalos if (chmod(tty, (mode_t) 0666) < 0) 9218de8d7fSPeter Avalos error("chmod %.100s 0666 failed: %.100s", tty, strerror(errno)); 93e9778795SPeter Avalos #endif /* !__APPLE_PRIVPTY__ && !HAVE_OPENPTY */ 9418de8d7fSPeter Avalos } 9518de8d7fSPeter Avalos 9618de8d7fSPeter Avalos /* Makes the tty the process's controlling tty and sets it to sane modes. */ 9718de8d7fSPeter Avalos 9818de8d7fSPeter Avalos void 9918de8d7fSPeter Avalos pty_make_controlling_tty(int *ttyfd, const char *tty) 10018de8d7fSPeter Avalos { 10118de8d7fSPeter Avalos int fd; 10218de8d7fSPeter Avalos 10318de8d7fSPeter Avalos #ifdef _UNICOS 10418de8d7fSPeter Avalos if (setsid() < 0) 10518de8d7fSPeter Avalos error("setsid: %.100s", strerror(errno)); 10618de8d7fSPeter Avalos 10718de8d7fSPeter Avalos fd = open(tty, O_RDWR|O_NOCTTY); 10818de8d7fSPeter Avalos if (fd != -1) { 10918de8d7fSPeter Avalos signal(SIGHUP, SIG_IGN); 11018de8d7fSPeter Avalos ioctl(fd, TCVHUP, (char *)NULL); 11118de8d7fSPeter Avalos signal(SIGHUP, SIG_DFL); 11218de8d7fSPeter Avalos setpgid(0, 0); 11318de8d7fSPeter Avalos close(fd); 11418de8d7fSPeter Avalos } else { 11518de8d7fSPeter Avalos error("Failed to disconnect from controlling tty."); 11618de8d7fSPeter Avalos } 11718de8d7fSPeter Avalos 11818de8d7fSPeter Avalos debug("Setting controlling tty using TCSETCTTY."); 11918de8d7fSPeter Avalos ioctl(*ttyfd, TCSETCTTY, NULL); 12018de8d7fSPeter Avalos fd = open("/dev/tty", O_RDWR); 12118de8d7fSPeter Avalos if (fd < 0) 12218de8d7fSPeter Avalos error("%.100s: %.100s", tty, strerror(errno)); 12318de8d7fSPeter Avalos close(*ttyfd); 12418de8d7fSPeter Avalos *ttyfd = fd; 12518de8d7fSPeter Avalos #else /* _UNICOS */ 12618de8d7fSPeter Avalos 12718de8d7fSPeter Avalos /* First disconnect from the old controlling tty. */ 12818de8d7fSPeter Avalos #ifdef TIOCNOTTY 12918de8d7fSPeter Avalos fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); 13018de8d7fSPeter Avalos if (fd >= 0) { 13118de8d7fSPeter Avalos (void) ioctl(fd, TIOCNOTTY, NULL); 13218de8d7fSPeter Avalos close(fd); 13318de8d7fSPeter Avalos } 13418de8d7fSPeter Avalos #endif /* TIOCNOTTY */ 13518de8d7fSPeter Avalos if (setsid() < 0) 13618de8d7fSPeter Avalos error("setsid: %.100s", strerror(errno)); 13718de8d7fSPeter Avalos 13818de8d7fSPeter Avalos /* 13918de8d7fSPeter Avalos * Verify that we are successfully disconnected from the controlling 14018de8d7fSPeter Avalos * tty. 14118de8d7fSPeter Avalos */ 14218de8d7fSPeter Avalos fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); 14318de8d7fSPeter Avalos if (fd >= 0) { 14418de8d7fSPeter Avalos error("Failed to disconnect from controlling tty."); 14518de8d7fSPeter Avalos close(fd); 14618de8d7fSPeter Avalos } 14718de8d7fSPeter Avalos /* Make it our controlling tty. */ 14818de8d7fSPeter Avalos #ifdef TIOCSCTTY 14918de8d7fSPeter Avalos debug("Setting controlling tty using TIOCSCTTY."); 15018de8d7fSPeter Avalos if (ioctl(*ttyfd, TIOCSCTTY, NULL) < 0) 15118de8d7fSPeter Avalos error("ioctl(TIOCSCTTY): %.100s", strerror(errno)); 15218de8d7fSPeter Avalos #endif /* TIOCSCTTY */ 15318de8d7fSPeter Avalos #ifdef NEED_SETPGRP 15418de8d7fSPeter Avalos if (setpgrp(0,0) < 0) 15518de8d7fSPeter Avalos error("SETPGRP %s",strerror(errno)); 15618de8d7fSPeter Avalos #endif /* NEED_SETPGRP */ 15718de8d7fSPeter Avalos fd = open(tty, O_RDWR); 158*ce74bacaSMatthew Dillon if (fd < 0) 15918de8d7fSPeter Avalos error("%.100s: %.100s", tty, strerror(errno)); 160*ce74bacaSMatthew Dillon else 16118de8d7fSPeter Avalos close(fd); 162*ce74bacaSMatthew Dillon 16318de8d7fSPeter Avalos /* Verify that we now have a controlling tty. */ 16418de8d7fSPeter Avalos fd = open(_PATH_TTY, O_WRONLY); 16518de8d7fSPeter Avalos if (fd < 0) 16618de8d7fSPeter Avalos error("open /dev/tty failed - could not set controlling tty: %.100s", 16718de8d7fSPeter Avalos strerror(errno)); 16818de8d7fSPeter Avalos else 16918de8d7fSPeter Avalos close(fd); 17018de8d7fSPeter Avalos #endif /* _UNICOS */ 17118de8d7fSPeter Avalos } 17218de8d7fSPeter Avalos 17318de8d7fSPeter Avalos /* Changes the window size associated with the pty. */ 17418de8d7fSPeter Avalos 17518de8d7fSPeter Avalos void 17618de8d7fSPeter Avalos pty_change_window_size(int ptyfd, u_int row, u_int col, 17718de8d7fSPeter Avalos u_int xpixel, u_int ypixel) 17818de8d7fSPeter Avalos { 17918de8d7fSPeter Avalos struct winsize w; 18018de8d7fSPeter Avalos 18118de8d7fSPeter Avalos /* may truncate u_int -> u_short */ 18218de8d7fSPeter Avalos w.ws_row = row; 18318de8d7fSPeter Avalos w.ws_col = col; 18418de8d7fSPeter Avalos w.ws_xpixel = xpixel; 18518de8d7fSPeter Avalos w.ws_ypixel = ypixel; 18618de8d7fSPeter Avalos (void) ioctl(ptyfd, TIOCSWINSZ, &w); 18718de8d7fSPeter Avalos } 18818de8d7fSPeter Avalos 18918de8d7fSPeter Avalos void 19018de8d7fSPeter Avalos pty_setowner(struct passwd *pw, const char *tty) 19118de8d7fSPeter Avalos { 19218de8d7fSPeter Avalos struct group *grp; 19318de8d7fSPeter Avalos gid_t gid; 19418de8d7fSPeter Avalos mode_t mode; 19518de8d7fSPeter Avalos struct stat st; 19618de8d7fSPeter Avalos 19718de8d7fSPeter Avalos /* Determine the group to make the owner of the tty. */ 19818de8d7fSPeter Avalos grp = getgrnam("tty"); 199e9778795SPeter Avalos gid = (grp != NULL) ? grp->gr_gid : pw->pw_gid; 200e9778795SPeter Avalos mode = (grp != NULL) ? 0620 : 0600; 20118de8d7fSPeter Avalos 20218de8d7fSPeter Avalos /* 20318de8d7fSPeter Avalos * Change owner and mode of the tty as required. 20418de8d7fSPeter Avalos * Warn but continue if filesystem is read-only and the uids match/ 20518de8d7fSPeter Avalos * tty is owned by root. 20618de8d7fSPeter Avalos */ 20718de8d7fSPeter Avalos if (stat(tty, &st)) 20818de8d7fSPeter Avalos fatal("stat(%.100s) failed: %.100s", tty, 20918de8d7fSPeter Avalos strerror(errno)); 21018de8d7fSPeter Avalos 21118de8d7fSPeter Avalos #ifdef WITH_SELINUX 21218de8d7fSPeter Avalos ssh_selinux_setup_pty(pw->pw_name, tty); 21318de8d7fSPeter Avalos #endif 21418de8d7fSPeter Avalos 21518de8d7fSPeter Avalos if (st.st_uid != pw->pw_uid || st.st_gid != gid) { 21618de8d7fSPeter Avalos if (chown(tty, pw->pw_uid, gid) < 0) { 21718de8d7fSPeter Avalos if (errno == EROFS && 21818de8d7fSPeter Avalos (st.st_uid == pw->pw_uid || st.st_uid == 0)) 21918de8d7fSPeter Avalos debug("chown(%.100s, %u, %u) failed: %.100s", 22018de8d7fSPeter Avalos tty, (u_int)pw->pw_uid, (u_int)gid, 22118de8d7fSPeter Avalos strerror(errno)); 22218de8d7fSPeter Avalos else 22318de8d7fSPeter Avalos fatal("chown(%.100s, %u, %u) failed: %.100s", 22418de8d7fSPeter Avalos tty, (u_int)pw->pw_uid, (u_int)gid, 22518de8d7fSPeter Avalos strerror(errno)); 22618de8d7fSPeter Avalos } 22718de8d7fSPeter Avalos } 22818de8d7fSPeter Avalos 22918de8d7fSPeter Avalos if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) { 23018de8d7fSPeter Avalos if (chmod(tty, mode) < 0) { 23118de8d7fSPeter Avalos if (errno == EROFS && 23218de8d7fSPeter Avalos (st.st_mode & (S_IRGRP | S_IROTH)) == 0) 23318de8d7fSPeter Avalos debug("chmod(%.100s, 0%o) failed: %.100s", 23418de8d7fSPeter Avalos tty, (u_int)mode, strerror(errno)); 23518de8d7fSPeter Avalos else 23618de8d7fSPeter Avalos fatal("chmod(%.100s, 0%o) failed: %.100s", 23718de8d7fSPeter Avalos tty, (u_int)mode, strerror(errno)); 23818de8d7fSPeter Avalos } 23918de8d7fSPeter Avalos } 24018de8d7fSPeter Avalos } 241*ce74bacaSMatthew Dillon 242*ce74bacaSMatthew Dillon /* Disconnect from the controlling tty. */ 243*ce74bacaSMatthew Dillon void 244*ce74bacaSMatthew Dillon disconnect_controlling_tty(void) 245*ce74bacaSMatthew Dillon { 246*ce74bacaSMatthew Dillon #ifdef TIOCNOTTY 247*ce74bacaSMatthew Dillon int fd; 248*ce74bacaSMatthew Dillon 249*ce74bacaSMatthew Dillon if ((fd = open(_PATH_TTY, O_RDWR | O_NOCTTY)) >= 0) { 250*ce74bacaSMatthew Dillon (void) ioctl(fd, TIOCNOTTY, NULL); 251*ce74bacaSMatthew Dillon close(fd); 252*ce74bacaSMatthew Dillon } 253*ce74bacaSMatthew Dillon #endif /* TIOCNOTTY */ 254*ce74bacaSMatthew Dillon } 255