diff options
| author | orcas | 2019-09-15 00:32:54 +0800 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2020-02-28 22:17:59 +0100 | 
| commit | 9ec042f2f62bc95154d6c8b3215a2e7853f8f5c6 (patch) | |
| tree | 3a2225151361c7f2f51f57b51d613e29d6ce809b | |
| parent | eeefddad6a254607329c08a4ed6051c068283a06 (diff) | |
Show undecryptable filenames if they match supplied glob
Resolves https://github.com/rfjakob/gocryptfs/issues/393
| -rw-r--r-- | cli_args.go | 5 | ||||
| -rw-r--r-- | internal/nametransform/names.go | 22 | ||||
| -rw-r--r-- | mount.go | 11 | ||||
| -rw-r--r-- | tests/cli/cli_test.go | 41 | 
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 @@ -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) +	} +} | 
