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