Add "-extpass" cli option and associated tests
This commit is contained in:
parent
066c2c90eb
commit
d95fc2333a
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
46
integration_tests/cli_test.go
Normal file
46
integration_tests/cli_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
@ -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
25
main.go
@ -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()
|
||||||
}
|
}
|
||||||
|
54
password.go
54
password.go
@ -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
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user