]> andersk Git - openssh.git/blob - pty.c
83b219b904685a89549696233ff143bdd09da433
[openssh.git] / pty.c
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: pty.c,v 1.20 2001/01/21 19:05:53 markus Exp $");
16
17 #ifdef HAVE_UTIL_H
18 # include <util.h>
19 #endif /* HAVE_UTIL_H */
20
21 #include "pty.h"
22 #include "log.h"
23
24 /* Pty allocated with _getpty gets broken if we do I_PUSH:es to it. */
25 #if defined(HAVE__GETPTY) || defined(HAVE_OPENPTY)
26 #undef HAVE_DEV_PTMX
27 #endif
28
29 #ifdef HAVE_PTY_H
30 # include <pty.h>
31 #endif
32 #if defined(HAVE_DEV_PTMX) && defined(HAVE_SYS_STROPTS_H)
33 # include <sys/stropts.h>
34 #endif
35
36 #ifndef O_NOCTTY
37 #define O_NOCTTY 0
38 #endif
39
40 /*
41  * Allocates and opens a pty.  Returns 0 if no pty could be allocated, or
42  * nonzero if a pty was successfully allocated.  On success, open file
43  * descriptors for the pty and tty sides and the name of the tty side are
44  * returned (the buffer must be able to hold at least 64 characters).
45  */
46
47 int
48 pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, int namebuflen)
49 {
50 #if defined(HAVE_OPENPTY) || defined(BSD4_4)
51         /* openpty(3) exists in OSF/1 and some other os'es */
52         char buf[64];
53         int i;
54
55         i = openpty(ptyfd, ttyfd, buf, NULL, NULL);
56         if (i < 0) {
57                 error("openpty: %.100s", strerror(errno));
58                 return 0;
59         }
60         strlcpy(namebuf, buf, namebuflen);      /* possible truncation */
61         return 1;
62 #else /* HAVE_OPENPTY */
63 #ifdef HAVE__GETPTY
64         /*
65          * _getpty(3) exists in SGI Irix 4.x, 5.x & 6.x -- it generates more
66          * pty's automagically when needed
67          */
68         char *slave;
69
70         slave = _getpty(ptyfd, O_RDWR, 0622, 0);
71         if (slave == NULL) {
72                 error("_getpty: %.100s", strerror(errno));
73                 return 0;
74         }
75         strlcpy(namebuf, slave, namebuflen);
76         /* Open the slave side. */
77         *ttyfd = open(namebuf, O_RDWR | O_NOCTTY);
78         if (*ttyfd < 0) {
79                 error("%.200s: %.100s", namebuf, strerror(errno));
80                 close(*ptyfd);
81                 return 0;
82         }
83         return 1;
84 #else /* HAVE__GETPTY */
85 #if defined(HAVE_DEV_PTMX)
86         /*
87          * This code is used e.g. on Solaris 2.x.  (Note that Solaris 2.3
88          * also has bsd-style ptys, but they simply do not work.)
89          */
90         int ptm;
91         char *pts;
92
93         ptm = open("/dev/ptmx", O_RDWR | O_NOCTTY);
94         if (ptm < 0) {
95                 error("/dev/ptmx: %.100s", strerror(errno));
96                 return 0;
97         }
98         if (grantpt(ptm) < 0) {
99                 error("grantpt: %.100s", strerror(errno));
100                 return 0;
101         }
102         if (unlockpt(ptm) < 0) {
103                 error("unlockpt: %.100s", strerror(errno));
104                 return 0;
105         }
106         pts = ptsname(ptm);
107         if (pts == NULL)
108                 error("Slave pty side name could not be obtained.");
109         strlcpy(namebuf, pts, namebuflen);
110         *ptyfd = ptm;
111
112         /* Open the slave side. */
113         *ttyfd = open(namebuf, O_RDWR | O_NOCTTY);
114         if (*ttyfd < 0) {
115                 error("%.100s: %.100s", namebuf, strerror(errno));
116                 close(*ptyfd);
117                 return 0;
118         }
119 #ifndef HAVE_CYGWIN
120         /*
121          * Push the appropriate streams modules, as described in Solaris pts(7).
122          * HP-UX pts(7) doesn't have ttcompat module.
123          */
124         if (ioctl(*ttyfd, I_PUSH, "ptem") < 0)
125                 error("ioctl I_PUSH ptem: %.100s", strerror(errno));
126         if (ioctl(*ttyfd, I_PUSH, "ldterm") < 0)
127                 error("ioctl I_PUSH ldterm: %.100s", strerror(errno));
128 #ifndef __hpux
129         if (ioctl(*ttyfd, I_PUSH, "ttcompat") < 0)
130                 error("ioctl I_PUSH ttcompat: %.100s", strerror(errno));
131 #endif
132 #endif
133         return 1;
134 #else /* HAVE_DEV_PTMX */
135 #ifdef HAVE_DEV_PTS_AND_PTC
136         /* AIX-style pty code. */
137         const char *name;
138
139         *ptyfd = open("/dev/ptc", O_RDWR | O_NOCTTY);
140         if (*ptyfd < 0) {
141                 error("Could not open /dev/ptc: %.100s", strerror(errno));
142                 return 0;
143         }
144         name = ttyname(*ptyfd);
145         if (!name)
146                 fatal("Open of /dev/ptc returns device for which ttyname fails.");
147         strlcpy(namebuf, name, namebuflen);
148         *ttyfd = open(name, O_RDWR | O_NOCTTY);
149         if (*ttyfd < 0) {
150                 error("Could not open pty slave side %.100s: %.100s",
151                       name, strerror(errno));
152                 close(*ptyfd);
153                 return 0;
154         }
155         return 1;
156 #else /* HAVE_DEV_PTS_AND_PTC */
157         /* BSD-style pty code. */
158         char buf[64];
159         int i;
160         const char *ptymajors = "pqrstuvwxyzabcdefghijklmnoABCDEFGHIJKLMNOPQRSTUVWXYZ";
161         const char *ptyminors = "0123456789abcdef";
162         int num_minors = strlen(ptyminors);
163         int num_ptys = strlen(ptymajors) * num_minors;
164
165         for (i = 0; i < num_ptys; i++) {
166                 snprintf(buf, sizeof buf, "/dev/pty%c%c", ptymajors[i / num_minors],
167                          ptyminors[i % num_minors]);
168                 snprintf(namebuf, namebuflen, "/dev/tty%c%c",
169                     ptymajors[i / num_minors], ptyminors[i % num_minors]);
170
171                 *ptyfd = open(buf, O_RDWR | O_NOCTTY);
172                 if (*ptyfd < 0) {
173                         /* Try SCO style naming */
174                         snprintf(buf, sizeof buf, "/dev/ptyp%d", i);
175                         snprintf(namebuf, namebuflen, "/dev/ttyp%d", i);
176                         *ptyfd = open(buf, O_RDWR | O_NOCTTY);
177                         if (*ptyfd < 0)
178                                 continue;
179                 }
180
181                 /* Open the slave side. */
182                 *ttyfd = open(namebuf, O_RDWR | O_NOCTTY);
183                 if (*ttyfd < 0) {
184                         error("%.100s: %.100s", namebuf, strerror(errno));
185                         close(*ptyfd);
186                         return 0;
187                 }
188                 return 1;
189         }
190         return 0;
191 #endif /* HAVE_DEV_PTS_AND_PTC */
192 #endif /* HAVE_DEV_PTMX */
193 #endif /* HAVE__GETPTY */
194 #endif /* HAVE_OPENPTY */
195 }
196
197 /* Releases the tty.  Its ownership is returned to root, and permissions to 0666. */
198
199 void
200 pty_release(const char *ttyname)
201 {
202         if (chown(ttyname, (uid_t) 0, (gid_t) 0) < 0)
203                 error("chown %.100s 0 0 failed: %.100s", ttyname, strerror(errno));
204         if (chmod(ttyname, (mode_t) 0666) < 0)
205                 error("chmod %.100s 0666 failed: %.100s", ttyname, strerror(errno));
206 }
207
208 /* Makes the tty the processes controlling tty and sets it to sane modes. */
209
210 void
211 pty_make_controlling_tty(int *ttyfd, const char *ttyname)
212 {
213         int fd;
214 #ifdef USE_VHANGUP
215         void *old;
216 #endif /* USE_VHANGUP */
217
218         /* First disconnect from the old controlling tty. */
219 #ifdef TIOCNOTTY
220         fd = open("/dev/tty", O_RDWR | O_NOCTTY);
221         if (fd >= 0) {
222                 (void) ioctl(fd, TIOCNOTTY, NULL);
223                 close(fd);
224         }
225 #endif /* TIOCNOTTY */
226         if (setsid() < 0)
227                 error("setsid: %.100s", strerror(errno));
228
229         /*
230          * Verify that we are successfully disconnected from the controlling
231          * tty.
232          */
233         fd = open("/dev/tty", O_RDWR | O_NOCTTY);
234         if (fd >= 0) {
235                 error("Failed to disconnect from controlling tty.");
236                 close(fd);
237         }
238         /* Make it our controlling tty. */
239 #ifdef TIOCSCTTY
240         debug("Setting controlling tty using TIOCSCTTY.");
241         if (ioctl(*ttyfd, TIOCSCTTY, NULL) < 0)
242                 error("ioctl(TIOCSCTTY): %.100s", strerror(errno));
243 #endif /* TIOCSCTTY */
244 #ifdef HAVE_NEWS4
245         if (setpgrp(0,0) < 0)
246                 error("SETPGRP %s",strerror(errno));
247 #endif /* HAVE_NEWS4 */
248 #ifdef USE_VHANGUP
249         old = signal(SIGHUP, SIG_IGN);
250         vhangup();
251         signal(SIGHUP, old);
252 #endif /* USE_VHANGUP */
253         fd = open(ttyname, O_RDWR);
254         if (fd < 0) {
255                 error("%.100s: %.100s", ttyname, strerror(errno));
256         } else {
257 #ifdef USE_VHANGUP
258                 close(*ttyfd);
259                 *ttyfd = fd;
260 #else /* USE_VHANGUP */
261                 close(fd);
262 #endif /* USE_VHANGUP */
263         }
264         /* Verify that we now have a controlling tty. */
265         fd = open("/dev/tty", O_WRONLY);
266         if (fd < 0)
267                 error("open /dev/tty failed - could not set controlling tty: %.100s",
268                       strerror(errno));
269         else {
270                 close(fd);
271         }
272 }
273
274 /* Changes the window size associated with the pty. */
275
276 void
277 pty_change_window_size(int ptyfd, int row, int col,
278                        int xpixel, int ypixel)
279 {
280         struct winsize w;
281         w.ws_row = row;
282         w.ws_col = col;
283         w.ws_xpixel = xpixel;
284         w.ws_ypixel = ypixel;
285         (void) ioctl(ptyfd, TIOCSWINSZ, &w);
286 }
287
288 void
289 pty_setowner(struct passwd *pw, const char *ttyname)
290 {
291         struct group *grp;
292         gid_t gid;
293         mode_t mode;
294         struct stat st;
295
296         /* Determine the group to make the owner of the tty. */
297         grp = getgrnam("tty");
298         if (grp) {
299                 gid = grp->gr_gid;
300                 mode = S_IRUSR | S_IWUSR | S_IWGRP;
301         } else {
302                 gid = pw->pw_gid;
303                 mode = S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH;
304         }
305
306         /*
307          * Change owner and mode of the tty as required.
308          * Warn but continue if filesystem is read-only and the uids match.
309          */
310         if (stat(ttyname, &st))
311                 fatal("stat(%.100s) failed: %.100s", ttyname,
312                     strerror(errno));
313
314         if (st.st_uid != pw->pw_uid || st.st_gid != gid) {
315                 if (chown(ttyname, pw->pw_uid, gid) < 0) {
316                         if (errno == EROFS && st.st_uid == pw->pw_uid)
317                                 error("chown(%.100s, %d, %d) failed: %.100s",
318                                       ttyname, pw->pw_uid, gid,
319                                       strerror(errno));
320                         else
321                                 fatal("chown(%.100s, %d, %d) failed: %.100s",
322                                       ttyname, pw->pw_uid, gid,
323                                       strerror(errno));
324                 }
325         }
326
327         if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) {
328                 if (chmod(ttyname, mode) < 0) {
329                         if (errno == EROFS &&
330                             (st.st_mode & (S_IRGRP | S_IROTH)) == 0)
331                                 error("chmod(%.100s, 0%o) failed: %.100s",
332                                       ttyname, mode, strerror(errno));
333                         else
334                                 fatal("chmod(%.100s, 0%o) failed: %.100s",
335                                       ttyname, mode, strerror(errno));
336                 }
337         }
338 }
This page took 0.127039 seconds and 3 git commands to generate.