X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Ftls-gnu.c;h=c26a9bac6b551677acc7f1a12b91f0465a0652cf;hp=34b03e68565a7a5b8cd7a98e6a841a81029adc71;hb=36f12725ebda2bfd6ed4fe98b0eeaf1ce01f2604;hpb=87054a3157ebc73881a49adfa3aec755babd2052 diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 34b03e685..c26a9bac6 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -1,10 +1,10 @@ -/* $Cambridge: exim/src/src/tls-gnu.c,v 1.15 2006/12/04 15:15:00 ph10 Exp $ */ +/* $Cambridge: exim/src/src/tls-gnu.c,v 1.22 2009/10/14 13:52:48 nm4 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2006 */ +/* Copyright (c) University of Cambridge 1995 - 2007 */ /* See the file NOTICE for conditions of use and distribution. */ /* This module provides TLS (aka SSL) support for Exim using the GnuTLS @@ -26,14 +26,12 @@ functions from the GnuTLS library. */ #define PARAM_SIZE 2*1024 -/* Values for verify_requirment and initialized */ +/* Values for verify_requirment */ enum { VERIFY_NONE, VERIFY_OPTIONAL, VERIFY_REQUIRED }; -enum { INITIALIZED_NOT, INITIALIZED_SERVER, INITIALIZED_CLIENT }; /* Local static variables for GNUTLS */ -static BOOL initialized = INITIALIZED_NOT; static host_item *client_host; static gnutls_dh_params dh_params = NULL; @@ -46,17 +44,24 @@ static char ssl_errstring[256]; static int ssl_session_timeout = 200; static int verify_requirement; -/* Priorities for TLS algorithms to use. At present, only the cipher priority -vector can be altered. */ +/* Priorities for TLS algorithms to use. In each case there's a default table, +and space into which it can be copied and altered. */ -static const int protocol_priority[16] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 }; +static const int default_proto_priority[16] = { + GNUTLS_TLS1, + GNUTLS_SSL3, + 0 }; + +static int proto_priority[16]; -static const int kx_priority[16] = { +static const int default_kx_priority[16] = { GNUTLS_KX_RSA, GNUTLS_KX_DHE_DSS, GNUTLS_KX_DHE_RSA, 0 }; +static int kx_priority[16]; + static int default_cipher_priority[16] = { GNUTLS_CIPHER_AES_256_CBC, GNUTLS_CIPHER_AES_128_CBC, @@ -66,21 +71,50 @@ static int default_cipher_priority[16] = { static int cipher_priority[16]; -static const int mac_priority[16] = { +static const int default_mac_priority[16] = { GNUTLS_MAC_SHA, GNUTLS_MAC_MD5, 0 }; +static int mac_priority[16]; + +/* These two are currently not changeable. */ + static const int comp_priority[16] = { GNUTLS_COMP_NULL, 0 }; static const int cert_type_priority[16] = { GNUTLS_CRT_X509, 0 }; -/* Tables of cipher names and equivalent numbers */ +/* Tables of priority names and equivalent numbers */ typedef struct pri_item { uschar *name; int *values; } pri_item; + +static int tls1_codes[] = { GNUTLS_TLS1, 0 }; +static int ssl3_codes[] = { GNUTLS_SSL3, 0 }; + +static pri_item proto_index[] = { + { US"TLS1", tls1_codes }, + { US"SSL3", ssl3_codes } +}; + + +static int kx_rsa_codes[] = { GNUTLS_KX_RSA, + GNUTLS_KX_DHE_RSA, 0 }; +static int kx_dhe_codes[] = { GNUTLS_KX_DHE_DSS, + GNUTLS_KX_DHE_RSA, 0 }; +static int kx_dhe_dss_codes[] = { GNUTLS_KX_DHE_DSS, 0 }; +static int kx_dhe_rsa_codes[] = { GNUTLS_KX_DHE_RSA, 0 }; + +static pri_item kx_index[] = { + { US"DHE_DSS", kx_dhe_dss_codes }, + { US"DHE_RSA", kx_dhe_rsa_codes }, + { US"RSA", kx_rsa_codes }, + { US"DHE", kx_dhe_codes } +}; + + static int arcfour_128_codes[] = { GNUTLS_CIPHER_ARCFOUR_128, 0 }; static int arcfour_40_codes[] = { GNUTLS_CIPHER_ARCFOUR_40, 0 }; static int arcfour_codes[] = { GNUTLS_CIPHER_ARCFOUR_128, @@ -102,6 +136,16 @@ static pri_item cipher_index[] = { }; +static int mac_sha_codes[] = { GNUTLS_MAC_SHA, 0 }; +static int mac_md5_codes[] = { GNUTLS_MAC_MD5, 0 }; + +static pri_item mac_index[] = { + { US"SHA", mac_sha_codes }, + { US"SHA1", mac_sha_codes }, + { US"MD5", mac_md5_codes } +}; + + /************************************************* * Handle TLS error * @@ -118,27 +162,28 @@ Argument: prefix text to include in the logged error host NULL if setting up a server; the connected host if setting up a client - err a GnuTLS error number, or 0 if local error + msg additional error string (may be NULL) + usually obtained from gnutls_strerror() Returns: OK/DEFER/FAIL */ static int -tls_error(uschar *prefix, host_item *host, int err) +tls_error(uschar *prefix, host_item *host, const char *msg) { -uschar *errtext = US""; -if (err != 0) errtext = string_sprintf(": %s", gnutls_strerror(err)); if (host == NULL) { - log_write(0, LOG_MAIN, "TLS error on connection from %s (%s)%s", - (sender_fullhost != NULL)? sender_fullhost : US "local process", - prefix, errtext); + uschar *conn_info = smtp_get_connection_info(); + if (strncmp(conn_info, "SMTP ", 5) == 0) + conn_info += 5; + log_write(0, LOG_MAIN, "TLS error on %s (%s)%s%s", + conn_info, prefix, msg ? ": " : "", msg ? msg : ""); return DEFER; } else { - log_write(0, LOG_MAIN, "TLS error on connection to %s [%s] (%s)%s", - host->name, host->address, prefix, errtext); + log_write(0, LOG_MAIN, "TLS error on connection to %s [%s] (%s)%s%s", + host->name, host->address, prefix, msg ? ": " : "", msg ? msg : ""); return FAIL; } } @@ -160,7 +205,7 @@ Returns: TRUE/FALSE */ static BOOL -verify_certificate(gnutls_session session, uschar **error) +verify_certificate(gnutls_session session, const char **error) { int verify; uschar *dn_string = US""; @@ -195,7 +240,7 @@ else { DEBUG(D_tls) debug_printf("no peer certificate supplied\n"); verify = GNUTLS_CERT_INVALID; - *error = US"not supplied"; + *error = "not supplied"; } /* Handle the result of verification. INVALID seems to be set as well @@ -205,7 +250,7 @@ if ((verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED)) != 0) { tls_certificate_verified = FALSE; if (*error == NULL) *error = ((verify & GNUTLS_CERT_REVOKED) != 0)? - US"revoked" : US"invalid"; + "revoked" : "invalid"; if (verify_requirement == VERIFY_REQUIRED) { DEBUG(D_tls) debug_printf("TLS certificate verification failed (%s): " @@ -260,13 +305,13 @@ uschar filename[200]; /* Initialize the data structures for holding the parameters */ ret = gnutls_dh_params_init(&dh_params); -if (ret < 0) return tls_error(US"init dh_params", host, ret); +if (ret < 0) return tls_error(US"init dh_params", host, gnutls_strerror(ret)); /* Set up the name of the cache file */ if (!string_format(filename, sizeof(filename), "%s/gnutls-params", spool_directory)) - return tls_error(US"overlong filename", host, 0); + return tls_error(US"overlong filename", host, NULL); /* Open the cache file for reading and if successful, read it and set up the parameters. */ @@ -278,19 +323,21 @@ if (fd >= 0) if (fstat(fd, &statbuf) < 0) { (void)close(fd); - return tls_error(US"TLS cache stat failed", host, 0); + return tls_error(US"TLS cache stat failed", host, strerror(errno)); } m.size = statbuf.st_size; m.data = malloc(m.size); if (m.data == NULL) - return tls_error(US"memory allocation failed", host, 0); + return tls_error(US"memory allocation failed", host, strerror(errno)); + errno = 0; if (read(fd, m.data, m.size) != m.size) - return tls_error(US"TLS cache read failed", host, 0); + return tls_error(US"TLS cache read failed", host, strerror(errno)); (void)close(fd); ret = gnutls_dh_params_import_pkcs3(dh_params, &m, GNUTLS_X509_FMT_PEM); - if (ret < 0) return tls_error(US"DH params import", host, ret); + if (ret < 0) + return tls_error(US"DH params import", host, gnutls_strerror(ret)); DEBUG(D_tls) debug_printf("read D-H parameters from file\n"); free(m.data); @@ -307,7 +354,7 @@ else if (errno == ENOENT) } else return tls_error(string_open_failed(errno, "%s for reading", filename), - host, 0); + host, NULL); /* If ret < 0, either the cache file does not exist, or the data it contains is not useful. One particular case of this is when upgrading from an older @@ -322,7 +369,7 @@ if (ret < 0) DEBUG(D_tls) debug_printf("generating %d bit Diffie-Hellman key...\n", DH_BITS); ret = gnutls_dh_params_generate2(dh_params, DH_BITS); - if (ret < 0) return tls_error(US"D-H key generation", host, ret); + if (ret < 0) return tls_error(US"D-H key generation", host, gnutls_strerror(ret)); /* Write the parameters to a file in the spool directory so that we can use them from other Exim processes. */ @@ -331,7 +378,7 @@ if (ret < 0) fd = Uopen(tempfilename, O_WRONLY|O_CREAT, 0400); if (fd < 0) return tls_error(string_open_failed(errno, "%s for writing", filename), - host, 0); + host, NULL); (void)fchown(fd, exim_uid, exim_gid); /* Probably not necessary */ /* export the parameters in a format that can be generated using GNUTLS' @@ -344,23 +391,25 @@ if (ret < 0) m.size = PARAM_SIZE; m.data = malloc(m.size); if (m.data == NULL) - return tls_error(US"memory allocation failed", host, 0); + return tls_error(US"memory allocation failed", host, strerror(errno)); m.size = PARAM_SIZE; ret = gnutls_dh_params_export_pkcs3(dh_params, GNUTLS_X509_FMT_PEM, m.data, &m.size); - if (ret < 0) return tls_error(US"DH params export", host, ret); + if (ret < 0) + return tls_error(US"DH params export", host, gnutls_strerror(ret)); m.size = Ustrlen(m.data); + errno = 0; if (write(fd, m.data, m.size) != m.size || write(fd, "\n", 1) != 1) - return tls_error(US"TLS cache write failed", host, 0); + return tls_error(US"TLS cache write failed", host, strerror(errno)); free(m.data); (void)close(fd); if (rename(CS tempfilename, CS filename) < 0) - return tls_error(string_sprintf("failed to rename %s as %s: %s", - tempfilename, filename, strerror(errno)), host, 0); + return tls_error(string_sprintf("failed to rename %s as %s", + tempfilename, filename), host, strerror(errno)); DEBUG(D_tls) debug_printf("wrote D-H parameters to file %s\n", filename); } @@ -396,10 +445,10 @@ tls_init(host_item *host, uschar *certificate, uschar *privatekey, uschar *cas, int rc; uschar *cert_expanded, *key_expanded, *cas_expanded, *crl_expanded; -initialized = (host == NULL)? INITIALIZED_SERVER : INITIALIZED_CLIENT; +client_host = host; rc = gnutls_global_init(); -if (rc < 0) return tls_error(US"tls-init", host, rc); +if (rc < 0) return tls_error(US"tls-init", host, gnutls_strerror(rc)); /* Create D-H parameters, or read them from the cache file. This function does its own SMTP error messaging. */ @@ -410,7 +459,9 @@ if (rc != OK) return rc; /* Create the credentials structure */ rc = gnutls_certificate_allocate_credentials(&x509_cred); -if (rc < 0) return tls_error(US"certificate_allocate_credentials", host, rc); +if (rc < 0) + return tls_error(US"certificate_allocate_credentials", + host, gnutls_strerror(rc)); /* This stuff must be done for each session, because different certificates may be required for different sessions. */ @@ -444,7 +495,7 @@ if (cert_expanded != NULL) { uschar *msg = string_sprintf("cert/key setup: cert=%s key=%s", cert_expanded, key_expanded); - return tls_error(msg, host, rc); + return tls_error(msg, host, gnutls_strerror(rc)); } } @@ -453,7 +504,7 @@ if (cert_expanded != NULL) else { if (host == NULL) - return tls_error(US"no TLS server certificate is specified", host, 0); + return tls_error(US"no TLS server certificate is specified", NULL, NULL); DEBUG(D_tls) debug_printf("no TLS client certificate is specified\n"); } @@ -486,7 +537,7 @@ if (cas != NULL) { rc = gnutls_certificate_set_x509_trust_file(x509_cred, CS cas_expanded, GNUTLS_X509_FMT_PEM); - if (rc < 0) return tls_error(US"setup_certs", host, rc); + if (rc < 0) return tls_error(US"setup_certs", host, gnutls_strerror(rc)); if (crl != NULL && *crl != 0) { @@ -495,7 +546,7 @@ if (cas != NULL) DEBUG(D_tls) debug_printf("loading CRL file = %s\n", crl_expanded); rc = gnutls_certificate_set_x509_crl_file(x509_cred, CS crl_expanded, GNUTLS_X509_FMT_PEM); - if (rc < 0) return tls_error(US"CRL setup", host, rc); + if (rc < 0) return tls_error(US"CRL setup", host, gnutls_strerror(rc)); } } } @@ -512,7 +563,7 @@ return OK; /************************************************* -* Remove ciphers from priority list * +* Remove from a priority list * *************************************************/ /* Cautiously written so that it will remove duplicates if present. @@ -525,7 +576,7 @@ Returns: nothing */ static void -remove_ciphers(int *list, int *remove_list) +remove_priority(int *list, int *remove_list) { for (; *remove_list != 0; remove_list++) { @@ -545,7 +596,7 @@ for (; *remove_list != 0; remove_list++) /************************************************* -* Add ciphers to priority list * +* Add to a priority list * *************************************************/ /* Cautiously written to check the list size @@ -559,7 +610,7 @@ Returns: TRUE if OK; FALSE if list overflows */ static BOOL -add_ciphers(int *list, int list_max, int *add_list) +add_priority(int *list, int list_max, int *add_list) { int next = 0; while (list[next] != 0) next++; @@ -574,6 +625,78 @@ return TRUE; +/************************************************* +* Adjust a priority list * +*************************************************/ + +/* This function is called to adjust the lists of cipher algorithms, MAC +algorithms, key-exchange methods, and protocols. + +Arguments: + plist the appropriate priority list + psize the length of the list + s the configuation string + index the index of recognized strings + isize the length of the index + + + which text for an error message + +Returns: FALSE if the table overflows, else TRUE +*/ + +static BOOL +set_priority(int *plist, int psize, uschar *s, pri_item *index, int isize, + uschar *which) +{ +int sep = 0; +BOOL first = TRUE; +uschar *t; + +while ((t = string_nextinlist(&s, &sep, big_buffer, big_buffer_size)) != NULL) + { + int i; + BOOL exclude = t[0] == '!'; + if (first && !exclude) plist[0] = 0; + first = FALSE; + for (i = 0; i < isize; i++) + { + uschar *ss = strstric(t, index[i].name, FALSE); + if (ss != NULL) + { + uschar *endss = ss + Ustrlen(index[i].name); + if ((ss == t || !isalnum(ss[-1])) && !isalnum(*endss)) + { + if (exclude) + remove_priority(plist, index[i].values); + else + { + if (!add_priority(plist, psize, index[i].values)) + { + log_write(0, LOG_MAIN|LOG_PANIC, "GnuTLS init failed: %s " + "priority table overflow", which); + return FALSE; + } + } + } + } + } + } + +DEBUG(D_tls) + { + int *ptr = plist; + debug_printf("adjusted %s priorities:", which); + while (*ptr != 0) debug_printf(" %d", *ptr++); + debug_printf("\n"); + } + +return TRUE; +} + + + + /************************************************* * Initialize a single GNUTLS session * *************************************************/ @@ -591,78 +714,60 @@ cipher. Arguments: side one of GNUTLS_SERVER, GNUTLS_CLIENT - expciphers expanded ciphers list + expciphers expanded ciphers list or NULL + expmac expanded MAC list or NULL + expkx expanded key-exchange list or NULL + expproto expanded protocol list or NULL Returns: a gnutls_session, or NULL if there is a problem */ static gnutls_session -tls_session_init(int side, uschar *expciphers) +tls_session_init(int side, uschar *expciphers, uschar *expmac, uschar *expkx, + uschar *expproto) { gnutls_session session; gnutls_init(&session, side); -/* Handle the list of permitted ciphers */ +/* Initialize the lists of permitted protocols, key-exchange methods, ciphers, +and MACs. */ memcpy(cipher_priority, default_cipher_priority, sizeof(cipher_priority)); +memcpy(mac_priority, default_mac_priority, sizeof(mac_priority)); +memcpy(kx_priority, default_kx_priority, sizeof(kx_priority)); +memcpy(proto_priority, default_proto_priority, sizeof(proto_priority)); + +/* The names OpenSSL uses in tls_require_ciphers are of the form DES-CBC3-SHA, +using hyphen separators. GnuTLS uses underscore separators. So that I can use +either form for tls_require_ciphers in my tests, and also for general +convenience, we turn hyphens into underscores before scanning the list. */ if (expciphers != NULL) { - int sep = 0; - BOOL first = TRUE; - uschar *cipher; - - /* The names OpenSSL uses are of the form DES-CBC3-SHA, using hyphen - separators. GnuTLS uses underscore separators. So that I can use either form - in my tests, and also for general convenience, we turn hyphens into - underscores before scanning the list. */ - uschar *s = expciphers; while (*s != 0) { if (*s == '-') *s = '_'; s++; } + } - while ((cipher = string_nextinlist(&expciphers, &sep, big_buffer, - big_buffer_size)) != NULL) - { - int i; - BOOL exclude = cipher[0] == '!'; - if (first && !exclude) cipher_priority[0] = 0; - first = FALSE; - - for (i = 0; i < sizeof(cipher_index)/sizeof(pri_item); i++) - { - uschar *ss = strstric(cipher, cipher_index[i].name, FALSE); - if (ss != NULL) - { - uschar *endss = ss + Ustrlen(cipher_index[i].name); - if ((ss == cipher || !isalnum(ss[-1])) && !isalnum(*endss)) - { - if (exclude) - remove_ciphers(cipher_priority, cipher_index[i].values); - else - { - if (!add_ciphers(cipher_priority, - sizeof(cipher_priority)/sizeof(pri_item), - cipher_index[i].values)) - { - log_write(0, LOG_MAIN|LOG_PANIC, "GnuTLS init failed: cipher " - "priority table overflow"); - gnutls_deinit(session); - return NULL; - } - } - } - } - } - } - - DEBUG(D_tls) - { - int *ptr = cipher_priority; - debug_printf("adjusted cipher priorities:"); - while (*ptr != 0) debug_printf(" %d", *ptr++); - debug_printf("\n"); - } +if ((expciphers != NULL && + !set_priority(cipher_priority, sizeof(cipher_priority)/sizeof(int), + expciphers, cipher_index, sizeof(cipher_index)/sizeof(pri_item), + US"cipher")) || + (expmac != NULL && + !set_priority(mac_priority, sizeof(mac_priority)/sizeof(int), + expmac, mac_index, sizeof(mac_index)/sizeof(pri_item), + US"MAC")) || + (expkx != NULL && + !set_priority(kx_priority, sizeof(kx_priority)/sizeof(int), + expkx, kx_index, sizeof(kx_index)/sizeof(pri_item), + US"key-exchange")) || + (expproto != NULL && + !set_priority(proto_priority, sizeof(proto_priority)/sizeof(int), + expproto, proto_index, sizeof(proto_index)/sizeof(pri_item), + US"protocol"))) + { + gnutls_deinit(session); + return NULL; } /* Define the various priorities */ @@ -670,7 +775,7 @@ if (expciphers != NULL) gnutls_cipher_set_priority(session, cipher_priority); gnutls_compression_set_priority(session, comp_priority); gnutls_kx_set_priority(session, kx_priority); -gnutls_protocol_set_priority(session, protocol_priority); +gnutls_protocol_set_priority(session, proto_priority); gnutls_mac_set_priority(session, mac_priority); gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred); @@ -739,7 +844,10 @@ the STARTTLS command. It must respond to that command, and then negotiate a TLS session. Arguments: - require_ciphers list of allowed ciphers + require_ciphers list of allowed ciphers or NULL + require_mac list of allowed MACs or NULL + require_kx list of allowed key_exchange methods or NULL + require_proto list of allowed protocols or NULL Returns: OK on success DEFER for errors before the start of the negotiation @@ -748,19 +856,21 @@ Returns: OK on success */ int -tls_server_start(uschar *require_ciphers) +tls_server_start(uschar *require_ciphers, uschar *require_mac, + uschar *require_kx, uschar *require_proto) { int rc; -uschar *error; +const char *error; uschar *expciphers = NULL; +uschar *expmac = NULL; +uschar *expkx = NULL; +uschar *expproto = NULL; /* Check for previous activation */ if (tls_active >= 0) { - log_write(0, LOG_MAIN, "STARTTLS received in already encrypted " - "connection from %s", - (sender_fullhost != NULL)? sender_fullhost : US"local process"); + tls_error("STARTTLS received after TLS started", NULL, ""); smtp_printf("554 Already in TLS\r\n"); return FAIL; } @@ -774,7 +884,10 @@ rc = tls_init(NULL, tls_certificate, tls_privatekey, tls_verify_certificates, tls_crl); if (rc != OK) return rc; -if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers)) +if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers) || + !expand_check(require_mac, US"gnutls_require_mac", &expmac) || + !expand_check(require_kx, US"gnutls_require_kx", &expkx) || + !expand_check(require_proto, US"gnutls_require_proto", &expproto)) return FAIL; /* If this is a host for which certificate verification is mandatory or @@ -790,9 +903,11 @@ else if (verify_check_host(&tls_try_verify_hosts) == OK) /* Prepare for new connection */ -tls_session = tls_session_init(GNUTLS_SERVER, expciphers); +tls_session = tls_session_init(GNUTLS_SERVER, expciphers, expmac, expkx, + expproto); if (tls_session == NULL) - return tls_error(US"tls_session_init", NULL, GNUTLS_E_MEMORY_ERROR); + return tls_error(US"tls_session_init", NULL, + gnutls_strerror(GNUTLS_E_MEMORY_ERROR)); /* Set context and tell client to go ahead, except in the case of TLS startup on connection, where outputting anything now upsets the clients and tends to @@ -809,7 +924,8 @@ if (!tls_on_connect) /* Now negotiate the TLS session. We put our own timer on it, since it seems that the GnuTLS library doesn't. */ -gnutls_transport_set_ptr(tls_session, (gnutls_transport_ptr)fileno(smtp_out)); +gnutls_transport_set_ptr2(tls_session, (gnutls_transport_ptr)fileno(smtp_in), + (gnutls_transport_ptr)fileno(smtp_out)); sigalrm_seen = FALSE; if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout); @@ -818,14 +934,8 @@ alarm(0); if (rc < 0) { - if (sigalrm_seen) - Ustrcpy(ssl_errstring, "timed out"); - else - Ustrcpy(ssl_errstring, gnutls_strerror(rc)); - log_write(0, LOG_MAIN, - "TLS error on connection from %s (gnutls_handshake): %s", - (sender_fullhost != NULL)? sender_fullhost : US"local process", - ssl_errstring); + tls_error(US"gnutls_handshake", NULL, + sigalrm_seen ? "timed out" : gnutls_strerror(rc)); /* It seems that, except in the case of a timeout, we have to close the connection right here; otherwise if the other end is running OpenSSL it hangs @@ -845,9 +955,7 @@ DEBUG(D_tls) debug_printf("gnutls_handshake was successful\n"); if (verify_requirement != VERIFY_NONE && !verify_certificate(tls_session, &error)) { - log_write(0, LOG_MAIN, - "TLS error on connection from %s: certificate verification failed (%s)", - (sender_fullhost != NULL)? sender_fullhost : US"local process", error); + tls_error(US"certificate verification failed", NULL, error); return FAIL; } @@ -864,6 +972,7 @@ receive_getc = tls_getc; receive_ungetc = tls_ungetc; receive_feof = tls_feof; receive_ferror = tls_ferror; +receive_smtp_buffered = tls_smtp_buffered; tls_active = fileno(smtp_out); @@ -882,13 +991,16 @@ return OK; Arguments: fd the fd of the connection host connected host (for messages) - addr + addr the first address (not used) dhparam DH parameter file certificate certificate file privatekey private key file verify_certs file for certificate verify verify_crl CRL for verify - require_ciphers list of allowed ciphers + require_ciphers list of allowed ciphers or NULL + require_mac list of allowed MACs or NULL + require_kx list of allowed key_exchange methods or NULL + require_proto list of allowed protocols or NULL timeout startup timeout Returns: OK/DEFER/FAIL (because using common functions), @@ -898,27 +1010,36 @@ Returns: OK/DEFER/FAIL (because using common functions), int tls_client_start(int fd, host_item *host, address_item *addr, uschar *dhparam, uschar *certificate, uschar *privatekey, uschar *verify_certs, - uschar *verify_crl, uschar *require_ciphers, int timeout) + uschar *verify_crl, uschar *require_ciphers, uschar *require_mac, + uschar *require_kx, uschar *require_proto, int timeout) { const gnutls_datum *server_certs; uschar *expciphers = NULL; -uschar *error; +uschar *expmac = NULL; +uschar *expkx = NULL; +uschar *expproto = NULL; +const char *error; unsigned int server_certs_size; int rc; DEBUG(D_tls) debug_printf("initializing GnuTLS as a client\n"); -client_host = host; verify_requirement = (verify_certs == NULL)? VERIFY_NONE : VERIFY_REQUIRED; rc = tls_init(host, certificate, privatekey, verify_certs, verify_crl); if (rc != OK) return rc; -if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers)) +if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers) || + !expand_check(require_mac, US"gnutls_require_mac", &expmac) || + !expand_check(require_kx, US"gnutls_require_kx", &expkx) || + !expand_check(require_proto, US"gnutls_require_proto", &expproto)) return FAIL; -tls_session = tls_session_init(GNUTLS_CLIENT, expciphers); +tls_session = tls_session_init(GNUTLS_CLIENT, expciphers, expmac, expkx, + expproto); + if (tls_session == NULL) - return tls_error(US "tls_session_init", host, GNUTLS_E_MEMORY_ERROR); + return tls_error(US "tls_session_init", host, + gnutls_strerror(GNUTLS_E_MEMORY_ERROR)); gnutls_transport_set_ptr(tls_session, (gnutls_transport_ptr)fd); @@ -930,15 +1051,8 @@ rc = gnutls_handshake(tls_session); alarm(0); if (rc < 0) - { - if (sigalrm_seen) - { - log_write(0, LOG_MAIN, "TLS error on connection to %s [%s]: " - "gnutls_handshake timed out", host->name, host->address); - return FAIL; - } - else return tls_error(US "gnutls_handshake", host, rc); - } + return tls_error(US "gnutls_handshake", host, + sigalrm_seen ? "timed out" : gnutls_strerror(rc)); server_certs = gnutls_certificate_get_peers(tls_session, &server_certs_size); @@ -962,12 +1076,7 @@ if (server_certs != NULL) if (verify_requirement != VERIFY_NONE && !verify_certificate(tls_session, &error)) - { - log_write(0, LOG_MAIN, - "TLS error on connection to %s [%s]: certificate verification failed (%s)", - host->name, host->address, error); - return FAIL; - } + return tls_error(US"certificate verification failed", host, error); construct_cipher_name(tls_session); /* Sets tls_cipher */ tls_active = fd; @@ -993,21 +1102,15 @@ Returns: nothing static void record_io_error(int ec, uschar *when, uschar *text) { -uschar *additional = US""; +const char *msg; if (ec == GNUTLS_E_FATAL_ALERT_RECEIVED) - additional = string_sprintf(": %s", + msg = string_sprintf("%s: %s", gnutls_strerror(ec), gnutls_alert_get_name(gnutls_alert_get(tls_session))); - -if (initialized == INITIALIZED_SERVER) - log_write(0, LOG_MAIN, "TLS %s error on connection from %s: %s%s", when, - (sender_fullhost != NULL)? sender_fullhost : US "local process", - (ec == 0)? text : US gnutls_strerror(ec), additional); - else - log_write(0, LOG_MAIN, "TLS %s error on connection to %s [%s]: %s%s", when, - client_host->name, client_host->address, - (ec == 0)? text : US gnutls_strerror(ec), additional); + msg = gnutls_strerror(ec); + +tls_error(when, client_host, msg); } @@ -1050,6 +1153,7 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm) receive_ungetc = smtp_ungetc; receive_feof = smtp_feof; receive_ferror = smtp_ferror; + receive_smtp_buffered = smtp_buffered; gnutls_deinit(tls_session); tls_session = NULL; @@ -1068,7 +1172,9 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm) ssl_xfer_error = 1; return EOF; } - +#ifndef DISABLE_DKIM + dkim_exim_verify_feed(ssl_xfer_buffer, inbytes); +#endif ssl_xfer_buffer_hwm = inbytes; ssl_xfer_buffer_lwm = 0; } @@ -1192,4 +1298,24 @@ gnutls_global_deinit(); tls_active = -1; } + + + +/************************************************* +* Report the library versions. * +*************************************************/ + +/* See a description in tls-openssl.c for an explanation of why this exists. + +Arguments: a FILE* to print the results to +Returns: nothing +*/ + +void +tls_version_report(FILE *f) +{ +fprintf(f, "GnuTLS compile-time version: %s\n", LIBGNUTLS_VERSION); +fprintf(f, "GnuTLS runtime version: %s\n", gnutls_check_version(NULL)); +} + /* End of tls-gnu.c */