Coverage Report

Created: 2025-10-10 07:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libssh/src/bind.c
Line
Count
Source
1
/*
2
 * bind.c : all ssh_bind functions
3
 *
4
 * This file is part of the SSH Library
5
 *
6
 * Copyright (c) 2004-2005 by Aris Adamantiadis
7
 *
8
 * The SSH Library is free software; you can redistribute it and/or modify
9
 * it under the terms of the GNU Lesser General Public License as published by
10
 * the Free Software Foundation; either version 2.1 of the License, or (at your
11
 * option) any later version.
12
 *
13
 * The SSH Library is distributed in the hope that it will be useful, but
14
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
16
 * License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public License
19
 * along with the SSH Library; see the file COPYING.  If not, write to
20
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
21
 * MA 02111-1307, USA.
22
 */
23
24
25
#include "config.h"
26
27
#include <errno.h>
28
#include <fcntl.h>
29
#include <stdio.h>
30
#include <string.h>
31
#include <stdlib.h>
32
33
#include "libssh/priv.h"
34
#include "libssh/bind.h"
35
#include "libssh/libssh.h"
36
#include "libssh/server.h"
37
#include "libssh/pki.h"
38
#include "libssh/buffer.h"
39
#include "libssh/socket.h"
40
#include "libssh/session.h"
41
#include "libssh/token.h"
42
43
/**
44
 * @addtogroup libssh_server
45
 *
46
 * @{
47
 */
48
49
50
#ifdef _WIN32
51
#include <io.h>
52
#include <winsock2.h>
53
#include <ws2tcpip.h>
54
55
/*
56
 * <wspiapi.h> is necessary for getaddrinfo before Windows XP, but it isn't
57
 * available on some platforms like MinGW.
58
 */
59
#ifdef HAVE_WSPIAPI_H
60
# include <wspiapi.h>
61
#endif
62
63
#define SOCKOPT_TYPE_ARG4 char
64
65
#else /* _WIN32 */
66
67
#include <sys/socket.h>
68
#include <netinet/in.h>
69
#include <netdb.h>
70
#define SOCKOPT_TYPE_ARG4 int
71
72
#endif /* _WIN32 */
73
74
static socket_t bind_socket(ssh_bind sshbind, const char *hostname,
75
0
    int port) {
76
0
    char port_c[6];
77
0
    struct addrinfo *ai = NULL;
78
0
    struct addrinfo hints;
79
0
    int opt = 1;
80
0
    socket_t s;
81
0
    int rc;
82
0
    char err_msg[SSH_ERRNO_MSG_MAX] = {0};
83
84
0
    ZERO_STRUCT(hints);
85
86
0
    hints.ai_flags = AI_PASSIVE;
87
0
    hints.ai_socktype = SOCK_STREAM;
88
89
0
    snprintf(port_c, 6, "%d", port);
90
0
    rc = getaddrinfo(hostname, port_c, &hints, &ai);
91
0
    if (rc != 0) {
92
0
        ssh_set_error(sshbind,
93
0
                      SSH_FATAL,
94
0
                      "Resolving %s: %s", hostname, gai_strerror(rc));
95
0
        return -1;
96
0
    }
97
98
0
    s = socket (ai->ai_family,
99
0
                           ai->ai_socktype,
100
0
                           ai->ai_protocol);
101
0
    if (s == SSH_INVALID_SOCKET) {
102
0
        ssh_set_error(sshbind, SSH_FATAL, "%s",
103
0
                      ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
104
0
        freeaddrinfo (ai);
105
0
        return -1;
106
0
    }
107
108
0
    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
109
0
                   (char *)&opt, sizeof(opt)) < 0) {
110
0
        ssh_set_error(sshbind,
111
0
                      SSH_FATAL,
112
0
                      "Setting socket options failed: %s",
113
0
                      ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
114
0
        freeaddrinfo (ai);
115
0
        CLOSE_SOCKET(s);
116
0
        return -1;
117
0
    }
118
119
0
    if (bind(s, ai->ai_addr, ai->ai_addrlen) != 0) {
120
0
        ssh_set_error(sshbind,
121
0
                      SSH_FATAL,
122
0
                      "Binding to %s:%d: %s",
123
0
                      hostname,
124
0
                      port,
125
0
                      ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
126
0
        freeaddrinfo (ai);
127
0
        CLOSE_SOCKET(s);
128
0
        return -1;
129
0
    }
130
131
0
    freeaddrinfo (ai);
132
0
    return s;
133
0
}
134
135
ssh_bind ssh_bind_new(void)
136
7.52k
{
137
7.52k
    ssh_bind ptr = NULL;
138
139
7.52k
    ptr = calloc(1, sizeof(struct ssh_bind_struct));
140
7.52k
    if (ptr == NULL) {
141
29
        return NULL;
142
29
    }
143
7.49k
    ptr->bindfd = SSH_INVALID_SOCKET;
144
7.49k
    ptr->bindport = 22;
145
7.49k
    ptr->common.log_verbosity = 0;
146
147
7.49k
    return ptr;
148
7.52k
}
149
150
0
static int ssh_bind_import_keys(ssh_bind sshbind) {
151
0
  int rc;
152
153
0
#ifdef HAVE_ECC
154
0
  if (sshbind->ecdsa == NULL && sshbind->ecdsakey != NULL) {
155
0
      rc = ssh_pki_import_privkey_file(sshbind->ecdsakey,
156
0
                                       NULL,
157
0
                                       NULL,
158
0
                                       NULL,
159
0
                                       &sshbind->ecdsa);
160
0
      if (rc == SSH_ERROR || rc == SSH_EOF) {
161
0
          ssh_set_error(sshbind, SSH_FATAL,
162
0
                  "Failed to import private ECDSA host key");
163
0
          return SSH_ERROR;
164
0
      }
165
166
0
      if (!is_ecdsa_key_type(ssh_key_type(sshbind->ecdsa))) {
167
0
          ssh_set_error(sshbind, SSH_FATAL,
168
0
                  "The ECDSA host key has the wrong type");
169
0
          ssh_key_free(sshbind->ecdsa);
170
0
          sshbind->ecdsa = NULL;
171
0
          return SSH_ERROR;
172
0
      }
173
0
  }
174
0
#endif
175
176
0
  if (sshbind->rsa == NULL && sshbind->rsakey != NULL) {
177
0
      rc = ssh_pki_import_privkey_file(sshbind->rsakey,
178
0
                                       NULL,
179
0
                                       NULL,
180
0
                                       NULL,
181
0
                                       &sshbind->rsa);
182
0
      if (rc == SSH_ERROR || rc == SSH_EOF) {
183
0
          ssh_set_error(sshbind, SSH_FATAL,
184
0
                  "Failed to import private RSA host key");
185
0
          return SSH_ERROR;
186
0
      }
187
188
0
      if (ssh_key_type(sshbind->rsa) != SSH_KEYTYPE_RSA) {
189
0
          ssh_set_error(sshbind, SSH_FATAL,
190
0
                  "The RSA host key has the wrong type");
191
0
          ssh_key_free(sshbind->rsa);
192
0
          sshbind->rsa = NULL;
193
0
          return SSH_ERROR;
194
0
      }
195
0
  }
196
197
0
  if (sshbind->ed25519 == NULL && sshbind->ed25519key != NULL) {
198
0
      rc = ssh_pki_import_privkey_file(sshbind->ed25519key,
199
0
                                       NULL,
200
0
                                       NULL,
201
0
                                       NULL,
202
0
                                       &sshbind->ed25519);
203
0
      if (rc == SSH_ERROR || rc == SSH_EOF) {
204
0
          ssh_set_error(sshbind, SSH_FATAL,
205
0
                  "Failed to import private ED25519 host key");
206
0
          return SSH_ERROR;
207
0
      }
208
209
0
      if (ssh_key_type(sshbind->ed25519) != SSH_KEYTYPE_ED25519) {
210
0
          ssh_set_error(sshbind, SSH_FATAL,
211
0
                  "The ED25519 host key has the wrong type");
212
0
          ssh_key_free(sshbind->ed25519);
213
0
          sshbind->ed25519 = NULL;
214
0
          return SSH_ERROR;
215
0
      }
216
0
  }
217
218
0
  return SSH_OK;
219
0
}
220
221
int ssh_bind_listen(ssh_bind sshbind)
222
0
{
223
0
    const char *host = NULL;
224
0
    socket_t fd;
225
0
    int rc;
226
227
    /* Apply global bind configurations, if it hasn't been applied before */
228
0
    rc = ssh_bind_options_parse_config(sshbind, NULL);
229
0
    if (rc != 0) {
230
0
        ssh_set_error(sshbind, SSH_FATAL, "Could not parse global config");
231
0
        return SSH_ERROR;
232
0
    }
233
234
    /* Set default hostkey paths if no hostkey was found before */
235
0
    if (sshbind->ecdsakey == NULL &&
236
0
        sshbind->rsakey == NULL &&
237
0
        sshbind->ed25519key == NULL) {
238
239
0
        sshbind->ecdsakey = strdup("/etc/ssh/ssh_host_ecdsa_key");
240
0
        sshbind->rsakey = strdup("/etc/ssh/ssh_host_rsa_key");
241
0
        sshbind->ed25519key = strdup("/etc/ssh/ssh_host_ed25519_key");
242
0
    }
243
244
0
    if (sshbind->rsa == NULL &&
245
0
        sshbind->ecdsa == NULL &&
246
0
        sshbind->ed25519 == NULL) {
247
0
        rc = ssh_bind_import_keys(sshbind);
248
0
        if (rc != SSH_OK) {
249
0
            return SSH_ERROR;
250
0
        }
251
0
    }
252
253
0
    if (sshbind->bindfd == SSH_INVALID_SOCKET) {
254
0
        host = sshbind->bindaddr;
255
0
        if (host == NULL) {
256
0
            host = "0.0.0.0";
257
0
        }
258
259
0
        fd = bind_socket(sshbind, host, sshbind->bindport);
260
0
        if (fd == SSH_INVALID_SOCKET) {
261
0
            return SSH_ERROR;
262
0
        }
263
264
0
        if (listen(fd, 10) < 0) {
265
0
            char err_msg[SSH_ERRNO_MSG_MAX] = {0};
266
0
            ssh_set_error(sshbind,
267
0
                          SSH_FATAL,
268
0
                          "Listening to socket %d: %s",
269
0
                          fd,
270
0
                          ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
271
0
            CLOSE_SOCKET(fd);
272
0
            return SSH_ERROR;
273
0
        }
274
275
0
        sshbind->bindfd = fd;
276
0
    } else {
277
0
        SSH_LOG(SSH_LOG_DEBUG, "Using app-provided bind socket");
278
0
    }
279
0
    return 0;
280
0
}
281
282
int ssh_bind_set_callbacks(ssh_bind sshbind, ssh_bind_callbacks callbacks, void *userdata)
283
0
{
284
0
    if (sshbind == NULL) {
285
0
        return SSH_ERROR;
286
0
    }
287
0
    if (callbacks == NULL) {
288
0
        ssh_set_error_invalid(sshbind);
289
0
        return SSH_ERROR;
290
0
    }
291
0
    if (callbacks->size <= 0 || callbacks->size > 1024 * sizeof(void *)) {
292
0
        ssh_set_error(sshbind,
293
0
                      SSH_FATAL,
294
0
                      "Invalid callback passed in (badly initialized)");
295
0
        return SSH_ERROR;
296
0
    }
297
0
    sshbind->bind_callbacks = callbacks;
298
0
    sshbind->bind_callbacks_userdata = userdata;
299
0
    return 0;
300
0
}
301
302
/** @internal
303
 * @brief callback being called by poll when an event happens
304
 *
305
 */
306
static int ssh_bind_poll_callback(ssh_poll_handle sshpoll, socket_t fd, int revents, void *user)
307
0
{
308
0
    ssh_bind sshbind = (ssh_bind)user;
309
0
    (void)sshpoll;
310
0
    (void)fd;
311
312
0
    if (revents & POLLIN) {
313
        /* new incoming connection */
314
0
        if (ssh_callbacks_exists(sshbind->bind_callbacks, incoming_connection)) {
315
0
            sshbind->bind_callbacks->incoming_connection(sshbind,
316
0
                                                         sshbind->bind_callbacks_userdata);
317
0
        }
318
0
    }
319
0
    return 0;
320
0
}
321
322
/** @internal
323
 * @brief returns the current poll handle, or creates it
324
 * @param sshbind the ssh_bind object
325
 * @returns a ssh_poll handle suitable for operation
326
 */
327
ssh_poll_handle ssh_bind_get_poll(ssh_bind sshbind)
328
0
{
329
0
    short events = POLLIN;
330
331
0
    if (sshbind->poll) {
332
0
        return sshbind->poll;
333
0
    }
334
335
#ifdef POLLRDHUP
336
    events |= POLLRDHUP;
337
#endif /* POLLRDHUP */
338
339
0
    sshbind->poll = ssh_poll_new(sshbind->bindfd,
340
0
                                 events,
341
0
                                 ssh_bind_poll_callback,
342
0
                                 sshbind);
343
344
0
    return sshbind->poll;
345
0
}
346
347
void ssh_bind_set_blocking(ssh_bind sshbind, int blocking)
348
0
{
349
0
    sshbind->blocking = blocking ? 1 : 0;
350
0
}
351
352
socket_t ssh_bind_get_fd(ssh_bind sshbind)
353
0
{
354
0
    return sshbind->bindfd;
355
0
}
356
357
void ssh_bind_set_fd(ssh_bind sshbind, socket_t fd)
358
0
{
359
0
    sshbind->bindfd = fd;
360
0
}
361
362
void ssh_bind_fd_toaccept(ssh_bind sshbind)
363
0
{
364
0
    sshbind->toaccept = 1;
365
0
}
366
367
7.51k
void ssh_bind_free(ssh_bind sshbind){
368
7.51k
  int i;
369
370
7.51k
  if (sshbind == NULL) {
371
24
    return;
372
24
  }
373
374
7.49k
  if (sshbind->bindfd >= 0) {
375
0
      CLOSE_SOCKET(sshbind->bindfd);
376
0
  }
377
7.49k
  sshbind->bindfd = SSH_INVALID_SOCKET;
378
379
  /* options */
380
7.49k
  SAFE_FREE(sshbind->banner);
381
7.49k
  SAFE_FREE(sshbind->moduli_file);
382
7.49k
  SAFE_FREE(sshbind->bindaddr);
383
7.49k
  SAFE_FREE(sshbind->config_dir);
384
7.49k
  SAFE_FREE(sshbind->pubkey_accepted_key_types);
385
386
7.49k
  SAFE_FREE(sshbind->rsakey);
387
7.49k
  SAFE_FREE(sshbind->ecdsakey);
388
7.49k
  SAFE_FREE(sshbind->ed25519key);
389
390
7.49k
  ssh_key_free(sshbind->rsa);
391
7.49k
  sshbind->rsa = NULL;
392
7.49k
  ssh_key_free(sshbind->ecdsa);
393
7.49k
  sshbind->ecdsa = NULL;
394
7.49k
  ssh_key_free(sshbind->ed25519);
395
7.49k
  sshbind->ed25519 = NULL;
396
397
82.4k
  for (i = 0; i < SSH_KEX_METHODS; i++) {
398
74.9k
    if (sshbind->wanted_methods[i]) {
399
27.4k
      SAFE_FREE(sshbind->wanted_methods[i]);
400
27.4k
    }
401
74.9k
  }
402
403
7.49k
  SAFE_FREE(sshbind);
404
7.49k
}
405
406
int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd)
407
6.71k
{
408
6.71k
    ssh_poll_handle handle = NULL;
409
6.71k
    int i, rc;
410
411
6.71k
    if (sshbind == NULL) {
412
0
        return SSH_ERROR;
413
0
    }
414
415
6.71k
    if (session == NULL){
416
0
        ssh_set_error(sshbind, SSH_FATAL,"session is null");
417
0
        return SSH_ERROR;
418
0
    }
419
420
6.71k
    session->server = 1;
421
422
    /* Copy options from bind to session */
423
73.8k
    for (i = 0; i < SSH_KEX_METHODS; i++) {
424
67.1k
      if (sshbind->wanted_methods[i]) {
425
26.8k
        session->opts.wanted_methods[i] = strdup(sshbind->wanted_methods[i]);
426
26.8k
        if (session->opts.wanted_methods[i] == NULL) {
427
0
          return SSH_ERROR;
428
0
        }
429
26.8k
      }
430
67.1k
    }
431
432
6.71k
    if (sshbind->bindaddr == NULL)
433
6.71k
      session->opts.bindaddr = NULL;
434
0
    else {
435
0
      SAFE_FREE(session->opts.bindaddr);
436
0
      session->opts.bindaddr = strdup(sshbind->bindaddr);
437
0
      if (session->opts.bindaddr == NULL) {
438
0
        return SSH_ERROR;
439
0
      }
440
0
    }
441
442
6.71k
    if (sshbind->pubkey_accepted_key_types != NULL) {
443
0
        if (session->opts.pubkey_accepted_types == NULL) {
444
0
            session->opts.pubkey_accepted_types = strdup(sshbind->pubkey_accepted_key_types);
445
0
            if (session->opts.pubkey_accepted_types == NULL) {
446
0
                ssh_set_error_oom(sshbind);
447
0
                return SSH_ERROR;
448
0
            }
449
0
        } else {
450
0
            char *p = NULL;
451
            /* If something was set to the session prior to calling this
452
             * function, keep only what is allowed by the options set in
453
             * sshbind */
454
0
            p = ssh_find_all_matching(sshbind->pubkey_accepted_key_types,
455
0
                                      session->opts.pubkey_accepted_types);
456
0
            if (p == NULL) {
457
0
                return SSH_ERROR;
458
0
            }
459
460
0
            SAFE_FREE(session->opts.pubkey_accepted_types);
461
0
            session->opts.pubkey_accepted_types = p;
462
0
        }
463
0
    }
464
465
6.71k
    session->common.log_verbosity = sshbind->common.log_verbosity;
466
467
6.71k
    if (sshbind->banner != NULL) {
468
0
        session->server_opts.custombanner = strdup(sshbind->banner);
469
0
        if (session->server_opts.custombanner == NULL) {
470
0
            ssh_set_error_oom(sshbind);
471
0
            return SSH_ERROR;
472
0
        }
473
0
    }
474
475
6.71k
    if (sshbind->moduli_file != NULL) {
476
0
        session->server_opts.moduli_file = strdup(sshbind->moduli_file);
477
0
        if (session->server_opts.moduli_file == NULL) {
478
0
            ssh_set_error_oom(sshbind);
479
0
            return SSH_ERROR;
480
0
        }
481
0
    }
482
483
6.71k
    session->opts.rsa_min_size = sshbind->rsa_min_size;
484
485
6.71k
    ssh_socket_free(session->socket);
486
6.71k
    session->socket = ssh_socket_new(session);
487
6.71k
    if (session->socket == NULL) {
488
      /* perhaps it may be better to copy the error from session to sshbind */
489
0
      ssh_set_error_oom(sshbind);
490
0
      return SSH_ERROR;
491
0
    }
492
6.71k
    rc = ssh_socket_set_fd(session->socket, fd);
493
6.71k
    if (rc != SSH_OK) {
494
0
        return rc;
495
0
    }
496
6.71k
    handle = ssh_socket_get_poll_handle(session->socket);
497
6.71k
    if (handle == NULL) {
498
0
        ssh_set_error_oom(sshbind);
499
0
        return SSH_ERROR;
500
0
    }
501
6.71k
    ssh_socket_set_connected(session->socket, handle);
502
503
    /* We must try to import any keys that could be imported in case
504
     * we are not using ssh_bind_listen (which is the other place
505
     * where keys can be imported) on this ssh_bind and are instead
506
     * only using ssh_bind_accept_fd to manage sockets ourselves.
507
     */
508
6.71k
    if (sshbind->rsa == NULL &&
509
0
        sshbind->ecdsa == NULL &&
510
0
        sshbind->ed25519 == NULL) {
511
0
        rc = ssh_bind_import_keys(sshbind);
512
0
        if (rc != SSH_OK) {
513
0
            return SSH_ERROR;
514
0
        }
515
0
    }
516
517
6.71k
#ifdef HAVE_ECC
518
6.71k
    if (sshbind->ecdsa) {
519
0
        session->srv.ecdsa_key = ssh_key_dup(sshbind->ecdsa);
520
0
        if (session->srv.ecdsa_key == NULL) {
521
0
          ssh_set_error_oom(sshbind);
522
0
          return SSH_ERROR;
523
0
        }
524
0
    }
525
6.71k
#endif
526
6.71k
    if (sshbind->rsa) {
527
6.71k
        session->srv.rsa_key = ssh_key_dup(sshbind->rsa);
528
6.71k
        if (session->srv.rsa_key == NULL) {
529
0
          ssh_set_error_oom(sshbind);
530
0
          return SSH_ERROR;
531
0
        }
532
6.71k
    }
533
6.71k
    if (sshbind->ed25519 != NULL) {
534
0
        session->srv.ed25519_key = ssh_key_dup(sshbind->ed25519);
535
0
        if (session->srv.ed25519_key == NULL){
536
0
            ssh_set_error_oom(sshbind);
537
0
            return SSH_ERROR;
538
0
        }
539
0
    }
540
541
    /* force PRNG to change state in case we fork after ssh_bind_accept */
542
6.71k
    ssh_reseed();
543
6.71k
    return SSH_OK;
544
6.71k
}
545
546
int ssh_bind_accept(ssh_bind sshbind, ssh_session session)
547
0
{
548
0
    socket_t fd = SSH_INVALID_SOCKET;
549
0
    int rc;
550
551
0
    if (sshbind->bindfd == SSH_INVALID_SOCKET) {
552
0
        ssh_set_error(sshbind, SSH_FATAL,
553
0
                      "Can't accept new clients on a not bound socket.");
554
0
        return SSH_ERROR;
555
0
    }
556
557
0
    if (session == NULL) {
558
0
        ssh_set_error(sshbind, SSH_FATAL, "session is null");
559
0
        return SSH_ERROR;
560
0
    }
561
562
0
    fd = accept(sshbind->bindfd, NULL, NULL);
563
0
    if (fd == SSH_INVALID_SOCKET) {
564
0
        char err_msg[SSH_ERRNO_MSG_MAX] = {0};
565
0
        if (errno == EINTR) {
566
0
            ssh_set_error(sshbind, SSH_EINTR,
567
0
                          "Accepting a new connection (child signal error): %s",
568
0
                          ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
569
0
        } else {
570
0
            ssh_set_error(sshbind, SSH_FATAL,
571
0
                          "Accepting a new connection: %s",
572
0
                          ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
573
0
        }
574
0
        return SSH_ERROR;
575
0
    }
576
0
    rc = ssh_bind_accept_fd(sshbind, session, fd);
577
578
0
    if (rc == SSH_ERROR) {
579
0
        CLOSE_SOCKET(fd);
580
0
        ssh_socket_free(session->socket);
581
0
    }
582
583
0
    return rc;
584
0
}
585
586
587
/**
588
 * @}
589
 */