8fcd39a3b0
The fsck operation is not yet implemented, this commits just adds the flag and improves cli flag handling.
455 lines
11 KiB
Go
455 lines
11 KiB
Go
package cli
|
|
|
|
// Test CLI operations like "-init", "-password" etc
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/rfjakob/gocryptfs/internal/configfile"
|
|
"github.com/rfjakob/gocryptfs/internal/exitcodes"
|
|
|
|
"github.com/rfjakob/gocryptfs/tests/test_helpers"
|
|
)
|
|
|
|
var testPw = []byte("test")
|
|
|
|
// Extract the exit code from an error value that was returned from
|
|
// exec.Run()
|
|
func extractExitCode(err error) int {
|
|
if err == nil {
|
|
return 0
|
|
}
|
|
// OMG this is convoluted
|
|
err2 := err.(*exec.ExitError)
|
|
code := err2.Sys().(syscall.WaitStatus).ExitStatus()
|
|
return code
|
|
}
|
|
|
|
func TestMain(m *testing.M) {
|
|
test_helpers.ResetTmpDir(false)
|
|
r := m.Run()
|
|
os.Exit(r)
|
|
}
|
|
|
|
// Test -init flag
|
|
func TestInit(t *testing.T) {
|
|
dir := test_helpers.InitFS(t)
|
|
_, c, err := configfile.LoadConfFile(dir+"/"+configfile.ConfDefaultName, testPw)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if c.IsFeatureFlagSet(configfile.FlagAESSIV) {
|
|
t.Error("AESSIV flag should not be set")
|
|
}
|
|
}
|
|
|
|
// Test -init with -devrandom flag
|
|
func TestInitDevRandom(t *testing.T) {
|
|
test_helpers.InitFS(t, "-devrandom")
|
|
}
|
|
|
|
// Test -init with -aessiv
|
|
func TestInitAessiv(t *testing.T) {
|
|
dir := test_helpers.InitFS(t, "-aessiv")
|
|
_, c, err := configfile.LoadConfFile(dir+"/"+configfile.ConfDefaultName, testPw)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !c.IsFeatureFlagSet(configfile.FlagAESSIV) {
|
|
t.Error("AESSIV flag should be set but is not")
|
|
}
|
|
}
|
|
|
|
// Test -init with -reverse
|
|
func TestInitReverse(t *testing.T) {
|
|
dir := test_helpers.InitFS(t, "-reverse")
|
|
_, c, err := configfile.LoadConfFile(dir+"/"+configfile.ConfReverseName, testPw)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !c.IsFeatureFlagSet(configfile.FlagAESSIV) {
|
|
t.Error("AESSIV flag should be set but is not")
|
|
}
|
|
}
|
|
|
|
func testPasswd(t *testing.T, dir string, extraArgs ...string) {
|
|
// Change password using "-extpass"
|
|
args := []string{"-q", "-passwd", "-extpass", "echo test"}
|
|
args = append(args, extraArgs...)
|
|
args = append(args, dir)
|
|
cmd := exec.Command(test_helpers.GocryptfsBinary, args...)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
err := cmd.Run()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
// Change password using stdin
|
|
args = []string{"-q", "-passwd"}
|
|
args = append(args, extraArgs...)
|
|
args = append(args, dir)
|
|
cmd = exec.Command(test_helpers.GocryptfsBinary, args...)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
p, err := cmd.StdinPipe()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = cmd.Start()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
// Old password
|
|
p.Write([]byte("test\n"))
|
|
// New password
|
|
p.Write([]byte("newpasswd\n"))
|
|
p.Close()
|
|
err = cmd.Wait()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
// Test -passwd flag
|
|
func TestPasswd(t *testing.T) {
|
|
// Create FS
|
|
dir := test_helpers.InitFS(t)
|
|
mnt := dir + ".mnt"
|
|
// Add content
|
|
test_helpers.MountOrFatal(t, dir, mnt, "-extpass", "echo test")
|
|
file1 := mnt + "/file1"
|
|
err := ioutil.WriteFile(file1, []byte("somecontent"), 0600)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = test_helpers.UnmountErr(mnt)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Change password to "newpasswd"
|
|
testPasswd(t, dir)
|
|
// Mount and verify
|
|
test_helpers.MountOrFatal(t, dir, mnt, "-extpass", "echo newpasswd")
|
|
content, err := ioutil.ReadFile(file1)
|
|
if err != nil {
|
|
t.Error(err)
|
|
} else if string(content) != "somecontent" {
|
|
t.Errorf("wrong content: %q", string(content))
|
|
}
|
|
err = test_helpers.UnmountErr(mnt)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// Test -passwd with -masterkey
|
|
func TestPasswdMasterkey(t *testing.T) {
|
|
// Create FS
|
|
dir := test_helpers.InitFS(t)
|
|
// Overwrite with config with known master key
|
|
conf, err := ioutil.ReadFile("gocryptfs.conf.b9e5ba23")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
syscall.Unlink(dir + "/gocryptfs.conf")
|
|
err = ioutil.WriteFile(dir+"/gocryptfs.conf", conf, 0600)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Add content
|
|
mnt := dir + ".mnt"
|
|
test_helpers.MountOrFatal(t, dir, mnt, "-extpass", "echo test")
|
|
file1 := mnt + "/file1"
|
|
err = ioutil.WriteFile(file1, []byte("somecontent"), 0600)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
test_helpers.UnmountPanic(mnt)
|
|
// Change password using stdin
|
|
args := []string{"-q", "-passwd", "-masterkey",
|
|
"b9e5ba23-981a22b8-c8d790d8-627add29-f680513f-b7b7035f-d203fb83-21d82205"}
|
|
args = append(args, dir)
|
|
cmd := exec.Command(test_helpers.GocryptfsBinary, args...)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
p, err := cmd.StdinPipe()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = cmd.Start()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
// New password
|
|
p.Write([]byte("newpasswd\n"))
|
|
p.Close()
|
|
err = cmd.Wait()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
// Mount and verify
|
|
test_helpers.MountOrFatal(t, dir, mnt, "-extpass", "echo newpasswd")
|
|
content, err := ioutil.ReadFile(file1)
|
|
if err != nil {
|
|
t.Error(err)
|
|
} else if string(content) != "somecontent" {
|
|
t.Errorf("wrong content: %q", string(content))
|
|
}
|
|
test_helpers.UnmountPanic(mnt)
|
|
}
|
|
|
|
// Test -passwd with -reverse
|
|
func TestPasswdReverse(t *testing.T) {
|
|
// Create FS
|
|
dir := test_helpers.InitFS(t, "-reverse")
|
|
testPasswd(t, dir, "-reverse")
|
|
}
|
|
|
|
// Test -init & -config flag
|
|
func TestInitConfig(t *testing.T) {
|
|
config := test_helpers.TmpDir + "/TestInitConfig.conf"
|
|
dir := test_helpers.InitFS(t, "-config="+config)
|
|
|
|
_, err := os.Stat(config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Test -passwd & -config
|
|
cmd2 := exec.Command(test_helpers.GocryptfsBinary, "-q", "-passwd", "-extpass", "echo test",
|
|
"-config", config, dir)
|
|
cmd2.Stdout = os.Stdout
|
|
cmd2.Stderr = os.Stderr
|
|
err = cmd2.Run()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
// Test -ro
|
|
func TestRo(t *testing.T) {
|
|
dir := test_helpers.InitFS(t)
|
|
mnt := dir + ".mnt"
|
|
test_helpers.MountOrFatal(t, dir, mnt, "-ro", "-extpass=echo test")
|
|
defer test_helpers.UnmountPanic(mnt)
|
|
|
|
file := mnt + "/file"
|
|
err := os.Mkdir(file, 0777)
|
|
if err == nil {
|
|
t.Errorf("Mkdir should have failed")
|
|
}
|
|
_, err = os.Create(file)
|
|
if err == nil {
|
|
t.Errorf("Create should have failed")
|
|
}
|
|
}
|
|
|
|
// Test "-nonempty"
|
|
func TestNonempty(t *testing.T) {
|
|
dir := test_helpers.InitFS(t)
|
|
mnt := dir + ".mnt"
|
|
err := os.Mkdir(mnt, 0700)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = ioutil.WriteFile(mnt+"/somefile", []byte("xyz"), 0600)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = test_helpers.Mount(dir, mnt, false, "-extpass=echo test")
|
|
if err == nil {
|
|
t.Errorf("Mounting over a file should fail per default")
|
|
}
|
|
// Should work with "-nonempty"
|
|
test_helpers.MountOrFatal(t, dir, mnt, "-nonempty", "-extpass=echo test")
|
|
test_helpers.UnmountPanic(mnt)
|
|
}
|
|
|
|
// Test "mountpoint shadows cipherdir" handling
|
|
func TestShadows(t *testing.T) {
|
|
mnt := test_helpers.InitFS(t)
|
|
cipher := mnt + ".cipher"
|
|
err := os.Rename(mnt, cipher)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// This should work
|
|
// (note that MountOrFatal creates "mnt" again)
|
|
test_helpers.MountOrFatal(t, cipher, mnt, "-extpass=echo test")
|
|
test_helpers.UnmountPanic(mnt)
|
|
cipher2 := mnt + "/cipher"
|
|
err = os.Rename(cipher, cipher2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// This should fail
|
|
err = test_helpers.Mount(cipher2, mnt, false, "-extpass=echo test")
|
|
if err == nil {
|
|
t.Errorf("Should have failed")
|
|
}
|
|
}
|
|
|
|
// TestInitTrailingGarbage verfies that gocryptfs exits with an error if we
|
|
// pass additional data after the password.
|
|
func TestInitTrailingGarbage(t *testing.T) {
|
|
table := []struct {
|
|
pw string
|
|
closeStdin bool
|
|
expectSuccess bool
|
|
}{
|
|
{"foo\n", false, true},
|
|
{"foo", true, true},
|
|
{"foo\n", true, true},
|
|
{"foo\n\n", false, false},
|
|
{"foo\nbar", false, false},
|
|
{"foo\n\n", true, false},
|
|
{"foo\nbar", true, false},
|
|
}
|
|
for _, row := range table {
|
|
dir, err := ioutil.TempDir(test_helpers.TmpDir, "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cmd := exec.Command(test_helpers.GocryptfsBinary, "-q", "-init", "-scryptn=10", dir)
|
|
childStdin, err := cmd.StdinPipe()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = cmd.Start()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
childStdin.Write([]byte(row.pw))
|
|
if row.closeStdin {
|
|
childStdin.Close()
|
|
}
|
|
err = cmd.Wait()
|
|
success := (err == nil)
|
|
if success == true && row.expectSuccess == false {
|
|
t.Errorf("pw=%q should have failed, but succeeded", row.pw)
|
|
} else if success == false && row.expectSuccess == true {
|
|
t.Errorf("pw=%q should have succeeded, but failed", row.pw)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestMountPasswordIncorrect makes sure the correct exit code is used when the password
|
|
// was incorrect while mounting
|
|
func TestMountPasswordIncorrect(t *testing.T) {
|
|
cDir := test_helpers.InitFS(t) // Create filesystem with password "test"
|
|
pDir := cDir + ".mnt"
|
|
err := test_helpers.Mount(cDir, pDir, false, "-extpass", "echo WRONG", "-wpanic=false")
|
|
exitCode := extractExitCode(err)
|
|
if exitCode != exitcodes.PasswordIncorrect {
|
|
t.Errorf("want=%d, got=%d", exitcodes.PasswordIncorrect, exitCode)
|
|
}
|
|
}
|
|
|
|
// TestPasswdPasswordIncorrect makes sure the correct exit code is used when the password
|
|
// was incorrect while changing the password
|
|
func TestPasswdPasswordIncorrect(t *testing.T) {
|
|
cDir := test_helpers.InitFS(t) // Create filesystem with password "test"
|
|
// Change password
|
|
cmd := exec.Command(test_helpers.GocryptfsBinary, "-passwd", cDir)
|
|
childStdin, err := cmd.StdinPipe()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = cmd.Start()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = childStdin.Write([]byte("WRONGPASSWORD\nNewPassword"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = childStdin.Close()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = cmd.Wait()
|
|
exitCode := extractExitCode(err)
|
|
if exitCode != exitcodes.PasswordIncorrect {
|
|
t.Errorf("want=%d, got=%d", exitcodes.PasswordIncorrect, exitCode)
|
|
}
|
|
}
|
|
|
|
// Check that we correctly background on mount and close stderr and stdout.
|
|
// Something like
|
|
// gocryptfs a b | cat
|
|
// must not hang ( https://github.com/rfjakob/gocryptfs/issues/130 ).
|
|
func TestMountBackground(t *testing.T) {
|
|
dir := test_helpers.InitFS(t)
|
|
mnt := dir + ".mnt"
|
|
err := os.Mkdir(mnt, 0700)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Manually create a pipe pair and connect the child's stdout and stderr
|
|
// to it. We cannot use StdoutPipe because that will close the pipe
|
|
// when the child forks away.
|
|
pr, pw, err := os.Pipe()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
args := []string{"-extpass", "echo test", dir, mnt}
|
|
cmd := exec.Command(test_helpers.GocryptfsBinary, args...)
|
|
cmd.Stdout = pw
|
|
cmd.Stderr = pw
|
|
err = cmd.Run()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
pw.Close()
|
|
defer test_helpers.UnmountPanic(mnt)
|
|
// Read until we get EOF.
|
|
c1 := make(chan struct{}, 1)
|
|
go func() {
|
|
buf := make([]byte, 1000)
|
|
for {
|
|
_, err = pr.Read(buf)
|
|
// We should get io.EOF when the child closes stdout
|
|
// and stderr.
|
|
if err != nil {
|
|
c1 <- struct{}{}
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
select {
|
|
case <-c1:
|
|
return
|
|
case <-time.After(time.Second * 5):
|
|
t.Fatal("timeout")
|
|
}
|
|
}
|
|
|
|
// Test that "gocryptfs -init -info CIPHERDIR" returns an error to the
|
|
// user. Only one operation flag is allowed.
|
|
func TestMultipleOperationFlags(t *testing.T) {
|
|
// Test all combinations
|
|
opFlags := []string{"-init", "-info", "-passwd", "-fsck"}
|
|
for _, flag1 := range opFlags {
|
|
var flag2 string
|
|
for _, flag2 = range opFlags {
|
|
if flag1 == flag2 {
|
|
continue
|
|
}
|
|
args := []string{flag1, flag2, "/tmp"}
|
|
//t.Logf("testing %v", args)
|
|
cmd := exec.Command(test_helpers.GocryptfsBinary, args...)
|
|
err := cmd.Run()
|
|
exitCode := extractExitCode(err)
|
|
if exitCode != exitcodes.Usage {
|
|
t.Fatalf("this should have failed with code %d, but returned %d",
|
|
exitcodes.Usage, exitCode)
|
|
}
|
|
}
|
|
}
|
|
}
|