1*19261079SEd Maste /* $OpenBSD: sshpty.c,v 1.34 2019/07/04 16:20:10 deraadt Exp $ */
21e8db6e2SBrian Feldman /*
31e8db6e2SBrian Feldman * Author: Tatu Ylonen <ylo@cs.hut.fi>
41e8db6e2SBrian Feldman * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
51e8db6e2SBrian Feldman * All rights reserved
61e8db6e2SBrian Feldman * Allocating a pseudo-terminal, and making it the controlling tty.
71e8db6e2SBrian Feldman *
81e8db6e2SBrian Feldman * As far as I am concerned, the code I have written for this software
91e8db6e2SBrian Feldman * can be used freely for any purpose. Any derived versions of this
101e8db6e2SBrian Feldman * software must be clearly marked as such, and if the derived work is
111e8db6e2SBrian Feldman * incompatible with the protocol description in the RFC file, it must be
121e8db6e2SBrian Feldman * called by a name other than "ssh" or "Secure Shell".
131e8db6e2SBrian Feldman */
141e8db6e2SBrian Feldman
151e8db6e2SBrian Feldman #include "includes.h"
161e8db6e2SBrian Feldman
17333ee039SDag-Erling Smørgrav #include <sys/types.h>
18333ee039SDag-Erling Smørgrav #include <sys/ioctl.h>
19333ee039SDag-Erling Smørgrav #include <sys/stat.h>
20333ee039SDag-Erling Smørgrav #include <signal.h>
21333ee039SDag-Erling Smørgrav
22333ee039SDag-Erling Smørgrav #include <errno.h>
23333ee039SDag-Erling Smørgrav #include <fcntl.h>
24333ee039SDag-Erling Smørgrav #include <grp.h>
25333ee039SDag-Erling Smørgrav #ifdef HAVE_PATHS_H
26333ee039SDag-Erling Smørgrav # include <paths.h>
27333ee039SDag-Erling Smørgrav #endif
28333ee039SDag-Erling Smørgrav #include <pwd.h>
29333ee039SDag-Erling Smørgrav #include <stdarg.h>
30*19261079SEd Maste #include <stdio.h>
31333ee039SDag-Erling Smørgrav #include <string.h>
32333ee039SDag-Erling Smørgrav #include <termios.h>
33989dd127SDag-Erling Smørgrav #ifdef HAVE_UTIL_H
34989dd127SDag-Erling Smørgrav # include <util.h>
35333ee039SDag-Erling Smørgrav #endif
36333ee039SDag-Erling Smørgrav #include <unistd.h>
37989dd127SDag-Erling Smørgrav
381e8db6e2SBrian Feldman #include "sshpty.h"
391e8db6e2SBrian Feldman #include "log.h"
40989dd127SDag-Erling Smørgrav #include "misc.h"
411e8db6e2SBrian Feldman
42989dd127SDag-Erling Smørgrav #ifdef HAVE_PTY_H
43989dd127SDag-Erling Smørgrav # include <pty.h>
44989dd127SDag-Erling Smørgrav #endif
45989dd127SDag-Erling Smørgrav
461e8db6e2SBrian Feldman #ifndef O_NOCTTY
471e8db6e2SBrian Feldman #define O_NOCTTY 0
481e8db6e2SBrian Feldman #endif
491e8db6e2SBrian Feldman
50cce7d346SDag-Erling Smørgrav #ifdef __APPLE__
51cce7d346SDag-Erling Smørgrav # include <AvailabilityMacros.h>
52cce7d346SDag-Erling Smørgrav # if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
53cce7d346SDag-Erling Smørgrav # define __APPLE_PRIVPTY__
54cce7d346SDag-Erling Smørgrav # endif
55cce7d346SDag-Erling Smørgrav #endif
56cce7d346SDag-Erling Smørgrav
571e8db6e2SBrian Feldman /*
581e8db6e2SBrian Feldman * Allocates and opens a pty. Returns 0 if no pty could be allocated, or
591e8db6e2SBrian Feldman * nonzero if a pty was successfully allocated. On success, open file
601e8db6e2SBrian Feldman * descriptors for the pty and tty sides and the name of the tty side are
611e8db6e2SBrian Feldman * returned (the buffer must be able to hold at least 64 characters).
621e8db6e2SBrian Feldman */
631e8db6e2SBrian Feldman
641e8db6e2SBrian Feldman int
pty_allocate(int * ptyfd,int * ttyfd,char * namebuf,size_t namebuflen)65333ee039SDag-Erling Smørgrav pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, size_t namebuflen)
661e8db6e2SBrian Feldman {
671e8db6e2SBrian Feldman /* openpty(3) exists in OSF/1 and some other os'es */
68989dd127SDag-Erling Smørgrav char *name;
691e8db6e2SBrian Feldman int i;
701e8db6e2SBrian Feldman
71989dd127SDag-Erling Smørgrav i = openpty(ptyfd, ttyfd, NULL, NULL, NULL);
72*19261079SEd Maste if (i == -1) {
731e8db6e2SBrian Feldman error("openpty: %.100s", strerror(errno));
741e8db6e2SBrian Feldman return 0;
751e8db6e2SBrian Feldman }
76989dd127SDag-Erling Smørgrav name = ttyname(*ttyfd);
77989dd127SDag-Erling Smørgrav if (!name)
78989dd127SDag-Erling Smørgrav fatal("openpty returns device for which ttyname fails.");
79989dd127SDag-Erling Smørgrav
80989dd127SDag-Erling Smørgrav strlcpy(namebuf, name, namebuflen); /* possible truncation */
811e8db6e2SBrian Feldman return 1;
821e8db6e2SBrian Feldman }
831e8db6e2SBrian Feldman
841e8db6e2SBrian Feldman /* Releases the tty. Its ownership is returned to root, and permissions to 0666. */
851e8db6e2SBrian Feldman
861e8db6e2SBrian Feldman void
pty_release(const char * tty)8721e764dfSDag-Erling Smørgrav pty_release(const char *tty)
881e8db6e2SBrian Feldman {
89557f75e5SDag-Erling Smørgrav #if !defined(__APPLE_PRIVPTY__) && !defined(HAVE_OPENPTY)
90*19261079SEd Maste if (chown(tty, (uid_t) 0, (gid_t) 0) == -1)
9121e764dfSDag-Erling Smørgrav error("chown %.100s 0 0 failed: %.100s", tty, strerror(errno));
92*19261079SEd Maste if (chmod(tty, (mode_t) 0666) == -1)
9321e764dfSDag-Erling Smørgrav error("chmod %.100s 0666 failed: %.100s", tty, strerror(errno));
94557f75e5SDag-Erling Smørgrav #endif /* !__APPLE_PRIVPTY__ && !HAVE_OPENPTY */
951e8db6e2SBrian Feldman }
961e8db6e2SBrian Feldman
97cf2b5f3bSDag-Erling Smørgrav /* Makes the tty the process's controlling tty and sets it to sane modes. */
981e8db6e2SBrian Feldman
991e8db6e2SBrian Feldman void
pty_make_controlling_tty(int * ttyfd,const char * tty)10021e764dfSDag-Erling Smørgrav pty_make_controlling_tty(int *ttyfd, const char *tty)
1011e8db6e2SBrian Feldman {
1021e8db6e2SBrian Feldman int fd;
103989dd127SDag-Erling Smørgrav
1041e8db6e2SBrian Feldman /* First disconnect from the old controlling tty. */
1051e8db6e2SBrian Feldman #ifdef TIOCNOTTY
1061e8db6e2SBrian Feldman fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
1071e8db6e2SBrian Feldman if (fd >= 0) {
1081e8db6e2SBrian Feldman (void) ioctl(fd, TIOCNOTTY, NULL);
1091e8db6e2SBrian Feldman close(fd);
1101e8db6e2SBrian Feldman }
1111e8db6e2SBrian Feldman #endif /* TIOCNOTTY */
112*19261079SEd Maste if (setsid() == -1)
1131e8db6e2SBrian Feldman error("setsid: %.100s", strerror(errno));
1141e8db6e2SBrian Feldman
1151e8db6e2SBrian Feldman /*
1161e8db6e2SBrian Feldman * Verify that we are successfully disconnected from the controlling
1171e8db6e2SBrian Feldman * tty.
1181e8db6e2SBrian Feldman */
1191e8db6e2SBrian Feldman fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
1201e8db6e2SBrian Feldman if (fd >= 0) {
1211e8db6e2SBrian Feldman error("Failed to disconnect from controlling tty.");
1221e8db6e2SBrian Feldman close(fd);
1231e8db6e2SBrian Feldman }
1241e8db6e2SBrian Feldman /* Make it our controlling tty. */
1251e8db6e2SBrian Feldman #ifdef TIOCSCTTY
1261e8db6e2SBrian Feldman debug("Setting controlling tty using TIOCSCTTY.");
1271e8db6e2SBrian Feldman if (ioctl(*ttyfd, TIOCSCTTY, NULL) < 0)
1281e8db6e2SBrian Feldman error("ioctl(TIOCSCTTY): %.100s", strerror(errno));
1291e8db6e2SBrian Feldman #endif /* TIOCSCTTY */
130d4ecd108SDag-Erling Smørgrav #ifdef NEED_SETPGRP
131989dd127SDag-Erling Smørgrav if (setpgrp(0,0) < 0)
132989dd127SDag-Erling Smørgrav error("SETPGRP %s",strerror(errno));
133d4ecd108SDag-Erling Smørgrav #endif /* NEED_SETPGRP */
13421e764dfSDag-Erling Smørgrav fd = open(tty, O_RDWR);
135*19261079SEd Maste if (fd == -1)
13621e764dfSDag-Erling Smørgrav error("%.100s: %.100s", tty, strerror(errno));
137ca86bcf2SDag-Erling Smørgrav else
1381e8db6e2SBrian Feldman close(fd);
139ca86bcf2SDag-Erling Smørgrav
1401e8db6e2SBrian Feldman /* Verify that we now have a controlling tty. */
1411e8db6e2SBrian Feldman fd = open(_PATH_TTY, O_WRONLY);
142*19261079SEd Maste if (fd == -1)
1431e8db6e2SBrian Feldman error("open /dev/tty failed - could not set controlling tty: %.100s",
1441e8db6e2SBrian Feldman strerror(errno));
145a82e551fSDag-Erling Smørgrav else
1461e8db6e2SBrian Feldman close(fd);
1471e8db6e2SBrian Feldman }
1481e8db6e2SBrian Feldman
1491e8db6e2SBrian Feldman /* Changes the window size associated with the pty. */
1501e8db6e2SBrian Feldman
1511e8db6e2SBrian Feldman void
pty_change_window_size(int ptyfd,u_int row,u_int col,u_int xpixel,u_int ypixel)152333ee039SDag-Erling Smørgrav pty_change_window_size(int ptyfd, u_int row, u_int col,
153333ee039SDag-Erling Smørgrav u_int xpixel, u_int ypixel)
1541e8db6e2SBrian Feldman {
1551e8db6e2SBrian Feldman struct winsize w;
156a82e551fSDag-Erling Smørgrav
157333ee039SDag-Erling Smørgrav /* may truncate u_int -> u_short */
1581e8db6e2SBrian Feldman w.ws_row = row;
1591e8db6e2SBrian Feldman w.ws_col = col;
1601e8db6e2SBrian Feldman w.ws_xpixel = xpixel;
1611e8db6e2SBrian Feldman w.ws_ypixel = ypixel;
1621e8db6e2SBrian Feldman (void) ioctl(ptyfd, TIOCSWINSZ, &w);
1631e8db6e2SBrian Feldman }
1641e8db6e2SBrian Feldman
1651e8db6e2SBrian Feldman void
pty_setowner(struct passwd * pw,const char * tty)16621e764dfSDag-Erling Smørgrav pty_setowner(struct passwd *pw, const char *tty)
1671e8db6e2SBrian Feldman {
1681e8db6e2SBrian Feldman struct group *grp;
1691e8db6e2SBrian Feldman gid_t gid;
1701e8db6e2SBrian Feldman mode_t mode;
1711e8db6e2SBrian Feldman struct stat st;
1721e8db6e2SBrian Feldman
1731e8db6e2SBrian Feldman /* Determine the group to make the owner of the tty. */
1741e8db6e2SBrian Feldman grp = getgrnam("tty");
175*19261079SEd Maste if (grp == NULL)
176*19261079SEd Maste debug("%s: no tty group", __func__);
177bc5531deSDag-Erling Smørgrav gid = (grp != NULL) ? grp->gr_gid : pw->pw_gid;
178eccfee6eSDag-Erling Smørgrav mode = (grp != NULL) ? 0620 : 0600;
1791e8db6e2SBrian Feldman
1801e8db6e2SBrian Feldman /*
1811e8db6e2SBrian Feldman * Change owner and mode of the tty as required.
182af12a3e7SDag-Erling Smørgrav * Warn but continue if filesystem is read-only and the uids match/
183af12a3e7SDag-Erling Smørgrav * tty is owned by root.
1841e8db6e2SBrian Feldman */
185*19261079SEd Maste if (stat(tty, &st) == -1)
18621e764dfSDag-Erling Smørgrav fatal("stat(%.100s) failed: %.100s", tty,
1871e8db6e2SBrian Feldman strerror(errno));
1881e8db6e2SBrian Feldman
189333ee039SDag-Erling Smørgrav #ifdef WITH_SELINUX
190333ee039SDag-Erling Smørgrav ssh_selinux_setup_pty(pw->pw_name, tty);
191333ee039SDag-Erling Smørgrav #endif
192333ee039SDag-Erling Smørgrav
1931e8db6e2SBrian Feldman if (st.st_uid != pw->pw_uid || st.st_gid != gid) {
194*19261079SEd Maste if (chown(tty, pw->pw_uid, gid) == -1) {
195af12a3e7SDag-Erling Smørgrav if (errno == EROFS &&
196af12a3e7SDag-Erling Smørgrav (st.st_uid == pw->pw_uid || st.st_uid == 0))
197e73e9afaSDag-Erling Smørgrav debug("chown(%.100s, %u, %u) failed: %.100s",
19821e764dfSDag-Erling Smørgrav tty, (u_int)pw->pw_uid, (u_int)gid,
1991e8db6e2SBrian Feldman strerror(errno));
2001e8db6e2SBrian Feldman else
201a82e551fSDag-Erling Smørgrav fatal("chown(%.100s, %u, %u) failed: %.100s",
20221e764dfSDag-Erling Smørgrav tty, (u_int)pw->pw_uid, (u_int)gid,
2031e8db6e2SBrian Feldman strerror(errno));
2041e8db6e2SBrian Feldman }
2051e8db6e2SBrian Feldman }
2061e8db6e2SBrian Feldman
2071e8db6e2SBrian Feldman if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) {
208*19261079SEd Maste if (chmod(tty, mode) == -1) {
2091e8db6e2SBrian Feldman if (errno == EROFS &&
2101e8db6e2SBrian Feldman (st.st_mode & (S_IRGRP | S_IROTH)) == 0)
211e73e9afaSDag-Erling Smørgrav debug("chmod(%.100s, 0%o) failed: %.100s",
21221e764dfSDag-Erling Smørgrav tty, (u_int)mode, strerror(errno));
2131e8db6e2SBrian Feldman else
2141e8db6e2SBrian Feldman fatal("chmod(%.100s, 0%o) failed: %.100s",
21521e764dfSDag-Erling Smørgrav tty, (u_int)mode, strerror(errno));
2161e8db6e2SBrian Feldman }
2171e8db6e2SBrian Feldman }
2181e8db6e2SBrian Feldman }
219ca86bcf2SDag-Erling Smørgrav
220ca86bcf2SDag-Erling Smørgrav /* Disconnect from the controlling tty. */
221ca86bcf2SDag-Erling Smørgrav void
disconnect_controlling_tty(void)222ca86bcf2SDag-Erling Smørgrav disconnect_controlling_tty(void)
223ca86bcf2SDag-Erling Smørgrav {
224ca86bcf2SDag-Erling Smørgrav #ifdef TIOCNOTTY
225ca86bcf2SDag-Erling Smørgrav int fd;
226ca86bcf2SDag-Erling Smørgrav
227ca86bcf2SDag-Erling Smørgrav if ((fd = open(_PATH_TTY, O_RDWR | O_NOCTTY)) >= 0) {
228ca86bcf2SDag-Erling Smørgrav (void) ioctl(fd, TIOCNOTTY, NULL);
229ca86bcf2SDag-Erling Smørgrav close(fd);
230ca86bcf2SDag-Erling Smørgrav }
231ca86bcf2SDag-Erling Smørgrav #endif /* TIOCNOTTY */
232ca86bcf2SDag-Erling Smørgrav }
233