Mercurial > hg > nginx
comparison src/os/win32/ngx_files.c @ 4678:01dbbe7236ee stable-1.2
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).
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Tue, 05 Jun 2012 13:52:37 +0000 |
parents | 13eb3193cd63 |
children | 726346e1ae66 |
comparison
equal
deleted
inserted
replaced
4672:4a4516a725dc | 4678:01dbbe7236ee |
---|---|
9 #include <ngx_core.h> | 9 #include <ngx_core.h> |
10 | 10 |
11 | 11 |
12 #define NGX_UTF16_BUFLEN 256 | 12 #define NGX_UTF16_BUFLEN 256 |
13 | 13 |
14 static ngx_int_t ngx_win32_check_filename(u_char *name, u_short *u, | |
15 size_t len); | |
14 static u_short *ngx_utf8_to_utf16(u_short *utf16, u_char *utf8, size_t *len); | 16 static u_short *ngx_utf8_to_utf16(u_short *utf16, u_char *utf8, size_t *len); |
15 | 17 |
16 | 18 |
17 /* FILE_FLAG_BACKUP_SEMANTICS allows to obtain a handle to a directory */ | 19 /* FILE_FLAG_BACKUP_SEMANTICS allows to obtain a handle to a directory */ |
18 | 20 |
19 ngx_fd_t | 21 ngx_fd_t |
20 ngx_open_file(u_char *name, u_long mode, u_long create, u_long access) | 22 ngx_open_file(u_char *name, u_long mode, u_long create, u_long access) |
21 { | 23 { |
22 size_t len; | 24 size_t len; |
23 u_long n; | 25 u_short *u; |
24 u_short *u, *lu; | |
25 ngx_fd_t fd; | 26 ngx_fd_t fd; |
26 ngx_err_t err; | 27 ngx_err_t err; |
27 u_short utf16[NGX_UTF16_BUFLEN]; | 28 u_short utf16[NGX_UTF16_BUFLEN]; |
28 | 29 |
29 len = NGX_UTF16_BUFLEN; | 30 len = NGX_UTF16_BUFLEN; |
32 if (u == NULL) { | 33 if (u == NULL) { |
33 return INVALID_HANDLE_VALUE; | 34 return INVALID_HANDLE_VALUE; |
34 } | 35 } |
35 | 36 |
36 fd = INVALID_HANDLE_VALUE; | 37 fd = INVALID_HANDLE_VALUE; |
37 lu = NULL; | 38 |
38 | 39 if (create == NGX_FILE_OPEN |
39 if (create == NGX_FILE_OPEN) { | 40 && ngx_win32_check_filename(name, u, len) != NGX_OK) |
40 | 41 { |
41 lu = malloc(len * 2); | 42 goto failed; |
42 if (lu == NULL) { | |
43 goto failed; | |
44 } | |
45 | |
46 n = GetLongPathNameW(u, lu, len); | |
47 | |
48 if (n == 0) { | |
49 goto failed; | |
50 } | |
51 | |
52 if (n != len - 1 || _wcsicmp(u, lu) != 0) { | |
53 ngx_set_errno(NGX_ENOENT); | |
54 goto failed; | |
55 } | |
56 } | 43 } |
57 | 44 |
58 fd = CreateFileW(u, mode, | 45 fd = CreateFileW(u, mode, |
59 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, | 46 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, |
60 NULL, create, FILE_FLAG_BACKUP_SEMANTICS, NULL); | 47 NULL, create, FILE_FLAG_BACKUP_SEMANTICS, NULL); |
61 | 48 |
62 failed: | 49 failed: |
63 | 50 |
64 err = ngx_errno; | |
65 | |
66 if (lu) { | |
67 ngx_free(lu); | |
68 } | |
69 | |
70 if (u != utf16) { | 51 if (u != utf16) { |
52 err = ngx_errno; | |
71 ngx_free(u); | 53 ngx_free(u); |
72 } | 54 ngx_set_errno(err); |
73 | 55 } |
74 ngx_set_errno(err); | |
75 | 56 |
76 return fd; | 57 return fd; |
77 } | 58 } |
78 | 59 |
79 | 60 |
292 | 273 |
293 if (u == NULL) { | 274 if (u == NULL) { |
294 return NGX_FILE_ERROR; | 275 return NGX_FILE_ERROR; |
295 } | 276 } |
296 | 277 |
278 rc = NGX_FILE_ERROR; | |
279 | |
280 if (ngx_win32_check_filename(file, u, len) != NGX_OK) { | |
281 goto failed; | |
282 } | |
283 | |
297 rc = GetFileAttributesExW(u, GetFileExInfoStandard, &fa); | 284 rc = GetFileAttributesExW(u, GetFileExInfoStandard, &fa); |
298 | |
299 if (u != utf16) { | |
300 err = ngx_errno; | |
301 ngx_free(u); | |
302 ngx_set_errno(err); | |
303 } | |
304 | 285 |
305 sb->dwFileAttributes = fa.dwFileAttributes; | 286 sb->dwFileAttributes = fa.dwFileAttributes; |
306 sb->ftCreationTime = fa.ftCreationTime; | 287 sb->ftCreationTime = fa.ftCreationTime; |
307 sb->ftLastAccessTime = fa.ftLastAccessTime; | 288 sb->ftLastAccessTime = fa.ftLastAccessTime; |
308 sb->ftLastWriteTime = fa.ftLastWriteTime; | 289 sb->ftLastWriteTime = fa.ftLastWriteTime; |
309 sb->nFileSizeHigh = fa.nFileSizeHigh; | 290 sb->nFileSizeHigh = fa.nFileSizeHigh; |
310 sb->nFileSizeLow = fa.nFileSizeLow; | 291 sb->nFileSizeLow = fa.nFileSizeLow; |
292 | |
293 failed: | |
294 | |
295 if (u != utf16) { | |
296 err = ngx_errno; | |
297 ngx_free(u); | |
298 ngx_set_errno(err); | |
299 } | |
311 | 300 |
312 return rc; | 301 return rc; |
313 } | 302 } |
314 | 303 |
315 | 304 |
638 | 627 |
639 return sc * bs; | 628 return sc * bs; |
640 } | 629 } |
641 | 630 |
642 | 631 |
632 static ngx_int_t | |
633 ngx_win32_check_filename(u_char *name, u_short *u, size_t len) | |
634 { | |
635 u_char *p, ch; | |
636 u_long n; | |
637 u_short *lu; | |
638 ngx_err_t err; | |
639 enum { | |
640 sw_start = 0, | |
641 sw_normal, | |
642 sw_after_slash, | |
643 sw_after_colon, | |
644 sw_after_dot | |
645 } state; | |
646 | |
647 /* check for NTFS streams (":"), trailing dots and spaces */ | |
648 | |
649 lu = NULL; | |
650 state = sw_start; | |
651 | |
652 for (p = name; *p; p++) { | |
653 ch = *p; | |
654 | |
655 switch (state) { | |
656 | |
657 case sw_start: | |
658 | |
659 /* | |
660 * skip till first "/" to allow paths starting with drive and | |
661 * relative path, like "c:html/" | |
662 */ | |
663 | |
664 if (ch == '/' || ch == '\\') { | |
665 state = sw_after_slash; | |
666 } | |
667 | |
668 break; | |
669 | |
670 case sw_normal: | |
671 | |
672 if (ch == ':') { | |
673 state = sw_after_colon; | |
674 break; | |
675 } | |
676 | |
677 if (ch == '.' || ch == ' ') { | |
678 state = sw_after_dot; | |
679 break; | |
680 } | |
681 | |
682 if (ch == '/' || ch == '\\') { | |
683 state = sw_after_slash; | |
684 break; | |
685 } | |
686 | |
687 break; | |
688 | |
689 case sw_after_slash: | |
690 | |
691 if (ch == '/' || ch == '\\') { | |
692 break; | |
693 } | |
694 | |
695 if (ch == '.') { | |
696 break; | |
697 } | |
698 | |
699 if (ch == ':') { | |
700 state = sw_after_colon; | |
701 break; | |
702 } | |
703 | |
704 state = sw_normal; | |
705 break; | |
706 | |
707 case sw_after_colon: | |
708 | |
709 if (ch == '/' || ch == '\\') { | |
710 state = sw_after_slash; | |
711 break; | |
712 } | |
713 | |
714 goto invalid; | |
715 | |
716 case sw_after_dot: | |
717 | |
718 if (ch == '/' || ch == '\\') { | |
719 goto invalid; | |
720 } | |
721 | |
722 if (ch == ':') { | |
723 goto invalid; | |
724 } | |
725 | |
726 if (ch == '.' || ch == ' ') { | |
727 break; | |
728 } | |
729 | |
730 state = sw_normal; | |
731 break; | |
732 } | |
733 } | |
734 | |
735 if (state == sw_after_dot) { | |
736 goto invalid; | |
737 } | |
738 | |
739 /* check if long name match */ | |
740 | |
741 lu = malloc(len * 2); | |
742 if (lu == NULL) { | |
743 return NGX_ERROR; | |
744 } | |
745 | |
746 n = GetLongPathNameW(u, lu, len); | |
747 | |
748 if (n == 0) { | |
749 goto failed; | |
750 } | |
751 | |
752 if (n != len - 1 || _wcsicmp(u, lu) != 0) { | |
753 goto invalid; | |
754 } | |
755 | |
756 return NGX_OK; | |
757 | |
758 invalid: | |
759 | |
760 ngx_set_errno(NGX_ENOENT); | |
761 | |
762 failed: | |
763 | |
764 if (lu) { | |
765 err = ngx_errno; | |
766 ngx_free(lu); | |
767 ngx_set_errno(err); | |
768 } | |
769 | |
770 return NGX_ERROR; | |
771 } | |
772 | |
773 | |
643 static u_short * | 774 static u_short * |
644 ngx_utf8_to_utf16(u_short *utf16, u_char *utf8, size_t *len) | 775 ngx_utf8_to_utf16(u_short *utf16, u_char *utf8, size_t *len) |
645 { | 776 { |
646 u_char *p; | 777 u_char *p; |
647 u_short *u, *last; | 778 u_short *u, *last; |