dh-gex: Add support for moduli file parsing
Signed-off-by: Aris Adamantiadis <aris@0xbadc0de.be> Reviewed-by: Jakub Jelen <jjelen@redhat.com> Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Этот коммит содержится в:
родитель
31da8025b2
Коммит
b36219369d
252
src/dh-gex.c
252
src/dh-gex.c
@ -23,6 +23,11 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/dh-gex.h"
|
||||
#include "libssh/libssh.h"
|
||||
@ -252,3 +257,250 @@ error:
|
||||
|
||||
return SSH_PACKET_USED;
|
||||
}
|
||||
|
||||
#ifdef WITH_SERVER
|
||||
|
||||
#define MODULI_FILE "/etc/ssh/moduli"
|
||||
/* 2 "Safe" prime; (p-1)/2 is also prime. */
|
||||
#define SAFE_PRIME 2
|
||||
/* 0x04 Probabilistic Miller-Rabin primality tests. */
|
||||
#define PRIM_TEST_REQUIRED 0x04
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Determines if the proposed modulus size is more appropriate than the
|
||||
* current one.
|
||||
*
|
||||
* @returns 1 if it's more appropriate. Returns 0 if same or less appropriate
|
||||
*/
|
||||
static bool dhgroup_better_size(uint32_t pmin,
|
||||
uint32_t pn,
|
||||
uint32_t pmax,
|
||||
size_t current_size,
|
||||
size_t proposed_size)
|
||||
{
|
||||
if (current_size == proposed_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current_size == pn) {
|
||||
/* can't do better */
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current_size == 0 && proposed_size >= pmin && proposed_size <= pmax) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (proposed_size < pmin || proposed_size > pmax) {
|
||||
/* out of bounds */
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current_size == 0) {
|
||||
/* not in the allowed window */
|
||||
return false;
|
||||
}
|
||||
|
||||
if (proposed_size >= pn && proposed_size < current_size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (proposed_size <= pn && proposed_size > current_size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (proposed_size >= pn && current_size < pn) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* We're in the allowed window but a better match already exists. */
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief returns 1 with 1/n probability
|
||||
* @returns 1 on with P(1/n), 0 with P(n-1/n).
|
||||
*/
|
||||
static bool invn_chance(int n)
|
||||
{
|
||||
uint32_t nounce;
|
||||
ssh_get_random(&nounce, sizeof(nounce), 0);
|
||||
return (nounce % n) == 0;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief retrieves a DH group from an open moduli file.
|
||||
*/
|
||||
static int ssh_retrieve_dhgroup_file(FILE *moduli,
|
||||
uint32_t pmin,
|
||||
uint32_t pn,
|
||||
uint32_t pmax,
|
||||
size_t *best_size,
|
||||
char **best_generator,
|
||||
char **best_modulus)
|
||||
{
|
||||
char timestamp[32] = {0};
|
||||
char generator[32] = {0};
|
||||
char modulus[4096] = {0};
|
||||
size_t type, tests, tries, size, proposed_size;
|
||||
int firstbyte;
|
||||
int rc;
|
||||
size_t line = 0;
|
||||
size_t best_nlines = 0;
|
||||
|
||||
for(;;) {
|
||||
line++;
|
||||
firstbyte = getc(moduli);
|
||||
if (firstbyte == '#'){
|
||||
do {
|
||||
firstbyte = getc(moduli);
|
||||
} while(firstbyte != '\n' && firstbyte != EOF);
|
||||
continue;
|
||||
}
|
||||
if (firstbyte == EOF) {
|
||||
break;
|
||||
}
|
||||
ungetc(firstbyte, moduli);
|
||||
rc = fscanf(moduli,
|
||||
"%31s %zu %zu %zu %zu %31s %4095s\n",
|
||||
timestamp,
|
||||
&type,
|
||||
&tests,
|
||||
&tries,
|
||||
&size,
|
||||
generator,
|
||||
modulus);
|
||||
if (rc != 7){
|
||||
if (rc == EOF) {
|
||||
break;
|
||||
}
|
||||
SSH_LOG(SSH_LOG_INFO, "Invalid moduli entry line %zu", line);
|
||||
do {
|
||||
firstbyte = getc(moduli);
|
||||
} while(firstbyte != '\n' && firstbyte != EOF);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* we only want safe primes that were tested */
|
||||
if (type != SAFE_PRIME || !(tests & PRIM_TEST_REQUIRED)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
proposed_size = size + 1;
|
||||
if (proposed_size != *best_size &&
|
||||
dhgroup_better_size(pmin, pn, pmax, *best_size, proposed_size)) {
|
||||
best_nlines = 0;
|
||||
*best_size = proposed_size;
|
||||
}
|
||||
if (proposed_size == *best_size) {
|
||||
best_nlines++;
|
||||
}
|
||||
|
||||
/* Use reservoir sampling algorithm */
|
||||
if (proposed_size == *best_size && invn_chance(best_nlines)) {
|
||||
SAFE_FREE(*best_generator);
|
||||
SAFE_FREE(*best_modulus);
|
||||
*best_generator = strdup(generator);
|
||||
if (*best_generator == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
*best_modulus = strdup(modulus);
|
||||
if (*best_modulus == NULL) {
|
||||
SAFE_FREE(*best_generator);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (*best_size != 0) {
|
||||
SSH_LOG(SSH_LOG_INFO,
|
||||
"Selected %zu bits modulus out of %zu candidates in %zu lines",
|
||||
*best_size,
|
||||
best_nlines - 1,
|
||||
line);
|
||||
} else {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"No moduli found for [%u:%u:%u]",
|
||||
pmin,
|
||||
pn,
|
||||
pmax);
|
||||
}
|
||||
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief retrieves a DH group from the moduli file based on bits len parameters
|
||||
* @param[in] pmin minimum group size in bits
|
||||
* @param[in] pn preferred group size
|
||||
* @param[in] pmax maximum group size
|
||||
* @param[out] size size of the chosen modulus
|
||||
* @param[out] p modulus
|
||||
* @param[out] g generator
|
||||
* @return SSH_OK on success, SSH_ERROR otherwise.
|
||||
*/
|
||||
/* TODO Make this function static when only used in this file */
|
||||
int ssh_retrieve_dhgroup(uint32_t pmin,
|
||||
uint32_t pn,
|
||||
uint32_t pmax,
|
||||
size_t *size,
|
||||
bignum *p,
|
||||
bignum *g);
|
||||
int ssh_retrieve_dhgroup(uint32_t pmin,
|
||||
uint32_t pn,
|
||||
uint32_t pmax,
|
||||
size_t *size,
|
||||
bignum *p,
|
||||
bignum *g)
|
||||
{
|
||||
FILE *moduli = NULL;
|
||||
char *generator = NULL;
|
||||
char *modulus = NULL;
|
||||
int rc;
|
||||
|
||||
moduli = fopen(MODULI_FILE, "r");
|
||||
if (moduli == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Unable to open moduli file: %s",
|
||||
strerror(errno));
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
*size = 0;
|
||||
*p = NULL;
|
||||
*g = NULL;
|
||||
|
||||
rc = ssh_retrieve_dhgroup_file(moduli,
|
||||
pmin,
|
||||
pn,
|
||||
pmax,
|
||||
size,
|
||||
&generator,
|
||||
&modulus);
|
||||
if (rc == SSH_ERROR || *size == 0) {
|
||||
goto error;
|
||||
}
|
||||
rc = bignum_hex2bn(generator, g);
|
||||
if (rc == 0) {
|
||||
goto error;
|
||||
}
|
||||
rc = bignum_hex2bn(modulus, p);
|
||||
if (rc == 0) {
|
||||
goto error;
|
||||
}
|
||||
SAFE_FREE(generator);
|
||||
SAFE_FREE(modulus);
|
||||
|
||||
return SSH_OK;
|
||||
|
||||
error:
|
||||
bignum_safe_free(*g);
|
||||
bignum_safe_free(*p);
|
||||
SAFE_FREE(generator);
|
||||
SAFE_FREE(modulus);
|
||||
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
#endif /* WITH_SERVER */
|
||||
|
Загрузка…
x
Ссылка в новой задаче
Block a user