209 lines
5.7 KiB
Go
209 lines
5.7 KiB
Go
// gocryptfs is an encrypted overlay filesystem written in Go.
|
|
// See README.md ( https://github.com/rfjakob/gocryptfs/blob/master/README.md )
|
|
// and the official website ( https://nuetzlich.net/gocryptfs/ ) for details.
|
|
package main
|
|
|
|
import (
|
|
"C"
|
|
"os"
|
|
"sync"
|
|
"syscall"
|
|
"path/filepath"
|
|
|
|
"libgocryptfs/v2/internal/configfile"
|
|
"libgocryptfs/v2/internal/contentenc"
|
|
"libgocryptfs/v2/internal/cryptocore"
|
|
"libgocryptfs/v2/internal/nametransform"
|
|
"libgocryptfs/v2/internal/stupidgcm"
|
|
"libgocryptfs/v2/internal/syscallcompat"
|
|
)
|
|
|
|
type File struct {
|
|
fd *os.File
|
|
path string
|
|
ID []byte
|
|
// fdLock prevents the fd to be closed while we are in the middle of
|
|
// an operation.
|
|
// Every FUSE entrypoint should RLock(). The only user of Lock() is
|
|
// Release(), which closes the fd and sets "released" to true.
|
|
fdLock sync.RWMutex
|
|
// ContentLock protects on-disk content from concurrent writes. Every writer
|
|
// must take this lock before modifying the file content.
|
|
contentLock sync.RWMutex
|
|
}
|
|
|
|
type Volume struct {
|
|
volumeID int
|
|
rootCipherDir string
|
|
plainTextNames bool
|
|
// dirIVLock: Lock()ed if any "gocryptfs.diriv" file is modified
|
|
// Readers must RLock() it to prevent them from seeing intermediate
|
|
// states
|
|
dirIVLock sync.RWMutex
|
|
nameTransform *nametransform.NameTransform
|
|
cryptoCore *cryptocore.CryptoCore
|
|
contentEnc *contentenc.ContentEnc
|
|
dirCache dirCache
|
|
handlesLock sync.RWMutex
|
|
fileHandles map[int]*File
|
|
}
|
|
|
|
var OpenedVolumes sync.Map
|
|
|
|
func wipe(d []byte) {
|
|
for i := range d {
|
|
d[i] = 0
|
|
}
|
|
d = nil
|
|
}
|
|
|
|
func errToBool(err error) bool {
|
|
return err == nil
|
|
}
|
|
|
|
func registerNewVolume(rootCipherDir string, masterkey []byte, cf *configfile.ConfFile) int {
|
|
var newVolume Volume
|
|
|
|
newVolume.plainTextNames = cf.IsFeatureFlagSet(configfile.FlagPlaintextNames)
|
|
|
|
cryptoBackend, err := cf.ContentEncryption()
|
|
if err != nil {
|
|
return -1
|
|
}
|
|
if cryptoBackend == cryptocore.BackendXChaCha20Poly1305 && stupidgcm.PreferOpenSSLXchacha20poly1305() {
|
|
cryptoBackend = cryptocore.BackendXChaCha20Poly1305OpenSSL
|
|
} else if cryptoBackend == cryptocore.BackendGoGCM && stupidgcm.PreferOpenSSLAES256GCM() {
|
|
cryptoBackend = cryptocore.BackendOpenSSL
|
|
}
|
|
newVolume.cryptoCore = cryptocore.New(masterkey, cryptoBackend, cryptoBackend.NonceSize*8, cf.IsFeatureFlagSet(configfile.FlagHKDF))
|
|
newVolume.contentEnc = contentenc.New(newVolume.cryptoCore, contentenc.DefaultBS)
|
|
var badname []string
|
|
newVolume.nameTransform = nametransform.New(
|
|
newVolume.cryptoCore.EMECipher,
|
|
true,
|
|
cf.IsFeatureFlagSet(configfile.FlagRaw64),
|
|
badname,
|
|
!cf.IsFeatureFlagSet(configfile.FlagDirIV),
|
|
)
|
|
|
|
//copying rootCipherDir
|
|
newVolume.rootCipherDir = string([]byte(rootCipherDir[:]))
|
|
|
|
ivLen := nametransform.DirIVLen
|
|
if newVolume.plainTextNames {
|
|
ivLen = 0
|
|
}
|
|
newVolume.dirCache = dirCache{ivLen: ivLen}
|
|
newVolume.fileHandles = make(map[int]*File)
|
|
|
|
//find unused volumeID
|
|
volumeID := -1
|
|
c := 0
|
|
for volumeID == -1 {
|
|
_, ok := OpenedVolumes.Load(c)
|
|
if !ok {
|
|
volumeID = c
|
|
}
|
|
c++
|
|
}
|
|
OpenedVolumes.Store(volumeID, &newVolume)
|
|
return volumeID
|
|
}
|
|
|
|
//export gcf_init
|
|
func gcf_init(rootCipherDir string, password, givenScryptHash, returnedScryptHashBuff []byte) int {
|
|
volumeID := -1
|
|
cf, err := configfile.Load(filepath.Join(rootCipherDir, configfile.ConfDefaultName))
|
|
if err == nil {
|
|
masterkey := cf.GetMasterkey(password, givenScryptHash, returnedScryptHashBuff)
|
|
if masterkey != nil {
|
|
volumeID = registerNewVolume(rootCipherDir, masterkey, cf)
|
|
wipe(masterkey)
|
|
}
|
|
}
|
|
return volumeID
|
|
}
|
|
|
|
//export gcf_close
|
|
func gcf_close(volumeID int) {
|
|
value, ok := OpenedVolumes.Load(volumeID)
|
|
if !ok {
|
|
return
|
|
}
|
|
volume := value.(*Volume)
|
|
volume.cryptoCore.Wipe()
|
|
volume.handlesLock.RLock()
|
|
fileHandles := make([]int, 0, len(volume.fileHandles))
|
|
for i := range volume.fileHandles {
|
|
fileHandles = append(fileHandles, i)
|
|
}
|
|
volume.handlesLock.RUnlock()
|
|
for i := range fileHandles {
|
|
gcf_close_file(volumeID, i)
|
|
}
|
|
volume.dirCache.Clear()
|
|
OpenedVolumes.Delete(volumeID)
|
|
}
|
|
|
|
//export gcf_is_closed
|
|
func gcf_is_closed(volumeID int) bool {
|
|
_, ok := OpenedVolumes.Load(volumeID)
|
|
return !ok
|
|
}
|
|
|
|
//export gcf_change_password
|
|
func gcf_change_password(rootCipherDir string, oldPassword, givenScryptHash, new_password, returnedScryptHashBuff []byte) bool {
|
|
success := false
|
|
cf, err := configfile.Load(filepath.Join(rootCipherDir, configfile.ConfDefaultName))
|
|
if err == nil {
|
|
masterkey := cf.GetMasterkey(oldPassword, givenScryptHash, nil)
|
|
if masterkey != nil {
|
|
logN := cf.ScryptObject.LogN()
|
|
scryptHash := cf.EncryptKey(masterkey, new_password, logN, len(returnedScryptHashBuff) > 0)
|
|
wipe(masterkey)
|
|
for i := range scryptHash {
|
|
returnedScryptHashBuff[i] = scryptHash[i]
|
|
scryptHash[i] = 0
|
|
}
|
|
success = errToBool(cf.WriteFile())
|
|
}
|
|
}
|
|
return success
|
|
}
|
|
|
|
//export gcf_create_volume
|
|
func gcf_create_volume(rootCipherDir string, password []byte, plaintextNames bool, xchacha int8, logN int, creator string, returnedScryptHashBuff []byte) bool {
|
|
var useXChaCha bool
|
|
switch xchacha {
|
|
case 1:
|
|
useXChaCha = true
|
|
case 0:
|
|
useXChaCha = false
|
|
default:
|
|
useXChaCha = !stupidgcm.CpuHasAES()
|
|
}
|
|
err := configfile.Create(&configfile.CreateArgs{
|
|
Filename: filepath.Join(rootCipherDir, configfile.ConfDefaultName),
|
|
Password: password,
|
|
PlaintextNames: plaintextNames,
|
|
LogN: logN,
|
|
Creator: creator,
|
|
AESSIV: false,
|
|
DeterministicNames: false,
|
|
XChaCha20Poly1305: useXChaCha,
|
|
}, returnedScryptHashBuff)
|
|
if err == nil {
|
|
if plaintextNames {
|
|
return true
|
|
} else {
|
|
dirfd, err := syscall.Open(rootCipherDir, syscall.O_DIRECTORY|syscallcompat.O_PATH, 0)
|
|
if err == nil {
|
|
err = nametransform.WriteDirIVAt(dirfd)
|
|
syscall.Close(dirfd)
|
|
return errToBool(err)
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|