diff src/event/ngx_event_openssl.c @ 96:ca4f70b3ccc6 NGINX_0_2_2

nginx 0.2.2 *) Feature: the "config errmsg" command of the ngx_http_ssi_module. *) Change: the ngx_http_geo_module variables can be overridden by the "set" directive. *) Feature: the "ssl_protocols" and "ssl_prefer_server_ciphers" directives of the ngx_http_ssl_module and ngx_imap_ssl_module. *) Bugfix: the ngx_http_autoindex_module did not show correctly the long file names; *) Bugfix: the ngx_http_autoindex_module now do not show the files starting by dot. *) Bugfix: if the SSL handshake failed then another connection may be closed too. Thanks to Rob Mueller. *) Bugfix: the export versions of MSIE 5.x could not connect via HTTPS.
author Igor Sysoev <http://sysoev.ru>
date Fri, 30 Sep 2005 00:00:00 +0400
parents 45945fa8b8ba
children 8bf57caa374a
line wrap: on
line diff
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -14,9 +14,12 @@ typedef struct {
 } ngx_openssl_conf_t;
 
 
+static void ngx_ssl_handshake_handler(ngx_event_t *ev);
 static ngx_int_t ngx_ssl_handle_recv(ngx_connection_t *c, int n);
 static void ngx_ssl_write_handler(ngx_event_t *wev);
 static void ngx_ssl_read_handler(ngx_event_t *rev);
+static void ngx_ssl_connection_error(ngx_connection_t *c, int sslerr,
+    ngx_err_t err, char *text);
 static void *ngx_openssl_create_conf(ngx_cycle_t *cycle);
 static char *ngx_openssl_init_conf(ngx_cycle_t *cycle, void *conf);
 
@@ -63,7 +66,19 @@ ngx_module_t  ngx_openssl_module = {
     NULL,                                  /* exit process */
     NULL,                                  /* exit master */
     NGX_MODULE_V1_PADDING
-};  
+};
+
+
+static long  ngx_ssl_protocols[] = {
+    SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1,
+    SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1,
+    SSL_OP_NO_SSLv2|SSL_OP_NO_TLSv1,
+    SSL_OP_NO_TLSv1,
+    SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3,
+    SSL_OP_NO_SSLv3,
+    SSL_OP_NO_SSLv2,
+    0,
+};
 
 
 ngx_int_t
@@ -81,45 +96,241 @@ ngx_ssl_init(ngx_log_t *log)
 
 
 ngx_int_t
-ngx_ssl_create_connection(ngx_ssl_ctx_t *ssl_ctx, ngx_connection_t *c,
-    ngx_uint_t flags)
+ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols)
+{
+    ssl->ctx = SSL_CTX_new(SSLv23_server_method());
+
+    if (ssl->ctx == NULL) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "SSL_CTX_new() failed");
+        return NGX_ERROR;
+    }
+
+    SSL_CTX_set_options(ssl->ctx, SSL_OP_ALL);
+
+    if (ngx_ssl_protocols[protocols >> 1] != 0) {
+        SSL_CTX_set_options(ssl->ctx, ngx_ssl_protocols[protocols >> 1]);
+    }
+
+    SSL_CTX_set_mode(ssl->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+    SSL_CTX_set_read_ahead(ssl->ctx, 1);
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_certificate(ngx_ssl_t *ssl, u_char *cert, u_char *key)
+{
+    if (SSL_CTX_use_certificate_chain_file(ssl->ctx, (char *) cert) == 0) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "SSL_CTX_use_certificate_chain_file(\"%s\") failed",
+                      cert);
+        return NGX_ERROR;
+    }
+
+    if (SSL_CTX_use_PrivateKey_file(ssl->ctx, (char *) key, SSL_FILETYPE_PEM)
+        == 0)
+    {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "SSL_CTX_use_PrivateKey_file(\"%s\") failed", key);
+        return NGX_ERROR;
+    }
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_generate_rsa512_key(ngx_ssl_t *ssl)
+{
+    ssl->rsa512_key = RSA_generate_key(512, RSA_F4, NULL, NULL);
+
+    if (ssl->rsa512_key) {
+        SSL_CTX_set_tmp_rsa(ssl->ctx, ssl->rsa512_key);
+        return NGX_OK;
+    }
+
+    ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "RSA_generate_key(512) failed");
+
+    return NGX_ERROR;
+}
+
+
+ngx_int_t
+ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags)
 {   
-    ngx_ssl_t  *ssl;
+    ngx_ssl_connection_t  *sc;
 
-    ssl = ngx_pcalloc(c->pool, sizeof(ngx_ssl_t));
-    if (ssl == NULL) {
+    sc = ngx_pcalloc(c->pool, sizeof(ngx_ssl_connection_t));
+    if (sc == NULL) {
         return NGX_ERROR;
     }
 
     if (flags & NGX_SSL_BUFFER) {
-        ssl->buffer = 1;
+        sc->buffer = 1;
 
-        ssl->buf = ngx_create_temp_buf(c->pool, NGX_SSL_BUFSIZE);
-        if (ssl->buf == NULL) {
+        sc->buf = ngx_create_temp_buf(c->pool, NGX_SSL_BUFSIZE);
+        if (sc->buf == NULL) {
             return NGX_ERROR;
         }
     }
 
-    ssl->connection = SSL_new(ssl_ctx);
+    sc->connection = SSL_new(ssl->ctx);
 
-    if (ssl->connection == NULL) {
+    if (sc->connection == NULL) {
         ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_new() failed");
         return NGX_ERROR;
     }
 
-    if (SSL_set_fd(ssl->connection, c->fd) == 0) {
+    if (SSL_set_fd(sc->connection, c->fd) == 0) {
         ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_fd() failed");
         return NGX_ERROR;
     }
 
-    SSL_set_accept_state(ssl->connection);
+    SSL_set_accept_state(sc->connection);
 
-    c->ssl = ssl;
+    c->ssl = sc;
 
     return NGX_OK;
 }
 
 
+ngx_int_t
+ngx_ssl_handshake(ngx_connection_t *c)
+{
+    int        n, sslerr;
+    ngx_err_t  err;
+
+    n = SSL_do_handshake(c->ssl->connection);
+
+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); 
+
+    if (n == 1) {
+
+        if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+            return NGX_ERROR;
+        }
+
+        if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
+            return NGX_ERROR;
+        }
+
+#if (NGX_DEBUG)
+        {
+        char         buf[129], *s, *d;
+        SSL_CIPHER  *cipher;
+
+        cipher = SSL_get_current_cipher(c->ssl->connection);
+
+        if (cipher) {
+            SSL_CIPHER_description(cipher, &buf[1], 128);
+
+            for (s = &buf[1], d = buf; *s; s++) {
+                if (*s == ' ' && *d == ' ') {
+                    continue;
+                }
+
+                if (*s == LF || *s == CR) {
+                    continue;
+                }
+
+                *++d = *s;
+            }
+
+            if (*d != ' ') {
+                d++;
+            }
+
+            *d = '\0';
+
+            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                           "SSL: %s, cipher: \"%s\"",
+                           SSL_get_version(c->ssl->connection), &buf[1]); 
+
+            if (SSL_session_reused(c->ssl->connection)) {
+                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                               "SSL reused session");
+            }
+
+        } else {
+            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                           "SSL no shared ciphers"); 
+        }
+        }
+#endif
+
+        c->ssl->handshaked = 1;
+
+        c->recv = ngx_ssl_recv;
+        c->send = ngx_ssl_write;
+        c->send_chain = ngx_ssl_send_chain; 
+
+        return NGX_OK;
+    }
+
+    sslerr = SSL_get_error(c->ssl->connection, n);
+
+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr);
+
+    if (sslerr == SSL_ERROR_WANT_READ) {
+        c->read->ready = 0;
+        c->read->handler = ngx_ssl_handshake_handler;
+
+        if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+            return NGX_ERROR;
+        }
+
+        return NGX_AGAIN;
+    }
+
+    if (sslerr == SSL_ERROR_WANT_WRITE) {
+        c->write->ready = 0;
+        c->write->handler = ngx_ssl_handshake_handler;
+
+        if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
+            return NGX_ERROR;
+        }
+
+        return NGX_AGAIN;
+    }
+
+    err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
+
+    c->ssl->no_wait_shutdown = 1;
+    c->ssl->no_send_shutdown = 1;
+
+    if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) {
+        ngx_log_error(NGX_LOG_INFO, c->log, err,
+                      "client closed connection in SSL handshake");
+
+        return NGX_ERROR;
+    }
+
+    ngx_ssl_connection_error(c, sslerr, err, "SSL_do_handshake() failed");
+
+    return NGX_ERROR;
+}
+
+
+static void
+ngx_ssl_handshake_handler(ngx_event_t *ev)
+{
+    ngx_connection_t  *c;
+
+    c = ev->data;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "ssl handshake handler: %d", ev->write);
+
+    if (ngx_ssl_handshake(c) == NGX_AGAIN) {
+        return;
+    }
+
+    c->ssl->handler(c);
+}
+
+
 ssize_t
 ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size)
 {
@@ -143,56 +354,7 @@ ngx_ssl_recv(ngx_connection_t *c, u_char
         ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_read: %d", n); 
 
         if (n > 0) {
-
             bytes += n;
-
-#if (NGX_DEBUG)
-
-            if (!c->ssl->handshaked && SSL_is_init_finished(c->ssl->connection))
-            {
-                char         buf[129], *s, *d;
-                SSL_CIPHER  *cipher;
-
-                c->ssl->handshaked = 1;
-
-                cipher = SSL_get_current_cipher(c->ssl->connection);
-
-                if (cipher) {
-                    SSL_CIPHER_description(cipher, &buf[1], 128);
-
-                    for (s = &buf[1], d = buf; *s; s++) {
-                        if (*s == ' ' && *d == ' ') {
-                            continue;
-                        }
-
-                        if (*s == LF || *s == CR) {
-                            continue;
-                        }
-
-                        *++d = *s;
-                    }
-
-                    if (*d != ' ') {
-                        d++;
-                    }
-
-                    *d = '\0';
-
-                    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                                   "SSL cipher: \"%s\"", &buf[1]); 
-
-                    if (SSL_session_reused(c->ssl->connection)) {
-                        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                                       "SSL reused session");
-                    }
-
-                } else {
-                    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                                   "SSL no shared ciphers"); 
-                }
-            }
-#endif
-
         }
 
         c->ssl->last = ngx_ssl_handle_recv(c, n);
@@ -221,10 +383,8 @@ ngx_ssl_recv(ngx_connection_t *c, u_char
 static ngx_int_t
 ngx_ssl_handle_recv(ngx_connection_t *c, int n)
 {
-    int          sslerr;
-    char        *handshake;
-    ngx_err_t    err;
-    ngx_uint_t   level;
+    int        sslerr;
+    ngx_err_t  err;
 
     if (n > 0) {
 
@@ -250,13 +410,6 @@ ngx_ssl_handle_recv(ngx_connection_t *c,
         return NGX_OK;
     }
 
-    if (!SSL_is_init_finished(c->ssl->connection)) {
-        handshake = " in SSL handshake";
-
-    } else {
-        handshake = "";
-    }
-
     sslerr = SSL_get_error(c->ssl->connection, n);
 
     err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
@@ -270,9 +423,8 @@ ngx_ssl_handle_recv(ngx_connection_t *c,
 
     if (sslerr == SSL_ERROR_WANT_WRITE) {
 
-        ngx_log_error(NGX_LOG_INFO, c->log, err,
-                      "client does SSL %shandshake",
-                      SSL_is_init_finished(c->ssl->connection) ? "re" : "");
+        ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                      "client started SSL renegotiation");
 
         c->write->ready = 0;
 
@@ -292,44 +444,16 @@ ngx_ssl_handle_recv(ngx_connection_t *c,
         return NGX_AGAIN;
     }
 
-    c->ssl->no_rcv_shut = 1;
-    c->ssl->no_send_shut = 1;
+    c->ssl->no_wait_shutdown = 1;
+    c->ssl->no_send_shutdown = 1;
 
     if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) {
-        ngx_log_error(NGX_LOG_INFO, c->log, err,
-                      "client closed connection%s", handshake);
+        ngx_log_error(NGX_LOG_INFO, c->log, err, "client closed connection");
 
         return NGX_ERROR;
     }
 
-    level = NGX_LOG_CRIT;
-
-    if (sslerr == SSL_ERROR_SYSCALL) {
-
-        if (err == NGX_ECONNRESET
-            || err == NGX_EPIPE
-            || err == NGX_ENOTCONN
-            || err == NGX_ECONNREFUSED
-            || err == NGX_EHOSTUNREACH)
-        {
-            switch (c->log_error) {
-
-            case NGX_ERROR_IGNORE_ECONNRESET:
-            case NGX_ERROR_INFO:
-                level = NGX_LOG_INFO;
-                break;
-
-            case NGX_ERROR_ERR:
-                level = NGX_LOG_ERR;
-                break;
-
-            default:
-                break;
-            }
-        }
-    }
-
-    ngx_ssl_error(level, c->log, err, "SSL_read() failed%s", handshake);
+    ngx_ssl_connection_error(c, sslerr, err, "SSL_read() failed");
 
     return NGX_ERROR;
 }
@@ -341,6 +465,7 @@ ngx_ssl_write_handler(ngx_event_t *wev)
     ngx_connection_t  *c;
 
     c = wev->data;
+
     c->read->handler(c->read);
 }
 
@@ -482,9 +607,8 @@ ngx_ssl_send_chain(ngx_connection_t *c, 
 ssize_t
 ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size)
 {
-    int         n, sslerr;
-    ngx_err_t   err;
-    ngx_uint_t  level;
+    int        n, sslerr;
+    ngx_err_t  err;
 
     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL to write: %d", size);
 
@@ -494,52 +618,6 @@ ngx_ssl_write(ngx_connection_t *c, u_cha
 
     if (n > 0) {
 
-#if (NGX_DEBUG)
-
-        if (!c->ssl->handshaked && SSL_is_init_finished(c->ssl->connection)) {
-            char         buf[129], *s, *d;
-            SSL_CIPHER  *cipher;
-
-            c->ssl->handshaked = 1;
-
-            cipher = SSL_get_current_cipher(c->ssl->connection);
-
-            if (cipher) {
-                SSL_CIPHER_description(cipher, &buf[1], 128);
-
-                for (s = &buf[1], d = buf; *s; s++) {
-                    if (*s == ' ' && *d == ' ') {
-                        continue;
-                    }
-
-                    if (*s == LF || *s == CR) {
-                        continue;
-                    }
-
-                    *++d = *s;
-                }
-
-                if (*d != ' ') {
-                    d++;
-                }
-
-                *d = '\0';
-
-                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                               "SSL cipher: \"%s\"", &buf[1]); 
-
-                if (SSL_session_reused(c->ssl->connection)) {
-                    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                                   "SSL reused session");
-                }
-
-            } else {
-                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                               "SSL no shared ciphers"); 
-            }
-        }
-#endif
-
         if (c->ssl->saved_read_handler) {
 
             c->read->handler = c->ssl->saved_read_handler;
@@ -575,9 +653,8 @@ ngx_ssl_write(ngx_connection_t *c, u_cha
 
     if (sslerr == SSL_ERROR_WANT_READ) {
 
-        ngx_log_error(NGX_LOG_INFO, c->log, err,
-                      "client does SSL %shandshake",
-                      SSL_is_init_finished(c->ssl->connection) ? "re" : "");
+        ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                      "client started SSL renegotiation");
 
         c->read->ready = 0;
 
@@ -598,37 +675,10 @@ ngx_ssl_write(ngx_connection_t *c, u_cha
         return NGX_AGAIN;
     }
 
-    c->ssl->no_rcv_shut = 1;
-    c->ssl->no_send_shut = 1;
-
-    level = NGX_LOG_CRIT;
-
-    if (sslerr == SSL_ERROR_SYSCALL) {
-
-        if (err == NGX_ECONNRESET
-            || err == NGX_EPIPE
-            || err == NGX_ENOTCONN
-            || err == NGX_ECONNREFUSED
-            || err == NGX_EHOSTUNREACH)
-        {
-            switch (c->log_error) {
+    c->ssl->no_wait_shutdown = 1;
+    c->ssl->no_send_shutdown = 1;
 
-            case NGX_ERROR_IGNORE_ECONNRESET:
-            case NGX_ERROR_INFO:
-                level = NGX_LOG_INFO;
-                break;
-
-            case NGX_ERROR_ERR:
-                level = NGX_LOG_ERR;
-                break;
-
-            default:
-                break;
-            }
-        }
-    }
-
-    ngx_ssl_error(level, c->log, err, "SSL_write() failed");
+    ngx_ssl_connection_error(c, sslerr, err, "SSL_write() failed");
 
     return NGX_ERROR;
 }
@@ -640,6 +690,7 @@ ngx_ssl_read_handler(ngx_event_t *rev)
     ngx_connection_t  *c;
 
     c = rev->data;
+
     c->write->handler(c->write);
 }
 
@@ -650,31 +701,23 @@ ngx_ssl_shutdown(ngx_connection_t *c)
     int         n, sslerr, mode;
     ngx_uint_t  again;
 
-    if (!c->ssl->shutdown_set) {
-
-        /* it seems that SSL_set_shutdown() could be called once only */
-
-        if (c->read->timedout) {
-            mode = SSL_RECEIVED_SHUTDOWN|SSL_SENT_SHUTDOWN;
+    if (c->read->timedout) {
+        mode = SSL_RECEIVED_SHUTDOWN|SSL_SENT_SHUTDOWN;
 
-        } else {
-            mode = 0;
+    } else {
+        mode = SSL_get_shutdown(c->ssl->connection);
 
-            if (c->ssl->no_rcv_shut) {
-                mode = SSL_RECEIVED_SHUTDOWN;
-            }
-
-            if (c->ssl->no_send_shut) {
-                mode |= SSL_SENT_SHUTDOWN;
-            }
+        if (c->ssl->no_wait_shutdown) {
+            mode |= SSL_RECEIVED_SHUTDOWN;
         }
 
-        if (mode) {
-            SSL_set_shutdown(c->ssl->connection, mode);
-            c->ssl->shutdown_set = 1;
+        if (c->ssl->no_send_shutdown) {
+            mode |= SSL_SENT_SHUTDOWN;
         }
     }
 
+    SSL_set_shutdown(c->ssl->connection, mode);
+
     again = 0;
 #if (NGX_SUPPRESS_WARN)
     sslerr = 0;
@@ -736,11 +779,49 @@ ngx_ssl_shutdown(ngx_connection_t *c)
 }
 
 
+static void
+ngx_ssl_connection_error(ngx_connection_t *c, int sslerr, ngx_err_t err,
+    char *text)
+{
+    ngx_uint_t  level;
+
+    level = NGX_LOG_CRIT;
+
+    if (sslerr == SSL_ERROR_SYSCALL) {
+
+        if (err == NGX_ECONNRESET
+            || err == NGX_EPIPE
+            || err == NGX_ENOTCONN
+            || err == NGX_ECONNREFUSED
+            || err == NGX_EHOSTUNREACH)
+        {
+            switch (c->log_error) {
+
+            case NGX_ERROR_IGNORE_ECONNRESET:
+            case NGX_ERROR_INFO:
+                level = NGX_LOG_INFO;
+                break;
+
+            case NGX_ERROR_ERR:
+                level = NGX_LOG_ERR;
+                break;
+
+            default:
+                break;
+            }
+        }
+    }
+
+    ngx_ssl_error(level, c->log, err, text);
+}
+
+
 void
 ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...)
 {   
+    u_long   n;
+    va_list  args;
     u_char   errstr[NGX_MAX_CONF_ERRSTR], *p, *last;
-    va_list  args;
 
     last = errstr + NGX_MAX_CONF_ERRSTR;
 
@@ -748,9 +829,18 @@ ngx_ssl_error(ngx_uint_t level, ngx_log_
     p = ngx_vsnprintf(errstr, sizeof(errstr) - 1, fmt, args);
     va_end(args);
 
-    p = ngx_cpystrn(p, (u_char *) " (SSL: ", last - p);
+    p = ngx_cpystrn(p, (u_char *) " (SSL:", last - p);
+
+    while (p < last && (n = ERR_get_error())) {
+
+        *p++ = ' ';
 
-    ERR_error_string_n(ERR_get_error(), (char *) p, last - p);
+        ERR_error_string_n(n, (char *) p, last - p);
+
+        while (p < last && *p) {
+            p++;
+        }
+    }
 
     ngx_log_error(level, log, err, "%s)", errstr);
 }
@@ -759,9 +849,10 @@ ngx_ssl_error(ngx_uint_t level, ngx_log_
 void
 ngx_ssl_cleanup_ctx(void *data)
 {
-   SSL_CTX  *ctx = data;
+   ngx_ssl_t  *ssl = data;
 
-   SSL_CTX_free(ctx);
+   RSA_free(ssl->rsa512_key);
+   SSL_CTX_free(ssl->ctx);
 }