1*cd4ada6aSchristos /* $NetBSD: sshpty.c,v 1.8 2019/10/12 18:32:22 christos Exp $ */
2*cd4ada6aSchristos /* $OpenBSD: sshpty.c,v 1.34 2019/07/04 16:20:10 deraadt Exp $ */
3ca32bd8dSchristos /*
4ca32bd8dSchristos * Author: Tatu Ylonen <ylo@cs.hut.fi>
5ca32bd8dSchristos * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
6ca32bd8dSchristos * All rights reserved
7ca32bd8dSchristos * Allocating a pseudo-terminal, and making it the controlling tty.
8ca32bd8dSchristos *
9ca32bd8dSchristos * As far as I am concerned, the code I have written for this software
10ca32bd8dSchristos * can be used freely for any purpose. Any derived versions of this
11ca32bd8dSchristos * software must be clearly marked as such, and if the derived work is
12ca32bd8dSchristos * incompatible with the protocol description in the RFC file, it must be
13ca32bd8dSchristos * called by a name other than "ssh" or "Secure Shell".
14ca32bd8dSchristos */
15ca32bd8dSchristos
16313c6c94Schristos #include "includes.h"
17*cd4ada6aSchristos __RCSID("$NetBSD: sshpty.c,v 1.8 2019/10/12 18:32:22 christos Exp $");
18ca32bd8dSchristos #include <sys/types.h>
19ca32bd8dSchristos #include <sys/ioctl.h>
20ca32bd8dSchristos #include <sys/stat.h>
21ca32bd8dSchristos
22ca32bd8dSchristos #include <errno.h>
23ca32bd8dSchristos #include <fcntl.h>
24ca32bd8dSchristos #include <grp.h>
25ca32bd8dSchristos #include <paths.h>
26ca32bd8dSchristos #include <pwd.h>
27ca32bd8dSchristos #include <stdarg.h>
28ca32bd8dSchristos #include <string.h>
29ca32bd8dSchristos #include <termios.h>
30ca32bd8dSchristos #include <unistd.h>
31ca32bd8dSchristos #include <util.h>
32ca32bd8dSchristos
33ca32bd8dSchristos #include "sshpty.h"
34ca32bd8dSchristos #include "log.h"
35ca32bd8dSchristos
36ca32bd8dSchristos #ifndef O_NOCTTY
37ca32bd8dSchristos #define O_NOCTTY 0
38ca32bd8dSchristos #endif
39ca32bd8dSchristos
40ca32bd8dSchristos /*
41ca32bd8dSchristos * Allocates and opens a pty. Returns 0 if no pty could be allocated, or
42ca32bd8dSchristos * nonzero if a pty was successfully allocated. On success, open file
43ca32bd8dSchristos * descriptors for the pty and tty sides and the name of the tty side are
44ca32bd8dSchristos * returned (the buffer must be able to hold at least 64 characters).
45ca32bd8dSchristos */
46ca32bd8dSchristos
47ca32bd8dSchristos int
pty_allocate(int * ptyfd,int * ttyfd,char * namebuf,size_t namebuflen)48ca32bd8dSchristos pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, size_t namebuflen)
49ca32bd8dSchristos {
50ca32bd8dSchristos char buf[64];
51ca32bd8dSchristos int i;
52ca32bd8dSchristos
53ca32bd8dSchristos i = openpty(ptyfd, ttyfd, buf, NULL, NULL);
54*cd4ada6aSchristos if (i == -1) {
55ca32bd8dSchristos error("openpty: %.100s", strerror(errno));
56ca32bd8dSchristos return 0;
57ca32bd8dSchristos }
58ca32bd8dSchristos strlcpy(namebuf, buf, namebuflen); /* possible truncation */
59ca32bd8dSchristos return 1;
60ca32bd8dSchristos }
61ca32bd8dSchristos
62ca32bd8dSchristos /* Releases the tty. Its ownership is returned to root, and permissions to 0666. */
63ca32bd8dSchristos
64ca32bd8dSchristos void
pty_release(const char * tty)65ca32bd8dSchristos pty_release(const char *tty)
66ca32bd8dSchristos {
67*cd4ada6aSchristos if (chown(tty, (uid_t) 0, (gid_t) 0) == -1)
68ca32bd8dSchristos error("chown %.100s 0 0 failed: %.100s", tty, strerror(errno));
69*cd4ada6aSchristos if (chmod(tty, (mode_t) 0666) == -1)
70ca32bd8dSchristos error("chmod %.100s 0666 failed: %.100s", tty, strerror(errno));
71ca32bd8dSchristos }
72ca32bd8dSchristos
73ca32bd8dSchristos /* Makes the tty the process's controlling tty and sets it to sane modes. */
74ca32bd8dSchristos
75ca32bd8dSchristos void
pty_make_controlling_tty(int * ttyfd,const char * tty)76ca32bd8dSchristos pty_make_controlling_tty(int *ttyfd, const char *tty)
77ca32bd8dSchristos {
78ca32bd8dSchristos int fd;
79ca32bd8dSchristos
80ca32bd8dSchristos /* First disconnect from the old controlling tty. */
81ca32bd8dSchristos #ifdef TIOCNOTTY
82ca32bd8dSchristos fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
83ca32bd8dSchristos if (fd >= 0) {
84ca32bd8dSchristos (void) ioctl(fd, TIOCNOTTY, NULL);
85ca32bd8dSchristos close(fd);
86ca32bd8dSchristos }
87ca32bd8dSchristos #endif /* TIOCNOTTY */
88*cd4ada6aSchristos if (setsid() == -1)
89ca32bd8dSchristos error("setsid: %.100s", strerror(errno));
90ca32bd8dSchristos
91ca32bd8dSchristos /*
92ca32bd8dSchristos * Verify that we are successfully disconnected from the controlling
93ca32bd8dSchristos * tty.
94ca32bd8dSchristos */
95ca32bd8dSchristos fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
96ca32bd8dSchristos if (fd >= 0) {
97ca32bd8dSchristos error("Failed to disconnect from controlling tty.");
98ca32bd8dSchristos close(fd);
99ca32bd8dSchristos }
100ca32bd8dSchristos /* Make it our controlling tty. */
101ca32bd8dSchristos #ifdef TIOCSCTTY
102ca32bd8dSchristos debug("Setting controlling tty using TIOCSCTTY.");
103*cd4ada6aSchristos if (ioctl(*ttyfd, TIOCSCTTY, NULL) == -1)
104ca32bd8dSchristos error("ioctl(TIOCSCTTY): %.100s", strerror(errno));
105ca32bd8dSchristos #endif /* TIOCSCTTY */
106ca32bd8dSchristos fd = open(tty, O_RDWR);
107*cd4ada6aSchristos if (fd == -1)
108ca32bd8dSchristos error("%.100s: %.100s", tty, strerror(errno));
109ca32bd8dSchristos else
110ca32bd8dSchristos close(fd);
111ca32bd8dSchristos
112ca32bd8dSchristos /* Verify that we now have a controlling tty. */
113ca32bd8dSchristos fd = open(_PATH_TTY, O_WRONLY);
114*cd4ada6aSchristos if (fd == -1)
115ca32bd8dSchristos error("open /dev/tty failed - could not set controlling tty: %.100s",
116ca32bd8dSchristos strerror(errno));
117ca32bd8dSchristos else
118ca32bd8dSchristos close(fd);
119ca32bd8dSchristos }
120ca32bd8dSchristos
121ca32bd8dSchristos /* Changes the window size associated with the pty. */
122ca32bd8dSchristos
123ca32bd8dSchristos void
pty_change_window_size(int ptyfd,u_int row,u_int col,u_int xpixel,u_int ypixel)124ca32bd8dSchristos pty_change_window_size(int ptyfd, u_int row, u_int col,
125ca32bd8dSchristos u_int xpixel, u_int ypixel)
126ca32bd8dSchristos {
127ca32bd8dSchristos struct winsize w;
128ca32bd8dSchristos
129ca32bd8dSchristos /* may truncate u_int -> u_short */
130ca32bd8dSchristos w.ws_row = row;
131ca32bd8dSchristos w.ws_col = col;
132ca32bd8dSchristos w.ws_xpixel = xpixel;
133ca32bd8dSchristos w.ws_ypixel = ypixel;
134ca32bd8dSchristos (void) ioctl(ptyfd, TIOCSWINSZ, &w);
135ca32bd8dSchristos }
136ca32bd8dSchristos
137ca32bd8dSchristos void
pty_setowner(struct passwd * pw,const char * tty)138ca32bd8dSchristos pty_setowner(struct passwd *pw, const char *tty)
139ca32bd8dSchristos {
140ca32bd8dSchristos struct group *grp;
141ca32bd8dSchristos gid_t gid;
142ca32bd8dSchristos mode_t mode;
143ca32bd8dSchristos struct stat st;
144ca32bd8dSchristos
145ca32bd8dSchristos /* Determine the group to make the owner of the tty. */
146ca32bd8dSchristos grp = getgrnam("tty");
147*cd4ada6aSchristos if (grp == NULL)
148*cd4ada6aSchristos fatal("no tty group");
149e4d43b82Schristos gid = (grp != NULL) ? grp->gr_gid : pw->pw_gid;
1508395c133Schristos mode = (grp != NULL) ? 0620 : 0600;
151ca32bd8dSchristos
152ca32bd8dSchristos /*
153ca32bd8dSchristos * Change owner and mode of the tty as required.
154ca32bd8dSchristos * Warn but continue if filesystem is read-only and the uids match/
155ca32bd8dSchristos * tty is owned by root.
156ca32bd8dSchristos */
157*cd4ada6aSchristos if (stat(tty, &st) == -1)
158ca32bd8dSchristos fatal("stat(%.100s) failed: %.100s", tty,
159ca32bd8dSchristos strerror(errno));
160ca32bd8dSchristos
161ca32bd8dSchristos if (st.st_uid != pw->pw_uid || st.st_gid != gid) {
162*cd4ada6aSchristos if (chown(tty, pw->pw_uid, gid) == -1) {
163ca32bd8dSchristos if (errno == EROFS &&
164ca32bd8dSchristos (st.st_uid == pw->pw_uid || st.st_uid == 0))
165ca32bd8dSchristos debug("chown(%.100s, %u, %u) failed: %.100s",
166ca32bd8dSchristos tty, (u_int)pw->pw_uid, (u_int)gid,
167ca32bd8dSchristos strerror(errno));
168ca32bd8dSchristos else
169ca32bd8dSchristos fatal("chown(%.100s, %u, %u) failed: %.100s",
170ca32bd8dSchristos tty, (u_int)pw->pw_uid, (u_int)gid,
171ca32bd8dSchristos strerror(errno));
172ca32bd8dSchristos }
173ca32bd8dSchristos }
174ca32bd8dSchristos
175ca32bd8dSchristos if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) {
176*cd4ada6aSchristos if (chmod(tty, mode) == -1) {
177ca32bd8dSchristos if (errno == EROFS &&
178ca32bd8dSchristos (st.st_mode & (S_IRGRP | S_IROTH)) == 0)
179ca32bd8dSchristos debug("chmod(%.100s, 0%o) failed: %.100s",
180ca32bd8dSchristos tty, (u_int)mode, strerror(errno));
181ca32bd8dSchristos else
182ca32bd8dSchristos fatal("chmod(%.100s, 0%o) failed: %.100s",
183ca32bd8dSchristos tty, (u_int)mode, strerror(errno));
184ca32bd8dSchristos }
185ca32bd8dSchristos }
186ca32bd8dSchristos }
187ee85abc4Schristos
188ee85abc4Schristos /* Disconnect from the controlling tty. */
189ee85abc4Schristos void
disconnect_controlling_tty(void)190ee85abc4Schristos disconnect_controlling_tty(void)
191ee85abc4Schristos {
192ee85abc4Schristos int fd;
193ee85abc4Schristos
194ee85abc4Schristos if ((fd = open(_PATH_TTY, O_RDWR | O_NOCTTY)) >= 0) {
195ee85abc4Schristos (void) ioctl(fd, TIOCNOTTY, NULL);
196ee85abc4Schristos close(fd);
197ee85abc4Schristos }
198ee85abc4Schristos }
199