X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/cf0c61644d7dd2dfb29f6418d95bf4d8cae199ea..d4fd1b83a197d73cbac114fe53f3448d8b5c7cc2:/src/src/tls-openssl.c diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index dd9affb65..7735bd971 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 @@ -93,6 +94,10 @@ functions from the OpenSSL library. */ # define DISABLE_OCSP #endif +#ifdef EXIM_HAVE_OPENSSL_CHECKHOST +# include +#endif + /* Structure for collecting random data for seeding. */ typedef struct randstuff { @@ -146,8 +151,8 @@ static BOOL reexpand_tls_files_for_sni = FALSE; typedef struct tls_ext_ctx_cb { uschar *certificate; uschar *privatekey; -#ifndef DISABLE_OCSP BOOL is_server; +#ifndef DISABLE_OCSP STACK_OF(X509) *verify_stack; /* chain for verifying the proof */ union { struct { @@ -227,14 +232,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 +273,6 @@ if (!(rsa_key = RSA_generate_key(keylength, RSA_F4, NULL, NULL))) } return rsa_key; } -#endif @@ -383,11 +386,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 +453,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 +463,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 +604,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 +645,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", - 8*DH_size(dh), tls_dh_max_bits); + debug_printf("dhparams file %d bits, is > tls_dh_max_bits limit of %d\n", + 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); @@ -727,7 +750,7 @@ if (Ustrcmp(exp_curve, "auto") == 0) #if OPENSSL_VERSION_NUMBER < 0x10002000L DEBUG(D_tls) debug_printf( "ECDH OpenSSL < 1.0.2: temp key parameter settings: overriding \"auto\" with \"prime256v1\"\n"); - exp_curve = "prime256v1"; + exp_curve = US"prime256v1"; #else # if defined SSL_CTRL_SET_ECDH_AUTO DEBUG(D_tls) debug_printf( @@ -914,7 +937,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 +945,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 +979,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 +1025,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 +1073,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 +1081,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 +1092,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 +1116,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 +1272,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 +1290,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,21 +1482,22 @@ 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) { +SSL_CTX * ctx; long init_options; int rc; -BOOL okay; tls_ext_ctx_cb * cbinfo; cbinfo = store_malloc(sizeof(tls_ext_ctx_cb)); cbinfo->certificate = certificate; cbinfo->privatekey = privatekey; +cbinfo->is_server = host==NULL; #ifndef DISABLE_OCSP cbinfo->verify_stack = NULL; -if ((cbinfo->is_server = host==NULL)) +if (!host) { cbinfo->u_ocsp.server.file = ocsp_file; cbinfo->u_ocsp.server.file_expanded = NULL; @@ -1461,9 +1530,8 @@ when OpenSSL is built without SSLv2 support. By disabling with openssl_options, we can let admins re-enable with the existing knob. */ -*ctxp = SSL_CTX_new(host ? SSLv23_client_method() : SSLv23_server_method()); - -if (!*ctxp) return tls_error(US"SSL_CTX_new", host, NULL, errstr); +if (!(ctx = SSL_CTX_new(host ? SSLv23_client_method() : SSLv23_server_method()))) + return tls_error(US"SSL_CTX_new", host, NULL, errstr); /* It turns out that we need to seed the random number generator this early in order to get the full complement of ciphers to work. It took me roughly a day @@ -1479,9 +1547,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, @@ -1491,10 +1559,10 @@ if (!RAND_status()) /* Set up the information callback, which outputs if debugging is at a suitable level. */ -DEBUG(D_tls) SSL_CTX_set_info_callback(*ctxp, (void (*)())info_callback); +DEBUG(D_tls) SSL_CTX_set_info_callback(ctx, (void (*)())info_callback); /* Automatically re-try reads/writes after renegotiation. */ -(void) SSL_CTX_set_mode(*ctxp, SSL_MODE_AUTO_RETRY); +(void) SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); /* Apply administrator-supplied work-arounds. Historically we applied just one requested option, @@ -1505,31 +1573,34 @@ grandfathered in the first one as the default value for "openssl_options". No OpenSSL version number checks: the options we accept depend upon the availability of the option value macros from OpenSSL. */ -okay = tls_openssl_options_parse(openssl_options, &init_options); -if (!okay) +if (!tls_openssl_options_parse(openssl_options, &init_options)) return tls_error(US"openssl_options parsing failed", host, NULL, errstr); if (init_options) { DEBUG(D_tls) debug_printf("setting SSL CTX options: %#lx\n", init_options); - if (!(SSL_CTX_set_options(*ctxp, init_options))) + if (!(SSL_CTX_set_options(ctx, init_options))) return tls_error(string_sprintf( "SSL_CTX_set_option(%#lx)", init_options), host, NULL, errstr); } else DEBUG(D_tls) debug_printf("no SSL CTX options to set\n"); +/* Disable session cache unconditionally */ + +(void) SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); + /* Initialize with DH parameters if supplied */ /* Initialize ECDH temp key parameter selection */ -if ( !init_dh(*ctxp, dhparam, host, errstr) - || !init_ecdh(*ctxp, host, errstr) +if ( !init_dh(ctx, dhparam, host, errstr) + || !init_ecdh(ctx, host, errstr) ) return DEFER; /* Set up certificate and key (and perhaps OCSP info) */ -if ((rc = tls_expand_session_files(*ctxp, cbinfo, errstr)) != OK) +if ((rc = tls_expand_session_files(ctx, cbinfo, errstr)) != OK) return rc; /* If we need to handle SNI or OCSP, do so */ @@ -1552,14 +1623,14 @@ if (host == NULL) /* server */ callback is invoked. */ if (cbinfo->u_ocsp.server.file) { - SSL_CTX_set_tlsext_status_cb(server_ctx, tls_server_stapling_cb); - SSL_CTX_set_tlsext_status_arg(server_ctx, cbinfo); + SSL_CTX_set_tlsext_status_cb(ctx, tls_server_stapling_cb); + SSL_CTX_set_tlsext_status_arg(ctx, cbinfo); } # endif /* We always do this, so that $tls_sni is available even if not used in tls_certificate */ - SSL_CTX_set_tlsext_servername_callback(*ctxp, tls_servername_cb); - SSL_CTX_set_tlsext_servername_arg(*ctxp, cbinfo); + SSL_CTX_set_tlsext_servername_callback(ctx, tls_servername_cb); + SSL_CTX_set_tlsext_servername_arg(ctx, cbinfo); } # ifndef DISABLE_OCSP else /* client */ @@ -1570,8 +1641,8 @@ else /* client */ DEBUG(D_tls) debug_printf("failed to create store for stapling verify\n"); return FAIL; } - SSL_CTX_set_tlsext_status_cb(*ctxp, tls_client_stapling_cb); - SSL_CTX_set_tlsext_status_arg(*ctxp, cbinfo); + SSL_CTX_set_tlsext_status_cb(ctx, tls_client_stapling_cb); + SSL_CTX_set_tlsext_status_arg(ctx, cbinfo); } # endif #endif @@ -1580,15 +1651,16 @@ cbinfo->verify_cert_hostnames = NULL; #ifdef EXIM_HAVE_EPHEM_RSA_KEX /* Set up the RSA callback */ -SSL_CTX_set_tmp_rsa_callback(*ctxp, rsa_callback); +SSL_CTX_set_tmp_rsa_callback(ctx, rsa_callback); #endif /* Finally, set the timeout, and we are done */ -SSL_CTX_set_timeout(*ctxp, ssl_session_timeout); +SSL_CTX_set_timeout(ctx, ssl_session_timeout); DEBUG(D_tls) debug_printf("Initialized TLS\n"); *cbp = cbinfo; +*ctxp = ctx; return OK; } @@ -1699,6 +1771,7 @@ uschar *expcerts, *expcrl; if (!expand_check(certs, US"tls_verify_certificates", &expcerts, errstr)) return DEFER; +DEBUG(D_tls) debug_printf("tls_verify_certificates: %s\n", expcerts); if (expcerts && *expcerts) { @@ -1871,7 +1944,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; } @@ -1880,7 +1953,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; @@ -1955,7 +2028,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); } @@ -2012,6 +2085,7 @@ ssl_xfer_buffer_lwm = ssl_xfer_buffer_hwm = 0; ssl_xfer_eof = ssl_xfer_error = 0; receive_getc = tls_getc; +receive_getbuf = tls_getbuf; receive_get_cache = tls_get_cache; receive_ungetc = tls_ungetc; receive_feof = tls_feof; @@ -2348,6 +2422,74 @@ return OK; +static BOOL +tls_refill(unsigned lim) +{ +int error; +int inbytes; + +DEBUG(D_tls) debug_printf("Calling SSL_read(%p, %p, %u)\n", server_ssl, + ssl_xfer_buffer, ssl_xfer_buffer_size); + +if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout); +inbytes = SSL_read(server_ssl, CS ssl_xfer_buffer, + MIN(ssl_xfer_buffer_size, lim)); +error = SSL_get_error(server_ssl, inbytes); +alarm(0); + +/* SSL_ERROR_ZERO_RETURN appears to mean that the SSL session has been +closed down, not that the socket itself has been closed down. Revert to +non-SSL handling. */ + +if (error == SSL_ERROR_ZERO_RETURN) + { + DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n"); + + receive_getc = smtp_getc; + receive_getbuf = smtp_getbuf; + receive_get_cache = smtp_get_cache; + receive_ungetc = smtp_ungetc; + receive_feof = smtp_feof; + receive_ferror = smtp_ferror; + receive_smtp_buffered = smtp_buffered; + + SSL_free(server_ssl); + server_ssl = NULL; + tls_in.active = -1; + tls_in.bits = 0; + tls_in.cipher = NULL; + tls_in.peerdn = NULL; + tls_in.sni = NULL; + + return FALSE; + } + +/* Handle genuine errors */ + +else if (error == SSL_ERROR_SSL) + { + ERR_error_string(ERR_get_error(), ssl_errstring); + log_write(0, LOG_MAIN, "TLS error (SSL_read): %s", ssl_errstring); + ssl_xfer_error = 1; + return FALSE; + } + +else if (error != SSL_ERROR_NONE) + { + DEBUG(D_tls) debug_printf("Got SSL error %d\n", error); + ssl_xfer_error = 1; + return FALSE; + } + +#ifndef DISABLE_DKIM +dkim_exim_verify_feed(ssl_xfer_buffer, inbytes); +#endif +ssl_xfer_buffer_hwm = inbytes; +ssl_xfer_buffer_lwm = 0; +return TRUE; +} + + /************************************************* * TLS version of getc * *************************************************/ @@ -2365,74 +2507,37 @@ int tls_getc(unsigned lim) { if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm) - { - int error; - int inbytes; + if (!tls_refill(lim)) + return ssl_xfer_error ? EOF : smtp_getc(lim); - DEBUG(D_tls) debug_printf("Calling SSL_read(%p, %p, %u)\n", server_ssl, - ssl_xfer_buffer, ssl_xfer_buffer_size); - - if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout); - inbytes = SSL_read(server_ssl, CS ssl_xfer_buffer, - MIN(ssl_xfer_buffer_size, lim)); - error = SSL_get_error(server_ssl, inbytes); - alarm(0); - - /* SSL_ERROR_ZERO_RETURN appears to mean that the SSL session has been - closed down, not that the socket itself has been closed down. Revert to - non-SSL handling. */ - - if (error == SSL_ERROR_ZERO_RETURN) - { - DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n"); - - receive_getc = smtp_getc; - receive_get_cache = smtp_get_cache; - receive_ungetc = smtp_ungetc; - receive_feof = smtp_feof; - receive_ferror = smtp_ferror; - receive_smtp_buffered = smtp_buffered; - - SSL_free(server_ssl); - server_ssl = NULL; - tls_in.active = -1; - tls_in.bits = 0; - tls_in.cipher = NULL; - tls_in.peerdn = NULL; - tls_in.sni = NULL; - - return smtp_getc(lim); - } +/* Something in the buffer; return next uschar */ - /* Handle genuine errors */ +return ssl_xfer_buffer[ssl_xfer_buffer_lwm++]; +} - else if (error == SSL_ERROR_SSL) - { - ERR_error_string(ERR_get_error(), ssl_errstring); - log_write(0, LOG_MAIN, "TLS error (SSL_read): %s", ssl_errstring); - ssl_xfer_error = 1; - return EOF; - } +uschar * +tls_getbuf(unsigned * len) +{ +unsigned size; +uschar * buf; - else if (error != SSL_ERROR_NONE) +if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm) + if (!tls_refill(*len)) { - DEBUG(D_tls) debug_printf("Got SSL error %d\n", error); - ssl_xfer_error = 1; - return EOF; + if (!ssl_xfer_error) return smtp_getbuf(len); + *len = 0; + return NULL; } -#ifndef DISABLE_DKIM - dkim_exim_verify_feed(ssl_xfer_buffer, inbytes); -#endif - ssl_xfer_buffer_hwm = inbytes; - ssl_xfer_buffer_lwm = 0; - } - -/* Something in the buffer; return next uschar */ - -return ssl_xfer_buffer[ssl_xfer_buffer_lwm++]; +if ((size = ssl_xfer_buffer_hwm - ssl_xfer_buffer_lwm) > *len) + size = *len; +buf = &ssl_xfer_buffer[ssl_xfer_buffer_lwm]; +ssl_xfer_buffer_lwm += size; +*len = size; +return buf; } + void tls_get_cache() { @@ -2444,6 +2549,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 * @@ -2479,9 +2590,7 @@ if (error == SSL_ERROR_ZERO_RETURN) return -1; } else if (error != SSL_ERROR_NONE) - { return -1; - } return inbytes; } @@ -2499,6 +2608,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 @@ -2507,15 +2617,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); @@ -2740,7 +2866,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 @@ -2952,7 +3078,7 @@ uschar *s, *end; uschar keep_c; BOOL adding, item_parsed; -result = 0L; +result = SSL_OP_NO_TICKET; /* Prior to 4.80 we or'd in SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; removed * from default because it increases BEAST susceptibility. */ #ifdef SSL_OP_NO_SSLv2 @@ -2962,7 +3088,7 @@ result |= SSL_OP_NO_SSLv2; result |= SSL_OP_SINGLE_DH_USE; #endif -if (option_spec == NULL) +if (!option_spec) { *results = result; return TRUE;