ctlsock: implement EncryptPath for reverse mode, add tests

This commit is contained in:
Jakob Unterwurzacher 2016-11-10 23:32:51 +01:00
parent d3764b7753
commit c03fc46a51
13 changed files with 139 additions and 33 deletions

View File

@ -14,6 +14,6 @@ func (fs *FS) EncryptPath(plainPath string) (string, error) {
} }
// DecryptPath implements ctlsock.Backend // DecryptPath implements ctlsock.Backend
func (fs *FS) DecryptPath(plainPath string) (string, error) { func (fs *FS) DecryptPath(cipherPath string) (string, error) {
return "", errors.New("Not implemented") return "", errors.New("not implemented (yet?)")
} }

View File

@ -1,19 +1,39 @@
package fusefrontend_reverse package fusefrontend_reverse
import ( import (
"errors" "path/filepath"
"strings"
"syscall"
"github.com/rfjakob/gocryptfs/internal/ctlsock" "github.com/rfjakob/gocryptfs/internal/ctlsock"
"github.com/rfjakob/gocryptfs/internal/nametransform"
) )
var _ ctlsock.Interface = &ReverseFS{} // Verify that interface is implemented. var _ ctlsock.Interface = &ReverseFS{} // Verify that interface is implemented.
// EncryptPath implements ctlsock.Backend // EncryptPath implements ctlsock.Backend.
// This is actually not used inside reverse mode, but we implement it because
// third-party tools want to encrypt paths through the control socket.
func (rfs *ReverseFS) EncryptPath(plainPath string) (string, error) { func (rfs *ReverseFS) EncryptPath(plainPath string) (string, error) {
return "", errors.New("Not implemented") if rfs.args.PlaintextNames || plainPath == "" {
return plainPath, nil
}
cipherPath := ""
parts := strings.Split(plainPath, "/")
for _, part := range parts {
dirIV := derivePathIV(cipherPath, ivPurposeDirIV)
encryptedPart := rfs.nameTransform.EncryptName(part, dirIV)
if rfs.args.LongNames && len(encryptedPart) > syscall.NAME_MAX {
encryptedPart = nametransform.HashLongName(encryptedPart)
}
cipherPath = filepath.Join(cipherPath, encryptedPart)
}
return cipherPath, nil
} }
// DecryptPath implements ctlsock.Backend // DecryptPath implements ctlsock.Backend
func (rfs *ReverseFS) DecryptPath(plainPath string) (string, error) { func (rfs *ReverseFS) DecryptPath(cipherPath string) (string, error) {
return rfs.decryptPath(plainPath) p, err := rfs.decryptPath(cipherPath)
//fmt.Printf("rfs DecryptPath: %q -> %q %v\n", cipherPath, p, err)
return p, err
} }

View File

@ -2,14 +2,10 @@
package defaults package defaults
import ( import (
"encoding/json"
"fmt"
"net"
"os" "os"
"os/exec" "os/exec"
"syscall" "syscall"
"testing" "testing"
"time"
"github.com/rfjakob/gocryptfs/internal/ctlsock" "github.com/rfjakob/gocryptfs/internal/ctlsock"
"github.com/rfjakob/gocryptfs/tests/test_helpers" "github.com/rfjakob/gocryptfs/tests/test_helpers"
@ -48,27 +44,16 @@ func TestCtlSock(t *testing.T) {
sock := cDir + ".sock" sock := cDir + ".sock"
test_helpers.MountOrFatal(t, cDir, pDir, "-ctlsock="+sock, "-extpass", "echo test") test_helpers.MountOrFatal(t, cDir, pDir, "-ctlsock="+sock, "-extpass", "echo test")
defer test_helpers.UnmountPanic(pDir) defer test_helpers.UnmountPanic(pDir)
conn, err := net.DialTimeout("unix", sock, 1*time.Second) req := ctlsock.RequestStruct{
if err != nil { EncryptPath: "foobar",
t.Fatal(err)
} }
defer conn.Close() response := test_helpers.QueryCtlSock(t, sock, req)
conn.SetDeadline(time.Now().Add(time.Second))
msg := []byte(`{"EncryptPath": "foobar"}`)
_, err = conn.Write(msg)
if err != nil {
t.Fatal(err)
}
buf := make([]byte, 2*syscall.PathMax)
n, err := conn.Read(buf)
if err != nil {
t.Fatal(err)
}
buf = buf[:n]
var response ctlsock.ResponseStruct
json.Unmarshal(buf, &response)
if response.Result == "" || response.ErrNo != 0 { if response.Result == "" || response.ErrNo != 0 {
fmt.Printf("%s\n", string(buf)) t.Errorf("got an error reply: %+v", response)
t.Errorf("got an error reply") }
req.EncryptPath = "not-existing-dir/xyz"
response = test_helpers.QueryCtlSock(t, sock, req)
if response.ErrNo != int32(syscall.ENOENT) || response.Result != "" {
t.Errorf("incorrect error handling: %+v", response)
} }
} }

View File

@ -0,0 +1,19 @@
{
"Creator": "gocryptfs v1.1.1-16-g75ebb28-dirty",
"EncryptedKey": "cE5bhCZl1iL1tn0dNLh8aQy8n55NMbojmmkwM8iM8/y0uChO0CGaK16sNHffAKJ++qH287JlCpk/BFyi",
"ScryptObject": {
"Salt": "yUxEmtl4KeUkCxL8b6aYcEGVtFe2NAlwy0WsFLt8p+Y=",
"N": 1024,
"R": 8,
"P": 1,
"KeyLen": 32
},
"Version": 2,
"FeatureFlags": [
"GCMIV128",
"DirIV",
"EMENames",
"LongNames",
"AESSIV"
]
}

View File

@ -0,0 +1,52 @@
package reverse_test
import (
"io/ioutil"
"testing"
"github.com/rfjakob/gocryptfs/internal/ctlsock"
"github.com/rfjakob/gocryptfs/tests/test_helpers"
)
var ctlSockTestCases = [][]string{
{"4RQq1dJlfvQPaVU5Xypf0w==", "file"},
{"gocryptfs.longname.ZQCAoi5li3xvDZRO8McBV0L_kzJc4IcAOEzuW-2S1Y4=", "longfile." + x240},
{"v6puXntoQOk7Mhl8zJ4Idg==", "dir"},
{"v6puXntoQOk7Mhl8zJ4Idg==/UVy2gV0RQTUC8AE4wYoMwg==", "dir/file"},
{"v6puXntoQOk7Mhl8zJ4Idg==/fvHFLHlxHCQ7EpVMJu0AZg==", "dir/dir"},
{"v6puXntoQOk7Mhl8zJ4Idg==/fvHFLHlxHCQ7EpVMJu0AZg==/_4uudIGniACke55JoDsqDA==", "dir/dir/dir"},
{"v6puXntoQOk7Mhl8zJ4Idg==/fvHFLHlxHCQ7EpVMJu0AZg==/QvPahkkeVRKTw2kdZFZxwQ==", "dir/dir/file"},
{"v6puXntoQOk7Mhl8zJ4Idg==/gocryptfs.longname.y6rxCn6Id8hIZL2t_STpdLZpu-aE2HpprJR25xD60mk=", "dir/longfile." + x240},
{"gocryptfs.longname.cvRximo1ATRJVEzw_V9MZieHFlod9y2iv2Sug1kbiTE=", "longdir." + x240},
{"gocryptfs.longname.cvRximo1ATRJVEzw_V9MZieHFlod9y2iv2Sug1kbiTE=/-LMdFgFt6UxO-z5iJvuC9w==", "longdir." + x240 + "/dir"},
{"gocryptfs.longname.cvRximo1ATRJVEzw_V9MZieHFlod9y2iv2Sug1kbiTE=/rBPJYAzcHWLdPj1T8kgh8A==", "longdir." + x240 + "/file"},
}
func TestCtlSockDecryptPath(t *testing.T) {
mnt, err := ioutil.TempDir(test_helpers.TmpDir, "reverse_mnt_")
if err != nil {
t.Fatal(err)
}
sock := mnt + ".sock"
test_helpers.MountOrFatal(t, "ctlsock_reverse_test_fs", mnt, "-reverse", "-extpass", "echo test", "-ctlsock="+sock)
defer test_helpers.UnmountPanic(mnt)
var req ctlsock.RequestStruct
for i, tc := range ctlSockTestCases {
// Decrypt
req = ctlsock.RequestStruct{DecryptPath: tc[0]}
response := test_helpers.QueryCtlSock(t, sock, req)
if response.ErrNo != 0 {
t.Errorf("Testcase %d Decrypt: %q ErrNo=%d ErrText=%s", i, tc[0], response.ErrNo, response.ErrText)
} else if response.Result != tc[1] {
t.Errorf("Testcase %d Decrypt: Want %q got %q", i, tc[1], response.Result)
}
// Encrypt
req = ctlsock.RequestStruct{EncryptPath: tc[1]}
response = test_helpers.QueryCtlSock(t, sock, req)
if response.ErrNo != 0 {
t.Errorf("Testcase %d Encrypt: %q ErrNo=%d ErrText=%s", i, tc[0], response.ErrNo, response.ErrText)
} else if response.Result != tc[0] {
t.Errorf("Testcase %d Encrypt: Want %q got %q", i, tc[1], response.Result)
}
}
}

View File

@ -9,11 +9,10 @@ import (
) )
var dirA, dirB, dirC string var dirA, dirB, dirC string
var x240 string var x240 = string(bytes.Repeat([]byte("x"), 240))
var plaintextnames bool var plaintextnames bool
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
x240 = string(bytes.Repeat([]byte("x"), 240))
var r int var r int
for _, plaintextnames = range []bool{false, true} { for _, plaintextnames = range []bool{false, true} {
argsA := []string{"-reverse"} argsA := []string{"-reverse"}

View File

@ -3,15 +3,19 @@ package test_helpers
import ( import (
"crypto/md5" "crypto/md5"
"encoding/hex" "encoding/hex"
"encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"runtime" "runtime"
"syscall" "syscall"
"testing" "testing"
"time"
"github.com/rfjakob/gocryptfs/internal/ctlsock"
"github.com/rfjakob/gocryptfs/internal/nametransform" "github.com/rfjakob/gocryptfs/internal/nametransform"
) )
@ -323,3 +327,30 @@ func Du(t *testing.T, fd int) (nBytes int64) {
// st.Blocks = number of 512-byte blocks // st.Blocks = number of 512-byte blocks
return st.Blocks * 512 return st.Blocks * 512
} }
// QueryCtlSock sends a request to the control socket at "socketPath" and
// returns the response.
func QueryCtlSock(t *testing.T, socketPath string, req ctlsock.RequestStruct) (response ctlsock.ResponseStruct) {
conn, err := net.DialTimeout("unix", socketPath, 1*time.Second)
if err != nil {
t.Fatal(err)
}
defer conn.Close()
conn.SetDeadline(time.Now().Add(time.Second))
msg, err := json.Marshal(req)
if err != nil {
t.Fatal(err)
}
_, err = conn.Write(msg)
if err != nil {
t.Fatal(err)
}
buf := make([]byte, 2*syscall.PathMax)
n, err := conn.Read(buf)
if err != nil {
t.Fatal(err)
}
buf = buf[:n]
json.Unmarshal(buf, &response)
return response
}