+ if (state->tls_crl && *state->tls_crl)
+ if (!expand_check_tlsvar(tls_crl))
+ return DEFER;
+
+ if (!(state->exp_tls_verify_certificates &&
+ *state->exp_tls_verify_certificates))
+ {
+ DEBUG(D_tls)
+ debug_printf("TLS: tls_verify_certificates expanded empty, ignoring\n");
+ /* With no tls_verify_certificates, we ignore tls_crl too */
+ return OK;
+ }
+ }
+else
+ {
+ DEBUG(D_tls)
+ debug_printf("TLS: tls_verify_certificates not set or empty, ignoring\n");
+ return OK;
+ }
+
+if (Ustat(state->exp_tls_verify_certificates, &statbuf) < 0)
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC, "could not stat %s "
+ "(tls_verify_certificates): %s", state->exp_tls_verify_certificates,
+ strerror(errno));
+ return DEFER;
+ }
+
+/* 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 messsaging ... */
+if (S_ISDIR(statbuf.st_mode))
+ {
+ DEBUG(D_tls)
+ debug_printf("verify certificates path is a dir: \"%s\"\n",
+ state->exp_tls_verify_certificates);
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "tls_verify_certificates \"%s\" is a directory",
+ state->exp_tls_verify_certificates);
+ return DEFER;
+ }
+
+DEBUG(D_tls) debug_printf("verify certificates = %s size=" OFF_T_FMT "\n",
+ state->exp_tls_verify_certificates, 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 = gnutls_certificate_set_x509_trust_file(state->x509_cred,
+ CS state->exp_tls_verify_certificates, GNUTLS_X509_FMT_PEM);
+if (cert_count < 0)
+ {
+ rc = cert_count;
+ exim_gnutls_err_check(US"gnutls_certificate_set_x509_trust_file");
+ }
+DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n", cert_count);
+
+if (state->tls_crl && *state->tls_crl &&
+ state->exp_tls_crl && *state->exp_tls_crl)
+ {
+ DEBUG(D_tls) debug_printf("loading CRL file = %s\n", state->exp_tls_crl);
+ cert_count = gnutls_certificate_set_x509_crl_file(state->x509_cred,
+ CS state->exp_tls_crl, GNUTLS_X509_FMT_PEM);
+ if (cert_count < 0)
+ {
+ rc = cert_count;
+ exim_gnutls_err_check(US"gnutls_certificate_set_x509_crl_file");
+ }
+ DEBUG(D_tls) debug_printf("Processed %d CRLs.\n", cert_count);
+ }
+
+return OK;
+}
+
+
+
+
+/*************************************************
+* Set X.509 state variables *
+*************************************************/
+
+/* In GnuTLS, the registered cert/key are not replaced by a later
+set of a cert/key, so for SNI support we need a whole new x509_cred
+structure. Which means various other non-re-expanded pieces of state
+need to be re-set in the new struct, so the setting logic is pulled
+out to this.
+
+Arguments:
+ state exim_gnutls_state_st *
+
+Returns: OK/DEFER/FAIL
+*/
+
+static int
+tls_set_remaining_x509(exim_gnutls_state_st *state)
+{
+int rc;
+const host_item *host = state->host; /* macro should be reconsidered? */
+
+/* Create D-H parameters, or read them from the cache file. This function does
+its own SMTP error messaging. This only happens for the server, TLS D-H ignores
+client-side params. */
+
+if (!state->host)
+ {
+ if (!dh_server_params)
+ {
+ rc = init_server_dh();
+ if (rc != OK) return rc;
+ }
+ gnutls_certificate_set_dh_params(state->x509_cred, dh_server_params);
+ }
+
+/* Link the credentials to the session. */
+
+rc = gnutls_credentials_set(state->session, GNUTLS_CRD_CERTIFICATE, state->x509_cred);
+exim_gnutls_err_check(US"gnutls_credentials_set");
+
+return OK;
+}
+
+/*************************************************
+* Initialize for GnuTLS *
+*************************************************/
+
+/* Called from both server and client code. In the case of a server, errors
+before actual TLS negotiation return DEFER.
+
+Arguments:
+ host connected host, if client; NULL if server
+ certificate certificate file
+ privatekey private key file
+ sni TLS SNI to send, sometimes when client; else NULL
+ cas CA certs file
+ crl CRL file
+ require_ciphers tls_require_ciphers setting
+ caller_state returned state-info structure
+
+Returns: OK/DEFER/FAIL
+*/
+
+static int
+tls_init(
+ const host_item *host,
+ const uschar *certificate,
+ const uschar *privatekey,
+ const uschar *sni,
+ const uschar *cas,
+ const uschar *crl,
+ const uschar *require_ciphers,
+ exim_gnutls_state_st **caller_state)
+{
+exim_gnutls_state_st *state;
+int rc;
+size_t sz;
+const char *errpos;
+uschar *p;
+BOOL want_default_priorities;