aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authororcas2019-09-15 00:32:54 +0800
committerJakob Unterwurzacher2020-02-28 22:17:59 +0100
commit9ec042f2f62bc95154d6c8b3215a2e7853f8f5c6 (patch)
tree3a2225151361c7f2f51f57b51d613e29d6ce809b
parenteeefddad6a254607329c08a4ed6051c068283a06 (diff)
Show undecryptable filenames if they match supplied glob
Resolves https://github.com/rfjakob/gocryptfs/issues/393
-rw-r--r--cli_args.go5
-rw-r--r--internal/nametransform/names.go22
-rw-r--r--mount.go11
-rw-r--r--tests/cli/cli_test.go41
4 files changed, 75 insertions, 4 deletions
diff --git a/cli_args.go b/cli_args.go
index 0462fb4..a036a2a 100644
--- a/cli_args.go
+++ b/cli_args.go
@@ -32,8 +32,8 @@ type argContainer struct {
dev, nodev, suid, nosuid, exec, noexec, rw, ro bool
masterkey, mountpoint, cipherdir, cpuprofile,
memprofile, ko, passfile, ctlsock, fsname, force_owner, trace string
- // -extpass can be passed multiple times
- extpass multipleStrings
+ // -extpass and -badname can be passed multiple times
+ extpass, badname multipleStrings
// For reverse mode, several ways to specify exclusions. All can be specified multiple times.
exclude, excludeWildcard, excludeFrom multipleStrings
// Configuration file name override
@@ -199,6 +199,7 @@ func parseCliOpts() (args argContainer) {
// -extpass
flagSet.Var(&args.extpass, "extpass", "Use external program for the password prompt")
+ flagSet.Var(&args.badname, "badname", "Glob pattern invalid file names that should be shown")
flagSet.IntVar(&args.notifypid, "notifypid", 0, "Send USR1 to the specified process after "+
"successful mount - used internally for daemonization")
diff --git a/internal/nametransform/names.go b/internal/nametransform/names.go
index d5c2c8b..de70bce 100644
--- a/internal/nametransform/names.go
+++ b/internal/nametransform/names.go
@@ -5,6 +5,7 @@ import (
"bytes"
"crypto/aes"
"encoding/base64"
+ "path/filepath"
"syscall"
"github.com/rfjakob/eme"
@@ -35,6 +36,8 @@ type NameTransform struct {
// B64 = either base64.URLEncoding or base64.RawURLEncoding, depending
// on the Raw64 feature flag
B64 *base64.Encoding
+ // Patterns to bypass decryption
+ BadnamePatterns []string
}
// New returns a new NameTransform instance.
@@ -50,9 +53,24 @@ func New(e *eme.EMECipher, longNames bool, raw64 bool) *NameTransform {
}
}
-// DecryptName decrypts a base64-encoded encrypted filename "cipherName" using the
-// initialization vector "iv".
+// DecryptName calls decryptName to try and decrypt a base64-encoded encrypted
+// filename "cipherName", and failing that checks if it can be bypassed
func (n *NameTransform) DecryptName(cipherName string, iv []byte) (string, error) {
+ res, err := n.decryptName(cipherName, iv)
+ if err != nil {
+ for _, pattern := range n.BadnamePatterns {
+ match, err := filepath.Match(pattern, cipherName)
+ if err == nil && match { // Pattern should have been validated already
+ return "GOCRYPTFS_BAD_NAME " + cipherName, nil
+ }
+ }
+ }
+ return res, err
+}
+
+// decryptName decrypts a base64-encoded encrypted filename "cipherName" using the
+// initialization vector "iv".
+func (n *NameTransform) decryptName(cipherName string, iv []byte) (string, error) {
bin, err := n.B64.DecodeString(cipherName)
if err != nil {
return "", err
diff --git a/mount.go b/mount.go
index 5107bee..6d9dd8f 100644
--- a/mount.go
+++ b/mount.go
@@ -286,6 +286,17 @@ func initFuseFrontend(args *argContainer) (pfs pathfs.FileSystem, wipeKeys func(
cCore := cryptocore.New(masterkey, cryptoBackend, contentenc.DefaultIVBits, args.hkdf, args.forcedecode)
cEnc := contentenc.New(cCore, contentenc.DefaultBS, args.forcedecode)
nameTransform := nametransform.New(cCore.EMECipher, frontendArgs.LongNames, args.raw64)
+ // Init badname patterns
+ nameTransform.BadnamePatterns = make([]string, 0)
+ for _, pattern := range args.badname {
+ _, err := filepath.Match(pattern, "") // Make sure pattern is valid
+ if err != nil {
+ tlog.Fatal.Printf("-badname: invalid pattern %q supplied", pattern)
+ os.Exit(exitcodes.Usage)
+ } else {
+ nameTransform.BadnamePatterns = append(nameTransform.BadnamePatterns, pattern)
+ }
+ }
// After the crypto backend is initialized,
// we can purge the master key from memory.
for i := range masterkey {
diff --git a/tests/cli/cli_test.go b/tests/cli/cli_test.go
index 3e1bf42..232c8cc 100644
--- a/tests/cli/cli_test.go
+++ b/tests/cli/cli_test.go
@@ -8,6 +8,7 @@ import (
"os"
"os/exec"
"strconv"
+ "strings"
"syscall"
"testing"
"time"
@@ -644,3 +645,43 @@ func TestSymlinkedCipherdir(t *testing.T) {
t.Errorf("wrong Readdirnames result: %v", names)
}
}
+
+func TestBypass(t *testing.T) {
+ dir := test_helpers.InitFS(t)
+ mnt := dir + ".mnt"
+
+ test_helpers.MountOrFatal(t, dir, mnt, "-badname=*", "-extpass=echo test")
+ defer test_helpers.UnmountPanic(mnt)
+
+ file := mnt + "/file"
+ err := ioutil.WriteFile(file, []byte("somecontent"), 0600)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ invalid_file_name := "invalid_file"
+ invalid_file := dir + "/" + invalid_file_name
+ err = ioutil.WriteFile(invalid_file, []byte("somecontent"), 0600)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ f, err := os.Open(mnt)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+
+ names, err := f.Readdirnames(0)
+ found := false
+ for _, name := range names {
+ if strings.Contains(name, invalid_file_name) {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ t.Errorf("did not find invalid name %s in %v", invalid_file_name, names)
+ }
+}