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