Home | History | Annotate | Download | only in sshd
      1 /*
      2  * Author: Tatu Ylonen <ylo (at) cs.hut.fi>
      3  * Copyright (c) 1995 Tatu Ylonen <ylo (at) 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