aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAris Adamantiadis <aris@0xbadc0de.be>2016-06-02 11:51:02 +0200
committerAndreas Schneider <asn@cryptomilk.org>2019-01-24 12:51:54 +0100
commitb36219369d3ed563470c620a5ea959fdd34cfd59 (patch)
treec4fac22a8e78e90eab9ea8355cc147e6c807da65 /src
parent31da8025b2a138be823194866581741c568f44fe (diff)
downloadlibssh-b36219369d3ed563470c620a5ea959fdd34cfd59.tar.gz
libssh-b36219369d3ed563470c620a5ea959fdd34cfd59.tar.xz
libssh-b36219369d3ed563470c620a5ea959fdd34cfd59.zip
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>
Diffstat (limited to 'src')
-rw-r--r--src/dh-gex.c252
1 files changed, 252 insertions, 0 deletions
diff --git a/src/dh-gex.c b/src/dh-gex.c
index 407c2391..508b6363 100644
--- a/src/dh-gex.c
+++ b/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 */