1
1

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>
Этот коммит содержится в:
Aris Adamantiadis 2016-06-02 11:51:02 +02:00 коммит произвёл Andreas Schneider
родитель 31da8025b2
Коммит b36219369d

Просмотреть файл

@ -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 */