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.12 2004/06/21 17:36:31 avsm Exp $"); 16 17 #ifdef HAVE_UTIL_H 18 # include <util.h> 19 #endif /* HAVE_UTIL_H */ 20 21 #include "sshpty.h" 22 #include "log.h" 23 #include "misc.h" 24 25 #ifdef HAVE_PTY_H 26 # include <pty.h> 27 #endif 28 29 #ifndef O_NOCTTY 30 #define O_NOCTTY 0 31 #endif 32 33 /* 34 * Allocates and opens a pty. Returns 0 if no pty could be allocated, or 35 * nonzero if a pty was successfully allocated. On success, open file 36 * descriptors for the pty and tty sides and the name of the tty side are 37 * returned (the buffer must be able to hold at least 64 characters). 38 */ 39 40 int 41 pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, int namebuflen) 42 { 43 /* openpty(3) exists in OSF/1 and some other os'es */ 44 char *name; 45 int i; 46 47 i = openpty(ptyfd, ttyfd, NULL, NULL, NULL); 48 if (i < 0) { 49 error("openpty: %.100s", strerror(errno)); 50 return 0; 51 } 52 name = ttyname(*ttyfd); 53 if (!name) 54 fatal("openpty returns device for which ttyname fails."); 55 56 strlcpy(namebuf, name, namebuflen); /* possible truncation */ 57 return 1; 58 } 59 60 /* Releases the tty. Its ownership is returned to root, and permissions to 0666. */ 61 62 void 63 pty_release(const char *tty) 64 { 65 if (chown(tty, (uid_t) 0, (gid_t) 0) < 0) 66 error("chown %.100s 0 0 failed: %.100s", tty, strerror(errno)); 67 if (chmod(tty, (mode_t) 0666) < 0) 68 error("chmod %.100s 0666 failed: %.100s", tty, strerror(errno)); 69 } 70 71 /* Makes the tty the process's controlling tty and sets it to sane modes. */ 72 73 void 74 pty_make_controlling_tty(int *ttyfd, const char *tty) 75 { 76 int fd; 77 #ifdef USE_VHANGUP 78 void *old; 79 #endif /* USE_VHANGUP */ 80 81 #ifdef _UNICOS 82 if (setsid() < 0) 83 error("setsid: %.100s", strerror(errno)); 84 85 fd = open(tty, O_RDWR|O_NOCTTY); 86 if (fd != -1) { 87 signal(SIGHUP, SIG_IGN); 88 ioctl(fd, TCVHUP, (char *)NULL); 89 signal(SIGHUP, SIG_DFL); 90 setpgid(0, 0); 91 close(fd); 92 } else { 93 error("Failed to disconnect from controlling tty."); 94 } 95 96 debug("Setting controlling tty using TCSETCTTY."); 97 ioctl(*ttyfd, TCSETCTTY, NULL); 98 fd = open("/dev/tty", O_RDWR); 99 if (fd < 0) 100 error("%.100s: %.100s", tty, strerror(errno)); 101 close(*ttyfd); 102 *ttyfd = fd; 103 #else /* _UNICOS */ 104 105 /* First disconnect from the old controlling tty. */ 106 #ifdef TIOCNOTTY 107 fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); 108 if (fd >= 0) { 109 (void) ioctl(fd, TIOCNOTTY, NULL); 110 close(fd); 111 } 112 #endif /* TIOCNOTTY */ 113 if (setsid() < 0) 114 error("setsid: %.100s", strerror(errno)); 115 116 /* 117 * Verify that we are successfully disconnected from the controlling 118 * tty. 119 */ 120 fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); 121 if (fd >= 0) { 122 error("Failed to disconnect from controlling tty."); 123 close(fd); 124 } 125 /* Make it our controlling tty. */ 126 #ifdef TIOCSCTTY 127 debug("Setting controlling tty using TIOCSCTTY."); 128 if (ioctl(*ttyfd, TIOCSCTTY, NULL) < 0) 129 error("ioctl(TIOCSCTTY): %.100s", strerror(errno)); 130 #endif /* TIOCSCTTY */ 131 #ifdef HAVE_NEWS4 132 if (setpgrp(0,0) < 0) 133 error("SETPGRP %s",strerror(errno)); 134 #endif /* HAVE_NEWS4 */ 135 #ifdef USE_VHANGUP 136 old = signal(SIGHUP, SIG_IGN); 137 vhangup(); 138 signal(SIGHUP, old); 139 #endif /* USE_VHANGUP */ 140 fd = open(tty, O_RDWR); 141 if (fd < 0) { 142 error("%.100s: %.100s", tty, strerror(errno)); 143 } else { 144 #ifdef USE_VHANGUP 145 close(*ttyfd); 146 *ttyfd = fd; 147 #else /* USE_VHANGUP */ 148 close(fd); 149 #endif /* USE_VHANGUP */ 150 } 151 /* Verify that we now have a controlling tty. */ 152 fd = open(_PATH_TTY, O_WRONLY); 153 if (fd < 0) 154 error("open /dev/tty failed - could not set controlling tty: %.100s", 155 strerror(errno)); 156 else 157 close(fd); 158 #endif /* _UNICOS */ 159 } 160 161 /* Changes the window size associated with the pty. */ 162 163 void 164 pty_change_window_size(int ptyfd, int row, int col, 165 int xpixel, int ypixel) 166 { 167 struct winsize w; 168 169 w.ws_row = row; 170 w.ws_col = col; 171 w.ws_xpixel = xpixel; 172 w.ws_ypixel = ypixel; 173 (void) ioctl(ptyfd, TIOCSWINSZ, &w); 174 } 175 176 void 177 pty_setowner(struct passwd *pw, const char *tty) 178 { 179 struct group *grp; 180 gid_t gid; 181 mode_t mode; 182 struct stat st; 183 184 /* Determine the group to make the owner of the tty. */ 185 grp = getgrnam("tty"); 186 if (grp) { 187 gid = grp->gr_gid; 188 mode = S_IRUSR | S_IWUSR | S_IWGRP; 189 } else { 190 gid = pw->pw_gid; 191 mode = S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH; 192 } 193 194 /* 195 * Change owner and mode of the tty as required. 196 * Warn but continue if filesystem is read-only and the uids match/ 197 * tty is owned by root. 198 */ 199 if (stat(tty, &st)) 200 fatal("stat(%.100s) failed: %.100s", tty, 201 strerror(errno)); 202 203 if (st.st_uid != pw->pw_uid || st.st_gid != gid) { 204 if (chown(tty, pw->pw_uid, gid) < 0) { 205 if (errno == EROFS && 206 (st.st_uid == pw->pw_uid || st.st_uid == 0)) 207 debug("chown(%.100s, %u, %u) failed: %.100s", 208 tty, (u_int)pw->pw_uid, (u_int)gid, 209 strerror(errno)); 210 else 211 fatal("chown(%.100s, %u, %u) failed: %.100s", 212 tty, (u_int)pw->pw_uid, (u_int)gid, 213 strerror(errno)); 214 } 215 } 216 217 if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) { 218 if (chmod(tty, mode) < 0) { 219 if (errno == EROFS && 220 (st.st_mode & (S_IRGRP | S_IROTH)) == 0) 221 debug("chmod(%.100s, 0%o) failed: %.100s", 222 tty, (u_int)mode, strerror(errno)); 223 else 224 fatal("chmod(%.100s, 0%o) failed: %.100s", 225 tty, (u_int)mode, strerror(errno)); 226 } 227 } 228 } 229