[PATCH v2] Make ngx_http_parse_unsafe_uri() to be able to unescape uri
Raman Shishniou
rommer at activecloud.com
Wed Dec 11 05:48:54 UTC 2013
# HG changeset patch
# User Raman Shishniou <rommer at activecloud.com>
# Date 1386740673 -10800
# Node ID 353840924c0ffd1203c8d0894f2128a2548f80c5
# Parent a279d2a33dbfbad511f4415f833c35a60e46bb76
Make ngx_http_parse_unsafe_uri() to be able to unescape uri
It makes possible to use an escaped uri with "X-Accel-Redirect" header in
http_upstream module and "Destination" header http_dav module. No need
to unescape uri in http_ssi_filter module any more.
diff --git a/src/http/modules/ngx_http_ssi_filter_module.c b/src/http/modules/ngx_http_ssi_filter_module.c
--- a/src/http/modules/ngx_http_ssi_filter_module.c
+++ b/src/http/modules/ngx_http_ssi_filter_module.c
@@ -1982,8 +1982,6 @@
ngx_http_ssi_include(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
ngx_str_t **params)
{
- u_char *dst, *src;
- size_t len;
ngx_int_t rc, key;
ngx_str_t *uri, *file, *wait, *set, *stub, args;
ngx_buf_t *b;
@@ -2054,18 +2052,6 @@
return rc;
}
- dst = uri->data;
- src = uri->data;
-
- ngx_unescape_uri(&dst, &src, uri->len, NGX_UNESCAPE_URI);
-
- len = (uri->data + uri->len) - src;
- if (len) {
- dst = ngx_movemem(dst, src, len);
- }
-
- uri->len = dst - uri->data;
-
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"ssi include: \"%V\"", uri);
diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -9,6 +9,8 @@
#include <ngx_core.h>
#include <ngx_http.h>
+static ngx_inline ngx_int_t
+ngx_http_parse_test_doubledot(const u_char *p, const u_char *begin);
static uint32_t usual[] = {
0xffffdbfe, /* 1111 1111 1111 1111 1101 1011 1111 1110 */
@@ -1776,35 +1778,75 @@
}
+static ngx_inline ngx_int_t
+ngx_http_parse_test_doubledot(const u_char *p, const u_char *begin)
+{
+ /* assume *p is the path separator or
+ p points to the next byte after the end */
+
+ if (p - 2 > begin &&
+ *(p - 1) == '.' && *(p - 2) == '.' &&
+ ngx_path_separator(*(p - 3))) {
+ return 1;
+ }
+
+ if (p - 2 == begin &&
+ begin[0] == '.' && begin[1] == '.') {
+ return 1;
+ }
+
+ return 0;
+}
+
+
ngx_int_t
ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri,
ngx_str_t *args, ngx_uint_t *flags)
{
- u_char ch, *p;
- size_t len;
-
+ u_char *src, *dst, *newuri, ch, c, decoded;
+ ngx_int_t unescape;
+ size_t len;
+ enum {
+ sw_usual = 0,
+ sw_quoted,
+ sw_quoted_second
+ } state;
+
+ src = uri->data;
len = uri->len;
- p = uri->data;
-
- if (len == 0 || p[0] == '?') {
+ unescape = 0;
+
+ if (len == 0 || src[0] == '?') {
goto unsafe;
}
- if (p[0] == '.' && len == 3 && p[1] == '.' && (ngx_path_separator(p[2]))) {
- goto unsafe;
- }
-
for ( /* void */ ; len; len--) {
- ch = *p++;
+ ch = *src++;
if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
continue;
}
+ if (ch == '%') {
+ unescape++;
+ continue;
+ }
+
if (ch == '?') {
args->len = len - 1;
- args->data = p;
+ args->data = src;
+
+ if (unescape) {
+ src--;
+ goto unescape;
+ }
+
+ /* detect "/.." at the end or whole uri is ".." */
+ if (ngx_http_parse_test_doubledot(src - 1, uri->data)) {
+ goto unsafe;
+ }
+
uri->len -= len;
return NGX_OK;
@@ -1814,16 +1856,151 @@
goto unsafe;
}
- if (ngx_path_separator(ch) && len > 2) {
-
- /* detect "/../" */
-
- if (p[0] == '.' && p[1] == '.' && ngx_path_separator(p[2])) {
+ /* detect "../" at the beginning or "/../" in the middle */
+ if (ngx_path_separator(ch) &&
+ ngx_http_parse_test_doubledot(src - 1, uri->data)) {
+ goto unsafe;
+ }
+ }
+
+ /* detect "/.." at the end or whole uri is ".." */
+ if (ngx_http_parse_test_doubledot(src, uri->data)) {
+ goto unsafe;
+ }
+
+ if (!unescape) {
+ return NGX_OK;
+ }
+
+unescape:
+
+ len = src - uri->data;
+ newuri = ngx_pnalloc(r->pool, len);
+
+ if (newuri == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(newuri, uri->data, len);
+
+ src = uri->data;
+ dst = newuri;
+ decoded = 0;
+ state = 0;
+
+ for ( /* void */ ; len; len--) {
+
+ ch = *src++;
+
+ switch (state) {
+
+ case sw_usual:
+ if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
+ *dst++ = ch;
+ break;
+ }
+
+ if (ch == '%') {
+ state = sw_quoted;
+ break;
+ }
+
+ /* detect "../" at the beginning or "/../" in the middle */
+ if (ngx_path_separator(ch) &&
+ ngx_http_parse_test_doubledot(dst, newuri)) {
goto unsafe;
}
+
+ *dst++ = ch;
+ break;
+
+ case sw_quoted:
+ if (ch >= '0' && ch <= '9') {
+ decoded = (u_char) (ch - '0');
+ state = sw_quoted_second;
+ break;
+ }
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'f') {
+ decoded = (u_char) (c - 'a' + 10);
+ state = sw_quoted_second;
+ break;
+ }
+
+ if (ch == '%') {
+ *dst++ = '%';
+ break;
+ }
+
+ *dst++ = '%';
+ *dst++ = ch;
+ state = sw_usual;
+ break;
+
+ case sw_quoted_second:
+
+ state = sw_usual;
+
+ if (ch >= '0' && ch <= '9') {
+ ch = (u_char) ((decoded << 4) + ch - '0');
+
+ if (ch == '\0') {
+ goto unsafe;
+ }
+
+ *dst++ = ch;
+ break;
+ }
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'f') {
+ ch = (u_char) ((decoded << 4) + c - 'a' + 10);
+
+ /* detect "../" at the beginning or "/../" in the middle */
+ if (ngx_path_separator(ch) &&
+ ngx_http_parse_test_doubledot(dst, newuri)) {
+ goto unsafe;
+ }
+
+ *dst++ = ch;
+ break;
+ }
+
+ if (ch == '%') {
+ *dst++ = '%';
+ *dst++ = *(src - 2);
+ state = sw_quoted;
+ break;
+ }
+
+ *dst++ = '%';
+ *dst++ = *(src - 2);
+ *dst++ = ch;
+ break;
}
}
+ switch (state) {
+ case sw_usual:
+ break;
+ case sw_quoted:
+ *dst++ = '%';
+ break;
+ case sw_quoted_second:
+ *dst++ = '%';
+ *dst++ = *(src - 1);
+ break;
+ }
+
+ /* detect "/.." at the end or whole uri is ".." */
+ if (ngx_http_parse_test_doubledot(dst, newuri)) {
+ goto unsafe;
+ }
+
+ uri->len = dst - newuri;
+ uri->data = newuri;
+
return NGX_OK;
unsafe:
More information about the nginx-devel
mailing list