Mercurial > hg > nginx
comparison src/core/ngx_open_file_cache.c @ 4477:7033faf6dc3c
Added disable_symlinks directive.
To completely disable symlinks (disable_symlinks on)
we use openat(O_NOFOLLOW) for each path component
to avoid races.
To allow symlinks with the same owner (disable_symlinks if_not_owner),
use openat() (followed by fstat()) and fstatat(AT_SYMLINK_NOFOLLOW),
and then compare uids between fstat() and fstatat().
As there is a race between openat() and fstatat() we don't
know if openat() in fact opened symlink or not. Therefore,
we have to compare uids even if fstatat() reports the opened
component isn't a symlink (as we don't know whether it was
symlink during openat() or not).
Default value is off, i.e. symlinks are allowed.
author | Andrey Belov <defan@nginx.com> |
---|---|
date | Mon, 13 Feb 2012 16:29:04 +0000 |
parents | 94ef9d25ec5b |
children | 5e6436812c9a |
comparison
equal
deleted
inserted
replaced
4476:94ef9d25ec5b | 4477:7033faf6dc3c |
---|---|
20 | 20 |
21 #define NGX_MIN_READ_AHEAD (128 * 1024) | 21 #define NGX_MIN_READ_AHEAD (128 * 1024) |
22 | 22 |
23 | 23 |
24 static void ngx_open_file_cache_cleanup(void *data); | 24 static void ngx_open_file_cache_cleanup(void *data); |
25 #if (NGX_HAVE_OPENAT) | |
26 static ngx_fd_t ngx_openat_file_owner(ngx_fd_t at_fd, const u_char *name, | |
27 ngx_int_t mode, ngx_int_t create, ngx_int_t access); | |
28 #endif | |
29 static ngx_fd_t ngx_open_file_wrapper(ngx_str_t *name, | |
30 ngx_open_file_info_t *of, ngx_int_t mode, ngx_int_t create, | |
31 ngx_int_t access); | |
32 static ngx_int_t ngx_file_info_wrapper(ngx_str_t *name, | |
33 ngx_open_file_info_t *of, ngx_file_info_t *fi); | |
25 static ngx_int_t ngx_open_and_stat_file(ngx_str_t *name, | 34 static ngx_int_t ngx_open_and_stat_file(ngx_str_t *name, |
26 ngx_open_file_info_t *of, ngx_log_t *log); | 35 ngx_open_file_info_t *of, ngx_log_t *log); |
27 static void ngx_open_file_add_event(ngx_open_file_cache_t *cache, | 36 static void ngx_open_file_add_event(ngx_open_file_cache_t *cache, |
28 ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log); | 37 ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log); |
29 static void ngx_open_file_cleanup(void *data); | 38 static void ngx_open_file_cleanup(void *data); |
145 | 154 |
146 if (cache == NULL) { | 155 if (cache == NULL) { |
147 | 156 |
148 if (of->test_only) { | 157 if (of->test_only) { |
149 | 158 |
150 if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) { | 159 if (ngx_file_info_wrapper(name, of, &fi) == NGX_FILE_ERROR) { |
151 of->err = ngx_errno; | |
152 of->failed = ngx_file_info_n; | |
153 return NGX_ERROR; | 160 return NGX_ERROR; |
154 } | 161 } |
155 | 162 |
156 of->uniq = ngx_file_uniq(&fi); | 163 of->uniq = ngx_file_uniq(&fi); |
157 of->mtime = ngx_file_mtime(&fi); | 164 of->mtime = ngx_file_mtime(&fi); |
215 } | 222 } |
216 | 223 |
217 if (file->use_event | 224 if (file->use_event |
218 || (file->event == NULL | 225 || (file->event == NULL |
219 && (of->uniq == 0 || of->uniq == file->uniq) | 226 && (of->uniq == 0 || of->uniq == file->uniq) |
220 && now - file->created < of->valid)) | 227 && now - file->created < of->valid |
228 #if (NGX_HAVE_OPENAT) | |
229 && of->disable_symlinks == file->disable_symlinks | |
230 #endif | |
231 )) | |
221 { | 232 { |
222 if (file->err == 0) { | 233 if (file->err == 0) { |
223 | 234 |
224 of->fd = file->fd; | 235 of->fd = file->fd; |
225 of->uniq = file->uniq; | 236 of->uniq = file->uniq; |
237 ngx_open_file_add_event(cache, file, of, pool->log); | 248 ngx_open_file_add_event(cache, file, of, pool->log); |
238 } | 249 } |
239 | 250 |
240 } else { | 251 } else { |
241 of->err = file->err; | 252 of->err = file->err; |
253 #if (NGX_HAVE_OPENAT) | |
254 of->failed = file->disable_symlinks ? ngx_openat_file_n | |
255 : ngx_open_file_n; | |
256 #else | |
242 of->failed = ngx_open_file_n; | 257 of->failed = ngx_open_file_n; |
258 #endif | |
243 } | 259 } |
244 | 260 |
245 goto found; | 261 goto found; |
246 } | 262 } |
247 | 263 |
373 | 389 |
374 update: | 390 update: |
375 | 391 |
376 file->fd = of->fd; | 392 file->fd = of->fd; |
377 file->err = of->err; | 393 file->err = of->err; |
394 #if (NGX_HAVE_OPENAT) | |
395 file->disable_symlinks = of->disable_symlinks; | |
396 #endif | |
378 | 397 |
379 if (of->err == 0) { | 398 if (of->err == 0) { |
380 file->uniq = of->uniq; | 399 file->uniq = of->uniq; |
381 file->mtime = of->mtime; | 400 file->mtime = of->mtime; |
382 file->size = of->size; | 401 file->size = of->size; |
457 | 476 |
458 return NGX_ERROR; | 477 return NGX_ERROR; |
459 } | 478 } |
460 | 479 |
461 | 480 |
481 #if (NGX_HAVE_OPENAT) | |
482 | |
483 static ngx_fd_t | |
484 ngx_openat_file_owner(ngx_fd_t at_fd, const u_char *name, | |
485 ngx_int_t mode, ngx_int_t create, ngx_int_t access) | |
486 { | |
487 ngx_fd_t fd; | |
488 ngx_file_info_t fi, atfi; | |
489 | |
490 /* | |
491 * To allow symlinks with the same owner, use openat() (followed | |
492 * by fstat()) and fstatat(AT_SYMLINK_NOFOLLOW), and then compare | |
493 * uids between fstat() and fstatat(). | |
494 * | |
495 * As there is a race between openat() and fstatat() we don't | |
496 * know if openat() in fact opened symlink or not. Therefore, | |
497 * we have to compare uids even if fstatat() reports the opened | |
498 * component isn't a symlink (as we don't know whether it was | |
499 * symlink during openat() or not). | |
500 */ | |
501 | |
502 fd = ngx_openat_file(at_fd, name, mode, create, access); | |
503 | |
504 if (fd == NGX_FILE_ERROR) { | |
505 return NGX_FILE_ERROR; | |
506 } | |
507 | |
508 if (ngx_file_at_info(at_fd, name, &atfi, AT_SYMLINK_NOFOLLOW) | |
509 == NGX_FILE_ERROR) | |
510 { | |
511 ngx_close_file(fd); | |
512 return NGX_FILE_ERROR; | |
513 } | |
514 | |
515 if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { | |
516 ngx_close_file(fd); | |
517 return NGX_FILE_ERROR; | |
518 } | |
519 | |
520 if (fi.st_uid != atfi.st_uid) { | |
521 ngx_close_file(fd); | |
522 ngx_set_errno(NGX_ELOOP); | |
523 return NGX_FILE_ERROR; | |
524 } | |
525 | |
526 return fd; | |
527 } | |
528 | |
529 #endif | |
530 | |
531 | |
532 static ngx_fd_t | |
533 ngx_open_file_wrapper(ngx_str_t *name, ngx_open_file_info_t *of, | |
534 ngx_int_t mode, ngx_int_t create, ngx_int_t access) | |
535 { | |
536 ngx_fd_t fd; | |
537 | |
538 #if !(NGX_HAVE_OPENAT) | |
539 | |
540 fd = ngx_open_file(name->data, mode, create, access); | |
541 | |
542 if (fd == NGX_FILE_ERROR) { | |
543 of->err = ngx_errno; | |
544 of->failed = ngx_open_file_n; | |
545 return NGX_FILE_ERROR; | |
546 } | |
547 | |
548 return fd; | |
549 | |
550 #else | |
551 | |
552 u_char *p, *cp, *end; | |
553 ngx_fd_t at_fd; | |
554 | |
555 if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) { | |
556 fd = ngx_open_file(name->data, mode, create, access); | |
557 | |
558 if (fd == NGX_FILE_ERROR) { | |
559 of->err = ngx_errno; | |
560 of->failed = ngx_open_file_n; | |
561 return NGX_FILE_ERROR; | |
562 } | |
563 | |
564 return fd; | |
565 } | |
566 | |
567 at_fd = ngx_openat_file(AT_FDCWD, "/", NGX_FILE_RDONLY|NGX_FILE_NONBLOCK, | |
568 NGX_FILE_OPEN, 0); | |
569 | |
570 if (at_fd == NGX_FILE_ERROR) { | |
571 of->err = ngx_errno; | |
572 of->failed = ngx_openat_file_n; | |
573 return NGX_FILE_ERROR; | |
574 } | |
575 | |
576 end = name->data + name->len; | |
577 p = name->data + 1; | |
578 | |
579 for ( ;; ) { | |
580 cp = ngx_strlchr(p, end, '/'); | |
581 if (cp == NULL) { | |
582 break; | |
583 } | |
584 | |
585 *cp = '\0'; | |
586 | |
587 if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER) { | |
588 fd = ngx_openat_file_owner(at_fd, p, | |
589 NGX_FILE_RDONLY|NGX_FILE_NONBLOCK, | |
590 NGX_FILE_OPEN, 0); | |
591 | |
592 } else { | |
593 fd = ngx_openat_file(at_fd, p, | |
594 NGX_FILE_RDONLY|NGX_FILE_NONBLOCK|NGX_FILE_NOFOLLOW, | |
595 NGX_FILE_OPEN, 0); | |
596 } | |
597 | |
598 *cp = '/'; | |
599 | |
600 ngx_close_file(at_fd); | |
601 | |
602 if (fd == NGX_FILE_ERROR) { | |
603 of->err = ngx_errno; | |
604 of->failed = ngx_openat_file_n; | |
605 return NGX_FILE_ERROR; | |
606 } | |
607 | |
608 p = cp + 1; | |
609 at_fd = fd; | |
610 } | |
611 | |
612 if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER) { | |
613 fd = ngx_openat_file_owner(at_fd, p, mode, create, access); | |
614 | |
615 } else { | |
616 fd = ngx_openat_file(at_fd, p, mode|NGX_FILE_NOFOLLOW, create, access); | |
617 } | |
618 | |
619 if (fd == NGX_FILE_ERROR) { | |
620 of->err = ngx_errno; | |
621 of->failed = ngx_openat_file_n; | |
622 } | |
623 | |
624 ngx_close_file(at_fd); | |
625 | |
626 return fd; | |
627 #endif | |
628 } | |
629 | |
630 | |
631 static ngx_int_t | |
632 ngx_file_info_wrapper(ngx_str_t *name, ngx_open_file_info_t *of, | |
633 ngx_file_info_t *fi) | |
634 { | |
635 ngx_int_t rc; | |
636 | |
637 #if !(NGX_HAVE_OPENAT) | |
638 | |
639 rc = ngx_file_info(name->data, fi); | |
640 | |
641 if (rc == NGX_FILE_ERROR) { | |
642 of->err = ngx_errno; | |
643 of->failed = ngx_file_info_n; | |
644 return NGX_FILE_ERROR; | |
645 } | |
646 | |
647 return rc; | |
648 | |
649 #else | |
650 | |
651 ngx_fd_t fd; | |
652 | |
653 if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) { | |
654 | |
655 rc = ngx_file_info(name->data, fi); | |
656 | |
657 if (rc == NGX_FILE_ERROR) { | |
658 of->err = ngx_errno; | |
659 of->failed = ngx_file_info_n; | |
660 return NGX_FILE_ERROR; | |
661 } | |
662 | |
663 return rc; | |
664 } | |
665 | |
666 fd = ngx_open_file_wrapper(name, of, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK, | |
667 NGX_FILE_OPEN, 0); | |
668 | |
669 if (fd == NGX_FILE_ERROR) { | |
670 return NGX_FILE_ERROR; | |
671 } | |
672 | |
673 if (ngx_fd_info(fd, fi) == NGX_FILE_ERROR) { | |
674 of->err = ngx_errno; | |
675 of->failed = ngx_fd_info_n; | |
676 ngx_close_file(fd); | |
677 return NGX_FILE_ERROR; | |
678 } | |
679 | |
680 ngx_close_file(fd); | |
681 | |
682 return NGX_OK; | |
683 #endif | |
684 } | |
685 | |
686 | |
462 static ngx_int_t | 687 static ngx_int_t |
463 ngx_open_and_stat_file(ngx_str_t *name, ngx_open_file_info_t *of, | 688 ngx_open_and_stat_file(ngx_str_t *name, ngx_open_file_info_t *of, |
464 ngx_log_t *log) | 689 ngx_log_t *log) |
465 { | 690 { |
466 ngx_fd_t fd; | 691 ngx_fd_t fd; |
467 ngx_file_info_t fi; | 692 ngx_file_info_t fi; |
468 | 693 |
469 if (of->fd != NGX_INVALID_FILE) { | 694 if (of->fd != NGX_INVALID_FILE) { |
470 | 695 |
471 if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) { | 696 if (ngx_file_info_wrapper(name, of, &fi) == NGX_FILE_ERROR) { |
472 of->failed = ngx_file_info_n; | 697 of->fd = NGX_INVALID_FILE; |
473 goto failed; | 698 return NGX_ERROR; |
474 } | 699 } |
475 | 700 |
476 if (of->uniq == ngx_file_uniq(&fi)) { | 701 if (of->uniq == ngx_file_uniq(&fi)) { |
477 goto done; | 702 goto done; |
478 } | 703 } |
479 | 704 |
480 } else if (of->test_dir) { | 705 } else if (of->test_dir) { |
481 | 706 |
482 if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) { | 707 if (ngx_file_info_wrapper(name, of, &fi) == NGX_FILE_ERROR) { |
483 of->failed = ngx_file_info_n; | 708 of->fd = NGX_INVALID_FILE; |
484 goto failed; | 709 return NGX_ERROR; |
485 } | 710 } |
486 | 711 |
487 if (ngx_is_dir(&fi)) { | 712 if (ngx_is_dir(&fi)) { |
488 goto done; | 713 goto done; |
489 } | 714 } |
494 /* | 719 /* |
495 * Use non-blocking open() not to hang on FIFO files, etc. | 720 * Use non-blocking open() not to hang on FIFO files, etc. |
496 * This flag has no effect on a regular files. | 721 * This flag has no effect on a regular files. |
497 */ | 722 */ |
498 | 723 |
499 fd = ngx_open_file(name->data, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK, | 724 fd = ngx_open_file_wrapper(name, of, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK, |
500 NGX_FILE_OPEN, 0); | 725 NGX_FILE_OPEN, 0); |
501 | 726 |
502 } else { | 727 } else { |
503 fd = ngx_open_file(name->data, NGX_FILE_APPEND, | 728 fd = ngx_open_file_wrapper(name, of, NGX_FILE_APPEND, |
504 NGX_FILE_CREATE_OR_OPEN, | 729 NGX_FILE_CREATE_OR_OPEN, |
505 NGX_FILE_DEFAULT_ACCESS); | 730 NGX_FILE_DEFAULT_ACCESS); |
506 } | 731 } |
507 | 732 |
508 if (fd == NGX_INVALID_FILE) { | 733 if (fd == NGX_INVALID_FILE) { |
509 of->failed = ngx_open_file_n; | 734 of->fd = NGX_INVALID_FILE; |
510 goto failed; | 735 return NGX_ERROR; |
511 } | 736 } |
512 | 737 |
513 if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { | 738 if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { |
514 ngx_log_error(NGX_LOG_CRIT, log, ngx_errno, | 739 ngx_log_error(NGX_LOG_CRIT, log, ngx_errno, |
515 ngx_fd_info_n " \"%V\" failed", name); | 740 ngx_fd_info_n " \"%V\" failed", name); |
563 of->is_file = ngx_is_file(&fi); | 788 of->is_file = ngx_is_file(&fi); |
564 of->is_link = ngx_is_link(&fi); | 789 of->is_link = ngx_is_link(&fi); |
565 of->is_exec = ngx_is_exec(&fi); | 790 of->is_exec = ngx_is_exec(&fi); |
566 | 791 |
567 return NGX_OK; | 792 return NGX_OK; |
568 | |
569 failed: | |
570 | |
571 of->fd = NGX_INVALID_FILE; | |
572 of->err = ngx_errno; | |
573 | |
574 return NGX_ERROR; | |
575 } | 793 } |
576 | 794 |
577 | 795 |
578 /* | 796 /* |
579 * we ignore any possible event setting error and | 797 * we ignore any possible event setting error and |