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