+#ifdef SUPPORT_GNUTLS_EXT_RAW_PARSE
+/* Make a note that we saw a status-request */
+static int
+tls_server_clienthello_ext(void * ctx, unsigned tls_id,
+ const uschar * data, unsigned size)
+{
+/* The values for tls_id are documented here:
+https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml */
+switch (tls_id)
+ {
+ case 5: /* Status Request */
+ DEBUG(D_tls) debug_printf("Seen status_request extension from client\n");
+ tls_in.ocsp = OCSP_NOT_RESP;
+ break;
+#ifdef EXIM_HAVE_ALPN
+ case 16: /* Application Layer Protocol Notification */
+ /* The format of "data" here doesn't seem to be documented, but appears
+ to be a 2-byte field with a (redundant, given the "size" arg) total length
+ then a sequence of one-byte size then string (not nul-term) names. The
+ latter is as described in OpenSSL documentation. */
+
+ DEBUG(D_tls) debug_printf("Seen ALPN extension from client (s=%u):", size);
+ for (const uschar * s = data+2; s-data < size-1; s += *s + 1)
+ {
+ server_seen_alpn++;
+ DEBUG(D_tls) debug_printf(" '%.*s'", (int)*s, s+1);
+ }
+ DEBUG(D_tls) debug_printf("\n");
+ if (server_seen_alpn > 1)
+ {
+ DEBUG(D_tls) debug_printf("TLS: too many ALPNs presented in handshake\n");
+ return GNUTLS_E_NO_APPLICATION_PROTOCOL;
+ }
+ break;
+#endif
+ }
+return 0;
+}
+
+/* Callback for client-hello, on server, if we think we might serve stapled-OCSP */
+static int
+tls_server_clienthello_cb(gnutls_session_t session, unsigned int htype,
+ unsigned when, unsigned int incoming, const gnutls_datum_t * msg)
+{
+/* Call fn for each extension seen. 3.6.3 onwards */
+int rc = gnutls_ext_raw_parse(NULL, tls_server_clienthello_ext, msg,
+ GNUTLS_EXT_RAW_FLAG_TLS_CLIENT_HELLO);
+return rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE ? 0 : rc;
+}
+
+
+# ifdef notdef_crashes
+/* Make a note that we saw a status-response */
+static int
+tls_server_servercerts_ext(void * ctx, unsigned tls_id,
+ const unsigned char *data, unsigned size)
+{
+/* debug_printf("%s %u\n", __FUNCTION__, tls_id); */
+/* https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml */
+if (FALSE && tls_id == 5) /* status_request */
+ {
+ DEBUG(D_tls) debug_printf("Seen status_request extension\n");
+ tls_in.ocsp = exim_testharness_disable_ocsp_validity_check
+ ? OCSP_VFY_NOT_TRIED : OCSP_VFIED; /* We know that GnuTLS verifies responses */
+ }
+return 0;
+}
+# endif
+
+/* Callback for certificates packet, on server, if we think we might serve stapled-OCSP */
+static int
+tls_server_servercerts_cb(gnutls_session_t session, unsigned int htype,
+ unsigned when, unsigned int incoming, const gnutls_datum_t * msg)
+{
+/* Call fn for each extension seen. 3.6.3 onwards */
+# ifdef notdef_crashes
+ /*XXX crashes */
+return gnutls_ext_raw_parse(NULL, tls_server_servercerts_ext, msg, 0);
+# endif
+}
+#endif /*SUPPORT_GNUTLS_EXT_RAW_PARSE*/
+
+/*XXX in tls1.3 the cert-status travel as an extension next to the cert, in the
+ "Handshake Protocol: Certificate" record.
+So we need to spot the Certificate handshake message, parse it and spot any status_request extension(s)
+
+This is different to tls1.2 - where it is a separate record (wireshark term) / handshake message (gnutls term).
+*/
+
+#if defined(EXIM_HAVE_TLS_RESUME) || defined(SUPPORT_GNUTLS_EXT_RAW_PARSE)
+/* Callback for certificate-status, on server. We sent stapled OCSP. */
+static int
+tls_server_certstatus_cb(gnutls_session_t session, unsigned int htype,
+ unsigned when, unsigned int incoming, const gnutls_datum_t * msg)
+{
+DEBUG(D_tls) debug_printf("Sending certificate-status\n"); /*XXX we get this for tls1.2 but not for 1.3 */
+# ifdef SUPPORT_SRV_OCSP_STACK
+tls_in.ocsp = exim_testharness_disable_ocsp_validity_check
+ ? OCSP_VFY_NOT_TRIED : OCSP_VFIED; /* We know that GnuTLS verifies responses */
+# else
+tls_in.ocsp = OCSP_VFY_NOT_TRIED;
+# endif
+return 0;
+}
+
+/* Callback for handshake messages, on server */
+static int
+tls_server_hook_cb(gnutls_session_t sess, u_int htype, unsigned when,
+ unsigned incoming, const gnutls_datum_t * msg)
+{
+/* debug_printf("%s: htype %u\n", __FUNCTION__, htype); */
+switch (htype)
+ {
+# ifdef SUPPORT_GNUTLS_EXT_RAW_PARSE
+ case GNUTLS_HANDSHAKE_CLIENT_HELLO:
+ return tls_server_clienthello_cb(sess, htype, when, incoming, msg);
+ case GNUTLS_HANDSHAKE_CERTIFICATE_PKT:
+ return tls_server_servercerts_cb(sess, htype, when, incoming, msg);
+# endif
+ case GNUTLS_HANDSHAKE_CERTIFICATE_STATUS:
+ return tls_server_certstatus_cb(sess, htype, when, incoming, msg);
+# ifdef EXIM_HAVE_TLS_RESUME
+ case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET:
+ return tls_server_ticket_cb(sess, htype, when, incoming, msg);
+# endif
+ default:
+ return 0;
+ }
+}
+#endif
+
+
+#if !defined(DISABLE_OCSP) && defined(SUPPORT_GNUTLS_EXT_RAW_PARSE)
+static void
+tls_server_testharness_ocsp_fiddle(void)
+{
+extern char ** environ;
+if (environ) for (uschar ** p = USS environ; *p; p++)
+ if (Ustrncmp(*p, "EXIM_TESTHARNESS_DISABLE_OCSPVALIDITYCHECK", 42) == 0)
+ {
+ DEBUG(D_tls) debug_printf("Permitting known bad OCSP response\n");
+ exim_testharness_disable_ocsp_validity_check = TRUE;
+ }
+}
+#endif
+
+/**************************************************
+* One-time init credentials for server and client *
+**************************************************/
+
+static void
+creds_basic_init(gnutls_certificate_credentials_t x509_cred, BOOL server)
+{
+#ifdef SUPPORT_SRV_OCSP_STACK
+gnutls_certificate_set_flags(x509_cred, GNUTLS_CERTIFICATE_API_V2);
+
+# if !defined(DISABLE_OCSP) && defined(SUPPORT_GNUTLS_EXT_RAW_PARSE)
+if (server && tls_ocsp_file)
+ {
+ if (f.running_in_test_harness)
+ tls_server_testharness_ocsp_fiddle();
+
+ if (exim_testharness_disable_ocsp_validity_check)
+ gnutls_certificate_set_flags(x509_cred,
+ GNUTLS_CERTIFICATE_API_V2 | GNUTLS_CERTIFICATE_SKIP_OCSP_RESPONSE_CHECK);
+ }
+# endif
+#endif
+DEBUG(D_tls)
+ debug_printf("TLS: basic cred init, %s\n", server ? "server" : "client");
+}
+
+static int
+creds_load_server_certs(exim_gnutls_state_st * state, const uschar * cert,
+ const uschar * pkey, const uschar * ocsp, uschar ** errstr)
+{
+const uschar * clist = cert;
+const uschar * klist = pkey;
+const uschar * olist;
+int csep = 0, ksep = 0, osep = 0, cnt = 0, rc;
+uschar * cfile, * kfile, * ofile;
+#ifndef DISABLE_OCSP
+# ifdef SUPPORT_GNUTLS_EXT_RAW_PARSE
+gnutls_x509_crt_fmt_t ocsp_fmt = GNUTLS_X509_FMT_DER;
+# endif
+
+if (!expand_check(ocsp, US"tls_ocsp_file", &ofile, errstr))
+ return DEFER;
+olist = ofile;
+#endif
+
+while (cfile = string_nextinlist(&clist, &csep, NULL, 0))