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)