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