aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAris Adamantiadis <aris@0xbadc0de.be>2016-06-02 11:51:02 +0200
committerAris Adamantiadis <aris@0xbadc0de.be>2017-06-08 10:45:54 +0200
commitef8bc65388506eb1a6eb62d78ffab05984b9ebfa (patch)
treea5f1fdc2f0b3f671ffc0a963585e223f046408ba
parent1fcf3768035b28a74914cff65b3af0e0f53186fb (diff)
downloadlibssh-ef8bc65388506eb1a6eb62d78ffab05984b9ebfa.tar.gz
libssh-ef8bc65388506eb1a6eb62d78ffab05984b9ebfa.tar.xz
libssh-ef8bc65388506eb1a6eb62d78ffab05984b9ebfa.zip
dh-gex: moduli file parsing
-rw-r--r--include/libssh/libcrypto.h3
-rw-r--r--src/dh-gex.c178
2 files changed, 180 insertions, 1 deletions
diff --git a/include/libssh/libcrypto.h b/include/libssh/libcrypto.h
index 407ae708..0d803c67 100644
--- a/include/libssh/libcrypto.h
+++ b/include/libssh/libcrypto.h
@@ -80,7 +80,8 @@ typedef BN_CTX* bignum_CTX;
BN_bin2bn(data,datalen,(*dest)); \
} while(0)
#define bignum_bn2dec(num) BN_bn2dec(num)
-#define bignum_dec2bn(bn,data) BN_dec2bn(data,bn)
+#define bignum_dec2bn(data, bn) BN_dec2bn(bn, data)
+#define bignum_hex2bn(data, bn) BN_hex2bn(bn, data)
#define bignum_bn2hex(num, dest) (*dest)=(unsigned char *)BN_bn2hex(num)
#define bignum_rand_range(rnd, max) BN_rand_range(rnd, max)
#define bignum_ctx_new() BN_CTX_new()
diff --git a/src/dh-gex.c b/src/dh-gex.c
index 40887c7c..35d7ec97 100644
--- a/src/dh-gex.c
+++ b/src/dh-gex.c
@@ -21,6 +21,9 @@
* MA 02111-1307, USA.
*/
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
#include "libssh/dh-gex.h"
#include "libssh/libssh.h"
#include "libssh/ssh2.h"
@@ -201,3 +204,178 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply) {
session->session_state=SSH_SESSION_STATE_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 int dhgroup_better_size(int pmin, int pn, int pmax,
+ int current_size, int proposed_size){
+ if (current_size == proposed_size)
+ return 0;
+ if (current_size == pn){
+ /* can't do better */
+ return 0;
+ }
+ if (current_size == 0 && proposed_size >= pmin && proposed_size <= pmax)
+ return 1;
+ if (proposed_size < pmin || proposed_size > pmax){
+ /* out of bounds */
+ return 0;
+ }
+ if (current_size == 0){
+ /* not in the allowed window */
+ return 0;
+ }
+ if (proposed_size >= pn && proposed_size < current_size)
+ return 1;
+ if (proposed_size <= pn && proposed_size > current_size)
+ return 1;
+ if (proposed_size >= pn && current_size < pn)
+ return 1;
+ /* we're in the allowed window but a better match already exists */
+ return 0;
+}
+
+/** @internal
+ * @brief returns 1 with 1/n probability
+ * @returns 1 on with P(1/n), 0 with P(n-1/n).
+ */
+static int 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 void ssh_retrieve_dhgroup_file(FILE *moduli, int pmin, int pn, int pmax,
+ int *best_size, char **best_generator, char **best_modulus) {
+ char *timestamp = NULL, *generator = NULL, *modulus = NULL;
+ int type, tests, tries, size, proposed_size;
+ int firstbyte, rc;
+ int line=0;
+ int best_nlines = 0;
+
+ while (1){
+ 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, "%ms %d %d %d %d %ms %ms\n", &timestamp, &type, &tests,
+ &tries, &size, &generator, &modulus);
+ if (rc != 7){
+ SAFE_FREE(timestamp);
+ SAFE_FREE(generator);
+ SAFE_FREE(modulus);
+ if (rc == EOF){
+ break;
+ }
+ SSH_LOG(SSH_LOG_INFO, "invalid moduli entry line %d", 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)){
+ goto next;
+ }
+
+ 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 = generator;
+ *best_modulus = modulus;
+ generator = NULL;
+ modulus = NULL;
+ }
+ next:
+ SAFE_FREE(timestamp);
+ SAFE_FREE(generator);
+ SAFE_FREE(modulus);
+ }
+ if (*best_size != 0){
+ SSH_LOG(SSH_LOG_INFO, "Selected a %d bits modulus out of %d candidates in %d lines",
+ *best_size, best_nlines-1, line);
+ } else {
+ SSH_LOG(SSH_LOG_WARNING, "No moduli found for [%d:%d:%d]", pmin,pn,pmax);
+ }
+}
+
+/** @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.
+ */
+static int ssh_retrieve_dhgroup(int pmin, int pn, int pmax, int *size, bignum *p, bignum *g){
+ FILE *moduli;
+ 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;
+ ssh_retrieve_dhgroup_file(moduli, pmin, pn, pmax, size, &generator, &modulus);
+ if (*size == 0){
+ return SSH_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 */