X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/7006ee24ecfd9d8f405f70d38cc36bdd91f8de87..a5ffa9b475a426bc73366db01f7cc92a3811bc3a:/src/src/tls-openssl.c diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 2d3688263..c09d9bdf6 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -641,7 +641,7 @@ if (!(dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL))) if ((8*DH_size(dh)) > tls_dh_max_bits) { DEBUG(D_tls) - debug_printf("dhparams file %d bits, is > tls_dh_max_bits limit of %d", + debug_printf("dhparams file %d bits, is > tls_dh_max_bits limit of %d\n", 8*DH_size(dh), tls_dh_max_bits); } else @@ -727,7 +727,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( @@ -1875,7 +1875,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; } @@ -1959,7 +1959,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); } @@ -2016,6 +2016,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; @@ -2352,6 +2353,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 * *************************************************/ @@ -2369,74 +2438,37 @@ int tls_getc(unsigned lim) { if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm) - { - 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 (!tls_refill(lim)) + return ssl_xfer_error ? EOF : smtp_getc(lim); - 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() { @@ -2448,6 +2480,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 * @@ -2501,6 +2539,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 @@ -2509,15 +2548,32 @@ 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 uschar * corked = NULL; +static int c_size = 0, c_len = 0; + +DEBUG(D_tls) debug_printf("%s(%p, %d%s)\n", __FUNCTION__, + buff, left, 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, &c_size, &c_len, buff, len); + if (more) + return len; + buff = CUS corked; + len = c_len; + corked = NULL; c_size = c_len = 0; + } -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);