comparison src/os/win32/ngx_files.c @ 4673:dc6c658942a8

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.
author Maxim Dounin <mdounin@mdounin.ru>
date Tue, 05 Jun 2012 13:36:09 +0000
parents 13eb3193cd63
children 726346e1ae66
comparison
equal deleted inserted replaced
4671:af9342747669 4673:dc6c658942a8
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;