diff --git a/src/ngx_http_lua_control.c b/src/ngx_http_lua_control.c index 5cd1d64c..d8022cc5 100644 --- a/src/ngx_http_lua_control.c +++ b/src/ngx_http_lua_control.c @@ -248,6 +248,10 @@ ngx_http_lua_ngx_redirect(lua_State *L) "the headers"); } + if (ngx_http_lua_check_unsafe_string(r, p, len, "redirect uri") != NGX_OK) { + return luaL_error(L, "attempt to set unsafe redirect uri"); + } + uri = ngx_palloc(r->pool, len); if (uri == NULL) { return luaL_error(L, "no memory"); diff --git a/src/ngx_http_lua_headers_in.c b/src/ngx_http_lua_headers_in.c index c52cd13d..b8befc34 100644 --- a/src/ngx_http_lua_headers_in.c +++ b/src/ngx_http_lua_headers_in.c @@ -664,6 +664,14 @@ ngx_http_lua_set_input_header(ngx_http_request_t *r, ngx_str_t key, dd("set header value: %.*s", (int) value.len, value.data); + if (ngx_http_lua_check_unsafe_string(r, key.data, key.len, + "header name") != NGX_OK + || ngx_http_lua_check_unsafe_string(r, value.data, value.len, + "header value") != NGX_OK) + { + return NGX_ERROR; + } + hv.hash = ngx_hash_key_lc(key.data, key.len); hv.key = key; diff --git a/src/ngx_http_lua_headers_out.c b/src/ngx_http_lua_headers_out.c index cf94bd04..8cd36841 100644 --- a/src/ngx_http_lua_headers_out.c +++ b/src/ngx_http_lua_headers_out.c @@ -491,6 +491,14 @@ ngx_http_lua_set_output_header(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, dd("set header value: %.*s", (int) value.len, value.data); + if (ngx_http_lua_check_unsafe_string(r, key.data, key.len, + "header name") != NGX_OK + || ngx_http_lua_check_unsafe_string(r, value.data, value.len, + "header value") != NGX_OK) + { + return NGX_ERROR; + } + hv.hash = ngx_hash_key_lc(key.data, key.len); hv.key = key; diff --git a/src/ngx_http_lua_uri.c b/src/ngx_http_lua_uri.c index 3559b5c0..569cb502 100644 --- a/src/ngx_http_lua_uri.c +++ b/src/ngx_http_lua_uri.c @@ -55,6 +55,10 @@ ngx_http_lua_ngx_req_set_uri(lua_State *L) return luaL_error(L, "attempt to use zero-length uri"); } + if (ngx_http_lua_check_unsafe_string(r, p, len, "uri") != NGX_OK) { + return luaL_error(L, "attempt to set unsafe uri"); + } + if (n == 2) { luaL_checktype(L, 2, LUA_TBOOLEAN); @@ -107,4 +111,5 @@ ngx_http_lua_ngx_req_set_uri(lua_State *L) return 0; } + /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_util.c b/src/ngx_http_lua_util.c index c12262e8..515e14ad 100644 --- a/src/ngx_http_lua_util.c +++ b/src/ngx_http_lua_util.c @@ -4267,4 +4267,71 @@ ngx_http_lua_set_sa_restart(ngx_log_t *log) #endif +size_t +ngx_http_lua_escape_log(u_char *dst, u_char *src, size_t size) +{ + size_t n; + u_char c; + static u_char hex[] = "0123456789ABCDEF"; + + static uint32_t escape[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x00000004, /* 0000 0000 0000 0000 0000 0000 0000 0100 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x10000000, /* 0001 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + if (dst == NULL) { + + /* find the number of characters to be escaped */ + + n = 0; + + while (size) { + c = *src; + if (escape[c >> 5] & (1 << (c & 0x1f))) { + n += 4; + + } else { + n++; + } + + src++; + size--; + } + + return n; + } + + while (size) { + c = *src; + if (escape[c >> 5] & (1 << (c & 0x1f))) { + *dst++ = '\\'; + *dst++ = 'x'; + *dst++ = hex[*src >> 4]; + *dst++ = hex[*src & 0xf]; + src++; + + } else { + *dst++ = *src++; + } + + size--; + } + + return 0; +} + + /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_util.h b/src/ngx_http_lua_util.h index f08f4815..e1f4d75d 100644 --- a/src/ngx_http_lua_util.h +++ b/src/ngx_http_lua_util.h @@ -132,6 +132,12 @@ ngx_http_lua_ffi_check_context(ngx_http_lua_ctx_t *ctx, unsigned flags, } +#define ngx_http_lua_check_if_abortable(L, ctx) \ + if ((ctx)->no_abort) { \ + return luaL_error(L, "attempt to abort with pending subrequests"); \ + } + + #define ngx_http_lua_ssl_get_ctx(ssl_conn) \ SSL_get_ex_data(ssl_conn, ngx_http_lua_ssl_ctx_index) @@ -254,10 +260,7 @@ void ngx_http_lua_cleanup_free(ngx_http_request_t *r, void ngx_http_lua_set_sa_restart(ngx_log_t *log); #endif -#define ngx_http_lua_check_if_abortable(L, ctx) \ - if ((ctx)->no_abort) { \ - return luaL_error(L, "attempt to abort with pending subrequests"); \ - } +size_t ngx_http_lua_escape_log(u_char *dst, u_char *src, size_t size); static ngx_inline void @@ -485,6 +488,59 @@ ngx_inet_get_port(struct sockaddr *sa) #endif +static ngx_inline ngx_int_t +ngx_http_lua_check_unsafe_string(ngx_http_request_t *r, u_char *str, size_t len, + const char *name) +{ + size_t i, buf_len; + u_char c; + u_char *buf, *src = str; + + /* %00-%1F, %7F */ + + static uint32_t unsafe[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ + + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000 /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + }; + + for (i = 0; i < len; i++, str++) { + c = *str; + if (unsafe[c >> 5] & (1 << (c & 0x1f))) { + buf_len = ngx_http_lua_escape_log(NULL, src, len); + buf = ngx_palloc(r->pool, buf_len); + if (buf == NULL) { + return NGX_ERROR; + } + + ngx_http_lua_escape_log(buf, src, len); + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "unsafe byte \"0x%uxd\" in %s \"%*s\"", + (unsigned) c, name, buf_len, buf); + + ngx_pfree(r->pool, buf); + + return NGX_ERROR; + } + } + + return NGX_OK; +} + + extern ngx_uint_t ngx_http_lua_location_hash; extern ngx_uint_t ngx_http_lua_content_length_hash;