2 * Dropbear - a SSH2 server
4 * Copied from OpenSSH-3.5p1 source, modified by Matt Johnston 2003
6 * Author: Tatu Ylonen <ylo@cs.hut.fi>
7 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
9 * Allocating a pseudo-terminal, and making it the controlling tty.
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".
18 /*RCSID("OpenBSD: sshpty.c,v 1.7 2002/06/24 17:57:20 deraadt Exp ");*/
25 /* Pty allocated with _getpty gets broken if we do I_PUSH:es to it. */
26 #if defined(HAVE__GETPTY) || defined(HAVE_OPENPTY)
33 #if defined(USE_DEV_PTMX) && defined(HAVE_STROPTS_H)
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).
49 pty_allocate(int *ptyfd
, int *ttyfd
, char *namebuf
, int namebuflen
)
51 #if defined(HAVE_OPENPTY)
52 /* exists in recent (4.4) BSDs and OSF/1 */
56 i
= openpty(ptyfd
, ttyfd
, NULL
, NULL
, NULL
);
58 dropbear_log(LOG_WARNING
,
59 "pty_allocate: openpty: %.100s", strerror(errno
));
62 name
= ttyname(*ttyfd
);
64 dropbear_exit("ttyname fails for openpty device");
67 strlcpy(namebuf
, name
, namebuflen
); /* possible truncation */
69 #else /* HAVE_OPENPTY */
72 * _getpty(3) exists in SGI Irix 4.x, 5.x & 6.x -- it generates more
73 * pty's automagically when needed
77 slave
= _getpty(ptyfd
, O_RDWR
, 0622, 0);
79 dropbear_log(LOG_WARNING
,
80 "pty_allocate: _getpty: %.100s", strerror(errno
));
83 strlcpy(namebuf
, slave
, namebuflen
);
84 /* Open the slave side. */
85 *ttyfd
= open(namebuf
, O_RDWR
| O_NOCTTY
);
87 dropbear_log(LOG_WARNING
,
88 "pty_allocate error: ttyftd open error");
93 #else /* HAVE__GETPTY */
94 #if defined(USE_DEV_PTMX)
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.)
99 * Linux systems may have the /dev/ptmx device, but this code won't work.
104 ptm
= open("/dev/ptmx", O_RDWR
| O_NOCTTY
);
106 dropbear_log(LOG_WARNING
,
107 "pty_allocate: /dev/ptmx: %.100s", strerror(errno
));
110 if (grantpt(ptm
) < 0) {
111 dropbear_log(LOG_WARNING
,
112 "grantpt: %.100s", strerror(errno
));
115 if (unlockpt(ptm
) < 0) {
116 dropbear_log(LOG_WARNING
,
117 "unlockpt: %.100s", strerror(errno
));
122 dropbear_log(LOG_WARNING
,
123 "Slave pty side name could not be obtained.");
125 strlcpy(namebuf
, pts
, namebuflen
);
128 /* Open the slave side. */
129 *ttyfd
= open(namebuf
, O_RDWR
| O_NOCTTY
);
131 dropbear_log(LOG_ERR
,
132 "error opening pts %.100s: %.100s", namebuf
, strerror(errno
));
136 #if !defined(HAVE_CYGWIN) && defined(I_PUSH)
138 * Push the appropriate streams modules, as described in Solaris pts(7).
139 * HP-UX pts(7) doesn't have ttcompat module.
141 if (ioctl(*ttyfd
, I_PUSH
, "ptem") < 0) {
142 dropbear_log(LOG_WARNING
,
143 "ioctl I_PUSH ptem: %.100s", strerror(errno
));
145 if (ioctl(*ttyfd
, I_PUSH
, "ldterm") < 0) {
146 dropbear_log(LOG_WARNING
,
147 "ioctl I_PUSH ldterm: %.100s", strerror(errno
));
150 if (ioctl(*ttyfd
, I_PUSH
, "ttcompat") < 0) {
151 dropbear_log(LOG_WARNING
,
152 "ioctl I_PUSH ttcompat: %.100s", strerror(errno
));
157 #else /* USE_DEV_PTMX */
158 #ifdef HAVE_DEV_PTS_AND_PTC
159 /* AIX-style pty code. */
162 *ptyfd
= open("/dev/ptc", O_RDWR
| O_NOCTTY
);
164 dropbear_log(LOG_ERR
,
165 "Could not open /dev/ptc: %.100s", strerror(errno
));
168 name
= ttyname(*ptyfd
);
170 dropbear_exit("ttyname fails for /dev/ptc device");
172 strlcpy(namebuf
, name
, namebuflen
);
173 *ttyfd
= open(name
, O_RDWR
| O_NOCTTY
);
175 dropbear_log(LOG_ERR
,
176 "Could not open pty slave side %.100s: %.100s",
177 name
, strerror(errno
));
182 #else /* HAVE_DEV_PTS_AND_PTC */
184 /* BSD-style pty code. */
187 const char *ptymajors
= "pqrstuvwxyzabcdefghijklmnoABCDEFGHIJKLMNOPQRSTUVWXYZ";
188 const char *ptyminors
= "0123456789abcdef";
189 int num_minors
= strlen(ptyminors
);
190 int num_ptys
= strlen(ptymajors
) * num_minors
;
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
]);
199 *ptyfd
= open(buf
, O_RDWR
| O_NOCTTY
);
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
);
210 /* Open the slave side. */
211 *ttyfd
= open(namebuf
, O_RDWR
| O_NOCTTY
);
213 dropbear_log(LOG_ERR
,
214 "pty_allocate: %.100s: %.100s", namebuf
, strerror(errno
));
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
));
223 tio
.c_lflag
|= (ECHO
| ISIG
| ICANON
);
224 tio
.c_oflag
|= (OPOST
| ONLCR
);
225 tio
.c_iflag
|= ICRNL
;
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",
237 dropbear_log(LOG_WARNING
, "Failed to open any /dev/pty?? devices");
239 #endif /* HAVE_DEV_PTS_AND_PTC */
240 #endif /* USE_DEV_PTMX */
241 #endif /* HAVE__GETPTY */
242 #endif /* HAVE_OPENPTY */
245 /* Releases the tty. Its ownership is returned to root, and permissions to 0666. */
248 pty_release(const char *tty_name
)
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
));
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
));
262 /* Makes the tty the processes controlling tty and sets it to sane modes. */
265 pty_make_controlling_tty(int *ttyfd
, const char *tty_name
)
270 #endif /* USE_VHANGUP */
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
);
276 /* First disconnect from the old controlling tty. */
278 fd
= open(_PATH_TTY
, O_RDWR
| O_NOCTTY
);
280 (void) ioctl(fd
, TIOCNOTTY
, NULL
);
283 #endif /* TIOCNOTTY */
285 dropbear_log(LOG_ERR
,
286 "setsid: %.100s", strerror(errno
));
290 * Verify that we are successfully disconnected from the controlling
293 fd
= open(_PATH_TTY
, O_RDWR
| O_NOCTTY
);
295 dropbear_log(LOG_ERR
,
296 "Failed to disconnect from controlling tty.\n");
299 /* Make it our controlling tty. */
301 if (ioctl(*ttyfd
, TIOCSCTTY
, NULL
) < 0) {
302 dropbear_log(LOG_ERR
,
303 "ioctl(TIOCSCTTY): %.100s", strerror(errno
));
305 #endif /* TIOCSCTTY */
307 if (setpgrp(0,0) < 0) {
308 dropbear_log(LOG_ERR
,
309 error("SETPGRP %s",strerror(errno
)));
311 #endif /* HAVE_NEWS4 */
313 old
= mysignal(SIGHUP
, SIG_IGN
);
315 mysignal(SIGHUP
, old
);
316 #endif /* USE_VHANGUP */
317 fd
= open(tty_name
, O_RDWR
);
319 dropbear_log(LOG_ERR
,
320 "%.100s: %.100s", tty_name
, strerror(errno
));
325 #else /* USE_VHANGUP */
327 #endif /* USE_VHANGUP */
329 /* Verify that we now have a controlling tty. */
330 fd
= open(_PATH_TTY
, O_WRONLY
);
332 dropbear_log(LOG_ERR
,
333 "open /dev/tty failed - could not set controlling tty: %.100s",
340 /* Changes the window size associated with the pty. */
343 pty_change_window_size(int ptyfd
, int row
, int col
,
344 int xpixel
, int ypixel
)
350 w
.ws_xpixel
= xpixel
;
351 w
.ws_ypixel
= ypixel
;
352 (void) ioctl(ptyfd
, TIOCSWINSZ
, &w
);
356 pty_setowner(struct passwd
*pw
, const char *tty_name
)
363 /* Determine the group to make the owner of the tty. */
364 grp
= getgrnam("tty");
367 mode
= S_IRUSR
| S_IWUSR
| S_IWGRP
;
370 mode
= S_IRUSR
| S_IWUSR
| S_IWGRP
| S_IWOTH
;
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.
378 if (stat(tty_name
, &st
)) {
379 dropbear_exit("pty_setowner: stat(%.101s) failed: %.100s",
380 tty_name
, strerror(errno
));
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
,
392 dropbear_exit("chown(%.100s, %u, %u) failed: %.100s",
393 tty_name
, (unsigned int)pw
->pw_uid
, (unsigned int)gid
,
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
));
407 dropbear_exit("chmod(%.100s, 0%o) failed: %.100s",
408 tty_name
, mode
, strerror(errno
));