2014-10-21 22:13:36 +02:00
|
|
|
#!/usr/bin/perl -w
|
|
|
|
|
|
|
|
# Test EncFS --reverse mode
|
|
|
|
|
2014-11-17 00:31:33 +01:00
|
|
|
use warnings;
|
2018-03-18 00:01:05 +01:00
|
|
|
use Test::More tests => 34;
|
2014-10-21 22:13:36 +02:00
|
|
|
use File::Path;
|
|
|
|
use File::Temp;
|
2014-11-17 00:31:33 +01:00
|
|
|
use IO::Handle;
|
2014-11-30 14:13:25 +01:00
|
|
|
use Errno qw(EROFS);
|
2014-10-21 22:13:36 +02:00
|
|
|
|
2017-08-08 01:52:03 +02:00
|
|
|
require("integration/common.pl");
|
2014-11-16 17:23:47 +01:00
|
|
|
|
2014-10-21 22:13:36 +02:00
|
|
|
my $tempDir = $ENV{'TMPDIR'} || "/tmp";
|
|
|
|
|
2017-06-28 15:59:00 +02:00
|
|
|
if($^O eq "linux" and $tempDir eq "/tmp") {
|
|
|
|
# On Linux, /tmp is often a tmpfs mount that does not support
|
|
|
|
# extended attributes. Use /var/tmp instead.
|
|
|
|
$tempDir = "/var/tmp";
|
|
|
|
}
|
|
|
|
|
2017-06-13 11:12:30 +02:00
|
|
|
# Find attr binary
|
|
|
|
# Linux
|
|
|
|
my @binattr = ("attr", "-l");
|
|
|
|
if(system("which xattr > /dev/null 2>&1") == 0)
|
|
|
|
{
|
|
|
|
# Mac OS X
|
2017-06-28 15:59:00 +02:00
|
|
|
@binattr = ("xattr", "-s");
|
2017-06-13 11:12:30 +02:00
|
|
|
}
|
|
|
|
if(system("which lsextattr > /dev/null 2>&1") == 0)
|
|
|
|
{
|
|
|
|
# FreeBSD
|
2017-06-28 15:59:00 +02:00
|
|
|
@binattr = ("lsextattr", "-h", "user");
|
|
|
|
}
|
|
|
|
# Do we support xattr ?
|
|
|
|
my $have_xattr = 1;
|
|
|
|
if(system("./build/encfs --verbose --version 2>&1 | grep -q HAVE_XATTR") != 0)
|
|
|
|
{
|
|
|
|
$have_xattr = 0;
|
2017-06-13 11:12:30 +02:00
|
|
|
}
|
|
|
|
|
2014-10-21 22:13:36 +02:00
|
|
|
# Helper function
|
|
|
|
# Create a new empty working directory
|
|
|
|
sub newWorkingDir
|
|
|
|
{
|
|
|
|
our $workingDir = mkdtemp("$tempDir/encfs-reverse-tests-XXXX")
|
|
|
|
|| BAIL_OUT("Could not create temporary directory");
|
|
|
|
|
|
|
|
our $plain = "$workingDir/plain";
|
|
|
|
mkdir($plain);
|
|
|
|
our $ciphertext = "$workingDir/ciphertext";
|
|
|
|
mkdir($ciphertext);
|
|
|
|
our $decrypted = "$workingDir/decrypted";
|
|
|
|
mkdir($decrypted);
|
|
|
|
}
|
|
|
|
|
|
|
|
# Helper function
|
|
|
|
# Unmount and delete mountpoint
|
|
|
|
sub cleanup
|
|
|
|
{
|
2014-12-30 23:05:23 +01:00
|
|
|
portable_unmount($decrypted);
|
|
|
|
portable_unmount($ciphertext);
|
2014-11-17 00:31:33 +01:00
|
|
|
our $workingDir;
|
2014-10-21 22:13:36 +02:00
|
|
|
rmtree($workingDir);
|
|
|
|
ok( ! -d $workingDir, "working dir removed");
|
|
|
|
}
|
|
|
|
|
|
|
|
# Helper function
|
|
|
|
# Mount encryption-decryption chain
|
|
|
|
#
|
|
|
|
# Directory structure: plain -[encrypt]-> ciphertext -[decrypt]-> decrypted
|
|
|
|
sub mount
|
|
|
|
{
|
2014-11-30 22:45:51 +01:00
|
|
|
delete $ENV{"ENCFS6_CONFIG"};
|
2015-11-12 19:08:52 +01:00
|
|
|
system("./build/encfs --extpass=\"echo test\" --standard $plain $ciphertext --reverse --nocache");
|
2014-11-17 00:31:33 +01:00
|
|
|
ok(waitForFile("$plain/.encfs6.xml"), "plain .encfs6.xml exists") or BAIL_OUT("'$plain/.encfs6.xml'");
|
|
|
|
my $e = encName(".encfs6.xml");
|
|
|
|
ok(waitForFile("$ciphertext/$e"), "encrypted .encfs6.xml exists") or BAIL_OUT("'$ciphertext/$e'");
|
2015-11-12 19:08:52 +01:00
|
|
|
system("ENCFS6_CONFIG=$plain/.encfs6.xml ./build/encfs --nocache --extpass=\"echo test\" $ciphertext $decrypted");
|
2014-11-17 00:31:33 +01:00
|
|
|
ok(waitForFile("$decrypted/.encfs6.xml"), "decrypted .encfs6.xml exists") or BAIL_OUT("'$decrypted/.encfs6.xml'");
|
2014-11-16 17:23:47 +01:00
|
|
|
}
|
2014-10-21 22:13:36 +02:00
|
|
|
|
2014-11-16 17:23:47 +01:00
|
|
|
# Helper function
|
|
|
|
#
|
|
|
|
# Get encrypted name for file
|
|
|
|
sub encName
|
|
|
|
{
|
|
|
|
my $name = shift;
|
2015-11-12 19:08:52 +01:00
|
|
|
my $enc = qx(ENCFS6_CONFIG=$plain/.encfs6.xml ./build/encfsctl encode --extpass="echo test" $ciphertext $name);
|
2014-11-16 17:23:47 +01:00
|
|
|
chomp($enc);
|
|
|
|
return $enc;
|
2014-10-21 22:13:36 +02:00
|
|
|
}
|
|
|
|
|
2018-03-17 17:58:04 +01:00
|
|
|
# Copy a directory tree and verify that the decrypted data is identical, we also create a foo/.encfs6.xml file, to be sure it correctly shows-up
|
2014-10-21 22:13:36 +02:00
|
|
|
sub copy_test
|
|
|
|
{
|
2018-03-17 17:58:04 +01:00
|
|
|
ok(system("cp -a encfs $plain && mkdir $plain/foo && touch $plain/foo/.encfs6.xml")==0, "copying files to plain");
|
|
|
|
ok(system("diff -r -q --exclude='.encfs6.xml' $plain $decrypted")==0, "decrypted files are identical");
|
2014-10-21 22:13:36 +02:00
|
|
|
ok(-f "$plain/encfs/encfs.cpp", "file exists");
|
|
|
|
unlink("$plain/encfs/encfs.cpp");
|
|
|
|
ok(! -f "$decrypted/encfs.cpp", "file deleted");
|
|
|
|
}
|
|
|
|
|
2018-03-18 00:01:05 +01:00
|
|
|
# Encfsctl cat test
|
|
|
|
sub encfsctl_cat_test
|
|
|
|
{
|
|
|
|
my $contents = "hello world\n";
|
|
|
|
ok( open(OUT, "> $plain/hello.txt"), "create file for encfsctl cat test" );
|
|
|
|
print OUT $contents;
|
|
|
|
close OUT;
|
|
|
|
qx(ENCFS6_CONFIG=$plain/.encfs6.xml ./build/encfsctl cat --extpass="echo test" $ciphertext hello.txt > $plain/hellodec.txt);
|
|
|
|
qx(ENCFS6_CONFIG=$plain/.encfs6.xml ./build/encfsctl cat --extpass="echo test" --reverse $plain hello.txt > $plain/helloenc.txt);
|
|
|
|
my $cname = encName("hello.txt");
|
|
|
|
ok(system("diff -q $plain/helloenc.txt $ciphertext/$cname")==0, "encfsctl correctly encrypts");
|
|
|
|
ok(system("diff -q $plain/hello.txt $plain/hellodec.txt")==0, "encfsctl correctly decrypts");
|
|
|
|
}
|
|
|
|
|
2014-11-04 23:08:33 +01:00
|
|
|
# Create symlinks and verify they are correctly decrypted
|
|
|
|
# Parameter: symlink target
|
|
|
|
sub symlink_test
|
|
|
|
{
|
2014-11-30 22:22:20 +01:00
|
|
|
my $target = shift;
|
2014-11-04 23:08:33 +01:00
|
|
|
symlink($target, "$plain/symlink");
|
2014-11-30 22:22:20 +01:00
|
|
|
$dec = readlink("$decrypted/symlink");
|
|
|
|
ok( $dec eq $target, "symlink to '$target'") or
|
|
|
|
print("# (original) $target' != '$dec' (decrypted)\n");
|
2017-06-28 15:59:00 +02:00
|
|
|
my $return_code = ($have_xattr) ? system(@binattr, "$decrypted/symlink") : 0;
|
2017-02-14 06:15:09 +01:00
|
|
|
is($return_code, 0, "symlink to '$target' extended attributes can be read (return code was $return_code)");
|
2014-11-04 23:08:33 +01:00
|
|
|
unlink("$plain/symlink");
|
|
|
|
}
|
|
|
|
|
2014-11-17 00:31:33 +01:00
|
|
|
# Grow a file from 0 to x kB and
|
|
|
|
# * check the ciphertext length is correct (stat + read)
|
|
|
|
# * check that the decrypted length is correct (stat + read)
|
|
|
|
# * check that plaintext and decrypted are identical
|
|
|
|
sub grow {
|
2014-11-14 23:01:41 +01:00
|
|
|
# pfh ... plaintext file handle
|
|
|
|
open(my $pfh, ">", "$plain/grow");
|
|
|
|
# vfh ... verification file handle
|
|
|
|
open(my $vfh, "<", "$plain/grow");
|
|
|
|
$pfh->autoflush;
|
|
|
|
# ciphertext file name
|
|
|
|
my $cname = encName("grow");
|
|
|
|
# cfh ... ciphertext file handle
|
|
|
|
ok(open(my $cfh, "<", "$ciphertext/$cname"), "open ciphertext grow file");
|
|
|
|
# dfh ... decrypted file handle
|
|
|
|
ok(open(my $dfh, "<", "$decrypted/grow"), "open decrypted grow file");
|
|
|
|
|
|
|
|
# csz ... ciphertext size
|
|
|
|
ok(sizeVerify($cfh, 0), "ciphertext of empty file is empty");
|
|
|
|
ok(sizeVerify($dfh, 0), "decrypted empty file is empty");
|
|
|
|
|
|
|
|
my $ok = 1;
|
|
|
|
my $max = 9000;
|
|
|
|
for($i=5; $i < $max; $i += 5)
|
|
|
|
{
|
2014-11-23 16:59:17 +01:00
|
|
|
print($pfh "abcde") or die("write failed");
|
2014-11-14 23:01:41 +01:00
|
|
|
# autoflush should make sure the write goes to the kernel
|
|
|
|
# immediately. Just to be sure, check it here.
|
|
|
|
sizeVerify($vfh, $i) or die("unexpected plain file size");
|
2015-03-15 23:05:29 +01:00
|
|
|
sizeVerify($cfh, $i) or $ok = 0;
|
2014-11-17 00:31:33 +01:00
|
|
|
sizeVerify($dfh, $i) or $ok = 0;
|
2014-11-23 16:59:17 +01:00
|
|
|
|
|
|
|
if(md5fh($vfh) ne md5fh($dfh))
|
|
|
|
{
|
|
|
|
$ok = 0;
|
|
|
|
print("# content is different, unified diff:\n");
|
|
|
|
system("diff -u $plain/grow $decrypted/grow");
|
|
|
|
}
|
2014-11-14 23:01:41 +01:00
|
|
|
|
|
|
|
last unless $ok;
|
|
|
|
}
|
|
|
|
ok($ok, "ciphertext and decrypted size of file grown to $i bytes");
|
2017-04-13 18:24:02 +02:00
|
|
|
close($pfh);
|
|
|
|
close($vfh);
|
|
|
|
close($cfh);
|
|
|
|
close($dfh);
|
|
|
|
unlink("$plain/grow");
|
2014-11-14 23:01:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
sub largeRead {
|
2014-11-29 13:35:02 +01:00
|
|
|
writeZeroes("$plain/largeRead", 1024*1024);
|
2014-11-14 23:01:41 +01:00
|
|
|
# ciphertext file name
|
|
|
|
my $cname = encName("largeRead");
|
|
|
|
# cfh ... ciphertext file handle
|
|
|
|
ok(open(my $cfh, "<", "$ciphertext/$cname"), "open ciphertext largeRead file");
|
2015-03-15 23:05:29 +01:00
|
|
|
ok(sizeVerify($cfh, 1024*1024), "1M file size");
|
2014-11-17 00:31:33 +01:00
|
|
|
}
|
|
|
|
|
2014-11-30 14:13:25 +01:00
|
|
|
# Check that the reverse mount is read-only
|
|
|
|
# (writing is not supported in reverse mode because of the added
|
|
|
|
# complexity and the marginal use case)
|
|
|
|
sub writesDenied {
|
|
|
|
$fn = "$plain/writesDenied";
|
|
|
|
writeZeroes($fn, 1024);
|
|
|
|
my $efn = $ciphertext . "/" . encName("writesDenied");
|
|
|
|
open(my $fh, ">", $efn);
|
|
|
|
if( ok( $! == EROFS, "open for write denied, EROFS")) {
|
2014-11-30 22:45:51 +01:00
|
|
|
ok( 1, "writing denied, filehandle not open");
|
2014-11-30 14:13:25 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
print($fh "foo");
|
|
|
|
ok( $! == EROFS, "writing denied, EROFS");
|
|
|
|
}
|
|
|
|
$target = $ciphertext . "/" . encName("writesDenied2");
|
|
|
|
rename($efn, $target);
|
|
|
|
ok( $! == EROFS, "rename denied, EROFS") or die();
|
|
|
|
unlink($efn);
|
|
|
|
ok( $! == EROFS, "unlink denied, EROFS");
|
|
|
|
utime(undef, undef, $efn) ;
|
|
|
|
ok( $! == EROFS, "touch denied, EROFS");
|
|
|
|
truncate($efn, 10);
|
|
|
|
ok( $! == EROFS, "truncate denied, EROFS");
|
|
|
|
}
|
|
|
|
|
2014-11-14 23:01:41 +01:00
|
|
|
# Setup mounts
|
2014-10-21 22:13:36 +02:00
|
|
|
newWorkingDir();
|
|
|
|
mount();
|
|
|
|
|
2014-11-14 23:01:41 +01:00
|
|
|
# Actual tests
|
|
|
|
grow();
|
|
|
|
largeRead();
|
2014-10-21 22:13:36 +02:00
|
|
|
copy_test();
|
2018-03-18 00:01:05 +01:00
|
|
|
encfsctl_cat_test();
|
2014-11-04 23:08:33 +01:00
|
|
|
symlink_test("/"); # absolute
|
|
|
|
symlink_test("foo"); # relative
|
|
|
|
symlink_test("/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/15/17/18"); # long
|
|
|
|
symlink_test("!§\$%&/()\\<>#+="); # special characters
|
2014-11-30 22:22:20 +01:00
|
|
|
symlink_test("$plain/foo");
|
2017-10-02 19:02:55 +02:00
|
|
|
writesDenied();
|
2014-10-21 22:13:36 +02:00
|
|
|
|
2014-11-14 23:01:41 +01:00
|
|
|
# Umount and delete files
|
2014-10-21 22:13:36 +02:00
|
|
|
cleanup();
|