From 2f9b55b87b42c770204fb9a4fdb5bd1096a2d3ad Mon Sep 17 00:00:00 2001 From: Jamie Nguyen Date: Tue, 26 Jan 2016 18:06:38 +0000 Subject: [PATCH] Fix various CVEs from 1.8.1 --- ...change-ngx_resolver_create-arguments.patch | 181 +++++++++++++ ...-1.6.3-Resolver-fix-CNAME-processing.patch | 80 ++++++ ...olver-fix-crashes-in-timeout-handler.patch | 134 ++++++++++ ...lver-fix-possible-segmentation-fault.patch | 22 ++ ...solver-fix-use-after-free-with-CNAME.patch | 252 ++++++++++++++++++ ...1.6.3-Resolver-limit-CNAME-recursion.patch | 68 +++++ nginx.spec | 23 +- 7 files changed, 759 insertions(+), 1 deletion(-) create mode 100644 nginx-1.6.3-Resolver-change-ngx_resolver_create-arguments.patch create mode 100644 nginx-1.6.3-Resolver-fix-CNAME-processing.patch create mode 100644 nginx-1.6.3-Resolver-fix-crashes-in-timeout-handler.patch create mode 100644 nginx-1.6.3-Resolver-fix-possible-segmentation-fault.patch create mode 100644 nginx-1.6.3-Resolver-fix-use-after-free-with-CNAME.patch create mode 100644 nginx-1.6.3-Resolver-limit-CNAME-recursion.patch diff --git a/nginx-1.6.3-Resolver-change-ngx_resolver_create-arguments.patch b/nginx-1.6.3-Resolver-change-ngx_resolver_create-arguments.patch new file mode 100644 index 0000000..cb40874 --- /dev/null +++ b/nginx-1.6.3-Resolver-change-ngx_resolver_create-arguments.patch @@ -0,0 +1,181 @@ +exporting patch: +# HG changeset patch +# User Roman Arutyunyan +# Date 1453816008 -10800 +# Tue Jan 26 16:46:48 2016 +0300 +# Branch stable-1.8 +# Node ID 5557bf31e25da68d5cda19dbc91d86f47430df1f +# Parent 838946300825379ccdd3acfb131cf66d6ae3cb85 +Resolver: changed the ngx_resolver_create_*_query() arguments. + +No functional changes. + +This is needed by the following change. + +diff -r 838946300825 -r 5557bf31e25d src/core/ngx_resolver.c +--- a/src/core/ngx_resolver.c Tue Jan 26 16:46:38 2016 +0300 ++++ b/src/core/ngx_resolver.c Tue Jan 26 16:46:48 2016 +0300 +@@ -64,10 +64,10 @@ + ngx_queue_t *queue); + static ngx_int_t ngx_resolver_send_query(ngx_resolver_t *r, + ngx_resolver_node_t *rn); +-static ngx_int_t ngx_resolver_create_name_query(ngx_resolver_node_t *rn, +- ngx_resolver_ctx_t *ctx); +-static ngx_int_t ngx_resolver_create_addr_query(ngx_resolver_node_t *rn, +- ngx_resolver_ctx_t *ctx); ++static ngx_int_t ngx_resolver_create_name_query(ngx_resolver_t *r, ++ ngx_resolver_node_t *rn, ngx_str_t *name); ++static ngx_int_t ngx_resolver_create_addr_query(ngx_resolver_t *r, ++ ngx_resolver_node_t *rn, ngx_addr_t *addr); + static void ngx_resolver_resend_handler(ngx_event_t *ev); + static time_t ngx_resolver_resend(ngx_resolver_t *r, ngx_rbtree_t *tree, + ngx_queue_t *queue); +@@ -651,7 +651,7 @@ + ngx_rbtree_insert(&r->name_rbtree, &rn->node); + } + +- rc = ngx_resolver_create_name_query(rn, ctx); ++ rc = ngx_resolver_create_name_query(r, rn, &ctx->name); + + if (rc == NGX_ERROR) { + goto failed; +@@ -878,7 +878,7 @@ + ngx_rbtree_insert(tree, &rn->node); + } + +- if (ngx_resolver_create_addr_query(rn, ctx) != NGX_OK) { ++ if (ngx_resolver_create_addr_query(r, rn, &ctx->addr) != NGX_OK) { + goto failed; + } + +@@ -2511,27 +2511,23 @@ + + + static ngx_int_t +-ngx_resolver_create_name_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx) ++ngx_resolver_create_name_query(ngx_resolver_t *r, ngx_resolver_node_t *rn, ++ ngx_str_t *name) + { + u_char *p, *s; + size_t len, nlen; + ngx_uint_t ident; +-#if (NGX_HAVE_INET6) +- ngx_resolver_t *r; +-#endif + ngx_resolver_qs_t *qs; + ngx_resolver_hdr_t *query; + +- nlen = ctx->name.len ? (1 + ctx->name.len + 1) : 1; ++ nlen = name->len ? (1 + name->len + 1) : 1; + + len = sizeof(ngx_resolver_hdr_t) + nlen + sizeof(ngx_resolver_qs_t); + + #if (NGX_HAVE_INET6) +- r = ctx->resolver; +- +- p = ngx_resolver_alloc(ctx->resolver, r->ipv6 ? len * 2 : len); ++ p = ngx_resolver_alloc(r, r->ipv6 ? len * 2 : len); + #else +- p = ngx_resolver_alloc(ctx->resolver, len); ++ p = ngx_resolver_alloc(r, len); + #endif + if (p == NULL) { + return NGX_ERROR; +@@ -2550,8 +2546,8 @@ + + ident = ngx_random(); + +- ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0, +- "resolve: \"%V\" A %i", &ctx->name, ident & 0xffff); ++ ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0, ++ "resolve: \"%V\" A %i", name, ident & 0xffff); + + query->ident_hi = (u_char) ((ident >> 8) & 0xff); + query->ident_lo = (u_char) (ident & 0xff); +@@ -2581,11 +2577,11 @@ + p--; + *p-- = '\0'; + +- if (ctx->name.len == 0) { ++ if (name->len == 0) { + return NGX_DECLINED; + } + +- for (s = ctx->name.data + ctx->name.len - 1; s >= ctx->name.data; s--) { ++ for (s = name->data + name->len - 1; s >= name->data; s--) { + if (*s != '.') { + *p = *s; + len++; +@@ -2621,8 +2617,8 @@ + + ident = ngx_random(); + +- ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0, +- "resolve: \"%V\" AAAA %i", &ctx->name, ident & 0xffff); ++ ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0, ++ "resolve: \"%V\" AAAA %i", name, ident & 0xffff); + + query->ident_hi = (u_char) ((ident >> 8) & 0xff); + query->ident_lo = (u_char) (ident & 0xff); +@@ -2639,11 +2635,12 @@ + + + static ngx_int_t +-ngx_resolver_create_addr_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx) ++ngx_resolver_create_addr_query(ngx_resolver_t *r, ngx_resolver_node_t *rn, ++ ngx_addr_t *addr) + { + u_char *p, *d; + size_t len; +- in_addr_t addr; ++ in_addr_t inaddr; + ngx_int_t n; + ngx_uint_t ident; + ngx_resolver_hdr_t *query; +@@ -2652,7 +2649,7 @@ + struct sockaddr_in6 *sin6; + #endif + +- switch (ctx->addr.sockaddr->sa_family) { ++ switch (addr->sockaddr->sa_family) { + + #if (NGX_HAVE_INET6) + case AF_INET6: +@@ -2669,7 +2666,7 @@ + + sizeof(ngx_resolver_qs_t); + } + +- p = ngx_resolver_alloc(ctx->resolver, len); ++ p = ngx_resolver_alloc(r, len); + if (p == NULL) { + return NGX_ERROR; + } +@@ -2693,11 +2690,11 @@ + + p += sizeof(ngx_resolver_hdr_t); + +- switch (ctx->addr.sockaddr->sa_family) { ++ switch (addr->sockaddr->sa_family) { + + #if (NGX_HAVE_INET6) + case AF_INET6: +- sin6 = (struct sockaddr_in6 *) ctx->addr.sockaddr; ++ sin6 = (struct sockaddr_in6 *) addr->sockaddr; + + for (n = 15; n >= 0; n--) { + p = ngx_sprintf(p, "\1%xd\1%xd", +@@ -2712,11 +2709,11 @@ + + default: /* AF_INET */ + +- sin = (struct sockaddr_in *) ctx->addr.sockaddr; +- addr = ntohl(sin->sin_addr.s_addr); ++ sin = (struct sockaddr_in *) addr->sockaddr; ++ inaddr = ntohl(sin->sin_addr.s_addr); + + for (n = 0; n < 32; n += 8) { +- d = ngx_sprintf(&p[1], "%ud", (addr >> n) & 0xff); ++ d = ngx_sprintf(&p[1], "%ud", (inaddr >> n) & 0xff); + *p = (u_char) (d - &p[1]); + p = d; + } diff --git a/nginx-1.6.3-Resolver-fix-CNAME-processing.patch b/nginx-1.6.3-Resolver-fix-CNAME-processing.patch new file mode 100644 index 0000000..4bc734d --- /dev/null +++ b/nginx-1.6.3-Resolver-fix-CNAME-processing.patch @@ -0,0 +1,80 @@ +exporting patch: +# HG changeset patch +# User Ruslan Ermilov +# Date 1453815998 -10800 +# Tue Jan 26 16:46:38 2016 +0300 +# Branch stable-1.8 +# Node ID 838946300825379ccdd3acfb131cf66d6ae3cb85 +# Parent f63dd04c158062d73fcb6aff59124910fa1fae75 +Resolver: fixed CNAME processing for several requests. + +When several requests were waiting for a response, then after getting +a CNAME response only the last request was properly processed, while +others were left waiting. + +diff -r f63dd04c1580 -r 838946300825 src/core/ngx_resolver.c +--- a/src/core/ngx_resolver.c Tue Jan 26 16:46:31 2016 +0300 ++++ b/src/core/ngx_resolver.c Tue Jan 26 16:46:38 2016 +0300 +@@ -473,7 +473,7 @@ + ngx_int_t rc; + ngx_uint_t naddrs; + ngx_addr_t *addrs; +- ngx_resolver_ctx_t *next; ++ ngx_resolver_ctx_t *next, *last; + ngx_resolver_node_t *rn; + + ngx_strlow(ctx->name.data, ctx->name.data, ctx->name.len); +@@ -484,6 +484,9 @@ + + if (rn) { + ++ /* ctx can be a list after NGX_RESOLVE_CNAME */ ++ for (last = ctx; last->next; last = last->next); ++ + if (rn->valid >= ngx_time()) { + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve cached"); +@@ -511,7 +514,7 @@ + } + } + +- ctx->next = rn->waiting; ++ last->next = rn->waiting; + rn->waiting = NULL; + + /* unlock name mutex */ +@@ -557,7 +560,7 @@ + return ngx_resolve_name_locked(r, ctx); + } + +- ctx->next = rn->waiting; ++ last->next = rn->waiting; + rn->waiting = NULL; + + /* unlock name mutex */ +@@ -590,7 +593,7 @@ + ngx_add_timer(ctx->event, ctx->timeout); + } + +- ctx->next = rn->waiting; ++ last->next = rn->waiting; + rn->waiting = ctx; + ctx->state = NGX_AGAIN; + +@@ -661,8 +664,14 @@ + ngx_resolver_free(r, rn->name); + ngx_resolver_free(r, rn); + +- ctx->state = NGX_RESOLVE_NXDOMAIN; +- ctx->handler(ctx); ++ do { ++ ctx->state = NGX_RESOLVE_NXDOMAIN; ++ next = ctx->next; ++ ++ ctx->handler(ctx); ++ ++ ctx = next; ++ } while (ctx); + + return NGX_OK; + } diff --git a/nginx-1.6.3-Resolver-fix-crashes-in-timeout-handler.patch b/nginx-1.6.3-Resolver-fix-crashes-in-timeout-handler.patch new file mode 100644 index 0000000..277a7e4 --- /dev/null +++ b/nginx-1.6.3-Resolver-fix-crashes-in-timeout-handler.patch @@ -0,0 +1,134 @@ +exporting patch: +# HG changeset patch +# User Ruslan Ermilov +# Date 1453815991 -10800 +# Tue Jan 26 16:46:31 2016 +0300 +# Branch stable-1.8 +# Node ID f63dd04c158062d73fcb6aff59124910fa1fae75 +# Parent c36482d0a79fe0f2e1467f80ec2fbcd0a2d682c6 +Resolver: fixed crashes in timeout handler. + +If one or more requests were waiting for a response, then after +getting a CNAME response, the timeout event on the first request +remained active, pointing to the wrong node with an empty +rn->waiting list, and that could cause either null pointer +dereference or use-after-free memory access if this timeout +expired. + +If several requests were waiting for a response, and the first +request terminated (e.g., due to client closing a connection), +other requests were left without a timeout and could potentially +wait indefinitely. + +This is fixed by introducing per-request independent timeouts. +This change also reverts 954867a2f0a6 and 5004210e8c78. + +diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c +index fe0ce50..7aa88a6 100644 +--- a/src/core/ngx_resolver.c ++++ b/src/core/ngx_resolver.c +@@ -417,7 +417,7 @@ ngx_resolve_name_done(ngx_resolver_ctx_t *ctx) + + /* lock name mutex */ + +- if (ctx->state == NGX_AGAIN) { ++ if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) { + + hash = ngx_crc32_short(ctx->name.data, ctx->name.len); + +@@ -571,6 +571,20 @@ ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx) + + if (rn->waiting) { + ++ if (ctx->event == NULL) { ++ ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t)); ++ if (ctx->event == NULL) { ++ return NGX_ERROR; ++ } ++ ++ ctx->event->handler = ngx_resolver_timeout_handler; ++ ctx->event->data = ctx; ++ ctx->event->log = r->log; ++ ctx->ident = -1; ++ ++ ngx_add_timer(ctx->event, ctx->timeout); ++ } ++ + ctx->next = rn->waiting; + rn->waiting = ctx; + ctx->state = NGX_AGAIN; +@@ -664,7 +678,7 @@ ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx) + } + + ctx->event->handler = ngx_resolver_timeout_handler; +- ctx->event->data = rn; ++ ctx->event->data = ctx; + ctx->event->log = r->log; + ctx->ident = -1; + +@@ -794,6 +808,18 @@ ngx_resolve_addr(ngx_resolver_ctx_t *ctx) + + if (rn->waiting) { + ++ ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t)); ++ if (ctx->event == NULL) { ++ return NGX_ERROR; ++ } ++ ++ ctx->event->handler = ngx_resolver_timeout_handler; ++ ctx->event->data = ctx; ++ ctx->event->log = r->log; ++ ctx->ident = -1; ++ ++ ngx_add_timer(ctx->event, ctx->timeout); ++ + ctx->next = rn->waiting; + rn->waiting = ctx; + ctx->state = NGX_AGAIN; +@@ -857,7 +883,7 @@ ngx_resolve_addr(ngx_resolver_ctx_t *ctx) + } + + ctx->event->handler = ngx_resolver_timeout_handler; +- ctx->event->data = rn; ++ ctx->event->data = ctx; + ctx->event->log = r->log; + ctx->ident = -1; + +@@ -949,7 +975,7 @@ ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx) + + /* lock addr mutex */ + +- if (ctx->state == NGX_AGAIN) { ++ if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) { + + switch (ctx->addr.sockaddr->sa_family) { + +@@ -2791,21 +2817,13 @@ done: + static void + ngx_resolver_timeout_handler(ngx_event_t *ev) + { +- ngx_resolver_ctx_t *ctx, *next; +- ngx_resolver_node_t *rn; ++ ngx_resolver_ctx_t *ctx; + +- rn = ev->data; +- ctx = rn->waiting; +- rn->waiting = NULL; ++ ctx = ev->data; + +- do { +- ctx->state = NGX_RESOLVE_TIMEDOUT; +- next = ctx->next; +- +- ctx->handler(ctx); ++ ctx->state = NGX_RESOLVE_TIMEDOUT; + +- ctx = next; +- } while (ctx); ++ ctx->handler(ctx); + } + + +-- +2.5.0 + diff --git a/nginx-1.6.3-Resolver-fix-possible-segmentation-fault.patch b/nginx-1.6.3-Resolver-fix-possible-segmentation-fault.patch new file mode 100644 index 0000000..97d4fdb --- /dev/null +++ b/nginx-1.6.3-Resolver-fix-possible-segmentation-fault.patch @@ -0,0 +1,22 @@ +exporting patch: +# HG changeset patch +# User Roman Arutyunyan +# Date 1453815978 -10800 +# Tue Jan 26 16:46:18 2016 +0300 +# Branch stable-1.8 +# Node ID c36482d0a79fe0f2e1467f80ec2fbcd0a2d682c6 +# Parent e9a4531a2a5dabb9bee93cb8b41f24b8aeeba504 +Resolver: fixed possible segmentation fault on DNS format error. + +diff -r e9a4531a2a5d -r c36482d0a79f src/core/ngx_resolver.c +--- a/src/core/ngx_resolver.c Mon Jan 25 21:58:21 2016 +0300 ++++ b/src/core/ngx_resolver.c Tue Jan 26 16:46:18 2016 +0300 +@@ -1292,7 +1292,7 @@ + times = 0; + + for (q = ngx_queue_head(&r->name_resend_queue); +- q != ngx_queue_sentinel(&r->name_resend_queue) || times++ < 100; ++ q != ngx_queue_sentinel(&r->name_resend_queue) && times++ < 100; + q = ngx_queue_next(q)) + { + rn = ngx_queue_data(q, ngx_resolver_node_t, queue); diff --git a/nginx-1.6.3-Resolver-fix-use-after-free-with-CNAME.patch b/nginx-1.6.3-Resolver-fix-use-after-free-with-CNAME.patch new file mode 100644 index 0000000..ba6a7d7 --- /dev/null +++ b/nginx-1.6.3-Resolver-fix-use-after-free-with-CNAME.patch @@ -0,0 +1,252 @@ +exporting patch: +# HG changeset patch +# User Roman Arutyunyan +# Date 1453816019 -10800 +# Tue Jan 26 16:46:59 2016 +0300 +# Branch stable-1.8 +# Node ID dac6eda40475f08b7372159d78dad1e13cd5bc7f +# Parent 5557bf31e25da68d5cda19dbc91d86f47430df1f +Resolver: fixed use-after-free memory accesses with CNAME. + +When several requests were waiting for a response, then after getting +a CNAME response only the last request's context had the name updated. +Contexts of other requests had the wrong name. This name was used by +ngx_resolve_name_done() to find the node to remove the request context +from. When the name was wrong, the request could not be properly +cancelled, its context was freed but stayed linked to the node's waiting +list. This happened e.g. when the first request was aborted or timed +out before the resolving completed. When it completed, this triggered +a use-after-free memory access by calling ctx->handler of already freed +request context. The bug manifests itself by +"could not cancel resolving" alerts in error_log. + +When a request was responded with a CNAME, the request context kept +the pointer to the original node's rn->u.cname. If the original node +expired before the resolving timed out or completed with an error, +this would trigger a use-after-free memory access via ctx->name in +ctx->handler(). + +The fix is to keep ctx->name unmodified. The name from context +is no longer used by ngx_resolve_name_done(). Instead, we now keep +the pointer to resolver node to which this request is linked. +Keeping the original name intact also improves logging. + +diff -r 5557bf31e25d -r dac6eda40475 src/core/ngx_resolver.c +--- a/src/core/ngx_resolver.c Tue Jan 26 16:46:48 2016 +0300 ++++ b/src/core/ngx_resolver.c Tue Jan 26 16:46:59 2016 +0300 +@@ -59,7 +59,7 @@ + static void ngx_resolver_cleanup(void *data); + static void ngx_resolver_cleanup_tree(ngx_resolver_t *r, ngx_rbtree_t *tree); + static ngx_int_t ngx_resolve_name_locked(ngx_resolver_t *r, +- ngx_resolver_ctx_t *ctx); ++ ngx_resolver_ctx_t *ctx, ngx_str_t *name); + static void ngx_resolver_expire(ngx_resolver_t *r, ngx_rbtree_t *tree, + ngx_queue_t *queue); + static ngx_int_t ngx_resolver_send_query(ngx_resolver_t *r, +@@ -375,7 +375,7 @@ + + /* lock name mutex */ + +- rc = ngx_resolve_name_locked(r, ctx); ++ rc = ngx_resolve_name_locked(r, ctx, &ctx->name); + + if (rc == NGX_OK) { + return NGX_OK; +@@ -402,7 +402,6 @@ + void + ngx_resolve_name_done(ngx_resolver_ctx_t *ctx) + { +- uint32_t hash; + ngx_resolver_t *r; + ngx_resolver_ctx_t *w, **p; + ngx_resolver_node_t *rn; +@@ -424,9 +423,7 @@ + + if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) { + +- hash = ngx_crc32_short(ctx->name.data, ctx->name.len); +- +- rn = ngx_resolver_lookup_name(r, &ctx->name, hash); ++ rn = ctx->node; + + if (rn) { + p = &rn->waiting; +@@ -467,20 +464,22 @@ + + + static ngx_int_t +-ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx) ++ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx, ++ ngx_str_t *name) + { + uint32_t hash; + ngx_int_t rc; ++ ngx_str_t cname; + ngx_uint_t naddrs; + ngx_addr_t *addrs; + ngx_resolver_ctx_t *next, *last; + ngx_resolver_node_t *rn; + +- ngx_strlow(ctx->name.data, ctx->name.data, ctx->name.len); +- +- hash = ngx_crc32_short(ctx->name.data, ctx->name.len); +- +- rn = ngx_resolver_lookup_name(r, &ctx->name, hash); ++ ngx_strlow(name->data, name->data, name->len); ++ ++ hash = ngx_crc32_short(name->data, name->len); ++ ++ rn = ngx_resolver_lookup_name(r, name, hash); + + if (rn) { + +@@ -554,10 +553,10 @@ + + if (ctx->recursion++ < NGX_RESOLVER_MAX_RECURSION) { + +- ctx->name.len = rn->cnlen; +- ctx->name.data = rn->u.cname; +- +- return ngx_resolve_name_locked(r, ctx); ++ cname.len = rn->cnlen; ++ cname.data = rn->u.cname; ++ ++ return ngx_resolve_name_locked(r, ctx, &cname); + } + + last->next = rn->waiting; +@@ -597,6 +596,11 @@ + rn->waiting = ctx; + ctx->state = NGX_AGAIN; + ++ do { ++ ctx->node = rn; ++ ctx = ctx->next; ++ } while (ctx); ++ + return NGX_AGAIN; + } + +@@ -635,14 +639,14 @@ + return NGX_ERROR; + } + +- rn->name = ngx_resolver_dup(r, ctx->name.data, ctx->name.len); ++ rn->name = ngx_resolver_dup(r, name->data, name->len); + if (rn->name == NULL) { + ngx_resolver_free(r, rn); + return NGX_ERROR; + } + + rn->node.key = hash; +- rn->nlen = (u_short) ctx->name.len; ++ rn->nlen = (u_short) name->len; + rn->query = NULL; + #if (NGX_HAVE_INET6) + rn->query6 = NULL; +@@ -651,7 +655,7 @@ + ngx_rbtree_insert(&r->name_rbtree, &rn->node); + } + +- rc = ngx_resolver_create_name_query(r, rn, &ctx->name); ++ rc = ngx_resolver_create_name_query(r, rn, name); + + if (rc == NGX_ERROR) { + goto failed; +@@ -715,6 +719,11 @@ + + ctx->state = NGX_AGAIN; + ++ do { ++ ctx->node = rn; ++ ctx = ctx->next; ++ } while (ctx); ++ + return NGX_AGAIN; + + failed: +@@ -837,6 +846,7 @@ + ctx->next = rn->waiting; + rn->waiting = ctx; + ctx->state = NGX_AGAIN; ++ ctx->node = rn; + + /* unlock addr mutex */ + +@@ -922,6 +932,7 @@ + /* unlock addr mutex */ + + ctx->state = NGX_AGAIN; ++ ctx->node = rn; + + return NGX_OK; + +@@ -952,17 +963,11 @@ + void + ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx) + { +- in_addr_t addr; + ngx_queue_t *expire_queue; + ngx_rbtree_t *tree; + ngx_resolver_t *r; + ngx_resolver_ctx_t *w, **p; +- struct sockaddr_in *sin; + ngx_resolver_node_t *rn; +-#if (NGX_HAVE_INET6) +- uint32_t hash; +- struct sockaddr_in6 *sin6; +-#endif + + r = ctx->resolver; + +@@ -991,21 +996,7 @@ + + if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) { + +- switch (ctx->addr.sockaddr->sa_family) { +- +-#if (NGX_HAVE_INET6) +- case AF_INET6: +- sin6 = (struct sockaddr_in6 *) ctx->addr.sockaddr; +- hash = ngx_crc32_short(sin6->sin6_addr.s6_addr, 16); +- rn = ngx_resolver_lookup_addr6(r, &sin6->sin6_addr, hash); +- break; +-#endif +- +- default: /* AF_INET */ +- sin = (struct sockaddr_in *) ctx->addr.sockaddr; +- addr = ntohl(sin->sin_addr.s_addr); +- rn = ngx_resolver_lookup_addr(r, addr); +- } ++ rn = ctx->node; + + if (rn) { + p = &rn->waiting; +@@ -1994,9 +1985,12 @@ + rn->waiting = NULL; + + if (ctx) { +- ctx->name = name; +- +- (void) ngx_resolve_name_locked(r, ctx); ++ ++ for (next = ctx; next; next = next->next) { ++ next->node = NULL; ++ } ++ ++ (void) ngx_resolve_name_locked(r, ctx, &name); + } + + ngx_resolver_free(r, rn->query); +diff -r 5557bf31e25d -r dac6eda40475 src/core/ngx_resolver.h +--- a/src/core/ngx_resolver.h Tue Jan 26 16:46:48 2016 +0300 ++++ b/src/core/ngx_resolver.h Tue Jan 26 16:46:59 2016 +0300 +@@ -161,6 +161,8 @@ + ngx_uint_t quick; /* unsigned quick:1; */ + ngx_uint_t recursion; + ngx_event_t *event; ++ ++ ngx_resolver_node_t *node; + }; + + diff --git a/nginx-1.6.3-Resolver-limit-CNAME-recursion.patch b/nginx-1.6.3-Resolver-limit-CNAME-recursion.patch new file mode 100644 index 0000000..f812ae5 --- /dev/null +++ b/nginx-1.6.3-Resolver-limit-CNAME-recursion.patch @@ -0,0 +1,68 @@ +exporting patch: +# HG changeset patch +# User Ruslan Ermilov +# Date 1453816034 -10800 +# Tue Jan 26 16:47:14 2016 +0300 +# Branch stable-1.8 +# Node ID 93d70d87914c350948ab701cc99569680320e198 +# Parent dac6eda40475f08b7372159d78dad1e13cd5bc7f +Resolver: limited CNAME recursion. + +Previously, the recursion was only limited for cached responses. + +diff -r dac6eda40475 -r 93d70d87914c src/core/ngx_resolver.c +--- a/src/core/ngx_resolver.c Tue Jan 26 16:46:59 2016 +0300 ++++ b/src/core/ngx_resolver.c Tue Jan 26 16:47:14 2016 +0300 +@@ -1981,24 +1981,40 @@ + + ngx_queue_insert_head(&r->name_expire_queue, &rn->queue); + +- ctx = rn->waiting; +- rn->waiting = NULL; +- +- if (ctx) { +- +- for (next = ctx; next; next = next->next) { +- next->node = NULL; +- } +- +- (void) ngx_resolve_name_locked(r, ctx, &name); +- } +- + ngx_resolver_free(r, rn->query); + rn->query = NULL; + #if (NGX_HAVE_INET6) + rn->query6 = NULL; + #endif + ++ ctx = rn->waiting; ++ rn->waiting = NULL; ++ ++ if (ctx) { ++ ++ if (ctx->recursion++ >= NGX_RESOLVER_MAX_RECURSION) { ++ ++ /* unlock name mutex */ ++ ++ do { ++ ctx->state = NGX_RESOLVE_NXDOMAIN; ++ next = ctx->next; ++ ++ ctx->handler(ctx); ++ ++ ctx = next; ++ } while (ctx); ++ ++ return; ++ } ++ ++ for (next = ctx; next; next = next->next) { ++ next->node = NULL; ++ } ++ ++ (void) ngx_resolve_name_locked(r, ctx, &name); ++ } ++ + /* unlock name mutex */ + + return; diff --git a/nginx.spec b/nginx.spec index 3c16a2b..d94ef12 100644 --- a/nginx.spec +++ b/nginx.spec @@ -21,7 +21,7 @@ Name: nginx Epoch: 1 Version: 1.6.3 -Release: 7%{?dist} +Release: 8%{?dist} Summary: A high performance web server and reverse proxy server Group: System Environment/Daemons @@ -47,6 +47,16 @@ Source104: 50x.html # -D_FORTIFY_SOURCE=2 causing warnings to turn into errors. Patch0: nginx-auto-cc-gcc.patch +# Patches taken from 1.8.1 release. Only the second patch in this series +# failed to apply and had to be modified. +Patch10: nginx-1.6.3-Resolver-fix-possible-segmentation-fault.patch +Patch11: nginx-1.6.3-Resolver-fix-crashes-in-timeout-handler.patch +Patch12: nginx-1.6.3-Resolver-fix-CNAME-processing.patch +Patch13: nginx-1.6.3-Resolver-change-ngx_resolver_create-arguments.patch +Patch14: nginx-1.6.3-Resolver-fix-use-after-free-with-CNAME.patch +Patch15: nginx-1.6.3-Resolver-limit-CNAME-recursion.patch + + BuildRequires: GeoIP-devel BuildRequires: gd-devel %if 0%{?with_gperftools} @@ -93,6 +103,12 @@ directories. %prep %setup -q %patch0 -p0 +%patch10 -p1 +%patch11 -p1 +%patch12 -p1 +%patch13 -p1 +%patch14 -p1 +%patch15 -p1 %build @@ -256,6 +272,11 @@ fi %changelog +* Tue Jan 26 2016 Jamie Nguyen - 1:1.6.3-8 +- CVE-2016-0747: Insufficient limits of CNAME resolution in resolver +- CVE-2016-0746: Use-after-free during CNAME response processing in resolver +- CVE-2016-0742: Invalid pointer dereference in resolver + * Sun Oct 04 2015 Jamie Nguyen - 1:1.6.3-7 - remove PID file before starting nginx (#1268621)