1 /*
2 * Copyright (c) 2009 by Daiki Ueno
3 * Copyright (C) 2010-2014 by Daniel Stenberg
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms,
7 * with or without modification, are permitted provided
8 * that the following conditions are met:
9 *
10 * Redistributions of source code must retain the above
11 * copyright notice, this list of conditions and the
12 * following disclaimer.
13 *
14 * Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials
17 * provided with the distribution.
18 *
19 * Neither the name of the copyright holder nor the names
20 * of any other contributors may be used to endorse or
21 * promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
25 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
26 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
34 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
36 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
37 * OF SUCH DAMAGE.
38 */
39
40 #include "libssh2_priv.h"
41 #include "agent.h"
42 #include "misc.h"
43 #include <errno.h>
44 #ifdef HAVE_SYS_UN_H
45 #include <sys/un.h>
46 #else
47 /* Use the existence of sys/un.h as a test if Unix domain socket is
48 supported. winsock*.h define PF_UNIX/AF_UNIX but do not actually
49 support them. */
50 #undef PF_UNIX
51 #endif
52 #include "userauth.h"
53 #include "session.h"
54 #ifdef WIN32
55 #include <stdlib.h>
56 #endif
57
58 #ifdef WIN32
59 /* Code to talk to OpenSSH was taken and modified from the Win32 port of
60 * Portable OpenSSH by the PowerShell team. Commit
61 * 8ab565c53f3619d6a1f5ac229e212cad8a52852c of
62 * https://github.com/PowerShell/openssh-portable.git was used as the base,
63 * specificaly the following files:
64 *
65 * - contrib\win32\win32compat\fileio.c
66 * - Structure of agent_connect_openssh from ssh_get_authentication_socket
67 * - Structure of agent_transact_openssh from ssh_request_reply
68 * - contrib\win32\win32compat\wmain_common.c
69 * - Windows equivalent functions for common Unix functions, inlined into
70 * this implementation
71 * - fileio_connect replacing connect
72 * - fileio_read replacing read
73 * - fileio_write replacing write
74 * - fileio_close replacing close
75 *
76 * Author: Tatu Ylonen <ylo@cs.hut.fi>
77 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
78 * All rights reserved
79 * Functions for connecting the local authentication agent.
80 *
81 * As far as I am concerned, the code I have written for this software
82 * can be used freely for any purpose. Any derived versions of this
83 * software must be clearly marked as such, and if the derived work is
84 * incompatible with the protocol description in the RFC file, it must be
85 * called by a name other than "ssh" or "Secure Shell".
86 *
87 * SSH2 implementation,
88 * Copyright (c) 2000 Markus Friedl. All rights reserved.
89 *
90 * Redistribution and use in source and binary forms, with or without
91 * modification, are permitted provided that the following conditions
92 * are met:
93 * 1. Redistributions of source code must retain the above copyright
94 * notice, this list of conditions and the following disclaimer.
95 * 2. Redistributions in binary form must reproduce the above copyright
96 * notice, this list of conditions and the following disclaimer in the
97 * documentation and/or other materials provided with the distribution.
98 *
99 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
100 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
101 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
102 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
103 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
104 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
105 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
106 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
107 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
108 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
109 *
110 * Copyright (c) 2015 Microsoft Corp.
111 * All rights reserved
112 *
113 * Microsoft openssh win32 port
114 *
115 * Redistribution and use in source and binary forms, with or without
116 * modification, are permitted provided that the following conditions
117 * are met:
118 *
119 * 1. Redistributions of source code must retain the above copyright
120 * notice, this list of conditions and the following disclaimer.
121 * 2. Redistributions in binary form must reproduce the above copyright
122 * notice, this list of conditions and the following disclaimer in the
123 * documentation and/or other materials provided with the distribution.
124 *
125 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
126 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
127 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
128 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
129 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
130 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
131 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
132 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
133 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
134 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
135 */
136
137 #define WIN32_OPENSSH_AGENT_SOCK "\\\\.\\pipe\\openssh-ssh-agent"
138
139 static int
agent_connect_openssh(LIBSSH2_AGENT * agent)140 agent_connect_openssh(LIBSSH2_AGENT *agent)
141 {
142 int ret = LIBSSH2_ERROR_NONE;
143 const char *path;
144 HANDLE pipe = INVALID_HANDLE_VALUE;
145 HANDLE event = NULL;
146
147 path = agent->identity_agent_path;
148 if(!path) {
149 path = getenv("SSH_AUTH_SOCK");
150 if(!path)
151 path = WIN32_OPENSSH_AGENT_SOCK;
152 }
153
154 for(;;) {
155 pipe = CreateFileA(
156 path,
157 GENERIC_READ | GENERIC_WRITE,
158 0,
159 NULL,
160 OPEN_EXISTING,
161 /* Non-blocking mode for agent connections is not implemented at
162 * the point this was implemented. The code for Win32 OpenSSH
163 * should support non-blocking IO, but the code calling it doesn't
164 * support it as of yet.
165 * When non-blocking IO is implemented for the surrounding code,
166 * uncomment the following line to enable support within the Win32
167 * OpenSSH code.
168 */
169 /* FILE_FLAG_OVERLAPPED | */
170 SECURITY_SQOS_PRESENT |
171 SECURITY_IDENTIFICATION,
172 NULL
173 );
174
175 if(pipe != INVALID_HANDLE_VALUE)
176 break;
177 if(GetLastError() != ERROR_PIPE_BUSY)
178 break;
179
180 /* Wait up to 1 second for a pipe instance to become available */
181 if(!WaitNamedPipeA(path, 1000))
182 break;
183 }
184
185 if(pipe == INVALID_HANDLE_VALUE) {
186 ret = _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
187 "unable to connect to agent pipe");
188 goto cleanup;
189 }
190
191 if(SetHandleInformation(pipe, HANDLE_FLAG_INHERIT, 0) == FALSE) {
192 ret = _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
193 "unable to set handle information of agent pipe");
194 goto cleanup;
195 }
196
197 event = CreateEventA(NULL, TRUE, FALSE, NULL);
198 if(event == NULL) {
199 ret = _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
200 "unable to create async I/O event");
201 goto cleanup;
202 }
203
204 agent->pipe = pipe;
205 pipe = INVALID_HANDLE_VALUE;
206 agent->overlapped.hEvent = event;
207 event = NULL;
208 agent->fd = 0; /* Mark as the connection has been established */
209
210 cleanup:
211 if(event != NULL)
212 CloseHandle(event);
213 if(pipe != INVALID_HANDLE_VALUE)
214 CloseHandle(pipe);
215 return ret;
216 }
217
218 #define RECV_SEND_ALL(func, agent, buffer, length, total) \
219 DWORD bytes_transfered; \
220 BOOL ret; \
221 DWORD err; \
222 int rc; \
223 \
224 while(*total < length) { \
225 if(!agent->pending_io) \
226 ret = func(agent->pipe, (char *)buffer + *total, \
227 (DWORD)(length - *total), &bytes_transfered, \
228 &agent->overlapped); \
229 else \
230 ret = GetOverlappedResult(agent->pipe, &agent->overlapped, \
231 &bytes_transfered, FALSE); \
232 \
233 *total += bytes_transfered; \
234 if(!ret) { \
235 err = GetLastError(); \
236 if((!agent->pending_io && ERROR_IO_PENDING == err) \
237 || (agent->pending_io && ERROR_IO_INCOMPLETE == err)) { \
238 agent->pending_io = TRUE; \
239 return LIBSSH2_ERROR_EAGAIN; \
240 } \
241 \
242 return LIBSSH2_ERROR_SOCKET_NONE; \
243 } \
244 agent->pending_io = FALSE; \
245 } \
246 \
247 rc = (int)*total; \
248 *total = 0; \
249 return rc;
250
251 static int
win32_openssh_send_all(LIBSSH2_AGENT * agent,void * buffer,size_t length,size_t * send_recv_total)252 win32_openssh_send_all(LIBSSH2_AGENT *agent, void *buffer, size_t length,
253 size_t *send_recv_total)
254 {
255 RECV_SEND_ALL(WriteFile, agent, buffer, length, send_recv_total)
256 }
257
258 static int
win32_openssh_recv_all(LIBSSH2_AGENT * agent,void * buffer,size_t length,size_t * send_recv_total)259 win32_openssh_recv_all(LIBSSH2_AGENT *agent, void *buffer, size_t length,
260 size_t *send_recv_total)
261 {
262 RECV_SEND_ALL(ReadFile, agent, buffer, length, send_recv_total)
263 }
264
265 #undef RECV_SEND_ALL
266
267 static int
agent_transact_openssh(LIBSSH2_AGENT * agent,agent_transaction_ctx_t transctx)268 agent_transact_openssh(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx)
269 {
270 unsigned char buf[4];
271 int rc;
272
273 /* Send the length of the request */
274 if(transctx->state == agent_NB_state_request_created) {
275 _libssh2_htonu32(buf, (uint32_t)transctx->request_len);
276 rc = win32_openssh_send_all(agent, buf, sizeof buf,
277 &transctx->send_recv_total);
278 if(rc == LIBSSH2_ERROR_EAGAIN)
279 return LIBSSH2_ERROR_EAGAIN;
280 else if(rc < 0)
281 return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND,
282 "agent send failed");
283 transctx->state = agent_NB_state_request_length_sent;
284 }
285
286 /* Send the request body */
287 if(transctx->state == agent_NB_state_request_length_sent) {
288 rc = win32_openssh_send_all(agent, transctx->request,
289 transctx->request_len,
290 &transctx->send_recv_total);
291 if(rc == LIBSSH2_ERROR_EAGAIN)
292 return LIBSSH2_ERROR_EAGAIN;
293 else if(rc < 0)
294 return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND,
295 "agent send failed");
296 transctx->state = agent_NB_state_request_sent;
297 }
298
299 /* Receive the length of the body */
300 if(transctx->state == agent_NB_state_request_sent) {
301 rc = win32_openssh_recv_all(agent, buf, sizeof buf,
302 &transctx->send_recv_total);
303 if(rc == LIBSSH2_ERROR_EAGAIN)
304 return LIBSSH2_ERROR_EAGAIN;
305 else if(rc < 0)
306 return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_RECV,
307 "agent recv failed");
308
309 transctx->response_len = _libssh2_ntohu32(buf);
310 transctx->response = LIBSSH2_ALLOC(agent->session,
311 transctx->response_len);
312 if(!transctx->response)
313 return LIBSSH2_ERROR_ALLOC;
314
315 transctx->state = agent_NB_state_response_length_received;
316 }
317
318 /* Receive the response body */
319 if(transctx->state == agent_NB_state_response_length_received) {
320 rc = win32_openssh_recv_all(agent, transctx->response,
321 transctx->response_len,
322 &transctx->send_recv_total);
323 if(rc == LIBSSH2_ERROR_EAGAIN)
324 return LIBSSH2_ERROR_EAGAIN;
325 else if(rc < 0)
326 return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_RECV,
327 "agent recv failed");
328 transctx->state = agent_NB_state_response_received;
329 }
330
331 return LIBSSH2_ERROR_NONE;
332 }
333
334 static int
agent_disconnect_openssh(LIBSSH2_AGENT * agent)335 agent_disconnect_openssh(LIBSSH2_AGENT *agent)
336 {
337 if(!CancelIo(agent->pipe))
338 return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_DISCONNECT,
339 "failed to cancel pending IO of agent pipe");
340 if(!CloseHandle(agent->overlapped.hEvent))
341 return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_DISCONNECT,
342 "failed to close handle to async I/O event");
343 agent->overlapped.hEvent = NULL;
344 /* let queued APCs (if any) drain */
345 SleepEx(0, TRUE);
346 if(!CloseHandle(agent->pipe))
347 return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_DISCONNECT,
348 "failed to close handle to agent pipe");
349
350 agent->pipe = INVALID_HANDLE_VALUE;
351 agent->fd = LIBSSH2_INVALID_SOCKET;
352
353 return LIBSSH2_ERROR_NONE;
354 }
355
356 struct agent_ops agent_ops_openssh = {
357 agent_connect_openssh,
358 agent_transact_openssh,
359 agent_disconnect_openssh
360 };
361 #endif /* WIN32 */
362