Disable symlinks: fixed edge cases of path handling.
This includes non-absolute pathnames, multiple slashes and trailing
slashes. In collaboration with Valentin Bartenev.
Maxim Dounin
10 years ago
561 | 561 | |
562 | 562 | #else |
563 | 563 | |
564 | u_char *p, *cp, *end; | |
565 | ngx_fd_t at_fd; | |
566 | ngx_str_t at_name; | |
564 | u_char *p, *cp, *end; | |
565 | ngx_fd_t at_fd; | |
566 | ngx_str_t at_name; | |
567 | ngx_file_info_t fi; | |
567 | 568 | |
568 | 569 | if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) { |
569 | 570 | fd = ngx_open_file(name->data, mode, create, access); |
577 | 578 | return fd; |
578 | 579 | } |
579 | 580 | |
580 | at_fd = ngx_openat_file(AT_FDCWD, "/", NGX_FILE_RDONLY|NGX_FILE_NONBLOCK, | |
581 | NGX_FILE_OPEN, 0); | |
582 | ||
583 | if (at_fd == NGX_INVALID_FILE) { | |
584 | of->err = ngx_errno; | |
585 | of->failed = ngx_openat_file_n; | |
586 | return NGX_INVALID_FILE; | |
587 | } | |
588 | ||
581 | p = name->data; | |
582 | end = p + name->len; | |
583 | ||
584 | at_fd = AT_FDCWD; | |
589 | 585 | at_name = *name; |
590 | at_name.len = 1; | |
591 | ||
592 | end = name->data + name->len; | |
593 | p = name->data + 1; | |
586 | ||
587 | if (p[0] == '/') { | |
588 | at_fd = ngx_openat_file(at_fd, "/", | |
589 | NGX_FILE_RDONLY|NGX_FILE_NONBLOCK, | |
590 | NGX_FILE_OPEN, 0); | |
591 | ||
592 | if (at_fd == NGX_FILE_ERROR) { | |
593 | of->err = ngx_errno; | |
594 | of->failed = ngx_openat_file_n; | |
595 | return NGX_FILE_ERROR; | |
596 | } | |
597 | ||
598 | at_name.len = 1; | |
599 | p++; | |
600 | } | |
594 | 601 | |
595 | 602 | for ( ;; ) { |
596 | 603 | cp = ngx_strlchr(p, end, '/'); |
598 | 605 | break; |
599 | 606 | } |
600 | 607 | |
608 | if (cp == p) { | |
609 | p++; | |
610 | continue; | |
611 | } | |
612 | ||
601 | 613 | *cp = '\0'; |
602 | 614 | |
603 | 615 | if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER) { |
627 | 639 | p = cp + 1; |
628 | 640 | at_fd = fd; |
629 | 641 | at_name.len = cp - at_name.data; |
642 | } | |
643 | ||
644 | if (p == end && at_fd != AT_FDCWD) { | |
645 | ||
646 | /* | |
647 | * If pathname ends with a trailing slash, check if last path | |
648 | * component is a directory; if not, fail with ENOTDIR as per | |
649 | * POSIX. | |
650 | * | |
651 | * We use separate check instead of O_DIRECTORY in the loop above, | |
652 | * as O_DIRECTORY doesn't work on FreeBSD 8. | |
653 | * | |
654 | * Note this returns already opened file descriptor, with different | |
655 | * mode/create/access. This is believed to be safe as we don't | |
656 | * use this codepath to create directories. | |
657 | */ | |
658 | ||
659 | if (ngx_fd_info(at_fd, &fi) == NGX_FILE_ERROR) { | |
660 | of->err = ngx_errno; | |
661 | of->failed = ngx_fd_info_n; | |
662 | fd = NGX_INVALID_FILE; | |
663 | ||
664 | goto failed; | |
665 | } | |
666 | ||
667 | if (ngx_is_dir(&fi)) { | |
668 | return at_fd; | |
669 | } | |
670 | ||
671 | of->err = ENOTDIR; | |
672 | of->failed = ngx_openat_file_n; | |
673 | fd = NGX_INVALID_FILE; | |
674 | ||
675 | goto failed; | |
630 | 676 | } |
631 | 677 | |
632 | 678 | if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER) { |