diff options
| -rw-r--r-- | MANPAGE.md | 5 | ||||
| -rw-r--r-- | cryptfs/filter.go | 5 | ||||
| -rw-r--r-- | integration_tests/cli_test.go | 46 | ||||
| -rw-r--r-- | integration_tests/helpers.go | 16 | ||||
| -rw-r--r-- | main.go | 25 | ||||
| -rw-r--r-- | password.go | 54 | 
6 files changed, 116 insertions, 35 deletions
@@ -39,6 +39,11 @@ Options:  **-debug**  :	Enable debug output +**-extpass string** +:	Use an external program (like ssh-askpass) for the password prompt. +The program should return the password on stdout, a trailing newline is +stripped by gocryptfs. +  **-f**  :	Stay in the foreground diff --git a/cryptfs/filter.go b/cryptfs/filter.go index 079b64f..f80889d 100644 --- a/cryptfs/filter.go +++ b/cryptfs/filter.go @@ -6,8 +6,9 @@ package cryptfs  // when file names are not encrypted  func (be *CryptFS) IsFiltered(path string) bool {  	// gocryptfs.conf in the root directory is forbidden -	if be.plaintextNames == true && path == "gocryptfs.conf" { -		Warn.Printf("The name \"/gocryptfs.conf\" is reserved when \"--plaintextnames\" is used\n") +	if be.plaintextNames == true && path == ConfDefaultName { +		Warn.Printf("The name /%s is reserved when -plaintextnames is used\n", +			ConfDefaultName)  		return true  	}  	return false diff --git a/integration_tests/cli_test.go b/integration_tests/cli_test.go new file mode 100644 index 0000000..a696600 --- /dev/null +++ b/integration_tests/cli_test.go @@ -0,0 +1,46 @@ +package integration_tests + +// Test CLI operations like "-init", "-password" etc + +import ( +	"os" +	"os/exec" +	"testing" + +	"github.com/rfjakob/gocryptfs/cryptfs" +) + +func TestInit(t *testing.T) { +	dir := tmpDir + "TestInit/" +	err := os.Mkdir(dir, 0777) +	if err != nil { +		t.Fatal(err) +	} +	cmd := exec.Command(gocryptfsBinary, "-init", "-extpass", "echo test", dir) +	if testing.Verbose() { +		cmd.Stdout = os.Stdout +		cmd.Stderr = os.Stderr +	} +	err = cmd.Run() +	if err != nil { +		t.Error(err) +	} +	_, err = os.Stat(dir + cryptfs.ConfDefaultName) +	if err != nil { +		t.Error(err) +	} +} + +// "dir" has been initialized by TestInit +func TestPasswd(t *testing.T) { +	dir := tmpDir + "TestInit/" +	cmd := exec.Command(gocryptfsBinary, "-passwd", "-extpass", "echo test", dir) +	if testing.Verbose() { +		cmd.Stdout = os.Stdout +		cmd.Stderr = os.Stderr +	} +	err := cmd.Run() +	if err != nil { +		t.Error(err) +	} +} diff --git a/integration_tests/helpers.go b/integration_tests/helpers.go index fdad28b..3ab1d21 100644 --- a/integration_tests/helpers.go +++ b/integration_tests/helpers.go @@ -10,13 +10,13 @@ import (  	"testing"  ) +// Note: the code assumes that all have a trailing slash  const tmpDir = "/tmp/gocryptfs_main_test/" - -// Mountpoint -// Note: the code assumes that both have a trailing slash!  const plainDir = tmpDir + "plain/"  const cipherDir = tmpDir + "cipher/" +const gocryptfsBinary = "../gocryptfs" +  func resetTmpDir() {  	fu := exec.Command("fusermount", "-z", "-u", plainDir)  	fu.Run() @@ -40,11 +40,11 @@ func mount(extraArgs ...string) {  	//args = append(args, "--fusedebug")  	args = append(args, cipherDir)  	args = append(args, plainDir) -	c := exec.Command("../gocryptfs", args...) -	// Warning messages clutter the test output. Uncomment if you want to debug -	// failures. -	//c.Stdout = os.Stdout -	//c.Stderr = os.Stderr +	c := exec.Command(gocryptfsBinary, args...) +	if testing.Verbose() { +		c.Stdout = os.Stdout +		c.Stderr = os.Stderr +	}  	err := c.Run()  	if err != nil {  		fmt.Println(err) @@ -44,7 +44,7 @@ func initDir(args *argContainer) {  	}  	cryptfs.Info.Printf("Choose a password for protecting your files.\n") -	password := readPasswordTwice() +	password := readPasswordTwice(args.extpass)  	err = cryptfs.CreateConfFile(args.config, password, args.plaintextnames)  	if err != nil {  		fmt.Println(err) @@ -66,25 +66,25 @@ func usageText() {  type argContainer struct {  	debug, init, zerokey, fusedebug, openssl, passwd, foreground, version,  	plaintextnames, quiet bool -	masterkey, mountpoint, cipherdir, cpuprofile, config string -	notifypid                                    int +	masterkey, mountpoint, cipherdir, cpuprofile, config, extpass string +	notifypid                                                     int  }  var flagSet *flag.FlagSet  // loadConfig - load the config file "filename", prompting the user for the password -func loadConfig(filename string) (masterkey []byte, confFile *cryptfs.ConfFile) { +func loadConfig(args *argContainer) (masterkey []byte, confFile *cryptfs.ConfFile) {  	// Check if the file exists at all before prompting for a password -	_, err := os.Stat(filename) +	_, err := os.Stat(args.config)  	if err != nil {  		fmt.Print(err)  		os.Exit(ERREXIT_LOADCONF)  	}  	fmt.Printf("Password: ") -	pw := readPassword() +	pw := readPassword(args.extpass)  	cryptfs.Info.Printf("Decrypting master key... ")  	cryptfs.Warn.Disable() // Silence DecryptBlock() error messages on incorrect password -	masterkey, confFile, err = cryptfs.LoadConfFile(filename, pw) +	masterkey, confFile, err = cryptfs.LoadConfFile(args.config, pw)  	cryptfs.Warn.Enable()  	if err != nil {  		fmt.Println(err) @@ -97,10 +97,10 @@ func loadConfig(filename string) (masterkey []byte, confFile *cryptfs.ConfFile)  }  // changePassword - change the password of config file "filename" -func changePassword(filename string) { -	masterkey, confFile := loadConfig(filename) +func changePassword(args *argContainer) { +	masterkey, confFile := loadConfig(args)  	fmt.Printf("Please enter your new password.\n") -	newPw := readPasswordTwice() +	newPw := readPasswordTwice(args.extpass)  	confFile.EncryptKey(masterkey, newPw)  	err := confFile.WriteFile()  	if err != nil { @@ -139,6 +139,7 @@ func main() {  	flagSet.StringVar(&args.masterkey, "masterkey", "", "Mount with explicit master key")  	flagSet.StringVar(&args.cpuprofile, "cpuprofile", "", "Write cpu profile to specified file")  	flagSet.StringVar(&args.config, "config", "", "Use specified config file instead of CIPHERDIR/gocryptfs.conf") +	flagSet.StringVar(&args.extpass, "extpass", "", "Use external program for the password prompt")  	flagSet.IntVar(&args.notifypid, "notifypid", 0, "Send USR1 to the specified process after "+  		"successful mount - used internally for daemonization")  	flagSet.Parse(os.Args[1:]) @@ -215,7 +216,7 @@ func main() {  			fmt.Printf("Usage: %s -passwd [OPTIONS] CIPHERDIR\n", PROGRAM_NAME)  			os.Exit(ERREXIT_USAGE)  		} -		changePassword(args.config) // does not return +		changePassword(&args) // does not return  	}  	// Mount  	// Check mountpoint @@ -248,7 +249,7 @@ func main() {  	} else {  		// Load master key from config file  		var confFile *cryptfs.ConfFile -		masterkey, confFile = loadConfig(args.config) +		masterkey, confFile = loadConfig(&args)  		printMasterKey(masterkey)  		args.plaintextnames = confFile.PlaintextNames()  	} diff --git a/password.go b/password.go index 6b3452a..ede65dd 100644 --- a/password.go +++ b/password.go @@ -2,15 +2,18 @@ package main  import (  	"fmt" -	"golang.org/x/crypto/ssh/terminal"  	"os" +	"os/exec" +	"strings" + +	"golang.org/x/crypto/ssh/terminal"  ) -func readPasswordTwice() string { +func readPasswordTwice(extpass string) string {  	fmt.Printf("Password: ") -	p1 := readPassword() -	fmt.Printf("Repeat: ") -	p2 := readPassword() +	p1 := readPassword(extpass) +	fmt.Printf("Repeat:   ") +	p2 := readPassword(extpass)  	if p1 != p2 {  		fmt.Printf("Passwords do not match\n")  		os.Exit(ERREXIT_PASSWORD) @@ -18,14 +21,39 @@ func readPasswordTwice() string {  	return p1  } -// Get password from terminal -func readPassword() string { -	fd := int(os.Stdin.Fd()) -	p, err := terminal.ReadPassword(fd) -	fmt.Printf("\n") -	if err != nil { -		fmt.Printf("Error: Could not read password: %v\n", err) +// readPassword - get password from terminal +// or from the "extpass" program +func readPassword(extpass string) string { +	var password string +	var err error +	var output []byte +	if extpass != "" { +		parts := strings.Split(extpass, " ") +		cmd := exec.Command(parts[0], parts[1:]...) +		cmd.Stderr = os.Stderr +		output, err = cmd.Output() +		if err != nil { +			fmt.Printf("extpass program returned error: %v\n", err) +			os.Exit(ERREXIT_PASSWORD) +		} +		fmt.Printf("(extpass)\n") +		// Trim trailing newline like terminal.ReadPassword() does +		if output[len(output)-1] == '\n' { +			output = output[:len(output)-1] +		} +	} else { +		fd := int(os.Stdin.Fd()) +		output, err = terminal.ReadPassword(fd) +		if err != nil { +			fmt.Printf("Error: Could not read password from terminal: %v\n", err) +			os.Exit(ERREXIT_PASSWORD) +		} +		fmt.Printf("\n") +	} +	password = string(output) +	if password == "" { +		fmt.Printf("Error: password is empty\n")  		os.Exit(ERREXIT_PASSWORD)  	} -	return string(p) +	return password  }  | 
