+ s SSL connection (not used)
+ export not used
+ keylength keylength
+
+Returns: pointer to generated key
+*/
+
+static RSA *
+rsa_callback(SSL *s, int export, int keylength)
+{
+RSA *rsa_key;
+#ifdef EXIM_HAVE_RSA_GENKEY_EX
+BIGNUM *bn = BN_new();
+#endif
+
+DEBUG(D_tls) debug_printf("Generating %d bit RSA key...\n", keylength);
+
+#ifdef EXIM_HAVE_RSA_GENKEY_EX
+if ( !BN_set_word(bn, (unsigned long)RSA_F4)
+ || !(rsa_key = RSA_new())
+ || !RSA_generate_key_ex(rsa_key, keylength, bn, NULL)
+ )
+#else
+if (!(rsa_key = RSA_generate_key(keylength, RSA_F4, NULL, NULL)))
+#endif
+
+ {
+ ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
+ log_write(0, LOG_MAIN|LOG_PANIC, "TLS error (RSA_generate_key): %s",
+ ssl_errstring);
+ return NULL;
+ }
+return rsa_key;
+}
+
+
+
+/* Create and install a selfsigned certificate, for use in server mode */
+/*XXX we could arrange to call this during prelo for a null tls_certificate option.
+The normal cache inval + relo will suffice.
+Just need a timer for inval. */
+
+static int
+tls_install_selfsign(SSL_CTX * sctx, uschar ** errstr)
+{
+X509 * x509 = NULL;
+EVP_PKEY * pkey;
+RSA * rsa;
+X509_NAME * name;
+uschar * where;
+
+DEBUG(D_tls) debug_printf("TLS: generating selfsigned server cert\n");
+where = US"allocating pkey";
+if (!(pkey = EVP_PKEY_new()))
+ goto err;
+
+where = US"allocating cert";
+if (!(x509 = X509_new()))
+ goto err;
+
+where = US"generating pkey";
+if (!(rsa = rsa_callback(NULL, 0, 2048)))
+ goto err;
+
+where = US"assigning pkey";
+if (!EVP_PKEY_assign_RSA(pkey, rsa))
+ goto err;
+
+X509_set_version(x509, 2); /* N+1 - version 3 */
+ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);
+X509_gmtime_adj(X509_get_notBefore(x509), 0);
+X509_gmtime_adj(X509_get_notAfter(x509), (long)2 * 60 * 60); /* 2 hour */
+X509_set_pubkey(x509, pkey);
+
+name = X509_get_subject_name(x509);
+X509_NAME_add_entry_by_txt(name, "C",
+ MBSTRING_ASC, CUS "UK", -1, -1, 0);
+X509_NAME_add_entry_by_txt(name, "O",
+ MBSTRING_ASC, CUS "Exim Developers", -1, -1, 0);
+X509_NAME_add_entry_by_txt(name, "CN",
+ MBSTRING_ASC, CUS smtp_active_hostname, -1, -1, 0);
+X509_set_issuer_name(x509, name);
+
+where = US"signing cert";
+if (!X509_sign(x509, pkey, EVP_md5()))
+ goto err;
+
+where = US"installing selfsign cert";
+if (!SSL_CTX_use_certificate(sctx, x509))
+ goto err;
+
+where = US"installing selfsign key";
+if (!SSL_CTX_use_PrivateKey(sctx, pkey))
+ goto err;
+
+return OK;
+
+err:
+ (void) tls_error(where, NULL, NULL, errstr);
+ if (x509) X509_free(x509);
+ if (pkey) EVP_PKEY_free(pkey);
+ return DEFER;
+}
+
+
+
+
+
+
+
+/*************************************************
+* Information callback *
+*************************************************/
+
+/* The SSL library functions call this from time to time to indicate what they
+are doing. We copy the string to the debugging output when TLS debugging has
+been requested.
+
+Arguments:
+ s the SSL connection
+ where
+ ret
+
+Returns: nothing
+*/
+
+static void
+info_callback(SSL *s, int where, int ret)
+{
+DEBUG(D_tls)
+ {
+ const uschar * str;
+
+ if (where & SSL_ST_CONNECT)
+ str = US"SSL_connect";
+ else if (where & SSL_ST_ACCEPT)
+ str = US"SSL_accept";
+ else
+ str = US"SSL info (undefined)";
+
+ if (where & SSL_CB_LOOP)
+ debug_printf("%s: %s\n", str, SSL_state_string_long(s));
+ else if (where & SSL_CB_ALERT)
+ debug_printf("SSL3 alert %s:%s:%s\n",
+ str = where & SSL_CB_READ ? US"read" : US"write",
+ SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
+ else if (where & SSL_CB_EXIT)
+ {
+ if (ret == 0)
+ debug_printf("%s: failed in %s\n", str, SSL_state_string_long(s));
+ else if (ret < 0)
+ debug_printf("%s: error in %s\n", str, SSL_state_string_long(s));
+ }
+ else if (where & SSL_CB_HANDSHAKE_START)
+ debug_printf("%s: hshake start: %s\n", str, SSL_state_string_long(s));
+ else if (where & SSL_CB_HANDSHAKE_DONE)
+ debug_printf("%s: hshake done: %s\n", str, SSL_state_string_long(s));
+ }
+}
+
+#ifdef OPENSSL_HAVE_KEYLOG_CB
+static void
+keylog_callback(const SSL *ssl, const char *line)
+{
+char * filename;
+FILE * fp;
+DEBUG(D_tls) debug_printf("%.200s\n", line);
+if (!(filename = getenv("SSLKEYLOGFILE"))) return;
+if (!(fp = fopen(filename, "a"))) return;
+fprintf(fp, "%s\n", line);
+fclose(fp);
+}
+#endif
+
+
+
+
+
+#ifndef DISABLE_EVENT
+static int
+verify_event(tls_support * tlsp, X509 * cert, int depth, const uschar * dn,
+ BOOL *calledp, const BOOL *optionalp, const uschar * what)
+{
+uschar * ev;
+uschar * yield;
+X509 * old_cert;
+
+ev = tlsp == &tls_out ? client_static_state->event_action : event_action;
+if (ev)
+ {
+ DEBUG(D_tls) debug_printf("verify_event: %s %d\n", what, depth);
+ old_cert = tlsp->peercert;
+ tlsp->peercert = X509_dup(cert);
+ /* NB we do not bother setting peerdn */
+ if ((yield = event_raise(ev, US"tls:cert", string_sprintf("%d", depth))))
+ {
+ log_write(0, LOG_MAIN, "[%s] %s verify denied by event-action: "
+ "depth=%d cert=%s: %s",
+ tlsp == &tls_out ? deliver_host_address : sender_host_address,
+ what, depth, dn, yield);
+ *calledp = TRUE;
+ if (!*optionalp)
+ {
+ if (old_cert) tlsp->peercert = old_cert; /* restore 1st failing cert */
+ return 1; /* reject (leaving peercert set) */
+ }
+ DEBUG(D_tls) debug_printf("Event-action verify failure overridden "
+ "(host in tls_try_verify_hosts)\n");
+ tlsp->verify_override = TRUE;
+ }
+ X509_free(tlsp->peercert);
+ tlsp->peercert = old_cert;
+ }
+return 0;
+}
+#endif
+
+/*************************************************
+* Callback for verification *
+*************************************************/
+
+/* The SSL library does certificate verification if set up to do so. This
+callback has the current yes/no state is in "state". If verification succeeded,
+we set the certificate-verified flag. If verification failed, what happens
+depends on whether the client is required to present a verifiable certificate
+or not.
+
+If verification is optional, we change the state to yes, but still log the
+verification error. For some reason (it really would help to have proper
+documentation of OpenSSL), this callback function then gets called again, this
+time with state = 1. We must take care not to set the private verified flag on
+the second time through.
+
+Note: this function is not called if the client fails to present a certificate
+when asked. We get here only if a certificate has been received. Handling of
+optional verification for this case is done when requesting SSL to verify, by
+setting SSL_VERIFY_FAIL_IF_NO_PEER_CERT in the non-optional case.
+
+May be called multiple times for different issues with a certificate, even
+for a given "depth" in the certificate chain.
+
+Arguments:
+ preverify_ok current yes/no state as 1/0
+ x509ctx certificate information.
+ tlsp per-direction (client vs. server) support data
+ calledp has-been-called flag
+ optionalp verification-is-optional flag
+
+Returns: 0 if verification should fail, otherwise 1
+*/
+
+static int
+verify_callback(int preverify_ok, X509_STORE_CTX * x509ctx,
+ tls_support * tlsp, BOOL * calledp, BOOL * optionalp)
+{
+X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx);
+int depth = X509_STORE_CTX_get_error_depth(x509ctx);
+uschar dn[256];
+
+if (!X509_NAME_oneline(X509_get_subject_name(cert), CS dn, sizeof(dn)))
+ {
+ DEBUG(D_tls) debug_printf("X509_NAME_oneline() error\n");
+ log_write(0, LOG_MAIN, "[%s] SSL verify error: internal error",
+ tlsp == &tls_out ? deliver_host_address : sender_host_address);
+ return 0;
+ }
+dn[sizeof(dn)-1] = '\0';
+
+tlsp->verify_override = FALSE;
+if (preverify_ok == 0)
+ {
+ uschar * extra = verify_mode ? string_sprintf(" (during %c-verify for [%s])",
+ *verify_mode, sender_host_address)
+ : US"";
+ log_write(0, LOG_MAIN, "[%s] SSL verify error%s: depth=%d error=%s cert=%s",
+ tlsp == &tls_out ? deliver_host_address : sender_host_address,
+ extra, depth,
+ X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509ctx)), dn);
+ *calledp = TRUE;
+ if (!*optionalp)
+ {
+ if (!tlsp->peercert)
+ tlsp->peercert = X509_dup(cert); /* record failing cert */
+ return 0; /* reject */
+ }
+ DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in "
+ "tls_try_verify_hosts)\n");
+ tlsp->verify_override = TRUE;
+ }
+
+else if (depth != 0)
+ {
+ DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d SN=%s\n", depth, dn);
+#ifndef DISABLE_OCSP
+ if (tlsp == &tls_out && client_static_state->u_ocsp.client.verify_store)
+ { /* client, wanting stapling */
+ /* Add the server cert's signing chain as the one
+ for the verification of the OCSP stapled information. */
+
+ if (!X509_STORE_add_cert(client_static_state->u_ocsp.client.verify_store,
+ cert))
+ ERR_clear_error();
+ sk_X509_push(client_static_state->verify_stack, cert);
+ }
+#endif
+#ifndef DISABLE_EVENT
+ if (verify_event(tlsp, cert, depth, dn, calledp, optionalp, US"SSL"))
+ return 0; /* reject, with peercert set */
+#endif
+ }
+else
+ {
+ const uschar * verify_cert_hostnames;
+
+ if ( tlsp == &tls_out
+ && ((verify_cert_hostnames = client_static_state->verify_cert_hostnames)))
+ /* client, wanting hostname check */
+ {
+
+#ifdef EXIM_HAVE_OPENSSL_CHECKHOST
+# ifndef X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS
+# define X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS 0
+# endif
+# ifndef X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS
+# define X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS 0
+# endif
+ int sep = 0;
+ const uschar * list = verify_cert_hostnames;
+ uschar * name;
+ int rc;
+ while ((name = string_nextinlist(&list, &sep, NULL, 0)))
+ if ((rc = X509_check_host(cert, CCS name, 0,
+ X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS
+ | X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS,
+ NULL)))
+ {
+ if (rc < 0)
+ {
+ log_write(0, LOG_MAIN, "[%s] SSL verify error: internal error",
+ tlsp == &tls_out ? deliver_host_address : sender_host_address);
+ name = NULL;
+ }
+ break;
+ }
+ if (!name)
+#else
+ if (!tls_is_name_for_cert(verify_cert_hostnames, cert))
+#endif
+ {
+ uschar * extra = verify_mode
+ ? string_sprintf(" (during %c-verify for [%s])",
+ *verify_mode, sender_host_address)
+ : US"";
+ log_write(0, LOG_MAIN,
+ "[%s] SSL verify error%s: certificate name mismatch: DN=\"%s\" H=\"%s\"",
+ tlsp == &tls_out ? deliver_host_address : sender_host_address,
+ extra, dn, verify_cert_hostnames);
+ *calledp = TRUE;
+ if (!*optionalp)
+ {
+ if (!tlsp->peercert)
+ tlsp->peercert = X509_dup(cert); /* record failing cert */
+ return 0; /* reject */
+ }
+ DEBUG(D_tls) debug_printf("SSL verify name failure overridden (host in "
+ "tls_try_verify_hosts)\n");
+ tlsp->verify_override = TRUE;
+ }
+ }
+
+#ifndef DISABLE_EVENT
+ if (verify_event(tlsp, cert, depth, dn, calledp, optionalp, US"SSL"))
+ return 0; /* reject, with peercert set */
+#endif
+
+ DEBUG(D_tls) debug_printf("SSL%s verify ok: depth=0 SN=%s\n",
+ *calledp ? "" : " authenticated", dn);
+ *calledp = TRUE;
+ }
+
+return 1; /* accept, at least for this level */
+}
+
+static int
+verify_callback_client(int preverify_ok, X509_STORE_CTX *x509ctx)
+{
+return verify_callback(preverify_ok, x509ctx, &tls_out,
+ &client_verify_callback_called, &client_verify_optional);
+}
+
+static int
+verify_callback_server(int preverify_ok, X509_STORE_CTX *x509ctx)
+{
+return verify_callback(preverify_ok, x509ctx, &tls_in,
+ &server_verify_callback_called, &server_verify_optional);
+}
+
+
+#ifdef SUPPORT_DANE
+
+/* This gets called *by* the dane library verify callback, which interposes
+itself.
+*/
+static int
+verify_callback_client_dane(int preverify_ok, X509_STORE_CTX * x509ctx)
+{
+X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx);
+uschar dn[256];
+int depth = X509_STORE_CTX_get_error_depth(x509ctx);
+#ifndef DISABLE_EVENT
+BOOL dummy_called, optional = FALSE;
+#endif
+
+if (!X509_NAME_oneline(X509_get_subject_name(cert), CS dn, sizeof(dn)))
+ {
+ DEBUG(D_tls) debug_printf("X509_NAME_oneline() error\n");
+ log_write(0, LOG_MAIN, "[%s] SSL verify error: internal error",
+ deliver_host_address);
+ return 0;
+ }
+dn[sizeof(dn)-1] = '\0';
+
+DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s depth %d %s\n",
+ preverify_ok ? "ok":"BAD", depth, dn);
+
+#ifndef DISABLE_EVENT
+ if (verify_event(&tls_out, cert, depth, dn,
+ &dummy_called, &optional, US"DANE"))
+ return 0; /* reject, with peercert set */
+#endif
+
+if (preverify_ok == 1)
+ {
+ tls_out.dane_verified = TRUE;
+#ifndef DISABLE_OCSP
+ if (client_static_state->u_ocsp.client.verify_store)
+ { /* client, wanting stapling */
+ /* Add the server cert's signing chain as the one
+ for the verification of the OCSP stapled information. */
+
+ if (!X509_STORE_add_cert(client_static_state->u_ocsp.client.verify_store,
+ cert))
+ ERR_clear_error();
+ sk_X509_push(client_static_state->verify_stack, cert);
+ }
+#endif
+ }
+else
+ {
+ int err = X509_STORE_CTX_get_error(x509ctx);
+ DEBUG(D_tls)
+ debug_printf(" - err %d '%s'\n", err, X509_verify_cert_error_string(err));
+ if (err == X509_V_ERR_APPLICATION_VERIFICATION)
+ preverify_ok = 1;
+ }
+return preverify_ok;
+}
+
+#endif /*SUPPORT_DANE*/
+
+
+#ifndef DISABLE_OCSP
+/*************************************************
+* Load OCSP information into state *
+*************************************************/
+/* Called to load the server OCSP response from the given file into memory, once
+caller has determined this is needed. Checks validity. Debugs a message
+if invalid.