+Returns: OK/DEFER/FAIL
+*/
+
+static int
+tls_expand_session_files(exim_gnutls_state_st *state)
+{
+struct stat statbuf;
+int rc;
+const host_item *host = state->host; /* macro should be reconsidered? */
+uschar *saved_tls_certificate = NULL;
+uschar *saved_tls_privatekey = NULL;
+uschar *saved_tls_verify_certificates = NULL;
+uschar *saved_tls_crl = NULL;
+int cert_count;
+
+/* We check for tls_sni *before* expansion. */
+if (!host) /* server */
+ {
+ if (!state->received_sni)
+ {
+ if (state->tls_certificate &&
+ (Ustrstr(state->tls_certificate, US"tls_sni") ||
+ Ustrstr(state->tls_certificate, US"tls_in_sni") ||
+ Ustrstr(state->tls_certificate, US"tls_out_sni")
+ ))
+ {
+ DEBUG(D_tls) debug_printf("We will re-expand TLS session files if we receive SNI.\n");
+ state->trigger_sni_changes = TRUE;
+ }
+ }
+ else
+ {
+ /* useful for debugging */
+ saved_tls_certificate = state->exp_tls_certificate;
+ saved_tls_privatekey = state->exp_tls_privatekey;
+ saved_tls_verify_certificates = state->exp_tls_verify_certificates;
+ saved_tls_crl = state->exp_tls_crl;
+ }
+ }
+
+rc = gnutls_certificate_allocate_credentials(&state->x509_cred);
+exim_gnutls_err_check(US"gnutls_certificate_allocate_credentials");
+
+/* remember: expand_check_tlsvar() is expand_check() but fiddling with
+state members, assuming consistent naming; and expand_check() returns
+false if expansion failed, unless expansion was forced to fail. */
+
+/* check if we at least have a certificate, before doing expensive
+D-H generation. */
+
+if (!expand_check_tlsvar(tls_certificate))
+ return DEFER;
+
+/* certificate is mandatory in server, optional in client */
+
+if ((state->exp_tls_certificate == NULL) ||
+ (*state->exp_tls_certificate == '\0'))
+ {
+ if (!host)
+ return tls_error(US"no TLS server certificate is specified", NULL, NULL);
+ else
+ DEBUG(D_tls) debug_printf("TLS: no client certificate specified; okay\n");
+ }
+
+if (state->tls_privatekey && !expand_check_tlsvar(tls_privatekey))
+ return DEFER;
+
+/* tls_privatekey is optional, defaulting to same file as certificate */
+
+if (state->tls_privatekey == NULL || *state->tls_privatekey == '\0')
+ {
+ state->tls_privatekey = state->tls_certificate;
+ state->exp_tls_privatekey = state->exp_tls_certificate;
+ }
+
+
+if (state->exp_tls_certificate && *state->exp_tls_certificate)
+ {
+ DEBUG(D_tls) debug_printf("certificate file = %s\nkey file = %s\n",
+ state->exp_tls_certificate, state->exp_tls_privatekey);
+
+ if (state->received_sni)
+ {
+ if ((Ustrcmp(state->exp_tls_certificate, saved_tls_certificate) == 0) &&
+ (Ustrcmp(state->exp_tls_privatekey, saved_tls_privatekey) == 0))
+ {
+ DEBUG(D_tls) debug_printf("TLS SNI: cert and key unchanged\n");
+ }
+ else
+ {
+ DEBUG(D_tls) debug_printf("TLS SNI: have a changed cert/key pair.\n");
+ }
+ }
+
+ rc = gnutls_certificate_set_x509_key_file(state->x509_cred,
+ CS state->exp_tls_certificate, CS state->exp_tls_privatekey,
+ GNUTLS_X509_FMT_PEM);
+ exim_gnutls_err_check(
+ string_sprintf("cert/key setup: cert=%s key=%s",
+ state->exp_tls_certificate, state->exp_tls_privatekey));
+ DEBUG(D_tls) debug_printf("TLS: cert/key registered\n");
+ } /* tls_certificate */
+
+
+/* Set the OCSP stapling server info */
+
+#ifdef EXPERIMENTAL_OCSP
+if ( !host /* server */
+ && tls_ocsp_file
+ )
+ {
+ if (!expand_check(tls_ocsp_file, US"tls_ocsp_file",
+ &state->exp_tls_ocsp_file))
+ return DEFER;
+
+ /* Use the full callback method for stapling just to get observability.
+ More efficient would be to read the file once only, if it never changed
+ (due to SNI). Would need restart on file update, or watch datestamp. */
+
+ gnutls_certificate_set_ocsp_status_request_function(state->x509_cred,
+ server_ocsp_stapling_cb, state->exp_tls_ocsp_file);
+
+ DEBUG(D_tls) debug_printf("Set OCSP response file %s\n", &state->exp_tls_ocsp_file);