| #!/usr/bin/perl | |
| # (C) Andrey Belov | |
| # Tests for disable_symlinks directive. | |
| ############################################################################### | |
| use warnings; | |
| use strict; | |
| use Test::More; | |
| use POSIX; | |
| use Cwd qw/ realpath /; | |
| BEGIN { use FindBin; chdir($FindBin::Bin); } | |
| use lib 'lib'; | |
| use Test::Nginx; | |
| ############################################################################### | |
| select STDERR; $| = 1; | |
| select STDOUT; $| = 1; | |
| my $t = Test::Nginx->new()->has(qw/http rewrite symlink/) | |
| ->write_file_expand('nginx.conf', <<'EOF'); | |
| %%TEST_GLOBALS%% | |
| daemon off; | |
| events { | |
| } | |
| http { | |
| %%TEST_GLOBALS_HTTP%% | |
| server { | |
| listen 127.0.0.1:8080; | |
| server_name s1; | |
| location /on/ { | |
| disable_symlinks on; | |
| } | |
| location /not_owner/ { | |
| disable_symlinks if_not_owner; | |
| } | |
| location /try_on/ { | |
| disable_symlinks on; | |
| try_files $uri $uri.html =404; | |
| } | |
| location /try_not_owner/ { | |
| disable_symlinks if_not_owner; | |
| try_files $uri $uri.txt =404; | |
| } | |
| location /if_on/ { | |
| disable_symlinks on; | |
| if (-f $request_filename) { | |
| return 204; | |
| } | |
| } | |
| location /if_not_owner/ { | |
| disable_symlinks if_not_owner; | |
| if (-f $request_filename) { | |
| return 204; | |
| } | |
| } | |
| location /complex/1/ { | |
| disable_symlinks on; | |
| alias %%TESTDIR%%/./cached/../; | |
| } | |
| location /complex/2/ { | |
| disable_symlinks on; | |
| alias %%TESTDIR%%//./cached/..//; | |
| } | |
| location /complex/3/ { | |
| disable_symlinks on; | |
| alias ///%%TESTDIR%%//./cached/..//; | |
| } | |
| location ~ (.+/)tail$ { | |
| disable_symlinks on; | |
| alias %%TESTDIR%%/$1; | |
| } | |
| location ~ (.+/)tailowner$ { | |
| disable_symlinks if_not_owner; | |
| alias %%TESTDIR%%/$1; | |
| } | |
| location ~ (.+/)tailoff$ { | |
| disable_symlinks off; | |
| alias %%TESTDIR%%/$1; | |
| } | |
| location /dir { | |
| disable_symlinks on; | |
| try_files $uri/ =404; | |
| } | |
| location /from { | |
| disable_symlinks on from=$document_root; | |
| location /from/wo_slash { | |
| alias %%TESTDIR%%/dirlink; | |
| } | |
| location /from/with_slash/ { | |
| alias %%TESTDIR%%/dirlink/; | |
| } | |
| location ~ ^/from/exact/(.+)$ { | |
| alias %%TESTDIR%%/$1; | |
| } | |
| } | |
| } | |
| server { | |
| listen 127.0.0.1:8080; | |
| server_name s2; | |
| open_file_cache max=16 inactive=60s; | |
| open_file_cache_valid 30s; | |
| open_file_cache_min_uses 1; | |
| open_file_cache_errors on; | |
| location /cached-off/ { | |
| disable_symlinks off; | |
| alias %%TESTDIR%%/cached/; | |
| } | |
| location /cached-on/ { | |
| disable_symlinks on; | |
| alias %%TESTDIR%%/cached/; | |
| } | |
| location /cached-if-not-owner/ { | |
| disable_symlinks if_not_owner; | |
| alias %%TESTDIR%%/cached/; | |
| } | |
| location / { | |
| disable_symlinks off; | |
| } | |
| } | |
| } | |
| EOF | |
| my $uid = getuid(); | |
| my ($extfile) = grep { -f && !-l && $uid != (stat())[4] } | |
| ('/etc/resolv.conf', '/etc/protocols', '/etc/host.conf'); | |
| plan(skip_all => 'no external file found') | |
| if !defined $extfile; | |
| $t->try_run('no disable_symlinks')->plan(28); | |
| my $d = $t->testdir(); | |
| mkdir("$d/on"); | |
| mkdir("$d/not_owner"); | |
| mkdir("$d/try_on"); | |
| mkdir("$d/try_not_owner"); | |
| mkdir("$d/if_on"); | |
| mkdir("$d/if_not_owner"); | |
| mkdir("$d/cached"); | |
| $t->write_file("empty.html", ""); | |
| symlink("empty.html", "$d/link"); | |
| symlink($extfile, "$d/link2"); | |
| $t->write_file("on/empty.html", ""); | |
| symlink("empty.html", "$d/on/link"); | |
| symlink($extfile, "$d/on/link2"); | |
| $t->write_file("not_owner/empty.html", ""); | |
| symlink("empty.html", "$d/not_owner/link"); | |
| symlink($extfile, "$d/not_owner/link2"); | |
| $t->write_file("try_on/try.html", "LOCAL TRY"); | |
| symlink($extfile, "$d/try_on/try"); | |
| $t->write_file("try_not_owner/try.html", "LOCAL TRY"); | |
| symlink($extfile, "$d/try_not_owner/try"); | |
| symlink("try.html", "$d/try_not_owner/try.txt"); | |
| $t->write_file("if_on/empty.html", ""); | |
| symlink("empty.html", "$d/if_on/link"); | |
| symlink($extfile, "$d/if_on/link2"); | |
| $t->write_file("if_not_owner/empty.html", ""); | |
| symlink("empty.html", "$d/if_not_owner/link"); | |
| symlink($extfile, "$d/if_not_owner/link2"); | |
| mkdir("$d/dir"); | |
| $t->write_file("dir/empty.html", ""); | |
| symlink("dir", "$d/dirlink"); | |
| symlink($extfile, "$d/cached/link"); | |
| ############################################################################### | |
| SKIP: { | |
| skip 'cannot test under symlink', 25 if $d ne realpath($d) or $^O eq 'netbsd'; | |
| like(http_get_host('s1', '/link'), qr!200 OK!, 'static (off, same uid)'); | |
| like(http_get_host('s1', '/link2'), qr!200 OK!, 'static (off, other uid)'); | |
| like(http_get_host('s1', '/on/link'), qr!403 Forbidden!, | |
| 'static (on, same uid)'); | |
| like(http_get_host('s1', '/on/link2'), qr!403 Forbidden!, | |
| 'static (on, other uid)'); | |
| like(http_get_host('s1', '/not_owner/link'), qr!200 OK!, | |
| 'static (if_not_owner, same uid)'); | |
| like(http_get_host('s1', '/not_owner/link2'), qr!403 Forbidden!, | |
| 'static (if_not_owner, other uid)'); | |
| like(http_get_host('s1', '/try_on/try'), qr/LOCAL TRY/, | |
| 'try_files (on)'); | |
| like(http_get_host('s1', '/try_not_owner/try'), qr/LOCAL TRY/, | |
| 'try_files (if_not_owner)'); | |
| like(http_get_host('s1', '/if_on/link'), qr!403 Forbidden!, | |
| 'if (on, same uid)'); | |
| like(http_get_host('s1', '/if_on/link2'), qr!403 Forbidden!, | |
| 'if (on, other uid)'); | |
| like(http_get_host('s1', '/if_not_owner/link'), qr!204 No Content!, | |
| 'if (if_not_owner, same uid)'); | |
| like(http_get_host('s1', '/if_not_owner/link2'), qr!403 Forbidden!, | |
| 'if (if_not_owner, other uid)'); | |
| like(http_get_host('s2', '/cached-off/link'), qr!200 OK!, | |
| 'open_file_cache (pass 1)'); | |
| like(http_get_host('s2', '/cached-on/link'), qr!403 Forbidden!, | |
| 'open_file_cache (pass 2)'); | |
| like(http_get_host('s2', '/cached-off/link'), qr!200 OK!, | |
| 'open_file_cache (pass 3)'); | |
| like(http_get_host('s2', '/cached-if-not-owner/link'), qr!403 Forbidden!, | |
| 'open_file_cache (pass 4)'); | |
| like(http_get_host('s2', '/cached-off/link'), qr!200 OK!, | |
| 'open_file_cache (pass 5)'); | |
| like(http_get('/complex/1/empty.html'), qr!200 OK!, 'complex root 1'); | |
| like(http_get('/complex/2/empty.html'), qr!200 OK!, 'complex root 2'); | |
| like(http_get('/complex/3/empty.html'), qr!200 OK!, 'complex root 3'); | |
| # workaround for freebsd 8: we use O_EXEC instead of O_SEARCH (since there | |
| # is no O_SEARCH), and O_DIRECTORY does nothing. setting the 'x' bit | |
| # tests to pass as openat() will correctly fail with ENOTDIR | |
| chmod(0700, "$d/link"); | |
| my $rc = $^O eq 'darwin' ? 200 : 404; | |
| like(http_get('/link/tail'), qr!40[34] !, 'file with trailing /, on'); | |
| like(http_get('/link/tailowner'), qr!404 !, 'file with trailing /, owner'); | |
| like(http_get('/link/tailoff'), qr!$rc !, 'file with trailing /, off'); | |
| like(http_get('/dirlink'), qr!404 !, 'directory without /'); | |
| like(http_get('/dirlink/'), qr!404 !, 'directory with trailing /'); | |
| } # SKIP: cannot test under symlink | |
| like(http_get('/from/wo_slash/empty.html'), qr!200 OK!, '"from=" without /'); | |
| like(http_get('/from/with_slash/empty.html'), qr!200 OK!, '"from=" with /'); | |
| like(http_get('/from/exact/link'), qr!200 OK!, '"from=" exact match'); | |
| ############################################################################### | |
| sub http_get_host { | |
| my ($host, $url) = @_; | |
| return http(<<EOF); | |
| GET $url HTTP/1.0 | |
| Host: $host | |
| EOF | |
| } | |
| ############################################################################### |