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