+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");
+}
+
+static int
+tls_client_stapling_cb(SSL *s, void *arg)
+{
+tls_ext_ctx_cb * cbinfo = arg;
+const unsigned char * p;
+int len;
+OCSP_RESPONSE * rsp;
+OCSP_BASICRESP * bs;
+int i;
+
+DEBUG(D_tls) debug_printf("Received TLS status response (OCSP stapling):");
+len = SSL_get_tlsext_status_ocsp_resp(s, &p);
+if(!p)
+ {
+ /* Expect this when we requested ocsp but got none */
+ if ( cbinfo->u_ocsp.client.verify_required
+ && log_extra_selector & LX_tls_cipher)
+ log_write(0, LOG_MAIN, "Received TLS status callback, null content");
+ else
+ DEBUG(D_tls) debug_printf(" null\n");
+ return cbinfo->u_ocsp.client.verify_required ? 0 : 1;
+ }
+
+if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
+ {
+ tls_out.ocsp = OCSP_FAILED;
+ if (log_extra_selector & LX_tls_cipher)
+ log_write(0, LOG_MAIN, "Received TLS cert status response, parse error");
+ else
+ DEBUG(D_tls) debug_printf(" parse error\n");
+ return 0;
+ }
+
+if(!(bs = OCSP_response_get1_basic(rsp)))
+ {
+ tls_out.ocsp = OCSP_FAILED;
+ if (log_extra_selector & LX_tls_cipher)
+ log_write(0, LOG_MAIN, "Received TLS cert status response, error parsing response");
+ else
+ DEBUG(D_tls) debug_printf(" error parsing response\n");
+ OCSP_RESPONSE_free(rsp);
+ return 0;
+ }
+
+/* We'd check the nonce here if we'd put one in the request. */
+/* However that would defeat cacheability on the server so we don't. */
+
+/* This section of code reworked from OpenSSL apps source;
+ The OpenSSL Project retains copyright:
+ Copyright (c) 1999 The OpenSSL Project. All rights reserved.
+*/
+ {
+ BIO * bp = NULL;
+ int status, reason;
+ ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
+
+ DEBUG(D_tls) bp = BIO_new_fp(stderr, BIO_NOCLOSE);
+
+ /*OCSP_RESPONSE_print(bp, rsp, 0); extreme debug: stapling content */
+
+ /* Use the chain that verified the server cert to verify the stapled info */
+ /* DEBUG(D_tls) x509_store_dump_cert_s_names(cbinfo->u_ocsp.client.verify_store); */
+
+ if ((i = OCSP_basic_verify(bs, NULL,
+ cbinfo->u_ocsp.client.verify_store, 0)) <= 0)
+ {
+ tls_out.ocsp = OCSP_FAILED;
+ if (log_extra_selector & LX_tls_cipher)
+ log_write(0, LOG_MAIN, "Received TLS cert status response, itself unverifiable");
+ BIO_printf(bp, "OCSP response verify failure\n");
+ ERR_print_errors(bp);
+ i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
+ goto out;
+ }
+
+ BIO_printf(bp, "OCSP response well-formed and signed OK\n");
+
+ {
+ STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses;
+ OCSP_SINGLERESP * single;
+
+ if (sk_OCSP_SINGLERESP_num(sresp) != 1)
+ {
+ tls_out.ocsp = OCSP_FAILED;
+ log_write(0, LOG_MAIN, "OCSP stapling "
+ "with multiple responses not handled");
+ i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
+ goto out;
+ }
+ single = OCSP_resp_get0(bs, 0);
+ status = OCSP_single_get0_status(single, &reason, &rev,
+ &thisupd, &nextupd);
+ }
+
+ DEBUG(D_tls) time_print(bp, "This OCSP Update", thisupd);
+ DEBUG(D_tls) if(nextupd) time_print(bp, "Next OCSP Update", nextupd);
+ if (!OCSP_check_validity(thisupd, nextupd,
+ EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE))
+ {
+ tls_out.ocsp = OCSP_FAILED;
+ DEBUG(D_tls) ERR_print_errors(bp);
+ log_write(0, LOG_MAIN, "Server OSCP dates invalid");
+ i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
+ }
+ else
+ {
+ DEBUG(D_tls) BIO_printf(bp, "Certificate status: %s\n",
+ OCSP_cert_status_str(status));
+ switch(status)
+ {
+ case V_OCSP_CERTSTATUS_GOOD:
+ tls_out.ocsp = OCSP_VFIED;
+ i = 1;
+ break;
+ case V_OCSP_CERTSTATUS_REVOKED:
+ tls_out.ocsp = OCSP_FAILED;
+ log_write(0, LOG_MAIN, "Server certificate revoked%s%s",
+ reason != -1 ? "; reason: " : "",
+ reason != -1 ? OCSP_crl_reason_str(reason) : "");
+ DEBUG(D_tls) time_print(bp, "Revocation Time", rev);
+ i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
+ break;
+ default:
+ tls_out.ocsp = OCSP_FAILED;
+ log_write(0, LOG_MAIN,
+ "Server certificate status unknown, in OCSP stapling");
+ i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
+ break;
+ }
+ }
+ out:
+ BIO_free(bp);
+ }