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