xref: /dflybsd-src/crypto/openssh/sshpty.c (revision 50a69bb51183a7916e776f2c9f5fa64c999f1a2f)
10cbfa66cSDaniel Fojt /* $OpenBSD: sshpty.c,v 1.34 2019/07/04 16:20:10 deraadt 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>
30*50a69bb5SSascha Wildner #include <stdio.h>
3118de8d7fSPeter Avalos #include <string.h>
3218de8d7fSPeter Avalos #include <termios.h>
3318de8d7fSPeter Avalos #ifdef HAVE_UTIL_H
3418de8d7fSPeter Avalos # include <util.h>
3518de8d7fSPeter Avalos #endif
3618de8d7fSPeter Avalos #include <unistd.h>
3718de8d7fSPeter Avalos 
3818de8d7fSPeter Avalos #include "sshpty.h"
3918de8d7fSPeter Avalos #include "log.h"
4018de8d7fSPeter Avalos #include "misc.h"
4118de8d7fSPeter Avalos 
4218de8d7fSPeter Avalos #ifdef HAVE_PTY_H
4318de8d7fSPeter Avalos # include <pty.h>
4418de8d7fSPeter Avalos #endif
4518de8d7fSPeter Avalos 
4618de8d7fSPeter Avalos #ifndef O_NOCTTY
4718de8d7fSPeter Avalos #define O_NOCTTY 0
4818de8d7fSPeter Avalos #endif
4918de8d7fSPeter Avalos 
50cb5eb4f1SPeter Avalos #ifdef __APPLE__
51cb5eb4f1SPeter Avalos # include <AvailabilityMacros.h>
52cb5eb4f1SPeter Avalos # if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
53cb5eb4f1SPeter Avalos #  define __APPLE_PRIVPTY__
54cb5eb4f1SPeter Avalos # endif
55cb5eb4f1SPeter Avalos #endif
56cb5eb4f1SPeter Avalos 
5718de8d7fSPeter Avalos /*
5818de8d7fSPeter Avalos  * Allocates and opens a pty.  Returns 0 if no pty could be allocated, or
5918de8d7fSPeter Avalos  * nonzero if a pty was successfully allocated.  On success, open file
6018de8d7fSPeter Avalos  * descriptors for the pty and tty sides and the name of the tty side are
6118de8d7fSPeter Avalos  * returned (the buffer must be able to hold at least 64 characters).
6218de8d7fSPeter Avalos  */
6318de8d7fSPeter Avalos 
6418de8d7fSPeter Avalos int
pty_allocate(int * ptyfd,int * ttyfd,char * namebuf,size_t namebuflen)6518de8d7fSPeter Avalos pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, size_t namebuflen)
6618de8d7fSPeter Avalos {
6718de8d7fSPeter Avalos 	/* openpty(3) exists in OSF/1 and some other os'es */
6818de8d7fSPeter Avalos 	char *name;
6918de8d7fSPeter Avalos 	int i;
7018de8d7fSPeter Avalos 
7118de8d7fSPeter Avalos 	i = openpty(ptyfd, ttyfd, NULL, NULL, NULL);
720cbfa66cSDaniel Fojt 	if (i == -1) {
7318de8d7fSPeter Avalos 		error("openpty: %.100s", strerror(errno));
7418de8d7fSPeter Avalos 		return 0;
7518de8d7fSPeter Avalos 	}
7618de8d7fSPeter Avalos 	name = ttyname(*ttyfd);
7718de8d7fSPeter Avalos 	if (!name)
7818de8d7fSPeter Avalos 		fatal("openpty returns device for which ttyname fails.");
7918de8d7fSPeter Avalos 
8018de8d7fSPeter Avalos 	strlcpy(namebuf, name, namebuflen);	/* possible truncation */
8118de8d7fSPeter Avalos 	return 1;
8218de8d7fSPeter Avalos }
8318de8d7fSPeter Avalos 
8418de8d7fSPeter Avalos /* Releases the tty.  Its ownership is returned to root, and permissions to 0666. */
8518de8d7fSPeter Avalos 
8618de8d7fSPeter Avalos void
pty_release(const char * tty)8718de8d7fSPeter Avalos pty_release(const char *tty)
8818de8d7fSPeter Avalos {
89e9778795SPeter Avalos #if !defined(__APPLE_PRIVPTY__) && !defined(HAVE_OPENPTY)
900cbfa66cSDaniel Fojt 	if (chown(tty, (uid_t) 0, (gid_t) 0) == -1)
9118de8d7fSPeter Avalos 		error("chown %.100s 0 0 failed: %.100s", tty, strerror(errno));
920cbfa66cSDaniel Fojt 	if (chmod(tty, (mode_t) 0666) == -1)
9318de8d7fSPeter Avalos 		error("chmod %.100s 0666 failed: %.100s", tty, strerror(errno));
94e9778795SPeter Avalos #endif /* !__APPLE_PRIVPTY__ && !HAVE_OPENPTY */
9518de8d7fSPeter Avalos }
9618de8d7fSPeter Avalos 
9718de8d7fSPeter Avalos /* Makes the tty the process's controlling tty and sets it to sane modes. */
9818de8d7fSPeter Avalos 
9918de8d7fSPeter Avalos void
pty_make_controlling_tty(int * ttyfd,const char * tty)10018de8d7fSPeter Avalos pty_make_controlling_tty(int *ttyfd, const char *tty)
10118de8d7fSPeter Avalos {
10218de8d7fSPeter Avalos 	int fd;
10318de8d7fSPeter Avalos 
10418de8d7fSPeter Avalos 	/* First disconnect from the old controlling tty. */
10518de8d7fSPeter Avalos #ifdef TIOCNOTTY
10618de8d7fSPeter Avalos 	fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
10718de8d7fSPeter Avalos 	if (fd >= 0) {
10818de8d7fSPeter Avalos 		(void) ioctl(fd, TIOCNOTTY, NULL);
10918de8d7fSPeter Avalos 		close(fd);
11018de8d7fSPeter Avalos 	}
11118de8d7fSPeter Avalos #endif /* TIOCNOTTY */
1120cbfa66cSDaniel Fojt 	if (setsid() == -1)
11318de8d7fSPeter Avalos 		error("setsid: %.100s", strerror(errno));
11418de8d7fSPeter Avalos 
11518de8d7fSPeter Avalos 	/*
11618de8d7fSPeter Avalos 	 * Verify that we are successfully disconnected from the controlling
11718de8d7fSPeter Avalos 	 * tty.
11818de8d7fSPeter Avalos 	 */
11918de8d7fSPeter Avalos 	fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
12018de8d7fSPeter Avalos 	if (fd >= 0) {
12118de8d7fSPeter Avalos 		error("Failed to disconnect from controlling tty.");
12218de8d7fSPeter Avalos 		close(fd);
12318de8d7fSPeter Avalos 	}
12418de8d7fSPeter Avalos 	/* Make it our controlling tty. */
12518de8d7fSPeter Avalos #ifdef TIOCSCTTY
12618de8d7fSPeter Avalos 	debug("Setting controlling tty using TIOCSCTTY.");
12718de8d7fSPeter Avalos 	if (ioctl(*ttyfd, TIOCSCTTY, NULL) < 0)
12818de8d7fSPeter Avalos 		error("ioctl(TIOCSCTTY): %.100s", strerror(errno));
12918de8d7fSPeter Avalos #endif /* TIOCSCTTY */
13018de8d7fSPeter Avalos #ifdef NEED_SETPGRP
13118de8d7fSPeter Avalos 	if (setpgrp(0,0) < 0)
13218de8d7fSPeter Avalos 		error("SETPGRP %s",strerror(errno));
13318de8d7fSPeter Avalos #endif /* NEED_SETPGRP */
13418de8d7fSPeter Avalos 	fd = open(tty, O_RDWR);
1350cbfa66cSDaniel Fojt 	if (fd == -1)
13618de8d7fSPeter Avalos 		error("%.100s: %.100s", tty, strerror(errno));
137ce74bacaSMatthew Dillon 	else
13818de8d7fSPeter Avalos 		close(fd);
139ce74bacaSMatthew Dillon 
14018de8d7fSPeter Avalos 	/* Verify that we now have a controlling tty. */
14118de8d7fSPeter Avalos 	fd = open(_PATH_TTY, O_WRONLY);
1420cbfa66cSDaniel Fojt 	if (fd == -1)
14318de8d7fSPeter Avalos 		error("open /dev/tty failed - could not set controlling tty: %.100s",
14418de8d7fSPeter Avalos 		    strerror(errno));
14518de8d7fSPeter Avalos 	else
14618de8d7fSPeter Avalos 		close(fd);
14718de8d7fSPeter Avalos }
14818de8d7fSPeter Avalos 
14918de8d7fSPeter Avalos /* Changes the window size associated with the pty. */
15018de8d7fSPeter Avalos 
15118de8d7fSPeter Avalos void
pty_change_window_size(int ptyfd,u_int row,u_int col,u_int xpixel,u_int ypixel)15218de8d7fSPeter Avalos pty_change_window_size(int ptyfd, u_int row, u_int col,
15318de8d7fSPeter Avalos 	u_int xpixel, u_int ypixel)
15418de8d7fSPeter Avalos {
15518de8d7fSPeter Avalos 	struct winsize w;
15618de8d7fSPeter Avalos 
15718de8d7fSPeter Avalos 	/* may truncate u_int -> u_short */
15818de8d7fSPeter Avalos 	w.ws_row = row;
15918de8d7fSPeter Avalos 	w.ws_col = col;
16018de8d7fSPeter Avalos 	w.ws_xpixel = xpixel;
16118de8d7fSPeter Avalos 	w.ws_ypixel = ypixel;
16218de8d7fSPeter Avalos 	(void) ioctl(ptyfd, TIOCSWINSZ, &w);
16318de8d7fSPeter Avalos }
16418de8d7fSPeter Avalos 
16518de8d7fSPeter Avalos void
pty_setowner(struct passwd * pw,const char * tty)16618de8d7fSPeter Avalos pty_setowner(struct passwd *pw, const char *tty)
16718de8d7fSPeter Avalos {
16818de8d7fSPeter Avalos 	struct group *grp;
16918de8d7fSPeter Avalos 	gid_t gid;
17018de8d7fSPeter Avalos 	mode_t mode;
17118de8d7fSPeter Avalos 	struct stat st;
17218de8d7fSPeter Avalos 
17318de8d7fSPeter Avalos 	/* Determine the group to make the owner of the tty. */
17418de8d7fSPeter Avalos 	grp = getgrnam("tty");
1750cbfa66cSDaniel Fojt 	if (grp == NULL)
1760cbfa66cSDaniel Fojt 		debug("%s: no tty group", __func__);
177e9778795SPeter Avalos 	gid = (grp != NULL) ? grp->gr_gid : pw->pw_gid;
178e9778795SPeter Avalos 	mode = (grp != NULL) ? 0620 : 0600;
17918de8d7fSPeter Avalos 
18018de8d7fSPeter Avalos 	/*
18118de8d7fSPeter Avalos 	 * Change owner and mode of the tty as required.
18218de8d7fSPeter Avalos 	 * Warn but continue if filesystem is read-only and the uids match/
18318de8d7fSPeter Avalos 	 * tty is owned by root.
18418de8d7fSPeter Avalos 	 */
1850cbfa66cSDaniel Fojt 	if (stat(tty, &st) == -1)
18618de8d7fSPeter Avalos 		fatal("stat(%.100s) failed: %.100s", tty,
18718de8d7fSPeter Avalos 		    strerror(errno));
18818de8d7fSPeter Avalos 
18918de8d7fSPeter Avalos #ifdef WITH_SELINUX
19018de8d7fSPeter Avalos 	ssh_selinux_setup_pty(pw->pw_name, tty);
19118de8d7fSPeter Avalos #endif
19218de8d7fSPeter Avalos 
19318de8d7fSPeter Avalos 	if (st.st_uid != pw->pw_uid || st.st_gid != gid) {
1940cbfa66cSDaniel Fojt 		if (chown(tty, pw->pw_uid, gid) == -1) {
19518de8d7fSPeter Avalos 			if (errno == EROFS &&
19618de8d7fSPeter Avalos 			    (st.st_uid == pw->pw_uid || st.st_uid == 0))
19718de8d7fSPeter Avalos 				debug("chown(%.100s, %u, %u) failed: %.100s",
19818de8d7fSPeter Avalos 				    tty, (u_int)pw->pw_uid, (u_int)gid,
19918de8d7fSPeter Avalos 				    strerror(errno));
20018de8d7fSPeter Avalos 			else
20118de8d7fSPeter Avalos 				fatal("chown(%.100s, %u, %u) failed: %.100s",
20218de8d7fSPeter Avalos 				    tty, (u_int)pw->pw_uid, (u_int)gid,
20318de8d7fSPeter Avalos 				    strerror(errno));
20418de8d7fSPeter Avalos 		}
20518de8d7fSPeter Avalos 	}
20618de8d7fSPeter Avalos 
20718de8d7fSPeter Avalos 	if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) {
2080cbfa66cSDaniel Fojt 		if (chmod(tty, mode) == -1) {
20918de8d7fSPeter Avalos 			if (errno == EROFS &&
21018de8d7fSPeter Avalos 			    (st.st_mode & (S_IRGRP | S_IROTH)) == 0)
21118de8d7fSPeter Avalos 				debug("chmod(%.100s, 0%o) failed: %.100s",
21218de8d7fSPeter Avalos 				    tty, (u_int)mode, strerror(errno));
21318de8d7fSPeter Avalos 			else
21418de8d7fSPeter Avalos 				fatal("chmod(%.100s, 0%o) failed: %.100s",
21518de8d7fSPeter Avalos 				    tty, (u_int)mode, strerror(errno));
21618de8d7fSPeter Avalos 		}
21718de8d7fSPeter Avalos 	}
21818de8d7fSPeter Avalos }
219ce74bacaSMatthew Dillon 
220ce74bacaSMatthew Dillon /* Disconnect from the controlling tty. */
221ce74bacaSMatthew Dillon void
disconnect_controlling_tty(void)222ce74bacaSMatthew Dillon disconnect_controlling_tty(void)
223ce74bacaSMatthew Dillon {
224ce74bacaSMatthew Dillon #ifdef TIOCNOTTY
225ce74bacaSMatthew Dillon 	int fd;
226ce74bacaSMatthew Dillon 
227ce74bacaSMatthew Dillon 	if ((fd = open(_PATH_TTY, O_RDWR | O_NOCTTY)) >= 0) {
228ce74bacaSMatthew Dillon 		(void) ioctl(fd, TIOCNOTTY, NULL);
229ce74bacaSMatthew Dillon 		close(fd);
230ce74bacaSMatthew Dillon 	}
231ce74bacaSMatthew Dillon #endif /* TIOCNOTTY */
232ce74bacaSMatthew Dillon }
233