Show undecryptable filenames if they match supplied glob
Resolves https://github.com/rfjakob/gocryptfs/issues/393
This commit is contained in:
parent
eeefddad6a
commit
9ec042f2f6
@ -32,8 +32,8 @@ type argContainer struct {
|
|||||||
dev, nodev, suid, nosuid, exec, noexec, rw, ro bool
|
dev, nodev, suid, nosuid, exec, noexec, rw, ro bool
|
||||||
masterkey, mountpoint, cipherdir, cpuprofile,
|
masterkey, mountpoint, cipherdir, cpuprofile,
|
||||||
memprofile, ko, passfile, ctlsock, fsname, force_owner, trace string
|
memprofile, ko, passfile, ctlsock, fsname, force_owner, trace string
|
||||||
// -extpass can be passed multiple times
|
// -extpass and -badname can be passed multiple times
|
||||||
extpass multipleStrings
|
extpass, badname multipleStrings
|
||||||
// For reverse mode, several ways to specify exclusions. All can be specified multiple times.
|
// For reverse mode, several ways to specify exclusions. All can be specified multiple times.
|
||||||
exclude, excludeWildcard, excludeFrom multipleStrings
|
exclude, excludeWildcard, excludeFrom multipleStrings
|
||||||
// Configuration file name override
|
// Configuration file name override
|
||||||
@ -199,6 +199,7 @@ func parseCliOpts() (args argContainer) {
|
|||||||
|
|
||||||
// -extpass
|
// -extpass
|
||||||
flagSet.Var(&args.extpass, "extpass", "Use external program for the password prompt")
|
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 "+
|
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")
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/rfjakob/eme"
|
"github.com/rfjakob/eme"
|
||||||
@ -35,6 +36,8 @@ type NameTransform struct {
|
|||||||
// B64 = either base64.URLEncoding or base64.RawURLEncoding, depending
|
// B64 = either base64.URLEncoding or base64.RawURLEncoding, depending
|
||||||
// on the Raw64 feature flag
|
// on the Raw64 feature flag
|
||||||
B64 *base64.Encoding
|
B64 *base64.Encoding
|
||||||
|
// Patterns to bypass decryption
|
||||||
|
BadnamePatterns []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new NameTransform instance.
|
// 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
|
// DecryptName calls decryptName to try and decrypt a base64-encoded encrypted
|
||||||
// initialization vector "iv".
|
// filename "cipherName", and failing that checks if it can be bypassed
|
||||||
func (n *NameTransform) DecryptName(cipherName string, iv []byte) (string, error) {
|
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)
|
bin, err := n.B64.DecodeString(cipherName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
11
mount.go
11
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)
|
cCore := cryptocore.New(masterkey, cryptoBackend, contentenc.DefaultIVBits, args.hkdf, args.forcedecode)
|
||||||
cEnc := contentenc.New(cCore, contentenc.DefaultBS, args.forcedecode)
|
cEnc := contentenc.New(cCore, contentenc.DefaultBS, args.forcedecode)
|
||||||
nameTransform := nametransform.New(cCore.EMECipher, frontendArgs.LongNames, args.raw64)
|
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,
|
// After the crypto backend is initialized,
|
||||||
// we can purge the master key from memory.
|
// we can purge the master key from memory.
|
||||||
for i := range masterkey {
|
for i := range masterkey {
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -644,3 +645,43 @@ func TestSymlinkedCipherdir(t *testing.T) {
|
|||||||
t.Errorf("wrong Readdirnames result: %v", names)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user