+ log_write(0, LOG_MAIN|LOG_PANIC, "could not stat '%s' "
+ "(tls_verify_certificates): %s", bundle, strerror(errno));
+ return DEFER;
+ }
+
+#ifndef SUPPORT_CA_DIR
+ /* The test suite passes in /dev/null; we could check for that path explicitly,
+ but who knows if someone has some weird FIFO which always dumps some certs, or
+ other weirdness. The thing we really want to check is that it's not a
+ directory, since while OpenSSL supports that, GnuTLS does not.
+ So s/!S_ISREG/S_ISDIR/ and change some messaging ... */
+ if (S_ISDIR(statbuf.st_mode))
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "tls_verify_certificates \"%s\" is a directory", bundle);
+ return DEFER;
+ }
+#endif
+
+ DEBUG(D_tls) debug_printf("verify certificates = %s size=" OFF_T_FMT "\n",
+ bundle, statbuf.st_size);
+
+ if (statbuf.st_size == 0)
+ {
+ DEBUG(D_tls)
+ debug_printf("cert file empty, no certs, no verification, ignoring any CRL\n");
+ return OK;
+ }
+
+ cert_count =
+
+#ifdef SUPPORT_CA_DIR
+ (statbuf.st_mode & S_IFMT) == S_IFDIR
+ ?
+ gnutls_certificate_set_x509_trust_dir(state->lib_state.x509_cred,
+ CS bundle, GNUTLS_X509_FMT_PEM)
+ :
+#endif
+ gnutls_certificate_set_x509_trust_file(state->lib_state.x509_cred,
+ CS bundle, GNUTLS_X509_FMT_PEM);
+
+#ifdef SUPPORT_CA_DIR
+ /* Mimic the behaviour with OpenSSL of not advertising a usable-cert list
+ when using the directory-of-certs config model. */
+
+ if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
+ if (state->session)
+ gnutls_certificate_send_x509_rdn_sequence(state->session, 1);
+ else
+ state->lib_state.ca_rdn_emulate = TRUE;
+#endif
+ }
+
+if (cert_count < 0)
+ return tls_error_gnu(state, US"setting certificate trust", cert_count, errstr);
+DEBUG(D_tls)
+ debug_printf("Added %d certificate authorities\n", cert_count);
+
+return OK;
+}
+
+
+static int
+creds_load_crl(exim_gnutls_state_st * state, const uschar * crl, uschar ** errstr)
+{
+int cert_count;
+DEBUG(D_tls) debug_printf("loading CRL file = %s\n", crl);
+if ((cert_count = gnutls_certificate_set_x509_crl_file(state->lib_state.x509_cred,
+ CS crl, GNUTLS_X509_FMT_PEM)) < 0)
+ return tls_error_gnu(state, US"gnutls_certificate_set_x509_crl_file",
+ cert_count, errstr);
+
+DEBUG(D_tls) debug_printf("Processed %d CRLs\n", cert_count);
+return OK;
+}
+
+
+static int
+creds_load_pristring(exim_gnutls_state_st * state, const uschar * p,
+ const char ** errpos)
+{
+if (!p)
+ {
+ p = exim_default_gnutls_priority;
+ DEBUG(D_tls)
+ debug_printf("GnuTLS using default session cipher/priority \"%s\"\n", p);
+ }
+return gnutls_priority_init( (gnutls_priority_t *) &state->lib_state.pri_cache,
+ CCS p, errpos);
+}
+
+static unsigned
+tls_server_creds_init(void)
+{
+uschar * dummy_errstr;
+unsigned lifetime = 0;
+
+state_server.lib_state = null_tls_preload;
+if (gnutls_certificate_allocate_credentials(
+ (gnutls_certificate_credentials_t *) &state_server.lib_state.x509_cred))
+ {
+ state_server.lib_state.x509_cred = NULL;
+ return lifetime;
+ }
+creds_basic_init(state_server.lib_state.x509_cred, TRUE);
+
+#if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
+/* If tls_certificate has any $ indicating expansions, it is not good.
+If tls_privatekey is set but has $, not good. Likewise for tls_ocsp_file.
+If all good (and tls_certificate set), load the cert(s). */
+
+if ( opt_set_and_noexpand(tls_certificate)
+# ifndef DISABLE_OCSP
+ && opt_unset_or_noexpand(tls_ocsp_file)