Add "-extpass" cli option and associated tests

This commit is contained in:
Jakob Unterwurzacher 2015-11-15 13:38:19 +01:00
parent 066c2c90eb
commit d95fc2333a
6 changed files with 116 additions and 35 deletions

View File

@ -39,6 +39,11 @@ Options:
**-debug** **-debug**
: Enable debug output : 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** **-f**
: Stay in the foreground : Stay in the foreground

View File

@ -6,8 +6,9 @@ package cryptfs
// when file names are not encrypted // when file names are not encrypted
func (be *CryptFS) IsFiltered(path string) bool { func (be *CryptFS) IsFiltered(path string) bool {
// gocryptfs.conf in the root directory is forbidden // gocryptfs.conf in the root directory is forbidden
if be.plaintextNames == true && path == "gocryptfs.conf" { if be.plaintextNames == true && path == ConfDefaultName {
Warn.Printf("The name \"/gocryptfs.conf\" is reserved when \"--plaintextnames\" is used\n") Warn.Printf("The name /%s is reserved when -plaintextnames is used\n",
ConfDefaultName)
return true return true
} }
return false return false

View File

@ -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)
}
}

View File

@ -10,13 +10,13 @@ import (
"testing" "testing"
) )
// Note: the code assumes that all have a trailing slash
const tmpDir = "/tmp/gocryptfs_main_test/" const tmpDir = "/tmp/gocryptfs_main_test/"
// Mountpoint
// Note: the code assumes that both have a trailing slash!
const plainDir = tmpDir + "plain/" const plainDir = tmpDir + "plain/"
const cipherDir = tmpDir + "cipher/" const cipherDir = tmpDir + "cipher/"
const gocryptfsBinary = "../gocryptfs"
func resetTmpDir() { func resetTmpDir() {
fu := exec.Command("fusermount", "-z", "-u", plainDir) fu := exec.Command("fusermount", "-z", "-u", plainDir)
fu.Run() fu.Run()
@ -40,11 +40,11 @@ func mount(extraArgs ...string) {
//args = append(args, "--fusedebug") //args = append(args, "--fusedebug")
args = append(args, cipherDir) args = append(args, cipherDir)
args = append(args, plainDir) args = append(args, plainDir)
c := exec.Command("../gocryptfs", args...) c := exec.Command(gocryptfsBinary, args...)
// Warning messages clutter the test output. Uncomment if you want to debug if testing.Verbose() {
// failures. c.Stdout = os.Stdout
//c.Stdout = os.Stdout c.Stderr = os.Stderr
//c.Stderr = os.Stderr }
err := c.Run() err := c.Run()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)

25
main.go
View File

@ -44,7 +44,7 @@ func initDir(args *argContainer) {
} }
cryptfs.Info.Printf("Choose a password for protecting your files.\n") 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) err = cryptfs.CreateConfFile(args.config, password, args.plaintextnames)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
@ -66,25 +66,25 @@ func usageText() {
type argContainer struct { type argContainer struct {
debug, init, zerokey, fusedebug, openssl, passwd, foreground, version, debug, init, zerokey, fusedebug, openssl, passwd, foreground, version,
plaintextnames, quiet bool plaintextnames, quiet bool
masterkey, mountpoint, cipherdir, cpuprofile, config string masterkey, mountpoint, cipherdir, cpuprofile, config, extpass string
notifypid int notifypid int
} }
var flagSet *flag.FlagSet var flagSet *flag.FlagSet
// loadConfig - load the config file "filename", prompting the user for the password // 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 // Check if the file exists at all before prompting for a password
_, err := os.Stat(filename) _, err := os.Stat(args.config)
if err != nil { if err != nil {
fmt.Print(err) fmt.Print(err)
os.Exit(ERREXIT_LOADCONF) os.Exit(ERREXIT_LOADCONF)
} }
fmt.Printf("Password: ") fmt.Printf("Password: ")
pw := readPassword() pw := readPassword(args.extpass)
cryptfs.Info.Printf("Decrypting master key... ") cryptfs.Info.Printf("Decrypting master key... ")
cryptfs.Warn.Disable() // Silence DecryptBlock() error messages on incorrect password 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() cryptfs.Warn.Enable()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
@ -97,10 +97,10 @@ func loadConfig(filename string) (masterkey []byte, confFile *cryptfs.ConfFile)
} }
// changePassword - change the password of config file "filename" // changePassword - change the password of config file "filename"
func changePassword(filename string) { func changePassword(args *argContainer) {
masterkey, confFile := loadConfig(filename) masterkey, confFile := loadConfig(args)
fmt.Printf("Please enter your new password.\n") fmt.Printf("Please enter your new password.\n")
newPw := readPasswordTwice() newPw := readPasswordTwice(args.extpass)
confFile.EncryptKey(masterkey, newPw) confFile.EncryptKey(masterkey, newPw)
err := confFile.WriteFile() err := confFile.WriteFile()
if err != nil { if err != nil {
@ -139,6 +139,7 @@ func main() {
flagSet.StringVar(&args.masterkey, "masterkey", "", "Mount with explicit master key") flagSet.StringVar(&args.masterkey, "masterkey", "", "Mount with explicit master key")
flagSet.StringVar(&args.cpuprofile, "cpuprofile", "", "Write cpu profile to specified file") 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.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 "+ flagSet.IntVar(&args.notifypid, "notifypid", 0, "Send USR1 to the specified process after "+
"successful mount - used internally for daemonization") "successful mount - used internally for daemonization")
flagSet.Parse(os.Args[1:]) flagSet.Parse(os.Args[1:])
@ -215,7 +216,7 @@ func main() {
fmt.Printf("Usage: %s -passwd [OPTIONS] CIPHERDIR\n", PROGRAM_NAME) fmt.Printf("Usage: %s -passwd [OPTIONS] CIPHERDIR\n", PROGRAM_NAME)
os.Exit(ERREXIT_USAGE) os.Exit(ERREXIT_USAGE)
} }
changePassword(args.config) // does not return changePassword(&args) // does not return
} }
// Mount // Mount
// Check mountpoint // Check mountpoint
@ -248,7 +249,7 @@ func main() {
} else { } else {
// Load master key from config file // Load master key from config file
var confFile *cryptfs.ConfFile var confFile *cryptfs.ConfFile
masterkey, confFile = loadConfig(args.config) masterkey, confFile = loadConfig(&args)
printMasterKey(masterkey) printMasterKey(masterkey)
args.plaintextnames = confFile.PlaintextNames() args.plaintextnames = confFile.PlaintextNames()
} }

View File

@ -2,15 +2,18 @@ package main
import ( import (
"fmt" "fmt"
"golang.org/x/crypto/ssh/terminal"
"os" "os"
"os/exec"
"strings"
"golang.org/x/crypto/ssh/terminal"
) )
func readPasswordTwice() string { func readPasswordTwice(extpass string) string {
fmt.Printf("Password: ") fmt.Printf("Password: ")
p1 := readPassword() p1 := readPassword(extpass)
fmt.Printf("Repeat: ") fmt.Printf("Repeat: ")
p2 := readPassword() p2 := readPassword(extpass)
if p1 != p2 { if p1 != p2 {
fmt.Printf("Passwords do not match\n") fmt.Printf("Passwords do not match\n")
os.Exit(ERREXIT_PASSWORD) os.Exit(ERREXIT_PASSWORD)
@ -18,14 +21,39 @@ func readPasswordTwice() string {
return p1 return p1
} }
// Get password from terminal // readPassword - get password from terminal
func readPassword() string { // or from the "extpass" program
fd := int(os.Stdin.Fd()) func readPassword(extpass string) string {
p, err := terminal.ReadPassword(fd) var password string
fmt.Printf("\n") var err error
if err != nil { var output []byte
fmt.Printf("Error: Could not read password: %v\n", err) 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) os.Exit(ERREXIT_PASSWORD)
} }
return string(p) return password
} }