Mercurial > dropbear
comparison sshpty.c @ 389:5ff8218bcee9
propagate from branch 'au.asn.ucc.matt.ltm.dropbear' (head 2af95f00ebd5bb7a28b3817db1218442c935388e)
to branch 'au.asn.ucc.matt.dropbear' (head ecd779509ef23a8cdf64888904fc9b31d78aa933)
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Thu, 11 Jan 2007 03:14:55 +0000 |
parents | 5c6f9d27ea1c |
children | 740e782679be a98a2138364a |
comparison
equal
deleted
inserted
replaced
388:fb54020f78e1 | 389:5ff8218bcee9 |
---|---|
1 /* | |
2 * Dropbear - a SSH2 server | |
3 * | |
4 * Copied from OpenSSH-3.5p1 source, modified by Matt Johnston 2003 | |
5 * | |
6 * Author: Tatu Ylonen <[email protected]> | |
7 * Copyright (c) 1995 Tatu Ylonen <[email protected]>, Espoo, Finland | |
8 * All rights reserved | |
9 * Allocating a pseudo-terminal, and making it the controlling tty. | |
10 * | |
11 * As far as I am concerned, the code I have written for this software | |
12 * can be used freely for any purpose. Any derived versions of this | |
13 * software must be clearly marked as such, and if the derived work is | |
14 * incompatible with the protocol description in the RFC file, it must be | |
15 * called by a name other than "ssh" or "Secure Shell". | |
16 */ | |
17 | |
18 /*RCSID("OpenBSD: sshpty.c,v 1.7 2002/06/24 17:57:20 deraadt Exp ");*/ | |
19 | |
20 #include "includes.h" | |
21 #include "dbutil.h" | |
22 #include "errno.h" | |
23 #include "sshpty.h" | |
24 | |
25 /* Pty allocated with _getpty gets broken if we do I_PUSH:es to it. */ | |
26 #if defined(HAVE__GETPTY) || defined(HAVE_OPENPTY) | |
27 #undef HAVE_DEV_PTMX | |
28 #endif | |
29 | |
30 #ifdef HAVE_PTY_H | |
31 # include <pty.h> | |
32 #endif | |
33 #if defined(USE_DEV_PTMX) && defined(HAVE_STROPTS_H) | |
34 # include <stropts.h> | |
35 #endif | |
36 | |
37 #ifndef O_NOCTTY | |
38 #define O_NOCTTY 0 | |
39 #endif | |
40 | |
41 /* | |
42 * Allocates and opens a pty. Returns 0 if no pty could be allocated, or | |
43 * nonzero if a pty was successfully allocated. On success, open file | |
44 * descriptors for the pty and tty sides and the name of the tty side are | |
45 * returned (the buffer must be able to hold at least 64 characters). | |
46 */ | |
47 | |
48 int | |
49 pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, int namebuflen) | |
50 { | |
51 #if defined(HAVE_OPENPTY) | |
52 /* exists in recent (4.4) BSDs and OSF/1 */ | |
53 char *name; | |
54 int i; | |
55 | |
56 i = openpty(ptyfd, ttyfd, NULL, NULL, NULL); | |
57 if (i < 0) { | |
58 dropbear_log(LOG_WARNING, | |
59 "pty_allocate: openpty: %.100s", strerror(errno)); | |
60 return 0; | |
61 } | |
62 name = ttyname(*ttyfd); | |
63 if (!name) { | |
64 dropbear_exit("ttyname fails for openpty device"); | |
65 } | |
66 | |
67 strlcpy(namebuf, name, namebuflen); /* possible truncation */ | |
68 return 1; | |
69 #else /* HAVE_OPENPTY */ | |
70 #ifdef HAVE__GETPTY | |
71 /* | |
72 * _getpty(3) exists in SGI Irix 4.x, 5.x & 6.x -- it generates more | |
73 * pty's automagically when needed | |
74 */ | |
75 char *slave; | |
76 | |
77 slave = _getpty(ptyfd, O_RDWR, 0622, 0); | |
78 if (slave == NULL) { | |
79 dropbear_log(LOG_WARNING, | |
80 "pty_allocate: _getpty: %.100s", strerror(errno)); | |
81 return 0; | |
82 } | |
83 strlcpy(namebuf, slave, namebuflen); | |
84 /* Open the slave side. */ | |
85 *ttyfd = open(namebuf, O_RDWR | O_NOCTTY); | |
86 if (*ttyfd < 0) { | |
87 dropbear_log(LOG_WARNING, | |
88 "pty_allocate error: ttyftd open error"); | |
89 close(*ptyfd); | |
90 return 0; | |
91 } | |
92 return 1; | |
93 #else /* HAVE__GETPTY */ | |
94 #if defined(USE_DEV_PTMX) | |
95 /* | |
96 * This code is used e.g. on Solaris 2.x. (Note that Solaris 2.3 | |
97 * also has bsd-style ptys, but they simply do not work.) | |
98 * | |
99 * Linux systems may have the /dev/ptmx device, but this code won't work. | |
100 */ | |
101 int ptm; | |
102 char *pts; | |
103 | |
104 ptm = open("/dev/ptmx", O_RDWR | O_NOCTTY); | |
105 if (ptm < 0) { | |
106 dropbear_log(LOG_WARNING, | |
107 "pty_allocate: /dev/ptmx: %.100s", strerror(errno)); | |
108 return 0; | |
109 } | |
110 if (grantpt(ptm) < 0) { | |
111 dropbear_log(LOG_WARNING, | |
112 "grantpt: %.100s", strerror(errno)); | |
113 return 0; | |
114 } | |
115 if (unlockpt(ptm) < 0) { | |
116 dropbear_log(LOG_WARNING, | |
117 "unlockpt: %.100s", strerror(errno)); | |
118 return 0; | |
119 } | |
120 pts = ptsname(ptm); | |
121 if (pts == NULL) { | |
122 dropbear_log(LOG_WARNING, | |
123 "Slave pty side name could not be obtained."); | |
124 } | |
125 strlcpy(namebuf, pts, namebuflen); | |
126 *ptyfd = ptm; | |
127 | |
128 /* Open the slave side. */ | |
129 *ttyfd = open(namebuf, O_RDWR | O_NOCTTY); | |
130 if (*ttyfd < 0) { | |
131 dropbear_log(LOG_ERR, | |
132 "error opening pts %.100s: %.100s", namebuf, strerror(errno)); | |
133 close(*ptyfd); | |
134 return 0; | |
135 } | |
136 #ifndef HAVE_CYGWIN | |
137 /* | |
138 * Push the appropriate streams modules, as described in Solaris pts(7). | |
139 * HP-UX pts(7) doesn't have ttcompat module. | |
140 */ | |
141 if (ioctl(*ttyfd, I_PUSH, "ptem") < 0) { | |
142 dropbear_log(LOG_WARNING, | |
143 "ioctl I_PUSH ptem: %.100s", strerror(errno)); | |
144 } | |
145 if (ioctl(*ttyfd, I_PUSH, "ldterm") < 0) { | |
146 dropbear_log(LOG_WARNING, | |
147 "ioctl I_PUSH ldterm: %.100s", strerror(errno)); | |
148 } | |
149 #ifndef __hpux | |
150 if (ioctl(*ttyfd, I_PUSH, "ttcompat") < 0) { | |
151 dropbear_log(LOG_WARNING, | |
152 "ioctl I_PUSH ttcompat: %.100s", strerror(errno)); | |
153 } | |
154 #endif | |
155 #endif | |
156 return 1; | |
157 #else /* USE_DEV_PTMX */ | |
158 #ifdef HAVE_DEV_PTS_AND_PTC | |
159 /* AIX-style pty code. */ | |
160 const char *name; | |
161 | |
162 *ptyfd = open("/dev/ptc", O_RDWR | O_NOCTTY); | |
163 if (*ptyfd < 0) { | |
164 dropbear_log(LOG_ERR, | |
165 "Could not open /dev/ptc: %.100s", strerror(errno)); | |
166 return 0; | |
167 } | |
168 name = ttyname(*ptyfd); | |
169 if (!name) { | |
170 dropbear_exit("ttyname fails for /dev/ptc device"); | |
171 } | |
172 strlcpy(namebuf, name, namebuflen); | |
173 *ttyfd = open(name, O_RDWR | O_NOCTTY); | |
174 if (*ttyfd < 0) { | |
175 dropbear_log(LOG_ERR, | |
176 "Could not open pty slave side %.100s: %.100s", | |
177 name, strerror(errno)); | |
178 close(*ptyfd); | |
179 return 0; | |
180 } | |
181 return 1; | |
182 #else /* HAVE_DEV_PTS_AND_PTC */ | |
183 | |
184 /* BSD-style pty code. */ | |
185 char buf[64]; | |
186 int i; | |
187 const char *ptymajors = "pqrstuvwxyzabcdefghijklmnoABCDEFGHIJKLMNOPQRSTUVWXYZ"; | |
188 const char *ptyminors = "0123456789abcdef"; | |
189 int num_minors = strlen(ptyminors); | |
190 int num_ptys = strlen(ptymajors) * num_minors; | |
191 struct termios tio; | |
192 | |
193 for (i = 0; i < num_ptys; i++) { | |
194 snprintf(buf, sizeof buf, "/dev/pty%c%c", ptymajors[i / num_minors], | |
195 ptyminors[i % num_minors]); | |
196 snprintf(namebuf, namebuflen, "/dev/tty%c%c", | |
197 ptymajors[i / num_minors], ptyminors[i % num_minors]); | |
198 | |
199 *ptyfd = open(buf, O_RDWR | O_NOCTTY); | |
200 if (*ptyfd < 0) { | |
201 /* Try SCO style naming */ | |
202 snprintf(buf, sizeof buf, "/dev/ptyp%d", i); | |
203 snprintf(namebuf, namebuflen, "/dev/ttyp%d", i); | |
204 *ptyfd = open(buf, O_RDWR | O_NOCTTY); | |
205 if (*ptyfd < 0) { | |
206 continue; | |
207 } | |
208 } | |
209 | |
210 /* Open the slave side. */ | |
211 *ttyfd = open(namebuf, O_RDWR | O_NOCTTY); | |
212 if (*ttyfd < 0) { | |
213 dropbear_log(LOG_ERR, | |
214 "pty_allocate: %.100s: %.100s", namebuf, strerror(errno)); | |
215 close(*ptyfd); | |
216 return 0; | |
217 } | |
218 /* set tty modes to a sane state for broken clients */ | |
219 if (tcgetattr(*ptyfd, &tio) < 0) { | |
220 dropbear_log(LOG_WARNING, | |
221 "ptyallocate: tty modes failed: %.100s", strerror(errno)); | |
222 } else { | |
223 tio.c_lflag |= (ECHO | ISIG | ICANON); | |
224 tio.c_oflag |= (OPOST | ONLCR); | |
225 tio.c_iflag |= ICRNL; | |
226 | |
227 /* Set the new modes for the terminal. */ | |
228 if (tcsetattr(*ptyfd, TCSANOW, &tio) < 0) { | |
229 dropbear_log(LOG_WARNING, | |
230 "Setting tty modes for pty failed: %.100s", | |
231 strerror(errno)); | |
232 } | |
233 } | |
234 | |
235 return 1; | |
236 } | |
237 dropbear_log(LOG_WARNING, "failed to open any /dev/pty?? devices"); | |
238 return 0; | |
239 #endif /* HAVE_DEV_PTS_AND_PTC */ | |
240 #endif /* USE_DEV_PTMX */ | |
241 #endif /* HAVE__GETPTY */ | |
242 #endif /* HAVE_OPENPTY */ | |
243 } | |
244 | |
245 /* Releases the tty. Its ownership is returned to root, and permissions to 0666. */ | |
246 | |
247 void | |
248 pty_release(const char *tty_name) | |
249 { | |
250 if (chown(tty_name, (uid_t) 0, (gid_t) 0) < 0 | |
251 && (errno != ENOENT)) { | |
252 dropbear_log(LOG_ERR, | |
253 "chown %.100s 0 0 failed: %.100s", tty_name, strerror(errno)); | |
254 } | |
255 if (chmod(tty_name, (mode_t) 0666) < 0 | |
256 && (errno != ENOENT)) { | |
257 dropbear_log(LOG_ERR, | |
258 "chmod %.100s 0666 failed: %.100s", tty_name, strerror(errno)); | |
259 } | |
260 } | |
261 | |
262 /* Makes the tty the processes controlling tty and sets it to sane modes. */ | |
263 | |
264 void | |
265 pty_make_controlling_tty(int *ttyfd, const char *tty_name) | |
266 { | |
267 int fd; | |
268 #ifdef USE_VHANGUP | |
269 void *old; | |
270 #endif /* USE_VHANGUP */ | |
271 | |
272 /* Solaris has a problem with TIOCNOTTY for a bg process, so | |
273 * we disable the signal which would STOP the process - matt */ | |
274 signal(SIGTTOU, SIG_IGN); | |
275 | |
276 /* First disconnect from the old controlling tty. */ | |
277 #ifdef TIOCNOTTY | |
278 fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); | |
279 if (fd >= 0) { | |
280 (void) ioctl(fd, TIOCNOTTY, NULL); | |
281 close(fd); | |
282 } | |
283 #endif /* TIOCNOTTY */ | |
284 if (setsid() < 0) { | |
285 dropbear_log(LOG_ERR, | |
286 "setsid: %.100s", strerror(errno)); | |
287 } | |
288 | |
289 /* | |
290 * Verify that we are successfully disconnected from the controlling | |
291 * tty. | |
292 */ | |
293 fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); | |
294 if (fd >= 0) { | |
295 dropbear_log(LOG_ERR, | |
296 "Failed to disconnect from controlling tty.\n"); | |
297 close(fd); | |
298 } | |
299 /* Make it our controlling tty. */ | |
300 #ifdef TIOCSCTTY | |
301 if (ioctl(*ttyfd, TIOCSCTTY, NULL) < 0) { | |
302 dropbear_log(LOG_ERR, | |
303 "ioctl(TIOCSCTTY): %.100s", strerror(errno)); | |
304 } | |
305 #endif /* TIOCSCTTY */ | |
306 #ifdef HAVE_NEWS4 | |
307 if (setpgrp(0,0) < 0) { | |
308 dropbear_log(LOG_ERR, | |
309 error("SETPGRP %s",strerror(errno))); | |
310 } | |
311 #endif /* HAVE_NEWS4 */ | |
312 #ifdef USE_VHANGUP | |
313 old = mysignal(SIGHUP, SIG_IGN); | |
314 vhangup(); | |
315 mysignal(SIGHUP, old); | |
316 #endif /* USE_VHANGUP */ | |
317 fd = open(tty_name, O_RDWR); | |
318 if (fd < 0) { | |
319 dropbear_log(LOG_ERR, | |
320 "%.100s: %.100s", tty_name, strerror(errno)); | |
321 } else { | |
322 #ifdef USE_VHANGUP | |
323 close(*ttyfd); | |
324 *ttyfd = fd; | |
325 #else /* USE_VHANGUP */ | |
326 close(fd); | |
327 #endif /* USE_VHANGUP */ | |
328 } | |
329 /* Verify that we now have a controlling tty. */ | |
330 fd = open(_PATH_TTY, O_WRONLY); | |
331 if (fd < 0) { | |
332 dropbear_log(LOG_ERR, | |
333 "open /dev/tty failed - could not set controlling tty: %.100s", | |
334 strerror(errno)); | |
335 } else { | |
336 close(fd); | |
337 } | |
338 } | |
339 | |
340 /* Changes the window size associated with the pty. */ | |
341 | |
342 void | |
343 pty_change_window_size(int ptyfd, int row, int col, | |
344 int xpixel, int ypixel) | |
345 { | |
346 struct winsize w; | |
347 | |
348 w.ws_row = row; | |
349 w.ws_col = col; | |
350 w.ws_xpixel = xpixel; | |
351 w.ws_ypixel = ypixel; | |
352 (void) ioctl(ptyfd, TIOCSWINSZ, &w); | |
353 } | |
354 | |
355 void | |
356 pty_setowner(struct passwd *pw, const char *tty_name) | |
357 { | |
358 struct group *grp; | |
359 gid_t gid; | |
360 mode_t mode; | |
361 struct stat st; | |
362 | |
363 /* Determine the group to make the owner of the tty. */ | |
364 grp = getgrnam("tty"); | |
365 if (grp) { | |
366 gid = grp->gr_gid; | |
367 mode = S_IRUSR | S_IWUSR | S_IWGRP; | |
368 } else { | |
369 gid = pw->pw_gid; | |
370 mode = S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH; | |
371 } | |
372 | |
373 /* | |
374 * Change owner and mode of the tty as required. | |
375 * Warn but continue if filesystem is read-only and the uids match/ | |
376 * tty is owned by root. | |
377 */ | |
378 if (stat(tty_name, &st)) { | |
379 dropbear_exit("pty_setowner: stat(%.101s) failed: %.100s", | |
380 tty_name, strerror(errno)); | |
381 } | |
382 | |
383 if (st.st_uid != pw->pw_uid || st.st_gid != gid) { | |
384 if (chown(tty_name, pw->pw_uid, gid) < 0) { | |
385 if (errno == EROFS && | |
386 (st.st_uid == pw->pw_uid || st.st_uid == 0)) { | |
387 dropbear_log(LOG_ERR, | |
388 "chown(%.100s, %u, %u) failed: %.100s", | |
389 tty_name, (unsigned int)pw->pw_uid, (unsigned int)gid, | |
390 strerror(errno)); | |
391 } else { | |
392 dropbear_exit("chown(%.100s, %u, %u) failed: %.100s", | |
393 tty_name, (unsigned int)pw->pw_uid, (unsigned int)gid, | |
394 strerror(errno)); | |
395 } | |
396 } | |
397 } | |
398 | |
399 if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) { | |
400 if (chmod(tty_name, mode) < 0) { | |
401 if (errno == EROFS && | |
402 (st.st_mode & (S_IRGRP | S_IROTH)) == 0) { | |
403 dropbear_log(LOG_ERR, | |
404 "chmod(%.100s, 0%o) failed: %.100s", | |
405 tty_name, mode, strerror(errno)); | |
406 } else { | |
407 dropbear_exit("chmod(%.100s, 0%o) failed: %.100s", | |
408 tty_name, mode, strerror(errno)); | |
409 } | |
410 } | |
411 } | |
412 } |