+/**************************************************
+* General library initalisation *
+**************************************************/
+
+static BOOL
+lib_rand_init(void * addr)
+{
+randstuff r;
+if (!RAND_status()) return TRUE;
+
+gettimeofday(&r.tv, NULL);
+r.p = getpid();
+RAND_seed(US (&r), sizeof(r));
+RAND_seed(US big_buffer, big_buffer_size);
+if (addr) RAND_seed(US addr, sizeof(addr));
+
+return RAND_status();
+}
+
+
+static void
+tls_openssl_init(void)
+{
+static BOOL once = FALSE;
+if (once) return;
+once = TRUE;
+
+#ifdef EXIM_NEED_OPENSSL_INIT
+SSL_load_error_strings(); /* basic set up */
+OpenSSL_add_ssl_algorithms();
+#endif
+
+#if defined(EXIM_HAVE_SHA256) && !defined(OPENSSL_AUTO_SHA256)
+/* SHA256 is becoming ever more popular. This makes sure it gets added to the
+list of available digests. */
+EVP_add_digest(EVP_sha256());
+#endif
+
+(void) lib_rand_init(NULL);
+(void) tls_openssl_options_parse(openssl_options, &init_options);
+}
+
+
+
+/*************************************************
+* Initialize for DH *
+*************************************************/
+
+/* If dhparam is set, expand it, and load up the parameters for DH encryption.
+
+Arguments:
+ sctx The current SSL CTX (inbound or outbound)
+ dhparam DH parameter file or fixed parameter identity string
+ host connected host, if client; NULL if server
+ errstr error string pointer
+
+Returns: TRUE if OK (nothing to set up, or setup worked)
+*/
+
+static BOOL
+init_dh(SSL_CTX *sctx, uschar *dhparam, const host_item *host, uschar ** errstr)
+{
+BIO *bio;
+DH *dh;
+uschar *dhexpanded;
+const char *pem;
+int dh_bitsize;
+
+if (!expand_check(dhparam, US"tls_dhparam", &dhexpanded, errstr))
+ return FALSE;
+
+if (!dhexpanded || !*dhexpanded)
+ bio = BIO_new_mem_buf(CS std_dh_prime_default(), -1);
+else if (dhexpanded[0] == '/')
+ {
+ if (!(bio = BIO_new_file(CS dhexpanded, "r")))
+ {
+ tls_error(string_sprintf("could not read dhparams file %s", dhexpanded),
+ host, US strerror(errno), errstr);
+ return FALSE;
+ }
+ }
+else
+ {
+ if (Ustrcmp(dhexpanded, "none") == 0)
+ {
+ DEBUG(D_tls) debug_printf("Requested no DH parameters.\n");
+ return TRUE;
+ }
+
+ if (!(pem = std_dh_prime_named(dhexpanded)))
+ {
+ tls_error(string_sprintf("Unknown standard DH prime \"%s\"", dhexpanded),
+ host, US strerror(errno), errstr);
+ return FALSE;
+ }
+ bio = BIO_new_mem_buf(CS pem, -1);
+ }
+
+if (!(dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL)))
+ {
+ BIO_free(bio);
+ tls_error(string_sprintf("Could not read tls_dhparams \"%s\"", dhexpanded),
+ host, NULL, errstr);
+ return FALSE;
+ }
+
+/* note: our default limit of 2236 is not a multiple of 8; the limit comes from
+ * an NSS limit, and the GnuTLS APIs handle bit-sizes fine, so we went with
+ * 2236. But older OpenSSL can only report in bytes (octets), not bits.
+ * If someone wants to dance at the edge, then they can raise the limit or use
+ * current libraries. */
+#ifdef EXIM_HAVE_OPENSSL_DH_BITS
+/* Added in commit 26c79d5641d; `git describe --contains` says OpenSSL_1_1_0-pre1~1022
+ * This predates OpenSSL_1_1_0 (before a, b, ...) so is in all 1.1.0 */
+dh_bitsize = DH_bits(dh);
+#else
+dh_bitsize = 8 * DH_size(dh);
+#endif
+
+/* Even if it is larger, we silently return success rather than cause things
+ * to fail out, so that a too-large DH will not knock out all TLS; it's a
+ * debatable choice. */
+if (dh_bitsize > tls_dh_max_bits)
+ {
+ DEBUG(D_tls)
+ debug_printf("dhparams file %d bits, is > tls_dh_max_bits limit of %d\n",
+ dh_bitsize, tls_dh_max_bits);
+ }
+else
+ {
+ SSL_CTX_set_tmp_dh(sctx, dh);
+ DEBUG(D_tls)
+ debug_printf("Diffie-Hellman initialized from %s with %d-bit prime\n",
+ dhexpanded ? dhexpanded : US"default", dh_bitsize);
+ }
+
+DH_free(dh);
+BIO_free(bio);
+
+return TRUE;
+}
+
+
+
+
+/*************************************************
+* Initialize for ECDH *
+*************************************************/
+
+/* Load parameters for ECDH encryption.
+
+For now, we stick to NIST P-256 because: it's simple and easy to configure;
+it avoids any patent issues that might bite redistributors; despite events in
+the news and concerns over curve choices, we're not cryptographers, we're not
+pretending to be, and this is "good enough" to be better than no support,
+protecting against most adversaries. Given another year or two, there might
+be sufficient clarity about a "right" way forward to let us make an informed
+decision, instead of a knee-jerk reaction.
+
+Longer-term, we should look at supporting both various named curves and
+external files generated with "openssl ecparam", much as we do for init_dh().
+We should also support "none" as a value, to explicitly avoid initialisation.
+
+Patches welcome.
+
+Arguments:
+ sctx The current SSL CTX (inbound or outbound)
+ host connected host, if client; NULL if server
+ errstr error string pointer
+
+Returns: TRUE if OK (nothing to set up, or setup worked)
+*/
+
+static BOOL
+init_ecdh(SSL_CTX * sctx, host_item * host, uschar ** errstr)
+{
+#ifdef OPENSSL_NO_ECDH
+return TRUE;
+#else
+
+EC_KEY * ecdh;
+uschar * exp_curve;
+int nid;
+BOOL rv;
+
+if (host) /* No ECDH setup for clients, only for servers */
+ return TRUE;
+
+# ifndef EXIM_HAVE_ECDH
+DEBUG(D_tls)
+ debug_printf("No OpenSSL API to define ECDH parameters, skipping\n");
+return TRUE;
+# else
+
+if (!expand_check(tls_eccurve, US"tls_eccurve", &exp_curve, errstr))
+ return FALSE;
+if (!exp_curve || !*exp_curve)
+ return TRUE;
+
+/* "auto" needs to be handled carefully.
+ * OpenSSL < 1.0.2: we do not select anything, but fallback to prime256v1
+ * OpenSSL < 1.1.0: we have to call SSL_CTX_set_ecdh_auto
+ * (openssl/ssl.h defines SSL_CTRL_SET_ECDH_AUTO)
+ * OpenSSL >= 1.1.0: we do not set anything, the libray does autoselection
+ * https://github.com/openssl/openssl/commit/fe6ef2472db933f01b59cad82aa925736935984b
+ */
+if (Ustrcmp(exp_curve, "auto") == 0)
+ {
+#if OPENSSL_VERSION_NUMBER < 0x10002000L
+ DEBUG(D_tls) debug_printf(
+ "ECDH OpenSSL < 1.0.2: temp key parameter settings: overriding \"auto\" with \"prime256v1\"\n");
+ exp_curve = US"prime256v1";
+#else
+# if defined SSL_CTRL_SET_ECDH_AUTO
+ DEBUG(D_tls) debug_printf(
+ "ECDH OpenSSL 1.0.2+: temp key parameter settings: autoselection\n");
+ SSL_CTX_set_ecdh_auto(sctx, 1);
+ return TRUE;
+# else
+ DEBUG(D_tls) debug_printf(
+ "ECDH OpenSSL 1.1.0+: temp key parameter settings: default selection\n");
+ return TRUE;
+# endif
+#endif
+ }
+
+DEBUG(D_tls) debug_printf("ECDH: curve '%s'\n", exp_curve);
+if ( (nid = OBJ_sn2nid (CCS exp_curve)) == NID_undef
+# ifdef EXIM_HAVE_OPENSSL_EC_NIST2NID
+ && (nid = EC_curve_nist2nid(CCS exp_curve)) == NID_undef
+# endif
+ )
+ {
+ tls_error(string_sprintf("Unknown curve name tls_eccurve '%s'", exp_curve),
+ host, NULL, errstr);
+ return FALSE;
+ }
+
+if (!(ecdh = EC_KEY_new_by_curve_name(nid)))
+ {
+ tls_error(US"Unable to create ec curve", host, NULL, errstr);
+ return FALSE;
+ }
+
+/* The "tmp" in the name here refers to setting a temporary key
+not to the stability of the interface. */
+
+if ((rv = SSL_CTX_set_tmp_ecdh(sctx, ecdh) == 0))
+ tls_error(string_sprintf("Error enabling '%s' curve", exp_curve), host, NULL, errstr);
+else
+ DEBUG(D_tls) debug_printf("ECDH: enabled '%s' curve\n", exp_curve);
+
+EC_KEY_free(ecdh);
+return !rv;
+
+# endif /*EXIM_HAVE_ECDH*/
+#endif /*OPENSSL_NO_ECDH*/
+}
+
+
+