aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Unterwurzacher2018-03-22 00:02:10 +0100
committerJakob Unterwurzacher2018-03-22 00:02:10 +0100
commit9bc039a4bac6b51d9ebe116de5c311e90343a088 (patch)
treef4ebd37e2cbe4088435421fb7c9550c186161b09
parent9c86daf499dca8a69b058ec56803d06fbba4fdab (diff)
Add `-masterkey=stdin` functionality
https://github.com/rfjakob/gocryptfs/issues/218
-rw-r--r--Documentation/MANPAGE.md16
-rw-r--r--README.md4
-rw-r--r--gocryptfs-xray/xray_main.go2
-rw-r--r--internal/readpassword/extpass_test.go4
-rw-r--r--internal/readpassword/read.go19
-rw-r--r--internal/readpassword/stdin_test.go6
-rw-r--r--main.go4
-rw-r--r--masterkey.go10
-rw-r--r--mount.go9
-rw-r--r--tests/example_filesystems/example_filesystems_test.go33
10 files changed, 79 insertions, 28 deletions
diff --git a/Documentation/MANPAGE.md b/Documentation/MANPAGE.md
index b9b000a..e20b786 100644
--- a/Documentation/MANPAGE.md
+++ b/Documentation/MANPAGE.md
@@ -142,20 +142,24 @@ This flag is useful when recovering old gocryptfs filesystems using
"-masterkey". It is ignored (stays at the default) otherwise.
#### -masterkey string
-Use a explicit master key specified on the command line. This
+Use a explicit master key specified on the command line or, if the special
+value "stdin" is used, read the masterkey from stdin. This
option can be used to mount a gocryptfs filesystem without a config file.
Note that the command line, and with it the master key, is visible to
-anybody on the machine who can execute "ps -auxwww".
-This is meant as a recovery option for emergencies, such as if you have
-forgotten the password or lost the config file.
+anybody on the machine who can execute "ps -auxwww". Use "-masterkey=stdin"
+to avoid that risk.
+
+The masterkey option is meant as a recovery option for emergencies, such as
+if you have forgotten the password or lost the config file.
Even if a config file exists, it will not be used. All non-standard
settings have to be passed on the command line: `-aessiv` when you
mount a filesystem that was created using reverse mode, or
`-plaintextnames` for a filesystem that was created with that option.
-Example master key:
-6f717d8b-6b5f8e8a-fd0aa206-778ec093-62c5669b-abd229cd-241e00cd-b4d6713d
+Examples:
+-masterkey=6f717d8b-6b5f8e8a-fd0aa206-778ec093-62c5669b-abd229cd-241e00cd-b4d6713d
+-masterkey=stdin
#### -memprofile string
Write memory profile to the specified file. This is useful when debugging
diff --git a/README.md b/README.md
index 7069fc1..ad86894 100644
--- a/README.md
+++ b/README.md
@@ -153,6 +153,10 @@ RM: 4.42
Changelog
---------
+vNEXT, in progress
+* Add `-masterkey=stdin` functionality
+ ([#218](https://github.com/rfjakob/gocryptfs/issues/218))
+
v1.4.4, 2018-03-18
* Overwrite secrets in memory with zeros as soon as possible
([#211](https://github.com/rfjakob/gocryptfs/issues/211))
diff --git a/gocryptfs-xray/xray_main.go b/gocryptfs-xray/xray_main.go
index 522878a..1531eb4 100644
--- a/gocryptfs-xray/xray_main.go
+++ b/gocryptfs-xray/xray_main.go
@@ -60,7 +60,7 @@ func main() {
func dumpMasterKey(fn string) {
tlog.Info.Enabled = false
- pw := readpassword.Once("")
+ pw := readpassword.Once("", "")
masterkey, _, err := configfile.LoadConfFile(fn, pw)
if err != nil {
fmt.Fprintln(os.Stderr, err)
diff --git a/internal/readpassword/extpass_test.go b/internal/readpassword/extpass_test.go
index cdfea4e..b35153f 100644
--- a/internal/readpassword/extpass_test.go
+++ b/internal/readpassword/extpass_test.go
@@ -26,7 +26,7 @@ func TestExtpass(t *testing.T) {
func TestOnceExtpass(t *testing.T) {
p1 := "lkadsf0923rdfi48rqwhdsf"
- p2 := string(Once("echo " + p1))
+ p2 := string(Once("echo "+p1, ""))
if p1 != p2 {
t.Errorf("p1=%q != p2=%q", p1, p2)
}
@@ -34,7 +34,7 @@ func TestOnceExtpass(t *testing.T) {
func TestTwiceExtpass(t *testing.T) {
p1 := "w5w44t3wfe45srz434"
- p2 := string(Once("echo " + p1))
+ p2 := string(Once("echo "+p1, ""))
if p1 != p2 {
t.Errorf("p1=%q != p2=%q", p1, p2)
}
diff --git a/internal/readpassword/read.go b/internal/readpassword/read.go
index e2fce8a..c99be5d 100644
--- a/internal/readpassword/read.go
+++ b/internal/readpassword/read.go
@@ -23,15 +23,18 @@ const (
)
// Once tries to get a password from the user, either from the terminal, extpass
-// or stdin.
-func Once(extpass string) []byte {
+// or stdin. Leave "prompt" empty to use the default "Password: " prompt.
+func Once(extpass string, prompt string) []byte {
if extpass != "" {
return readPasswordExtpass(extpass)
}
+ if prompt == "" {
+ prompt = "Password"
+ }
if !terminal.IsTerminal(int(os.Stdin.Fd())) {
- return readPasswordStdin()
+ return readPasswordStdin(prompt)
}
- return readPasswordTerminal("Password: ")
+ return readPasswordTerminal(prompt + ": ")
}
// Twice is the same as Once but will prompt twice if we get the password from
@@ -41,7 +44,7 @@ func Twice(extpass string) []byte {
return readPasswordExtpass(extpass)
}
if !terminal.IsTerminal(int(os.Stdin.Fd())) {
- return readPasswordStdin()
+ return readPasswordStdin("Password")
}
p1 := readPasswordTerminal("Password: ")
p2 := readPasswordTerminal("Repeat: ")
@@ -77,11 +80,11 @@ func readPasswordTerminal(prompt string) []byte {
// readPasswordStdin reads a line from stdin.
// It exits with a fatal error on read error or empty result.
-func readPasswordStdin() []byte {
- tlog.Info.Println("Reading password from stdin")
+func readPasswordStdin(prompt string) []byte {
+ tlog.Info.Printf("Reading %s from stdin", prompt)
p := readLineUnbuffered(os.Stdin)
if len(p) == 0 {
- tlog.Fatal.Println("Got empty password from stdin")
+ tlog.Fatal.Printf("Got empty %s from stdin", prompt)
os.Exit(exitcodes.ReadPassword)
}
return p
diff --git a/internal/readpassword/stdin_test.go b/internal/readpassword/stdin_test.go
index 8cf9954..01dd701 100644
--- a/internal/readpassword/stdin_test.go
+++ b/internal/readpassword/stdin_test.go
@@ -11,7 +11,7 @@ import (
func TestStdin(t *testing.T) {
p1 := "g55434t55wef"
if os.Getenv("TEST_SLAVE") == "1" {
- p2 := string(readPasswordStdin())
+ p2 := string(readPasswordStdin("foo"))
if p1 != p2 {
fmt.Fprintf(os.Stderr, "%q != %q", p1, p2)
os.Exit(1)
@@ -44,7 +44,7 @@ func TestStdin(t *testing.T) {
func TestStdinEof(t *testing.T) {
p1 := "asd45as5f4a36"
if os.Getenv("TEST_SLAVE") == "1" {
- p2 := string(readPasswordStdin())
+ p2 := string(readPasswordStdin("foo"))
if p1 != p2 {
fmt.Fprintf(os.Stderr, "%q != %q", p1, p2)
os.Exit(1)
@@ -76,7 +76,7 @@ func TestStdinEof(t *testing.T) {
// Provide empty password via stdin
func TestStdinEmpty(t *testing.T) {
if os.Getenv("TEST_SLAVE") == "1" {
- readPasswordStdin()
+ readPasswordStdin("foo")
}
cmd := exec.Command(os.Args[0], "-test.run=TestStdinEmpty$")
cmd.Env = append(os.Environ(), "TEST_SLAVE=1")
diff --git a/main.go b/main.go
index 1e1de11..8f857bc 100644
--- a/main.go
+++ b/main.go
@@ -43,10 +43,10 @@ func loadConfig(args *argContainer) (masterkey []byte, confFile *configfile.Conf
// The user has passed the master key (probably because he forgot the
// password).
if args.masterkey != "" {
- masterkey = parseMasterKey(args.masterkey)
+ masterkey = parseMasterKey(args.masterkey, false)
_, confFile, err = configfile.LoadConfFile(args.config, nil)
} else {
- pw := readpassword.Once(args.extpass)
+ pw := readpassword.Once(args.extpass, "")
tlog.Info.Println("Decrypting master key")
masterkey, confFile, err = configfile.LoadConfFile(args.config, pw)
for i := range pw {
diff --git a/masterkey.go b/masterkey.go
index 3d7afd1..6fda035 100644
--- a/masterkey.go
+++ b/masterkey.go
@@ -46,7 +46,7 @@ paper and store it in a drawer. Use "-q" to suppress this message.
// parseMasterKey - Parse a hex-encoded master key that was passed on the command line
// Calls os.Exit on failure
-func parseMasterKey(masterkey string) []byte {
+func parseMasterKey(masterkey string, fromStdin bool) []byte {
masterkey = strings.Replace(masterkey, "-", "", -1)
key, err := hex.DecodeString(masterkey)
if err != nil {
@@ -58,8 +58,10 @@ func parseMasterKey(masterkey string) []byte {
os.Exit(exitcodes.MasterKey)
}
tlog.Info.Printf("Using explicit master key.")
- tlog.Info.Printf(tlog.ColorYellow +
- "THE MASTER KEY IS VISIBLE VIA \"ps ax\" AND MAY BE STORED IN YOUR SHELL HISTORY!\n" +
- "ONLY USE THIS MODE FOR EMERGENCIES." + tlog.ColorReset)
+ if !fromStdin {
+ tlog.Info.Printf(tlog.ColorYellow +
+ "THE MASTER KEY IS VISIBLE VIA \"ps ax\" AND MAY BE STORED IN YOUR SHELL HISTORY!\n" +
+ "ONLY USE THIS MODE FOR EMERGENCIES" + tlog.ColorReset)
+ }
return key
}
diff --git a/mount.go b/mount.go
index 21a70af..96ff7f6 100644
--- a/mount.go
+++ b/mount.go
@@ -98,9 +98,14 @@ func doMount(args *argContainer) {
{
// Get master key (may prompt for the password)
var masterkey []byte
+ masterkeyFromStdin := false
+ if args.masterkey == "stdin" {
+ args.masterkey = string(readpassword.Once("", "Masterkey"))
+ masterkeyFromStdin = true
+ }
if args.masterkey != "" {
// "-masterkey"
- masterkey = parseMasterKey(args.masterkey)
+ masterkey = parseMasterKey(args.masterkey, masterkeyFromStdin)
} else if args.zerokey {
// "-zerokey"
tlog.Info.Printf("Using all-zero dummy master key.")
@@ -354,7 +359,7 @@ func initFuseFrontend(masterkey []byte, args *argContainer, confFile *configfile
}
srv, err := fuse.NewServer(conn.RawFS(), args.mountpoint, &mOpts)
if err != nil {
- tlog.Fatal.Printf("fuse.NewServer failed: %v", err)
+ tlog.Fatal.Printf("fuse.NewServer failed: %q", err)
if runtime.GOOS == "darwin" {
tlog.Info.Printf("Maybe you should run: /Library/Filesystems/osxfuse.fs/Contents/Resources/load_osxfuse")
}
diff --git a/tests/example_filesystems/example_filesystems_test.go b/tests/example_filesystems/example_filesystems_test.go
index 5979bdc..4f29f8d 100644
--- a/tests/example_filesystems/example_filesystems_test.go
+++ b/tests/example_filesystems/example_filesystems_test.go
@@ -10,6 +10,7 @@ import (
"flag"
"fmt"
"os"
+ "os/exec"
"syscall"
"testing"
@@ -252,6 +253,38 @@ func TestExampleFSv13(t *testing.T) {
test_helpers.UnmountPanic(pDir)
}
+// Check that the masterkey=stdin cli option works.
+func TestExampleFSv13MasterkeyStdin(t *testing.T) {
+ cDir := "v1.3"
+ pDir := test_helpers.TmpDir + "/TestExampleFSv13MasterkeyStdin.mnt"
+ err := os.Mkdir(pDir, 0777)
+ if err != nil {
+ t.Fatal(err)
+ }
+ args := []string{"-q", "-masterkey=stdin", opensslOpt, cDir, pDir}
+ 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)
+ }
+ // Write masterkey to stdin
+ p.Write([]byte("fd890dab-86bf61cf-ec5ad460-ad3ed01f-9c52d546-2a31783d-a56b088d-3d05232e"))
+ p.Close()
+ err = cmd.Wait()
+ if err != nil {
+ t.Error(err)
+ }
+ // Check that the fs decrypts ok & unmount
+ checkExampleFSLongnames(t, pDir)
+ test_helpers.UnmountPanic(pDir)
+}
+
// gocryptfs v1.3 introduced HKDF.
// We check the md5 sum of the encrypted version of a file to make sure we don't
// accidentially change the ciphertext generation.