diff --git a/Documentation/MANPAGE.md b/Documentation/MANPAGE.md index 656a400..c559bf1 100644 --- a/Documentation/MANPAGE.md +++ b/Documentation/MANPAGE.md @@ -67,11 +67,16 @@ failing with an IO error. Warning messages are still printed to syslog if corrup files are encountered. It can be useful to recover files from disks with bad sectors or other corrupted media. It shall not be used if the origin of corruption is unknown, specially -if you want to run executable files. For corrupted media, note that you probably want -to use dd_rescue(1) instead. -This option has no effect in reverse mode. It requires gocryptfs to be compiled with openssl +if you want to run executable files. + +For corrupted media, note that you probably want to use dd_rescue(1) +instead, which will recover all but the corrupted 4kB block. + +This option makes no sense in reverse mode. It requires gocryptfs to be compiled with openssl support and implies -openssl true. Because of this, it is not compatible with -aessiv, -that uses built-in Go crpyto. +that uses built-in Go crypto. + +Setting this option forces the filesystem to read-only and noexec. #### -fsname string Override the filesystem name (first column in df -T). Can also be diff --git a/cli_args.go b/cli_args.go index f0bfb48..e42d9ae 100644 --- a/cli_args.go +++ b/cli_args.go @@ -171,11 +171,17 @@ func parseCliOpts() (args argContainer) { tlog.Fatal.Printf("The reverse mode and the -forcedecode option are not compatible") os.Exit(ErrExitUsage) } - v, e := strconv.ParseBool(opensslAuto) - if e == nil && v == false { - tlog.Warn.Printf("-openssl set to true, as it is required by -forcedecode flag") + // Has the user explicitely disabled openssl using "-openssl=false/0"? + if !args.openssl && opensslAuto != "auto" { + tlog.Fatal.Printf("-forcedecode requires openssl, but is disabled via command-line option") + os.Exit(ErrExitUsage) } args.openssl = true + + // Try to make it harder for the user to shoot himself in the foot. + args.ro = true + args.allow_other = false + args.ko = "noexec" } // '-passfile FILE' is a shortcut for -extpass='/bin/cat -- FILE' if args.passfile != "" { diff --git a/internal/contentenc/content.go b/internal/contentenc/content.go index 9998c06..8220d89 100644 --- a/internal/contentenc/content.go +++ b/internal/contentenc/content.go @@ -86,7 +86,9 @@ func (be *ContentEnc) DecryptBlocks(ciphertext []byte, firstBlockNo uint64, file var pBlock []byte pBlock, err = be.DecryptBlock(cBlock, firstBlockNo, fileID) if err != nil { - if be.forceDecode == false || (be.forceDecode == true && stupidgcm.AuthError != err) { + if be.forceDecode && err == stupidgcm.ErrAuth { + tlog.Warn.Printf("DecryptBlocks: authentication failure in block #%d, overriden by forcedecode", firstBlockNo) + } else { break } } @@ -139,11 +141,10 @@ func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileID []b if err != nil { tlog.Warn.Printf("DecryptBlock: %s, len=%d", err.Error(), len(ciphertextOrig)) tlog.Debug.Println(hex.Dump(ciphertextOrig)) - if be.forceDecode == true { + if be.forceDecode && err == stupidgcm.ErrAuth { return plaintext, err - } else { - return nil, err } + return nil, err } return plaintext, nil diff --git a/internal/fusefrontend/file.go b/internal/fusefrontend/file.go index 4d75d64..111775c 100644 --- a/internal/fusefrontend/file.go +++ b/internal/fusefrontend/file.go @@ -18,6 +18,7 @@ import ( "github.com/rfjakob/gocryptfs/internal/contentenc" "github.com/rfjakob/gocryptfs/internal/serialize_reads" + "github.com/rfjakob/gocryptfs/internal/stupidgcm" "github.com/rfjakob/gocryptfs/internal/syscallcompat" "github.com/rfjakob/gocryptfs/internal/tlog" ) @@ -200,9 +201,14 @@ func (f *file) doRead(off uint64, length uint64) ([]byte, fuse.Status) { // Decrypt it plaintext, err := f.contentEnc.DecryptBlocks(ciphertext, firstBlockNo, fileID) if err != nil { - curruptBlockNo := firstBlockNo + f.contentEnc.PlainOffToBlockNo(uint64(len(plaintext))) - tlog.Warn.Printf("ino%d: doRead: corrupt block #%d: %v", f.devIno.ino, curruptBlockNo, err) - if (f.fs.args.ForceDecode == false) { + if f.fs.args.ForceDecode && err == stupidgcm.ErrAuth { + // We do not have the information which block was corrupt here anymore, + // but DecryptBlocks() has already logged it anyway. + tlog.Warn.Printf("ino%d: doRead off=%d len=%d: returning corrupt data due to forcedecode", + f.devIno.ino, off, length) + } else { + curruptBlockNo := firstBlockNo + f.contentEnc.PlainOffToBlockNo(uint64(len(plaintext))) + tlog.Warn.Printf("ino%d: doRead: corrupt block #%d: %v", f.devIno.ino, curruptBlockNo, err) return nil, fuse.EIO } } diff --git a/internal/stupidgcm/autherr.go b/internal/stupidgcm/autherr.go new file mode 100644 index 0000000..e59f92e --- /dev/null +++ b/internal/stupidgcm/autherr.go @@ -0,0 +1,8 @@ +package stupidgcm + +import ( + "fmt" +) + +// ErrAuth is returned when the message authentication fails +var ErrAuth = fmt.Errorf("stupidgcm: message authentication failed") diff --git a/internal/stupidgcm/stupidgcm.go b/internal/stupidgcm/stupidgcm.go index 133ee1a..5cc6c3c 100644 --- a/internal/stupidgcm/stupidgcm.go +++ b/internal/stupidgcm/stupidgcm.go @@ -10,7 +10,6 @@ import "C" import ( "crypto/cipher" - "fmt" "log" "unsafe" ) @@ -30,9 +29,6 @@ type stupidGCM struct { forceDecode bool } -//authentication error -var AuthError error = fmt.Errorf("stupidgcm: message authentication failed") - var _ cipher.AEAD = &stupidGCM{} // New returns a new cipher.AEAD implementation.. @@ -193,10 +189,9 @@ func (g stupidGCM) Open(dst, iv, in, authData []byte) ([]byte, error) { // The error code must always be checked by the calling function, because the decrypted buffer // may contain corrupted data that we are returning in case the user forced reads if g.forceDecode == true { - return append(dst, buf...), AuthError - } else { - return nil, AuthError + return append(dst, buf...), ErrAuth } + return nil, ErrAuth } return append(dst, buf...), nil diff --git a/internal/stupidgcm/without_openssl.go b/internal/stupidgcm/without_openssl.go index 52d8fa0..1c6ebcf 100644 --- a/internal/stupidgcm/without_openssl.go +++ b/internal/stupidgcm/without_openssl.go @@ -14,9 +14,6 @@ const ( BuiltWithoutOpenssl = true ) -//authentication error - needed to compile as same varaible is exported when openssl is enable via stupidgcm.go -var AuthError error = fmt.Errorf("stupidgcm: message authentication failed with openssl disabled!") - func errExit() { fmt.Fprintln(os.Stderr, "gocryptfs has been compiled without openssl support but you are still trying to use openssl") os.Exit(2) diff --git a/mount.go b/mount.go index 6da6492..59116d6 100644 --- a/mount.go +++ b/mount.go @@ -249,6 +249,10 @@ func initFuseFrontend(key []byte, args *argContainer, confFile *configfile.ConfF // Make the kernel check the file permissions for us mOpts.Options = append(mOpts.Options, "default_permissions") } + if args.forcedecode { + tlog.Info.Printf(tlog.ColorYellow + "THE OPTION \"-forcedecode\" IS ACTIVE. GOCRYPTFS WILL RETURN CORRUPT DATA!" + + tlog.ColorReset) + } if args.nonempty { mOpts.Options = append(mOpts.Options, "nonempty") }