Blame src/dh-gex.c

Packit 6c0a39
/*
Packit 6c0a39
 * dh-gex.c - diffie-hellman group exchange
Packit 6c0a39
 *
Packit 6c0a39
 * This file is part of the SSH Library
Packit 6c0a39
 *
Packit 6c0a39
 * Copyright (c) 2016 by Aris Adamantiadis <aris@0xbadc0de.be>
Packit 6c0a39
 *
Packit 6c0a39
 * The SSH Library is free software; you can redistribute it and/or modify
Packit 6c0a39
 * it under the terms of the GNU Lesser General Public License as published by
Packit 6c0a39
 * the Free Software Foundation; either version 2.1 of the License, or (at your
Packit 6c0a39
 * option) any later version.
Packit 6c0a39
 *
Packit 6c0a39
 * The SSH Library is distributed in the hope that it will be useful, but
Packit 6c0a39
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
Packit 6c0a39
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
Packit 6c0a39
 * License for more details.
Packit 6c0a39
 *
Packit 6c0a39
 * You should have received a copy of the GNU Lesser General Public License
Packit 6c0a39
 * along with the SSH Library; see the file COPYING.  If not, write to
Packit 6c0a39
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
Packit 6c0a39
 * MA 02111-1307, USA.
Packit 6c0a39
 */
Packit 6c0a39
Packit 6c0a39
#include "config.h"
Packit 6c0a39
Packit 6c0a39
#include <errno.h>
Packit 6c0a39
#include <stdbool.h>
Packit 6c0a39
#include <string.h>
Packit 6c0a39
#include <stdio.h>
Packit 6c0a39
Packit 6c0a39
#include "libssh/priv.h"
Packit 6c0a39
#include "libssh/dh-gex.h"
Packit 6c0a39
#include "libssh/libssh.h"
Packit 6c0a39
#include "libssh/ssh2.h"
Packit 6c0a39
#include "libssh/callbacks.h"
Packit 6c0a39
#include "libssh/dh.h"
Packit 6c0a39
#include "libssh/buffer.h"
Packit 6c0a39
#include "libssh/session.h"
Packit 6c0a39
Packit 6c0a39
/* Minimum, recommanded and maximum size of DH group */
Packit 6c0a39
#define DH_PMIN 2048
Packit 6c0a39
#define DH_PREQ 2048
Packit 6c0a39
#define DH_PMAX 8192
Packit 6c0a39
Packit 6c0a39
static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_group);
Packit 6c0a39
static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply);
Packit 6c0a39
Packit 6c0a39
static ssh_packet_callback dhgex_client_callbacks[] = {
Packit 6c0a39
    ssh_packet_client_dhgex_group, /* SSH_MSG_KEX_DH_GEX_GROUP */
Packit 6c0a39
    NULL,                          /* SSH_MSG_KEX_DH_GEX_INIT */
Packit 6c0a39
    ssh_packet_client_dhgex_reply  /* SSH_MSG_KEX_DH_GEX_REPLY */
Packit 6c0a39
};
Packit 6c0a39
Packit 6c0a39
static struct ssh_packet_callbacks_struct ssh_dhgex_client_callbacks = {
Packit 6c0a39
    .start = SSH2_MSG_KEX_DH_GEX_GROUP,
Packit 6c0a39
    .n_callbacks = 3,
Packit 6c0a39
    .callbacks = dhgex_client_callbacks,
Packit 6c0a39
    .user = NULL
Packit 6c0a39
};
Packit 6c0a39
Packit 6c0a39
/** @internal
Packit 6c0a39
 * @brief initiates a diffie-hellman-group-exchange kex
Packit 6c0a39
 */
Packit 6c0a39
int ssh_client_dhgex_init(ssh_session session)
Packit 6c0a39
{
Packit 6c0a39
    int rc;
Packit 6c0a39
Packit 6c0a39
    rc = ssh_dh_init_common(session->next_crypto);
Packit 6c0a39
    if (rc != SSH_OK){
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    session->next_crypto->dh_pmin = DH_PMIN;
Packit 6c0a39
    session->next_crypto->dh_pn = DH_PREQ;
Packit 6c0a39
    session->next_crypto->dh_pmax = DH_PMAX;
Packit 6c0a39
    /* Minimum group size, preferred group size, maximum group size */
Packit 6c0a39
    rc = ssh_buffer_pack(session->out_buffer,
Packit 6c0a39
                         "bddd",
Packit 6c0a39
                         SSH2_MSG_KEX_DH_GEX_REQUEST,
Packit 6c0a39
                         session->next_crypto->dh_pmin,
Packit 6c0a39
                         session->next_crypto->dh_pn,
Packit 6c0a39
                         session->next_crypto->dh_pmax);
Packit 6c0a39
    if (rc != SSH_OK) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* register the packet callbacks */
Packit 6c0a39
    ssh_packet_set_callbacks(session, &ssh_dhgex_client_callbacks);
Packit 6c0a39
    session->dh_handshake_state = DH_STATE_REQUEST_SENT;
Packit 6c0a39
    rc = ssh_packet_send(session);
Packit 6c0a39
    if (rc == SSH_ERROR) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
    return rc;
Packit 6c0a39
error:
Packit 6c0a39
    ssh_dh_cleanup(session->next_crypto);
Packit 6c0a39
    return SSH_ERROR;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/** @internal
Packit 6c0a39
 *  @brief handle a DH_GEX_GROUP packet, client side. This packet contains
Packit 6c0a39
 *         the group parameters.
Packit 6c0a39
 */
Packit 6c0a39
SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_group)
Packit 6c0a39
{
Packit 6c0a39
    int rc;
Packit 6c0a39
    int blen;
Packit 6c0a39
    bignum pmin1 = NULL, one = NULL;
Packit 6c0a39
    bignum_CTX ctx = bignum_ctx_new();
Packit 6c0a39
    bignum modulus, generator;
Packit 6c0a39
    const_bignum pubkey;
Packit 6c0a39
    (void) type;
Packit 6c0a39
    (void) user;
Packit 6c0a39
Packit 6c0a39
    SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEX_DH_GEX_GROUP received");
Packit 6c0a39
Packit 6c0a39
    if (bignum_ctx_invalid(ctx)) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (session->dh_handshake_state != DH_STATE_REQUEST_SENT) {
Packit 6c0a39
        ssh_set_error(session,
Packit 6c0a39
                      SSH_FATAL,
Packit 6c0a39
                      "Received DH_GEX_GROUP in invalid state");
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
    one = bignum_new();
Packit 6c0a39
    pmin1 = bignum_new();
Packit 6c0a39
    if (one == NULL || pmin1 == NULL) {
Packit 6c0a39
        ssh_set_error_oom(session);
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
    rc = ssh_buffer_unpack(packet,
Packit 6c0a39
                           "BB",
Packit 6c0a39
                           &modulus,
Packit 6c0a39
                           &generator);
Packit 6c0a39
    if (rc != SSH_OK) {
Packit 6c0a39
        ssh_set_error(session, SSH_FATAL, "Invalid DH_GEX_GROUP packet");
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
    /* basic checks */
Packit 6c0a39
    if (ssh_fips_mode() &&
Packit 6c0a39
        !ssh_dh_is_known_group(modulus, generator)) {
Packit 6c0a39
        ssh_set_error(session,
Packit 6c0a39
                      SSH_FATAL,
Packit 6c0a39
                      "The received DH group is not FIPS approved");
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
    rc = bignum_set_word(one, 1);
Packit 6c0a39
    if (rc != 1) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
    blen = bignum_num_bits(modulus);
Packit 6c0a39
    if (blen < DH_PMIN || blen > DH_PMAX) {
Packit 6c0a39
        ssh_set_error(session,
Packit 6c0a39
                SSH_FATAL,
Packit 6c0a39
                "Invalid dh group parameter p: %d not in [%d:%d]",
Packit 6c0a39
                blen,
Packit 6c0a39
                DH_PMIN,
Packit 6c0a39
                DH_PMAX);
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
    if (bignum_cmp(modulus, one) <= 0) {
Packit 6c0a39
        /* p must be positive and preferably bigger than one */
Packit 6c0a39
        ssh_set_error(session, SSH_FATAL, "Invalid dh group parameter p");
Packit 6c0a39
    }
Packit 6c0a39
    if (!bignum_is_bit_set(modulus, 0)) {
Packit 6c0a39
        /* p must be a prime and therefore not divisible by 2 */
Packit 6c0a39
        ssh_set_error(session, SSH_FATAL, "Invalid dh group parameter p");
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
    bignum_sub(pmin1, modulus, one);
Packit 6c0a39
    if (bignum_cmp(generator, one) <= 0 ||
Packit 6c0a39
        bignum_cmp(generator, pmin1) > 0) {
Packit 6c0a39
        /* generator must be at least 2 and smaller than p-1*/
Packit 6c0a39
        ssh_set_error(session, SSH_FATAL, "Invalid dh group parameter g");
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
    bignum_ctx_free(ctx);
Packit 6c0a39
    ctx = NULL;
Packit 6c0a39
Packit 6c0a39
    /* all checks passed, set parameters */
Packit 6c0a39
    rc = ssh_dh_set_parameters(session->next_crypto->dh_ctx,
Packit 6c0a39
                               modulus, generator);
Packit 6c0a39
    if (rc != SSH_OK) {
Packit 6c0a39
        bignum_safe_free(modulus);
Packit 6c0a39
        bignum_safe_free(generator);
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* compute and send DH public parameter */
Packit 6c0a39
    rc = ssh_dh_keypair_gen_keys(session->next_crypto->dh_ctx,
Packit 6c0a39
                                 DH_CLIENT_KEYPAIR);
Packit 6c0a39
    if (rc == SSH_ERROR) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_dh_keypair_get_keys(session->next_crypto->dh_ctx,
Packit 6c0a39
                                 DH_CLIENT_KEYPAIR, NULL, &pubkey);
Packit 6c0a39
    if (rc != SSH_OK) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_buffer_pack(session->out_buffer,
Packit 6c0a39
                         "bB",
Packit 6c0a39
                         SSH2_MSG_KEX_DH_GEX_INIT,
Packit 6c0a39
                         pubkey);
Packit 6c0a39
    if (rc != SSH_OK) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    session->dh_handshake_state = DH_STATE_INIT_SENT;
Packit 6c0a39
Packit 6c0a39
    rc = ssh_packet_send(session);
Packit 6c0a39
    if (rc == SSH_ERROR) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    bignum_safe_free(one);
Packit 6c0a39
    bignum_safe_free(pmin1);
Packit 6c0a39
    return SSH_PACKET_USED;
Packit 6c0a39
Packit 6c0a39
error:
Packit 6c0a39
    bignum_safe_free(one);
Packit 6c0a39
    bignum_safe_free(pmin1);
Packit 6c0a39
    if(!bignum_ctx_invalid(ctx)) {
Packit 6c0a39
        bignum_ctx_free(ctx);
Packit 6c0a39
    }
Packit 6c0a39
    ssh_dh_cleanup(session->next_crypto);
Packit 6c0a39
    session->session_state = SSH_SESSION_STATE_ERROR;
Packit 6c0a39
Packit 6c0a39
    return SSH_PACKET_USED;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply)
Packit 6c0a39
{
Packit 6c0a39
    struct ssh_crypto_struct *crypto=session->next_crypto;
Packit 6c0a39
    int rc;
Packit 6c0a39
    ssh_string pubkey_blob = NULL;
Packit 6c0a39
    bignum server_pubkey = NULL;
Packit 6c0a39
    (void)type;
Packit 6c0a39
    (void)user;
Packit 6c0a39
    SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEX_DH_GEX_REPLY received");
Packit 6c0a39
Packit 6c0a39
    ssh_packet_remove_callbacks(session, &ssh_dhgex_client_callbacks);
Packit 6c0a39
    rc = ssh_buffer_unpack(packet,
Packit 6c0a39
                           "SBS",
Packit 6c0a39
                           &pubkey_blob, &server_pubkey,
Packit 6c0a39
                           &crypto->dh_server_signature);
Packit 6c0a39
    if (rc == SSH_ERROR) {
Packit 6c0a39
        ssh_set_error(session, SSH_FATAL, "Invalid DH_GEX_REPLY packet");
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
    rc = ssh_dh_keypair_set_keys(crypto->dh_ctx, DH_SERVER_KEYPAIR,
Packit 6c0a39
                                 NULL, server_pubkey);
Packit 6c0a39
    if (rc != SSH_OK) {
Packit 6c0a39
        bignum_safe_free(server_pubkey);
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_dh_import_next_pubkey_blob(session, pubkey_blob);
Packit 6c0a39
    ssh_string_free(pubkey_blob);
Packit 6c0a39
    if (rc != 0) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_dh_compute_shared_secret(session->next_crypto->dh_ctx,
Packit 6c0a39
                                      DH_CLIENT_KEYPAIR, DH_SERVER_KEYPAIR,
Packit 6c0a39
                                      &session->next_crypto->shared_secret);
Packit 6c0a39
    if (rc == SSH_ERROR) {
Packit 6c0a39
        ssh_set_error(session, SSH_FATAL, "Could not generate shared secret");
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* Send the MSG_NEWKEYS */
Packit 6c0a39
    if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_packet_send(session);
Packit 6c0a39
    if (rc == SSH_ERROR) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
    SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
Packit 6c0a39
    session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
Packit 6c0a39
Packit 6c0a39
    return SSH_PACKET_USED;
Packit 6c0a39
error:
Packit 6c0a39
    ssh_dh_cleanup(session->next_crypto);
Packit 6c0a39
    session->session_state = SSH_SESSION_STATE_ERROR;
Packit 6c0a39
Packit 6c0a39
    return SSH_PACKET_USED;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
#ifdef WITH_SERVER
Packit 6c0a39
Packit 6c0a39
#define MODULI_FILE "/etc/ssh/moduli"
Packit 6c0a39
/* 2     "Safe" prime; (p-1)/2 is also prime. */
Packit 6c0a39
#define SAFE_PRIME 2
Packit 6c0a39
/* 0x04  Probabilistic Miller-Rabin primality tests. */
Packit 6c0a39
#define PRIM_TEST_REQUIRED 0x04
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 *
Packit 6c0a39
 * @brief Determines if the proposed modulus size is more appropriate than the
Packit 6c0a39
 * current one.
Packit 6c0a39
 *
Packit 6c0a39
 * @returns 1 if it's more appropriate. Returns 0 if same or less appropriate
Packit 6c0a39
 */
Packit 6c0a39
static bool dhgroup_better_size(uint32_t pmin,
Packit 6c0a39
                                uint32_t pn,
Packit 6c0a39
                                uint32_t pmax,
Packit 6c0a39
                                size_t current_size,
Packit 6c0a39
                                size_t proposed_size)
Packit 6c0a39
{
Packit 6c0a39
    if (current_size == proposed_size) {
Packit 6c0a39
        return false;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (current_size == pn) {
Packit 6c0a39
        /* can't do better */
Packit 6c0a39
        return false;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (current_size == 0 && proposed_size >= pmin && proposed_size <= pmax) {
Packit 6c0a39
        return true;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (proposed_size < pmin || proposed_size > pmax) {
Packit 6c0a39
        /* out of bounds */
Packit 6c0a39
        return false;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (current_size == 0) {
Packit 6c0a39
        /* not in the allowed window */
Packit 6c0a39
        return false;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (proposed_size >= pn && proposed_size < current_size) {
Packit 6c0a39
        return true;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (proposed_size <= pn && proposed_size > current_size) {
Packit 6c0a39
        return true;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (proposed_size >= pn && current_size < pn) {
Packit 6c0a39
        return true;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* We're in the allowed window but a better match already exists. */
Packit 6c0a39
    return false;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/** @internal
Packit 6c0a39
 * @brief returns 1 with 1/n probability
Packit 6c0a39
 * @returns 1 on with P(1/n), 0 with P(n-1/n).
Packit 6c0a39
 */
Packit 6c0a39
static bool invn_chance(int n)
Packit 6c0a39
{
Packit 6c0a39
    uint32_t nounce;
Packit 6c0a39
    ssh_get_random(&nounce, sizeof(nounce), 0);
Packit 6c0a39
    return (nounce % n) == 0;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/** @internal
Packit 6c0a39
 * @brief retrieves a DH group from an open moduli file.
Packit 6c0a39
 */
Packit 6c0a39
static int ssh_retrieve_dhgroup_file(FILE *moduli,
Packit 6c0a39
                                     uint32_t pmin,
Packit 6c0a39
                                     uint32_t pn,
Packit 6c0a39
                                     uint32_t pmax,
Packit 6c0a39
                                     size_t *best_size,
Packit 6c0a39
                                     char **best_generator,
Packit 6c0a39
                                     char **best_modulus)
Packit 6c0a39
{
Packit 6c0a39
    char timestamp[32] = {0};
Packit 6c0a39
    char generator[32] = {0};
Packit 6c0a39
    char modulus[4096] = {0};
Packit 6c0a39
    size_t type, tests, tries, size, proposed_size;
Packit 6c0a39
    int firstbyte;
Packit 6c0a39
    int rc;
Packit 6c0a39
    size_t line = 0;
Packit 6c0a39
    size_t best_nlines = 0;
Packit 6c0a39
Packit 6c0a39
    for(;;) {
Packit 6c0a39
        line++;
Packit 6c0a39
        firstbyte = getc(moduli);
Packit 6c0a39
        if (firstbyte == '#'){
Packit 6c0a39
            do {
Packit 6c0a39
                firstbyte = getc(moduli);
Packit 6c0a39
            } while(firstbyte != '\n' && firstbyte != EOF);
Packit 6c0a39
            continue;
Packit 6c0a39
        }
Packit 6c0a39
        if (firstbyte == EOF) {
Packit 6c0a39
            break;
Packit 6c0a39
        }
Packit 6c0a39
        ungetc(firstbyte, moduli);
Packit 6c0a39
        rc = fscanf(moduli,
Packit 6c0a39
                    "%31s %zu %zu %zu %zu %31s %4095s\n",
Packit 6c0a39
                    timestamp,
Packit 6c0a39
                    &type,
Packit 6c0a39
                    &tests,
Packit 6c0a39
                    &tries,
Packit 6c0a39
                    &size,
Packit 6c0a39
                    generator,
Packit 6c0a39
                    modulus);
Packit 6c0a39
        if (rc != 7){
Packit 6c0a39
            if (rc == EOF) {
Packit 6c0a39
                break;
Packit 6c0a39
            }
Packit 6c0a39
            SSH_LOG(SSH_LOG_INFO, "Invalid moduli entry line %zu", line);
Packit 6c0a39
            do {
Packit 6c0a39
                firstbyte = getc(moduli);
Packit 6c0a39
            } while(firstbyte != '\n' && firstbyte != EOF);
Packit 6c0a39
            continue;
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        /* we only want safe primes that were tested */
Packit 6c0a39
        if (type != SAFE_PRIME || !(tests & PRIM_TEST_REQUIRED)) {
Packit 6c0a39
            continue;
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        proposed_size = size + 1;
Packit 6c0a39
        if (proposed_size != *best_size &&
Packit 6c0a39
            dhgroup_better_size(pmin, pn, pmax, *best_size, proposed_size)) {
Packit 6c0a39
            best_nlines = 0;
Packit 6c0a39
            *best_size = proposed_size;
Packit 6c0a39
        }
Packit 6c0a39
        if (proposed_size == *best_size) {
Packit 6c0a39
            best_nlines++;
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        /* Use reservoir sampling algorithm */
Packit 6c0a39
        if (proposed_size == *best_size && invn_chance(best_nlines)) {
Packit 6c0a39
            SAFE_FREE(*best_generator);
Packit 6c0a39
            SAFE_FREE(*best_modulus);
Packit 6c0a39
            *best_generator = strdup(generator);
Packit 6c0a39
            if (*best_generator == NULL) {
Packit 6c0a39
                return SSH_ERROR;
Packit 6c0a39
            }
Packit 6c0a39
            *best_modulus = strdup(modulus);
Packit 6c0a39
            if (*best_modulus == NULL) {
Packit 6c0a39
                SAFE_FREE(*best_generator);
Packit 6c0a39
                return SSH_ERROR;
Packit 6c0a39
            }
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
    if (*best_size != 0) {
Packit 6c0a39
        SSH_LOG(SSH_LOG_INFO,
Packit 6c0a39
                "Selected %zu bits modulus out of %zu candidates in %zu lines",
Packit 6c0a39
                *best_size,
Packit 6c0a39
                best_nlines - 1,
Packit 6c0a39
                line);
Packit 6c0a39
    } else {
Packit 6c0a39
        SSH_LOG(SSH_LOG_WARNING,
Packit 6c0a39
                "No moduli found for [%u:%u:%u]",
Packit 6c0a39
                pmin,
Packit 6c0a39
                pn,
Packit 6c0a39
                pmax);
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return SSH_OK;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/** @internal
Packit 6c0a39
 * @brief retrieves a DH group from the moduli file based on bits len parameters
Packit 6c0a39
 * @param[in] pmin minimum group size in bits
Packit 6c0a39
 * @param[in] pn preferred group size
Packit 6c0a39
 * @param[in] pmax maximum group size
Packit 6c0a39
 * @param[out] size size of the chosen modulus
Packit 6c0a39
 * @param[out] p modulus
Packit 6c0a39
 * @param[out] g generator
Packit 6c0a39
 * @return SSH_OK on success, SSH_ERROR otherwise.
Packit 6c0a39
 */
Packit 6c0a39
static int ssh_retrieve_dhgroup(uint32_t pmin,
Packit 6c0a39
                                uint32_t pn,
Packit 6c0a39
                                uint32_t pmax,
Packit 6c0a39
                                size_t *size,
Packit 6c0a39
                                bignum *p,
Packit 6c0a39
                                bignum *g)
Packit 6c0a39
{
Packit 6c0a39
    FILE *moduli = NULL;
Packit 6c0a39
    char *generator = NULL;
Packit 6c0a39
    char *modulus = NULL;
Packit 6c0a39
    int rc;
Packit 6c0a39
Packit 6c0a39
    /* In FIPS mode, we can not negotiate arbitrary primes,
Packit 6c0a39
     * but just the approved ones */
Packit 6c0a39
    if (ssh_fips_mode()) {
Packit 6c0a39
        SSH_LOG(SSH_LOG_TRACE, "In FIPS mode, using built-in primes");
Packit 6c0a39
        return ssh_fallback_group(pmax, p, g);
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    moduli = fopen(MODULI_FILE, "r");
Packit 6c0a39
    if (moduli == NULL) {
Packit 6c0a39
        SSH_LOG(SSH_LOG_WARNING,
Packit 6c0a39
                "Unable to open moduli file: %s",
Packit 6c0a39
                strerror(errno));
Packit 6c0a39
        return ssh_fallback_group(pmax, p, g);
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    *size = 0;
Packit 6c0a39
    *p = NULL;
Packit 6c0a39
    *g = NULL;
Packit 6c0a39
Packit 6c0a39
    rc = ssh_retrieve_dhgroup_file(moduli,
Packit 6c0a39
                                   pmin,
Packit 6c0a39
                                   pn,
Packit 6c0a39
                                   pmax,
Packit 6c0a39
                                   size,
Packit 6c0a39
                                   &generator,
Packit 6c0a39
                                   &modulus);
Packit 6c0a39
    fclose(moduli);
Packit 6c0a39
    if (rc == SSH_ERROR || *size == 0) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
    rc = bignum_hex2bn(generator, g);
Packit 6c0a39
    if (rc == 0) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
    rc = bignum_hex2bn(modulus, p);
Packit 6c0a39
    if (rc == 0) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
    SAFE_FREE(generator);
Packit 6c0a39
    SAFE_FREE(modulus);
Packit 6c0a39
Packit 6c0a39
    return SSH_OK;
Packit 6c0a39
Packit 6c0a39
error:
Packit 6c0a39
    bignum_safe_free(*g);
Packit 6c0a39
    bignum_safe_free(*p);
Packit 6c0a39
    SAFE_FREE(generator);
Packit 6c0a39
    SAFE_FREE(modulus);
Packit 6c0a39
Packit 6c0a39
    return SSH_ERROR;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
static SSH_PACKET_CALLBACK(ssh_packet_server_dhgex_request);
Packit 6c0a39
static SSH_PACKET_CALLBACK(ssh_packet_server_dhgex_init);
Packit 6c0a39
Packit 6c0a39
static ssh_packet_callback dhgex_server_callbacks[]= {
Packit 6c0a39
    NULL, /* SSH_MSG_KEX_DH_GEX_REQUEST_OLD */
Packit 6c0a39
    NULL, /* SSH_MSG_KEX_DH_GEX_GROUP */
Packit 6c0a39
    ssh_packet_server_dhgex_init,   /* SSH_MSG_KEX_DH_GEX_INIT */
Packit 6c0a39
    NULL,                           /* SSH_MSG_KEX_DH_GEX_REPLY */
Packit 6c0a39
    ssh_packet_server_dhgex_request /* SSH_MSG_GEX_DH_GEX_REQUEST */
Packit 6c0a39
Packit 6c0a39
};
Packit 6c0a39
Packit 6c0a39
static struct ssh_packet_callbacks_struct ssh_dhgex_server_callbacks = {
Packit 6c0a39
    .start = SSH2_MSG_KEX_DH_GEX_REQUEST_OLD,
Packit 6c0a39
    .n_callbacks = 5,
Packit 6c0a39
    .callbacks = dhgex_server_callbacks,
Packit 6c0a39
    .user = NULL
Packit 6c0a39
};
Packit 6c0a39
Packit 6c0a39
/** @internal
Packit 6c0a39
 * @brief sets up the diffie-hellman-groupx kex callbacks
Packit 6c0a39
 */
Packit 6c0a39
void ssh_server_dhgex_init(ssh_session session){
Packit 6c0a39
    /* register the packet callbacks */
Packit 6c0a39
    ssh_packet_set_callbacks(session, &ssh_dhgex_server_callbacks);
Packit 6c0a39
    ssh_dh_init_common(session->next_crypto);
Packit 6c0a39
    session->dh_handshake_state = DH_STATE_INIT;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
static SSH_PACKET_CALLBACK(ssh_packet_server_dhgex_request)
Packit 6c0a39
{
Packit 6c0a39
    bignum modulus = NULL, generator = NULL;
Packit 6c0a39
    uint32_t pmin, pn, pmax;
Packit 6c0a39
    size_t size = 0;
Packit 6c0a39
    int rc;
Packit 6c0a39
Packit 6c0a39
    (void) type;
Packit 6c0a39
    (void) user;
Packit 6c0a39
Packit 6c0a39
    if (session->dh_handshake_state != DH_STATE_INIT) {
Packit 6c0a39
        ssh_set_error(session,
Packit 6c0a39
                      SSH_FATAL,
Packit 6c0a39
                      "Received DH_GEX_REQUEST in invalid state");
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* Minimum group size, preferred group size, maximum group size */
Packit 6c0a39
    rc = ssh_buffer_unpack(packet, "ddd", &pmin, &pn, &pmax);
Packit 6c0a39
    if (rc != SSH_OK){
Packit 6c0a39
        ssh_set_error_invalid(session);
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
    SSH_LOG(SSH_LOG_INFO, "dh-gex: DHGEX_REQUEST[%u:%u:%u]", pmin, pn, pmax);
Packit 6c0a39
Packit 6c0a39
    if (pmin > pn || pn > pmax || pn > DH_PMAX || pmax < DH_PMIN) {
Packit 6c0a39
        ssh_set_error(session,
Packit 6c0a39
                      SSH_FATAL,
Packit 6c0a39
                      "Invalid dh-gex arguments [%u:%u:%u]",
Packit 6c0a39
                      pmin,
Packit 6c0a39
                      pn,
Packit 6c0a39
                      pmax);
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
    session->next_crypto->dh_pmin = pmin;
Packit 6c0a39
    session->next_crypto->dh_pn = pn;
Packit 6c0a39
    session->next_crypto->dh_pmax = pmax;
Packit 6c0a39
Packit 6c0a39
    /* ensure safe parameters */
Packit 6c0a39
    if (pmin < DH_PMIN) {
Packit 6c0a39
        pmin = DH_PMIN;
Packit 6c0a39
        if (pn < pmin) {
Packit 6c0a39
            pn = pmin;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
    rc = ssh_retrieve_dhgroup(pmin,
Packit 6c0a39
                              pn,
Packit 6c0a39
                              pmax,
Packit 6c0a39
                              &size,
Packit 6c0a39
                              &modulus,
Packit 6c0a39
                              &generator);
Packit 6c0a39
    if (rc == SSH_ERROR) {
Packit 6c0a39
        ssh_set_error(session,
Packit 6c0a39
                      SSH_FATAL,
Packit 6c0a39
                      "Couldn't find DH group for [%u:%u:%u]",
Packit 6c0a39
                      pmin,
Packit 6c0a39
                      pn,
Packit 6c0a39
                      pmax);
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
    rc = ssh_dh_set_parameters(session->next_crypto->dh_ctx,
Packit 6c0a39
                               modulus, generator);
Packit 6c0a39
    if (rc != SSH_OK) {
Packit 6c0a39
        bignum_safe_free(generator);
Packit 6c0a39
        bignum_safe_free(modulus);
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
    rc = ssh_buffer_pack(session->out_buffer,
Packit 6c0a39
                         "bBB",
Packit 6c0a39
                         SSH2_MSG_KEX_DH_GEX_GROUP,
Packit 6c0a39
                         modulus,
Packit 6c0a39
                         generator);
Packit 6c0a39
Packit 6c0a39
#ifdef HAVE_LIBCRYPTO
Packit 6c0a39
        bignum_safe_free(generator);
Packit 6c0a39
        bignum_safe_free(modulus);
Packit 6c0a39
#endif
Packit 6c0a39
Packit 6c0a39
    if (rc != SSH_OK) {
Packit 6c0a39
        ssh_set_error_invalid(session);
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    session->dh_handshake_state = DH_STATE_GROUP_SENT;
Packit 6c0a39
Packit 6c0a39
    rc = ssh_packet_send(session);
Packit 6c0a39
    if (rc == SSH_ERROR) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
error:
Packit 6c0a39
    return SSH_PACKET_USED;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/** @internal
Packit 6c0a39
 * @brief parse an incoming SSH_MSG_KEX_DH_GEX_INIT packet and complete
Packit 6c0a39
 *        Diffie-Hellman key exchange
Packit 6c0a39
 **/
Packit 6c0a39
static SSH_PACKET_CALLBACK(ssh_packet_server_dhgex_init){
Packit 6c0a39
    (void) type;
Packit 6c0a39
    (void) user;
Packit 6c0a39
    SSH_LOG(SSH_LOG_DEBUG, "Received SSH_MSG_KEX_DHGEX_INIT");
Packit 6c0a39
    ssh_packet_remove_callbacks(session, &ssh_dhgex_server_callbacks);
Packit 6c0a39
    ssh_server_dh_process_init(session, packet);
Packit 6c0a39
    return SSH_PACKET_USED;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
#endif /* WITH_SERVER */