*/
typedef struct exim_gnutls_state {
- gnutls_session_t session;
+ gnutls_session_t session;
gnutls_certificate_credentials_t x509_cred;
- gnutls_priority_t priority_cache;
+ gnutls_priority_t priority_cache;
enum peer_verify_requirement verify_requirement;
- int fd_in;
- int fd_out;
- BOOL peer_cert_verified;
- BOOL trigger_sni_changes;
- BOOL have_set_peerdn;
+ int fd_in;
+ int fd_out;
+ BOOL peer_cert_verified;
+ BOOL trigger_sni_changes;
+ BOOL have_set_peerdn;
const struct host_item *host;
- uschar *peerdn;
- uschar *ciphersuite;
- uschar *received_sni;
+ gnutls_x509_crt_t peercert;
+ uschar *peerdn;
+ uschar *ciphersuite;
+ uschar *received_sni;
const uschar *tls_certificate;
const uschar *tls_privatekey;
* Set various Exim expansion vars *
*************************************************/
+#define exim_gnutls_cert_err(Label) do { \
+ if (rc != GNUTLS_E_SUCCESS) { \
+ DEBUG(D_tls) debug_printf("TLS: cert problem: %s: %s\n", (Label), gnutls_strerror(rc)); \
+ return rc; } } while (0)
+
+static int
+import_cert(const gnutls_datum * cert, gnutls_x509_crt_t * crtp)
+{
+int rc;
+
+rc = gnutls_x509_crt_init(crtp);
+exim_gnutls_cert_err(US"gnutls_x509_crt_init (crt)");
+
+rc = gnutls_x509_crt_import(*crtp, cert, GNUTLS_X509_FMT_DER);
+exim_gnutls_cert_err(US"failed to import certificate [gnutls_x509_crt_import(cert)]");
+
+return rc;
+}
+
+#undef exim_gnutls_cert_err
+
+
/* We set various Exim global variables from the state, once a session has
been established. With TLS callouts, may need to change this to stack
variables, or just re-call it with the server state after client callout
has finished.
-Make sure anything set here is inset in tls_getc().
+Make sure anything set here is unset in tls_getc().
Sets:
tls_active fd
tls_certificate_verified bool indicator
tls_channelbinding_b64 for some SASL mechanisms
tls_cipher a string
+ tls_peercert pointer to library internal
tls_peerdn a string
tls_sni a (UTF-8) string
+ tls_ourcert pointer to library internal
Argument:
state the relevant exim_gnutls_state_st *
*/
static void
-extract_exim_vars_from_tls_state(exim_gnutls_state_st *state, BOOL is_server)
+extract_exim_vars_from_tls_state(exim_gnutls_state_st * state)
{
gnutls_cipher_algorithm_t cipher;
#ifdef HAVE_GNUTLS_SESSION_CHANNEL_BINDING
int rc;
gnutls_datum_t channel;
#endif
+tls_support * tlsp = state->tlsp;
-state->tlsp->active = state->fd_out;
+tlsp->active = state->fd_out;
cipher = gnutls_cipher_get(state->session);
/* returns size in "bytes" */
-state->tlsp->bits = gnutls_cipher_get_key_size(cipher) * 8;
+tlsp->bits = gnutls_cipher_get_key_size(cipher) * 8;
-state->tlsp->cipher = state->ciphersuite;
+tlsp->cipher = state->ciphersuite;
DEBUG(D_tls) debug_printf("cipher: %s\n", state->ciphersuite);
-state->tlsp->certificate_verified = state->peer_cert_verified;
+tlsp->certificate_verified = state->peer_cert_verified;
/* note that tls_channelbinding_b64 is not saved to the spool file, since it's
only available for use for authenticators while this TLS session is running. */
}
#endif
-state->tlsp->peerdn = state->peerdn;
-state->tlsp->sni = state->received_sni;
+/* peercert is set in peer_status() */
+tlsp->peerdn = state->peerdn;
+tlsp->sni = state->received_sni;
+
+/* record our certificate */
+ {
+ const gnutls_datum * cert = gnutls_certificate_get_ours(state->session);
+ gnutls_x509_crt_t crt;
+
+ tlsp->ourcert = cert && import_cert(cert, &crt)==0 ? crt : NULL;
+ }
}
-
/*************************************************
* Extract peer information *
*************************************************/
if (state->verify_requirement == VERIFY_REQUIRED) { return tls_error((Label), gnutls_strerror(rc), state->host); } \
return OK; } } while (0)
-rc = gnutls_x509_crt_init(&crt);
-exim_gnutls_peer_err(US"gnutls_x509_crt_init (crt)");
+rc = import_cert(&cert_list[0], &crt);
+exim_gnutls_peer_err(US"cert 0");
+
+state->tlsp->peercert = state->peercert = crt;
-rc = gnutls_x509_crt_import(crt, &cert_list[0], GNUTLS_X509_FMT_DER);
-exim_gnutls_peer_err(US"failed to import certificate [gnutls_x509_crt_import(cert 0)]");
sz = 0;
rc = gnutls_x509_crt_get_dn(crt, NULL, &sz);
if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER)
dn_buf = store_get_perm(sz);
rc = gnutls_x509_crt_get_dn(crt, CS dn_buf, &sz);
exim_gnutls_peer_err(US"failed to extract certificate DN [gnutls_x509_crt_get_dn(cert 0)]");
+
state->peerdn = dn_buf;
return OK;
if (!state->tlsp->on_connect)
{
smtp_printf("220 TLS go ahead\r\n");
- fflush(smtp_out); /*XXX JGH */
+ fflush(smtp_out);
}
/* Now negotiate the TLS session. We put our own timer on it, since it seems
/* Verify after the fact */
-if (state->verify_requirement != VERIFY_NONE)
+if ( state->verify_requirement != VERIFY_NONE
+ && !verify_certificate(state, &error))
{
- if (!verify_certificate(state, &error))
+ if (state->verify_requirement != VERIFY_OPTIONAL)
{
- if (state->verify_requirement == VERIFY_OPTIONAL)
- {
- DEBUG(D_tls)
- debug_printf("TLS: continuing on only because verification was optional, after: %s\n",
- error);
- }
- else
- {
- tls_error(US"certificate verification failed", error, NULL);
- return FAIL;
- }
+ tls_error(US"certificate verification failed", error, NULL);
+ return FAIL;
}
+ DEBUG(D_tls)
+ debug_printf("TLS: continuing on only because verification was optional, after: %s\n",
+ error);
}
/* Figure out peer DN, and if authenticated, etc. */
/* Sets various Exim expansion variables; always safe within server */
-extract_exim_vars_from_tls_state(state, TRUE);
+extract_exim_vars_from_tls_state(state);
/* TLS has been set up. Adjust the input functions to read via TLS,
and initialize appropriately. */
}
#ifdef EXPERIMENTAL_OCSP /* since GnuTLS 3.1.3 */
-if (require_ocsp &&
- (rc = gnutls_ocsp_status_request_enable_client(state->session, NULL, 0, NULL))
- != OK)
- return tls_error(US"cert-status-req", gnutls_strerror(rc), state->host);
+if (require_ocsp)
+ {
+ DEBUG(D_tls) debug_printf("TLS: will request OCSP stapling\n");
+ rc = gnutls_ocsp_status_request_enable_client(state->session,
+ NULL, 0, NULL);
+ if (rc != OK)
+ return tls_error(US"cert-status-req",
+ gnutls_strerror(rc), state->host);
+ }
#endif
gnutls_transport_set_ptr(state->session, (gnutls_transport_ptr)fd);
state->fd_in = fd;
state->fd_out = fd;
+DEBUG(D_tls) debug_printf("about to gnutls_handshake\n");
/* There doesn't seem to be a built-in timeout on connection. */
sigalrm_seen = FALSE;
/* Sets various Exim expansion variables; may need to adjust for ACL callouts */
-extract_exim_vars_from_tls_state(state, FALSE);
+extract_exim_vars_from_tls_state(state);
return OK;
}
state->tlsp->active = -1;
state->tlsp->bits = 0;
state->tlsp->certificate_verified = FALSE;
- tls_channelbinding_b64 = NULL; /*XXX JGH */
+ tls_channelbinding_b64 = NULL;
state->tlsp->cipher = NULL;
+ state->tlsp->peercert = NULL;
state->tlsp->peerdn = NULL;
return smtp_getc();