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