#include <gnutls/x509.h>
/* man-page is incorrect, gnutls_rnd() is not in gnutls.h: */
#include <gnutls/crypto.h>
+
/* needed to disable PKCS11 autoload unless requested */
#if GNUTLS_VERSION_NUMBER >= 0x020c00
# include <gnutls/pkcs11.h>
#if GNUTLS_VERSION_NUMBER >= 0x030014
# define SUPPORT_SYSDEFAULT_CABUNDLE
#endif
+#if GNUTLS_VERSION_NUMBER >= 0x030104
+# define GNUTLS_CERT_VFY_STATUS_PRINT
+#endif
#if GNUTLS_VERSION_NUMBER >= 0x030109
# define SUPPORT_CORK
#endif
static const int ssl_session_timeout = 200;
-static const char * const exim_default_gnutls_priority = "NORMAL";
+static const uschar * const exim_default_gnutls_priority = US"NORMAL";
/* Guard library core initialisation */
#define exim_gnutls_err_check(rc, Label) do { \
if ((rc) != GNUTLS_E_SUCCESS) \
- return tls_error((Label), gnutls_strerror(rc), host, errstr); \
+ return tls_error((Label), US gnutls_strerror(rc), host, errstr); \
} while (0)
#define expand_check_tlsvar(Varname, errstr) \
*/
static int
-tls_error(const uschar *prefix, const char *msg, const host_item *host,
+tls_error(const uschar *prefix, const uschar *msg, const host_item *host,
uschar ** errstr)
{
if (errstr)
- *errstr = string_sprintf("(%s)%s%s", prefix, msg ? ": " : "", msg ? msg : "");
+ *errstr = string_sprintf("(%s)%s%s", prefix, msg ? ": " : "", msg ? msg : US"");
return host ? FAIL : DEFER;
}
static void
record_io_error(exim_gnutls_state_st *state, int rc, uschar *when, uschar *text)
{
-const char * msg;
+const uschar * msg;
uschar * errstr;
if (rc == GNUTLS_E_FATAL_ALERT_RECEIVED)
- msg = CS string_sprintf("%s: %s", US gnutls_strerror(rc),
+ msg = string_sprintf("%s: %s", US gnutls_strerror(rc),
US gnutls_alert_get_name(gnutls_alert_get(state->session)));
else
- msg = gnutls_strerror(rc);
+ msg = US gnutls_strerror(rc);
(void) tls_error(when, msg, state->host, &errstr);
else if (exp_tls_dhparam[0] != '/')
{
if (!(m.data = US std_dh_prime_named(exp_tls_dhparam)))
- return tls_error(US"No standard prime named", CS exp_tls_dhparam, NULL, errstr);
+ return tls_error(US"No standard prime named", exp_tls_dhparam, NULL, errstr);
m.size = Ustrlen(m.data);
}
else
{
saved_errno = errno;
(void)close(fd);
- return tls_error(US"TLS cache stat failed", strerror(saved_errno), NULL, errstr);
+ return tls_error(US"TLS cache stat failed", US strerror(saved_errno), NULL, errstr);
}
if (!S_ISREG(statbuf.st_mode))
{
saved_errno = errno;
(void)close(fd);
return tls_error(US"fdopen(TLS cache stat fd) failed",
- strerror(saved_errno), NULL, errstr);
+ US strerror(saved_errno), NULL, errstr);
}
m.size = statbuf.st_size;
if (!(m.data = malloc(m.size)))
{
fclose(fp);
- return tls_error(US"malloc failed", strerror(errno), NULL, errstr);
+ return tls_error(US"malloc failed", US strerror(errno), NULL, errstr);
}
if (!(sz = fread(m.data, m.size, 1, fp)))
{
saved_errno = errno;
fclose(fp);
free(m.data);
- return tls_error(US"fread failed", strerror(saved_errno), NULL, errstr);
+ return tls_error(US"fread failed", US strerror(saved_errno), NULL, errstr);
}
fclose(fp);
if ((PATH_MAX - Ustrlen(filename)) < 10)
return tls_error(US"Filename too long to generate replacement",
- CS filename, NULL, errstr);
+ filename, NULL, errstr);
- temp_fn = string_copy(US "%s.XXXXXXX");
+ temp_fn = string_copy(US"%s.XXXXXXX");
if ((fd = mkstemp(CS temp_fn)) < 0) /* modifies temp_fn */
- return tls_error(US"Unable to open temp file", strerror(errno), NULL, errstr);
+ return tls_error(US"Unable to open temp file", US strerror(errno), NULL, errstr);
(void)fchown(fd, exim_uid, exim_gid); /* Probably not necessary */
/* GnuTLS overshoots!
exim_gnutls_err_check(rc, US"gnutls_dh_params_export_pkcs3(NULL) sizing");
m.size = sz;
if (!(m.data = malloc(m.size)))
- return tls_error(US"memory allocation failed", strerror(errno), NULL, errstr);
+ return tls_error(US"memory allocation failed", US strerror(errno), NULL, errstr);
/* this will return a size 1 less than the allocation size above */
rc = gnutls_dh_params_export_pkcs3(dh_server_params, GNUTLS_X509_FMT_PEM,
{
free(m.data);
return tls_error(US"TLS cache write D-H params failed",
- strerror(errno), NULL, errstr);
+ US strerror(errno), NULL, errstr);
}
free(m.data);
if ((sz = write_to_fd_buf(fd, US"\n", 1)) != 1)
return tls_error(US"TLS cache write D-H params final newline failed",
- strerror(errno), NULL, errstr);
+ US strerror(errno), NULL, errstr);
if ((rc = close(fd)))
- return tls_error(US"TLS cache write close() failed", strerror(errno), NULL, errstr);
+ return tls_error(US"TLS cache write close() failed", US strerror(errno), NULL, errstr);
if (Urename(temp_fn, filename) < 0)
return tls_error(string_sprintf("failed to rename \"%s\" as \"%s\"",
- temp_fn, filename), strerror(errno), NULL, errstr);
+ temp_fn, filename), US strerror(errno), NULL, errstr);
DEBUG(D_tls) debug_printf("wrote D-H parameters to file \"%s\"\n", filename);
}
where = US"generating pkey";
if ((rc = gnutls_x509_privkey_generate(pkey, GNUTLS_PK_RSA,
#ifdef SUPPORT_PARAM_TO_PK_BITS
- gnutls_sec_param_to_pk_bits(GNUTLS_PK_RSA, GNUTLS_SEC_PARAM_LOW),
+# ifndef GNUTLS_SEC_PARAM_MEDIUM
+# define GNUTLS_SEC_PARAM_MEDIUM GNUTLS_SEC_PARAM_HIGH
+# endif
+ gnutls_sec_param_to_pk_bits(GNUTLS_PK_RSA, GNUTLS_SEC_PARAM_MEDIUM),
#else
- 1024,
+ 2048,
#endif
0)))
goto err;
return rc;
err:
- rc = tls_error(where, gnutls_strerror(rc), NULL, errstr);
+ rc = tls_error(where, US gnutls_strerror(rc), NULL, errstr);
goto out;
}
if (rc < 0)
return tls_error(
string_sprintf("cert/key setup: cert=%s key=%s", certfile, keyfile),
- gnutls_strerror(rc), host, errstr);
+ US gnutls_strerror(rc), host, errstr);
return -rc;
}
size_t sz;
const char *errpos;
uschar *p;
-BOOL want_default_priorities;
if (!exim_gnutls_base_init_done)
{
DEBUG(D_tls)
{
gnutls_global_set_log_function(exim_gnutls_logger_cb);
- /* arbitrarily chosen level; bump upto 9 for more */
+ /* arbitrarily chosen level; bump up to 9 for more */
gnutls_global_set_log_level(EXIM_GNUTLS_LIBRARY_LOG_LEVEL);
}
#endif
This was backwards incompatible, but means Exim no longer needs to track
all algorithms and provide string forms for them. */
-want_default_priorities = TRUE;
-
+p = NULL;
if (state->tls_require_ciphers && *state->tls_require_ciphers)
{
if (!expand_check_tlsvar(tls_require_ciphers, errstr))
return DEFER;
if (state->exp_tls_require_ciphers && *state->exp_tls_require_ciphers)
{
- DEBUG(D_tls) debug_printf("GnuTLS session cipher/priority \"%s\"\n",
- state->exp_tls_require_ciphers);
-
- rc = gnutls_priority_init(&state->priority_cache,
- CS state->exp_tls_require_ciphers, &errpos);
- want_default_priorities = FALSE;
p = state->exp_tls_require_ciphers;
+ DEBUG(D_tls) debug_printf("GnuTLS session cipher/priority \"%s\"\n", p);
}
}
-if (want_default_priorities)
+if (!p)
{
+ p = exim_default_gnutls_priority;
DEBUG(D_tls)
- debug_printf("GnuTLS using default session cipher/priority \"%s\"\n",
- exim_default_gnutls_priority);
- rc = gnutls_priority_init(&state->priority_cache,
- exim_default_gnutls_priority, &errpos);
- p = US exim_default_gnutls_priority;
+ debug_printf("GnuTLS using default session cipher/priority \"%s\"\n", p);
}
+rc = gnutls_priority_init(&state->priority_cache, CCS p, &errpos);
exim_gnutls_err_check(rc, string_sprintf(
"gnutls_priority_init(%s) failed at offset %ld, \"%.6s..\"",
cert_list, cert_list_size);
if (state->verify_requirement >= VERIFY_REQUIRED)
return tls_error(US"certificate verification failed",
- "no certificate received from peer", state->host, errstr);
+ US"no certificate received from peer", state->host, errstr);
return OK;
}
ct = gnutls_certificate_type_get(state->session);
if (ct != GNUTLS_CRT_X509)
{
- const char *ctn = gnutls_certificate_type_get_name(ct);
+ const uschar *ctn = US gnutls_certificate_type_get_name(ct);
DEBUG(D_tls)
debug_printf("TLS: peer cert not X.509 but instead \"%s\"\n", ctn);
if (state->verify_requirement >= VERIFY_REQUIRED)
DEBUG(D_tls) debug_printf("TLS: peer cert problem: %s: %s\n", \
(Label), gnutls_strerror(rc)); \
if (state->verify_requirement >= VERIFY_REQUIRED) \
- return tls_error((Label), gnutls_strerror(rc), state->host, errstr); \
+ return tls_error((Label), US gnutls_strerror(rc), state->host, errstr); \
return OK; \
} \
} while (0)
{
state->peer_cert_verified = FALSE;
if (!*errstr)
+ {
+#ifdef GNUTLS_CERT_VFY_STATUS_PRINT
+ DEBUG(D_tls)
+ {
+ gnutls_datum_t txt;
+
+ if (gnutls_certificate_verification_status_print(verify,
+ gnutls_certificate_type_get(state->session), &txt, 0)
+ == GNUTLS_E_SUCCESS)
+ {
+ debug_printf("%s\n", txt.data);
+ gnutls_free(txt.data);
+ }
+ }
+#endif
*errstr = verify & GNUTLS_CERT_REVOKED
? US"certificate revoked" : US"certificate invalid";
+ }
DEBUG(D_tls)
debug_printf("TLS certificate verification failed (%s): peerdn=\"%s\"\n",
/* Check for previous activation */
if (tls_in.active.sock >= 0)
{
- tls_error(US"STARTTLS received after TLS started", "", NULL, errstr);
+ tls_error(US"STARTTLS received after TLS started", US "", NULL, errstr);
smtp_printf("554 Already in TLS\r\n", FALSE);
return FAIL;
}
state->fd_out = fileno(smtp_out);
sigalrm_seen = FALSE;
-if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout);
+if (smtp_receive_timeout > 0) ALARM(smtp_receive_timeout);
do
rc = gnutls_handshake(state->session);
while (rc == GNUTLS_E_AGAIN || rc == GNUTLS_E_INTERRUPTED && !sigalrm_seen);
-alarm(0);
+ALARM_CLR(0);
if (rc != GNUTLS_E_SUCCESS)
{
if (sigalrm_seen)
{
- tls_error(US"gnutls_handshake", "timed out", NULL, errstr);
+ tls_error(US"gnutls_handshake", US"timed out", NULL, errstr);
gnutls_db_remove_session(state->session);
}
else
{
- tls_error(US"gnutls_handshake", gnutls_strerror(rc), NULL, errstr);
+ tls_error(US"gnutls_handshake", US gnutls_strerror(rc), NULL, errstr);
(void) gnutls_alert_send_appropriate(state->session, rc);
gnutls_deinit(state->session);
gnutls_certificate_free_credentials(state->x509_cred);
}
tls_out.tlsa_usage |= 1<<usage;
- dane_data[i] = p;
+ dane_data[i] = CS p;
dane_data_len[i++] = rr->size;
}
if ((rc = gnutls_ocsp_status_request_enable_client(state->session,
NULL, 0, NULL)) != OK)
{
- tls_error(US"cert-status-req", gnutls_strerror(rc), state->host, errstr);
+ tls_error(US"cert-status-req", US gnutls_strerror(rc), state->host, errstr);
return NULL;
}
tlsp->ocsp = OCSP_NOT_RESP;
/* There doesn't seem to be a built-in timeout on connection. */
sigalrm_seen = FALSE;
-alarm(ob->command_timeout);
+ALARM(ob->command_timeout);
do
rc = gnutls_handshake(state->session);
while (rc == GNUTLS_E_AGAIN || rc == GNUTLS_E_INTERRUPTED && !sigalrm_seen);
-alarm(0);
+ALARM_CLR(0);
if (rc != GNUTLS_E_SUCCESS)
{
if (sigalrm_seen)
{
gnutls_alert_send(state->session, GNUTLS_AL_FATAL, GNUTLS_A_USER_CANCELED);
- tls_error(US"gnutls_handshake", "timed out", state->host, errstr);
+ tls_error(US"gnutls_handshake", US"timed out", state->host, errstr);
}
else
- tls_error(US"gnutls_handshake", gnutls_strerror(rc), state->host, errstr);
+ tls_error(US"gnutls_handshake", US gnutls_strerror(rc), state->host, errstr);
return NULL;
}
gnutls_free(printed.data);
}
else
- (void) tls_error(US"ocsp decode", gnutls_strerror(rc), state->host, errstr);
+ (void) tls_error(US"ocsp decode", US gnutls_strerror(rc), state->host, errstr);
}
if (gnutls_ocsp_status_request_is_checked(state->session, 0) == 0)
DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS%s\n",
shutdown > 1 ? " (with response-wait)" : "");
- alarm(2);
+ ALARM(2);
gnutls_bye(state->session, shutdown > 1 ? GNUTLS_SHUT_RDWR : GNUTLS_SHUT_WR);
- alarm(0);
+ ALARM_CLR(0);
}
gnutls_deinit(state->session);
state->session, state->xfer_buffer, ssl_xfer_buffer_size);
sigalrm_seen = FALSE;
-if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout);
-inbytes = gnutls_record_recv(state->session, state->xfer_buffer,
- MIN(ssl_xfer_buffer_size, lim));
-if (smtp_receive_timeout > 0) alarm(0);
+if (smtp_receive_timeout > 0) ALARM(smtp_receive_timeout);
+
+do
+ inbytes = gnutls_record_recv(state->session, state->xfer_buffer,
+ MIN(ssl_xfer_buffer_size, lim));
+while (inbytes == GNUTLS_E_AGAIN);
+
+if (smtp_receive_timeout > 0) ALARM_CLR(0);
if (had_command_timeout) /* set by signal handler */
smtp_command_timeout_exit(); /* does not return */
else if (inbytes < 0)
{
-debug_printf("%s: err from gnutls_record_recv(\n", __FUNCTION__);
+ DEBUG(D_tls) debug_printf("%s: err from gnutls_record_recv(\n", __FUNCTION__);
record_io_error(state, (int) inbytes, US"recv", NULL);
state->xfer_error = TRUE;
return FALSE;
This feeds DKIM and should be used for all message-body reads.
-Arguments: lim Maximum amount to read/bufffer
+Arguments: lim Maximum amount to read/buffer
Returns: the next character or EOF
*/
debug_printf("Calling gnutls_record_recv(%p, %p, " SIZE_T_FMT ")\n",
state->session, buff, len);
-inbytes = gnutls_record_recv(state->session, buff, len);
+do
+ inbytes = gnutls_record_recv(state->session, buff, len);
+while (inbytes == GNUTLS_E_AGAIN);
+
if (inbytes > 0) return inbytes;
if (inbytes == 0)
{
DEBUG(D_tls) debug_printf("Got TLS_EOF\n");
}
else
-{
-debug_printf("%s: err from gnutls_record_recv(\n", __FUNCTION__);
-record_io_error(state, (int)inbytes, US"recv", NULL);
-}
+ {
+ DEBUG(D_tls) debug_printf("%s: err from gnutls_record_recv(\n", __FUNCTION__);
+ record_io_error(state, (int)inbytes, US"recv", NULL);
+ }
return -1;
}
{
DEBUG(D_tls) debug_printf("gnutls_record_send(SSL, %p, " SIZE_T_FMT ")\n",
buff, left);
- outbytes = gnutls_record_send(state->session, buff, left);
+
+ do
+ outbytes = gnutls_record_send(state->session, buff, left);
+ while (outbytes == GNUTLS_E_AGAIN);
DEBUG(D_tls) debug_printf("outbytes=" SSIZE_T_FMT "\n", outbytes);
if (outbytes < 0)