2
* Copyright (c) 2009 by Daiki Ueno
5
* Redistribution and use in source and binary forms,
6
* with or without modification, are permitted provided
7
* that the following conditions are met:
9
* Redistributions of source code must retain the above
10
* copyright notice, this list of conditions and the
11
* following disclaimer.
13
* Redistributions in binary form must reproduce the above
14
* copyright notice, this list of conditions and the following
15
* disclaimer in the documentation and/or other materials
16
* provided with the distribution.
18
* Neither the name of the copyright holder nor the names
19
* of any other contributors may be used to endorse or
20
* promote products derived from this software without
21
* specific prior written permission.
23
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
33
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
35
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
39
#include "libssh2_priv.h"
45
/* Use the existence of sys/un.h as a test if Unix domain socket is
46
supported. winsock*.h define PF_UNIX/AF_UNIX but do not actually
51
/* Requests from client to agent for protocol 1 key operations */
52
#define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1
53
#define SSH_AGENTC_RSA_CHALLENGE 3
54
#define SSH_AGENTC_ADD_RSA_IDENTITY 7
55
#define SSH_AGENTC_REMOVE_RSA_IDENTITY 8
56
#define SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES 9
57
#define SSH_AGENTC_ADD_RSA_ID_CONSTRAINED 24
59
/* Requests from client to agent for protocol 2 key operations */
60
#define SSH2_AGENTC_REQUEST_IDENTITIES 11
61
#define SSH2_AGENTC_SIGN_REQUEST 13
62
#define SSH2_AGENTC_ADD_IDENTITY 17
63
#define SSH2_AGENTC_REMOVE_IDENTITY 18
64
#define SSH2_AGENTC_REMOVE_ALL_IDENTITIES 19
65
#define SSH2_AGENTC_ADD_ID_CONSTRAINED 25
67
/* Key-type independent requests from client to agent */
68
#define SSH_AGENTC_ADD_SMARTCARD_KEY 20
69
#define SSH_AGENTC_REMOVE_SMARTCARD_KEY 21
70
#define SSH_AGENTC_LOCK 22
71
#define SSH_AGENTC_UNLOCK 23
72
#define SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED 26
74
/* Generic replies from agent to client */
75
#define SSH_AGENT_FAILURE 5
76
#define SSH_AGENT_SUCCESS 6
78
/* Replies from agent to client for protocol 1 key operations */
79
#define SSH_AGENT_RSA_IDENTITIES_ANSWER 2
80
#define SSH_AGENT_RSA_RESPONSE 4
82
/* Replies from agent to client for protocol 2 key operations */
83
#define SSH2_AGENT_IDENTITIES_ANSWER 12
84
#define SSH2_AGENT_SIGN_RESPONSE 14
86
/* Key constraint identifiers */
87
#define SSH_AGENT_CONSTRAIN_LIFETIME 1
88
#define SSH_AGENT_CONSTRAIN_CONFIRM 2
90
/* non-blocking mode on agent connection is not yet implemented, but
93
agent_NB_state_init = 0,
94
agent_NB_state_request_created,
95
agent_NB_state_request_length_sent,
96
agent_NB_state_request_sent,
97
agent_NB_state_response_length_received,
98
agent_NB_state_response_received
99
} agent_nonblocking_states;
101
typedef struct agent_transaction_ctx {
102
unsigned char *request;
104
unsigned char *response;
106
agent_nonblocking_states state;
107
} *agent_transaction_ctx_t;
109
typedef int (*agent_connect_func)(LIBSSH2_AGENT *agent);
110
typedef int (*agent_transact_func)(LIBSSH2_AGENT *agent,
111
agent_transaction_ctx_t transctx);
112
typedef int (*agent_disconnect_func)(LIBSSH2_AGENT *agent);
114
struct agent_publickey {
115
struct list_node node;
117
/* this is the struct we expose externally */
118
struct libssh2_agent_publickey external;
122
agent_connect_func connect;
123
agent_transact_func transact;
124
agent_disconnect_func disconnect;
127
struct _LIBSSH2_AGENT
129
LIBSSH2_SESSION *session; /* the session this "belongs to" */
133
struct agent_ops *ops;
135
struct agent_transaction_ctx transctx;
136
struct agent_publickey *identity;
137
struct list_head head; /* list of public keys */
142
agent_connect_unix(LIBSSH2_AGENT *agent)
145
struct sockaddr_un s_un;
147
path = getenv("SSH_AUTH_SOCK");
152
agent->fd = socket(PF_UNIX, SOCK_STREAM, 0);
157
s_un.sun_family = AF_UNIX;
158
strncpy (s_un.sun_path, path, sizeof s_un.sun_path);
159
if (connect(agent->fd, (struct sockaddr*)(&s_un), sizeof s_un) != 0) {
168
agent_transact_unix(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx)
170
unsigned char buf[4], *s;
173
/* Send the length of the request */
174
if (transctx->state == agent_NB_state_request_created) {
175
_libssh2_htonu32(buf, transctx->request_len);
176
rc = send(agent->fd, buf, sizeof buf, 0);
179
return LIBSSH2_ERROR_EAGAIN;
182
transctx->state = agent_NB_state_request_length_sent;
185
/* Send the request body */
186
if (transctx->state == agent_NB_state_request_length_sent) {
187
rc = send(agent->fd, transctx->request,
188
transctx->request_len, 0);
191
return LIBSSH2_ERROR_EAGAIN;
194
transctx->state = agent_NB_state_request_sent;
197
/* Receive the length of a response */
198
if (transctx->state == agent_NB_state_request_sent) {
199
rc = recv(agent->fd, buf, sizeof buf, 0);
202
return LIBSSH2_ERROR_EAGAIN;
205
transctx->response_len = _libssh2_ntohu32(buf);
206
s = transctx->response = LIBSSH2_ALLOC(agent->session,
207
transctx->response_len);
208
if (!transctx->response) {
209
return LIBSSH2_ERROR_ALLOC;
211
transctx->state = agent_NB_state_response_length_received;
214
/* Receive the response body */
215
if (transctx->state == agent_NB_state_response_length_received) {
216
rc = recv(agent->fd, transctx->response, transctx->response_len, 0);
219
return LIBSSH2_ERROR_EAGAIN;
222
transctx->state = agent_NB_state_response_received;
229
agent_disconnect_unix(LIBSSH2_AGENT *agent)
231
return close(agent->fd);
234
struct agent_ops agent_ops_unix = {
237
agent_disconnect_unix
242
/* Code to talk to Pageant was taken from PuTTY.
244
* Portions copyright Robert de Bath, Joris van Rantwijk, Delian
245
* Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas
246
* Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa,
247
* Markus Kuhn, Colin Watson, and CORE SDI S.A.
249
#define PAGEANT_COPYDATA_ID 0x804e50ba /* random goop */
250
#define PAGEANT_MAX_MSGLEN 8192
253
agent_connect_pageant(LIBSSH2_AGENT *agent)
256
hwnd = FindWindow("Pageant", "Pageant");
259
agent->fd = 0; /* Mark as the connection has been established */
264
agent_transact_pageant(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx)
273
if (!transctx || 4 + transctx->request_len > PAGEANT_MAX_MSGLEN) {
274
return LIBSSH2_ERROR_INVAL;
276
hwnd = FindWindow("Pageant", "Pageant");
280
sprintf(mapname, "PageantRequest%08x", (unsigned)GetCurrentThreadId());
281
filemap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
282
0, PAGEANT_MAX_MSGLEN, mapname);
283
if (filemap == NULL || filemap == INVALID_HANDLE_VALUE) {
286
p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
287
_libssh2_htonu32(p, transctx->request_len);
288
memcpy(p + 4, transctx->request, transctx->request_len);
289
cds.dwData = PAGEANT_COPYDATA_ID;
290
cds.cbData = 1 + strlen(mapname);
291
cds.lpData = mapname;
293
id = SendMessage(hwnd, WM_COPYDATA, (WPARAM) NULL, (LPARAM) &cds);
295
transctx->response_len = _libssh2_ntohu32(p);
296
if (transctx->response_len > PAGEANT_MAX_MSGLEN) {
298
CloseHandle(filemap);
299
return LIBSSH2_ERROR_AGENT_PROTOCOL;
301
transctx->response = LIBSSH2_ALLOC(agent->session,
302
transctx->response_len);
303
if (!transctx->response) {
305
CloseHandle(filemap);
306
return LIBSSH2_ERROR_ALLOC;
308
memcpy(transctx->response, p + 4, transctx->response_len);
312
CloseHandle(filemap);
317
agent_disconnect_pageant(LIBSSH2_AGENT *agent)
319
agent->fd = INVALID_SOCKET;
323
struct agent_ops agent_ops_pageant = {
324
agent_connect_pageant,
325
agent_transact_pageant,
326
agent_disconnect_pageant
332
struct agent_ops *ops;
333
} supported_backends[] = {
335
{"Pageant", &agent_ops_pageant},
338
{"Unix", &agent_ops_unix},
344
agent_sign(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len,
345
const unsigned char *data, size_t data_len, void **abstract)
347
LIBSSH2_AGENT *agent = (LIBSSH2_AGENT *) (*abstract);
348
agent_transaction_ctx_t transctx = &agent->transctx;
349
struct agent_publickey *identity = agent->identity;
350
ssize_t len = 1 + 4 + identity->external.blob_len + 4 + data_len + 4;
355
/* Create a request to sign the data */
356
if (transctx->state == agent_NB_state_init) {
357
s = transctx->request = LIBSSH2_ALLOC(session, len);
358
if (!transctx->request) {
359
return LIBSSH2_ERROR_ALLOC;
362
*s++ = SSH2_AGENTC_SIGN_REQUEST;
364
_libssh2_htonu32(s, identity->external.blob_len);
366
memcpy(s, identity->external.blob, identity->external.blob_len);
367
s += identity->external.blob_len;
369
_libssh2_htonu32(s, data_len);
371
memcpy(s, data, data_len);
374
_libssh2_htonu32(s, 0);
376
transctx->request_len = s - transctx->request;
377
transctx->state = agent_NB_state_request_created;
380
/* Make sure to be re-called as a result of EAGAIN. */
381
if (*transctx->request != SSH2_AGENTC_SIGN_REQUEST) {
382
return LIBSSH2_ERROR_BAD_USE;
385
rc = agent->ops->transact(agent, transctx);
389
LIBSSH2_FREE(session, transctx->request);
390
transctx->request = NULL;
392
len = transctx->response_len;
393
s = transctx->response;
396
rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
399
if (*s != SSH2_AGENT_SIGN_RESPONSE) {
400
rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
405
/* Skip the entire length of the signature */
408
rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
413
/* Skip signing method */
416
rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
419
method_len = _libssh2_ntohu32(s);
423
rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
428
/* Read the signature */
431
rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
434
*sig_len = _libssh2_ntohu32(s);
438
rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
442
*sig = LIBSSH2_ALLOC(session, *sig_len);
444
rc = LIBSSH2_ERROR_ALLOC;
447
memcpy(*sig, s, *sig_len);
450
LIBSSH2_FREE(session, transctx->request);
451
transctx->request = NULL;
453
LIBSSH2_FREE(session, transctx->response);
454
transctx->response = NULL;
460
agent_list_identities(LIBSSH2_AGENT *agent)
462
agent_transaction_ctx_t transctx = &agent->transctx;
463
ssize_t len, num_identities;
467
/* Create a request to list identities */
468
if (transctx->state == agent_NB_state_init) {
469
unsigned char c = SSH2_AGENTC_REQUEST_IDENTITIES;
470
transctx->request = &c;
471
transctx->request_len = 1;
472
transctx->state = agent_NB_state_request_created;
475
/* Make sure to be re-called as a result of EAGAIN. */
476
if (*transctx->request != SSH2_AGENTC_REQUEST_IDENTITIES) {
477
return LIBSSH2_ERROR_BAD_USE;
480
rc = agent->ops->transact(agent, transctx);
484
transctx->request = NULL;
486
len = transctx->response_len;
487
s = transctx->response;
490
rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
493
if (*s != SSH2_AGENT_IDENTITIES_ANSWER) {
494
rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
499
/* Read the length of identities */
502
rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
505
num_identities = _libssh2_ntohu32(s);
508
while (num_identities--) {
509
struct agent_publickey *identity;
512
identity = LIBSSH2_ALLOC(agent->session, sizeof *identity);
514
rc = LIBSSH2_ERROR_ALLOC;
518
/* Read the length of the blob */
521
rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
524
identity->external.blob_len = _libssh2_ntohu32(s);
528
len -= identity->external.blob_len;
530
rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
533
identity->external.blob = LIBSSH2_ALLOC(agent->session,
534
identity->external.blob_len);
535
if (!identity->external.blob) {
536
rc = LIBSSH2_ERROR_ALLOC;
539
memcpy(identity->external.blob, s, identity->external.blob_len);
540
s += identity->external.blob_len;
542
/* Read the length of the comment */
545
rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
548
comment_len = _libssh2_ntohu32(s);
551
/* Read the comment */
554
rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
557
identity->external.comment = LIBSSH2_ALLOC(agent->session,
559
if (!identity->external.comment) {
560
rc = LIBSSH2_ERROR_ALLOC;
563
identity->external.comment[comment_len] = '\0';
564
memcpy(identity->external.comment, s, comment_len);
567
_libssh2_list_add(&agent->head, &identity->node);
570
LIBSSH2_FREE(agent->session, transctx->response);
571
transctx->response = NULL;
577
agent_free_identities(LIBSSH2_AGENT *agent) {
578
struct agent_publickey *node;
579
struct agent_publickey *next;
581
for (node = _libssh2_list_first(&agent->head); node; node = next) {
582
next = _libssh2_list_next(&node->node);
583
LIBSSH2_FREE(agent->session, node->external.blob);
584
LIBSSH2_FREE(agent->session, node->external.comment);
585
LIBSSH2_FREE(agent->session, node);
587
_libssh2_list_init(&agent->head);
590
#define AGENT_PUBLICKEY_MAGIC 0x3bdefed2
592
* agent_publickey_to_external()
594
* Copies data from the internal to the external representation struct.
597
static struct libssh2_agent_publickey *
598
agent_publickey_to_external(struct agent_publickey *node)
600
struct libssh2_agent_publickey *ext = &node->external;
602
ext->magic = AGENT_PUBLICKEY_MAGIC;
611
* Init an ssh-agent handle. Returns the pointer to the handle.
614
LIBSSH2_API LIBSSH2_AGENT *
615
libssh2_agent_init(LIBSSH2_SESSION *session)
617
LIBSSH2_AGENT *agent;
619
agent = LIBSSH2_ALLOC(session, sizeof *agent);
621
libssh2_error(session, LIBSSH2_ERROR_ALLOC,
622
"Unable to allocate space for agent connection", 0);
625
memset(agent, 0, sizeof *agent);
626
agent->session = session;
627
_libssh2_list_init(&agent->head);
633
* libssh2_agent_connect()
635
* Connect to an ssh-agent.
637
* Returns 0 if succeeded, or a negative value for error.
640
libssh2_agent_connect(LIBSSH2_AGENT *agent)
643
for (i = 0; supported_backends[i].name; i++) {
644
agent->ops = supported_backends[i].ops;
645
rc = agent->ops->connect(agent);
653
* libssh2_agent_list_identities()
655
* Request ssh-agent to list identities.
657
* Returns 0 if succeeded, or a negative value for error.
660
libssh2_agent_list_identities(LIBSSH2_AGENT *agent)
662
memset(&agent->transctx, 0, sizeof agent->transctx);
663
/* Abondon the last fetched identities */
664
agent_free_identities(agent);
665
return agent_list_identities(agent);
669
* libssh2_agent_get_identity()
671
* Traverse the internal list of public keys. Pass NULL to 'prev' to get
672
* the first one. Or pass a poiner to the previously returned one to get the
676
* 0 if a fine public key was stored in 'store'
677
* 1 if end of public keys
678
* [negative] on errors
681
libssh2_agent_get_identity(LIBSSH2_AGENT *agent,
682
struct libssh2_agent_publickey **ext,
683
struct libssh2_agent_publickey *oprev)
685
struct agent_publickey *node;
686
if (oprev && oprev->node) {
687
/* we have a starting point */
688
struct agent_publickey *prev = oprev->node;
690
/* get the next node in the list */
691
node = _libssh2_list_next(&prev->node);
694
node = _libssh2_list_first(&agent->head);
700
*ext = agent_publickey_to_external(node);
706
* libssh2_agent_userauth()
708
* Do publickey user authentication with the help of ssh-agent.
710
* Returns 0 if succeeded, or a negative value for error.
713
libssh2_agent_userauth(LIBSSH2_AGENT *agent,
714
const char *username,
715
struct libssh2_agent_publickey *identity)
717
void *abstract = agent;
719
if (agent->session->userauth_pblc_state == libssh2_NB_state_idle) {
720
memset(&agent->transctx, 0, sizeof agent->transctx);
721
agent->identity = identity->node;
723
return libssh2_userauth_publickey(agent->session, username,
731
* libssh2_agent_disconnect()
733
* Close a connection to an ssh-agent.
735
* Returns 0 if succeeded, or a negative value for error.
738
libssh2_agent_disconnect(LIBSSH2_AGENT *agent)
740
if (agent->ops && agent->fd != INVALID_SOCKET)
741
return agent->ops->disconnect(agent);
746
* libssh2_agent_free()
748
* Free an ssh-agent handle. This function also frees the internal
749
* collection of public keys.
752
libssh2_agent_free(LIBSSH2_AGENT *agent) {
753
/* Allow connection freeing when the socket has lost its connection */
754
if (agent->fd != INVALID_SOCKET) {
755
libssh2_agent_disconnect(agent);
757
agent_free_identities(agent);
758
LIBSSH2_FREE(agent->session, agent);