Allow multiple -extpass arguments
To support arguments containing spaces, -extpass can now be passed multiple times. https://github.com/rfjakob/gocryptfs/issues/289
This commit is contained in:
parent
61940a9c06
commit
cf27037f20
@ -89,8 +89,19 @@ Enable (`-exec`) or disable (`-noexec`) executables in a gocryptfs mount
|
||||
#### -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. Using something like "cat /mypassword.txt" allows
|
||||
one to mount the gocryptfs filesystem without user interaction.
|
||||
stripped by gocryptfs. If you just want to read from a password file, see `-passfile`.
|
||||
|
||||
When `-extpass` is specified once, the string argument will be split on spaces.
|
||||
For example, `-extpass "md5sum my password.txt"` will be executed as
|
||||
`"md5sum" "my" "password.txt"`, which is NOT what you want.
|
||||
|
||||
Specify `-extpass` twice or more to use the string arguments as-is.
|
||||
For example, you DO want to call `md5sum` like this:
|
||||
`-extpass "md5sum" -extpass "my password.txt"`.
|
||||
|
||||
If you want to prevent splitting on spaces but don't want to pass arguments
|
||||
to your program, use `"--"`, which is accepted by most programs:
|
||||
`-extpass "my program" -extpass "--"`
|
||||
|
||||
#### -fg, -f
|
||||
Stay in the foreground instead of forking away. Implies "-nosyslog".
|
||||
|
@ -182,6 +182,8 @@ v1.7, in progress (v1.7-beta1: 2019-01-03, v1.7-rc1: 2019-01-04)
|
||||
([#320](https://github.com/rfjakob/gocryptfs/issues/320)).
|
||||
Prevents trouble in the unlikely case that gocryptfs is called with
|
||||
stdin,stdout and/or stderr closed.
|
||||
* `-extpass` now can be specified multiple times to support arguments containing spaces
|
||||
([#289](https://github.com/rfjakob/gocryptfs/issues/289))
|
||||
|
||||
v1.6.1, 2018-12-12
|
||||
* Fix "Operation not supported" chmod errors on Go 1.11
|
||||
|
20
cli_args.go
20
cli_args.go
@ -32,9 +32,11 @@ type argContainer struct {
|
||||
sharedstorage, devrandom, fsck, trezor bool
|
||||
// Mount options with opposites
|
||||
dev, nodev, suid, nosuid, exec, noexec, rw, ro bool
|
||||
masterkey, mountpoint, cipherdir, cpuprofile, extpass,
|
||||
masterkey, mountpoint, cipherdir, cpuprofile,
|
||||
memprofile, ko, passfile, ctlsock, fsname, force_owner, trace string
|
||||
// For reverse mode, --exclude is available. It can be specified multiple times.
|
||||
// -extpass can be passed multiple times
|
||||
extpass multipleStrings
|
||||
// For reverse mode, -exclude is available. It can be specified multiple times.
|
||||
exclude multipleStrings
|
||||
// Configuration file name override
|
||||
config string
|
||||
@ -62,6 +64,11 @@ func (s *multipleStrings) Set(val string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *multipleStrings) Empty() bool {
|
||||
s2 := []string(*s)
|
||||
return len(s2) == 0
|
||||
}
|
||||
|
||||
var flagSet *flag.FlagSet
|
||||
|
||||
// prefixOArgs transform options passed via "-o foo,bar" into regular options
|
||||
@ -179,7 +186,6 @@ func parseCliOpts() (args argContainer) {
|
||||
flagSet.StringVar(&args.cpuprofile, "cpuprofile", "", "Write cpu profile to specified file")
|
||||
flagSet.StringVar(&args.memprofile, "memprofile", "", "Write memory 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.StringVar(&args.passfile, "passfile", "", "Read password from file")
|
||||
flagSet.StringVar(&args.ko, "ko", "", "Pass additional options directly to the kernel, comma-separated list")
|
||||
flagSet.StringVar(&args.ctlsock, "ctlsock", "", "Create control socket at specified path")
|
||||
@ -190,6 +196,8 @@ func parseCliOpts() (args argContainer) {
|
||||
// -e, --exclude
|
||||
flagSet.Var(&args.exclude, "e", "Alias for -exclude")
|
||||
flagSet.Var(&args.exclude, "exclude", "Exclude relative path from reverse view")
|
||||
// -extpass
|
||||
flagSet.Var(&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")
|
||||
@ -248,7 +256,7 @@ func parseCliOpts() (args argContainer) {
|
||||
args.allow_other = false
|
||||
args.ko = "noexec"
|
||||
}
|
||||
if args.extpass != "" && args.passfile != "" {
|
||||
if !args.extpass.Empty() && args.passfile != "" {
|
||||
tlog.Fatal.Printf("The options -extpass and -passfile cannot be used at the same time")
|
||||
os.Exit(exitcodes.Usage)
|
||||
}
|
||||
@ -256,11 +264,11 @@ func parseCliOpts() (args argContainer) {
|
||||
tlog.Fatal.Printf("The options -passfile and -masterkey cannot be used at the same time")
|
||||
os.Exit(exitcodes.Usage)
|
||||
}
|
||||
if args.extpass != "" && args.masterkey != "" {
|
||||
if !args.extpass.Empty() && args.masterkey != "" {
|
||||
tlog.Fatal.Printf("The options -extpass and -masterkey cannot be used at the same time")
|
||||
os.Exit(exitcodes.Usage)
|
||||
}
|
||||
if args.extpass != "" && args.trezor {
|
||||
if !args.extpass.Empty() && args.trezor {
|
||||
tlog.Fatal.Printf("The options -extpass and -trezor cannot be used at the same time")
|
||||
os.Exit(exitcodes.Usage)
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ func main() {
|
||||
|
||||
func dumpMasterKey(fn string) {
|
||||
tlog.Info.Enabled = false
|
||||
pw := readpassword.Once("", "", "")
|
||||
pw := readpassword.Once(nil, "", "")
|
||||
masterkey, _, err := configfile.LoadAndDecrypt(fn, pw)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
|
@ -68,7 +68,7 @@ func initDir(args *argContainer) {
|
||||
}
|
||||
}
|
||||
// Choose password for config file
|
||||
if args.extpass == "" {
|
||||
if args.extpass.Empty() {
|
||||
tlog.Info.Printf("Choose a password for protecting your files.")
|
||||
}
|
||||
{
|
||||
@ -80,7 +80,7 @@ func initDir(args *argContainer) {
|
||||
password = readpassword.Trezor(trezorPayload)
|
||||
} else {
|
||||
// Normal password entry
|
||||
password = readpassword.Twice(args.extpass, args.passfile)
|
||||
password = readpassword.Twice([]string(args.extpass), args.passfile)
|
||||
readpassword.CheckTrailingGarbage()
|
||||
}
|
||||
creator := tlog.ProgramName + " " + GitVersion
|
||||
|
@ -18,7 +18,7 @@ func TestMain(m *testing.M) {
|
||||
|
||||
func TestExtpass(t *testing.T) {
|
||||
p1 := "ads2q4tw41reg52"
|
||||
p2 := string(readPasswordExtpass("echo " + p1))
|
||||
p2 := string(readPasswordExtpass([]string{"echo " + p1}))
|
||||
if p1 != p2 {
|
||||
t.Errorf("p1=%q != p2=%q", p1, p2)
|
||||
}
|
||||
@ -26,7 +26,33 @@ func TestExtpass(t *testing.T) {
|
||||
|
||||
func TestOnceExtpass(t *testing.T) {
|
||||
p1 := "lkadsf0923rdfi48rqwhdsf"
|
||||
p2 := string(Once("echo "+p1, "", ""))
|
||||
p2 := string(Once([]string{"echo " + p1}, "", ""))
|
||||
if p1 != p2 {
|
||||
t.Errorf("p1=%q != p2=%q", p1, p2)
|
||||
}
|
||||
}
|
||||
|
||||
// extpass with two arguments
|
||||
func TestOnceExtpass2(t *testing.T) {
|
||||
p1 := "foo"
|
||||
p2 := string(Once([]string{"echo", p1}, "", ""))
|
||||
if p1 != p2 {
|
||||
t.Errorf("p1=%q != p2=%q", p1, p2)
|
||||
}
|
||||
}
|
||||
|
||||
// extpass with three arguments
|
||||
func TestOnceExtpass3(t *testing.T) {
|
||||
p1 := "foo bar baz"
|
||||
p2 := string(Once([]string{"echo", "foo", "bar", "baz"}, "", ""))
|
||||
if p1 != p2 {
|
||||
t.Errorf("p1=%q != p2=%q", p1, p2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOnceExtpassSpaces(t *testing.T) {
|
||||
p1 := "mypassword"
|
||||
p2 := string(Once([]string{"cat", "passfile_test_files/file with spaces.txt"}, "", ""))
|
||||
if p1 != p2 {
|
||||
t.Errorf("p1=%q != p2=%q", p1, p2)
|
||||
}
|
||||
@ -34,7 +60,7 @@ func TestOnceExtpass(t *testing.T) {
|
||||
|
||||
func TestTwiceExtpass(t *testing.T) {
|
||||
p1 := "w5w44t3wfe45srz434"
|
||||
p2 := string(Once("echo "+p1, "", ""))
|
||||
p2 := string(Once([]string{"echo " + p1}, "", ""))
|
||||
if p1 != p2 {
|
||||
t.Errorf("p1=%q != p2=%q", p1, p2)
|
||||
}
|
||||
@ -46,7 +72,7 @@ func TestTwiceExtpass(t *testing.T) {
|
||||
// https://talks.golang.org/2014/testing.slide#23 .
|
||||
func TestExtpassEmpty(t *testing.T) {
|
||||
if os.Getenv("TEST_SLAVE") == "1" {
|
||||
readPasswordExtpass("echo")
|
||||
readPasswordExtpass([]string{"echo"})
|
||||
return
|
||||
}
|
||||
cmd := exec.Command(os.Args[0], "-test.run=TestExtpassEmpty$")
|
||||
|
@ -14,6 +14,7 @@ func TestPassfile(t *testing.T) {
|
||||
{"mypassword.txt", "mypassword"},
|
||||
{"mypassword_garbage.txt", "mypassword"},
|
||||
{"mypassword_missing_newline.txt", "mypassword"},
|
||||
{"file with spaces.txt", "mypassword"},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
pw := readPassFile("passfile_test_files/" + tc.file)
|
||||
|
@ -0,0 +1 @@
|
||||
mypassword
|
@ -24,11 +24,11 @@ const (
|
||||
|
||||
// Once tries to get a password from the user, either from the terminal, extpass
|
||||
// or stdin. Leave "prompt" empty to use the default "Password: " prompt.
|
||||
func Once(extpass string, passfile string, prompt string) []byte {
|
||||
func Once(extpass []string, passfile string, prompt string) []byte {
|
||||
if passfile != "" {
|
||||
return readPassFile(passfile)
|
||||
}
|
||||
if extpass != "" {
|
||||
if len(extpass) != 0 {
|
||||
return readPasswordExtpass(extpass)
|
||||
}
|
||||
if prompt == "" {
|
||||
@ -42,11 +42,11 @@ func Once(extpass string, passfile string, prompt string) []byte {
|
||||
|
||||
// Twice is the same as Once but will prompt twice if we get the password from
|
||||
// the terminal.
|
||||
func Twice(extpass string, passfile string) []byte {
|
||||
func Twice(extpass []string, passfile string) []byte {
|
||||
if passfile != "" {
|
||||
return readPassFile(passfile)
|
||||
}
|
||||
if extpass != "" {
|
||||
if len(extpass) != 0 {
|
||||
return readPasswordExtpass(extpass)
|
||||
}
|
||||
if !terminal.IsTerminal(int(os.Stdin.Fd())) {
|
||||
@ -99,9 +99,14 @@ func readPasswordStdin(prompt string) []byte {
|
||||
// readPasswordExtpass executes the "extpass" program and returns the first line
|
||||
// of the output.
|
||||
// Exits on read error or empty result.
|
||||
func readPasswordExtpass(extpass string) []byte {
|
||||
tlog.Info.Println("Reading password from extpass program")
|
||||
parts := strings.Split(extpass, " ")
|
||||
func readPasswordExtpass(extpass []string) []byte {
|
||||
var parts []string
|
||||
if len(extpass) == 1 {
|
||||
parts = strings.Split(extpass[0], " ")
|
||||
} else {
|
||||
parts = extpass
|
||||
}
|
||||
tlog.Info.Printf("Reading password from extpass program %q, arguments: %q\n", parts[0], parts[1:])
|
||||
cmd := exec.Command(parts[0], parts[1:]...)
|
||||
cmd.Stderr = os.Stderr
|
||||
pipe, err := cmd.StdoutPipe()
|
||||
|
4
main.go
4
main.go
@ -53,7 +53,7 @@ func loadConfig(args *argContainer) (masterkey []byte, cf *configfile.ConfFile,
|
||||
pw = readpassword.Trezor(cf.TrezorPayload)
|
||||
} else {
|
||||
// Normal password entry
|
||||
pw = readpassword.Once(args.extpass, args.passfile, "")
|
||||
pw = readpassword.Once([]string(args.extpass), args.passfile, "")
|
||||
}
|
||||
tlog.Info.Println("Decrypting master key")
|
||||
masterkey, err = cf.DecryptMasterKey(pw)
|
||||
@ -93,7 +93,7 @@ func changePassword(args *argContainer) {
|
||||
log.Panic("empty masterkey")
|
||||
}
|
||||
tlog.Info.Println("Please enter your new password.")
|
||||
newPw := readpassword.Twice(args.extpass, args.passfile)
|
||||
newPw := readpassword.Twice([]string(args.extpass), args.passfile)
|
||||
readpassword.CheckTrailingGarbage()
|
||||
confFile.EncryptKey(masterkey, newPw, confFile.ScryptObject.LogN())
|
||||
for i := range newPw {
|
||||
|
@ -43,7 +43,7 @@ func getMasterKey(args *argContainer) (masterkey []byte, confFile *configfile.Co
|
||||
masterkeyFromStdin := false
|
||||
// "-masterkey=stdin"
|
||||
if args.masterkey == "stdin" {
|
||||
args.masterkey = string(readpassword.Once("", "", "Masterkey"))
|
||||
args.masterkey = string(readpassword.Once(nil, "", "Masterkey"))
|
||||
masterkeyFromStdin = true
|
||||
}
|
||||
// "-masterkey=941a6029-3adc6a1c-..."
|
||||
|
Loading…
Reference in New Issue
Block a user