Merge of r4674, r4675, r4676: win32 fixes.
*) Win32: disallowed access to various non-canonical name variants. This includes trailings dots and spaces, NTFS streams (and short names, as previously checked). The checks are now also done in ngx_file_info(), thus allowing to use the "try_files" directive to protect external scripts. *) Win32: normalization of trailing dot inside uri. Windows treats "/directory./" identical to "/directory/". Do the same when working on Windows. Note that the behaviour is different from one with last path component (where multiple spaces and dots are ignored by Windows). *) Win32: uris with ":$" are now rejected. There are too many problems with special NTFS streams, notably "::$data", "::$index_allocation" and ":$i30:$index_allocation". For now we don't reject all URIs with ":" like Apache does as there are no good reasons seen yet, and there are multiple programs using it in URLs (e.g. MediaWiki).
This commit is contained in:
parent
5924995822
commit
da99f76591
|
@ -543,6 +543,13 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
|
|||
|
||||
switch (ch) {
|
||||
case '/':
|
||||
#if (NGX_WIN32)
|
||||
if (r->uri_ext == p) {
|
||||
r->complex_uri = 1;
|
||||
state = sw_uri;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
r->uri_ext = NULL;
|
||||
state = sw_after_slash_in_uri;
|
||||
break;
|
||||
|
@ -1117,6 +1124,12 @@ ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes)
|
|||
switch(ch) {
|
||||
#if (NGX_WIN32)
|
||||
case '\\':
|
||||
if (u - 2 >= r->uri.data
|
||||
&& *(u - 1) == '.' && *(u - 2) != '.')
|
||||
{
|
||||
u--;
|
||||
}
|
||||
|
||||
r->uri_ext = NULL;
|
||||
|
||||
if (p == r->uri_start + r->uri.len) {
|
||||
|
@ -1134,6 +1147,13 @@ ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes)
|
|||
break;
|
||||
#endif
|
||||
case '/':
|
||||
#if (NGX_WIN32)
|
||||
if (u - 2 >= r->uri.data
|
||||
&& *(u - 1) == '.' && *(u - 2) != '.')
|
||||
{
|
||||
u--;
|
||||
}
|
||||
#endif
|
||||
r->uri_ext = NULL;
|
||||
state = sw_slash;
|
||||
*u++ = ch;
|
||||
|
|
|
@ -812,7 +812,28 @@ ngx_http_process_request_line(ngx_event_t *rev)
|
|||
|
||||
#if (NGX_WIN32)
|
||||
{
|
||||
u_char *p;
|
||||
u_char *p, *last;
|
||||
|
||||
p = r->uri.data;
|
||||
last = r->uri.data + r->uri.len;
|
||||
|
||||
while (p < last) {
|
||||
|
||||
if (*p++ == ':') {
|
||||
|
||||
/*
|
||||
* this check covers "::$data", "::$index_allocation" and
|
||||
* ":$i30:$index_allocation"
|
||||
*/
|
||||
|
||||
if (p < last && *p == '$') {
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"client sent unsafe win32 URI");
|
||||
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p = r->uri.data + r->uri.len - 1;
|
||||
|
||||
|
@ -828,11 +849,6 @@ ngx_http_process_request_line(ngx_event_t *rev)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strncasecmp(p - 6, (u_char *) "::$data", 7) == 0) {
|
||||
p -= 7;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#define NGX_UTF16_BUFLEN 256
|
||||
|
||||
static ngx_int_t ngx_win32_check_filename(u_char *name, u_short *u,
|
||||
size_t len);
|
||||
static u_short *ngx_utf8_to_utf16(u_short *utf16, u_char *utf8, size_t *len);
|
||||
|
||||
|
||||
|
@ -20,8 +22,7 @@ ngx_fd_t
|
|||
ngx_open_file(u_char *name, u_long mode, u_long create, u_long access)
|
||||
{
|
||||
size_t len;
|
||||
u_long n;
|
||||
u_short *u, *lu;
|
||||
u_short *u;
|
||||
ngx_fd_t fd;
|
||||
ngx_err_t err;
|
||||
u_short utf16[NGX_UTF16_BUFLEN];
|
||||
|
@ -34,25 +35,11 @@ ngx_open_file(u_char *name, u_long mode, u_long create, u_long access)
|
|||
}
|
||||
|
||||
fd = INVALID_HANDLE_VALUE;
|
||||
lu = NULL;
|
||||
|
||||
if (create == NGX_FILE_OPEN) {
|
||||
|
||||
lu = malloc(len * 2);
|
||||
if (lu == NULL) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
n = GetLongPathNameW(u, lu, len);
|
||||
|
||||
if (n == 0) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (n != len - 1 || _wcsicmp(u, lu) != 0) {
|
||||
ngx_set_errno(NGX_ENOENT);
|
||||
goto failed;
|
||||
}
|
||||
if (create == NGX_FILE_OPEN
|
||||
&& ngx_win32_check_filename(name, u, len) != NGX_OK)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
|
||||
fd = CreateFileW(u, mode,
|
||||
|
@ -61,18 +48,12 @@ ngx_open_file(u_char *name, u_long mode, u_long create, u_long access)
|
|||
|
||||
failed:
|
||||
|
||||
err = ngx_errno;
|
||||
|
||||
if (lu) {
|
||||
ngx_free(lu);
|
||||
}
|
||||
|
||||
if (u != utf16) {
|
||||
err = ngx_errno;
|
||||
ngx_free(u);
|
||||
ngx_set_errno(err);
|
||||
}
|
||||
|
||||
ngx_set_errno(err);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
@ -294,14 +275,14 @@ ngx_file_info(u_char *file, ngx_file_info_t *sb)
|
|||
return NGX_FILE_ERROR;
|
||||
}
|
||||
|
||||
rc = GetFileAttributesExW(u, GetFileExInfoStandard, &fa);
|
||||
rc = NGX_FILE_ERROR;
|
||||
|
||||
if (u != utf16) {
|
||||
err = ngx_errno;
|
||||
ngx_free(u);
|
||||
ngx_set_errno(err);
|
||||
if (ngx_win32_check_filename(file, u, len) != NGX_OK) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
rc = GetFileAttributesExW(u, GetFileExInfoStandard, &fa);
|
||||
|
||||
sb->dwFileAttributes = fa.dwFileAttributes;
|
||||
sb->ftCreationTime = fa.ftCreationTime;
|
||||
sb->ftLastAccessTime = fa.ftLastAccessTime;
|
||||
|
@ -309,6 +290,14 @@ ngx_file_info(u_char *file, ngx_file_info_t *sb)
|
|||
sb->nFileSizeHigh = fa.nFileSizeHigh;
|
||||
sb->nFileSizeLow = fa.nFileSizeLow;
|
||||
|
||||
failed:
|
||||
|
||||
if (u != utf16) {
|
||||
err = ngx_errno;
|
||||
ngx_free(u);
|
||||
ngx_set_errno(err);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -640,6 +629,148 @@ ngx_fs_bsize(u_char *name)
|
|||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_win32_check_filename(u_char *name, u_short *u, size_t len)
|
||||
{
|
||||
u_char *p, ch;
|
||||
u_long n;
|
||||
u_short *lu;
|
||||
ngx_err_t err;
|
||||
enum {
|
||||
sw_start = 0,
|
||||
sw_normal,
|
||||
sw_after_slash,
|
||||
sw_after_colon,
|
||||
sw_after_dot
|
||||
} state;
|
||||
|
||||
/* check for NTFS streams (":"), trailing dots and spaces */
|
||||
|
||||
lu = NULL;
|
||||
state = sw_start;
|
||||
|
||||
for (p = name; *p; p++) {
|
||||
ch = *p;
|
||||
|
||||
switch (state) {
|
||||
|
||||
case sw_start:
|
||||
|
||||
/*
|
||||
* skip till first "/" to allow paths starting with drive and
|
||||
* relative path, like "c:html/"
|
||||
*/
|
||||
|
||||
if (ch == '/' || ch == '\\') {
|
||||
state = sw_after_slash;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case sw_normal:
|
||||
|
||||
if (ch == ':') {
|
||||
state = sw_after_colon;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ch == '.' || ch == ' ') {
|
||||
state = sw_after_dot;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ch == '/' || ch == '\\') {
|
||||
state = sw_after_slash;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case sw_after_slash:
|
||||
|
||||
if (ch == '/' || ch == '\\') {
|
||||
break;
|
||||
}
|
||||
|
||||
if (ch == '.') {
|
||||
break;
|
||||
}
|
||||
|
||||
if (ch == ':') {
|
||||
state = sw_after_colon;
|
||||
break;
|
||||
}
|
||||
|
||||
state = sw_normal;
|
||||
break;
|
||||
|
||||
case sw_after_colon:
|
||||
|
||||
if (ch == '/' || ch == '\\') {
|
||||
state = sw_after_slash;
|
||||
break;
|
||||
}
|
||||
|
||||
goto invalid;
|
||||
|
||||
case sw_after_dot:
|
||||
|
||||
if (ch == '/' || ch == '\\') {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
if (ch == ':') {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
if (ch == '.' || ch == ' ') {
|
||||
break;
|
||||
}
|
||||
|
||||
state = sw_normal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (state == sw_after_dot) {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
/* check if long name match */
|
||||
|
||||
lu = malloc(len * 2);
|
||||
if (lu == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
n = GetLongPathNameW(u, lu, len);
|
||||
|
||||
if (n == 0) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (n != len - 1 || _wcsicmp(u, lu) != 0) {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
|
||||
invalid:
|
||||
|
||||
ngx_set_errno(NGX_ENOENT);
|
||||
|
||||
failed:
|
||||
|
||||
if (lu) {
|
||||
err = ngx_errno;
|
||||
ngx_free(lu);
|
||||
ngx_set_errno(err);
|
||||
}
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static u_short *
|
||||
ngx_utf8_to_utf16(u_short *utf16, u_char *utf8, size_t *len)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue