blob: 327f1cf5a6bd6c22a95e3c735ec397176ebac21f [file] [log] [blame]
#!/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
}
###############################################################################