/src/dropbear/src/svr-agentfwd.c
Line | Count | Source |
1 | | /* |
2 | | * Dropbear - a SSH2 server |
3 | | * |
4 | | * Copyright (c) 2002,2003 Matt Johnston |
5 | | * All rights reserved. |
6 | | * |
7 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
8 | | * of this software and associated documentation files (the "Software"), to deal |
9 | | * in the Software without restriction, including without limitation the rights |
10 | | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
11 | | * copies of the Software, and to permit persons to whom the Software is |
12 | | * furnished to do so, subject to the following conditions: |
13 | | * |
14 | | * The above copyright notice and this permission notice shall be included in |
15 | | * all copies or substantial portions of the Software. |
16 | | * |
17 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
18 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
19 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
20 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
21 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
22 | | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
23 | | * SOFTWARE. */ |
24 | | |
25 | | /* This file (agentfwd.c) handles authentication agent forwarding, for OpenSSH |
26 | | * style agents. */ |
27 | | |
28 | | #include "includes.h" |
29 | | |
30 | | #if DROPBEAR_SVR_AGENTFWD |
31 | | |
32 | | #include "agentfwd.h" |
33 | | #include "session.h" |
34 | | #include "ssh.h" |
35 | | #include "dbutil.h" |
36 | | #include "chansession.h" |
37 | | #include "channel.h" |
38 | | #include "packet.h" |
39 | | #include "buffer.h" |
40 | | #include "dbrandom.h" |
41 | | #include "listener.h" |
42 | | #include "auth.h" |
43 | | |
44 | 0 | #define AGENTDIRPREFIX "/tmp/dropbear-" |
45 | | |
46 | | static int send_msg_channel_open_agent(int fd); |
47 | | static int bindagent(int fd, struct ChanSess * chansess); |
48 | | static void agentaccept(const struct Listener * listener, int sock); |
49 | | |
50 | | /* Handles client requests to start agent forwarding, sets up listening socket. |
51 | | * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ |
52 | 384 | int svr_agentreq(struct ChanSess * chansess) { |
53 | 384 | int fd = -1; |
54 | | |
55 | 384 | if (!svr_pubkey_allows_agentfwd()) { |
56 | 0 | return DROPBEAR_FAILURE; |
57 | 0 | } |
58 | | |
59 | 384 | if (chansess->agentlistener != NULL) { |
60 | 168 | return DROPBEAR_FAILURE; |
61 | 168 | } |
62 | | |
63 | 216 | #if DROPBEAR_FUZZ |
64 | 216 | if (fuzz.fuzzing) { |
65 | 216 | fd = wrapfd_new_dummy(); |
66 | 216 | } |
67 | 0 | else |
68 | 0 | #endif |
69 | 0 | { |
70 | | /* create listening socket */ |
71 | 0 | fd = socket(PF_UNIX, SOCK_STREAM, 0); |
72 | 0 | if (fd < 0) { |
73 | 0 | goto fail; |
74 | 0 | } |
75 | | |
76 | | /* create the unix socket dir and file */ |
77 | 0 | if (bindagent(fd, chansess) == DROPBEAR_FAILURE) { |
78 | 0 | goto fail; |
79 | 0 | } |
80 | | |
81 | | /* listen */ |
82 | 0 | if (listen(fd, 20) < 0) { |
83 | 0 | goto fail; |
84 | 0 | } |
85 | 0 | } |
86 | | |
87 | | /* set non-blocking */ |
88 | 216 | setnonblocking(fd); |
89 | | |
90 | | /* pass if off to listener */ |
91 | 216 | chansess->agentlistener = new_listener( &fd, 1, |
92 | 216 | LISTENER_TYPE_DEFAULT, chansess, |
93 | 216 | agentaccept, NULL); |
94 | | |
95 | 216 | if (chansess->agentlistener == NULL) { |
96 | 0 | goto fail; |
97 | 0 | } |
98 | | |
99 | 216 | return DROPBEAR_SUCCESS; |
100 | | |
101 | 0 | fail: |
102 | 0 | m_close(fd); |
103 | | /* cleanup */ |
104 | 0 | svr_agentcleanup(chansess); |
105 | |
|
106 | 0 | return DROPBEAR_FAILURE; |
107 | 216 | } |
108 | | |
109 | | /* accepts a connection on the forwarded socket and opens a new channel for it |
110 | | * back to the client */ |
111 | | /* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ |
112 | 8.84k | static void agentaccept(const struct Listener *UNUSED(listener), int sock) { |
113 | | |
114 | 8.84k | int fd; |
115 | | |
116 | 8.84k | fd = accept(sock, NULL, NULL); |
117 | 8.84k | if (fd < 0) { |
118 | 8.84k | TRACE(("accept failed")) |
119 | 8.84k | return; |
120 | 8.84k | } |
121 | | |
122 | 0 | if (send_msg_channel_open_agent(fd) != DROPBEAR_SUCCESS) { |
123 | 0 | close(fd); |
124 | 0 | } |
125 | |
|
126 | 0 | } |
127 | | |
128 | | /* set up the environment variable pointing to the socket. This is called |
129 | | * just before command/shell execution, after dropping privileges */ |
130 | 0 | void svr_agentset(const struct ChanSess * chansess) { |
131 | |
|
132 | 0 | char *path = NULL; |
133 | 0 | int len; |
134 | |
|
135 | 0 | if (chansess->agentlistener == NULL) { |
136 | 0 | return; |
137 | 0 | } |
138 | | |
139 | | /* 2 for "/" and "\0" */ |
140 | 0 | len = strlen(chansess->agentdir) + strlen(chansess->agentfile) + 2; |
141 | |
|
142 | 0 | path = m_malloc(len); |
143 | 0 | snprintf(path, len, "%s/%s", chansess->agentdir, chansess->agentfile); |
144 | 0 | addnewvar("SSH_AUTH_SOCK", path); |
145 | 0 | m_free(path); |
146 | 0 | } |
147 | | |
148 | | /* close the socket, remove the socket-file */ |
149 | 3.42k | void svr_agentcleanup(struct ChanSess * chansess) { |
150 | | |
151 | 3.42k | char *path = NULL; |
152 | 3.42k | uid_t uid; |
153 | 3.42k | gid_t gid; |
154 | 3.42k | int len; |
155 | | |
156 | 3.42k | if (chansess->agentlistener != NULL) { |
157 | 216 | remove_listener(chansess->agentlistener); |
158 | 216 | chansess->agentlistener = NULL; |
159 | 216 | } |
160 | | |
161 | 3.42k | if (chansess->agentfile != NULL && chansess->agentdir != NULL) { |
162 | |
|
163 | | #if !DROPBEAR_SVR_DROP_PRIVS |
164 | | /* Remove the dir as the user. That way they can't cause problems except |
165 | | * for themselves */ |
166 | | uid = getuid(); |
167 | | gid = getgid(); |
168 | | if ((setegid(ses.authstate.pw_gid)) < 0 || |
169 | | (seteuid(ses.authstate.pw_uid)) < 0) { |
170 | | dropbear_exit("Failed to set euid"); |
171 | | } |
172 | | #else |
173 | 0 | (void)uid; |
174 | 0 | (void)gid; |
175 | 0 | #endif |
176 | | |
177 | | /* 2 for "/" and "\0" */ |
178 | 0 | len = strlen(chansess->agentdir) + strlen(chansess->agentfile) + 2; |
179 | |
|
180 | 0 | path = m_malloc(len); |
181 | 0 | snprintf(path, len, "%s/%s", chansess->agentdir, chansess->agentfile); |
182 | 0 | unlink(path); |
183 | 0 | m_free(path); |
184 | |
|
185 | 0 | rmdir(chansess->agentdir); |
186 | |
|
187 | | #if !DROPBEAR_SVR_DROP_PRIVS |
188 | | if ((seteuid(uid)) < 0 || |
189 | | (setegid(gid)) < 0) { |
190 | | dropbear_exit("Failed to revert euid"); |
191 | | } |
192 | | #endif |
193 | |
|
194 | 0 | m_free(chansess->agentfile); |
195 | 0 | m_free(chansess->agentdir); |
196 | 0 | } |
197 | | |
198 | 3.42k | } |
199 | | |
200 | | static const struct ChanType chan_svr_agent = { |
201 | | "auth-agent@openssh.com", |
202 | | NULL, |
203 | | NULL, |
204 | | NULL, |
205 | | NULL, |
206 | | NULL |
207 | | }; |
208 | | |
209 | | |
210 | | /* helper for accepting an agent request */ |
211 | 0 | static int send_msg_channel_open_agent(int fd) { |
212 | |
|
213 | 0 | if (send_msg_channel_open_init(fd, &chan_svr_agent) == DROPBEAR_SUCCESS) { |
214 | 0 | encrypt_packet(); |
215 | 0 | return DROPBEAR_SUCCESS; |
216 | 0 | } else { |
217 | 0 | return DROPBEAR_FAILURE; |
218 | 0 | } |
219 | 0 | } |
220 | | |
221 | | /* helper for creating the agent socket-file |
222 | | returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ |
223 | 0 | static int bindagent(int fd, struct ChanSess * chansess) { |
224 | |
|
225 | 0 | struct sockaddr_un addr; |
226 | 0 | unsigned int prefix; |
227 | 0 | char path[(sizeof(addr.sun_path)-1)/2], sockfile[(sizeof(addr.sun_path)-1)/2]; |
228 | 0 | mode_t mode; |
229 | 0 | int i; |
230 | 0 | uid_t uid; |
231 | 0 | gid_t gid; |
232 | 0 | int ret = DROPBEAR_FAILURE; |
233 | |
|
234 | | #if !DROPBEAR_SVR_DROP_PRIVS |
235 | | /* drop to user privs to make the dir/file */ |
236 | | uid = getuid(); |
237 | | gid = getgid(); |
238 | | if ((setegid(ses.authstate.pw_gid)) < 0 || |
239 | | (seteuid(ses.authstate.pw_uid)) < 0) { |
240 | | dropbear_exit("Failed to set euid"); |
241 | | } |
242 | | #else |
243 | 0 | (void)uid; |
244 | 0 | (void)gid; |
245 | 0 | #endif |
246 | |
|
247 | 0 | memset((void*)&addr, 0x0, sizeof(addr)); |
248 | 0 | addr.sun_family = AF_UNIX; |
249 | |
|
250 | 0 | mode = S_IRWXU; |
251 | |
|
252 | 0 | for (i = 0; i < 20; i++) { |
253 | 0 | genrandom((unsigned char*)&prefix, sizeof(prefix)); |
254 | | /* we want 32 bits (8 hex digits) - "/tmp/dropbear-f19c62c0" */ |
255 | 0 | snprintf(path, sizeof(path), AGENTDIRPREFIX "%.8x", prefix); |
256 | |
|
257 | 0 | if (mkdir(path, mode) == 0) { |
258 | 0 | goto bindsocket; |
259 | 0 | } |
260 | 0 | if (errno != EEXIST) { |
261 | 0 | break; |
262 | 0 | } |
263 | 0 | } |
264 | | /* couldn't make a dir */ |
265 | 0 | goto out; |
266 | | |
267 | 0 | bindsocket: |
268 | | /* Format is "/tmp/dropbear-0246dead/auth-d00f7654-23". |
269 | | * The "23" is the file desc, the random data is to avoid collisions |
270 | | * between subsequent user processes reusing socket fds (odds are now |
271 | | * 1/(2^64) */ |
272 | 0 | genrandom((unsigned char*)&prefix, sizeof(prefix)); |
273 | 0 | snprintf(sockfile, sizeof(sockfile), "auth-%.8x-%d", prefix, fd); |
274 | | |
275 | 0 | snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", path, sockfile); |
276 | |
|
277 | 0 | if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) { |
278 | 0 | chansess->agentdir = m_strdup(path); |
279 | 0 | chansess->agentfile = m_strdup(sockfile); |
280 | 0 | ret = DROPBEAR_SUCCESS; |
281 | 0 | } |
282 | | |
283 | |
|
284 | 0 | out: |
285 | | #if !DROPBEAR_SVR_DROP_PRIVS |
286 | | if ((seteuid(uid)) < 0 || |
287 | | (setegid(gid)) < 0) { |
288 | | dropbear_exit("Failed to revert euid"); |
289 | | } |
290 | | #endif |
291 | 0 | return ret; |
292 | 0 | } |
293 | | |
294 | | #endif |