[PATCH] Make ngx_http_parse_unsafe_uri() to be able to unescape uri
Raman Shishniou
rommer at activecloud.com
Sat Dec 7 23:40:26 UTC 2013
# HG changeset patch
# User Raman Shishniou <rommer at activecloud.com>
# Date 1386459301 -10800
# Node ID 54f3670e04e82e00aa424d9773868749026bc693
# Parent 58716fd3bd2d63c93b0c04fa121232b7126e724b
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_dav_module.c b/src/http/modules/ngx_http_dav_module.c
--- a/src/http/modules/ngx_http_dav_module.c
+++ b/src/http/modules/ngx_http_dav_module.c
@@ -517,7 +517,7 @@
size_t len, root;
ngx_err_t err;
ngx_int_t rc, depth;
- ngx_uint_t overwrite, slash, dir, flags;
+ ngx_uint_t overwrite, slash, dir;
ngx_str_t path, uri, duri, args;
ngx_tree_ctx_t tree;
ngx_copy_file_t cf;
@@ -604,9 +604,8 @@
duri.len = last - p;
duri.data = p;
- flags = 0;
- if (ngx_http_parse_unsafe_uri(r, &duri, &args, &flags) != NGX_OK) {
+ if (ngx_http_parse_unsafe_uri(r, &duri, &args, 0) != NGX_OK) {
goto invalid_destination;
}
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,25 +2052,13 @@
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);
ngx_str_null(&args);
- flags = NGX_HTTP_LOG_UNSAFE;
-
- if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) != NGX_OK) {
+
+ if (ngx_http_parse_unsafe_uri(r, uri, &args,
+ NGX_HTTP_LOG_UNSAFE) != NGX_OK) {
return NGX_HTTP_SSI_ERROR;
}
@@ -2147,6 +2133,8 @@
}
}
+ flags = 0;
+
if (wait) {
flags |= NGX_HTTP_SUBREQUEST_WAITED;
}
diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -100,7 +100,7 @@
ngx_int_t ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b,
ngx_http_status_t *status);
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);
+ ngx_str_t *args, ngx_uint_t flags);
ngx_int_t ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b,
ngx_uint_t allow_underscores);
ngx_int_t ngx_http_parse_multi_header_lines(ngx_array_t *headers,
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
@@ -1778,57 +1778,233 @@
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)
+ ngx_str_t *args, ngx_uint_t flags)
{
- u_char ch, *p;
- size_t len;
-
+ u_char *src, *dst, *newuri, ch, c, decoded;
+ size_t len;
+ enum {
+ sw_usual = 0,
+ sw_quoted,
+ sw_quoted_second
+ } state;
+
+ dst = NULL;
+ newuri = NULL;
+ src = uri->data;
len = uri->len;
- p = uri->data;
-
- if (len == 0 || p[0] == '?') {
+
+ state = 0;
+ decoded = 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++;
-
- if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
- continue;
- }
-
- if (ch == '?') {
- args->len = len - 1;
- args->data = p;
- uri->len -= len;
-
- return NGX_OK;
- }
-
- if (ch == '\0') {
- goto unsafe;
- }
-
- if (ngx_path_separator(ch) && len > 2) {
-
- /* detect "/../" */
-
- if (p[0] == '.' && p[1] == '.' && ngx_path_separator(p[2])) {
+ while (len--) {
+
+ ch = *src++;
+
+ switch (state) {
+ case sw_usual:
+ if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
+ if (dst != NULL) {
+ *dst++ = ch;
+ }
+ break;
+ }
+
+ if (ch == '\0') {
goto unsafe;
}
+
+ if (ch == '?') {
+ goto args;
+ }
+
+ if (ch == '%') {
+ if (dst == NULL) {
+ newuri = ngx_pnalloc(r->pool, uri->len);
+ if (newuri == NULL) {
+ return NGX_ERROR;
+ }
+ if (src - 1 > uri->data) {
+ ngx_memcpy(newuri, uri->data, src - uri->data - 1);
+ dst = newuri + (src - uri->data - 1);
+ } else {
+ dst = newuri;
+ }
+ }
+ state = sw_quoted;
+ break;
+ }
+
+ /* detect "../" from begin or "/../" in the middle */
+ if (ngx_path_separator(ch)) {
+ if (dst == NULL) {
+
+ if (src - 2 == uri->data &&
+ *(src - 2) == '.' && *(src - 1) == '.') {
+ goto unsafe;
+ }
+
+ if (src - 2 > uri->data &&
+ ngx_path_separator(*(dst - 3)) &&
+ *(src - 2) == '.' && *(src - 1) == '.') {
+ goto unsafe;
+ }
+
+ } else {
+
+ if (dst - 2 == newuri &&
+ newuri[0] == '.' && newuri[1] == '.') {
+ goto unsafe;
+ }
+
+ if (dst - 2 > newuri &&
+ ngx_path_separator(*(dst - 3)) &&
+ *(dst - 2) == '.' && *(dst - 1) == '.') {
+ goto unsafe;
+ }
+
+ }
+ }
+
+ if (dst != NULL) {
+ *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 == '\0') {
+ goto unsafe;
+ }
+
+ if (ch == '%') {
+ *dst++ = '%';
+ break;
+ }
+
+ if (ch == '?') {
+ *dst++ = '%';
+ goto args;
+ }
+
+ *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 "../" from begin or "/../" in the middle */
+ if (ngx_path_separator(ch)) {
+
+ if (dst - 2 == newuri &&
+ newuri[0] == '.' && newuri[1] == '.') {
+ goto unsafe;
+ }
+
+ if (dst - 2 > newuri &&
+ ngx_path_separator(*(dst - 3)) &&
+ *(dst - 2) == '.' && *(dst - 1) == '.') {
+ goto unsafe;
+ }
+ }
+
+ *dst++ = ch;
+ break;
+ }
+
+ if (ch == '\0') {
+ goto unsafe;
+ }
+
+ if (ch == '%') {
+ *dst++ = '%';
+ *dst++ = *(src - 2);
+ state = sw_quoted;
+ break;
+ }
+
+ if (ch == '?') {
+ *dst++ = '%';
+ *dst++ = *(src - 2);
+ goto args;
+ }
+
+ *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;
+ }
+
+ if (dst != NULL) {
+ uri->len = dst - newuri;
+ uri->data = newuri;
+ }
+
return NGX_OK;
+args:
+
+ args->len = len;
+ args->data = src;
+
+ if (dst != NULL) {
+ uri->len = dst - newuri;
+ uri->data = newuri;
+ } else {
+ uri->len = src - uri->data;
+ }
+
+ return NGX_OK;
+
unsafe:
- if (*flags & NGX_HTTP_LOG_UNSAFE) {
+ if (flags & NGX_HTTP_LOG_UNSAFE) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"unsafe URI \"%V\" was detected", uri);
}
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -1994,7 +1994,7 @@
ngx_http_upstream_process_headers(ngx_http_request_t *r, ngx_http_upstream_t *u)
{
ngx_str_t *uri, args;
- ngx_uint_t i, flags;
+ ngx_uint_t i;
ngx_list_part_t *part;
ngx_table_elt_t *h;
ngx_http_upstream_header_t *hh;
@@ -2036,9 +2036,9 @@
uri = &u->headers_in.x_accel_redirect->value;
ngx_str_null(&args);
- flags = NGX_HTTP_LOG_UNSAFE;
-
- if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) != NGX_OK) {
+
+ if (ngx_http_parse_unsafe_uri(r, uri, &args,
+ NGX_HTTP_LOG_UNSAFE) != NGX_OK) {
ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
return NGX_DONE;
}
More information about the nginx-devel
mailing list