/src/dropbear/src/svr-agentfwd.c
Line | Count | Source (jump to first uncovered line) |
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 | 0 | int svr_agentreq(struct ChanSess * chansess) { |
53 | 0 | int fd = -1; |
54 | |
|
55 | 0 | if (!svr_pubkey_allows_agentfwd()) { |
56 | 0 | return DROPBEAR_FAILURE; |
57 | 0 | } |
58 | | |
59 | 0 | if (chansess->agentlistener != NULL) { |
60 | 0 | return DROPBEAR_FAILURE; |
61 | 0 | } |
62 | | |
63 | | /* create listening socket */ |
64 | 0 | fd = socket(PF_UNIX, SOCK_STREAM, 0); |
65 | 0 | if (fd < 0) { |
66 | 0 | goto fail; |
67 | 0 | } |
68 | | |
69 | | /* create the unix socket dir and file */ |
70 | 0 | if (bindagent(fd, chansess) == DROPBEAR_FAILURE) { |
71 | 0 | goto fail; |
72 | 0 | } |
73 | | |
74 | | /* listen */ |
75 | 0 | if (listen(fd, 20) < 0) { |
76 | 0 | goto fail; |
77 | 0 | } |
78 | | |
79 | | /* set non-blocking */ |
80 | 0 | setnonblocking(fd); |
81 | | |
82 | | /* pass if off to listener */ |
83 | 0 | chansess->agentlistener = new_listener( &fd, 1, 0, chansess, |
84 | 0 | agentaccept, NULL); |
85 | |
|
86 | 0 | if (chansess->agentlistener == NULL) { |
87 | 0 | goto fail; |
88 | 0 | } |
89 | | |
90 | 0 | return DROPBEAR_SUCCESS; |
91 | | |
92 | 0 | fail: |
93 | 0 | m_close(fd); |
94 | | /* cleanup */ |
95 | 0 | svr_agentcleanup(chansess); |
96 | |
|
97 | 0 | return DROPBEAR_FAILURE; |
98 | 0 | } |
99 | | |
100 | | /* accepts a connection on the forwarded socket and opens a new channel for it |
101 | | * back to the client */ |
102 | | /* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ |
103 | 0 | static void agentaccept(const struct Listener *UNUSED(listener), int sock) { |
104 | |
|
105 | 0 | int fd; |
106 | |
|
107 | 0 | fd = accept(sock, NULL, NULL); |
108 | 0 | if (fd < 0) { |
109 | 0 | TRACE(("accept failed")) |
110 | 0 | return; |
111 | 0 | } |
112 | | |
113 | 0 | if (send_msg_channel_open_agent(fd) != DROPBEAR_SUCCESS) { |
114 | 0 | close(fd); |
115 | 0 | } |
116 | |
|
117 | 0 | } |
118 | | |
119 | | /* set up the environment variable pointing to the socket. This is called |
120 | | * just before command/shell execution, after dropping privileges */ |
121 | 0 | void svr_agentset(const struct ChanSess * chansess) { |
122 | |
|
123 | 0 | char *path = NULL; |
124 | 0 | int len; |
125 | |
|
126 | 0 | if (chansess->agentlistener == NULL) { |
127 | 0 | return; |
128 | 0 | } |
129 | | |
130 | | /* 2 for "/" and "\0" */ |
131 | 0 | len = strlen(chansess->agentdir) + strlen(chansess->agentfile) + 2; |
132 | |
|
133 | 0 | path = m_malloc(len); |
134 | 0 | snprintf(path, len, "%s/%s", chansess->agentdir, chansess->agentfile); |
135 | 0 | addnewvar("SSH_AUTH_SOCK", path); |
136 | 0 | m_free(path); |
137 | 0 | } |
138 | | |
139 | | /* close the socket, remove the socket-file */ |
140 | 0 | void svr_agentcleanup(struct ChanSess * chansess) { |
141 | |
|
142 | 0 | char *path = NULL; |
143 | 0 | uid_t uid; |
144 | 0 | gid_t gid; |
145 | 0 | int len; |
146 | |
|
147 | 0 | if (chansess->agentlistener != NULL) { |
148 | 0 | remove_listener(chansess->agentlistener); |
149 | 0 | chansess->agentlistener = NULL; |
150 | 0 | } |
151 | |
|
152 | 0 | if (chansess->agentfile != NULL && chansess->agentdir != NULL) { |
153 | |
|
154 | 0 | #if DROPBEAR_SVR_MULTIUSER |
155 | | /* Remove the dir as the user. That way they can't cause problems except |
156 | | * for themselves */ |
157 | 0 | uid = getuid(); |
158 | 0 | gid = getgid(); |
159 | 0 | if ((setegid(ses.authstate.pw_gid)) < 0 || |
160 | 0 | (seteuid(ses.authstate.pw_uid)) < 0) { |
161 | 0 | dropbear_exit("Failed to set euid"); |
162 | 0 | } |
163 | 0 | #endif |
164 | | |
165 | | /* 2 for "/" and "\0" */ |
166 | 0 | len = strlen(chansess->agentdir) + strlen(chansess->agentfile) + 2; |
167 | |
|
168 | 0 | path = m_malloc(len); |
169 | 0 | snprintf(path, len, "%s/%s", chansess->agentdir, chansess->agentfile); |
170 | 0 | unlink(path); |
171 | 0 | m_free(path); |
172 | |
|
173 | 0 | rmdir(chansess->agentdir); |
174 | |
|
175 | 0 | #if DROPBEAR_SVR_MULTIUSER |
176 | 0 | if ((seteuid(uid)) < 0 || |
177 | 0 | (setegid(gid)) < 0) { |
178 | 0 | dropbear_exit("Failed to revert euid"); |
179 | 0 | } |
180 | 0 | #endif |
181 | | |
182 | 0 | m_free(chansess->agentfile); |
183 | 0 | m_free(chansess->agentdir); |
184 | 0 | } |
185 | |
|
186 | 0 | } |
187 | | |
188 | | static const struct ChanType chan_svr_agent = { |
189 | | "auth-agent@openssh.com", |
190 | | NULL, |
191 | | NULL, |
192 | | NULL, |
193 | | NULL, |
194 | | NULL |
195 | | }; |
196 | | |
197 | | |
198 | | /* helper for accepting an agent request */ |
199 | 0 | static int send_msg_channel_open_agent(int fd) { |
200 | |
|
201 | 0 | if (send_msg_channel_open_init(fd, &chan_svr_agent) == DROPBEAR_SUCCESS) { |
202 | 0 | encrypt_packet(); |
203 | 0 | return DROPBEAR_SUCCESS; |
204 | 0 | } else { |
205 | 0 | return DROPBEAR_FAILURE; |
206 | 0 | } |
207 | 0 | } |
208 | | |
209 | | /* helper for creating the agent socket-file |
210 | | returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ |
211 | 0 | static int bindagent(int fd, struct ChanSess * chansess) { |
212 | |
|
213 | 0 | struct sockaddr_un addr; |
214 | 0 | unsigned int prefix; |
215 | 0 | char path[(sizeof(addr.sun_path)-1)/2], sockfile[(sizeof(addr.sun_path)-1)/2]; |
216 | 0 | mode_t mode; |
217 | 0 | int i; |
218 | 0 | uid_t uid; |
219 | 0 | gid_t gid; |
220 | 0 | int ret = DROPBEAR_FAILURE; |
221 | |
|
222 | 0 | #if DROPBEAR_SVR_MULTIUSER |
223 | | /* drop to user privs to make the dir/file */ |
224 | 0 | uid = getuid(); |
225 | 0 | gid = getgid(); |
226 | 0 | if ((setegid(ses.authstate.pw_gid)) < 0 || |
227 | 0 | (seteuid(ses.authstate.pw_uid)) < 0) { |
228 | 0 | dropbear_exit("Failed to set euid"); |
229 | 0 | } |
230 | 0 | #endif |
231 | | |
232 | 0 | memset((void*)&addr, 0x0, sizeof(addr)); |
233 | 0 | addr.sun_family = AF_UNIX; |
234 | |
|
235 | 0 | mode = S_IRWXU; |
236 | |
|
237 | 0 | for (i = 0; i < 20; i++) { |
238 | 0 | genrandom((unsigned char*)&prefix, sizeof(prefix)); |
239 | | /* we want 32 bits (8 hex digits) - "/tmp/dropbear-f19c62c0" */ |
240 | 0 | snprintf(path, sizeof(path), AGENTDIRPREFIX "%.8x", prefix); |
241 | |
|
242 | 0 | if (mkdir(path, mode) == 0) { |
243 | 0 | goto bindsocket; |
244 | 0 | } |
245 | 0 | if (errno != EEXIST) { |
246 | 0 | break; |
247 | 0 | } |
248 | 0 | } |
249 | | /* couldn't make a dir */ |
250 | 0 | goto out; |
251 | | |
252 | 0 | bindsocket: |
253 | | /* Format is "/tmp/dropbear-0246dead/auth-d00f7654-23". |
254 | | * The "23" is the file desc, the random data is to avoid collisions |
255 | | * between subsequent user processes reusing socket fds (odds are now |
256 | | * 1/(2^64) */ |
257 | 0 | genrandom((unsigned char*)&prefix, sizeof(prefix)); |
258 | 0 | snprintf(sockfile, sizeof(sockfile), "auth-%.8x-%d", prefix, fd); |
259 | | |
260 | 0 | snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", path, sockfile); |
261 | |
|
262 | 0 | if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) { |
263 | 0 | chansess->agentdir = m_strdup(path); |
264 | 0 | chansess->agentfile = m_strdup(sockfile); |
265 | 0 | ret = DROPBEAR_SUCCESS; |
266 | 0 | } |
267 | | |
268 | |
|
269 | 0 | out: |
270 | 0 | #if DROPBEAR_SVR_MULTIUSER |
271 | 0 | if ((seteuid(uid)) < 0 || |
272 | 0 | (setegid(gid)) < 0) { |
273 | 0 | dropbear_exit("Failed to revert euid"); |
274 | 0 | } |
275 | 0 | #endif |
276 | 0 | return ret; |
277 | 0 | } |
278 | | |
279 | | #endif |