Coverage Report

Created: 2026-06-10 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libssh/src/dh-gex.c
Line
Count
Source
1
/*
2
 * dh-gex.c - diffie-hellman group exchange
3
 *
4
 * This file is part of the SSH Library
5
 *
6
 * Copyright (c) 2016 by Aris Adamantiadis <aris@0xbadc0de.be>
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
#include "config.h"
25
26
#include <errno.h>
27
#include <stdbool.h>
28
#include <string.h>
29
#include <stdio.h>
30
31
#include "libssh/priv.h"
32
#include "libssh/dh-gex.h"
33
#include "libssh/libssh.h"
34
#include "libssh/ssh2.h"
35
#include "libssh/callbacks.h"
36
#include "libssh/dh.h"
37
#include "libssh/buffer.h"
38
#include "libssh/session.h"
39
40
/* Minimum, recommended and maximum size of DH group */
41
394
#define DH_PMIN 2048
42
0
#define DH_PREQ 2048
43
2.30k
#define DH_PMAX 8192
44
45
static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_group);
46
static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply);
47
48
static ssh_packet_callback dhgex_client_callbacks[] = {
49
    ssh_packet_client_dhgex_group, /* SSH_MSG_KEX_DH_GEX_GROUP */
50
    NULL,                          /* SSH_MSG_KEX_DH_GEX_INIT */
51
    ssh_packet_client_dhgex_reply  /* SSH_MSG_KEX_DH_GEX_REPLY */
52
};
53
54
static struct ssh_packet_callbacks_struct ssh_dhgex_client_callbacks = {
55
    .start = SSH2_MSG_KEX_DH_GEX_GROUP,
56
    .n_callbacks = 3,
57
    .callbacks = dhgex_client_callbacks,
58
    .user = NULL
59
};
60
61
/** @internal
62
 * @brief initiates a diffie-hellman-group-exchange kex
63
 */
64
int ssh_client_dhgex_init(ssh_session session)
65
0
{
66
0
    int rc;
67
68
0
    rc = ssh_dh_init_common(session->next_crypto);
69
0
    if (rc != SSH_OK){
70
0
        goto error;
71
0
    }
72
73
0
    session->next_crypto->dh_pmin = DH_PMIN;
74
0
    session->next_crypto->dh_pn = DH_PREQ;
75
0
    session->next_crypto->dh_pmax = DH_PMAX;
76
    /* Minimum group size, preferred group size, maximum group size */
77
0
    rc = ssh_buffer_pack(session->out_buffer,
78
0
                         "bddd",
79
0
                         SSH2_MSG_KEX_DH_GEX_REQUEST,
80
0
                         session->next_crypto->dh_pmin,
81
0
                         session->next_crypto->dh_pn,
82
0
                         session->next_crypto->dh_pmax);
83
0
    if (rc != SSH_OK) {
84
0
        goto error;
85
0
    }
86
87
    /* register the packet callbacks */
88
0
    ssh_packet_set_callbacks(session, &ssh_dhgex_client_callbacks);
89
0
    session->dh_handshake_state = DH_STATE_REQUEST_SENT;
90
0
    rc = ssh_packet_send(session);
91
0
    if (rc == SSH_ERROR) {
92
0
        goto error;
93
0
    }
94
0
    return rc;
95
0
error:
96
0
    ssh_dh_cleanup(session->next_crypto);
97
0
    return SSH_ERROR;
98
0
}
99
100
/** @internal
101
 *  @brief handle a DH_GEX_GROUP packet, client side. This packet contains
102
 *         the group parameters.
103
 */
104
SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_group)
105
0
{
106
0
    int rc;
107
0
    int blen;
108
0
    bignum pmin1 = NULL, one = NULL;
109
0
    bignum modulus = NULL, generator = NULL;
110
0
#if !defined(HAVE_LIBCRYPTO) || OPENSSL_VERSION_NUMBER < 0x30000000L
111
0
    const_bignum pubkey;
112
#else
113
    bignum pubkey = NULL;
114
#endif /* OPENSSL_VERSION_NUMBER */
115
0
    (void) type;
116
0
    (void) user;
117
118
0
    SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_KEX_DH_GEX_GROUP received");
119
120
0
    if (session->dh_handshake_state != DH_STATE_REQUEST_SENT) {
121
0
        ssh_set_error(session,
122
0
                      SSH_FATAL,
123
0
                      "Received DH_GEX_GROUP in invalid state");
124
0
        goto error;
125
0
    }
126
0
    one = bignum_new();
127
0
    pmin1 = bignum_new();
128
0
    if (one == NULL || pmin1 == NULL) {
129
0
        ssh_set_error_oom(session);
130
0
        goto error;
131
0
    }
132
0
    rc = ssh_buffer_unpack(packet,
133
0
                           "BB",
134
0
                           &modulus,
135
0
                           &generator);
136
0
    if (rc != SSH_OK) {
137
0
        ssh_set_error(session, SSH_FATAL, "Invalid DH_GEX_GROUP packet");
138
0
        goto error;
139
0
    }
140
    /* basic checks */
141
0
    if (ssh_fips_mode() &&
142
0
        !ssh_dh_is_known_group(modulus, generator)) {
143
0
        ssh_set_error(session,
144
0
                      SSH_FATAL,
145
0
                      "The received DH group is not FIPS approved");
146
0
        goto error;
147
0
    }
148
0
    rc = bignum_set_word(one, 1);
149
0
    if (rc != 1) {
150
0
        goto error;
151
0
    }
152
0
    blen = bignum_num_bits(modulus);
153
0
    if (blen < DH_PMIN || blen > DH_PMAX) {
154
0
        ssh_set_error(session,
155
0
                SSH_FATAL,
156
0
                "Invalid dh group parameter p: %d not in [%d:%d]",
157
0
                blen,
158
0
                DH_PMIN,
159
0
                DH_PMAX);
160
0
        goto error;
161
0
    }
162
0
    if (bignum_cmp(modulus, one) <= 0) {
163
        /* p must be positive and preferably bigger than one */
164
0
        ssh_set_error(session, SSH_FATAL, "Invalid dh group parameter p");
165
0
        goto error;
166
0
    }
167
0
    if (!bignum_is_bit_set(modulus, 0)) {
168
        /* p must be a prime and therefore not divisible by 2 */
169
0
        ssh_set_error(session, SSH_FATAL, "Invalid dh group parameter p");
170
0
        goto error;
171
0
    }
172
0
    bignum_sub(pmin1, modulus, one);
173
0
    if (bignum_cmp(generator, one) <= 0 ||
174
0
        bignum_cmp(generator, pmin1) > 0) {
175
        /* generator must be at least 2 and smaller than p-1*/
176
0
        ssh_set_error(session, SSH_FATAL, "Invalid dh group parameter g");
177
0
        goto error;
178
0
    }
179
180
    /* all checks passed, set parameters (the BNs are copied in openssl backend) */
181
0
    rc = ssh_dh_set_parameters(session->next_crypto->dh_ctx,
182
0
                               modulus, generator);
183
0
    if (rc != SSH_OK) {
184
0
        goto error;
185
0
    }
186
0
#ifdef HAVE_LIBCRYPTO
187
0
    bignum_safe_free(modulus);
188
0
    bignum_safe_free(generator);
189
0
#endif
190
0
    modulus = NULL;
191
0
    generator = NULL;
192
193
    /* compute and send DH public parameter */
194
0
    rc = ssh_dh_keypair_gen_keys(session->next_crypto->dh_ctx,
195
0
                                 DH_CLIENT_KEYPAIR);
196
0
    if (rc == SSH_ERROR) {
197
0
        goto error;
198
0
    }
199
200
0
    rc = ssh_dh_keypair_get_keys(session->next_crypto->dh_ctx,
201
0
                                 DH_CLIENT_KEYPAIR, NULL, &pubkey);
202
0
    if (rc != SSH_OK) {
203
0
        goto error;
204
0
    }
205
206
0
    rc = ssh_buffer_pack(session->out_buffer,
207
0
                         "bB",
208
0
                         SSH2_MSG_KEX_DH_GEX_INIT,
209
0
                         pubkey);
210
0
    if (rc != SSH_OK) {
211
0
        goto error;
212
0
    }
213
#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
214
    bignum_safe_free(pubkey);
215
#endif /* OPENSSL_VERSION_NUMBER */
216
217
0
    session->dh_handshake_state = DH_STATE_INIT_SENT;
218
219
0
    rc = ssh_packet_send(session);
220
0
    if (rc == SSH_ERROR) {
221
0
        goto error;
222
0
    }
223
224
0
    bignum_safe_free(one);
225
0
    bignum_safe_free(pmin1);
226
0
    return SSH_PACKET_USED;
227
228
0
error:
229
0
    bignum_safe_free(modulus);
230
0
    bignum_safe_free(generator);
231
0
    bignum_safe_free(one);
232
0
    bignum_safe_free(pmin1);
233
#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
234
    bignum_safe_free(pubkey);
235
#endif /* OPENSSL_VERSION_NUMBER */
236
0
    ssh_dh_cleanup(session->next_crypto);
237
0
    session->session_state = SSH_SESSION_STATE_ERROR;
238
239
0
    return SSH_PACKET_USED;
240
0
}
241
242
void ssh_client_dhgex_remove_callbacks(ssh_session session)
243
0
{
244
0
    ssh_packet_remove_callbacks(session, &ssh_dhgex_client_callbacks);
245
0
}
246
247
static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply)
248
0
{
249
0
    struct ssh_crypto_struct *crypto=session->next_crypto;
250
0
    int rc;
251
0
    ssh_string pubkey_blob = NULL;
252
0
    bignum server_pubkey = NULL;
253
0
    (void)type;
254
0
    (void)user;
255
0
    SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_KEX_DH_GEX_REPLY received");
256
257
0
    ssh_client_dhgex_remove_callbacks(session);
258
0
    rc = ssh_buffer_unpack(packet,
259
0
                           "SBS",
260
0
                           &pubkey_blob, &server_pubkey,
261
0
                           &crypto->dh_server_signature);
262
0
    if (rc == SSH_ERROR) {
263
0
        ssh_set_error(session, SSH_FATAL, "Invalid DH_GEX_REPLY packet");
264
0
        goto error;
265
0
    }
266
0
    rc = ssh_dh_keypair_set_keys(crypto->dh_ctx, DH_SERVER_KEYPAIR,
267
0
                                 NULL, server_pubkey);
268
0
    if (rc != SSH_OK) {
269
0
        bignum_safe_free(server_pubkey);
270
0
        goto error;
271
0
    }
272
    /* The ownership was passed to the crypto structure */
273
0
    server_pubkey = NULL;
274
275
0
    rc = ssh_dh_import_next_pubkey_blob(session, pubkey_blob);
276
0
    SSH_STRING_FREE(pubkey_blob);
277
0
    if (rc != 0) {
278
0
        goto error;
279
0
    }
280
281
0
    rc = ssh_dh_compute_shared_secret(session->next_crypto->dh_ctx,
282
0
                                      DH_CLIENT_KEYPAIR, DH_SERVER_KEYPAIR,
283
0
                                      &session->next_crypto->shared_secret);
284
0
    ssh_dh_debug_crypto(session->next_crypto);
285
0
    if (rc == SSH_ERROR) {
286
0
        ssh_set_error(session, SSH_FATAL, "Could not generate shared secret");
287
0
        goto error;
288
0
    }
289
290
    /* Send the MSG_NEWKEYS */
291
0
    rc = ssh_packet_send_newkeys(session);
292
0
    if (rc == SSH_ERROR) {
293
0
        goto error;
294
0
    }
295
0
    session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
296
297
0
    return SSH_PACKET_USED;
298
0
error:
299
0
    SSH_STRING_FREE(pubkey_blob);
300
0
    ssh_dh_cleanup(session->next_crypto);
301
0
    session->session_state = SSH_SESSION_STATE_ERROR;
302
303
0
    return SSH_PACKET_USED;
304
0
}
305
306
#ifdef WITH_SERVER
307
308
71
#define MODULI_FILE "/etc/ssh/moduli"
309
/* 2     "Safe" prime; (p-1)/2 is also prime. */
310
0
#define SAFE_PRIME 2
311
/* 0x04  Probabilistic Miller-Rabin primality tests. */
312
0
#define PRIM_TEST_REQUIRED 0x04
313
314
/**
315
 * @internal
316
 *
317
 * @brief Determines if the proposed modulus size is more appropriate than the
318
 * current one.
319
 *
320
 * @returns 1 if it's more appropriate. Returns 0 if same or less appropriate
321
 */
322
static bool dhgroup_better_size(uint32_t pmin,
323
                                uint32_t pn,
324
                                uint32_t pmax,
325
                                size_t current_size,
326
                                size_t proposed_size)
327
0
{
328
0
    if (current_size == proposed_size) {
329
0
        return false;
330
0
    }
331
332
0
    if (current_size == pn) {
333
        /* can't do better */
334
0
        return false;
335
0
    }
336
337
0
    if (current_size == 0 && proposed_size >= pmin && proposed_size <= pmax) {
338
0
        return true;
339
0
    }
340
341
0
    if (proposed_size < pmin || proposed_size > pmax) {
342
        /* out of bounds */
343
0
        return false;
344
0
    }
345
346
0
    if (current_size == 0) {
347
        /* not in the allowed window */
348
0
        return false;
349
0
    }
350
351
0
    if (proposed_size >= pn && proposed_size < current_size) {
352
0
        return true;
353
0
    }
354
355
0
    if (proposed_size <= pn && proposed_size > current_size) {
356
0
        return true;
357
0
    }
358
359
0
    if (proposed_size >= pn && current_size < pn) {
360
0
        return true;
361
0
    }
362
363
    /* We're in the allowed window but a better match already exists. */
364
0
    return false;
365
0
}
366
367
/** @internal
368
 * @brief returns 1 with 1/n probability
369
 * @returns 1 on with P(1/n), 0 with P(n-1/n).
370
 */
371
static bool invn_chance(size_t n)
372
0
{
373
0
    size_t nounce = 0;
374
0
    int ok;
375
376
0
    ok = ssh_get_random(&nounce, sizeof(nounce), 0);
377
0
    if (!ok) {
378
0
        return false;
379
0
    }
380
0
    return (nounce % n) == 0;
381
0
}
382
383
/** @internal
384
 * @brief retrieves a DH group from an open moduli file.
385
 */
386
static int ssh_retrieve_dhgroup_file(FILE *moduli,
387
                                     uint32_t pmin,
388
                                     uint32_t pn,
389
                                     uint32_t pmax,
390
                                     size_t *best_size,
391
                                     char **best_generator,
392
                                     char **best_modulus)
393
0
{
394
0
    char timestamp[32] = {0};
395
0
    char generator[32] = {0};
396
0
    char modulus[4096] = {0};
397
0
    size_t type, tests, tries, size, proposed_size;
398
0
    int firstbyte;
399
0
    int rc;
400
0
    size_t line = 0;
401
0
    size_t best_nlines = 0;
402
403
0
    *best_size = 0;
404
0
    for(;;) {
405
0
        line++;
406
0
        firstbyte = getc(moduli);
407
0
        if (firstbyte == '#'){
408
0
            do {
409
0
                firstbyte = getc(moduli);
410
0
            } while(firstbyte != '\n' && firstbyte != EOF);
411
0
            if (firstbyte == EOF) {
412
0
                break;
413
0
            }
414
0
            continue;
415
0
        }
416
0
        if (firstbyte == EOF) {
417
0
            break;
418
0
        }
419
0
        ungetc(firstbyte, moduli);
420
0
        rc = fscanf(moduli,
421
0
                    "%31s %zu %zu %zu %zu %31s %4095s\n",
422
0
                    timestamp,
423
0
                    &type,
424
0
                    &tests,
425
0
                    &tries,
426
0
                    &size,
427
0
                    generator,
428
0
                    modulus);
429
0
        if (rc != 7){
430
0
            if (rc == EOF) {
431
0
                break;
432
0
            }
433
0
            SSH_LOG(SSH_LOG_DEBUG, "Invalid moduli entry line %zu", line);
434
0
            do {
435
0
                firstbyte = getc(moduli);
436
0
            } while(firstbyte != '\n' && firstbyte != EOF);
437
0
            if (firstbyte == EOF) {
438
0
                break;
439
0
            }
440
0
            continue;
441
0
        }
442
443
        /* we only want safe primes that were tested */
444
0
        if (type != SAFE_PRIME || !(tests & PRIM_TEST_REQUIRED)) {
445
0
            continue;
446
0
        }
447
448
0
        proposed_size = size + 1;
449
0
        if (proposed_size != *best_size &&
450
0
            dhgroup_better_size(pmin, pn, pmax, *best_size, proposed_size)) {
451
0
            best_nlines = 1;
452
0
            *best_size = proposed_size;
453
0
        } else if (proposed_size == *best_size) {
454
0
            best_nlines++;
455
0
        }
456
457
        /* Use reservoir sampling algorithm */
458
0
        if (proposed_size == *best_size && invn_chance(best_nlines)) {
459
0
            SAFE_FREE(*best_generator);
460
0
            SAFE_FREE(*best_modulus);
461
0
            *best_generator = strdup(generator);
462
0
            if (*best_generator == NULL) {
463
0
                return SSH_ERROR;
464
0
            }
465
0
            *best_modulus = strdup(modulus);
466
0
            if (*best_modulus == NULL) {
467
0
                SAFE_FREE(*best_generator);
468
0
                return SSH_ERROR;
469
0
            }
470
0
        }
471
0
    }
472
0
    if (*best_size != 0) {
473
0
        SSH_LOG(SSH_LOG_DEBUG,
474
0
                "Selected %zu bits modulus out of %zu candidates in %zu lines",
475
0
                *best_size,
476
0
                best_nlines - 1,
477
0
                line);
478
0
    } else {
479
0
        SSH_LOG(SSH_LOG_DEBUG,
480
0
                "No moduli found for [%" PRIu32 ":%" PRIu32 ":%" PRIu32 "]",
481
0
                pmin,
482
0
                pn,
483
0
                pmax);
484
0
    }
485
486
0
    return SSH_OK;
487
0
}
488
489
/** @internal
490
 * @brief retrieves a DH group from the moduli file based on bits len parameters
491
 * @param[in] pmin minimum group size in bits
492
 * @param[in] pn preferred group size
493
 * @param[in] pmax maximum group size
494
 * @param[out] size size of the chosen modulus
495
 * @param[out] p modulus
496
 * @param[out] g generator
497
 * @return SSH_OK on success, SSH_ERROR otherwise.
498
 */
499
static int ssh_retrieve_dhgroup(char *moduli_file,
500
                                uint32_t pmin,
501
                                uint32_t pn,
502
                                uint32_t pmax,
503
                                size_t *size,
504
                                bignum *p,
505
                                bignum *g)
506
71
{
507
71
    FILE *moduli = NULL;
508
71
    char *generator = NULL;
509
71
    char *modulus = NULL;
510
71
    int rc;
511
512
    /* In FIPS mode, we can not negotiate arbitrary primes,
513
     * but just the approved ones */
514
71
    if (ssh_fips_mode()) {
515
0
        SSH_LOG(SSH_LOG_TRACE, "In FIPS mode, using built-in primes");
516
0
        return ssh_fallback_group(pmax, p, g);
517
0
    }
518
519
71
    if (moduli_file != NULL)
520
0
        moduli = ssh_strict_fopen(moduli_file, SSH_MAX_CONFIG_FILE_SIZE);
521
71
    else
522
71
        moduli = ssh_strict_fopen(MODULI_FILE, SSH_MAX_CONFIG_FILE_SIZE);
523
524
71
    if (moduli == NULL) {
525
71
        SSH_LOG_STRERROR(SSH_LOG_DEBUG,
526
71
                         errno,
527
71
                         "Unable to open moduli file: %s");
528
71
        return ssh_fallback_group(pmax, p, g);
529
71
    }
530
531
0
    *size = 0;
532
0
    *p = NULL;
533
0
    *g = NULL;
534
535
0
    rc = ssh_retrieve_dhgroup_file(moduli,
536
0
                                   pmin,
537
0
                                   pn,
538
0
                                   pmax,
539
0
                                   size,
540
0
                                   &generator,
541
0
                                   &modulus);
542
0
    fclose(moduli);
543
0
    if (rc == SSH_ERROR || *size == 0) {
544
0
        goto error;
545
0
    }
546
0
    rc = bignum_hex2bn(generator, g);
547
0
    if (rc == 0) {
548
0
        goto error;
549
0
    }
550
0
    rc = bignum_hex2bn(modulus, p);
551
0
    if (rc == 0) {
552
0
        goto error;
553
0
    }
554
0
    SAFE_FREE(generator);
555
0
    SAFE_FREE(modulus);
556
557
0
    return SSH_OK;
558
559
0
error:
560
0
    bignum_safe_free(*g);
561
0
    bignum_safe_free(*p);
562
0
    SAFE_FREE(generator);
563
0
    SAFE_FREE(modulus);
564
565
0
    return SSH_ERROR;
566
0
}
567
568
static SSH_PACKET_CALLBACK(ssh_packet_server_dhgex_request);
569
static SSH_PACKET_CALLBACK(ssh_packet_server_dhgex_init);
570
571
static ssh_packet_callback dhgex_server_callbacks[] = {
572
    NULL,                           /* SSH_MSG_KEX_DH_GEX_REQUEST_OLD */
573
    NULL,                           /* SSH_MSG_KEX_DH_GEX_GROUP */
574
    ssh_packet_server_dhgex_init,   /* SSH_MSG_KEX_DH_GEX_INIT */
575
    NULL,                           /* SSH_MSG_KEX_DH_GEX_REPLY */
576
    ssh_packet_server_dhgex_request /* SSH_MSG_KEX_DH_GEX_REQUEST */
577
578
};
579
580
static struct ssh_packet_callbacks_struct ssh_dhgex_server_callbacks = {
581
    .start = SSH2_MSG_KEX_DH_GEX_REQUEST_OLD,
582
    .n_callbacks = 5,
583
    .callbacks = dhgex_server_callbacks,
584
    .user = NULL
585
};
586
587
/** @internal
588
 * @brief sets up the diffie-hellman-groupx kex callbacks
589
 */
590
180
void ssh_server_dhgex_init(ssh_session session){
591
    /* register the packet callbacks */
592
180
    ssh_packet_set_callbacks(session, &ssh_dhgex_server_callbacks);
593
180
    ssh_dh_init_common(session->next_crypto);
594
180
    session->dh_handshake_state = DH_STATE_INIT;
595
180
}
596
597
static SSH_PACKET_CALLBACK(ssh_packet_server_dhgex_request)
598
10.9k
{
599
10.9k
    bignum modulus = NULL, generator = NULL;
600
10.9k
    uint32_t pmin, pn, pmax;
601
10.9k
    size_t size = 0;
602
10.9k
    int rc;
603
604
10.9k
    (void) type;
605
10.9k
    (void) user;
606
607
10.9k
    if (session->dh_handshake_state != DH_STATE_INIT) {
608
0
        ssh_set_error(session,
609
0
                      SSH_FATAL,
610
0
                      "Received DH_GEX_REQUEST in invalid state");
611
0
        goto error;
612
0
    }
613
614
    /* Minimum group size, preferred group size, maximum group size */
615
10.9k
    rc = ssh_buffer_unpack(packet, "ddd", &pmin, &pn, &pmax);
616
10.9k
    if (rc != SSH_OK){
617
9.09k
        ssh_set_error_invalid(session);
618
9.09k
        goto error;
619
9.09k
    }
620
1.81k
    SSH_LOG(SSH_LOG_DEBUG, "dh-gex: DHGEX_REQUEST[%" PRIu32 ":%" PRIu32 ":%" PRIu32 "]", pmin, pn, pmax);
621
622
1.81k
    if (pmin > pn || pn > pmax || pn > DH_PMAX || pmax < DH_PMIN) {
623
1.74k
        ssh_set_error(session,
624
1.74k
                      SSH_FATAL,
625
1.74k
                      "Invalid dh-gex arguments [%" PRIu32 ":%" PRIu32 ":%" PRIu32 "]",
626
1.74k
                      pmin,
627
1.74k
                      pn,
628
1.74k
                      pmax);
629
1.74k
        goto error;
630
1.74k
    }
631
71
    session->next_crypto->dh_pmin = pmin;
632
71
    session->next_crypto->dh_pn = pn;
633
71
    session->next_crypto->dh_pmax = pmax;
634
635
    /* ensure safe parameters */
636
71
    if (pmin < DH_PMIN) {
637
58
        pmin = DH_PMIN;
638
58
        if (pn < pmin) {
639
28
            pn = pmin;
640
28
        }
641
58
    }
642
71
    rc = ssh_retrieve_dhgroup(session->server_opts.moduli_file,
643
71
                              pmin,
644
71
                              pn,
645
71
                              pmax,
646
71
                              &size,
647
71
                              &modulus,
648
71
                              &generator);
649
71
    if (rc == SSH_ERROR) {
650
0
        ssh_set_error(session,
651
0
                      SSH_FATAL,
652
0
                      "Couldn't find DH group for [%" PRIu32 ":%" PRIu32 ":%" PRIu32 "]",
653
0
                      pmin,
654
0
                      pn,
655
0
                      pmax);
656
0
        goto error;
657
0
    }
658
71
    rc = ssh_dh_set_parameters(session->next_crypto->dh_ctx,
659
71
                               modulus, generator);
660
71
    if (rc != SSH_OK) {
661
0
        bignum_safe_free(generator);
662
0
        bignum_safe_free(modulus);
663
0
        goto error;
664
0
    }
665
71
    rc = ssh_buffer_pack(session->out_buffer,
666
71
                         "bBB",
667
71
                         SSH2_MSG_KEX_DH_GEX_GROUP,
668
71
                         modulus,
669
71
                         generator);
670
671
71
#ifdef HAVE_LIBCRYPTO
672
71
    bignum_safe_free(generator);
673
71
    bignum_safe_free(modulus);
674
71
#endif
675
676
71
    if (rc != SSH_OK) {
677
0
        ssh_set_error_invalid(session);
678
0
        goto error;
679
0
    }
680
681
71
    session->dh_handshake_state = DH_STATE_GROUP_SENT;
682
683
71
    rc = ssh_packet_send(session);
684
71
    if (rc == SSH_ERROR) {
685
0
        goto error;
686
0
    }
687
688
10.9k
error:
689
10.9k
    return SSH_PACKET_USED;
690
71
}
691
692
/** @internal
693
 * @brief parse an incoming SSH_MSG_KEX_DH_GEX_INIT packet and complete
694
 *        Diffie-Hellman key exchange
695
 **/
696
5
static SSH_PACKET_CALLBACK(ssh_packet_server_dhgex_init){
697
5
    (void) type;
698
5
    (void) user;
699
5
    SSH_LOG(SSH_LOG_DEBUG, "Received SSH_MSG_KEX_DHGEX_INIT");
700
5
    ssh_packet_remove_callbacks(session, &ssh_dhgex_server_callbacks);
701
5
    ssh_server_dh_process_init(session, packet);
702
5
    return SSH_PACKET_USED;
703
5
}
704
705
#endif /* WITH_SERVER */