1295367Sdes/* $OpenBSD: sshpty.c,v 1.30 2015/07/30 23:09:15 djm Exp $ */
276259Sgreen/*
376259Sgreen * Author: Tatu Ylonen <ylo@cs.hut.fi>
476259Sgreen * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
576259Sgreen *                    All rights reserved
676259Sgreen * Allocating a pseudo-terminal, and making it the controlling tty.
776259Sgreen *
876259Sgreen * As far as I am concerned, the code I have written for this software
976259Sgreen * can be used freely for any purpose.  Any derived versions of this
1076259Sgreen * software must be clearly marked as such, and if the derived work is
1176259Sgreen * incompatible with the protocol description in the RFC file, it must be
1276259Sgreen * called by a name other than "ssh" or "Secure Shell".
1376259Sgreen */
1476259Sgreen
1576259Sgreen#include "includes.h"
1676259Sgreen
17162856Sdes#include <sys/types.h>
18162856Sdes#include <sys/ioctl.h>
19162856Sdes#include <sys/stat.h>
20162856Sdes#include <signal.h>
21162856Sdes
22162856Sdes#include <errno.h>
23162856Sdes#include <fcntl.h>
24162856Sdes#include <grp.h>
25162856Sdes#ifdef HAVE_PATHS_H
26162856Sdes# include <paths.h>
27162856Sdes#endif
28162856Sdes#include <pwd.h>
29162856Sdes#include <stdarg.h>
30162856Sdes#include <string.h>
31162856Sdes#include <termios.h>
3298941Sdes#ifdef HAVE_UTIL_H
3398941Sdes# include <util.h>
34162856Sdes#endif
35162856Sdes#include <unistd.h>
3698941Sdes
3776259Sgreen#include "sshpty.h"
3876259Sgreen#include "log.h"
3998941Sdes#include "misc.h"
4076259Sgreen
4198941Sdes#ifdef HAVE_PTY_H
4298941Sdes# include <pty.h>
4398941Sdes#endif
4498941Sdes
4576259Sgreen#ifndef O_NOCTTY
4676259Sgreen#define O_NOCTTY 0
4776259Sgreen#endif
4876259Sgreen
49192595Sdes#ifdef __APPLE__
50192595Sdes# include <AvailabilityMacros.h>
51192595Sdes# if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
52192595Sdes#  define __APPLE_PRIVPTY__
53192595Sdes# endif
54192595Sdes#endif
55192595Sdes
5676259Sgreen/*
5776259Sgreen * Allocates and opens a pty.  Returns 0 if no pty could be allocated, or
5876259Sgreen * nonzero if a pty was successfully allocated.  On success, open file
5976259Sgreen * descriptors for the pty and tty sides and the name of the tty side are
6076259Sgreen * returned (the buffer must be able to hold at least 64 characters).
6176259Sgreen */
6276259Sgreen
6376259Sgreenint
64162856Sdespty_allocate(int *ptyfd, int *ttyfd, char *namebuf, size_t namebuflen)
6576259Sgreen{
6676259Sgreen	/* openpty(3) exists in OSF/1 and some other os'es */
6798941Sdes	char *name;
6876259Sgreen	int i;
6976259Sgreen
7098941Sdes	i = openpty(ptyfd, ttyfd, NULL, NULL, NULL);
7176259Sgreen	if (i < 0) {
7276259Sgreen		error("openpty: %.100s", strerror(errno));
7376259Sgreen		return 0;
7476259Sgreen	}
7598941Sdes	name = ttyname(*ttyfd);
7698941Sdes	if (!name)
7798941Sdes		fatal("openpty returns device for which ttyname fails.");
7898941Sdes
7998941Sdes	strlcpy(namebuf, name, namebuflen);	/* possible truncation */
8076259Sgreen	return 1;
8176259Sgreen}
8276259Sgreen
8376259Sgreen/* Releases the tty.  Its ownership is returned to root, and permissions to 0666. */
8476259Sgreen
8576259Sgreenvoid
86137019Sdespty_release(const char *tty)
8776259Sgreen{
88295367Sdes#if !defined(__APPLE_PRIVPTY__) && !defined(HAVE_OPENPTY)
89137019Sdes	if (chown(tty, (uid_t) 0, (gid_t) 0) < 0)
90137019Sdes		error("chown %.100s 0 0 failed: %.100s", tty, strerror(errno));
91137019Sdes	if (chmod(tty, (mode_t) 0666) < 0)
92137019Sdes		error("chmod %.100s 0666 failed: %.100s", tty, strerror(errno));
93295367Sdes#endif /* !__APPLE_PRIVPTY__ && !HAVE_OPENPTY */
9476259Sgreen}
9576259Sgreen
96124211Sdes/* Makes the tty the process's controlling tty and sets it to sane modes. */
9776259Sgreen
9876259Sgreenvoid
99137019Sdespty_make_controlling_tty(int *ttyfd, const char *tty)
10076259Sgreen{
10176259Sgreen	int fd;
10276259Sgreen
103106130Sdes#ifdef _UNICOS
10498941Sdes	if (setsid() < 0)
10598941Sdes		error("setsid: %.100s", strerror(errno));
10698941Sdes
107137019Sdes	fd = open(tty, O_RDWR|O_NOCTTY);
10898941Sdes	if (fd != -1) {
109124211Sdes		signal(SIGHUP, SIG_IGN);
11098941Sdes		ioctl(fd, TCVHUP, (char *)NULL);
111124211Sdes		signal(SIGHUP, SIG_DFL);
11298941Sdes		setpgid(0, 0);
11398941Sdes		close(fd);
11498941Sdes	} else {
11598941Sdes		error("Failed to disconnect from controlling tty.");
11698941Sdes	}
11798941Sdes
11898941Sdes	debug("Setting controlling tty using TCSETCTTY.");
11998941Sdes	ioctl(*ttyfd, TCSETCTTY, NULL);
12098941Sdes	fd = open("/dev/tty", O_RDWR);
12198941Sdes	if (fd < 0)
122137019Sdes		error("%.100s: %.100s", tty, strerror(errno));
12398941Sdes	close(*ttyfd);
12498941Sdes	*ttyfd = fd;
125106130Sdes#else /* _UNICOS */
12698941Sdes
12776259Sgreen	/* First disconnect from the old controlling tty. */
12876259Sgreen#ifdef TIOCNOTTY
12976259Sgreen	fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
13076259Sgreen	if (fd >= 0) {
13176259Sgreen		(void) ioctl(fd, TIOCNOTTY, NULL);
13276259Sgreen		close(fd);
13376259Sgreen	}
13476259Sgreen#endif /* TIOCNOTTY */
13576259Sgreen	if (setsid() < 0)
13676259Sgreen		error("setsid: %.100s", strerror(errno));
13776259Sgreen
13876259Sgreen	/*
13976259Sgreen	 * Verify that we are successfully disconnected from the controlling
14076259Sgreen	 * tty.
14176259Sgreen	 */
14276259Sgreen	fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
14376259Sgreen	if (fd >= 0) {
14476259Sgreen		error("Failed to disconnect from controlling tty.");
14576259Sgreen		close(fd);
14676259Sgreen	}
14776259Sgreen	/* Make it our controlling tty. */
14876259Sgreen#ifdef TIOCSCTTY
14976259Sgreen	debug("Setting controlling tty using TIOCSCTTY.");
15076259Sgreen	if (ioctl(*ttyfd, TIOCSCTTY, NULL) < 0)
15176259Sgreen		error("ioctl(TIOCSCTTY): %.100s", strerror(errno));
15276259Sgreen#endif /* TIOCSCTTY */
153149753Sdes#ifdef NEED_SETPGRP
15498941Sdes	if (setpgrp(0,0) < 0)
15598941Sdes		error("SETPGRP %s",strerror(errno));
156149753Sdes#endif /* NEED_SETPGRP */
157137019Sdes	fd = open(tty, O_RDWR);
15898941Sdes	if (fd < 0) {
159137019Sdes		error("%.100s: %.100s", tty, strerror(errno));
16098941Sdes	} else {
16176259Sgreen		close(fd);
16298941Sdes	}
16376259Sgreen	/* Verify that we now have a controlling tty. */
16476259Sgreen	fd = open(_PATH_TTY, O_WRONLY);
16576259Sgreen	if (fd < 0)
16676259Sgreen		error("open /dev/tty failed - could not set controlling tty: %.100s",
16792559Sdes		    strerror(errno));
168126277Sdes	else
16976259Sgreen		close(fd);
170106130Sdes#endif /* _UNICOS */
17176259Sgreen}
17276259Sgreen
17376259Sgreen/* Changes the window size associated with the pty. */
17476259Sgreen
17576259Sgreenvoid
176162856Sdespty_change_window_size(int ptyfd, u_int row, u_int col,
177162856Sdes	u_int xpixel, u_int ypixel)
17876259Sgreen{
17976259Sgreen	struct winsize w;
18099063Sdes
181162856Sdes	/* may truncate u_int -> u_short */
18276259Sgreen	w.ws_row = row;
18376259Sgreen	w.ws_col = col;
18476259Sgreen	w.ws_xpixel = xpixel;
18576259Sgreen	w.ws_ypixel = ypixel;
18676259Sgreen	(void) ioctl(ptyfd, TIOCSWINSZ, &w);
18776259Sgreen}
18876259Sgreen
18976259Sgreenvoid
190137019Sdespty_setowner(struct passwd *pw, const char *tty)
19176259Sgreen{
19276259Sgreen	struct group *grp;
19376259Sgreen	gid_t gid;
19476259Sgreen	mode_t mode;
19576259Sgreen	struct stat st;
19676259Sgreen
19776259Sgreen	/* Determine the group to make the owner of the tty. */
19876259Sgreen	grp = getgrnam("tty");
199295367Sdes	gid = (grp != NULL) ? grp->gr_gid : pw->pw_gid;
200295367Sdes	mode = (grp != NULL) ? 0620 : 0600;
20176259Sgreen
20276259Sgreen	/*
20376259Sgreen	 * Change owner and mode of the tty as required.
20492559Sdes	 * Warn but continue if filesystem is read-only and the uids match/
20592559Sdes	 * tty is owned by root.
20676259Sgreen	 */
207137019Sdes	if (stat(tty, &st))
208137019Sdes		fatal("stat(%.100s) failed: %.100s", tty,
20976259Sgreen		    strerror(errno));
21076259Sgreen
211162856Sdes#ifdef WITH_SELINUX
212162856Sdes	ssh_selinux_setup_pty(pw->pw_name, tty);
213162856Sdes#endif
214162856Sdes
21576259Sgreen	if (st.st_uid != pw->pw_uid || st.st_gid != gid) {
216137019Sdes		if (chown(tty, pw->pw_uid, gid) < 0) {
21792559Sdes			if (errno == EROFS &&
21899063Sdes			    (st.st_uid == pw->pw_uid || st.st_uid == 0))
219113911Sdes				debug("chown(%.100s, %u, %u) failed: %.100s",
220137019Sdes				    tty, (u_int)pw->pw_uid, (u_int)gid,
22192559Sdes				    strerror(errno));
22276259Sgreen			else
22399063Sdes				fatal("chown(%.100s, %u, %u) failed: %.100s",
224137019Sdes				    tty, (u_int)pw->pw_uid, (u_int)gid,
22592559Sdes				    strerror(errno));
22676259Sgreen		}
22776259Sgreen	}
22876259Sgreen
22976259Sgreen	if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) {
230137019Sdes		if (chmod(tty, mode) < 0) {
23176259Sgreen			if (errno == EROFS &&
23276259Sgreen			    (st.st_mode & (S_IRGRP | S_IROTH)) == 0)
233113911Sdes				debug("chmod(%.100s, 0%o) failed: %.100s",
234137019Sdes				    tty, (u_int)mode, strerror(errno));
23576259Sgreen			else
23676259Sgreen				fatal("chmod(%.100s, 0%o) failed: %.100s",
237137019Sdes				    tty, (u_int)mode, strerror(errno));
23876259Sgreen		}
23976259Sgreen	}
24076259Sgreen}
241