Coverage Report

Created: 2026-02-26 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libssh/src/agent.c
Line
Count
Source
1
/*
2
 * agent.c - ssh agent functions
3
 *
4
 * This file is part of the SSH Library
5
 *
6
 * Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
7
 *
8
 * This library is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2.1 of the License, or (at your option) any later version.
12
 *
13
 * This library is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19
 * License along with this library; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
 */
22
23
/* This file is based on authfd.c from OpenSSH */
24
25
/*
26
 * How does the ssh-agent work?
27
 *
28
 * a) client sends a request to get a list of all keys
29
 *    the agent returns the count and all public keys
30
 * b) iterate over them to check if the server likes one
31
 * c) the client sends a sign request to the agent
32
 *    type, pubkey as blob, data to sign, flags
33
 *    the agent returns the signed data
34
 */
35
36
#include "config.h"
37
38
#include <stdlib.h>
39
#include <errno.h>
40
#include <string.h>
41
#include <stdio.h>
42
43
#ifdef HAVE_UNISTD_H
44
#include <unistd.h>
45
#endif
46
47
#ifndef _WIN32
48
#include <netinet/in.h>
49
#include <arpa/inet.h>
50
#include <sys/socket.h>
51
#else
52
#include <winsock2.h>
53
#include <ws2tcpip.h>
54
#endif
55
56
#include "libssh/agent.h"
57
#include "libssh/priv.h"
58
#include "libssh/socket.h"
59
#include "libssh/buffer.h"
60
#include "libssh/session.h"
61
#include "libssh/poll.h"
62
#include "libssh/pki.h"
63
#include "libssh/bytearray.h"
64
65
/* macro to check for "agent failure" message */
66
#define agent_failed(x) \
67
0
  (((x) == SSH_AGENT_FAILURE) || ((x) == SSH_COM_AGENT2_FAILURE) || \
68
0
   ((x) == SSH2_AGENT_FAILURE))
69
70
static uint32_t
71
atomicio(struct ssh_agent_struct *agent, void *buf, uint32_t n, int do_read)
72
0
{
73
0
    char *b = buf;
74
0
    uint32_t pos = 0;
75
0
    ssize_t res;
76
0
    ssh_pollfd_t pfd;
77
0
    ssh_channel channel = agent->channel;
78
0
    socket_t fd;
79
80
    /* Using a socket ? */
81
0
    if (channel == NULL) {
82
0
        fd = ssh_socket_get_fd(agent->sock);
83
0
        pfd.fd = fd;
84
0
        pfd.events = do_read ? POLLIN : POLLOUT;
85
86
0
        while (n > pos) {
87
0
            if (do_read) {
88
0
                res = recv(fd, b + pos, n - pos, 0);
89
0
            } else {
90
0
                res = send(fd, b + pos, n - pos, 0);
91
0
            }
92
0
            switch (res) {
93
0
            case -1:
94
0
                if (errno == EINTR) {
95
0
                    continue;
96
0
                }
97
0
#ifdef EWOULDBLOCK
98
0
                if (errno == EAGAIN || errno == EWOULDBLOCK) {
99
#else
100
                if (errno == EAGAIN) {
101
#endif
102
0
                    (void)ssh_poll(&pfd, 1, -1);
103
0
                    continue;
104
0
                }
105
0
                return 0;
106
0
            case 0:
107
                /* read returns 0 on end-of-file */
108
0
                errno = do_read ? 0 : EPIPE;
109
0
                return pos;
110
0
            default:
111
0
                pos += (uint32_t)res;
112
0
            }
113
0
        }
114
0
        return pos;
115
0
    } else {
116
        /* using an SSH channel */
117
0
        while (n > pos) {
118
0
            if (do_read) {
119
0
                res = ssh_channel_read(channel, b + pos, n - pos, 0);
120
0
            } else {
121
0
                res = ssh_channel_write(channel, b + pos, n - pos);
122
0
            }
123
0
            if (res == SSH_AGAIN) {
124
0
                continue;
125
0
            }
126
0
            if (res == SSH_ERROR) {
127
0
                return 0;
128
0
            }
129
0
            pos += (uint32_t)res;
130
0
        }
131
0
        return pos;
132
0
    }
133
0
}
134
135
ssh_agent ssh_agent_new(struct ssh_session_struct *session)
136
13.1k
{
137
13.1k
    ssh_agent agent = NULL;
138
139
13.1k
    agent = calloc(1, sizeof(struct ssh_agent_struct));
140
13.1k
    if (agent == NULL) {
141
0
        return NULL;
142
0
    }
143
144
13.1k
    agent->count = 0;
145
13.1k
    agent->sock = ssh_socket_new(session);
146
13.1k
    if (agent->sock == NULL) {
147
0
        SAFE_FREE(agent);
148
0
        return NULL;
149
0
    }
150
13.1k
    agent->channel = NULL;
151
13.1k
    return agent;
152
13.1k
}
153
154
static void agent_set_channel(struct ssh_agent_struct *agent,
155
                              ssh_channel channel)
156
0
{
157
0
    agent->channel = channel;
158
0
}
159
160
/**
161
 * @addtogroup libssh_auth
162
 *
163
 * @{
164
 */
165
166
/** @brief sets the SSH agent channel.
167
 * The SSH agent channel will be used to authenticate this client using
168
 * an agent through a channel, from another session. The most likely use
169
 * is to implement SSH Agent forwarding into a SSH proxy.
170
 *
171
 * @param session the session
172
 *
173
 * @param[in] channel a SSH channel from another session.
174
 *
175
 * @returns SSH_OK in case of success
176
 *          SSH_ERROR in case of an error
177
 */
178
int ssh_set_agent_channel(ssh_session session, ssh_channel channel)
179
0
{
180
0
    if (!session) {
181
0
        return SSH_ERROR;
182
0
    }
183
0
    if (!session->agent) {
184
0
        ssh_set_error(session,
185
0
                      SSH_REQUEST_DENIED,
186
0
                      "Session has no active agent");
187
0
        return SSH_ERROR;
188
0
    }
189
0
    agent_set_channel(session->agent, channel);
190
0
    return SSH_OK;
191
0
}
192
193
/** @brief sets the SSH agent socket.
194
 * The SSH agent will be used to authenticate this client using
195
 * the given socket to communicate with the ssh-agent. The caller
196
 * is responsible for connecting to the socket prior to calling
197
 * this function.
198
 * @returns SSH_OK in case of success
199
 *          SSH_ERROR in case of an error
200
 */
201
int ssh_set_agent_socket(ssh_session session, socket_t fd)
202
0
{
203
0
    if (!session) {
204
0
        return SSH_ERROR;
205
0
    }
206
0
    if (!session->agent) {
207
0
        ssh_set_error(session,
208
0
                      SSH_REQUEST_DENIED,
209
0
                      "Session has no active agent");
210
0
        return SSH_ERROR;
211
0
    }
212
213
0
    return ssh_socket_set_fd(session->agent->sock, fd);
214
0
}
215
216
/**
217
 * @}
218
 */
219
220
void ssh_agent_close(struct ssh_agent_struct *agent)
221
13.1k
{
222
13.1k
    if (agent == NULL) {
223
0
        return;
224
0
    }
225
226
13.1k
    ssh_socket_close(agent->sock);
227
13.1k
}
228
229
void ssh_agent_free(ssh_agent agent)
230
13.6k
{
231
13.6k
    if (agent) {
232
13.1k
        if (agent->ident) {
233
0
            SSH_BUFFER_FREE(agent->ident);
234
0
        }
235
13.1k
        if (agent->sock) {
236
13.1k
            ssh_agent_close(agent);
237
13.1k
            ssh_socket_free(agent->sock);
238
13.1k
        }
239
13.1k
        SAFE_FREE(agent);
240
13.1k
    }
241
13.6k
}
242
243
static int agent_connect(ssh_session session)
244
0
{
245
0
    const char *auth_sock = NULL;
246
247
0
    if (session == NULL || session->agent == NULL) {
248
0
        return -1;
249
0
    }
250
251
0
    if (session->agent->channel != NULL) {
252
0
        return 0;
253
0
    }
254
255
0
    auth_sock = session->opts.agent_socket ? session->opts.agent_socket
256
0
                                           : getenv("SSH_AUTH_SOCK");
257
258
0
    if (auth_sock && *auth_sock) {
259
0
        if (ssh_socket_unix(session->agent->sock, auth_sock) < 0) {
260
0
            return -1;
261
0
        }
262
0
        return 0;
263
0
    }
264
265
0
    return -1;
266
0
}
267
268
#if 0
269
static int agent_decode_reply(struct ssh_session_struct *session, int type) {
270
  switch (type) {
271
    case SSH_AGENT_FAILURE:
272
    case SSH2_AGENT_FAILURE:
273
    case SSH_COM_AGENT2_FAILURE:
274
      ssh_log(session, SSH_LOG_RARE, "SSH_AGENT_FAILURE");
275
      return 0;
276
    case SSH_AGENT_SUCCESS:
277
      return 1;
278
    default:
279
      ssh_set_error(session, SSH_FATAL,
280
          "Bad response from authentication agent: %d", type);
281
      break;
282
  }
283
284
  return -1;
285
}
286
#endif
287
288
static int agent_talk(struct ssh_session_struct *session,
289
                      struct ssh_buffer_struct *request,
290
                      struct ssh_buffer_struct *reply)
291
0
{
292
0
    uint32_t len = 0;
293
0
    uint8_t tmpbuf[4];
294
0
    uint8_t *payload = tmpbuf;
295
0
    char err_msg[SSH_ERRNO_MSG_MAX] = {0};
296
297
0
    len = ssh_buffer_get_len(request);
298
0
    SSH_LOG(SSH_LOG_TRACE, "Request length: %" PRIu32, len);
299
0
    PUSH_BE_U32(payload, 0, len);
300
301
    /* send length and then the request packet */
302
0
    if (atomicio(session->agent, payload, 4, 0) == 4) {
303
0
        if (atomicio(session->agent, ssh_buffer_get(request), len, 0) != len) {
304
0
            SSH_LOG(SSH_LOG_TRACE,
305
0
                    "atomicio sending request failed: %s",
306
0
                    ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
307
0
            return -1;
308
0
        }
309
0
    } else {
310
0
        SSH_LOG(SSH_LOG_TRACE,
311
0
                "atomicio sending request length failed: %s",
312
0
                ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
313
0
        return -1;
314
0
    }
315
316
    /* wait for response, read the length of the response packet */
317
0
    if (atomicio(session->agent, payload, 4, 1) != 4) {
318
0
        SSH_LOG(SSH_LOG_TRACE,
319
0
                "atomicio read response length failed: %s",
320
0
                ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
321
0
        return -1;
322
0
    }
323
324
0
    len = PULL_BE_U32(payload, 0);
325
0
    if (len > 256 * 1024) {
326
0
        ssh_set_error(session,
327
0
                      SSH_FATAL,
328
0
                      "Authentication response too long: %" PRIu32,
329
0
                      len);
330
0
        return -1;
331
0
    }
332
0
    SSH_LOG(SSH_LOG_TRACE, "Response length: %" PRIu32, len);
333
334
0
    payload = ssh_buffer_allocate(reply, len);
335
0
    if (payload == NULL) {
336
0
        SSH_LOG(SSH_LOG_DEBUG, "Not enough space");
337
0
        return -1;
338
0
    }
339
340
0
    if (atomicio(session->agent, payload, len, 1) != len) {
341
0
        SSH_LOG(SSH_LOG_DEBUG,
342
0
                "Error reading response from authentication socket.");
343
        /* Rollback the unused space */
344
0
        ssh_buffer_pass_bytes_end(reply, len);
345
0
        return -1;
346
0
    }
347
348
0
    return 0;
349
0
}
350
351
uint32_t ssh_agent_get_ident_count(struct ssh_session_struct *session)
352
0
{
353
0
    ssh_buffer request = NULL;
354
0
    ssh_buffer reply = NULL;
355
0
    unsigned int type = 0;
356
0
    uint32_t count = 0;
357
0
    uint32_t rc;
358
359
    /* send message to the agent requesting the list of identities */
360
0
    request = ssh_buffer_new();
361
0
    if (request == NULL) {
362
0
        ssh_set_error_oom(session);
363
0
        return 0;
364
0
    }
365
0
    if (ssh_buffer_add_u8(request, SSH2_AGENTC_REQUEST_IDENTITIES) < 0) {
366
0
        ssh_set_error_oom(session);
367
0
        SSH_BUFFER_FREE(request);
368
0
        return 0;
369
0
    }
370
371
0
    reply = ssh_buffer_new();
372
0
    if (reply == NULL) {
373
0
        SSH_BUFFER_FREE(request);
374
0
        ssh_set_error(session, SSH_FATAL, "Not enough space");
375
0
        return 0;
376
0
    }
377
378
0
    if (agent_talk(session, request, reply) < 0) {
379
0
        SSH_BUFFER_FREE(request);
380
0
        SSH_BUFFER_FREE(reply);
381
0
        return 0;
382
0
    }
383
0
    SSH_BUFFER_FREE(request);
384
385
    /* get message type and verify the answer */
386
0
    rc = ssh_buffer_get_u8(reply, (uint8_t *) &type);
387
0
    if (rc != sizeof(uint8_t)) {
388
0
        ssh_set_error(session, SSH_FATAL,
389
0
                "Bad authentication reply size: %" PRIu32, rc);
390
0
        SSH_BUFFER_FREE(reply);
391
0
        return 0;
392
0
    }
393
#ifdef WORDS_BIGENDIAN
394
    type = bswap_32(type);
395
#endif
396
397
0
    SSH_LOG(SSH_LOG_TRACE,
398
0
            "Answer type: %d, expected answer: %d",
399
0
            type, SSH2_AGENT_IDENTITIES_ANSWER);
400
401
0
    if (agent_failed(type)) {
402
0
        SSH_BUFFER_FREE(reply);
403
0
        return 0;
404
0
    } else if (type != SSH2_AGENT_IDENTITIES_ANSWER) {
405
0
        ssh_set_error(session, SSH_FATAL,
406
0
                "Bad authentication reply message type: %u", type);
407
0
        SSH_BUFFER_FREE(reply);
408
0
        return 0;
409
0
    }
410
411
0
    rc = ssh_buffer_get_u32(reply, &count);
412
0
    if (rc != 4) {
413
0
        ssh_set_error(session,
414
0
                SSH_FATAL,
415
0
                "Failed to read count");
416
0
        SSH_BUFFER_FREE(reply);
417
0
        return 0;
418
0
    }
419
0
    session->agent->count = ntohl(count);
420
0
    SSH_LOG(SSH_LOG_DEBUG, "Agent count: %d",
421
0
            session->agent->count);
422
0
    if (session->agent->count > 1024) {
423
0
        ssh_set_error(session, SSH_FATAL,
424
0
                "Too many identities in authentication reply: %d",
425
0
                session->agent->count);
426
0
        SSH_BUFFER_FREE(reply);
427
0
        return 0;
428
0
    }
429
430
0
    ssh_buffer_free(session->agent->ident);
431
0
    session->agent->ident = reply;
432
433
0
    return session->agent->count;
434
0
}
435
436
/* caller has to free comment */
437
ssh_key ssh_agent_get_first_ident(struct ssh_session_struct *session,
438
0
                              char **comment) {
439
0
    if (ssh_agent_get_ident_count(session) > 0) {
440
0
        return ssh_agent_get_next_ident(session, comment);
441
0
    }
442
443
0
    return NULL;
444
0
}
445
446
/* caller has to free comment */
447
ssh_key ssh_agent_get_next_ident(struct ssh_session_struct *session,
448
                                 char **comment)
449
0
{
450
0
    struct ssh_key_struct *key = NULL;
451
0
    struct ssh_string_struct *blob = NULL;
452
0
    struct ssh_string_struct *tmp = NULL;
453
0
    int rc;
454
455
0
    if (session->agent->count == 0) {
456
0
        return NULL;
457
0
    }
458
459
    /* get the blob */
460
0
    blob = ssh_buffer_get_ssh_string(session->agent->ident);
461
0
    if (blob == NULL) {
462
0
        return NULL;
463
0
    }
464
465
    /* get the comment */
466
0
    tmp = ssh_buffer_get_ssh_string(session->agent->ident);
467
0
    if (tmp == NULL) {
468
0
        SSH_STRING_FREE(blob);
469
470
0
        return NULL;
471
0
    }
472
473
0
    if (comment) {
474
0
        *comment = ssh_string_to_char(tmp);
475
0
    } else {
476
0
        SSH_STRING_FREE(blob);
477
0
        SSH_STRING_FREE(tmp);
478
479
0
        return NULL;
480
0
    }
481
0
    SSH_STRING_FREE(tmp);
482
483
    /* get key from blob */
484
0
    rc = ssh_pki_import_pubkey_blob(blob, &key);
485
0
    if (rc == SSH_ERROR) {
486
        /* Try again as a cert. */
487
0
        rc = ssh_pki_import_cert_blob(blob, &key);
488
0
    }
489
0
    SSH_STRING_FREE(blob);
490
0
    if (rc == SSH_ERROR) {
491
0
        return NULL;
492
0
    }
493
494
0
    return key;
495
0
}
496
497
int ssh_agent_is_running(ssh_session session)
498
0
{
499
0
    if (session == NULL || session->agent == NULL) {
500
0
        return 0;
501
0
    }
502
503
0
    if (ssh_socket_is_open(session->agent->sock)) {
504
0
        return 1;
505
0
    } else {
506
0
        if (agent_connect(session) < 0) {
507
0
            return 0;
508
0
        } else {
509
0
            return 1;
510
0
        }
511
0
    }
512
513
0
    return 0;
514
0
}
515
516
ssh_string ssh_agent_sign_data(ssh_session session,
517
                               const ssh_key pubkey,
518
                               struct ssh_buffer_struct *data)
519
0
{
520
0
    ssh_buffer request = NULL;
521
0
    ssh_buffer reply = NULL;
522
0
    ssh_string key_blob = NULL;
523
0
    ssh_string sig_blob = NULL;
524
0
    unsigned int type = 0;
525
0
    unsigned int flags = 0;
526
0
    uint32_t dlen;
527
0
    size_t request_len;
528
0
    int rc;
529
530
0
    request = ssh_buffer_new();
531
0
    if (request == NULL) {
532
0
        return NULL;
533
0
    }
534
535
    /* create request */
536
0
    if (ssh_buffer_add_u8(request, SSH2_AGENTC_SIGN_REQUEST) < 0) {
537
0
        SSH_BUFFER_FREE(request);
538
0
        return NULL;
539
0
    }
540
541
0
    rc = ssh_pki_export_pubkey_blob(pubkey, &key_blob);
542
0
    if (rc < 0) {
543
0
        SSH_BUFFER_FREE(request);
544
0
        return NULL;
545
0
    }
546
547
    /*
548
     * make sure it already can contain all the expected content:
549
     * - 1 x uint8_t
550
     * - 2 x uint32_t
551
     * - 1 x ssh_string (uint8_t + data)
552
     */
553
0
    request_len = sizeof(uint8_t) * 2 +
554
0
                  sizeof(uint32_t) * 2 +
555
0
                  ssh_string_len(key_blob);
556
    /* this can't overflow the uint32_t as the
557
     * STRING_SIZE_MAX is (UINT32_MAX >> 8) + 1 */
558
0
    rc = ssh_buffer_allocate_size(request, (uint32_t)request_len);
559
0
    if (rc < 0) {
560
0
        SSH_STRING_FREE(key_blob);
561
0
        SSH_BUFFER_FREE(request);
562
0
        return NULL;
563
0
    }
564
565
    /* adds len + blob */
566
0
    rc = ssh_buffer_add_ssh_string(request, key_blob);
567
0
    SSH_STRING_FREE(key_blob);
568
0
    if (rc < 0) {
569
0
        SSH_BUFFER_FREE(request);
570
0
        return NULL;
571
0
    }
572
573
    /* Add data */
574
0
    dlen = ssh_buffer_get_len(data);
575
0
    if (ssh_buffer_add_u32(request, htonl(dlen)) < 0) {
576
0
        SSH_BUFFER_FREE(request);
577
0
        return NULL;
578
0
    }
579
0
    if (ssh_buffer_add_data(request, ssh_buffer_get(data), dlen) < 0) {
580
0
        SSH_BUFFER_FREE(request);
581
0
        return NULL;
582
0
    }
583
584
    /* Add Flags: SHA2 extension (RFC 8332) if negotiated */
585
0
    if (ssh_key_type_plain(pubkey->type) == SSH_KEYTYPE_RSA) {
586
0
        if (session->extensions & SSH_EXT_SIG_RSA_SHA512) {
587
0
            flags |= SSH_AGENT_RSA_SHA2_512;
588
0
        } else if (session->extensions & SSH_EXT_SIG_RSA_SHA256) {
589
0
            flags |= SSH_AGENT_RSA_SHA2_256;
590
0
        }
591
0
    }
592
0
    if (ssh_buffer_add_u32(request, htonl(flags)) < 0) {
593
0
        SSH_BUFFER_FREE(request);
594
0
        return NULL;
595
0
    }
596
597
0
    reply = ssh_buffer_new();
598
0
    if (reply == NULL) {
599
0
        SSH_BUFFER_FREE(request);
600
0
        return NULL;
601
0
    }
602
603
    /* send the request */
604
0
    if (agent_talk(session, request, reply) < 0) {
605
0
        SSH_BUFFER_FREE(request);
606
0
        SSH_BUFFER_FREE(reply);
607
0
        return NULL;
608
0
    }
609
0
    SSH_BUFFER_FREE(request);
610
611
    /* check if reply is valid */
612
0
    if (ssh_buffer_get_u8(reply, (uint8_t *) &type) != sizeof(uint8_t)) {
613
0
        SSH_BUFFER_FREE(reply);
614
0
        return NULL;
615
0
    }
616
#ifdef WORDS_BIGENDIAN
617
    type = bswap_32(type);
618
#endif
619
620
0
    if (agent_failed(type)) {
621
0
        SSH_LOG(SSH_LOG_DEBUG, "Agent reports failure in signing the key");
622
0
        SSH_BUFFER_FREE(reply);
623
0
        return NULL;
624
0
    } else if (type != SSH2_AGENT_SIGN_RESPONSE) {
625
0
        ssh_set_error(session,
626
0
                      SSH_FATAL,
627
0
                      "Bad authentication response: %u",
628
0
                      type);
629
0
        SSH_BUFFER_FREE(reply);
630
0
        return NULL;
631
0
    }
632
633
0
    sig_blob = ssh_buffer_get_ssh_string(reply);
634
0
    SSH_BUFFER_FREE(reply);
635
636
0
    return sig_blob;
637
0
}