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 "misc.h"
42 #include <errno.h>
43 #ifdef HAVE_SYS_UN_H
44 #include <sys/un.h>
45 #else
46 /* Use the existence of sys/un.h as a test if Unix domain socket is
47    supported.  winsock*.h define PF_UNIX/AF_UNIX but do not actually
48    support them. */
49 #undef PF_UNIX
50 #endif
51 #include "userauth.h"
52 #include "session.h"
53 
54 /* Requests from client to agent for protocol 1 key operations */
55 #define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1
56 #define SSH_AGENTC_RSA_CHALLENGE 3
57 #define SSH_AGENTC_ADD_RSA_IDENTITY 7
58 #define SSH_AGENTC_REMOVE_RSA_IDENTITY 8
59 #define SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES 9
60 #define SSH_AGENTC_ADD_RSA_ID_CONSTRAINED 24
61 
62 /* Requests from client to agent for protocol 2 key operations */
63 #define SSH2_AGENTC_REQUEST_IDENTITIES 11
64 #define SSH2_AGENTC_SIGN_REQUEST 13
65 #define SSH2_AGENTC_ADD_IDENTITY 17
66 #define SSH2_AGENTC_REMOVE_IDENTITY 18
67 #define SSH2_AGENTC_REMOVE_ALL_IDENTITIES 19
68 #define SSH2_AGENTC_ADD_ID_CONSTRAINED 25
69 
70 /* Key-type independent requests from client to agent */
71 #define SSH_AGENTC_ADD_SMARTCARD_KEY 20
72 #define SSH_AGENTC_REMOVE_SMARTCARD_KEY 21
73 #define SSH_AGENTC_LOCK 22
74 #define SSH_AGENTC_UNLOCK 23
75 #define SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED 26
76 
77 /* Generic replies from agent to client */
78 #define SSH_AGENT_FAILURE 5
79 #define SSH_AGENT_SUCCESS 6
80 
81 /* Replies from agent to client for protocol 1 key operations */
82 #define SSH_AGENT_RSA_IDENTITIES_ANSWER 2
83 #define SSH_AGENT_RSA_RESPONSE 4
84 
85 /* Replies from agent to client for protocol 2 key operations */
86 #define SSH2_AGENT_IDENTITIES_ANSWER 12
87 #define SSH2_AGENT_SIGN_RESPONSE 14
88 
89 /* Key constraint identifiers */
90 #define SSH_AGENT_CONSTRAIN_LIFETIME 1
91 #define SSH_AGENT_CONSTRAIN_CONFIRM 2
92 
93 /* non-blocking mode on agent connection is not yet implemented, but
94    for future use. */
95 typedef enum {
96     agent_NB_state_init = 0,
97     agent_NB_state_request_created,
98     agent_NB_state_request_length_sent,
99     agent_NB_state_request_sent,
100     agent_NB_state_response_length_received,
101     agent_NB_state_response_received
102 } agent_nonblocking_states;
103 
104 typedef struct agent_transaction_ctx {
105     unsigned char *request;
106     size_t request_len;
107     unsigned char *response;
108     size_t response_len;
109     agent_nonblocking_states state;
110 } *agent_transaction_ctx_t;
111 
112 typedef int (*agent_connect_func)(LIBSSH2_AGENT *agent);
113 typedef int (*agent_transact_func)(LIBSSH2_AGENT *agent,
114                                    agent_transaction_ctx_t transctx);
115 typedef int (*agent_disconnect_func)(LIBSSH2_AGENT *agent);
116 
117 struct agent_publickey {
118     struct list_node node;
119 
120     /* this is the struct we expose externally */
121     struct libssh2_agent_publickey external;
122 };
123 
124 struct agent_ops {
125     agent_connect_func connect;
126     agent_transact_func transact;
127     agent_disconnect_func disconnect;
128 };
129 
130 struct _LIBSSH2_AGENT
131 {
132     LIBSSH2_SESSION *session;  /* the session this "belongs to" */
133 
134     libssh2_socket_t fd;
135 
136     struct agent_ops *ops;
137 
138     struct agent_transaction_ctx transctx;
139     struct agent_publickey *identity;
140     struct list_head head;              /* list of public keys */
141 
142     char *identity_agent_path; /* Path to a custom identity agent socket */
143 };
144 
145 #ifdef PF_UNIX
146 static int
agent_connect_unix(LIBSSH2_AGENT * agent)147 agent_connect_unix(LIBSSH2_AGENT *agent)
148 {
149     const char *path;
150     struct sockaddr_un s_un;
151 
152     path = agent->identity_agent_path;
153     if(!path) {
154         path = getenv("SSH_AUTH_SOCK");
155         if(!path)
156             return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_USE,
157                                   "no auth sock variable");
158     }
159 
160     agent->fd = socket(PF_UNIX, SOCK_STREAM, 0);
161     if(agent->fd < 0)
162         return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_SOCKET,
163                               "failed creating socket");
164 
165     s_un.sun_family = AF_UNIX;
166     strncpy(s_un.sun_path, path, sizeof s_un.sun_path);
167     s_un.sun_path[sizeof(s_un.sun_path)-1] = 0; /* make sure there's a trailing
168                                                    zero */
169     if(connect(agent->fd, (struct sockaddr*)(&s_un), sizeof s_un) != 0) {
170         close(agent->fd);
171         return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
172                               "failed connecting with agent");
173     }
174 
175     return LIBSSH2_ERROR_NONE;
176 }
177 
178 static int
agent_transact_unix(LIBSSH2_AGENT * agent,agent_transaction_ctx_t transctx)179 agent_transact_unix(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx)
180 {
181     unsigned char buf[4];
182     int rc;
183 
184     /* Send the length of the request */
185     if(transctx->state == agent_NB_state_request_created) {
186         _libssh2_htonu32(buf, transctx->request_len);
187         rc = LIBSSH2_SEND_FD(agent->session, agent->fd, buf, sizeof buf, 0);
188         if(rc == -EAGAIN)
189             return LIBSSH2_ERROR_EAGAIN;
190         else if(rc < 0)
191             return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND,
192                                   "agent send failed");
193         transctx->state = agent_NB_state_request_length_sent;
194     }
195 
196     /* Send the request body */
197     if(transctx->state == agent_NB_state_request_length_sent) {
198         rc = LIBSSH2_SEND_FD(agent->session, agent->fd, transctx->request,
199                            transctx->request_len, 0);
200         if(rc == -EAGAIN)
201             return LIBSSH2_ERROR_EAGAIN;
202         else if(rc < 0)
203             return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND,
204                                   "agent send failed");
205         transctx->state = agent_NB_state_request_sent;
206     }
207 
208     /* Receive the length of a response */
209     if(transctx->state == agent_NB_state_request_sent) {
210         rc = LIBSSH2_RECV_FD(agent->session, agent->fd, buf, sizeof buf, 0);
211         if(rc < 0) {
212             if(rc == -EAGAIN)
213                 return LIBSSH2_ERROR_EAGAIN;
214             return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_RECV,
215                                   "agent recv failed");
216         }
217         transctx->response_len = _libssh2_ntohu32(buf);
218         transctx->response = LIBSSH2_ALLOC(agent->session,
219                                            transctx->response_len);
220         if(!transctx->response)
221             return LIBSSH2_ERROR_ALLOC;
222 
223         transctx->state = agent_NB_state_response_length_received;
224     }
225 
226     /* Receive the response body */
227     if(transctx->state == agent_NB_state_response_length_received) {
228         rc = LIBSSH2_RECV_FD(agent->session, agent->fd, transctx->response,
229                            transctx->response_len, 0);
230         if(rc < 0) {
231             if(rc == -EAGAIN)
232                 return LIBSSH2_ERROR_EAGAIN;
233             return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND,
234                                   "agent recv failed");
235         }
236         transctx->state = agent_NB_state_response_received;
237     }
238 
239     return 0;
240 }
241 
242 static int
agent_disconnect_unix(LIBSSH2_AGENT * agent)243 agent_disconnect_unix(LIBSSH2_AGENT *agent)
244 {
245     int ret;
246     ret = close(agent->fd);
247     if(ret != -1)
248         agent->fd = LIBSSH2_INVALID_SOCKET;
249     else
250         return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_DISCONNECT,
251                               "failed closing the agent socket");
252     return LIBSSH2_ERROR_NONE;
253 }
254 
255 struct agent_ops agent_ops_unix = {
256     agent_connect_unix,
257     agent_transact_unix,
258     agent_disconnect_unix
259 };
260 #endif  /* PF_UNIX */
261 
262 #ifdef WIN32
263 /* Code to talk to Pageant was taken from PuTTY.
264  *
265  * Portions copyright Robert de Bath, Joris van Rantwijk, Delian
266  * Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas
267  * Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa,
268  * Markus Kuhn, Colin Watson, and CORE SDI S.A.
269  */
270 #define PAGEANT_COPYDATA_ID 0x804e50ba   /* random goop */
271 #define PAGEANT_MAX_MSGLEN  8192
272 
273 static int
agent_connect_pageant(LIBSSH2_AGENT * agent)274 agent_connect_pageant(LIBSSH2_AGENT *agent)
275 {
276     HWND hwnd;
277     hwnd = FindWindow("Pageant", "Pageant");
278     if(!hwnd)
279         return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
280                               "failed connecting agent");
281     agent->fd = 0;         /* Mark as the connection has been established */
282     return LIBSSH2_ERROR_NONE;
283 }
284 
285 static int
agent_transact_pageant(LIBSSH2_AGENT * agent,agent_transaction_ctx_t transctx)286 agent_transact_pageant(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx)
287 {
288     HWND hwnd;
289     char mapname[23];
290     HANDLE filemap;
291     unsigned char *p;
292     unsigned char *p2;
293     int id;
294     COPYDATASTRUCT cds;
295 
296     if(!transctx || 4 + transctx->request_len > PAGEANT_MAX_MSGLEN)
297         return _libssh2_error(agent->session, LIBSSH2_ERROR_INVAL,
298                               "illegal input");
299 
300     hwnd = FindWindow("Pageant", "Pageant");
301     if(!hwnd)
302         return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
303                               "found no pageant");
304 
305     snprintf(mapname, sizeof(mapname),
306              "PageantRequest%08x%c", (unsigned)GetCurrentThreadId(), '\0');
307     filemap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
308                                 0, PAGEANT_MAX_MSGLEN, mapname);
309 
310     if(filemap == NULL || filemap == INVALID_HANDLE_VALUE)
311         return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
312                               "failed setting up pageant filemap");
313 
314     p2 = p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
315     if(p == NULL || p2 == NULL) {
316         CloseHandle(filemap);
317         return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
318                               "failed to open pageant filemap for writing");
319     }
320 
321     _libssh2_store_str(&p2, (const char *)transctx->request,
322                        transctx->request_len);
323 
324     cds.dwData = PAGEANT_COPYDATA_ID;
325     cds.cbData = 1 + strlen(mapname);
326     cds.lpData = mapname;
327 
328     id = SendMessage(hwnd, WM_COPYDATA, (WPARAM) NULL, (LPARAM) &cds);
329     if(id > 0) {
330         transctx->response_len = _libssh2_ntohu32(p);
331         if(transctx->response_len > PAGEANT_MAX_MSGLEN) {
332             UnmapViewOfFile(p);
333             CloseHandle(filemap);
334             return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
335                                   "agent setup fail");
336         }
337         transctx->response = LIBSSH2_ALLOC(agent->session,
338                                            transctx->response_len);
339         if(!transctx->response) {
340             UnmapViewOfFile(p);
341             CloseHandle(filemap);
342             return _libssh2_error(agent->session, LIBSSH2_ERROR_ALLOC,
343                                   "agent malloc");
344         }
345         memcpy(transctx->response, p + 4, transctx->response_len);
346     }
347 
348     UnmapViewOfFile(p);
349     CloseHandle(filemap);
350     return 0;
351 }
352 
353 static int
agent_disconnect_pageant(LIBSSH2_AGENT * agent)354 agent_disconnect_pageant(LIBSSH2_AGENT *agent)
355 {
356     agent->fd = LIBSSH2_INVALID_SOCKET;
357     return 0;
358 }
359 
360 struct agent_ops agent_ops_pageant = {
361     agent_connect_pageant,
362     agent_transact_pageant,
363     agent_disconnect_pageant
364 };
365 #endif  /* WIN32 */
366 
367 static struct {
368     const char *name;
369     struct agent_ops *ops;
370 } supported_backends[] = {
371 #ifdef WIN32
372     {"Pageant", &agent_ops_pageant},
373 #endif  /* WIN32 */
374 #ifdef PF_UNIX
375     {"Unix", &agent_ops_unix},
376 #endif  /* PF_UNIX */
377     {NULL, NULL}
378 };
379 
380 static int
agent_sign(LIBSSH2_SESSION * session,unsigned char ** sig,size_t * sig_len,const unsigned char * data,size_t data_len,void ** abstract)381 agent_sign(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len,
382            const unsigned char *data, size_t data_len, void **abstract)
383 {
384     LIBSSH2_AGENT *agent = (LIBSSH2_AGENT *) (*abstract);
385     agent_transaction_ctx_t transctx = &agent->transctx;
386     struct agent_publickey *identity = agent->identity;
387     ssize_t len = 1 + 4 + identity->external.blob_len + 4 + data_len + 4;
388     ssize_t method_len;
389     unsigned char *s;
390     int rc;
391 
392     /* Create a request to sign the data */
393     if(transctx->state == agent_NB_state_init) {
394         s = transctx->request = LIBSSH2_ALLOC(session, len);
395         if(!transctx->request)
396             return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
397                                   "out of memory");
398 
399         *s++ = SSH2_AGENTC_SIGN_REQUEST;
400         /* key blob */
401         _libssh2_store_str(&s, (const char *)identity->external.blob,
402                            identity->external.blob_len);
403         /* data */
404         _libssh2_store_str(&s, (const char *)data, data_len);
405 
406         /* flags */
407         _libssh2_store_u32(&s, 0);
408 
409         transctx->request_len = s - transctx->request;
410         transctx->state = agent_NB_state_request_created;
411     }
412 
413     /* Make sure to be re-called as a result of EAGAIN. */
414     if(*transctx->request != SSH2_AGENTC_SIGN_REQUEST)
415         return _libssh2_error(session, LIBSSH2_ERROR_BAD_USE,
416                               "illegal request");
417 
418     if(!agent->ops)
419         /* if no agent has been connected, bail out */
420         return _libssh2_error(session, LIBSSH2_ERROR_BAD_USE,
421                               "agent not connected");
422 
423     rc = agent->ops->transact(agent, transctx);
424     if(rc) {
425         goto error;
426     }
427     LIBSSH2_FREE(session, transctx->request);
428     transctx->request = NULL;
429 
430     len = transctx->response_len;
431     s = transctx->response;
432     len--;
433     if(len < 0) {
434         rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
435         goto error;
436     }
437     if(*s != SSH2_AGENT_SIGN_RESPONSE) {
438         rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
439         goto error;
440     }
441     s++;
442 
443     /* Skip the entire length of the signature */
444     len -= 4;
445     if(len < 0) {
446         rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
447         goto error;
448     }
449     s += 4;
450 
451     /* Skip signing method */
452     len -= 4;
453     if(len < 0) {
454         rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
455         goto error;
456     }
457     method_len = _libssh2_ntohu32(s);
458     s += 4;
459     len -= method_len;
460     if(len < 0) {
461         rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
462         goto error;
463     }
464     s += method_len;
465 
466     /* Read the signature */
467     len -= 4;
468     if(len < 0) {
469         rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
470         goto error;
471     }
472     *sig_len = _libssh2_ntohu32(s);
473     s += 4;
474     len -= *sig_len;
475     if(len < 0) {
476         rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
477         goto error;
478     }
479 
480     *sig = LIBSSH2_ALLOC(session, *sig_len);
481     if(!*sig) {
482         rc = LIBSSH2_ERROR_ALLOC;
483         goto error;
484     }
485     memcpy(*sig, s, *sig_len);
486 
487   error:
488     LIBSSH2_FREE(session, transctx->request);
489     transctx->request = NULL;
490 
491     LIBSSH2_FREE(session, transctx->response);
492     transctx->response = NULL;
493 
494     return _libssh2_error(session, rc, "agent sign failure");
495 }
496 
497 static int
agent_list_identities(LIBSSH2_AGENT * agent)498 agent_list_identities(LIBSSH2_AGENT *agent)
499 {
500     agent_transaction_ctx_t transctx = &agent->transctx;
501     ssize_t len, num_identities;
502     unsigned char *s;
503     int rc;
504     unsigned char c = SSH2_AGENTC_REQUEST_IDENTITIES;
505 
506     /* Create a request to list identities */
507     if(transctx->state == agent_NB_state_init) {
508         transctx->request = &c;
509         transctx->request_len = 1;
510         transctx->state = agent_NB_state_request_created;
511     }
512 
513     /* Make sure to be re-called as a result of EAGAIN. */
514     if(*transctx->request != SSH2_AGENTC_REQUEST_IDENTITIES)
515         return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_USE,
516                               "illegal agent request");
517 
518     if(!agent->ops)
519         /* if no agent has been connected, bail out */
520         return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_USE,
521                               "agent not connected");
522 
523     rc = agent->ops->transact(agent, transctx);
524     if(rc) {
525         LIBSSH2_FREE(agent->session, transctx->response);
526         transctx->response = NULL;
527         return rc;
528     }
529     transctx->request = NULL;
530 
531     len = transctx->response_len;
532     s = transctx->response;
533     len--;
534     if(len < 0) {
535         rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
536         goto error;
537     }
538     if(*s != SSH2_AGENT_IDENTITIES_ANSWER) {
539         rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
540         goto error;
541     }
542     s++;
543 
544     /* Read the length of identities */
545     len -= 4;
546     if(len < 0) {
547         rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
548         goto error;
549     }
550     num_identities = _libssh2_ntohu32(s);
551     s += 4;
552 
553     while(num_identities--) {
554         struct agent_publickey *identity;
555         ssize_t comment_len;
556 
557         /* Read the length of the blob */
558         len -= 4;
559         if(len < 0) {
560             rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
561             goto error;
562         }
563         identity = LIBSSH2_ALLOC(agent->session, sizeof *identity);
564         if(!identity) {
565             rc = LIBSSH2_ERROR_ALLOC;
566             goto error;
567         }
568         identity->external.blob_len = _libssh2_ntohu32(s);
569         s += 4;
570 
571         /* Read the blob */
572         len -= identity->external.blob_len;
573         if(len < 0) {
574             rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
575             LIBSSH2_FREE(agent->session, identity);
576             goto error;
577         }
578 
579         identity->external.blob = LIBSSH2_ALLOC(agent->session,
580                                                 identity->external.blob_len);
581         if(!identity->external.blob) {
582             rc = LIBSSH2_ERROR_ALLOC;
583             LIBSSH2_FREE(agent->session, identity);
584             goto error;
585         }
586         memcpy(identity->external.blob, s, identity->external.blob_len);
587         s += identity->external.blob_len;
588 
589         /* Read the length of the comment */
590         len -= 4;
591         if(len < 0) {
592             rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
593             LIBSSH2_FREE(agent->session, identity->external.blob);
594             LIBSSH2_FREE(agent->session, identity);
595             goto error;
596         }
597         comment_len = _libssh2_ntohu32(s);
598         s += 4;
599 
600         /* Read the comment */
601         len -= comment_len;
602         if(len < 0) {
603             rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
604             LIBSSH2_FREE(agent->session, identity->external.blob);
605             LIBSSH2_FREE(agent->session, identity);
606             goto error;
607         }
608 
609         identity->external.comment = LIBSSH2_ALLOC(agent->session,
610                                                    comment_len + 1);
611         if(!identity->external.comment) {
612             rc = LIBSSH2_ERROR_ALLOC;
613             LIBSSH2_FREE(agent->session, identity->external.blob);
614             LIBSSH2_FREE(agent->session, identity);
615             goto error;
616         }
617         identity->external.comment[comment_len] = '\0';
618         memcpy(identity->external.comment, s, comment_len);
619         s += comment_len;
620 
621         _libssh2_list_add(&agent->head, &identity->node);
622     }
623  error:
624     LIBSSH2_FREE(agent->session, transctx->response);
625     transctx->response = NULL;
626 
627     return _libssh2_error(agent->session, rc,
628                           "agent list id failed");
629 }
630 
631 static void
agent_free_identities(LIBSSH2_AGENT * agent)632 agent_free_identities(LIBSSH2_AGENT *agent)
633 {
634     struct agent_publickey *node;
635     struct agent_publickey *next;
636 
637     for(node = _libssh2_list_first(&agent->head); node; node = next) {
638         next = _libssh2_list_next(&node->node);
639         LIBSSH2_FREE(agent->session, node->external.blob);
640         LIBSSH2_FREE(agent->session, node->external.comment);
641         LIBSSH2_FREE(agent->session, node);
642     }
643     _libssh2_list_init(&agent->head);
644 }
645 
646 #define AGENT_PUBLICKEY_MAGIC 0x3bdefed2
647 /*
648  * agent_publickey_to_external()
649  *
650  * Copies data from the internal to the external representation struct.
651  *
652  */
653 static struct libssh2_agent_publickey *
agent_publickey_to_external(struct agent_publickey * node)654 agent_publickey_to_external(struct agent_publickey *node)
655 {
656     struct libssh2_agent_publickey *ext = &node->external;
657 
658     ext->magic = AGENT_PUBLICKEY_MAGIC;
659     ext->node = node;
660 
661     return ext;
662 }
663 
664 /*
665  * libssh2_agent_init
666  *
667  * Init an ssh-agent handle. Returns the pointer to the handle.
668  *
669  */
670 LIBSSH2_API LIBSSH2_AGENT *
libssh2_agent_init(LIBSSH2_SESSION * session)671 libssh2_agent_init(LIBSSH2_SESSION *session)
672 {
673     LIBSSH2_AGENT *agent;
674 
675     agent = LIBSSH2_CALLOC(session, sizeof *agent);
676     if(!agent) {
677         _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
678                        "Unable to allocate space for agent connection");
679         return NULL;
680     }
681     agent->fd = LIBSSH2_INVALID_SOCKET;
682     agent->session = session;
683     agent->identity_agent_path = NULL;
684     _libssh2_list_init(&agent->head);
685 
686     return agent;
687 }
688 
689 /*
690  * libssh2_agent_connect()
691  *
692  * Connect to an ssh-agent.
693  *
694  * Returns 0 if succeeded, or a negative value for error.
695  */
696 LIBSSH2_API int
libssh2_agent_connect(LIBSSH2_AGENT * agent)697 libssh2_agent_connect(LIBSSH2_AGENT *agent)
698 {
699     int i, rc = -1;
700     for(i = 0; supported_backends[i].name; i++) {
701         agent->ops = supported_backends[i].ops;
702         rc = (agent->ops->connect)(agent);
703         if(!rc)
704             return 0;
705     }
706     return rc;
707 }
708 
709 /*
710  * libssh2_agent_list_identities()
711  *
712  * Request ssh-agent to list identities.
713  *
714  * Returns 0 if succeeded, or a negative value for error.
715  */
716 LIBSSH2_API int
libssh2_agent_list_identities(LIBSSH2_AGENT * agent)717 libssh2_agent_list_identities(LIBSSH2_AGENT *agent)
718 {
719     memset(&agent->transctx, 0, sizeof agent->transctx);
720     /* Abandon the last fetched identities */
721     agent_free_identities(agent);
722     return agent_list_identities(agent);
723 }
724 
725 /*
726  * libssh2_agent_get_identity()
727  *
728  * Traverse the internal list of public keys. Pass NULL to 'prev' to get
729  * the first one. Or pass a pointer to the previously returned one to get the
730  * next.
731  *
732  * Returns:
733  * 0 if a fine public key was stored in 'store'
734  * 1 if end of public keys
735  * [negative] on errors
736  */
737 LIBSSH2_API int
libssh2_agent_get_identity(LIBSSH2_AGENT * agent,struct libssh2_agent_publickey ** ext,struct libssh2_agent_publickey * oprev)738 libssh2_agent_get_identity(LIBSSH2_AGENT *agent,
739                            struct libssh2_agent_publickey **ext,
740                            struct libssh2_agent_publickey *oprev)
741 {
742     struct agent_publickey *node;
743     if(oprev && oprev->node) {
744         /* we have a starting point */
745         struct agent_publickey *prev = oprev->node;
746 
747         /* get the next node in the list */
748         node = _libssh2_list_next(&prev->node);
749     }
750     else
751         node = _libssh2_list_first(&agent->head);
752 
753     if(!node)
754         /* no (more) node */
755         return 1;
756 
757     *ext = agent_publickey_to_external(node);
758 
759     return 0;
760 }
761 
762 /*
763  * libssh2_agent_userauth()
764  *
765  * Do publickey user authentication with the help of ssh-agent.
766  *
767  * Returns 0 if succeeded, or a negative value for error.
768  */
769 LIBSSH2_API int
libssh2_agent_userauth(LIBSSH2_AGENT * agent,const char * username,struct libssh2_agent_publickey * identity)770 libssh2_agent_userauth(LIBSSH2_AGENT *agent,
771                        const char *username,
772                        struct libssh2_agent_publickey *identity)
773 {
774     void *abstract = agent;
775     int rc;
776 
777     if(agent->session->userauth_pblc_state == libssh2_NB_state_idle) {
778         memset(&agent->transctx, 0, sizeof agent->transctx);
779         agent->identity = identity->node;
780     }
781 
782     BLOCK_ADJUST(rc, agent->session,
783                  _libssh2_userauth_publickey(agent->session, username,
784                                              strlen(username),
785                                              identity->blob,
786                                              identity->blob_len,
787                                              agent_sign,
788                                              &abstract));
789     return rc;
790 }
791 
792 /*
793  * libssh2_agent_disconnect()
794  *
795  * Close a connection to an ssh-agent.
796  *
797  * Returns 0 if succeeded, or a negative value for error.
798  */
799 LIBSSH2_API int
libssh2_agent_disconnect(LIBSSH2_AGENT * agent)800 libssh2_agent_disconnect(LIBSSH2_AGENT *agent)
801 {
802     if(agent->ops && agent->fd != LIBSSH2_INVALID_SOCKET)
803         return agent->ops->disconnect(agent);
804     return 0;
805 }
806 
807 /*
808  * libssh2_agent_free()
809  *
810  * Free an ssh-agent handle.  This function also frees the internal
811  * collection of public keys.
812  */
813 LIBSSH2_API void
libssh2_agent_free(LIBSSH2_AGENT * agent)814 libssh2_agent_free(LIBSSH2_AGENT *agent)
815 {
816     /* Allow connection freeing when the socket has lost its connection */
817     if(agent->fd != LIBSSH2_INVALID_SOCKET) {
818         libssh2_agent_disconnect(agent);
819     }
820 
821     if(agent->identity_agent_path != NULL)
822         LIBSSH2_FREE(agent->session, agent->identity_agent_path);
823 
824     agent_free_identities(agent);
825     LIBSSH2_FREE(agent->session, agent);
826 }
827 
828 /*
829  * libssh2_agent_set_identity_path()
830  *
831  * Allows a custom agent socket path beyond SSH_AUTH_SOCK env
832  *
833  */
834 LIBSSH2_API void
libssh2_agent_set_identity_path(LIBSSH2_AGENT * agent,const char * path)835 libssh2_agent_set_identity_path(LIBSSH2_AGENT *agent, const char *path)
836 {
837     if(agent->identity_agent_path) {
838         LIBSSH2_FREE(agent->session, agent->identity_agent_path);
839         agent->identity_agent_path = NULL;
840     }
841 
842     if(path) {
843         size_t path_len = strlen(path);
844         if(path_len < SIZE_MAX - 1) {
845             char *path_buf = LIBSSH2_ALLOC(agent->session, path_len + 1);
846             memcpy(path_buf, path, path_len);
847             path_buf[path_len] = '\0';
848             agent->identity_agent_path = path_buf;
849         }
850     }
851 }
852 
853 /*
854  * libssh2_agent_get_identity_path()
855  *
856  * Returns the custom agent socket path if set
857  *
858  */
libssh2_agent_get_identity_path(LIBSSH2_AGENT * agent)859 LIBSSH2_API const char *libssh2_agent_get_identity_path(LIBSSH2_AGENT *agent)
860 {
861     return agent->identity_agent_path;
862 }
863