forcedecode: tighten checks

...and fix a few golint issues and print a scary warning message on mount.

Also, force the fs to ro,noexec.
This commit is contained in:
Jakob Unterwurzacher 2017-04-24 00:25:02 +02:00
parent f1945c4daa
commit 3409ade272
8 changed files with 46 additions and 24 deletions

View File

@ -67,11 +67,16 @@ failing with an IO error. Warning messages are still printed to syslog if corrup
files are encountered. files are encountered.
It can be useful to recover files from disks with bad sectors or other corrupted 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 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 if you want to run executable files.
to use dd_rescue(1) instead.
This option has no effect in reverse mode. It requires gocryptfs to be compiled with openssl 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, 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 #### -fsname string
Override the filesystem name (first column in df -T). Can also be Override the filesystem name (first column in df -T). Can also be

View File

@ -171,11 +171,17 @@ func parseCliOpts() (args argContainer) {
tlog.Fatal.Printf("The reverse mode and the -forcedecode option are not compatible") tlog.Fatal.Printf("The reverse mode and the -forcedecode option are not compatible")
os.Exit(ErrExitUsage) os.Exit(ErrExitUsage)
} }
v, e := strconv.ParseBool(opensslAuto) // Has the user explicitely disabled openssl using "-openssl=false/0"?
if e == nil && v == false { if !args.openssl && opensslAuto != "auto" {
tlog.Warn.Printf("-openssl set to true, as it is required by -forcedecode flag") tlog.Fatal.Printf("-forcedecode requires openssl, but is disabled via command-line option")
os.Exit(ErrExitUsage)
} }
args.openssl = true 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' // '-passfile FILE' is a shortcut for -extpass='/bin/cat -- FILE'
if args.passfile != "" { if args.passfile != "" {

View File

@ -86,7 +86,9 @@ func (be *ContentEnc) DecryptBlocks(ciphertext []byte, firstBlockNo uint64, file
var pBlock []byte var pBlock []byte
pBlock, err = be.DecryptBlock(cBlock, firstBlockNo, fileID) pBlock, err = be.DecryptBlock(cBlock, firstBlockNo, fileID)
if err != nil { 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 break
} }
} }
@ -139,11 +141,10 @@ func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileID []b
if err != nil { if err != nil {
tlog.Warn.Printf("DecryptBlock: %s, len=%d", err.Error(), len(ciphertextOrig)) tlog.Warn.Printf("DecryptBlock: %s, len=%d", err.Error(), len(ciphertextOrig))
tlog.Debug.Println(hex.Dump(ciphertextOrig)) tlog.Debug.Println(hex.Dump(ciphertextOrig))
if be.forceDecode == true { if be.forceDecode && err == stupidgcm.ErrAuth {
return plaintext, err return plaintext, err
} else {
return nil, err
} }
return nil, err
} }
return plaintext, nil return plaintext, nil

View File

@ -18,6 +18,7 @@ import (
"github.com/rfjakob/gocryptfs/internal/contentenc" "github.com/rfjakob/gocryptfs/internal/contentenc"
"github.com/rfjakob/gocryptfs/internal/serialize_reads" "github.com/rfjakob/gocryptfs/internal/serialize_reads"
"github.com/rfjakob/gocryptfs/internal/stupidgcm"
"github.com/rfjakob/gocryptfs/internal/syscallcompat" "github.com/rfjakob/gocryptfs/internal/syscallcompat"
"github.com/rfjakob/gocryptfs/internal/tlog" "github.com/rfjakob/gocryptfs/internal/tlog"
) )
@ -200,9 +201,14 @@ func (f *file) doRead(off uint64, length uint64) ([]byte, fuse.Status) {
// Decrypt it // Decrypt it
plaintext, err := f.contentEnc.DecryptBlocks(ciphertext, firstBlockNo, fileID) plaintext, err := f.contentEnc.DecryptBlocks(ciphertext, firstBlockNo, fileID)
if err != nil { if err != nil {
curruptBlockNo := firstBlockNo + f.contentEnc.PlainOffToBlockNo(uint64(len(plaintext))) if f.fs.args.ForceDecode && err == stupidgcm.ErrAuth {
tlog.Warn.Printf("ino%d: doRead: corrupt block #%d: %v", f.devIno.ino, curruptBlockNo, err) // We do not have the information which block was corrupt here anymore,
if (f.fs.args.ForceDecode == false) { // 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 return nil, fuse.EIO
} }
} }

View File

@ -0,0 +1,8 @@
package stupidgcm
import (
"fmt"
)
// ErrAuth is returned when the message authentication fails
var ErrAuth = fmt.Errorf("stupidgcm: message authentication failed")

View File

@ -10,7 +10,6 @@ import "C"
import ( import (
"crypto/cipher" "crypto/cipher"
"fmt"
"log" "log"
"unsafe" "unsafe"
) )
@ -30,9 +29,6 @@ type stupidGCM struct {
forceDecode bool forceDecode bool
} }
//authentication error
var AuthError error = fmt.Errorf("stupidgcm: message authentication failed")
var _ cipher.AEAD = &stupidGCM{} var _ cipher.AEAD = &stupidGCM{}
// New returns a new cipher.AEAD implementation.. // 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 // 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 // may contain corrupted data that we are returning in case the user forced reads
if g.forceDecode == true { if g.forceDecode == true {
return append(dst, buf...), AuthError return append(dst, buf...), ErrAuth
} else {
return nil, AuthError
} }
return nil, ErrAuth
} }
return append(dst, buf...), nil return append(dst, buf...), nil

View File

@ -14,9 +14,6 @@ const (
BuiltWithoutOpenssl = true 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() { func errExit() {
fmt.Fprintln(os.Stderr, "gocryptfs has been compiled without openssl support but you are still trying to use openssl") fmt.Fprintln(os.Stderr, "gocryptfs has been compiled without openssl support but you are still trying to use openssl")
os.Exit(2) os.Exit(2)

View File

@ -249,6 +249,10 @@ func initFuseFrontend(key []byte, args *argContainer, confFile *configfile.ConfF
// Make the kernel check the file permissions for us // Make the kernel check the file permissions for us
mOpts.Options = append(mOpts.Options, "default_permissions") 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 { if args.nonempty {
mOpts.Options = append(mOpts.Options, "nonempty") mOpts.Options = append(mOpts.Options, "nonempty")
} }