source: trunk/third/openssh/sshpty.c @ 18759

Revision 18759, 10.6 KB checked in by zacheiss, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18758, which included commits to RCS files with non-trunk default branches.
Line 
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"
15RCSID("$OpenBSD: sshpty.c,v 1.7 2002/06/24 17:57:20 deraadt Exp $");
16
17#ifdef HAVE_UTIL_H
18# include <util.h>
19#endif /* HAVE_UTIL_H */
20
21#include "sshpty.h"
22#include "log.h"
23#include "misc.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(HAVE_DEV_PTMX) && defined(HAVE_SYS_STROPTS_H)
34# include <sys/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
48int
49pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, int namebuflen)
50{
51#if defined(HAVE_OPENPTY) || defined(BSD4_4)
52        /* openpty(3) exists in OSF/1 and some other os'es */
53        char *name;
54        int i;
55
56        i = openpty(ptyfd, ttyfd, NULL, NULL, NULL);
57        if (i < 0) {
58                error("openpty: %.100s", strerror(errno));
59                return 0;
60        }
61        name = ttyname(*ttyfd);
62        if (!name)
63                fatal("openpty returns device for which ttyname fails.");
64
65        strlcpy(namebuf, name, namebuflen);     /* possible truncation */
66        return 1;
67#else /* HAVE_OPENPTY */
68#ifdef HAVE__GETPTY
69        /*
70         * _getpty(3) exists in SGI Irix 4.x, 5.x & 6.x -- it generates more
71         * pty's automagically when needed
72         */
73        char *slave;
74
75        slave = _getpty(ptyfd, O_RDWR, 0622, 0);
76        if (slave == NULL) {
77                error("_getpty: %.100s", strerror(errno));
78                return 0;
79        }
80        strlcpy(namebuf, slave, namebuflen);
81        /* Open the slave side. */
82        *ttyfd = open(namebuf, O_RDWR | O_NOCTTY);
83        if (*ttyfd < 0) {
84                error("%.200s: %.100s", namebuf, strerror(errno));
85                close(*ptyfd);
86                return 0;
87        }
88        return 1;
89#else /* HAVE__GETPTY */
90#if defined(HAVE_DEV_PTMX)
91        /*
92         * This code is used e.g. on Solaris 2.x.  (Note that Solaris 2.3
93         * also has bsd-style ptys, but they simply do not work.)
94         */
95        int ptm;
96        char *pts;
97        mysig_t old_signal;
98
99        ptm = open("/dev/ptmx", O_RDWR | O_NOCTTY);
100        if (ptm < 0) {
101                error("/dev/ptmx: %.100s", strerror(errno));
102                return 0;
103        }
104        old_signal = mysignal(SIGCHLD, SIG_DFL);
105        if (grantpt(ptm) < 0) {
106                error("grantpt: %.100s", strerror(errno));
107                return 0;
108        }
109        mysignal(SIGCHLD, old_signal);
110        if (unlockpt(ptm) < 0) {
111                error("unlockpt: %.100s", strerror(errno));
112                return 0;
113        }
114        pts = ptsname(ptm);
115        if (pts == NULL)
116                error("Slave pty side name could not be obtained.");
117        strlcpy(namebuf, pts, namebuflen);
118        *ptyfd = ptm;
119
120        /* Open the slave side. */
121        *ttyfd = open(namebuf, O_RDWR | O_NOCTTY);
122        if (*ttyfd < 0) {
123                error("%.100s: %.100s", namebuf, strerror(errno));
124                close(*ptyfd);
125                return 0;
126        }
127#ifndef HAVE_CYGWIN
128        /*
129         * Push the appropriate streams modules, as described in Solaris pts(7).
130         * HP-UX pts(7) doesn't have ttcompat module.
131         */
132        if (ioctl(*ttyfd, I_PUSH, "ptem") < 0)
133                error("ioctl I_PUSH ptem: %.100s", strerror(errno));
134        if (ioctl(*ttyfd, I_PUSH, "ldterm") < 0)
135                error("ioctl I_PUSH ldterm: %.100s", strerror(errno));
136#ifndef __hpux
137        if (ioctl(*ttyfd, I_PUSH, "ttcompat") < 0)
138                error("ioctl I_PUSH ttcompat: %.100s", strerror(errno));
139#endif
140#endif
141        return 1;
142#else /* HAVE_DEV_PTMX */
143#ifdef HAVE_DEV_PTS_AND_PTC
144        /* AIX-style pty code. */
145        const char *name;
146
147        *ptyfd = open("/dev/ptc", O_RDWR | O_NOCTTY);
148        if (*ptyfd < 0) {
149                error("Could not open /dev/ptc: %.100s", strerror(errno));
150                return 0;
151        }
152        name = ttyname(*ptyfd);
153        if (!name)
154                fatal("Open of /dev/ptc returns device for which ttyname fails.");
155        strlcpy(namebuf, name, namebuflen);
156        *ttyfd = open(name, O_RDWR | O_NOCTTY);
157        if (*ttyfd < 0) {
158                error("Could not open pty slave side %.100s: %.100s",
159                    name, strerror(errno));
160                close(*ptyfd);
161                return 0;
162        }
163        return 1;
164#else /* HAVE_DEV_PTS_AND_PTC */
165#ifdef _UNICOS
166        char buf[64];
167        int i;
168        int highpty;
169
170#ifdef _SC_CRAY_NPTY
171        highpty = sysconf(_SC_CRAY_NPTY);
172        if (highpty == -1)
173                highpty = 128;
174#else
175        highpty = 128;
176#endif
177
178        for (i = 0; i < highpty; i++) {
179                snprintf(buf, sizeof(buf), "/dev/pty/%03d", i);
180                *ptyfd = open(buf, O_RDWR|O_NOCTTY);
181                if (*ptyfd < 0)
182                        continue;
183                snprintf(namebuf, namebuflen, "/dev/ttyp%03d", i);
184                /* Open the slave side. */
185                *ttyfd = open(namebuf, O_RDWR|O_NOCTTY);
186                if (*ttyfd < 0) {
187                        error("%.100s: %.100s", namebuf, strerror(errno));
188                        close(*ptyfd);
189                        return 0;
190                }
191                return 1;
192        }
193        return 0;
194#else
195        /* BSD-style pty code. */
196        char buf[64];
197        int i;
198        const char *ptymajors = "pqrstuvwxyzabcdefghijklmnoABCDEFGHIJKLMNOPQRSTUVWXYZ";
199        const char *ptyminors = "0123456789abcdef";
200        int num_minors = strlen(ptyminors);
201        int num_ptys = strlen(ptymajors) * num_minors;
202        struct termios tio;
203
204        for (i = 0; i < num_ptys; i++) {
205                snprintf(buf, sizeof buf, "/dev/pty%c%c", ptymajors[i / num_minors],
206                         ptyminors[i % num_minors]);
207                snprintf(namebuf, namebuflen, "/dev/tty%c%c",
208                    ptymajors[i / num_minors], ptyminors[i % num_minors]);
209
210                *ptyfd = open(buf, O_RDWR | O_NOCTTY);
211                if (*ptyfd < 0) {
212                        /* Try SCO style naming */
213                        snprintf(buf, sizeof buf, "/dev/ptyp%d", i);
214                        snprintf(namebuf, namebuflen, "/dev/ttyp%d", i);
215                        *ptyfd = open(buf, O_RDWR | O_NOCTTY);
216                        if (*ptyfd < 0)
217                                continue;
218                }
219
220                /* Open the slave side. */
221                *ttyfd = open(namebuf, O_RDWR | O_NOCTTY);
222                if (*ttyfd < 0) {
223                        error("%.100s: %.100s", namebuf, strerror(errno));
224                        close(*ptyfd);
225                        return 0;
226                }
227                /* set tty modes to a sane state for broken clients */
228                if (tcgetattr(*ptyfd, &tio) < 0)
229                        log("Getting tty modes for pty failed: %.100s", strerror(errno));
230                else {
231                        tio.c_lflag |= (ECHO | ISIG | ICANON);
232                        tio.c_oflag |= (OPOST | ONLCR);
233                        tio.c_iflag |= ICRNL;
234
235                        /* Set the new modes for the terminal. */
236                        if (tcsetattr(*ptyfd, TCSANOW, &tio) < 0)
237                                log("Setting tty modes for pty failed: %.100s", strerror(errno));
238                }
239
240                return 1;
241        }
242        return 0;
243#endif /* CRAY */
244#endif /* HAVE_DEV_PTS_AND_PTC */
245#endif /* HAVE_DEV_PTMX */
246#endif /* HAVE__GETPTY */
247#endif /* HAVE_OPENPTY */
248}
249
250/* Releases the tty.  Its ownership is returned to root, and permissions to 0666. */
251
252void
253pty_release(const char *ttyname)
254{
255        if (chown(ttyname, (uid_t) 0, (gid_t) 0) < 0)
256                error("chown %.100s 0 0 failed: %.100s", ttyname, strerror(errno));
257        if (chmod(ttyname, (mode_t) 0666) < 0)
258                error("chmod %.100s 0666 failed: %.100s", ttyname, strerror(errno));
259}
260
261/* Makes the tty the processes controlling tty and sets it to sane modes. */
262
263void
264pty_make_controlling_tty(int *ttyfd, const char *ttyname)
265{
266        int fd;
267#ifdef USE_VHANGUP
268        void *old;
269#endif /* USE_VHANGUP */
270
271#ifdef _UNICOS
272        if (setsid() < 0)
273                error("setsid: %.100s", strerror(errno));
274
275        fd = open(ttyname, O_RDWR|O_NOCTTY);
276        if (fd != -1) {
277                mysignal(SIGHUP, SIG_IGN);
278                ioctl(fd, TCVHUP, (char *)NULL);
279                mysignal(SIGHUP, SIG_DFL);
280                setpgid(0, 0);
281                close(fd);
282        } else {
283                error("Failed to disconnect from controlling tty.");
284        }
285
286        debug("Setting controlling tty using TCSETCTTY.");
287        ioctl(*ttyfd, TCSETCTTY, NULL);
288        fd = open("/dev/tty", O_RDWR);
289        if (fd < 0)
290                error("%.100s: %.100s", ttyname, strerror(errno));
291        close(*ttyfd);
292        *ttyfd = fd;
293#else /* _UNICOS */
294
295        /* First disconnect from the old controlling tty. */
296#ifdef TIOCNOTTY
297        fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
298        if (fd >= 0) {
299                (void) ioctl(fd, TIOCNOTTY, NULL);
300                close(fd);
301        }
302#endif /* TIOCNOTTY */
303        if (setsid() < 0)
304                error("setsid: %.100s", strerror(errno));
305
306        /*
307         * Verify that we are successfully disconnected from the controlling
308         * tty.
309         */
310        fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
311        if (fd >= 0) {
312                error("Failed to disconnect from controlling tty.");
313                close(fd);
314        }
315        /* Make it our controlling tty. */
316#ifdef TIOCSCTTY
317        debug("Setting controlling tty using TIOCSCTTY.");
318        if (ioctl(*ttyfd, TIOCSCTTY, NULL) < 0)
319                error("ioctl(TIOCSCTTY): %.100s", strerror(errno));
320#endif /* TIOCSCTTY */
321#ifdef HAVE_NEWS4
322        if (setpgrp(0,0) < 0)
323                error("SETPGRP %s",strerror(errno));
324#endif /* HAVE_NEWS4 */
325#ifdef USE_VHANGUP
326        old = mysignal(SIGHUP, SIG_IGN);
327        vhangup();
328        mysignal(SIGHUP, old);
329#endif /* USE_VHANGUP */
330        fd = open(ttyname, O_RDWR);
331        if (fd < 0) {
332                error("%.100s: %.100s", ttyname, strerror(errno));
333        } else {
334#ifdef USE_VHANGUP
335                close(*ttyfd);
336                *ttyfd = fd;
337#else /* USE_VHANGUP */
338                close(fd);
339#endif /* USE_VHANGUP */
340        }
341        /* Verify that we now have a controlling tty. */
342        fd = open(_PATH_TTY, O_WRONLY);
343        if (fd < 0)
344                error("open /dev/tty failed - could not set controlling tty: %.100s",
345                    strerror(errno));
346        else
347                close(fd);
348#endif /* _UNICOS */
349}
350
351/* Changes the window size associated with the pty. */
352
353void
354pty_change_window_size(int ptyfd, int row, int col,
355        int xpixel, int ypixel)
356{
357        struct winsize w;
358
359        w.ws_row = row;
360        w.ws_col = col;
361        w.ws_xpixel = xpixel;
362        w.ws_ypixel = ypixel;
363        (void) ioctl(ptyfd, TIOCSWINSZ, &w);
364}
365
366void
367pty_setowner(struct passwd *pw, const char *ttyname)
368{
369        struct group *grp;
370        gid_t gid;
371        mode_t mode;
372        struct stat st;
373
374        /* Determine the group to make the owner of the tty. */
375        grp = getgrnam("tty");
376        if (grp) {
377                gid = grp->gr_gid;
378                mode = S_IRUSR | S_IWUSR | S_IWGRP;
379        } else {
380                gid = pw->pw_gid;
381                mode = S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH;
382        }
383
384        /*
385         * Change owner and mode of the tty as required.
386         * Warn but continue if filesystem is read-only and the uids match/
387         * tty is owned by root.
388         */
389        if (stat(ttyname, &st))
390                fatal("stat(%.100s) failed: %.100s", ttyname,
391                    strerror(errno));
392
393        if (st.st_uid != pw->pw_uid || st.st_gid != gid) {
394                if (chown(ttyname, pw->pw_uid, gid) < 0) {
395                        if (errno == EROFS &&
396                            (st.st_uid == pw->pw_uid || st.st_uid == 0))
397                                error("chown(%.100s, %u, %u) failed: %.100s",
398                                    ttyname, (u_int)pw->pw_uid, (u_int)gid,
399                                    strerror(errno));
400                        else
401                                fatal("chown(%.100s, %u, %u) failed: %.100s",
402                                    ttyname, (u_int)pw->pw_uid, (u_int)gid,
403                                    strerror(errno));
404                }
405        }
406
407        if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) {
408                if (chmod(ttyname, mode) < 0) {
409                        if (errno == EROFS &&
410                            (st.st_mode & (S_IRGRP | S_IROTH)) == 0)
411                                error("chmod(%.100s, 0%o) failed: %.100s",
412                                    ttyname, mode, strerror(errno));
413                        else
414                                fatal("chmod(%.100s, 0%o) failed: %.100s",
415                                    ttyname, mode, strerror(errno));
416                }
417        }
418}
Note: See TracBrowser for help on using the repository browser.