xref: /dflybsd-src/crypto/openssh/sshpty.c (revision ce74baca94b6dd2a80af6a625aba2cf14ab7fec8)
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