libssh2

Форк
0
/
agent.c 
1032 строки · 31.1 Кб
1
/*
2
 * Copyright (C) Daiki Ueno
3
 * Copyright (C) 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
 * SPDX-License-Identifier: BSD-3-Clause
40
 */
41

42
#include "libssh2_priv.h"
43

44
#include <errno.h>
45
#include <stdlib.h>  /* for getenv() */
46

47
#ifdef HAVE_SYS_UN_H
48
#include <sys/un.h>
49
#else
50
/* Use the existence of sys/un.h as a test if Unix domain socket is
51
   supported.  winsock*.h define PF_UNIX/AF_UNIX but do not actually
52
   support them. */
53
#undef PF_UNIX
54
#endif
55

56
#if defined(_WIN32) && !defined(LIBSSH2_WINDOWS_UWP)
57
#define HAVE_WIN32_AGENTS
58
#endif
59

60
#include "userauth.h"
61
#include "session.h"
62

63
#if 0
64
/* Requests from client to agent for protocol 1 key operations */
65
#define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1
66
#define SSH_AGENTC_RSA_CHALLENGE 3
67
#define SSH_AGENTC_ADD_RSA_IDENTITY 7
68
#define SSH_AGENTC_REMOVE_RSA_IDENTITY 8
69
#define SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES 9
70
#define SSH_AGENTC_ADD_RSA_ID_CONSTRAINED 24
71
#endif
72

73
/* Requests from client to agent for protocol 2 key operations */
74
#define SSH2_AGENTC_REQUEST_IDENTITIES 11
75
#define SSH2_AGENTC_SIGN_REQUEST 13
76
#if 0
77
#define SSH2_AGENTC_ADD_IDENTITY 17
78
#define SSH2_AGENTC_REMOVE_IDENTITY 18
79
#define SSH2_AGENTC_REMOVE_ALL_IDENTITIES 19
80
#define SSH2_AGENTC_ADD_ID_CONSTRAINED 25
81

82
/* Key-type independent requests from client to agent */
83
#define SSH_AGENTC_ADD_SMARTCARD_KEY 20
84
#define SSH_AGENTC_REMOVE_SMARTCARD_KEY 21
85
#define SSH_AGENTC_LOCK 22
86
#define SSH_AGENTC_UNLOCK 23
87
#define SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED 26
88

89
/* Generic replies from agent to client */
90
#define SSH_AGENT_FAILURE 5
91
#define SSH_AGENT_SUCCESS 6
92

93
/* Replies from agent to client for protocol 1 key operations */
94
#define SSH_AGENT_RSA_IDENTITIES_ANSWER 2
95
#define SSH_AGENT_RSA_RESPONSE 4
96

97
/* Key constraint identifiers */
98
#define SSH_AGENT_CONSTRAIN_LIFETIME 1
99
#define SSH_AGENT_CONSTRAIN_CONFIRM 2
100
#endif
101

102
/* Replies from agent to client for protocol 2 key operations */
103
#define SSH2_AGENT_IDENTITIES_ANSWER 12
104
#define SSH2_AGENT_SIGN_RESPONSE 14
105

106
/* Signature request methods */
107
#define SSH_AGENT_RSA_SHA2_256 2
108
#define SSH_AGENT_RSA_SHA2_512 4
109

110
/* non-blocking mode on agent connection is not yet implemented, but
111
   for future use. */
112
typedef enum {
113
    agent_NB_state_init = 0,
114
    agent_NB_state_request_created,
115
    agent_NB_state_request_length_sent,
116
    agent_NB_state_request_sent,
117
    agent_NB_state_response_length_received,
118
    agent_NB_state_response_received
119
} agent_nonblocking_states;
120

121
typedef struct agent_transaction_ctx {
122
    unsigned char *request;
123
    size_t request_len;
124
    unsigned char *response;
125
    size_t response_len;
126
    agent_nonblocking_states state;
127
    size_t send_recv_total;
128
} *agent_transaction_ctx_t;
129

130
typedef int (*agent_connect_func)(LIBSSH2_AGENT *agent);
131
typedef int (*agent_transact_func)(LIBSSH2_AGENT *agent,
132
                                   agent_transaction_ctx_t transctx);
133
typedef int (*agent_disconnect_func)(LIBSSH2_AGENT *agent);
134

135
struct agent_publickey {
136
    struct list_node node;
137

138
    /* this is the struct we expose externally */
139
    struct libssh2_agent_publickey external;
140
};
141

142
struct agent_ops {
143
    const agent_connect_func connect;
144
    const agent_transact_func transact;
145
    const agent_disconnect_func disconnect;
146
};
147

148
struct _LIBSSH2_AGENT
149
{
150
    LIBSSH2_SESSION *session;  /* the session this "belongs to" */
151

152
    libssh2_socket_t fd;
153

154
    struct agent_ops *ops;
155

156
    struct agent_transaction_ctx transctx;
157
    struct agent_publickey *identity;
158
    struct list_head head;              /* list of public keys */
159

160
    char *identity_agent_path; /* Path to a custom identity agent socket */
161

162
#ifdef HAVE_WIN32_AGENTS
163
    OVERLAPPED overlapped;
164
    HANDLE pipe;
165
    BOOL pending_io;
166
#endif
167
};
168

169
#include "agent_win.c"
170

171
#ifdef PF_UNIX
172
static int
173
agent_connect_unix(LIBSSH2_AGENT *agent)
174
{
175
    const char *path;
176
    struct sockaddr_un s_un;
177

178
    path = agent->identity_agent_path;
179
    if(!path) {
180
        path = getenv("SSH_AUTH_SOCK");
181
        if(!path)
182
            return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_USE,
183
                                  "no auth sock variable");
184
    }
185

186
    agent->fd = socket(PF_UNIX, SOCK_STREAM, 0);
187
    if(agent->fd < 0)
188
        return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_SOCKET,
189
                              "failed creating socket");
190

191
    s_un.sun_family = AF_UNIX;
192
    strncpy(s_un.sun_path, path, sizeof(s_un.sun_path));
193
    s_un.sun_path[sizeof(s_un.sun_path)-1] = 0; /* make sure there's a trailing
194
                                                   zero */
195
    if(connect(agent->fd, (struct sockaddr*)(&s_un), sizeof(s_un)) != 0) {
196
        close(agent->fd);
197
        return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
198
                              "failed connecting with agent");
199
    }
200

201
    return LIBSSH2_ERROR_NONE;
202
}
203

204
#define RECV_SEND_ALL(func, socket, buffer, length, flags, abstract) \
205
    do {                                                             \
206
        size_t finished = 0;                                         \
207
                                                                     \
208
        while(finished < length) {                                   \
209
            ssize_t rc;                                              \
210
            rc = func(socket,                                        \
211
                      (char *)buffer + finished, length - finished,  \
212
                      flags, abstract);                              \
213
            if(rc < 0)                                               \
214
                return rc;                                           \
215
                                                                     \
216
            finished += rc;                                          \
217
        }                                                            \
218
                                                                     \
219
        return finished;                                             \
220
    } while(0)
221

222
static ssize_t _send_all(LIBSSH2_SEND_FUNC(func), libssh2_socket_t socket,
223
                         const void *buffer, size_t length,
224
                         int flags, void **abstract)
225
{
226
    RECV_SEND_ALL(func, socket, buffer, length, flags, abstract);
227
}
228

229
static ssize_t _recv_all(LIBSSH2_RECV_FUNC(func), libssh2_socket_t socket,
230
                         void *buffer, size_t length,
231
                         int flags, void **abstract)
232
{
233
    RECV_SEND_ALL(func, socket, buffer, length, flags, abstract);
234
}
235

236
#undef RECV_SEND_ALL
237

238
static int
239
agent_transact_unix(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx)
240
{
241
    unsigned char buf[4];
242
    int rc;
243

244
    /* Send the length of the request */
245
    if(transctx->state == agent_NB_state_request_created) {
246
        _libssh2_htonu32(buf, (uint32_t)transctx->request_len);
247
        rc = (int)_send_all(agent->session->send, agent->fd,
248
                            buf, sizeof(buf), 0,
249
                            &agent->session->abstract);
250
        if(rc == -EAGAIN)
251
            return LIBSSH2_ERROR_EAGAIN;
252
        else if(rc < 0)
253
            return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND,
254
                                  "agent send failed");
255
        transctx->state = agent_NB_state_request_length_sent;
256
    }
257

258
    /* Send the request body */
259
    if(transctx->state == agent_NB_state_request_length_sent) {
260
        rc = (int)_send_all(agent->session->send, agent->fd,
261
                            transctx->request, transctx->request_len, 0,
262
                            &agent->session->abstract);
263
        if(rc == -EAGAIN)
264
            return LIBSSH2_ERROR_EAGAIN;
265
        else if(rc < 0)
266
            return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND,
267
                                  "agent send failed");
268
        transctx->state = agent_NB_state_request_sent;
269
    }
270

271
    /* Receive the length of a response */
272
    if(transctx->state == agent_NB_state_request_sent) {
273
        rc = (int)_recv_all(agent->session->recv, agent->fd,
274
                            buf, sizeof(buf), 0,
275
                            &agent->session->abstract);
276
        if(rc < 0) {
277
            if(rc == -EAGAIN)
278
                return LIBSSH2_ERROR_EAGAIN;
279
            return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_RECV,
280
                                  "agent recv failed");
281
        }
282
        transctx->response_len = _libssh2_ntohu32(buf);
283
        transctx->response = LIBSSH2_ALLOC(agent->session,
284
                                           transctx->response_len);
285
        if(!transctx->response)
286
            return LIBSSH2_ERROR_ALLOC;
287

288
        transctx->state = agent_NB_state_response_length_received;
289
    }
290

291
    /* Receive the response body */
292
    if(transctx->state == agent_NB_state_response_length_received) {
293
        rc = (int)_recv_all(agent->session->recv, agent->fd,
294
                            transctx->response, transctx->response_len, 0,
295
                            &agent->session->abstract);
296
        if(rc < 0) {
297
            if(rc == -EAGAIN)
298
                return LIBSSH2_ERROR_EAGAIN;
299
            return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND,
300
                                  "agent recv failed");
301
        }
302
        transctx->state = agent_NB_state_response_received;
303
    }
304

305
    return 0;
306
}
307

308
static int
309
agent_disconnect_unix(LIBSSH2_AGENT *agent)
310
{
311
    int ret;
312
    ret = close(agent->fd);
313
    if(ret != -1)
314
        agent->fd = LIBSSH2_INVALID_SOCKET;
315
    else
316
        return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_DISCONNECT,
317
                              "failed closing the agent socket");
318
    return LIBSSH2_ERROR_NONE;
319
}
320

321
static struct agent_ops agent_ops_unix = {
322
    agent_connect_unix,
323
    agent_transact_unix,
324
    agent_disconnect_unix
325
};
326
#endif  /* PF_UNIX */
327

328
#ifdef HAVE_WIN32_AGENTS
329
/* Code to talk to Pageant was taken from PuTTY.
330
 *
331
 * Portions copyright Robert de Bath, Joris van Rantwijk, Delian
332
 * Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas
333
 * Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa,
334
 * Markus Kuhn, Colin Watson, and CORE SDI S.A.
335
 */
336
#define PAGEANT_COPYDATA_ID 0x804e50ba   /* random goop */
337
#define PAGEANT_MAX_MSGLEN  8192
338

339
static int
340
agent_connect_pageant(LIBSSH2_AGENT *agent)
341
{
342
    HWND hwnd;
343
    hwnd = FindWindowA("Pageant", "Pageant");
344
    if(!hwnd)
345
        return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
346
                              "failed connecting agent");
347
    agent->fd = 0;         /* Mark as the connection has been established */
348
    return LIBSSH2_ERROR_NONE;
349
}
350

351
static int
352
agent_transact_pageant(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx)
353
{
354
    HWND hwnd;
355
    char mapname[23];
356
    HANDLE filemap;
357
    unsigned char *p;
358
    unsigned char *p2;
359
    LRESULT id;
360
    COPYDATASTRUCT cds;
361

362
    if(!transctx || 4 + transctx->request_len > PAGEANT_MAX_MSGLEN)
363
        return _libssh2_error(agent->session, LIBSSH2_ERROR_INVAL,
364
                              "illegal input");
365

366
    hwnd = FindWindowA("Pageant", "Pageant");
367
    if(!hwnd)
368
        return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
369
                              "found no pageant");
370

371
    snprintf(mapname, sizeof(mapname),
372
             "PageantRequest%08x", (unsigned)GetCurrentThreadId());
373
    filemap = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
374
                                 0, PAGEANT_MAX_MSGLEN, mapname);
375

376
    if(!filemap || filemap == INVALID_HANDLE_VALUE)
377
        return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
378
                              "failed setting up pageant filemap");
379

380
    p2 = p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
381
    if(!p || !p2) {
382
        CloseHandle(filemap);
383
        return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
384
                              "failed to open pageant filemap for writing");
385
    }
386

387
    _libssh2_store_str(&p2, (const char *)transctx->request,
388
                       transctx->request_len);
389

390
    cds.dwData = PAGEANT_COPYDATA_ID;
391
    cds.cbData = (DWORD)(1 + strlen(mapname));
392
    cds.lpData = mapname;
393

394
    id = SendMessage(hwnd, WM_COPYDATA, (WPARAM) NULL, (LPARAM) &cds);
395
    if(id > 0) {
396
        transctx->response_len = _libssh2_ntohu32(p);
397
        if(transctx->response_len > PAGEANT_MAX_MSGLEN) {
398
            UnmapViewOfFile(p);
399
            CloseHandle(filemap);
400
            return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
401
                                  "agent setup fail");
402
        }
403
        transctx->response = LIBSSH2_ALLOC(agent->session,
404
                                           transctx->response_len);
405
        if(!transctx->response) {
406
            UnmapViewOfFile(p);
407
            CloseHandle(filemap);
408
            return _libssh2_error(agent->session, LIBSSH2_ERROR_ALLOC,
409
                                  "agent malloc");
410
        }
411
        memcpy(transctx->response, p + 4, transctx->response_len);
412
    }
413

414
    UnmapViewOfFile(p);
415
    CloseHandle(filemap);
416
    return 0;
417
}
418

419
static int
420
agent_disconnect_pageant(LIBSSH2_AGENT *agent)
421
{
422
    agent->fd = LIBSSH2_INVALID_SOCKET;
423
    return 0;
424
}
425

426
static struct agent_ops agent_ops_pageant = {
427
    agent_connect_pageant,
428
    agent_transact_pageant,
429
    agent_disconnect_pageant
430
};
431
#endif /* HAVE_WIN32_AGENTS */
432

433
static struct {
434
    const char *name;
435
    struct agent_ops *ops;
436
} supported_backends[] = {
437
#ifdef HAVE_WIN32_AGENTS
438
    {"Pageant", &agent_ops_pageant},
439
    {"OpenSSH", &agent_ops_openssh},
440
#endif /* HAVE_WIN32_AGENTS */
441
#ifdef PF_UNIX
442
    {"Unix", &agent_ops_unix},
443
#endif  /* PF_UNIX */
444
    {NULL, NULL}
445
};
446

447
static int
448
agent_sign(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len,
449
           const unsigned char *data, size_t data_len, void **abstract)
450
{
451
    LIBSSH2_AGENT *agent = (LIBSSH2_AGENT *) (*abstract);
452
    agent_transaction_ctx_t transctx = &agent->transctx;
453
    struct agent_publickey *identity = agent->identity;
454
    ssize_t len = 1 + 4 + identity->external.blob_len + 4 + data_len + 4;
455
    ssize_t method_len;
456
    unsigned char *s;
457
    int rc;
458
    unsigned char *method_name = NULL;
459
    uint32_t sign_flags = 0;
460
    ssize_t plain_len;
461

462
    /* Create a request to sign the data */
463
    if(transctx->state == agent_NB_state_init) {
464
        s = transctx->request = LIBSSH2_ALLOC(session, len);
465
        if(!transctx->request)
466
            return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
467
                                  "out of memory");
468

469
        *s++ = SSH2_AGENTC_SIGN_REQUEST;
470
        /* key blob */
471
        _libssh2_store_str(&s, (const char *)identity->external.blob,
472
                           identity->external.blob_len);
473
        /* data */
474
        _libssh2_store_str(&s, (const char *)data, data_len);
475

476
        /* flags */
477
        if(session->userauth_pblc_method_len > 0 &&
478
            session->userauth_pblc_method) {
479
            if(session->userauth_pblc_method_len == 12 &&
480
                !memcmp(session->userauth_pblc_method, "rsa-sha2-512", 12)) {
481
                sign_flags = SSH_AGENT_RSA_SHA2_512;
482
            }
483
            else if(session->userauth_pblc_method_len == 12 &&
484
                !memcmp(session->userauth_pblc_method, "rsa-sha2-256", 12)) {
485
                sign_flags = SSH_AGENT_RSA_SHA2_256;
486
            }
487
        }
488
        _libssh2_store_u32(&s, sign_flags);
489

490
        transctx->request_len = s - transctx->request;
491
        transctx->send_recv_total = 0;
492
        transctx->state = agent_NB_state_request_created;
493
    }
494

495
    /* Make sure to be re-called as a result of EAGAIN. */
496
    if(*transctx->request != SSH2_AGENTC_SIGN_REQUEST)
497
        return _libssh2_error(session, LIBSSH2_ERROR_BAD_USE,
498
                              "illegal request");
499

500
    if(!agent->ops)
501
        /* if no agent has been connected, bail out */
502
        return _libssh2_error(session, LIBSSH2_ERROR_BAD_USE,
503
                              "agent not connected");
504

505
    rc = agent->ops->transact(agent, transctx);
506
    if(rc) {
507
        goto error;
508
    }
509
    LIBSSH2_FREE(session, transctx->request);
510
    transctx->request = NULL;
511

512
    len = transctx->response_len;
513
    s = transctx->response;
514
    len--;
515
    if(len < 0) {
516
        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
517
        goto error;
518
    }
519
    if(*s != SSH2_AGENT_SIGN_RESPONSE) {
520
        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
521
        goto error;
522
    }
523
    s++;
524

525
    /* Skip the entire length of the signature */
526
    len -= 4;
527
    if(len < 0) {
528
        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
529
        goto error;
530
    }
531
    s += 4;
532

533
    /* Skip signing method */
534
    len -= 4;
535
    if(len < 0) {
536
        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
537
        goto error;
538
    }
539
    method_len = _libssh2_ntohu32(s);
540
    s += 4;
541
    len -= method_len;
542
    if(len < 0) {
543
        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
544
        goto error;
545
    }
546

547
    /* method name */
548
    method_name = LIBSSH2_ALLOC(session, method_len);
549
    if(!method_name) {
550
        rc = LIBSSH2_ERROR_ALLOC;
551
        goto error;
552
    }
553
    memcpy(method_name, s, method_len);
554
    s += method_len;
555

556
    plain_len = plain_method((char *)session->userauth_pblc_method,
557
                             session->userauth_pblc_method_len);
558

559
    /* check to see if we match requested */
560
    if(((size_t)method_len != session->userauth_pblc_method_len &&
561
        method_len != plain_len) ||
562
       memcmp(method_name, session->userauth_pblc_method, method_len)) {
563
        _libssh2_debug((session,
564
                       LIBSSH2_TRACE_KEX,
565
                       "Agent sign method %.*s",
566
                       (int)method_len, method_name));
567

568
        rc = LIBSSH2_ERROR_ALGO_UNSUPPORTED;
569
        goto error;
570
    }
571

572
    /* Read the signature */
573
    len -= 4;
574
    if(len < 0) {
575
        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
576
        goto error;
577
    }
578
    *sig_len = _libssh2_ntohu32(s);
579
    s += 4;
580
    len -= *sig_len;
581
    if(len < 0) {
582
        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
583
        goto error;
584
    }
585

586
    *sig = LIBSSH2_ALLOC(session, *sig_len);
587
    if(!*sig) {
588
        rc = LIBSSH2_ERROR_ALLOC;
589
        goto error;
590
    }
591
    memcpy(*sig, s, *sig_len);
592

593
error:
594

595
    if(method_name)
596
        LIBSSH2_FREE(session, method_name);
597

598
    LIBSSH2_FREE(session, transctx->request);
599
    transctx->request = NULL;
600

601
    LIBSSH2_FREE(session, transctx->response);
602
    transctx->response = NULL;
603

604
    transctx->state = agent_NB_state_init;
605

606
    return _libssh2_error(session, rc, "agent sign failure");
607
}
608

609
static int
610
agent_list_identities(LIBSSH2_AGENT *agent)
611
{
612
    agent_transaction_ctx_t transctx = &agent->transctx;
613
    ssize_t len, num_identities;
614
    unsigned char *s;
615
    int rc;
616
    unsigned char c = SSH2_AGENTC_REQUEST_IDENTITIES;
617

618
    /* Create a request to list identities */
619
    if(transctx->state == agent_NB_state_init) {
620
        transctx->request = &c;
621
        transctx->request_len = 1;
622
        transctx->send_recv_total = 0;
623
        transctx->state = agent_NB_state_request_created;
624
    }
625

626
    /* Make sure to be re-called as a result of EAGAIN. */
627
    if(*transctx->request != SSH2_AGENTC_REQUEST_IDENTITIES)
628
        return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_USE,
629
                              "illegal agent request");
630

631
    if(!agent->ops)
632
        /* if no agent has been connected, bail out */
633
        return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_USE,
634
                              "agent not connected");
635

636
    rc = agent->ops->transact(agent, transctx);
637
    if(rc) {
638
        LIBSSH2_FREE(agent->session, transctx->response);
639
        transctx->response = NULL;
640
        return rc;
641
    }
642
    transctx->request = NULL;
643

644
    len = transctx->response_len;
645
    s = transctx->response;
646
    len--;
647
    if(len < 0) {
648
        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
649
        goto error;
650
    }
651
    if(*s != SSH2_AGENT_IDENTITIES_ANSWER) {
652
        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
653
        goto error;
654
    }
655
    s++;
656

657
    /* Read the length of identities */
658
    len -= 4;
659
    if(len < 0) {
660
        rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
661
        goto error;
662
    }
663
    num_identities = _libssh2_ntohu32(s);
664
    s += 4;
665

666
    while(num_identities--) {
667
        struct agent_publickey *identity;
668
        size_t comment_len;
669

670
        /* Read the length of the blob */
671
        len -= 4;
672
        if(len < 0) {
673
            rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
674
            goto error;
675
        }
676
        identity = LIBSSH2_ALLOC(agent->session, sizeof(*identity));
677
        if(!identity) {
678
            rc = LIBSSH2_ERROR_ALLOC;
679
            goto error;
680
        }
681
        identity->external.blob_len = _libssh2_ntohu32(s);
682
        s += 4;
683

684
        /* Read the blob */
685
        len -= identity->external.blob_len;
686
        if(len < 0) {
687
            rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
688
            LIBSSH2_FREE(agent->session, identity);
689
            goto error;
690
        }
691

692
        identity->external.blob = LIBSSH2_ALLOC(agent->session,
693
                                                identity->external.blob_len);
694
        if(!identity->external.blob) {
695
            rc = LIBSSH2_ERROR_ALLOC;
696
            LIBSSH2_FREE(agent->session, identity);
697
            goto error;
698
        }
699
        memcpy(identity->external.blob, s, identity->external.blob_len);
700
        s += identity->external.blob_len;
701

702
        /* Read the length of the comment */
703
        len -= 4;
704
        if(len < 0) {
705
            rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
706
            LIBSSH2_FREE(agent->session, identity->external.blob);
707
            LIBSSH2_FREE(agent->session, identity);
708
            goto error;
709
        }
710
        comment_len = _libssh2_ntohu32(s);
711
        s += 4;
712

713
        if(comment_len > (size_t)len) {
714
            rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
715
            LIBSSH2_FREE(agent->session, identity->external.blob);
716
            LIBSSH2_FREE(agent->session, identity);
717
            goto error;
718
        }
719
        /* Read the comment */
720
        len -= comment_len;
721

722
        identity->external.comment = LIBSSH2_ALLOC(agent->session,
723
                                                   comment_len + 1);
724
        if(!identity->external.comment) {
725
            rc = LIBSSH2_ERROR_ALLOC;
726
            LIBSSH2_FREE(agent->session, identity->external.blob);
727
            LIBSSH2_FREE(agent->session, identity);
728
            goto error;
729
        }
730
        identity->external.comment[comment_len] = '\0';
731
        memcpy(identity->external.comment, s, comment_len);
732
        s += comment_len;
733

734
        _libssh2_list_add(&agent->head, &identity->node);
735
    }
736
error:
737
    LIBSSH2_FREE(agent->session, transctx->response);
738
    transctx->response = NULL;
739

740
    return _libssh2_error(agent->session, rc,
741
                          "agent list id failed");
742
}
743

744
static void
745
agent_free_identities(LIBSSH2_AGENT *agent)
746
{
747
    struct agent_publickey *node;
748
    struct agent_publickey *next;
749

750
    for(node = _libssh2_list_first(&agent->head); node; node = next) {
751
        next = _libssh2_list_next(&node->node);
752
        LIBSSH2_FREE(agent->session, node->external.blob);
753
        LIBSSH2_FREE(agent->session, node->external.comment);
754
        LIBSSH2_FREE(agent->session, node);
755
    }
756
    _libssh2_list_init(&agent->head);
757
}
758

759
#define AGENT_PUBLICKEY_MAGIC 0x3bdefed2
760
/*
761
 * agent_publickey_to_external
762
 *
763
 * Copies data from the internal to the external representation struct.
764
 *
765
 */
766
static struct libssh2_agent_publickey *
767
agent_publickey_to_external(struct agent_publickey *node)
768
{
769
    struct libssh2_agent_publickey *ext = &node->external;
770

771
    ext->magic = AGENT_PUBLICKEY_MAGIC;
772
    ext->node = node;
773

774
    return ext;
775
}
776

777
/*
778
 * libssh2_agent_init
779
 *
780
 * Init an ssh-agent handle. Returns the pointer to the handle.
781
 *
782
 */
783
LIBSSH2_API LIBSSH2_AGENT *
784
libssh2_agent_init(LIBSSH2_SESSION *session)
785
{
786
    LIBSSH2_AGENT *agent;
787

788
    agent = LIBSSH2_CALLOC(session, sizeof(*agent));
789
    if(!agent) {
790
        _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
791
                       "Unable to allocate space for agent connection");
792
        return NULL;
793
    }
794
    agent->fd = LIBSSH2_INVALID_SOCKET;
795
    agent->session = session;
796
    agent->identity_agent_path = NULL;
797
    _libssh2_list_init(&agent->head);
798

799
#ifdef HAVE_WIN32_AGENTS
800
    agent->pipe = INVALID_HANDLE_VALUE;
801
    memset(&agent->overlapped, 0, sizeof(OVERLAPPED));
802
    agent->pending_io = FALSE;
803
#endif
804

805
    return agent;
806
}
807

808
/*
809
 * libssh2_agent_connect
810
 *
811
 * Connect to an ssh-agent.
812
 *
813
 * Returns 0 if succeeded, or a negative value for error.
814
 */
815
LIBSSH2_API int
816
libssh2_agent_connect(LIBSSH2_AGENT *agent)
817
{
818
    int i, rc = -1;
819
    for(i = 0; supported_backends[i].name; i++) {
820
        agent->ops = supported_backends[i].ops;
821
        rc = (agent->ops->connect)(agent);
822
        if(!rc)
823
            return 0;
824
    }
825
    return rc;
826
}
827

828
/*
829
 * libssh2_agent_list_identities
830
 *
831
 * Request ssh-agent to list identities.
832
 *
833
 * Returns 0 if succeeded, or a negative value for error.
834
 */
835
LIBSSH2_API int
836
libssh2_agent_list_identities(LIBSSH2_AGENT *agent)
837
{
838
    memset(&agent->transctx, 0, sizeof(agent->transctx));
839
    /* Abandon the last fetched identities */
840
    agent_free_identities(agent);
841
    return agent_list_identities(agent);
842
}
843

844
/*
845
 * libssh2_agent_get_identity
846
 *
847
 * Traverse the internal list of public keys. Pass NULL to 'prev' to get
848
 * the first one. Or pass a pointer to the previously returned one to get the
849
 * next.
850
 *
851
 * Returns:
852
 * 0 if a fine public key was stored in 'store'
853
 * 1 if end of public keys
854
 * [negative] on errors
855
 */
856
LIBSSH2_API int
857
libssh2_agent_get_identity(LIBSSH2_AGENT *agent,
858
                           struct libssh2_agent_publickey **ext,
859
                           struct libssh2_agent_publickey *oprev)
860
{
861
    struct agent_publickey *node;
862
    if(oprev && oprev->node) {
863
        /* we have a starting point */
864
        struct agent_publickey *prev = oprev->node;
865

866
        /* get the next node in the list */
867
        node = _libssh2_list_next(&prev->node);
868
    }
869
    else
870
        node = _libssh2_list_first(&agent->head);
871

872
    if(!node)
873
        /* no (more) node */
874
        return 1;
875

876
    *ext = agent_publickey_to_external(node);
877

878
    return 0;
879
}
880

881
/*
882
 * libssh2_agent_userauth
883
 *
884
 * Do publickey user authentication with the help of ssh-agent.
885
 *
886
 * Returns 0 if succeeded, or a negative value for error.
887
 */
888
LIBSSH2_API int
889
libssh2_agent_userauth(LIBSSH2_AGENT *agent,
890
                       const char *username,
891
                       struct libssh2_agent_publickey *identity)
892
{
893
    void *abstract = agent;
894
    int rc;
895

896
    if(agent->session->userauth_pblc_state == libssh2_NB_state_idle) {
897
        memset(&agent->transctx, 0, sizeof(agent->transctx));
898
        agent->identity = identity->node;
899
    }
900

901
    BLOCK_ADJUST(rc, agent->session,
902
                 _libssh2_userauth_publickey(agent->session, username,
903
                                             strlen(username),
904
                                             identity->blob,
905
                                             identity->blob_len,
906
                                             agent_sign,
907
                                             &abstract));
908
    return rc;
909
}
910

911
/*
912
 * libssh2_agent_sign
913
 *
914
 * Sign a payload using a system-installed ssh-agent.
915
 *
916
 * Returns 0 if succeeded, or a negative value for error.
917
 */
918
LIBSSH2_API int
919
libssh2_agent_sign(LIBSSH2_AGENT *agent,
920
                   struct libssh2_agent_publickey *identity,
921
                   unsigned char **sig,
922
                   size_t *s_len,
923
                   const unsigned char *data,
924
                   size_t d_len,
925
                   const char *method,
926
                   unsigned int method_len)
927
{
928
    void *abstract = agent;
929
    int rc;
930
    uint32_t methodLen;
931

932
    if(agent->session->userauth_pblc_state == libssh2_NB_state_idle) {
933
        memset(&agent->transctx, 0, sizeof(agent->transctx));
934
        agent->identity = identity->node;
935
    }
936

937
    if(identity->blob_len < sizeof(uint32_t)) {
938
        return LIBSSH2_ERROR_BUFFER_TOO_SMALL;
939
    }
940

941
    methodLen = _libssh2_ntohu32(identity->blob);
942

943
    if(identity->blob_len < sizeof(uint32_t) + methodLen) {
944
        return LIBSSH2_ERROR_BUFFER_TOO_SMALL;
945
    }
946

947
    agent->session->userauth_pblc_method_len = method_len;
948
    agent->session->userauth_pblc_method = LIBSSH2_ALLOC(agent->session,
949
                                                         method_len);
950

951
    memcpy(agent->session->userauth_pblc_method, method, methodLen);
952

953
    rc = agent_sign(agent->session, sig, s_len, data, d_len, &abstract);
954

955
    LIBSSH2_FREE(agent->session, agent->session->userauth_pblc_method);
956
    agent->session->userauth_pblc_method = NULL;
957
    agent->session->userauth_pblc_method_len = 0;
958

959
    return rc;
960
}
961

962
/*
963
 * libssh2_agent_disconnect
964
 *
965
 * Close a connection to an ssh-agent.
966
 *
967
 * Returns 0 if succeeded, or a negative value for error.
968
 */
969
LIBSSH2_API int
970
libssh2_agent_disconnect(LIBSSH2_AGENT *agent)
971
{
972
    if(agent->ops && agent->fd != LIBSSH2_INVALID_SOCKET)
973
        return agent->ops->disconnect(agent);
974
    return 0;
975
}
976

977
/*
978
 * libssh2_agent_free
979
 *
980
 * Free an ssh-agent handle.  This function also frees the internal
981
 * collection of public keys.
982
 */
983
LIBSSH2_API void
984
libssh2_agent_free(LIBSSH2_AGENT *agent)
985
{
986
    /* Allow connection freeing when the socket has lost its connection */
987
    if(agent->fd != LIBSSH2_INVALID_SOCKET) {
988
        libssh2_agent_disconnect(agent);
989
    }
990

991
    if(agent->identity_agent_path)
992
        LIBSSH2_FREE(agent->session, agent->identity_agent_path);
993

994
    agent_free_identities(agent);
995
    LIBSSH2_FREE(agent->session, agent);
996
}
997

998
/*
999
 * libssh2_agent_set_identity_path
1000
 *
1001
 * Allows a custom agent socket path beyond SSH_AUTH_SOCK env
1002
 *
1003
 */
1004
LIBSSH2_API void
1005
libssh2_agent_set_identity_path(LIBSSH2_AGENT *agent, const char *path)
1006
{
1007
    if(agent->identity_agent_path) {
1008
        LIBSSH2_FREE(agent->session, agent->identity_agent_path);
1009
        agent->identity_agent_path = NULL;
1010
    }
1011

1012
    if(path) {
1013
        size_t path_len = strlen(path);
1014
        if(path_len < SIZE_MAX - 1) {
1015
            char *path_buf = LIBSSH2_ALLOC(agent->session, path_len + 1);
1016
            memcpy(path_buf, path, path_len);
1017
            path_buf[path_len] = '\0';
1018
            agent->identity_agent_path = path_buf;
1019
        }
1020
    }
1021
}
1022

1023
/*
1024
 * libssh2_agent_get_identity_path
1025
 *
1026
 * Returns the custom agent socket path if set
1027
 *
1028
 */
1029
LIBSSH2_API const char *libssh2_agent_get_identity_path(LIBSSH2_AGENT *agent)
1030
{
1031
    return agent->identity_agent_path;
1032
}
1033

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.