Mercurial > hg > nginx
comparison src/event/ngx_event_openssl.c @ 7694:09fb2135a589
SSL: fixed shutdown handling.
Previously, bidirectional shutdown never worked, due to two issues
in the code:
1. The code only tested SSL_ERROR_WANT_READ and SSL_ERROR_WANT_WRITE
when there was an error in the error queue, which cannot happen.
The bug was introduced in an attempt to fix unexpected error logging
as reported with OpenSSL 0.9.8g
(http://mailman.nginx.org/pipermail/nginx/2008-January/003084.html).
2. The code never called SSL_shutdown() for the second time to wait for
the peer's close_notify alert.
This change fixes both issues.
Note that after this change bidirectional shutdown is expected to work for
the first time, so c->ssl->no_wait_shutdown now makes a difference. This
is not a problem for HTTP code which always uses c->ssl->no_wait_shutdown,
but might be a problem for stream and mail code, as well as 3rd party
modules.
To minimize the effect of the change, the timeout, which was used to be 30
seconds and not configurable, though never actually used, is now set to
3 seconds. It is also expanded to apply to both SSL_ERROR_WANT_READ and
SSL_ERROR_WANT_WRITE, so timeout is properly set if writing to the socket
buffer is not possible.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Mon, 10 Aug 2020 18:52:09 +0300 |
parents | 3dcb1aba894a |
children | 61011bfcdb49 532fe796b0e2 |
comparison
equal
deleted
inserted
replaced
7693:f5a2af0e7079 | 7694:09fb2135a589 |
---|---|
2772 | 2772 |
2773 | 2773 |
2774 ngx_int_t | 2774 ngx_int_t |
2775 ngx_ssl_shutdown(ngx_connection_t *c) | 2775 ngx_ssl_shutdown(ngx_connection_t *c) |
2776 { | 2776 { |
2777 int n, sslerr, mode; | 2777 int n, sslerr, mode; |
2778 ngx_err_t err; | 2778 ngx_err_t err; |
2779 ngx_uint_t tries; | |
2779 | 2780 |
2780 ngx_ssl_ocsp_cleanup(c); | 2781 ngx_ssl_ocsp_cleanup(c); |
2781 | 2782 |
2782 if (SSL_in_init(c->ssl->connection)) { | 2783 if (SSL_in_init(c->ssl->connection)) { |
2783 /* | 2784 /* |
2814 | 2815 |
2815 SSL_set_shutdown(c->ssl->connection, mode); | 2816 SSL_set_shutdown(c->ssl->connection, mode); |
2816 | 2817 |
2817 ngx_ssl_clear_error(c->log); | 2818 ngx_ssl_clear_error(c->log); |
2818 | 2819 |
2819 n = SSL_shutdown(c->ssl->connection); | 2820 tries = 2; |
2820 | 2821 |
2821 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_shutdown: %d", n); | 2822 for ( ;; ) { |
2822 | 2823 |
2823 sslerr = 0; | 2824 /* |
2824 | 2825 * For bidirectional shutdown, SSL_shutdown() needs to be called |
2825 /* before 0.9.8m SSL_shutdown() returned 0 instead of -1 on errors */ | 2826 * twice: first call sends the "close notify" alert and returns 0, |
2826 | 2827 * second call waits for the peer's "close notify" alert. |
2827 if (n != 1 && ERR_peek_error()) { | 2828 */ |
2829 | |
2830 n = SSL_shutdown(c->ssl->connection); | |
2831 | |
2832 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_shutdown: %d", n); | |
2833 | |
2834 if (n == 1) { | |
2835 SSL_free(c->ssl->connection); | |
2836 c->ssl = NULL; | |
2837 | |
2838 return NGX_OK; | |
2839 } | |
2840 | |
2841 if (n == 0 && tries-- > 1) { | |
2842 continue; | |
2843 } | |
2844 | |
2845 /* before 0.9.8m SSL_shutdown() returned 0 instead of -1 on errors */ | |
2846 | |
2828 sslerr = SSL_get_error(c->ssl->connection, n); | 2847 sslerr = SSL_get_error(c->ssl->connection, n); |
2829 | 2848 |
2830 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, | 2849 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, |
2831 "SSL_get_error: %d", sslerr); | 2850 "SSL_get_error: %d", sslerr); |
2832 } | 2851 |
2833 | 2852 if (sslerr == SSL_ERROR_WANT_READ || sslerr == SSL_ERROR_WANT_WRITE) { |
2834 if (n == 1 || sslerr == 0 || sslerr == SSL_ERROR_ZERO_RETURN) { | 2853 c->read->handler = ngx_ssl_shutdown_handler; |
2854 c->write->handler = ngx_ssl_shutdown_handler; | |
2855 | |
2856 if (ngx_handle_read_event(c->read, 0) != NGX_OK) { | |
2857 return NGX_ERROR; | |
2858 } | |
2859 | |
2860 if (ngx_handle_write_event(c->write, 0) != NGX_OK) { | |
2861 return NGX_ERROR; | |
2862 } | |
2863 | |
2864 ngx_add_timer(c->read, 3000); | |
2865 | |
2866 return NGX_AGAIN; | |
2867 } | |
2868 | |
2869 if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) { | |
2870 SSL_free(c->ssl->connection); | |
2871 c->ssl = NULL; | |
2872 | |
2873 return NGX_OK; | |
2874 } | |
2875 | |
2876 err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; | |
2877 | |
2878 ngx_ssl_connection_error(c, sslerr, err, "SSL_shutdown() failed"); | |
2879 | |
2835 SSL_free(c->ssl->connection); | 2880 SSL_free(c->ssl->connection); |
2836 c->ssl = NULL; | 2881 c->ssl = NULL; |
2837 | 2882 |
2838 return NGX_OK; | 2883 return NGX_ERROR; |
2839 } | 2884 } |
2840 | |
2841 if (sslerr == SSL_ERROR_WANT_READ || sslerr == SSL_ERROR_WANT_WRITE) { | |
2842 c->read->handler = ngx_ssl_shutdown_handler; | |
2843 c->write->handler = ngx_ssl_shutdown_handler; | |
2844 | |
2845 if (ngx_handle_read_event(c->read, 0) != NGX_OK) { | |
2846 return NGX_ERROR; | |
2847 } | |
2848 | |
2849 if (ngx_handle_write_event(c->write, 0) != NGX_OK) { | |
2850 return NGX_ERROR; | |
2851 } | |
2852 | |
2853 if (sslerr == SSL_ERROR_WANT_READ) { | |
2854 ngx_add_timer(c->read, 30000); | |
2855 } | |
2856 | |
2857 return NGX_AGAIN; | |
2858 } | |
2859 | |
2860 err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; | |
2861 | |
2862 ngx_ssl_connection_error(c, sslerr, err, "SSL_shutdown() failed"); | |
2863 | |
2864 SSL_free(c->ssl->connection); | |
2865 c->ssl = NULL; | |
2866 | |
2867 return NGX_ERROR; | |
2868 } | 2885 } |
2869 | 2886 |
2870 | 2887 |
2871 static void | 2888 static void |
2872 ngx_ssl_shutdown_handler(ngx_event_t *ev) | 2889 ngx_ssl_shutdown_handler(ngx_event_t *ev) |