sshpty.c revision 99063
135509Sjb/* 235509Sjb * Author: Tatu Ylonen <ylo@cs.hut.fi> 335509Sjb * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 435509Sjb * All rights reserved 535509Sjb * Allocating a pseudo-terminal, and making it the controlling tty. 635509Sjb * 735509Sjb * As far as I am concerned, the code I have written for this software 835509Sjb * can be used freely for any purpose. Any derived versions of this 935509Sjb * software must be clearly marked as such, and if the derived work is 1035509Sjb * incompatible with the protocol description in the RFC file, it must be 1135509Sjb * called by a name other than "ssh" or "Secure Shell". 1235509Sjb */ 13165967Simp 1435509Sjb#include "includes.h" 1535509SjbRCSID("$OpenBSD: sshpty.c,v 1.7 2002/06/24 17:57:20 deraadt Exp $"); 1635509SjbRCSID("$FreeBSD: head/crypto/openssh/sshpty.c 99063 2002-06-29 11:48:59Z des $"); 1735509Sjb 1835509Sjb#ifdef HAVE_UTIL_H 1935509Sjb# include <util.h> 2049439Sdeischen#endif /* HAVE_UTIL_H */ 2135509Sjb 2235509Sjb#include "sshpty.h" 2335509Sjb#include "log.h" 2435509Sjb#include "misc.h" 2535509Sjb 2635509Sjb/* Pty allocated with _getpty gets broken if we do I_PUSH:es to it. */ 2735509Sjb#if defined(HAVE__GETPTY) || defined(HAVE_OPENPTY) 2835509Sjb#undef HAVE_DEV_PTMX 2950476Speter#endif 3035509Sjb 3135509Sjb#ifdef HAVE_PTY_H 3235509Sjb# include <pty.h> 33174112Sdeischen#endif 34114187Sdeischen#if defined(HAVE_DEV_PTMX) && defined(HAVE_SYS_STROPTS_H) 35114187Sdeischen# include <sys/stropts.h> 36174112Sdeischen#endif 3793399Smarkm 38174112Sdeischen#ifndef O_NOCTTY 39113658Sdeischen#define O_NOCTTY 0 40103388Smini#endif 4135509Sjb 42154288Sjasone/* 43115381Sdeischen * Allocates and opens a pty. Returns 0 if no pty could be allocated, or 44115381Sdeischen * nonzero if a pty was successfully allocated. On success, open file 45122073Sdeischen * descriptors for the pty and tty sides and the name of the tty side are 46123314Sdavidxu * returned (the buffer must be able to hold at least 64 characters). 47115381Sdeischen */ 48115381Sdeischen 49174112Sdeischenint 50174112Sdeischenpty_allocate(int *ptyfd, int *ttyfd, char *namebuf, int namebuflen) 51174112Sdeischen{ 52174112Sdeischen#if defined(HAVE_OPENPTY) || defined(BSD4_4) 53174112Sdeischen /* openpty(3) exists in OSF/1 and some other os'es */ 54174112Sdeischen char *name; 55174112Sdeischen int i; 56174112Sdeischen 57115381Sdeischen i = openpty(ptyfd, ttyfd, NULL, NULL, NULL); 58115381Sdeischen if (i < 0) { 59139023Sdeischen error("openpty: %.100s", strerror(errno)); 60139023Sdeischen return 0; 61139023Sdeischen } 62139023Sdeischen name = ttyname(*ttyfd); 63123314Sdavidxu if (!name) 64115381Sdeischen fatal("openpty returns device for which ttyname fails."); 65115381Sdeischen 66115381Sdeischen strlcpy(namebuf, name, namebuflen); /* possible truncation */ 67115381Sdeischen return 1; 68156611Sdeischen#else /* HAVE_OPENPTY */ 69156611Sdeischen#ifdef HAVE__GETPTY 70156611Sdeischen /* 71156611Sdeischen * _getpty(3) exists in SGI Irix 4.x, 5.x & 6.x -- it generates more 72113658Sdeischen * pty's automagically when needed 73113658Sdeischen */ 74113658Sdeischen char *slave; 75113658Sdeischen 76113658Sdeischen slave = _getpty(ptyfd, O_RDWR, 0622, 0); 77112665Sjeff if (slave == NULL) { 78112665Sjeff error("_getpty: %.100s", strerror(errno)); 79112665Sjeff return 0; 80174112Sdeischen } 81114187Sdeischen strlcpy(namebuf, slave, namebuflen); 82174112Sdeischen /* Open the slave side. */ 83174112Sdeischen *ttyfd = open(namebuf, O_RDWR | O_NOCTTY); 84112665Sjeff if (*ttyfd < 0) { 85112665Sjeff error("%.200s: %.100s", namebuf, strerror(errno)); 8635509Sjb close(*ptyfd); 8735509Sjb return 0; 8835509Sjb } 8935509Sjb return 1; 9035509Sjb#else /* HAVE__GETPTY */ 9135509Sjb#if defined(HAVE_DEV_PTMX) 9235509Sjb /* 9336828Sjb * This code is used e.g. on Solaris 2.x. (Note that Solaris 2.3 9435509Sjb * also has bsd-style ptys, but they simply do not work.) 95174112Sdeischen */ 96114187Sdeischen int ptm; 97123314Sdavidxu char *pts; 98123314Sdavidxu mysig_t old_signal; 99123314Sdavidxu 100123314Sdavidxu ptm = open("/dev/ptmx", O_RDWR | O_NOCTTY); 10136828Sjb if (ptm < 0) { 10236828Sjb error("/dev/ptmx: %.100s", strerror(errno)); 10336828Sjb return 0; 10436828Sjb } 105115381Sdeischen old_signal = mysignal(SIGCHLD, SIG_DFL); 106115381Sdeischen if (grantpt(ptm) < 0) { 107174112Sdeischen error("grantpt: %.100s", strerror(errno)); 108174112Sdeischen return 0; 10936828Sjb } 11036828Sjb mysignal(SIGCHLD, old_signal); 11136828Sjb if (unlockpt(ptm) < 0) { 11236828Sjb error("unlockpt: %.100s", strerror(errno)); 11336828Sjb return 0; 11436828Sjb } 11536828Sjb pts = ptsname(ptm); 11636828Sjb if (pts == NULL) 11736828Sjb error("Slave pty side name could not be obtained."); 11836828Sjb strlcpy(namebuf, pts, namebuflen); 11936828Sjb *ptyfd = ptm; 12036828Sjb 12136828Sjb /* Open the slave side. */ 122174112Sdeischen *ttyfd = open(namebuf, O_RDWR | O_NOCTTY); 12336828Sjb if (*ttyfd < 0) { 124114187Sdeischen error("%.100s: %.100s", namebuf, strerror(errno)); 12535509Sjb close(*ptyfd); 126115381Sdeischen return 0; 127115381Sdeischen } 128115381Sdeischen#ifndef HAVE_CYGWIN 129115381Sdeischen /* 130139023Sdeischen * Push the appropriate streams modules, as described in Solaris pts(7). 131115381Sdeischen * HP-UX pts(7) doesn't have ttcompat module. 132115381Sdeischen */ 133122073Sdeischen if (ioctl(*ttyfd, I_PUSH, "ptem") < 0) 134115381Sdeischen error("ioctl I_PUSH ptem: %.100s", strerror(errno)); 135115381Sdeischen if (ioctl(*ttyfd, I_PUSH, "ldterm") < 0) 136139023Sdeischen error("ioctl I_PUSH ldterm: %.100s", strerror(errno)); 137123314Sdavidxu#ifndef __hpux 138123314Sdavidxu if (ioctl(*ttyfd, I_PUSH, "ttcompat") < 0) 139115381Sdeischen error("ioctl I_PUSH ttcompat: %.100s", strerror(errno)); 140115381Sdeischen#endif 141115381Sdeischen#endif 142115381Sdeischen return 1; 143115381Sdeischen#else /* HAVE_DEV_PTMX */ 144115381Sdeischen#ifdef HAVE_DEV_PTS_AND_PTC 145115381Sdeischen /* AIX-style pty code. */ 146115381Sdeischen const char *name; 147123314Sdavidxu 148123314Sdavidxu *ptyfd = open("/dev/ptc", O_RDWR | O_NOCTTY); 149123314Sdavidxu if (*ptyfd < 0) { 150122073Sdeischen error("Could not open /dev/ptc: %.100s", strerror(errno)); 151139023Sdeischen return 0; 152122073Sdeischen } 153115381Sdeischen name = ttyname(*ptyfd); 154139023Sdeischen if (!name) 155122073Sdeischen fatal("Open of /dev/ptc returns device for which ttyname fails."); 156115381Sdeischen strlcpy(namebuf, name, namebuflen); 157122073Sdeischen *ttyfd = open(name, O_RDWR | O_NOCTTY); 158115381Sdeischen if (*ttyfd < 0) { 159115381Sdeischen 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 253void 254pty_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 264void 265pty_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 354void 355pty_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 367void 368pty_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