~ubuntu-branches/ubuntu/vivid/libssh2/vivid-proposed

« back to all changes in this revision

Viewing changes to src/agent.c

  • Committer: Bazaar Package Importer
  • Author(s): Mikhail Gusarov
  • Date: 2010-02-28 13:11:14 UTC
  • mto: (1.1.6 upstream) (2.1.8 sid)
  • mto: This revision was merged to the branch mainline in revision 9.
  • Revision ID: james.westby@ubuntu.com-20100228131114-g8d2ps9p1u8i80s3
Tags: upstream-1.2.4
ImportĀ upstreamĀ versionĀ 1.2.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2009 by Daiki Ueno
 
3
 * All rights reserved.
 
4
 *
 
5
 * Redistribution and use in source and binary forms,
 
6
 * with or without modification, are permitted provided
 
7
 * that the following conditions are met:
 
8
 *
 
9
 *   Redistributions of source code must retain the above
 
10
 *   copyright notice, this list of conditions and the
 
11
 *   following disclaimer.
 
12
 *
 
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.
 
17
 *
 
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.
 
22
 *
 
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
 
36
 * OF SUCH DAMAGE.
 
37
 */
 
38
 
 
39
#include "libssh2_priv.h"
 
40
#include "misc.h"
 
41
#include <errno.h>
 
42
#ifdef HAVE_SYS_UN_H
 
43
#include <sys/un.h>
 
44
#else
 
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
 
47
   support them. */
 
48
#undef PF_UNIX
 
49
#endif
 
50
 
 
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
 
58
 
 
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
 
66
 
 
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
 
73
 
 
74
/* Generic replies from agent to client */
 
75
#define SSH_AGENT_FAILURE 5
 
76
#define SSH_AGENT_SUCCESS 6
 
77
 
 
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
 
81
 
 
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
 
85
 
 
86
/* Key constraint identifiers */
 
87
#define SSH_AGENT_CONSTRAIN_LIFETIME 1
 
88
#define SSH_AGENT_CONSTRAIN_CONFIRM 2
 
89
 
 
90
/* non-blocking mode on agent connection is not yet implemented, but
 
91
   for future use. */
 
92
typedef enum {
 
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;
 
100
 
 
101
typedef struct agent_transaction_ctx {
 
102
    unsigned char *request;
 
103
    size_t request_len;
 
104
    unsigned char *response;
 
105
    size_t response_len;
 
106
    agent_nonblocking_states state;
 
107
} *agent_transaction_ctx_t;
 
108
 
 
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);
 
113
 
 
114
struct agent_publickey {
 
115
    struct list_node node;
 
116
 
 
117
    /* this is the struct we expose externally */
 
118
    struct libssh2_agent_publickey external;
 
119
};
 
120
 
 
121
struct agent_ops {
 
122
    agent_connect_func connect;
 
123
    agent_transact_func transact;
 
124
    agent_disconnect_func disconnect;
 
125
};
 
126
 
 
127
struct _LIBSSH2_AGENT
 
128
{
 
129
    LIBSSH2_SESSION *session;  /* the session this "belongs to" */
 
130
 
 
131
    libssh2_socket_t fd;
 
132
 
 
133
    struct agent_ops *ops;
 
134
 
 
135
    struct agent_transaction_ctx transctx;
 
136
    struct agent_publickey *identity;
 
137
    struct list_head head;              /* list of public keys */
 
138
};
 
139
 
 
140
#ifdef PF_UNIX
 
141
static int
 
142
agent_connect_unix(LIBSSH2_AGENT *agent)
 
143
{
 
144
    const char *path;
 
145
    struct sockaddr_un s_un;
 
146
 
 
147
    path = getenv("SSH_AUTH_SOCK");
 
148
    if (!path) {
 
149
        return -1;
 
150
    }
 
151
 
 
152
    agent->fd = socket(PF_UNIX, SOCK_STREAM, 0);
 
153
    if (agent->fd < 0) {
 
154
        return -1;
 
155
    }
 
156
 
 
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) {
 
160
        close (agent->fd);
 
161
        return -1;
 
162
    }
 
163
 
 
164
    return 0;
 
165
}
 
166
 
 
167
static int
 
168
agent_transact_unix(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx)
 
169
{
 
170
    unsigned char buf[4], *s;
 
171
    int rc;
 
172
 
 
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);
 
177
        if (rc < 0) {
 
178
            if (errno == EAGAIN)
 
179
                return LIBSSH2_ERROR_EAGAIN;
 
180
            return -1;
 
181
        }
 
182
        transctx->state = agent_NB_state_request_length_sent;
 
183
    }
 
184
 
 
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);
 
189
        if (rc < 0) {
 
190
            if (errno == EAGAIN)
 
191
                return LIBSSH2_ERROR_EAGAIN;
 
192
            return -1;
 
193
        }
 
194
        transctx->state = agent_NB_state_request_sent;
 
195
    }
 
196
 
 
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);
 
200
        if (rc < 0) {
 
201
            if (errno == EAGAIN)
 
202
                return LIBSSH2_ERROR_EAGAIN;
 
203
            return -1;
 
204
        }
 
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;
 
210
        }
 
211
        transctx->state = agent_NB_state_response_length_received;
 
212
    }
 
213
 
 
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);
 
217
        if (rc < 0) {
 
218
            if (errno == EAGAIN)
 
219
                return LIBSSH2_ERROR_EAGAIN;
 
220
            return -1;
 
221
        }
 
222
        transctx->state = agent_NB_state_response_received;
 
223
    }
 
224
 
 
225
    return 0;
 
226
}
 
227
 
 
228
static int
 
229
agent_disconnect_unix(LIBSSH2_AGENT *agent)
 
230
{
 
231
    return close(agent->fd);
 
232
}
 
233
 
 
234
struct agent_ops agent_ops_unix = {
 
235
    agent_connect_unix,
 
236
    agent_transact_unix,
 
237
    agent_disconnect_unix
 
238
};
 
239
#endif  /* PF_UNIX */
 
240
 
 
241
#ifdef WIN32
 
242
/* Code to talk to Pageant was taken from PuTTY.
 
243
 *
 
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.
 
248
 */
 
249
#define PAGEANT_COPYDATA_ID 0x804e50ba   /* random goop */
 
250
#define PAGEANT_MAX_MSGLEN  8192
 
251
 
 
252
static int
 
253
agent_connect_pageant(LIBSSH2_AGENT *agent)
 
254
{
 
255
    HWND hwnd;
 
256
    hwnd = FindWindow("Pageant", "Pageant");
 
257
    if (!hwnd)
 
258
        return -1;
 
259
    agent->fd = 0;         /* Mark as the connection has been established */
 
260
    return 0;
 
261
}
 
262
 
 
263
static int
 
264
agent_transact_pageant(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx)
 
265
{
 
266
    HWND hwnd;
 
267
    char mapname[23];
 
268
    HANDLE filemap;
 
269
    unsigned char *p;
 
270
    int id;
 
271
    COPYDATASTRUCT cds;
 
272
 
 
273
    if (!transctx || 4 + transctx->request_len > PAGEANT_MAX_MSGLEN) {
 
274
        return LIBSSH2_ERROR_INVAL;
 
275
    }
 
276
    hwnd = FindWindow("Pageant", "Pageant");
 
277
    if (!hwnd) {
 
278
        return -1;
 
279
    }
 
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) {
 
284
        return -1;
 
285
    }
 
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;
 
292
 
 
293
    id = SendMessage(hwnd, WM_COPYDATA, (WPARAM) NULL, (LPARAM) &cds);
 
294
    if (id > 0) {
 
295
        transctx->response_len = _libssh2_ntohu32(p);
 
296
        if (transctx->response_len > PAGEANT_MAX_MSGLEN) {
 
297
            UnmapViewOfFile(p);
 
298
            CloseHandle(filemap);
 
299
            return LIBSSH2_ERROR_AGENT_PROTOCOL;
 
300
        }
 
301
        transctx->response = LIBSSH2_ALLOC(agent->session,
 
302
                                           transctx->response_len);
 
303
        if (!transctx->response) {
 
304
            UnmapViewOfFile(p);
 
305
            CloseHandle(filemap);
 
306
            return LIBSSH2_ERROR_ALLOC;
 
307
        }
 
308
        memcpy(transctx->response, p + 4, transctx->response_len);
 
309
    }
 
310
 
 
311
    UnmapViewOfFile(p);
 
312
    CloseHandle(filemap);
 
313
    return 0;
 
314
}
 
315
 
 
316
static int
 
317
agent_disconnect_pageant(LIBSSH2_AGENT *agent)
 
318
{
 
319
    agent->fd = INVALID_SOCKET;
 
320
    return 0;
 
321
}
 
322
 
 
323
struct agent_ops agent_ops_pageant = {
 
324
    agent_connect_pageant,
 
325
    agent_transact_pageant,
 
326
    agent_disconnect_pageant
 
327
};
 
328
#endif  /* WIN32 */
 
329
 
 
330
static struct {
 
331
    const char *name;
 
332
    struct agent_ops *ops;
 
333
} supported_backends[] = {
 
334
#ifdef WIN32
 
335
    {"Pageant", &agent_ops_pageant},
 
336
#endif  /* WIN32 */
 
337
#ifdef PF_UNIX
 
338
    {"Unix", &agent_ops_unix},
 
339
#endif  /* PF_UNIX */
 
340
    {NULL}
 
341
};
 
342
 
 
343
static int
 
344
agent_sign(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len,
 
345
           const unsigned char *data, size_t data_len, void **abstract)
 
346
{
 
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;
 
351
    ssize_t method_len;
 
352
    unsigned char *s;
 
353
    int rc;
 
354
 
 
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;
 
360
        }
 
361
 
 
362
        *s++ = SSH2_AGENTC_SIGN_REQUEST;
 
363
        /* key blob */
 
364
        _libssh2_htonu32(s, identity->external.blob_len);
 
365
        s += 4;
 
366
         memcpy(s, identity->external.blob, identity->external.blob_len);
 
367
        s += identity->external.blob_len;
 
368
        /* data */
 
369
        _libssh2_htonu32(s, data_len);
 
370
        s += 4;
 
371
        memcpy(s, data, data_len);
 
372
        s += data_len;
 
373
        /* flags */
 
374
        _libssh2_htonu32(s, 0);
 
375
        s += 4;
 
376
        transctx->request_len = s - transctx->request;
 
377
        transctx->state = agent_NB_state_request_created;
 
378
    }
 
379
 
 
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;
 
383
    }
 
384
 
 
385
    rc = agent->ops->transact(agent, transctx);
 
386
    if (rc) {
 
387
        goto error;
 
388
    }
 
389
    LIBSSH2_FREE(session, transctx->request);
 
390
    transctx->request = NULL;
 
391
 
 
392
    len = transctx->response_len;
 
393
    s = transctx->response;
 
394
    len--;
 
395
    if (len < 0) {
 
396
        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
 
397
        goto error;
 
398
    }
 
399
    if (*s != SSH2_AGENT_SIGN_RESPONSE) {
 
400
        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
 
401
        goto error;
 
402
    }
 
403
    s++;
 
404
 
 
405
    /* Skip the entire length of the signature */
 
406
    len -= 4;
 
407
    if (len < 0) {
 
408
        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
 
409
        goto error;
 
410
    }
 
411
    s += 4;
 
412
 
 
413
    /* Skip signing method */
 
414
    len -= 4;
 
415
    if (len < 0) {
 
416
        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
 
417
        goto error;
 
418
    }
 
419
    method_len = _libssh2_ntohu32(s);
 
420
    s += 4;
 
421
    len -= method_len;
 
422
    if (len < 0) {
 
423
        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
 
424
        goto error;
 
425
    }
 
426
    s += method_len;
 
427
 
 
428
    /* Read the signature */
 
429
    len -= 4;
 
430
    if (len < 0) {
 
431
        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
 
432
        goto error;
 
433
    }
 
434
    *sig_len = _libssh2_ntohu32(s);
 
435
    s += 4;
 
436
    len -= *sig_len;
 
437
    if (len < 0) {
 
438
        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
 
439
        goto error;
 
440
    }
 
441
 
 
442
    *sig = LIBSSH2_ALLOC(session, *sig_len);
 
443
    if (!*sig) {
 
444
        rc = LIBSSH2_ERROR_ALLOC;
 
445
        goto error;
 
446
    }
 
447
    memcpy(*sig, s, *sig_len);
 
448
 
 
449
  error:
 
450
    LIBSSH2_FREE(session, transctx->request);
 
451
    transctx->request = NULL;
 
452
 
 
453
    LIBSSH2_FREE(session, transctx->response);
 
454
    transctx->response = NULL;
 
455
 
 
456
    return rc;
 
457
}
 
458
 
 
459
static int
 
460
agent_list_identities(LIBSSH2_AGENT *agent)
 
461
{
 
462
    agent_transaction_ctx_t transctx = &agent->transctx;
 
463
    ssize_t len, num_identities;
 
464
    unsigned char *s;
 
465
    int rc;
 
466
 
 
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;
 
473
    }
 
474
 
 
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;
 
478
    }
 
479
 
 
480
    rc = agent->ops->transact(agent, transctx);
 
481
    if (rc) {
 
482
        goto error;
 
483
    }
 
484
    transctx->request = NULL;
 
485
 
 
486
    len = transctx->response_len;
 
487
    s = transctx->response;
 
488
    len--;
 
489
    if (len < 0) {
 
490
        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
 
491
        goto error;
 
492
    }
 
493
    if (*s != SSH2_AGENT_IDENTITIES_ANSWER) {
 
494
        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
 
495
        goto error;
 
496
    }
 
497
    s++;
 
498
 
 
499
    /* Read the length of identities */
 
500
    len -= 4;
 
501
    if (len < 0) {
 
502
        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
 
503
        goto error;
 
504
    }
 
505
    num_identities = _libssh2_ntohu32(s);
 
506
    s += 4;
 
507
 
 
508
    while (num_identities--) {
 
509
        struct agent_publickey *identity;
 
510
        ssize_t comment_len;
 
511
 
 
512
        identity = LIBSSH2_ALLOC(agent->session, sizeof *identity);
 
513
        if (!identity) {
 
514
            rc = LIBSSH2_ERROR_ALLOC;
 
515
            goto error;
 
516
        }
 
517
 
 
518
        /* Read the length of the blob */
 
519
        len -= 4;
 
520
        if (len < 0) {
 
521
            rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
 
522
            goto error;
 
523
        }
 
524
        identity->external.blob_len = _libssh2_ntohu32(s);
 
525
        s += 4;
 
526
 
 
527
        /* Read the blob */
 
528
        len -= identity->external.blob_len;
 
529
        if (len < 0) {
 
530
            rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
 
531
            goto error;
 
532
        }
 
533
        identity->external.blob = LIBSSH2_ALLOC(agent->session,
 
534
                                                identity->external.blob_len);
 
535
        if (!identity->external.blob) {
 
536
            rc = LIBSSH2_ERROR_ALLOC;
 
537
            goto error;
 
538
        }
 
539
        memcpy(identity->external.blob, s, identity->external.blob_len);
 
540
        s += identity->external.blob_len;
 
541
 
 
542
        /* Read the length of the comment */
 
543
        len -= 4;
 
544
        if (len < 0) {
 
545
            rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
 
546
            goto error;
 
547
        }
 
548
        comment_len = _libssh2_ntohu32(s);
 
549
        s += 4;
 
550
 
 
551
        /* Read the comment */
 
552
        len -= comment_len;
 
553
        if (len < 0) {
 
554
            rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
 
555
            goto error;
 
556
        }
 
557
        identity->external.comment = LIBSSH2_ALLOC(agent->session,
 
558
                                                   comment_len + 1);
 
559
        if (!identity->external.comment) {
 
560
            rc = LIBSSH2_ERROR_ALLOC;
 
561
            goto error;
 
562
        }
 
563
        identity->external.comment[comment_len] = '\0';
 
564
        memcpy(identity->external.comment, s, comment_len);
 
565
        s += comment_len;
 
566
 
 
567
        _libssh2_list_add(&agent->head, &identity->node);
 
568
    }
 
569
 error:
 
570
    LIBSSH2_FREE(agent->session, transctx->response);
 
571
    transctx->response = NULL;
 
572
 
 
573
    return rc;
 
574
}
 
575
 
 
576
static void
 
577
agent_free_identities(LIBSSH2_AGENT *agent) {
 
578
    struct agent_publickey *node;
 
579
    struct agent_publickey *next;
 
580
 
 
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);
 
586
    }
 
587
    _libssh2_list_init(&agent->head);
 
588
}
 
589
 
 
590
#define AGENT_PUBLICKEY_MAGIC 0x3bdefed2
 
591
/*
 
592
 * agent_publickey_to_external()
 
593
 *
 
594
 * Copies data from the internal to the external representation struct.
 
595
 *
 
596
 */
 
597
static struct libssh2_agent_publickey *
 
598
agent_publickey_to_external(struct agent_publickey *node)
 
599
{
 
600
    struct libssh2_agent_publickey *ext = &node->external;
 
601
 
 
602
    ext->magic = AGENT_PUBLICKEY_MAGIC;
 
603
    ext->node = node;
 
604
 
 
605
    return ext;
 
606
}
 
607
 
 
608
/*
 
609
 * libssh2_agent_init
 
610
 *
 
611
 * Init an ssh-agent handle. Returns the pointer to the handle.
 
612
 *
 
613
 */
 
614
LIBSSH2_API LIBSSH2_AGENT *
 
615
libssh2_agent_init(LIBSSH2_SESSION *session)
 
616
{
 
617
    LIBSSH2_AGENT *agent;
 
618
 
 
619
    agent = LIBSSH2_ALLOC(session, sizeof *agent);
 
620
    if (!agent) {
 
621
        libssh2_error(session, LIBSSH2_ERROR_ALLOC,
 
622
                      "Unable to allocate space for agent connection", 0);
 
623
        return NULL;
 
624
    }
 
625
    memset(agent, 0, sizeof *agent);
 
626
    agent->session = session;
 
627
    _libssh2_list_init(&agent->head);
 
628
 
 
629
    return agent;
 
630
}
 
631
 
 
632
/*
 
633
 * libssh2_agent_connect()
 
634
 *
 
635
 * Connect to an ssh-agent.
 
636
 *
 
637
 * Returns 0 if succeeded, or a negative value for error.
 
638
 */
 
639
LIBSSH2_API int
 
640
libssh2_agent_connect(LIBSSH2_AGENT *agent)
 
641
{
 
642
    int i, rc = -1;
 
643
    for (i = 0; supported_backends[i].name; i++) {
 
644
        agent->ops = supported_backends[i].ops;
 
645
        rc = agent->ops->connect(agent);
 
646
        if (!rc)
 
647
            return 0;
 
648
    }
 
649
    return rc;
 
650
}
 
651
 
 
652
/*
 
653
 * libssh2_agent_list_identities()
 
654
 *
 
655
 * Request ssh-agent to list identities.
 
656
 *
 
657
 * Returns 0 if succeeded, or a negative value for error.
 
658
 */
 
659
LIBSSH2_API int
 
660
libssh2_agent_list_identities(LIBSSH2_AGENT *agent)
 
661
{
 
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);
 
666
}
 
667
 
 
668
/*
 
669
 * libssh2_agent_get_identity()
 
670
 *
 
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
 
673
 * next.
 
674
 *
 
675
 * Returns:
 
676
 * 0 if a fine public key was stored in 'store'
 
677
 * 1 if end of public keys
 
678
 * [negative] on errors
 
679
 */
 
680
LIBSSH2_API int
 
681
libssh2_agent_get_identity(LIBSSH2_AGENT *agent,
 
682
                           struct libssh2_agent_publickey **ext,
 
683
                           struct libssh2_agent_publickey *oprev)
 
684
{
 
685
    struct agent_publickey *node;
 
686
    if (oprev && oprev->node) {
 
687
        /* we have a starting point */
 
688
        struct agent_publickey *prev = oprev->node;
 
689
 
 
690
        /* get the next node in the list */
 
691
        node = _libssh2_list_next(&prev->node);
 
692
    }
 
693
    else
 
694
        node = _libssh2_list_first(&agent->head);
 
695
 
 
696
    if (!node)
 
697
        /* no (more) node */
 
698
        return 1;
 
699
 
 
700
    *ext = agent_publickey_to_external(node);
 
701
 
 
702
    return 0;
 
703
}
 
704
 
 
705
/*
 
706
 * libssh2_agent_userauth()
 
707
 *
 
708
 * Do publickey user authentication with the help of ssh-agent.
 
709
 *
 
710
 * Returns 0 if succeeded, or a negative value for error.
 
711
 */
 
712
LIBSSH2_API int
 
713
libssh2_agent_userauth(LIBSSH2_AGENT *agent,
 
714
                       const char *username,
 
715
                       struct libssh2_agent_publickey *identity)
 
716
{
 
717
    void *abstract = agent;
 
718
 
 
719
    if (agent->session->userauth_pblc_state == libssh2_NB_state_idle) {
 
720
        memset(&agent->transctx, 0, sizeof agent->transctx);
 
721
        agent->identity = identity->node;
 
722
    }
 
723
    return libssh2_userauth_publickey(agent->session, username,
 
724
                                      identity->blob,
 
725
                                      identity->blob_len,
 
726
                                      agent_sign,
 
727
                                      &abstract);
 
728
}
 
729
 
 
730
/*
 
731
 * libssh2_agent_disconnect()
 
732
 *
 
733
 * Close a connection to an ssh-agent.
 
734
 *
 
735
 * Returns 0 if succeeded, or a negative value for error.
 
736
 */
 
737
LIBSSH2_API int
 
738
libssh2_agent_disconnect(LIBSSH2_AGENT *agent)
 
739
{
 
740
    if (agent->ops && agent->fd != INVALID_SOCKET)
 
741
        return agent->ops->disconnect(agent);
 
742
    return 0;
 
743
}
 
744
 
 
745
/*
 
746
 * libssh2_agent_free()
 
747
 *
 
748
 * Free an ssh-agent handle.  This function also frees the internal
 
749
 * collection of public keys.
 
750
 */
 
751
LIBSSH2_API void
 
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);
 
756
    }
 
757
    agent_free_identities(agent);
 
758
    LIBSSH2_FREE(agent->session, agent);
 
759
}