+if (!OCSP_check_validity(thisupd, nextupd, EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE))
+ {
+ DEBUG(D_tls) debug_printf("OCSP status invalid times.\n");
+ goto bad;
+ }
+
+supply_response:
+ cbinfo->u_ocsp.server.response = resp;
+return;
+
+bad:
+ if (running_in_test_harness)
+ {
+ extern char ** environ;
+ uschar ** p;
+ if (environ) for (p = USS environ; *p != NULL; p++)
+ if (Ustrncmp(*p, "EXIM_TESTHARNESS_DISABLE_OCSPVALIDITYCHECK", 42) == 0)
+ {
+ DEBUG(D_tls) debug_printf("Supplying known bad OCSP response\n");
+ goto supply_response;
+ }
+ }
+return;
+}
+#endif /*!DISABLE_OCSP*/
+
+
+
+
+/* Create and install a selfsigned certificate, for use in server mode */
+
+static int
+tls_install_selfsign(SSL_CTX * sctx, uschar ** errstr)
+{
+X509 * x509 = NULL;
+EVP_PKEY * pkey;
+RSA * rsa;
+X509_NAME * name;
+uschar * where;
+
+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";
+ /* deprecated, use RSA_generate_key_ex() */
+if (!(rsa = RSA_generate_key(1024, RSA_F4, NULL, NULL)))
+ 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), 0);
+X509_gmtime_adj(X509_get_notBefore(x509), 0);
+X509_gmtime_adj(X509_get_notAfter(x509), (long)60 * 60); /* 1 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;
+}
+
+
+
+
+/*************************************************
+* Expand key and cert file specs *
+*************************************************/
+
+/* Called once during tls_init and possibly again during TLS setup, for a
+new context, if Server Name Indication was used and tls_sni was seen in
+the certificate string.
+
+Arguments:
+ sctx the SSL_CTX* to update
+ cbinfo various parts of session state
+ errstr error string pointer
+
+Returns: OK/DEFER/FAIL
+*/
+
+static int
+tls_expand_session_files(SSL_CTX *sctx, tls_ext_ctx_cb *cbinfo,
+ uschar ** errstr)
+{
+uschar *expanded;
+
+if (!cbinfo->certificate)
+ {
+ if (cbinfo->host) /* client */
+ return OK;
+ /* server */
+ if (tls_install_selfsign(sctx, errstr) != OK)
+ return DEFER;
+ }
+else
+ {
+ if (Ustrstr(cbinfo->certificate, US"tls_sni") ||
+ Ustrstr(cbinfo->certificate, US"tls_in_sni") ||
+ Ustrstr(cbinfo->certificate, US"tls_out_sni")
+ )
+ reexpand_tls_files_for_sni = TRUE;
+
+ if (!expand_check(cbinfo->certificate, US"tls_certificate", &expanded, errstr))
+ return DEFER;
+
+ if (expanded != NULL)
+ {
+ DEBUG(D_tls) debug_printf("tls_certificate file %s\n", expanded);
+ if (!SSL_CTX_use_certificate_chain_file(sctx, CS expanded))
+ return tls_error(string_sprintf(
+ "SSL_CTX_use_certificate_chain_file file=%s", expanded),
+ cbinfo->host, NULL, errstr);
+ }
+
+ if (cbinfo->privatekey != NULL &&
+ !expand_check(cbinfo->privatekey, US"tls_privatekey", &expanded, errstr))
+ return DEFER;
+
+ /* If expansion was forced to fail, key_expanded will be NULL. If the result
+ of the expansion is an empty string, ignore it also, and assume the private
+ key is in the same file as the certificate. */
+
+ if (expanded && *expanded)
+ {
+ DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", expanded);
+ if (!SSL_CTX_use_PrivateKey_file(sctx, CS expanded, SSL_FILETYPE_PEM))
+ return tls_error(string_sprintf(
+ "SSL_CTX_use_PrivateKey_file file=%s", expanded), cbinfo->host, NULL, errstr);
+ }
+ }
+
+#ifndef DISABLE_OCSP
+if (cbinfo->is_server && cbinfo->u_ocsp.server.file)
+ {
+ if (!expand_check(cbinfo->u_ocsp.server.file, US"tls_ocsp_file", &expanded, errstr))
+ return DEFER;
+
+ if (expanded && *expanded)
+ {
+ DEBUG(D_tls) debug_printf("tls_ocsp_file %s\n", expanded);
+ if ( cbinfo->u_ocsp.server.file_expanded
+ && (Ustrcmp(expanded, cbinfo->u_ocsp.server.file_expanded) == 0))
+ {
+ DEBUG(D_tls) debug_printf(" - value unchanged, using existing values\n");
+ }
+ else
+ ocsp_load_response(sctx, cbinfo, expanded);
+ }
+ }
+#endif
+
+return OK;
+}
+
+
+
+
+/*************************************************
+* Callback to handle SNI *
+*************************************************/
+
+/* Called when acting as server during the TLS session setup if a Server Name
+Indication extension was sent by the client.
+
+API documentation is OpenSSL s_server.c implementation.
+
+Arguments:
+ s SSL* of the current session
+ ad unknown (part of OpenSSL API) (unused)
+ arg Callback of "our" registered data
+
+Returns: SSL_TLSEXT_ERR_{OK,ALERT_WARNING,ALERT_FATAL,NOACK}
+*/
+
+#ifdef EXIM_HAVE_OPENSSL_TLSEXT
+static int
+tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg)
+{
+const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
+tls_ext_ctx_cb *cbinfo = (tls_ext_ctx_cb *) arg;
+int rc;
+int old_pool = store_pool;
+uschar * dummy_errstr;
+
+if (!servername)
+ return SSL_TLSEXT_ERR_OK;
+
+DEBUG(D_tls) debug_printf("Received TLS SNI \"%s\"%s\n", servername,
+ reexpand_tls_files_for_sni ? "" : " (unused for certificate selection)");
+
+/* Make the extension value available for expansion */
+store_pool = POOL_PERM;
+tls_in.sni = string_copy(US servername);
+store_pool = old_pool;
+
+if (!reexpand_tls_files_for_sni)
+ return SSL_TLSEXT_ERR_OK;
+
+/* Can't find an SSL_CTX_clone() or equivalent, so we do it manually;
+not confident that memcpy wouldn't break some internal reference counting.
+Especially since there's a references struct member, which would be off. */
+
+if (!(server_sni = SSL_CTX_new(SSLv23_server_method())))
+ {
+ ERR_error_string(ERR_get_error(), ssl_errstring);
+ DEBUG(D_tls) debug_printf("SSL_CTX_new() failed: %s\n", ssl_errstring);
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+/* Not sure how many of these are actually needed, since SSL object
+already exists. Might even need this selfsame callback, for reneg? */
+
+SSL_CTX_set_info_callback(server_sni, SSL_CTX_get_info_callback(server_ctx));
+SSL_CTX_set_mode(server_sni, SSL_CTX_get_mode(server_ctx));
+SSL_CTX_set_options(server_sni, SSL_CTX_get_options(server_ctx));
+SSL_CTX_set_timeout(server_sni, SSL_CTX_get_timeout(server_ctx));
+SSL_CTX_set_tlsext_servername_callback(server_sni, tls_servername_cb);
+SSL_CTX_set_tlsext_servername_arg(server_sni, cbinfo);
+
+if ( !init_dh(server_sni, cbinfo->dhparam, NULL, &dummy_errstr)
+ || !init_ecdh(server_sni, NULL, &dummy_errstr)
+ )
+ return SSL_TLSEXT_ERR_NOACK;
+
+if (cbinfo->server_cipher_list)
+ SSL_CTX_set_cipher_list(server_sni, CS cbinfo->server_cipher_list);
+#ifndef DISABLE_OCSP
+if (cbinfo->u_ocsp.server.file)
+ {
+ SSL_CTX_set_tlsext_status_cb(server_sni, tls_server_stapling_cb);
+ SSL_CTX_set_tlsext_status_arg(server_sni, cbinfo);
+ }
+#endif
+
+if ((rc = setup_certs(server_sni, tls_verify_certificates, tls_crl, NULL, FALSE,
+ verify_callback_server, &dummy_errstr)) != OK)
+ return SSL_TLSEXT_ERR_NOACK;
+
+/* do this after setup_certs, because this can require the certs for verifying
+OCSP information. */
+if ((rc = tls_expand_session_files(server_sni, cbinfo, &dummy_errstr)) != OK)
+ return SSL_TLSEXT_ERR_NOACK;
+
+DEBUG(D_tls) debug_printf("Switching SSL context.\n");
+SSL_set_SSL_CTX(s, server_sni);
+
+return SSL_TLSEXT_ERR_OK;
+}
+#endif /* EXIM_HAVE_OPENSSL_TLSEXT */
+
+
+
+
+#ifndef DISABLE_OCSP
+
+/*************************************************
+* Callback to handle OCSP Stapling *
+*************************************************/
+
+/* Called when acting as server during the TLS session setup if the client
+requests OCSP information with a Certificate Status Request.
+
+Documentation via openssl s_server.c and the Apache patch from the OpenSSL
+project.
+
+*/
+
+static int
+tls_server_stapling_cb(SSL *s, void *arg)
+{
+const tls_ext_ctx_cb *cbinfo = (tls_ext_ctx_cb *) arg;
+uschar *response_der;
+int response_der_len;
+
+DEBUG(D_tls)
+ debug_printf("Received TLS status request (OCSP stapling); %s response\n",
+ cbinfo->u_ocsp.server.response ? "have" : "lack");
+
+tls_in.ocsp = OCSP_NOT_RESP;
+if (!cbinfo->u_ocsp.server.response)
+ return SSL_TLSEXT_ERR_NOACK;
+
+response_der = NULL;
+response_der_len = i2d_OCSP_RESPONSE(cbinfo->u_ocsp.server.response,
+ &response_der);
+if (response_der_len <= 0)
+ return SSL_TLSEXT_ERR_NOACK;
+
+SSL_set_tlsext_status_ocsp_resp(server_ssl, response_der, response_der_len);
+tls_in.ocsp = OCSP_VFIED;
+return SSL_TLSEXT_ERR_OK;
+}
+
+
+static void
+time_print(BIO * bp, const char * str, ASN1_GENERALIZEDTIME * time)
+{
+BIO_printf(bp, "\t%s: ", str);
+ASN1_GENERALIZEDTIME_print(bp, time);
+BIO_puts(bp, "\n");