BOOL have_set_peerdn;
const struct host_item *host;
uschar *peerdn;
+ uschar *ciphersuite;
uschar *received_sni;
const uschar *tls_certificate;
int xfer_buffer_hwm;
int xfer_eof;
int xfer_error;
-
- uschar cipherbuf[256];
} exim_gnutls_state_st;
static const exim_gnutls_state_st exim_gnutls_state_init = {
NULL, NULL, NULL, VERIFY_NONE, -1, -1, FALSE, FALSE, FALSE,
- NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, 0, 0, 0, 0,
- ""
};
/* Not only do we have our own APIs which don't pass around state, assuming
/* returns size in "bytes" */
tls_bits = gnutls_cipher_get_key_size(cipher) * 8;
-tls_cipher = state->cipherbuf;
+tls_cipher = state->ciphersuite;
DEBUG(D_tls) debug_printf("cipher: %s\n", tls_cipher);
{
if (!state->received_sni)
{
- if (Ustrstr(state->tls_certificate, US"tls_sni"))
+ if (state->tls_certificate && Ustrstr(state->tls_certificate, US"tls_sni"))
{
DEBUG(D_tls) debug_printf("We will re-expand TLS session files if we receive SNI.\n");
state->trigger_sni_changes = TRUE;
return DEFER;
}
-if (!S_ISREG(statbuf.st_mode))
+/* 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 not a file: \"%s\"\n%s\n",
- state->exp_tls_verify_certificates,
- S_ISDIR(statbuf.st_mode)
- ? " it's a directory, that's OpenSSL, this is GnuTLS"
- : " (not a directory either)");
+ 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 not a file",
+ "tls_verify_certificates \"%s\" is a directory",
state->exp_tls_verify_certificates);
return DEFER;
}
}
DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n", cert_count);
-if (state->tls_crl && *state->tls_crl)
+if (state->tls_crl && *state->tls_crl &&
+ state->exp_tls_crl && *state->exp_tls_crl)
{
- if (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)
{
- DEBUG(D_tls) debug_printf("loading CRL file = %s\n", state->exp_tls_crl);
- rc = gnutls_certificate_set_x509_crl_file(state->x509_cred,
- CS state->exp_tls_crl, GNUTLS_X509_FMT_PEM);
+ 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;
state->tls_certificate = certificate;
state->tls_privatekey = privatekey;
+state->tls_require_ciphers = require_ciphers;
state->tls_sni = sni;
state->tls_verify_certificates = cas;
state->tls_crl = crl;
Only this is allowed to set state->peerdn and state->have_set_peerdn
and we use that to detect double-calls.
+NOTE: the state blocks last while the TLS connection is up, which is fine
+for logging in the server side, but for the client side, we log after teardown
+in src/deliver.c. While the session is up, we can twist about states and
+repoint tls_* globals, but those variables used for logging or other variable
+expansion that happens _after_ delivery need to have a longer life-time.
+
+So for those, we get the data from POOL_PERM; the re-invoke guard keeps us from
+doing this more than once per generation of a state context. We set them in
+the state context, and repoint tls_* to them. After the state goes away, the
+tls_* copies of the pointers remain valid and client delivery logging is happy.
+
+tls_certificate_verified is a BOOL, so the tls_peerdn and tls_cipher issues
+don't apply.
+
Arguments:
state exim_gnutls_state_st *
static int
peer_status(exim_gnutls_state_st *state)
{
+uschar cipherbuf[256];
const gnutls_datum *cert_list;
-int rc;
+int old_pool, rc;
unsigned int cert_list_size = 0;
gnutls_protocol_t protocol;
gnutls_cipher_algorithm_t cipher;
mac = gnutls_mac_get(state->session);
kx = gnutls_kx_get(state->session);
-string_format(state->cipherbuf, sizeof(state->cipherbuf),
+string_format(cipherbuf, sizeof(cipherbuf),
"%s:%s:%d",
gnutls_protocol_get_name(protocol),
gnutls_cipher_suite_get_name(kx, cipher, mac),
/* I don't see a way that spaces could occur, in the current GnuTLS
code base, but it was a concern in the old code and perhaps older GnuTLS
releases did return "TLS 1.0"; play it safe, just in case. */
-for (p = state->cipherbuf; *p != '\0'; ++p)
+for (p = cipherbuf; *p != '\0'; ++p)
if (isspace(*p))
*p = '-';
+old_pool = store_pool;
+store_pool = POOL_PERM;
+state->ciphersuite = string_copy(cipherbuf);
+store_pool = old_pool;
+tls_cipher = state->ciphersuite;
/* tls_peerdn */
cert_list = gnutls_certificate_get_peers(state->session, &cert_list_size);
static void
exim_gnutls_logger_cb(int level, const char *message)
{
- DEBUG(D_tls) debug_printf("GnuTLS<%d>: %s\n", level, message);
+ size_t len = strlen(message);
+ if (len < 1)
+ {
+ DEBUG(D_tls) debug_printf("GnuTLS<%d> empty debug message\n", level);
+ return;
+ }
+ DEBUG(D_tls) debug_printf("GnuTLS<%d>: %s%s", level, message,
+ message[len-1] == '\n' ? "" : "\n");
}
#endif
do
{
rc = gnutls_handshake(state->session);
- } while ((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED));
+ } while ((rc == GNUTLS_E_AGAIN) ||
+ (rc == GNUTLS_E_INTERRUPTED && !sigalrm_seen));
alarm(0);
if (rc != GNUTLS_E_SUCCESS)
do
{
rc = gnutls_handshake(state->session);
- } while ((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED));
+ } while ((rc == GNUTLS_E_AGAIN) ||
+ (rc == GNUTLS_E_INTERRUPTED && !sigalrm_seen));
alarm(0);
if (rc != GNUTLS_E_SUCCESS)