changeset 504:706fef7f4dcc NGINX_0_7_64

nginx 0.7.64 *) Security: now SSL/TLS renegotiation is disabled. Thanks to Maxim Dounin. *) Bugfix: nginx sent gzipped responses to clients those do not support gzip, if "gzip_static on" and "gzip_vary off"; the bug had appeared in 0.7.63. *) Bugfix: if names .domain.tld, .sub.domain.tld, and .domain-some.tld were defined, then the name .sub.domain.tld was matched by .domain.tld. *) Bugfix: segmentation fault and infinite looping in resolver. *) Bugfix: in resolver. Thanks to Artem Bokhan. *) Bugfix: resolver cache poisoning. Thanks to Matthew Dempsky. *) Bugfix: memory leak in resolver. Thanks to Matthew Dempsky.
author Igor Sysoev <http://sysoev.ru>
date Mon, 16 Nov 2009 00:00:00 +0300
parents bedade69b1a4
children c62da3dcc544
files CHANGES CHANGES.ru src/core/nginx.h src/core/ngx_resolver.c src/core/ngx_string.c src/core/ngx_string.h src/event/ngx_event_openssl.c src/event/ngx_event_openssl.h src/http/modules/ngx_http_gzip_static_module.c src/http/modules/ngx_http_map_module.c src/http/modules/ngx_http_referer_module.c src/http/modules/perl/nginx.pm src/http/ngx_http.c src/http/ngx_http_core_module.c src/http/ngx_http_request.c src/mail/ngx_mail_smtp_handler.c src/os/unix/ngx_alloc.c
diffstat 17 files changed, 178 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,29 @@
 
+Changes with nginx 0.7.64                                        16 Nov 2009
+
+    *) Security: now SSL/TLS renegotiation is disabled.
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: nginx sent gzipped responses to clients those do not support 
+       gzip, if "gzip_static on" and "gzip_vary off"; the bug had appeared 
+       in 0.7.63.
+
+    *) Bugfix: if names .domain.tld, .sub.domain.tld, and .domain-some.tld 
+       were defined, then the name .sub.domain.tld was matched by 
+       .domain.tld.
+
+    *) Bugfix: segmentation fault and infinite looping in resolver.
+
+    *) Bugfix: in resolver.
+       Thanks to Artem Bokhan.
+
+    *) Bugfix: resolver cache poisoning.
+       Thanks to Matthew Dempsky.
+
+    *) Bugfix: memory leak in resolver.
+       Thanks to Matthew Dempsky.
+
+
 Changes with nginx 0.7.63                                        26 Oct 2009
 
     *) Security: now "/../" are disabled in "Destination" request header 
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,29 @@
 
+Изменения в nginx 0.7.64                                          16.11.2009
+
+    *) Безопасность: теперь SSL/TLS renegotiation запрещён.
+       Спасибо Максиму Дунину.
+
+    *) Исправление: nginx передавал сжатые ответы клиентам, не 
+       поддерживающим сжатие, при настройках gzip_static on и gzip_vary 
+       off; ошибка появилась в 0.7.63.
+
+    *) Исправление: если были описаны имена .domain.tld, .sub.domain.tld и 
+       .domain-some.tld, то имя .sub.domain.tld попадало под маску 
+       .domain.tld.
+
+    *) Исправление: segmentation fault и зацикливания в resolver'е.
+
+    *) Исправление: в resolver'е.
+       Спасибо Артёму Бохану.
+
+    *) Исправление: порчи кэша resolver'а.
+       Спасибо Matthew Dempsky.
+
+    *) Исправление: утечки памяти в resolver'е.
+       Спасибо Matthew Dempsky.
+
+
 Изменения в nginx 0.7.63                                          26.10.2009
 
     *) Безопасность: теперь символы "/../" запрещены в строке "Destination" 
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,8 +8,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define nginx_version         7063
-#define NGINX_VERSION      "0.7.63"
+#define nginx_version         7064
+#define NGINX_VERSION      "0.7.64"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_resolver.c
+++ b/src/core/ngx_resolver.c
@@ -464,6 +464,7 @@ ngx_resolve_name_locked(ngx_resolver_t *
 
             ctx->next = rn->waiting;
             rn->waiting = ctx;
+            ctx->state = NGX_AGAIN;
 
             return NGX_AGAIN;
         }
@@ -625,6 +626,7 @@ ngx_resolve_addr(ngx_resolver_ctx_t *ctx
 
             ctx->next = rn->waiting;
             rn->waiting = ctx;
+            ctx->state = NGX_AGAIN;
 
             /* unlock addr mutex */
 
@@ -1149,6 +1151,8 @@ ngx_resolver_process_a(ngx_resolver_t *r
         goto failed;
     }
 
+    ngx_resolver_free(r, name.data);
+
     if (code == 0 && nan == 0) {
         code = 3; /* NXDOMAIN */
     }
@@ -1400,6 +1404,8 @@ failed:
 
     /* unlock name mutex */
 
+    ngx_resolver_free(r, name.data);
+
     return;
 }
 
@@ -1595,7 +1601,6 @@ static ngx_resolver_node_t *
 ngx_resolver_lookup_name(ngx_resolver_t *r, ngx_str_t *name, uint32_t hash)
 {
     ngx_int_t             rc;
-    size_t                len;
     ngx_rbtree_node_t    *node, *sentinel;
     ngx_resolver_node_t  *rn;
 
@@ -1619,9 +1624,7 @@ ngx_resolver_lookup_name(ngx_resolver_t 
         do {
             rn = (ngx_resolver_node_t *) node;
 
-            len = (name->len > (size_t) rn->nlen) ? rn->nlen : name->len;
-
-            rc = ngx_strncmp(name->data, rn->name, len);
+            rc = ngx_memn2cmp(name->data, rn->name, name->len, rn->nlen);
 
             if (rc == 0) {
                 return rn;
@@ -1675,7 +1678,6 @@ static void
 ngx_resolver_rbtree_insert_value(ngx_rbtree_node_t *temp,
     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
 {
-    size_t                 len;
     ngx_rbtree_node_t    **p;
     ngx_resolver_node_t   *rn, *rn_temp;
 
@@ -1694,10 +1696,8 @@ ngx_resolver_rbtree_insert_value(ngx_rbt
             rn = (ngx_resolver_node_t *) node;
             rn_temp = (ngx_resolver_node_t *) temp;
 
-            len = (rn->nlen > rn_temp->nlen) ? rn_temp->nlen : rn->nlen;
-
-            p = (ngx_strncmp(rn->name, rn_temp->name, len) < 0)
-                    ? &temp->left : &temp->right;
+            p = (ngx_memn2cmp(rn->name, rn_temp->name, rn->nlen, rn_temp->nlen)
+                 < 0) ? &temp->left : &temp->right;
         }
 
         if (*p == sentinel) {
@@ -1719,15 +1719,16 @@ static ngx_int_t
 ngx_resolver_create_name_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx)
 {
     u_char                *p, *s;
-    size_t                 len;
+    size_t                 len, nlen;
     ngx_uint_t             ident;
     ngx_resolver_qs_t     *qs;
     ngx_resolver_query_t  *query;
 
-    len = sizeof(ngx_resolver_query_t)
-          + 1 + ctx->name.len + 1 + sizeof(ngx_resolver_qs_t);
-
-    p = ngx_resolver_calloc(ctx->resolver, len);
+    nlen = ctx->name.len ? (1 + ctx->name.len + 1) : 1;
+
+    len = sizeof(ngx_resolver_query_t) + nlen + sizeof(ngx_resolver_qs_t);
+
+    p = ngx_resolver_alloc(ctx->resolver, len);
     if (p == NULL) {
         return NGX_ERROR;
     }
@@ -1754,7 +1755,7 @@ ngx_resolver_create_name_query(ngx_resol
     query->nns_hi = 0; query->nns_lo = 0;
     query->nar_hi = 0; query->nar_lo = 0;
 
-    p += sizeof(ngx_resolver_query_t) + 1 + ctx->name.len + 1;
+    p += sizeof(ngx_resolver_query_t) + nlen;
 
     qs = (ngx_resolver_qs_t *) p;
 
@@ -1808,7 +1809,7 @@ ngx_resolver_create_addr_query(ngx_resol
           + sizeof(".255.255.255.255.in-addr.arpa.") - 1
           + sizeof(ngx_resolver_qs_t);
 
-    p = ngx_resolver_calloc(ctx->resolver, len);
+    p = ngx_resolver_alloc(ctx->resolver, len);
     if (p == NULL) {
         return NGX_ERROR;
     }
@@ -1902,6 +1903,12 @@ done:
         return NGX_OK;
     }
 
+    if (len == -1) {
+        name->len = 0;
+        name->data = NULL;
+        return NGX_OK;
+    }
+
     dst = ngx_resolver_alloc(r, len);
     if (dst == NULL) {
         return NGX_ERROR;
--- a/src/core/ngx_string.c
+++ b/src/core/ngx_string.c
@@ -820,6 +820,37 @@ ngx_memn2cmp(u_char *s1, u_char *s2, siz
 
 
 ngx_int_t
+ngx_dns_strcmp(u_char *s1, u_char *s2)
+{
+    ngx_uint_t  c1, c2;
+
+    for ( ;; ) {
+        c1 = (ngx_uint_t) *s1++;
+        c2 = (ngx_uint_t) *s2++;
+
+        c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;
+        c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;
+
+        if (c1 == c2) {
+
+            if (c1) {
+                continue;
+            }
+
+            return 0;
+        }
+
+        /* in ASCII '.' > '-', but we need '.' to be the lowest character */
+
+        c1 = (c1 == '.') ? ' ' : c1;
+        c2 = (c2 == '.') ? ' ' : c2;
+
+        return c1 - c2;
+    }
+}
+
+
+ngx_int_t
 ngx_atoi(u_char *line, size_t n)
 {
     ngx_int_t  value;
--- a/src/core/ngx_string.h
+++ b/src/core/ngx_string.h
@@ -158,6 +158,7 @@ u_char *ngx_strlcasestrn(u_char *s1, u_c
 ngx_int_t ngx_rstrncmp(u_char *s1, u_char *s2, size_t n);
 ngx_int_t ngx_rstrncasecmp(u_char *s1, u_char *s2, size_t n);
 ngx_int_t ngx_memn2cmp(u_char *s1, u_char *s2, size_t n1, size_t n2);
+ngx_int_t ngx_dns_strcmp(u_char *s1, u_char *s2);
 
 ngx_int_t ngx_atoi(u_char *line, size_t n);
 ssize_t ngx_atosz(u_char *line, size_t n);
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -15,6 +15,8 @@ typedef struct {
 
 
 static int ngx_http_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store);
+static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where,
+    int ret);
 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);
@@ -175,6 +177,8 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_
 
     SSL_CTX_set_read_ahead(ssl->ctx, 1);
 
+    SSL_CTX_set_info_callback(ssl->ctx, ngx_ssl_info_callback);
+
     return NGX_OK;
 }
 
@@ -350,6 +354,22 @@ ngx_http_ssl_verify_callback(int ok, X50
 }
 
 
+static void
+ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, int ret)
+{
+    ngx_connection_t  *c;
+
+    if (where & SSL_CB_HANDSHAKE_START) {
+        c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
+
+        if (c->ssl->handshaked) {
+            c->ssl->renegotiation = 1;
+            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL renegotiation");
+        }
+    }
+}
+
+
 ngx_int_t
 ngx_ssl_generate_rsa512_key(ngx_ssl_t *ssl)
 {
@@ -587,6 +607,11 @@ ngx_ssl_handshake(ngx_connection_t *c)
         c->recv_chain = ngx_ssl_recv_chain;
         c->send_chain = ngx_ssl_send_chain;
 
+        /* initial handshake done, disable renegotiation (CVE-2009-3555) */
+        if (c->ssl->connection->s3) {
+            c->ssl->connection->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS;
+        }
+
         return NGX_OK;
     }
 
@@ -789,6 +814,21 @@ ngx_ssl_handle_recv(ngx_connection_t *c,
     int        sslerr;
     ngx_err_t  err;
 
+    if (c->ssl->renegotiation) {
+        /*
+         * disable renegotiation (CVE-2009-3555):
+         * OpenSSL (at least up to 0.9.8l) does not handle disabled
+         * renegotiation gracefully, so drop connection here
+         */
+
+        ngx_log_error(NGX_LOG_NOTICE, c->log, 0, "SSL renegotiation disabled");
+
+        c->ssl->no_wait_shutdown = 1;
+        c->ssl->no_send_shutdown = 1;
+
+        return NGX_ERROR;
+    }
+
     if (n > 0) {
 
         if (c->ssl->saved_write_handler) {
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -41,6 +41,7 @@ typedef struct {
     ngx_event_handler_pt        saved_write_handler;
 
     unsigned                    handshaked:1;
+    unsigned                    renegotiation:1;
     unsigned                    buffer:1;
     unsigned                    no_wait_shutdown:1;
     unsigned                    no_send_shutdown:1;
--- a/src/http/modules/ngx_http_gzip_static_module.c
+++ b/src/http/modules/ngx_http_gzip_static_module.c
@@ -99,9 +99,11 @@ ngx_http_gzip_static_handler(ngx_http_re
         return NGX_DECLINED;
     }
 
+    rc = ngx_http_gzip_ok(r);
+
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
-    if (clcf->gzip_vary && ngx_http_gzip_ok(r) != NGX_OK) {
+    if (!clcf->gzip_vary && rc != NGX_OK) {
         return NGX_DECLINED;
     }
 
@@ -162,6 +164,10 @@ ngx_http_gzip_static_handler(ngx_http_re
         return NGX_DECLINED;
     }
 
+    if (rc != NGX_OK) {
+        return NGX_DECLINED;
+    }
+
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd);
 
     if (of.is_dir) {
--- a/src/http/modules/ngx_http_map_module.c
+++ b/src/http/modules/ngx_http_map_module.c
@@ -337,7 +337,7 @@ ngx_http_map_cmp_dns_wildcards(const voi
     first = (ngx_hash_key_t *) one;
     second = (ngx_hash_key_t *) two;
 
-    return ngx_strcmp(first->key.data, second->key.data);
+    return ngx_dns_strcmp(first->key.data, second->key.data);
 }
 
 
--- a/src/http/modules/ngx_http_referer_module.c
+++ b/src/http/modules/ngx_http_referer_module.c
@@ -506,6 +506,11 @@ ngx_http_add_regex_referer(ngx_conf_t *c
     ngx_regex_elt_t  *re;
     u_char            errstr[NGX_MAX_CONF_ERRSTR];
 
+    if (name->len == 1) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty regex in \"%V\"", name);
+        return NGX_CONF_ERROR;
+    }
+
     if (rlcf->regex == NGX_CONF_UNSET_PTR) {
         rlcf->regex = ngx_array_create(cf->pool, 2, sizeof(ngx_regex_elt_t));
         if (rlcf->regex == NULL) {
@@ -562,5 +567,5 @@ ngx_http_cmp_referer_wildcards(const voi
     first = (ngx_hash_key_t *) one;
     second = (ngx_hash_key_t *) two;
 
-    return ngx_strcmp(first->key.data, second->key.data);
+    return ngx_dns_strcmp(first->key.data, second->key.data);
 }
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -47,7 +47,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '0.7.63';
+our $VERSION = '0.7.64';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -1601,7 +1601,7 @@ ngx_http_cmp_dns_wildcards(const void *o
     first = (ngx_hash_key_t *) one;
     second = (ngx_hash_key_t *) two;
 
-    return ngx_strcmp(first->key.data, second->key.data);
+    return ngx_dns_strcmp(first->key.data, second->key.data);
 }
 
 
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -3512,6 +3512,12 @@ ngx_http_core_server_name(ngx_conf_t *cf
         ngx_str_t  err;
         u_char     errstr[NGX_MAX_CONF_ERRSTR];
 
+        if (value[i].len == 1) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "empty regex in server name \"%V\"", &value[i]);
+            return NGX_CONF_ERROR;
+        }
+
         err.len = NGX_MAX_CONF_ERRSTR;
         err.data = errstr;
 
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -1658,7 +1658,7 @@ ngx_http_find_virtual_server(ngx_http_re
 
 #if (NGX_PCRE)
 
-    if (r->virtual_names->nregex) {
+    if (len && r->virtual_names->nregex) {
         size_t                   ncaptures;
         ngx_int_t                n;
         ngx_uint_t               i;
--- a/src/mail/ngx_mail_smtp_handler.c
+++ b/src/mail/ngx_mail_smtp_handler.c
@@ -191,7 +191,7 @@ ngx_mail_smtp_resolve_name_handler(ngx_r
 
     if (ctx->state) {
         ngx_log_error(NGX_LOG_ERR, c->log, 0,
-                      "%V could not be resolved (%i: %s)",
+                      "\"%V\" could not be resolved (%i: %s)",
                       &ctx->name, ctx->state,
                       ngx_resolver_strerror(ctx->state));
 
--- a/src/os/unix/ngx_alloc.c
+++ b/src/os/unix/ngx_alloc.c
@@ -51,11 +51,15 @@ void *
 ngx_memalign(size_t alignment, size_t size, ngx_log_t *log)
 {
     void  *p;
+    int    err;
 
-    if (posix_memalign(&p, alignment, size) == -1) {
-        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+    err = posix_memalign(&p, alignment, size);
+
+    if (err) {
+        ngx_log_error(NGX_LOG_EMERG, log, err,
                       "posix_memalign() %uz bytes aligned to %uz failed",
                       size, alignment);
+        p = NULL;
     }
 
     ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0,