Annotation of src/usr.bin/ssh/sshpty.c, Revision 1.1
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"
! 15: RCSID("$OpenBSD: pty.c,v 1.22 2001/02/08 19:30:52 itojun Exp $");
! 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",
! 134: name, strerror(errno));
! 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",
! 227: strerror(errno));
! 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,
! 237: int xpixel, int ypixel)
! 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.
! 267: * Warn but continue if filesystem is read-only and the uids match.
! 268: */
! 269: if (stat(ttyname, &st))
! 270: fatal("stat(%.100s) failed: %.100s", ttyname,
! 271: strerror(errno));
! 272:
! 273: if (st.st_uid != pw->pw_uid || st.st_gid != gid) {
! 274: if (chown(ttyname, pw->pw_uid, gid) < 0) {
! 275: if (errno == EROFS && st.st_uid == pw->pw_uid)
! 276: error("chown(%.100s, %d, %d) failed: %.100s",
! 277: ttyname, pw->pw_uid, gid,
! 278: strerror(errno));
! 279: else
! 280: fatal("chown(%.100s, %d, %d) failed: %.100s",
! 281: ttyname, pw->pw_uid, gid,
! 282: strerror(errno));
! 283: }
! 284: }
! 285:
! 286: if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) {
! 287: if (chmod(ttyname, mode) < 0) {
! 288: if (errno == EROFS &&
! 289: (st.st_mode & (S_IRGRP | S_IROTH)) == 0)
! 290: error("chmod(%.100s, 0%o) failed: %.100s",
! 291: ttyname, mode, strerror(errno));
! 292: else
! 293: fatal("chmod(%.100s, 0%o) failed: %.100s",
! 294: ttyname, mode, strerror(errno));
! 295: }
! 296: }
! 297: }