Fix taint issue in transport with DSN. Bug 2491
[exim.git] / src / util / gen_pkcs3.c
index ae7e7610aa5e899c6cf088329899108341cad40d..6a467e07a99e950b345c5476fdc6ffac49cf6845 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2012 Phil Pennock.
+/* Copyright (C) 2012,2016 Phil Pennock.
  * This is distributed as part of Exim and licensed under the GPL.
  * See the file "NOTICE" for more details.
  */
@@ -7,6 +7,16 @@
  * c99 $(pkg-config --cflags openssl) gen_pkcs3.c $(pkg-config --libs openssl)
  */
 
+/*
+ * Rationale:
+ * The Diffie-Hellman primes which are embedded into Exim as named primes for
+ * the tls_dhparam option are in the std-crypto.c file.  The source for those
+ * comes from various RFCs, where they are given in hexadecimal form.
+ *
+ * This tool provides convenient conversion, to reduce the risk of human
+ * error in transcription.
+ */
+
 #include <ctype.h>
 #include <errno.h>
 #include <stdbool.h>
@@ -76,7 +86,7 @@ bn_from_text(const char *text)
   rc = BN_hex2bn(&b, spaceless);
 
   if (rc != p - spaceless)
-    die("BN_hex2bn did not convert entire input; took %d of %z bytes",
+    die("BN_hex2bn did not convert entire input; took %d of %zu bytes",
         rc, p - spaceless);
 
   return b;
@@ -124,7 +134,7 @@ emit_c_format_dh(FILE *stream, DH *dh)
       break;
     }
     *nl = '\0';
-    fprintf(stream, "\"%s\\n\"\n", p);
+    fprintf(stream, "\"%s\\n\"%s\n", p, (nl == end - 1 ? ";" : ""));
     p = nl + 1;
   }
 }
@@ -133,9 +143,11 @@ emit_c_format_dh(FILE *stream, DH *dh)
 void __attribute__((__noreturn__))
 usage(FILE *stream, int exitcode)
 {
-  fprintf(stream, "Usage: %s [-CPcst] <dh_p> <dh_g>\n"
+  fprintf(stream, "Usage: %s [-CPcst] <dh_p> <dh_g> [<dh_q>]\n"
 "Both dh_p and dh_g should be hex strings representing the numbers\n"
+"The same applies to the optional dh_q (prime-order subgroup).\n"
 "They may contain whitespace.\n"
+"Older values, dh_g is often just '2', not a long string.\n"
 "\n"
 " -C      show C string form of PEM result\n"
 " -P      do not show PEM\n"
@@ -151,7 +163,7 @@ usage(FILE *stream, int exitcode)
 int
 main(int argc, char *argv[])
 {
-  BIGNUM *p, *g;
+  BIGNUM *p, *g, *q;
   DH *dh;
   int ch;
   bool perform_dh_check = false;
@@ -159,6 +171,7 @@ main(int argc, char *argv[])
   bool show_numbers = false;
   bool show_pem = true;
   bool show_text = false;
+  bool given_q = false;
 
   while ((ch = getopt(argc, argv, "CPcsth")) != -1) {
     switch (ch) {
@@ -191,25 +204,49 @@ main(int argc, char *argv[])
   argc -= optind;
   argv += optind;
 
-  if (argc != 3) {
+  if ((argc < 3) || (argc > 4)) {
     fprintf(stderr, "argc: %d\n", argc);
     usage(stderr, 1);
   }
 
+  // If we use DH_set0_pqg instead of setting dh fields directly; the q value
+  // is optional and may be NULL.
+  // Just blank them all.
+  p = g = q = NULL;
+
   p = bn_from_text(argv[1]);
   g = bn_from_text(argv[2]);
+  if (argc >= 4) {
+    q = bn_from_text(argv[3]);
+    given_q = true;
+  }
 
   if (show_numbers) {
     printf("p = ");
     BN_print_fp(stdout, p);
     printf("\ng = ");
     BN_print_fp(stdout, g);
+    if (given_q) {
+      printf("\nq = ");
+      BN_print_fp(stdout, q);
+    }
     printf("\n");
   }
 
   dh = DH_new();
+  // The documented method for setting q appeared in OpenSSL 1.1.0.
+#if OPENSSL_VERSION_NUMBER >= 0x1010000f
+  // NULL okay for q; yes, the optional value is in the middle.
+  if (DH_set0_pqg(dh, p, q, g) != 1) {
+    die_openssl_err("initialising DH pqg values failed");
+  }
+#else
   dh->p = p;
   dh->g = g;
+  if (given_q) {
+    dh->q = q;
+  }
+#endif
 
   if (perform_dh_check)
     our_dh_check(dh);
@@ -224,6 +261,6 @@ main(int argc, char *argv[])
       PEM_write_DHparams(stdout, dh);
   }
 
-  DH_free(dh); /* should free p & g too */
+  DH_free(dh); /* should free p,g (& q if non-NULL) too */
   return 0;
 }