[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