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