X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Ftls-openssl.c;h=7c7362bc77b7a830a6f775d0ed777f3815b82aec;hb=47195144861c416c402191b697c5d3d489b1dcb2;hp=f17d94b5f85c6a8f2c3c154035ef5103f648e235;hpb=0d81dabc92972f340421d0f80fc04156215e2eb8;p=exim.git diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index f17d94b5f..7c7362bc7 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -29,7 +29,7 @@ functions from the OpenSSL library. */ # include #endif #ifdef EXPERIMENTAL_DANE -# include +# include "danessl.h" #endif @@ -69,6 +69,7 @@ functions from the OpenSSL library. */ #ifndef LIBRESSL_VERSION_NUMBER # if OPENSSL_VERSION_NUMBER >= 0x010100000L # define EXIM_HAVE_OPENSSL_CHECKHOST +# define EXIM_HAVE_OPENSSL_DH_BITS # endif # if OPENSSL_VERSION_NUMBER >= 0x010000000L \ && (OPENSSL_VERSION_NUMBER & 0x0000ff000L) >= 0x000002000L @@ -227,14 +228,13 @@ return host ? FAIL : DEFER; -#ifdef EXIM_HAVE_EPHEM_RSA_KEX /************************************************* * Callback to generate RSA key * *************************************************/ /* Arguments: - s SSL connection + s SSL connection (not used) export not used keylength keylength @@ -269,7 +269,6 @@ if (!(rsa_key = RSA_generate_key(keylength, RSA_F4, NULL, NULL))) } return rsa_key; } -#endif @@ -383,11 +382,13 @@ dn[sizeof(dn)-1] = '\0'; if (preverify_ok == 0) { - log_write(0, LOG_MAIN, "[%s] SSL verify error: depth=%d error=%s cert=%s", - tlsp == &tls_out ? deliver_host_address : sender_host_address, - depth, - X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509ctx)), - dn); + uschar * extra = verify_mode ? string_sprintf(" (during %c-verify for [%s])", + *verify_mode, sender_host_address) + : US""; + log_write(0, LOG_MAIN, "[%s] SSL verify error%s: depth=%d error=%s cert=%s", + tlsp == &tls_out ? deliver_host_address : sender_host_address, + extra, depth, + X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509ctx)), dn); *calledp = TRUE; if (!*optionalp) { @@ -448,7 +449,7 @@ else if (rc < 0) { log_write(0, LOG_MAIN, "[%s] SSL verify error: internal error", - deliver_host_address); + tlsp == &tls_out ? deliver_host_address : sender_host_address); name = NULL; } break; @@ -458,10 +459,14 @@ else if (!tls_is_name_for_cert(verify_cert_hostnames, cert)) #endif { + uschar * extra = verify_mode + ? string_sprintf(" (during %c-verify for [%s])", + *verify_mode, sender_host_address) + : US""; log_write(0, LOG_MAIN, - "[%s] SSL verify error: certificate name mismatch: " - "DN=\"%s\" H=\"%s\"", - deliver_host_address, dn, verify_cert_hostnames); + "[%s] SSL verify error%s: certificate name mismatch: DN=\"%s\" H=\"%s\"", + tlsp == &tls_out ? deliver_host_address : sender_host_address, + extra, dn, verify_cert_hostnames); *calledp = TRUE; if (!*optionalp) { @@ -595,6 +600,7 @@ BIO *bio; DH *dh; uschar *dhexpanded; const char *pem; +int dh_bitsize; if (!expand_check(dhparam, US"tls_dhparam", &dhexpanded, errstr)) return FALSE; @@ -635,21 +641,34 @@ if (!(dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL))) return FALSE; } +/* note: our default limit of 2236 is not a multiple of 8; the limit comes from + * an NSS limit, and the GnuTLS APIs handle bit-sizes fine, so we went with + * 2236. But older OpenSSL can only report in bytes (octets), not bits. + * If someone wants to dance at the edge, then they can raise the limit or use + * current libraries. */ +#ifdef EXIM_HAVE_OPENSSL_DH_BITS +/* Added in commit 26c79d5641d; `git describe --contains` says OpenSSL_1_1_0-pre1~1022 + * This predates OpenSSL_1_1_0 (before a, b, ...) so is in all 1.1.0 */ +dh_bitsize = DH_bits(dh); +#else +dh_bitsize = 8 * DH_size(dh); +#endif + /* Even if it is larger, we silently return success rather than cause things * to fail out, so that a too-large DH will not knock out all TLS; it's a * debatable choice. */ -if ((8*DH_size(dh)) > tls_dh_max_bits) +if (dh_bitsize > tls_dh_max_bits) { DEBUG(D_tls) debug_printf("dhparams file %d bits, is > tls_dh_max_bits limit of %d\n", - 8*DH_size(dh), tls_dh_max_bits); + dh_bitsize, tls_dh_max_bits); } else { SSL_CTX_set_tmp_dh(sctx, dh); DEBUG(D_tls) debug_printf("Diffie-Hellman initialized from %s with %d-bit prime\n", - dhexpanded ? dhexpanded : US"default", 8*DH_size(dh)); + dhexpanded ? dhexpanded : US"default", dh_bitsize); } DH_free(dh); @@ -914,7 +933,7 @@ if (!OCSP_check_validity(thisupd, nextupd, EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX } supply_response: - cbinfo->u_ocsp.server.response = resp; + cbinfo->u_ocsp.server.response = resp; /*XXX stack?*/ return; bad: @@ -922,7 +941,7 @@ bad: { extern char ** environ; uschar ** p; - if (environ) for (p = USS environ; *p != NULL; p++) + if (environ) for (p = USS environ; *p; p++) if (Ustrncmp(*p, "EXIM_TESTHARNESS_DISABLE_OCSPVALIDITYCHECK", 42) == 0) { DEBUG(D_tls) debug_printf("Supplying known bad OCSP response\n"); @@ -956,8 +975,7 @@ if (!(x509 = X509_new())) goto err; where = US"generating pkey"; - /* deprecated, use RSA_generate_key_ex() */ -if (!(rsa = RSA_generate_key(1024, RSA_F4, NULL, NULL))) +if (!(rsa = rsa_callback(NULL, 0, 1024))) goto err; where = US"assigning pkey"; @@ -1003,6 +1021,30 @@ err: +static int +tls_add_certfile(SSL_CTX * sctx, tls_ext_ctx_cb * cbinfo, uschar * file, + uschar ** errstr) +{ +DEBUG(D_tls) debug_printf("tls_certificate file %s\n", file); +if (!SSL_CTX_use_certificate_chain_file(sctx, CS file)) + return tls_error(string_sprintf( + "SSL_CTX_use_certificate_chain_file file=%s", file), + cbinfo->host, NULL, errstr); +return 0; +} + +static int +tls_add_pkeyfile(SSL_CTX * sctx, tls_ext_ctx_cb * cbinfo, uschar * file, + uschar ** errstr) +{ +DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", file); +if (!SSL_CTX_use_PrivateKey_file(sctx, CS file, SSL_FILETYPE_PEM)) + return tls_error(string_sprintf( + "SSL_CTX_use_PrivateKey_file file=%s", file), cbinfo->host, NULL, errstr); +return 0; +} + + /************************************************* * Expand key and cert file specs * *************************************************/ @@ -1027,7 +1069,7 @@ uschar *expanded; if (!cbinfo->certificate) { - if (cbinfo->host) /* client */ + if (!cbinfo->is_server) /* client */ return OK; /* server */ if (tls_install_selfsign(sctx, errstr) != OK) @@ -1035,6 +1077,8 @@ if (!cbinfo->certificate) } else { + int err; + if (Ustrstr(cbinfo->certificate, US"tls_sni") || Ustrstr(cbinfo->certificate, US"tls_in_sni") || Ustrstr(cbinfo->certificate, US"tls_out_sni") @@ -1044,14 +1088,20 @@ else if (!expand_check(cbinfo->certificate, US"tls_certificate", &expanded, errstr)) return DEFER; - if (expanded != NULL) - { - DEBUG(D_tls) debug_printf("tls_certificate file %s\n", expanded); - if (!SSL_CTX_use_certificate_chain_file(sctx, CS expanded)) - return tls_error(string_sprintf( - "SSL_CTX_use_certificate_chain_file file=%s", expanded), - cbinfo->host, NULL, errstr); - } + if (expanded) + if (cbinfo->is_server) + { + const uschar * file_list = expanded; + int sep = 0; + uschar * file; + + while (file = string_nextinlist(&file_list, &sep, NULL, 0)) + if ((err = tls_add_certfile(sctx, cbinfo, file, errstr))) + return err; + } + else /* would there ever be a need for multiple client certs? */ + if ((err = tls_add_certfile(sctx, cbinfo, expanded, errstr))) + return err; if (cbinfo->privatekey != NULL && !expand_check(cbinfo->privatekey, US"tls_privatekey", &expanded, errstr)) @@ -1062,17 +1112,25 @@ else key is in the same file as the certificate. */ if (expanded && *expanded) - { - DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", expanded); - if (!SSL_CTX_use_PrivateKey_file(sctx, CS expanded, SSL_FILETYPE_PEM)) - return tls_error(string_sprintf( - "SSL_CTX_use_PrivateKey_file file=%s", expanded), cbinfo->host, NULL, errstr); - } + if (cbinfo->is_server) + { + const uschar * file_list = expanded; + int sep = 0; + uschar * file; + + while (file = string_nextinlist(&file_list, &sep, NULL, 0)) + if ((err = tls_add_pkeyfile(sctx, cbinfo, file, errstr))) + return err; + } + else /* would there ever be a need for multiple client certs? */ + if ((err = tls_add_pkeyfile(sctx, cbinfo, expanded, errstr))) + return err; } #ifndef DISABLE_OCSP if (cbinfo->is_server && cbinfo->u_ocsp.server.file) { + /*XXX stack*/ if (!expand_check(cbinfo->u_ocsp.server.file, US"tls_ocsp_file", &expanded, errstr)) return DEFER; @@ -1210,9 +1268,15 @@ static int tls_server_stapling_cb(SSL *s, void *arg) { const tls_ext_ctx_cb *cbinfo = (tls_ext_ctx_cb *) arg; -uschar *response_der; +uschar *response_der; /*XXX blob */ int response_der_len; +/*XXX stack: use SSL_get_certificate() to see which cert; from that work +out which ocsp blob to send. Unfortunately, SSL_get_certificate is known +buggy in current OpenSSL; it returns the last cert loaded always rather than +the one actually presented. So we can't support a stack of OCSP proofs at +this time. */ + DEBUG(D_tls) debug_printf("Received TLS status request (OCSP stapling); %s response\n", cbinfo->u_ocsp.server.response ? "have" : "lack"); @@ -1222,7 +1286,7 @@ if (!cbinfo->u_ocsp.server.response) return SSL_TLSEXT_ERR_NOACK; response_der = NULL; -response_der_len = i2d_OCSP_RESPONSE(cbinfo->u_ocsp.server.response, +response_der_len = i2d_OCSP_RESPONSE(cbinfo->u_ocsp.server.response, /*XXX stack*/ &response_der); if (response_der_len <= 0) return SSL_TLSEXT_ERR_NOACK; @@ -1414,7 +1478,7 @@ static int tls_init(SSL_CTX **ctxp, host_item *host, uschar *dhparam, uschar *certificate, uschar *privatekey, #ifndef DISABLE_OCSP - uschar *ocsp_file, + uschar *ocsp_file, /*XXX stack, in server*/ #endif address_item *addr, tls_ext_ctx_cb ** cbp, uschar ** errstr) { @@ -1478,9 +1542,9 @@ if (!RAND_status()) gettimeofday(&r.tv, NULL); r.p = getpid(); - RAND_seed((uschar *)(&r), sizeof(r)); - RAND_seed((uschar *)big_buffer, big_buffer_size); - if (addr != NULL) RAND_seed((uschar *)addr, sizeof(addr)); + RAND_seed(US (&r), sizeof(r)); + RAND_seed(US big_buffer, big_buffer_size); + if (addr != NULL) RAND_seed(US addr, sizeof(addr)); if (!RAND_status()) return tls_error(US"RAND_status", host, @@ -1875,7 +1939,7 @@ static uschar cipherbuf[256]; if (tls_in.active >= 0) { tls_error(US"STARTTLS received after TLS started", NULL, US"", errstr); - smtp_printf("554 Already in TLS\r\n"); + smtp_printf("554 Already in TLS\r\n", FALSE); return FAIL; } @@ -1884,7 +1948,7 @@ the error. */ rc = tls_init(&server_ctx, NULL, tls_dhparam, tls_certificate, tls_privatekey, #ifndef DISABLE_OCSP - tls_ocsp_file, + tls_ocsp_file, /*XXX stack*/ #endif NULL, &server_static_cbinfo, errstr); if (rc != OK) return rc; @@ -1959,7 +2023,7 @@ mode, the fflush() happens when smtp_getc() is called. */ SSL_set_session_id_context(server_ssl, sid_ctx, Ustrlen(sid_ctx)); if (!tls_in.on_connect) { - smtp_printf("220 TLS go ahead\r\n"); + smtp_printf("220 TLS go ahead\r\n", FALSE); fflush(smtp_out); } @@ -2480,6 +2544,12 @@ if (n > 0) } +BOOL +tls_could_read(void) +{ +return ssl_xfer_buffer_lwm < ssl_xfer_buffer_hwm || SSL_pending(server_ssl) > 0; +} + /************************************************* * Read bytes from TLS channel * @@ -2533,6 +2603,7 @@ Arguments: is_server channel specifier buff buffer of data len number of bytes + more further data expected soon Returns: the number of bytes after a successful write, -1 after a failed write @@ -2541,15 +2612,31 @@ Used by both server-side and client-side TLS. */ int -tls_write(BOOL is_server, const uschar *buff, size_t len) +tls_write(BOOL is_server, const uschar *buff, size_t len, BOOL more) { -int outbytes; -int error; -int left = len; +int outbytes, error, left; SSL *ssl = is_server ? server_ssl : client_ssl; +static gstring * corked = NULL; + +DEBUG(D_tls) debug_printf("%s(%p, %lu%s)\n", __FUNCTION__, + buff, (unsigned long)len, more ? ", more" : ""); + +/* Lacking a CORK or MSG_MORE facility (such as GnuTLS has) we copy data when +"more" is notified. This hack is only ok if small amounts are involved AND only +one stream does it, in one context (i.e. no store reset). Currently it is used +for the responses to the received SMTP MAIL , RCPT, DATA sequence, only. */ + +if (is_server && (more || corked)) + { + corked = string_catn(corked, buff, len); + if (more) + return len; + buff = CUS corked->s; + len = corked->ptr; + corked = NULL; + } -DEBUG(D_tls) debug_printf("tls_do_write(%p, %d)\n", buff, left); -while (left > 0) +for (left = len; left > 0;) { DEBUG(D_tls) debug_printf("SSL_write(SSL, %p, %d)\n", buff, left); outbytes = SSL_write(ssl, CS buff, left); @@ -2774,7 +2861,7 @@ if (!RAND_status()) gettimeofday(&r.tv, NULL); r.p = getpid(); - RAND_seed((uschar *)(&r), sizeof(r)); + RAND_seed(US (&r), sizeof(r)); } /* We're after pseudo-random, not random; if we still don't have enough data in the internal PRNG then our options are limited. We could sleep and hope