Browse Source

Added an option "file_server.check_symlink" to disable symbolic link checks,

also improvement overall path handling
master
Artyom Beilis 8 years ago
parent
commit
2ac8bfc198
5 changed files with 106 additions and 25 deletions
  1. +6
    -0
      CMakeLists.txt
  2. +4
    -1
      private/internal_file_server.h
  3. +49
    -18
      src/internal_file_server.cpp
  4. +35
    -1
      tests/file_server_test.cpp
  5. +12
    -5
      tests/file_server_test.py

+ 6
- 0
CMakeLists.txt View File

@@ -831,6 +831,12 @@ add_test(file_server_test
"-U" "${CNF}"
)

add_test(file_server_test_no_links
file_server_test "-c" "${CNF}/file_server_test.js" "--file_server-check_symlink=false"
"--test-exec=${PYTHON} ${CNF}/file_server_test.py no_links"
"-U" "${CNF}"
)

add_test(file_server_with_listing_test
file_server_test "-c" "${CNF}/file_server_test.js"
"--file_server-listing=true"


+ 4
- 1
private/internal_file_server.h View File

@@ -13,9 +13,11 @@
namespace cppcms {
class service;
namespace impl {
class file_server : public application
class CPPCMS_API file_server : public application
{
public:
static void normalize_path(std::string &path);
file_server(cppcms::service &srv);
~file_server();
virtual void main(std::string file_name);
@@ -35,6 +37,7 @@ namespace impl {
typedef std::map<std::string,std::string> mime_type;
mime_type mime_;
bool list_directories_;
bool check_symlinks_;
std::string index_file_;
};



+ 49
- 18
src/internal_file_server.cpp View File

@@ -48,6 +48,7 @@ file_server::file_server(cppcms::service &srv) : application(srv)

list_directories_ = settings().get("file_server.listing",false);
index_file_ = settings().get("file_server.index","index.html");
check_symlinks_ = settings().get("file_server.check_symlink",true);

std::string mime_file=settings().get("file_server.mime_types","");

@@ -253,13 +254,51 @@ bool file_server::is_in_root(std::string const &input_path,std::string const &ro
return true;
}

bool file_server::check_in_document_root(std::string normal,std::string &real)
void file_server::normalize_path(std::string &path)
{
// Use only Unix file names
for(size_t i=0;i<normal.size();i++)
if(is_directory_separator(normal[i]))
normal[i]='/';
#ifdef CPPCMS_WIN32
for(size_t i=0;i<path.size();i++)
if(path[i]=='\\')
path[i]='/';
#endif
if(path.empty() || path[0]!='/')
path = "/" + path;
std::string::iterator out = path.begin() + 1;
std::string::iterator start = path.begin() + 1;
while(start < path.end()) {
std::string::iterator end = std::find(start,path.end(),'/');
if(end==start || (end-start == 1 && *start == '.')) { // case of "//" and "/./"
// nothing to do
}
else if(end-start == 2 && *start == '.' && *(start+1) == '.') {
std::string::iterator min_pos = path.begin() + 1;
if(out > min_pos)
out --;
while(out > min_pos) {
out --;
if(*out == '/')
break;
}
}
else {
out = std::copy(start,end,out);
if(end != path.end())
*out++ = '/';
}
if(end == path.end())
break;
start = end;
++start;
}
if(*(out-1) == '/' && out > path.begin()+1)
out--;
path.resize(out - path.begin());
}

bool file_server::check_in_document_root(std::string normal,std::string &real)
{
normalize_path(normal);
std::string root = document_root_;
for(unsigned i=0;i<alias_.size();i++) {
std::string const &ref=alias_[i].first;
@@ -276,21 +315,13 @@ bool file_server::check_in_document_root(std::string normal,std::string &real)
return false;
if(normal[0]!='/')
return false;
// Prevent the access to any valid file below like
// detecting that the files placed in /var/www
// by providing a path /../../var/www/known.txt
// whuch would be valid as known is placed in /var/www
// but yet we don't want user to detect that files
// exist in /var/www
for(size_t pos = 1;pos != std::string::npos; pos = normal.find('/',pos)) {
std::string sub_path = normal.substr(0,pos);
std::string tmp;
if(!is_in_root(sub_path,root,tmp))
if(check_symlinks_) {
if(!is_in_root(normal,root,real))
return false;
pos++;
}
if(!is_in_root(normal,root,real))
return false;
else {
real = root + normal;
}
return true;
}



+ 35
- 1
tests/file_server_test.cpp View File

@@ -15,6 +15,7 @@
#include <cppcms/mount_point.h>
#include <cppcms/json.h>
#include <cppcms/copy_filter.h>
#include "internal_file_server.h"
#include <iostream>
#include "client.h"
#include "test.h"
@@ -24,9 +25,42 @@
#include <unistd.h>
#endif


int test(std::string const &left,std::string const &expected)
{
std::string tmp=left;
cppcms::impl::file_server::normalize_path(tmp);
if(tmp!=expected) {
std::cout << "[" << left << "] -> expected [" << expected << "] got [" << tmp << "]\n";
return 1;
}
return 0;
}
int test_normalize()
{
std::cout << "- Tesing path normalization "<< std::endl;
int r = 0
+ test("/foo/bar","/foo/bar")
+ test("/foo/bar/","/foo/bar")
+ test("/foo////bar","/foo/bar")
+ test("///foo/./bar","/foo/bar")
+ test("///foo/./bar///","/foo/bar")
+ test("///foo/./bar/","/foo/bar")
+ test("/../bar","/bar")
+ test("/..","/")
+ test("../test/./","/test")
+ test("/var/www/../../../xx","/xx")
+ test("/var/www/../../xx","/xx")
+ test("../test/./x","/test/x")
+ test("/test/..","/")
+ test("/test/xx/../","/test")
+ test("/test/xx/..","/test")
;
return r;
}
int main(int argc,char **argv)
{
if(test_normalize()!=0)
return 1;
if(argc<5 || argv[argc-2]!=std::string("-U")) {
std::cerr <<"Usage -c config -U dir";
return 1;


+ 12
- 5
tests/file_server_test.py View File

@@ -48,8 +48,11 @@ def test_request(url,status,content='ignore',valid=[],notvalid=[]):


do_listing = False
check_links = True
if len(sys.argv) == 2:
do_listing = sys.argv[1] == 'listing'
do_listing = sys.argv[1] == 'listing'
if sys.argv[1] == 'no_links':
check_links = False



@@ -85,10 +88,6 @@ test_request('/alias/',200,'/al/index.html')
test_request('/alias/test.txt',200,'/al/test.txt')
test_request('/alias/foo/test.txt',200,'/al/foo/test.txt')

if os.name == 'posix':
print "- Testing symlinks"
test_request('/no.txt',404)
test_request('/yes.txt',200,'/yes')

print "- Testing directory traversal"

@@ -103,3 +102,11 @@ test_request('/../alias/never.txt','404')
test_request('/%2e%2e/never.txt','404')
test_request('/..%c0%afnever.txt','404')

if os.name == 'posix':
print "- Testing symlinks"
test_request('/yes.txt',200,'/yes')
if check_links:
test_request('/no.txt',404)
else:
test_request('/no.txt',200,'never.txt')


Loading…
Cancel
Save