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         goto error;
526     }
527     transctx->request = NULL;
528 
529     len = transctx->response_len;
530     s = transctx->response;
531     len--;
532     if(len < 0) {
533         rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
534         goto error;
535     }
536     if(*s != SSH2_AGENT_IDENTITIES_ANSWER) {
537         rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
538         goto error;
539     }
540     s++;
541 
542     /* Read the length of identities */
543     len -= 4;
544     if(len < 0) {
545         rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
546         goto error;
547     }
548     num_identities = _libssh2_ntohu32(s);
549     s += 4;
550 
551     while(num_identities--) {
552         struct agent_publickey *identity;
553         ssize_t comment_len;
554 
555         /* Read the length of the blob */
556         len -= 4;
557         if(len < 0) {
558             rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
559             goto error;
560         }
561         identity = LIBSSH2_ALLOC(agent->session, sizeof *identity);
562         if(!identity) {
563             rc = LIBSSH2_ERROR_ALLOC;
564             goto error;
565         }
566         identity->external.blob_len = _libssh2_ntohu32(s);
567         s += 4;
568 
569         /* Read the blob */
570         len -= identity->external.blob_len;
571         if(len < 0) {
572             rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
573             LIBSSH2_FREE(agent->session, identity);
574             goto error;
575         }
576 
577         identity->external.blob = LIBSSH2_ALLOC(agent->session,
578                                                 identity->external.blob_len);
579         if(!identity->external.blob) {
580             rc = LIBSSH2_ERROR_ALLOC;
581             LIBSSH2_FREE(agent->session, identity);
582             goto error;
583         }
584         memcpy(identity->external.blob, s, identity->external.blob_len);
585         s += identity->external.blob_len;
586 
587         /* Read the length of the comment */
588         len -= 4;
589         if(len < 0) {
590             rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
591             LIBSSH2_FREE(agent->session, identity->external.blob);
592             LIBSSH2_FREE(agent->session, identity);
593             goto error;
594         }
595         comment_len = _libssh2_ntohu32(s);
596         s += 4;
597 
598         /* Read the comment */
599         len -= comment_len;
600         if(len < 0) {
601             rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
602             LIBSSH2_FREE(agent->session, identity->external.blob);
603             LIBSSH2_FREE(agent->session, identity);
604             goto error;
605         }
606 
607         identity->external.comment = LIBSSH2_ALLOC(agent->session,
608                                                    comment_len + 1);
609         if(!identity->external.comment) {
610             rc = LIBSSH2_ERROR_ALLOC;
611             LIBSSH2_FREE(agent->session, identity->external.blob);
612             LIBSSH2_FREE(agent->session, identity);
613             goto error;
614         }
615         identity->external.comment[comment_len] = '\0';
616         memcpy(identity->external.comment, s, comment_len);
617         s += comment_len;
618 
619         _libssh2_list_add(&agent->head, &identity->node);
620     }
621  error:
622     LIBSSH2_FREE(agent->session, transctx->response);
623     transctx->response = NULL;
624 
625     return _libssh2_error(agent->session, rc,
626                           "agent list id failed");
627 }
628 
629 static void
agent_free_identities(LIBSSH2_AGENT * agent)630 agent_free_identities(LIBSSH2_AGENT *agent)
631 {
632     struct agent_publickey *node;
633     struct agent_publickey *next;
634 
635     for(node = _libssh2_list_first(&agent->head); node; node = next) {
636         next = _libssh2_list_next(&node->node);
637         LIBSSH2_FREE(agent->session, node->external.blob);
638         LIBSSH2_FREE(agent->session, node->external.comment);
639         LIBSSH2_FREE(agent->session, node);
640     }
641     _libssh2_list_init(&agent->head);
642 }
643 
644 #define AGENT_PUBLICKEY_MAGIC 0x3bdefed2
645 /*
646  * agent_publickey_to_external()
647  *
648  * Copies data from the internal to the external representation struct.
649  *
650  */
651 static struct libssh2_agent_publickey *
agent_publickey_to_external(struct agent_publickey * node)652 agent_publickey_to_external(struct agent_publickey *node)
653 {
654     struct libssh2_agent_publickey *ext = &node->external;
655 
656     ext->magic = AGENT_PUBLICKEY_MAGIC;
657     ext->node = node;
658 
659     return ext;
660 }
661 
662 /*
663  * libssh2_agent_init
664  *
665  * Init an ssh-agent handle. Returns the pointer to the handle.
666  *
667  */
668 LIBSSH2_API LIBSSH2_AGENT *
libssh2_agent_init(LIBSSH2_SESSION * session)669 libssh2_agent_init(LIBSSH2_SESSION *session)
670 {
671     LIBSSH2_AGENT *agent;
672 
673     agent = LIBSSH2_CALLOC(session, sizeof *agent);
674     if(!agent) {
675         _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
676                        "Unable to allocate space for agent connection");
677         return NULL;
678     }
679     agent->fd = LIBSSH2_INVALID_SOCKET;
680     agent->session = session;
681     agent->identity_agent_path = NULL;
682     _libssh2_list_init(&agent->head);
683 
684     return agent;
685 }
686 
687 /*
688  * libssh2_agent_connect()
689  *
690  * Connect to an ssh-agent.
691  *
692  * Returns 0 if succeeded, or a negative value for error.
693  */
694 LIBSSH2_API int
libssh2_agent_connect(LIBSSH2_AGENT * agent)695 libssh2_agent_connect(LIBSSH2_AGENT *agent)
696 {
697     int i, rc = -1;
698     for(i = 0; supported_backends[i].name; i++) {
699         agent->ops = supported_backends[i].ops;
700         rc = (agent->ops->connect)(agent);
701         if(!rc)
702             return 0;
703     }
704     return rc;
705 }
706 
707 /*
708  * libssh2_agent_list_identities()
709  *
710  * Request ssh-agent to list identities.
711  *
712  * Returns 0 if succeeded, or a negative value for error.
713  */
714 LIBSSH2_API int
libssh2_agent_list_identities(LIBSSH2_AGENT * agent)715 libssh2_agent_list_identities(LIBSSH2_AGENT *agent)
716 {
717     memset(&agent->transctx, 0, sizeof agent->transctx);
718     /* Abandon the last fetched identities */
719     agent_free_identities(agent);
720     return agent_list_identities(agent);
721 }
722 
723 /*
724  * libssh2_agent_get_identity()
725  *
726  * Traverse the internal list of public keys. Pass NULL to 'prev' to get
727  * the first one. Or pass a pointer to the previously returned one to get the
728  * next.
729  *
730  * Returns:
731  * 0 if a fine public key was stored in 'store'
732  * 1 if end of public keys
733  * [negative] on errors
734  */
735 LIBSSH2_API int
libssh2_agent_get_identity(LIBSSH2_AGENT * agent,struct libssh2_agent_publickey ** ext,struct libssh2_agent_publickey * oprev)736 libssh2_agent_get_identity(LIBSSH2_AGENT *agent,
737                            struct libssh2_agent_publickey **ext,
738                            struct libssh2_agent_publickey *oprev)
739 {
740     struct agent_publickey *node;
741     if(oprev && oprev->node) {
742         /* we have a starting point */
743         struct agent_publickey *prev = oprev->node;
744 
745         /* get the next node in the list */
746         node = _libssh2_list_next(&prev->node);
747     }
748     else
749         node = _libssh2_list_first(&agent->head);
750 
751     if(!node)
752         /* no (more) node */
753         return 1;
754 
755     *ext = agent_publickey_to_external(node);
756 
757     return 0;
758 }
759 
760 /*
761  * libssh2_agent_userauth()
762  *
763  * Do publickey user authentication with the help of ssh-agent.
764  *
765  * Returns 0 if succeeded, or a negative value for error.
766  */
767 LIBSSH2_API int
libssh2_agent_userauth(LIBSSH2_AGENT * agent,const char * username,struct libssh2_agent_publickey * identity)768 libssh2_agent_userauth(LIBSSH2_AGENT *agent,
769                        const char *username,
770                        struct libssh2_agent_publickey *identity)
771 {
772     void *abstract = agent;
773     int rc;
774 
775     if(agent->session->userauth_pblc_state == libssh2_NB_state_idle) {
776         memset(&agent->transctx, 0, sizeof agent->transctx);
777         agent->identity = identity->node;
778     }
779 
780     BLOCK_ADJUST(rc, agent->session,
781                  _libssh2_userauth_publickey(agent->session, username,
782                                              strlen(username),
783                                              identity->blob,
784                                              identity->blob_len,
785                                              agent_sign,
786                                              &abstract));
787     return rc;
788 }
789 
790 /*
791  * libssh2_agent_disconnect()
792  *
793  * Close a connection to an ssh-agent.
794  *
795  * Returns 0 if succeeded, or a negative value for error.
796  */
797 LIBSSH2_API int
libssh2_agent_disconnect(LIBSSH2_AGENT * agent)798 libssh2_agent_disconnect(LIBSSH2_AGENT *agent)
799 {
800     if(agent->ops && agent->fd != LIBSSH2_INVALID_SOCKET)
801         return agent->ops->disconnect(agent);
802     return 0;
803 }
804 
805 /*
806  * libssh2_agent_free()
807  *
808  * Free an ssh-agent handle.  This function also frees the internal
809  * collection of public keys.
810  */
811 LIBSSH2_API void
libssh2_agent_free(LIBSSH2_AGENT * agent)812 libssh2_agent_free(LIBSSH2_AGENT *agent)
813 {
814     /* Allow connection freeing when the socket has lost its connection */
815     if(agent->fd != LIBSSH2_INVALID_SOCKET) {
816         libssh2_agent_disconnect(agent);
817     }
818 
819     if(agent->identity_agent_path != NULL)
820         LIBSSH2_FREE(agent->session, agent->identity_agent_path);
821 
822     agent_free_identities(agent);
823     LIBSSH2_FREE(agent->session, agent);
824 }
825 
826 /*
827  * libssh2_agent_set_identity_path()
828  *
829  * Allows a custom agent socket path beyond SSH_AUTH_SOCK env
830  *
831  */
832 LIBSSH2_API void
libssh2_agent_set_identity_path(LIBSSH2_AGENT * agent,const char * path)833 libssh2_agent_set_identity_path(LIBSSH2_AGENT *agent, const char *path)
834 {
835     if(agent->identity_agent_path) {
836         LIBSSH2_FREE(agent->session, agent->identity_agent_path);
837         agent->identity_agent_path = NULL;
838     }
839 
840     if(path) {
841         size_t path_len = strlen(path);
842         if(path_len < SIZE_MAX - 1) {
843             char *path_buf = LIBSSH2_ALLOC(agent->session, path_len + 1);
844             memcpy(path_buf, path, path_len);
845             path_buf[path_len] = '\0';
846             agent->identity_agent_path = path_buf;
847         }
848     }
849 }
850 
851 /*
852  * libssh2_agent_get_identity_path()
853  *
854  * Returns the custom agent socket path if set
855  *
856  */
libssh2_agent_get_identity_path(LIBSSH2_AGENT * agent)857 LIBSSH2_API const char *libssh2_agent_get_identity_path(LIBSSH2_AGENT *agent)
858 {
859     return agent->identity_agent_path;
860 }
861