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