xref: /netbsd-src/crypto/external/bsd/openssh/dist/sshpty.c (revision cd4ada6a2b2ed68a114b2c5dd935d9f87d742bb1)
1*cd4ada6aSchristos /*	$NetBSD: sshpty.c,v 1.8 2019/10/12 18:32:22 christos Exp $	*/
2*cd4ada6aSchristos /* $OpenBSD: sshpty.c,v 1.34 2019/07/04 16:20:10 deraadt Exp $ */
3ca32bd8dSchristos /*
4ca32bd8dSchristos  * Author: Tatu Ylonen <ylo@cs.hut.fi>
5ca32bd8dSchristos  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
6ca32bd8dSchristos  *                    All rights reserved
7ca32bd8dSchristos  * Allocating a pseudo-terminal, and making it the controlling tty.
8ca32bd8dSchristos  *
9ca32bd8dSchristos  * As far as I am concerned, the code I have written for this software
10ca32bd8dSchristos  * can be used freely for any purpose.  Any derived versions of this
11ca32bd8dSchristos  * software must be clearly marked as such, and if the derived work is
12ca32bd8dSchristos  * incompatible with the protocol description in the RFC file, it must be
13ca32bd8dSchristos  * called by a name other than "ssh" or "Secure Shell".
14ca32bd8dSchristos  */
15ca32bd8dSchristos 
16313c6c94Schristos #include "includes.h"
17*cd4ada6aSchristos __RCSID("$NetBSD: sshpty.c,v 1.8 2019/10/12 18:32:22 christos Exp $");
18ca32bd8dSchristos #include <sys/types.h>
19ca32bd8dSchristos #include <sys/ioctl.h>
20ca32bd8dSchristos #include <sys/stat.h>
21ca32bd8dSchristos 
22ca32bd8dSchristos #include <errno.h>
23ca32bd8dSchristos #include <fcntl.h>
24ca32bd8dSchristos #include <grp.h>
25ca32bd8dSchristos #include <paths.h>
26ca32bd8dSchristos #include <pwd.h>
27ca32bd8dSchristos #include <stdarg.h>
28ca32bd8dSchristos #include <string.h>
29ca32bd8dSchristos #include <termios.h>
30ca32bd8dSchristos #include <unistd.h>
31ca32bd8dSchristos #include <util.h>
32ca32bd8dSchristos 
33ca32bd8dSchristos #include "sshpty.h"
34ca32bd8dSchristos #include "log.h"
35ca32bd8dSchristos 
36ca32bd8dSchristos #ifndef O_NOCTTY
37ca32bd8dSchristos #define O_NOCTTY 0
38ca32bd8dSchristos #endif
39ca32bd8dSchristos 
40ca32bd8dSchristos /*
41ca32bd8dSchristos  * Allocates and opens a pty.  Returns 0 if no pty could be allocated, or
42ca32bd8dSchristos  * nonzero if a pty was successfully allocated.  On success, open file
43ca32bd8dSchristos  * descriptors for the pty and tty sides and the name of the tty side are
44ca32bd8dSchristos  * returned (the buffer must be able to hold at least 64 characters).
45ca32bd8dSchristos  */
46ca32bd8dSchristos 
47ca32bd8dSchristos int
pty_allocate(int * ptyfd,int * ttyfd,char * namebuf,size_t namebuflen)48ca32bd8dSchristos pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, size_t namebuflen)
49ca32bd8dSchristos {
50ca32bd8dSchristos 	char buf[64];
51ca32bd8dSchristos 	int i;
52ca32bd8dSchristos 
53ca32bd8dSchristos 	i = openpty(ptyfd, ttyfd, buf, NULL, NULL);
54*cd4ada6aSchristos 	if (i == -1) {
55ca32bd8dSchristos 		error("openpty: %.100s", strerror(errno));
56ca32bd8dSchristos 		return 0;
57ca32bd8dSchristos 	}
58ca32bd8dSchristos 	strlcpy(namebuf, buf, namebuflen);	/* possible truncation */
59ca32bd8dSchristos 	return 1;
60ca32bd8dSchristos }
61ca32bd8dSchristos 
62ca32bd8dSchristos /* Releases the tty.  Its ownership is returned to root, and permissions to 0666. */
63ca32bd8dSchristos 
64ca32bd8dSchristos void
pty_release(const char * tty)65ca32bd8dSchristos pty_release(const char *tty)
66ca32bd8dSchristos {
67*cd4ada6aSchristos 	if (chown(tty, (uid_t) 0, (gid_t) 0) == -1)
68ca32bd8dSchristos 		error("chown %.100s 0 0 failed: %.100s", tty, strerror(errno));
69*cd4ada6aSchristos 	if (chmod(tty, (mode_t) 0666) == -1)
70ca32bd8dSchristos 		error("chmod %.100s 0666 failed: %.100s", tty, strerror(errno));
71ca32bd8dSchristos }
72ca32bd8dSchristos 
73ca32bd8dSchristos /* Makes the tty the process's controlling tty and sets it to sane modes. */
74ca32bd8dSchristos 
75ca32bd8dSchristos void
pty_make_controlling_tty(int * ttyfd,const char * tty)76ca32bd8dSchristos pty_make_controlling_tty(int *ttyfd, const char *tty)
77ca32bd8dSchristos {
78ca32bd8dSchristos 	int fd;
79ca32bd8dSchristos 
80ca32bd8dSchristos 	/* First disconnect from the old controlling tty. */
81ca32bd8dSchristos #ifdef TIOCNOTTY
82ca32bd8dSchristos 	fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
83ca32bd8dSchristos 	if (fd >= 0) {
84ca32bd8dSchristos 		(void) ioctl(fd, TIOCNOTTY, NULL);
85ca32bd8dSchristos 		close(fd);
86ca32bd8dSchristos 	}
87ca32bd8dSchristos #endif /* TIOCNOTTY */
88*cd4ada6aSchristos 	if (setsid() == -1)
89ca32bd8dSchristos 		error("setsid: %.100s", strerror(errno));
90ca32bd8dSchristos 
91ca32bd8dSchristos 	/*
92ca32bd8dSchristos 	 * Verify that we are successfully disconnected from the controlling
93ca32bd8dSchristos 	 * tty.
94ca32bd8dSchristos 	 */
95ca32bd8dSchristos 	fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
96ca32bd8dSchristos 	if (fd >= 0) {
97ca32bd8dSchristos 		error("Failed to disconnect from controlling tty.");
98ca32bd8dSchristos 		close(fd);
99ca32bd8dSchristos 	}
100ca32bd8dSchristos 	/* Make it our controlling tty. */
101ca32bd8dSchristos #ifdef TIOCSCTTY
102ca32bd8dSchristos 	debug("Setting controlling tty using TIOCSCTTY.");
103*cd4ada6aSchristos 	if (ioctl(*ttyfd, TIOCSCTTY, NULL) == -1)
104ca32bd8dSchristos 		error("ioctl(TIOCSCTTY): %.100s", strerror(errno));
105ca32bd8dSchristos #endif /* TIOCSCTTY */
106ca32bd8dSchristos 	fd = open(tty, O_RDWR);
107*cd4ada6aSchristos 	if (fd == -1)
108ca32bd8dSchristos 		error("%.100s: %.100s", tty, strerror(errno));
109ca32bd8dSchristos 	else
110ca32bd8dSchristos 		close(fd);
111ca32bd8dSchristos 
112ca32bd8dSchristos 	/* Verify that we now have a controlling tty. */
113ca32bd8dSchristos 	fd = open(_PATH_TTY, O_WRONLY);
114*cd4ada6aSchristos 	if (fd == -1)
115ca32bd8dSchristos 		error("open /dev/tty failed - could not set controlling tty: %.100s",
116ca32bd8dSchristos 		    strerror(errno));
117ca32bd8dSchristos 	else
118ca32bd8dSchristos 		close(fd);
119ca32bd8dSchristos }
120ca32bd8dSchristos 
121ca32bd8dSchristos /* Changes the window size associated with the pty. */
122ca32bd8dSchristos 
123ca32bd8dSchristos void
pty_change_window_size(int ptyfd,u_int row,u_int col,u_int xpixel,u_int ypixel)124ca32bd8dSchristos pty_change_window_size(int ptyfd, u_int row, u_int col,
125ca32bd8dSchristos 	u_int xpixel, u_int ypixel)
126ca32bd8dSchristos {
127ca32bd8dSchristos 	struct winsize w;
128ca32bd8dSchristos 
129ca32bd8dSchristos 	/* may truncate u_int -> u_short */
130ca32bd8dSchristos 	w.ws_row = row;
131ca32bd8dSchristos 	w.ws_col = col;
132ca32bd8dSchristos 	w.ws_xpixel = xpixel;
133ca32bd8dSchristos 	w.ws_ypixel = ypixel;
134ca32bd8dSchristos 	(void) ioctl(ptyfd, TIOCSWINSZ, &w);
135ca32bd8dSchristos }
136ca32bd8dSchristos 
137ca32bd8dSchristos void
pty_setowner(struct passwd * pw,const char * tty)138ca32bd8dSchristos pty_setowner(struct passwd *pw, const char *tty)
139ca32bd8dSchristos {
140ca32bd8dSchristos 	struct group *grp;
141ca32bd8dSchristos 	gid_t gid;
142ca32bd8dSchristos 	mode_t mode;
143ca32bd8dSchristos 	struct stat st;
144ca32bd8dSchristos 
145ca32bd8dSchristos 	/* Determine the group to make the owner of the tty. */
146ca32bd8dSchristos 	grp = getgrnam("tty");
147*cd4ada6aSchristos 	if (grp == NULL)
148*cd4ada6aSchristos 		fatal("no tty group");
149e4d43b82Schristos 	gid = (grp != NULL) ? grp->gr_gid : pw->pw_gid;
1508395c133Schristos 	mode = (grp != NULL) ? 0620 : 0600;
151ca32bd8dSchristos 
152ca32bd8dSchristos 	/*
153ca32bd8dSchristos 	 * Change owner and mode of the tty as required.
154ca32bd8dSchristos 	 * Warn but continue if filesystem is read-only and the uids match/
155ca32bd8dSchristos 	 * tty is owned by root.
156ca32bd8dSchristos 	 */
157*cd4ada6aSchristos 	if (stat(tty, &st) == -1)
158ca32bd8dSchristos 		fatal("stat(%.100s) failed: %.100s", tty,
159ca32bd8dSchristos 		    strerror(errno));
160ca32bd8dSchristos 
161ca32bd8dSchristos 	if (st.st_uid != pw->pw_uid || st.st_gid != gid) {
162*cd4ada6aSchristos 		if (chown(tty, pw->pw_uid, gid) == -1) {
163ca32bd8dSchristos 			if (errno == EROFS &&
164ca32bd8dSchristos 			    (st.st_uid == pw->pw_uid || st.st_uid == 0))
165ca32bd8dSchristos 				debug("chown(%.100s, %u, %u) failed: %.100s",
166ca32bd8dSchristos 				    tty, (u_int)pw->pw_uid, (u_int)gid,
167ca32bd8dSchristos 				    strerror(errno));
168ca32bd8dSchristos 			else
169ca32bd8dSchristos 				fatal("chown(%.100s, %u, %u) failed: %.100s",
170ca32bd8dSchristos 				    tty, (u_int)pw->pw_uid, (u_int)gid,
171ca32bd8dSchristos 				    strerror(errno));
172ca32bd8dSchristos 		}
173ca32bd8dSchristos 	}
174ca32bd8dSchristos 
175ca32bd8dSchristos 	if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) {
176*cd4ada6aSchristos 		if (chmod(tty, mode) == -1) {
177ca32bd8dSchristos 			if (errno == EROFS &&
178ca32bd8dSchristos 			    (st.st_mode & (S_IRGRP | S_IROTH)) == 0)
179ca32bd8dSchristos 				debug("chmod(%.100s, 0%o) failed: %.100s",
180ca32bd8dSchristos 				    tty, (u_int)mode, strerror(errno));
181ca32bd8dSchristos 			else
182ca32bd8dSchristos 				fatal("chmod(%.100s, 0%o) failed: %.100s",
183ca32bd8dSchristos 				    tty, (u_int)mode, strerror(errno));
184ca32bd8dSchristos 		}
185ca32bd8dSchristos 	}
186ca32bd8dSchristos }
187ee85abc4Schristos 
188ee85abc4Schristos /* Disconnect from the controlling tty. */
189ee85abc4Schristos void
disconnect_controlling_tty(void)190ee85abc4Schristos disconnect_controlling_tty(void)
191ee85abc4Schristos {
192ee85abc4Schristos 	int fd;
193ee85abc4Schristos 
194ee85abc4Schristos 	if ((fd = open(_PATH_TTY, O_RDWR | O_NOCTTY)) >= 0) {
195ee85abc4Schristos 		(void) ioctl(fd, TIOCNOTTY, NULL);
196ee85abc4Schristos 		close(fd);
197ee85abc4Schristos 	}
198ee85abc4Schristos }
199