ctlsock: sanitize paths before passing them to the backend
You used to be able to crash gocryptfs by passing "/foo" of "foo/" to the ctlsock. Fixes https://github.com/rfjakob/gocryptfs/issues/66
This commit is contained in:
parent
21904cd5f0
commit
2758c75cae
@ -5,6 +5,7 @@ package ctlsock
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
@ -35,6 +36,9 @@ type ResponseStruct struct {
|
||||
ErrNo int32
|
||||
// ErrText is a detailed error message.
|
||||
ErrText string
|
||||
// WarnText contains warnings that may have been encountered while
|
||||
// processing the message.
|
||||
WarnText string
|
||||
}
|
||||
|
||||
type ctlSockHandler struct {
|
||||
@ -102,14 +106,19 @@ func (ch *ctlSockHandler) handleConnection(conn *net.UnixConn) {
|
||||
func (ch *ctlSockHandler) handleRequest(in *RequestStruct, conn *net.UnixConn) {
|
||||
var err error
|
||||
var out ResponseStruct
|
||||
var inPath, clean string
|
||||
if in.DecryptPath != "" && in.EncryptPath != "" {
|
||||
err = errors.New("Ambigous")
|
||||
} else if in.DecryptPath == "" && in.EncryptPath == "" {
|
||||
err = errors.New("No operation")
|
||||
} else if in.DecryptPath != "" {
|
||||
out.Result, err = ch.fs.DecryptPath(in.DecryptPath)
|
||||
inPath = in.DecryptPath
|
||||
clean = SanitizePath(inPath)
|
||||
out.Result, err = ch.fs.DecryptPath(clean)
|
||||
} else if in.EncryptPath != "" {
|
||||
out.Result, err = ch.fs.EncryptPath(in.EncryptPath)
|
||||
inPath = in.EncryptPath
|
||||
clean = SanitizePath(inPath)
|
||||
out.Result, err = ch.fs.EncryptPath(clean)
|
||||
}
|
||||
if err != nil {
|
||||
out.ErrText = err.Error()
|
||||
@ -121,6 +130,9 @@ func (ch *ctlSockHandler) handleRequest(in *RequestStruct, conn *net.UnixConn) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if inPath != clean {
|
||||
out.WarnText = fmt.Sprintf("Non-canonical input path %q has been interpreted as %q", inPath, clean)
|
||||
}
|
||||
sendResponse(&out, conn)
|
||||
}
|
||||
|
||||
@ -130,6 +142,8 @@ func sendResponse(msg *ResponseStruct, conn *net.UnixConn) {
|
||||
tlog.Warn.Printf("ctlsock: Marshal failed: %v", err)
|
||||
return
|
||||
}
|
||||
// For convenience for the user, add a newline at the end.
|
||||
jsonMsg = append(jsonMsg, '\n')
|
||||
_, err = conn.Write(jsonMsg)
|
||||
if err != nil {
|
||||
tlog.Warn.Printf("ctlsock: Write failed: %v", err)
|
||||
|
20
internal/ctlsock/sanitize.go
Normal file
20
internal/ctlsock/sanitize.go
Normal file
@ -0,0 +1,20 @@
|
||||
package ctlsock
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// SanitizePath adapts filepath.Clean for FUSE paths.
|
||||
// 1) It always returns a relative path
|
||||
// 2) It returns "" instead of "."
|
||||
// See the TestSanitizePath testcases for examples.
|
||||
func SanitizePath(path string) string {
|
||||
clean := filepath.Clean(path)
|
||||
if clean == "." || clean == "/" {
|
||||
return ""
|
||||
}
|
||||
if clean[0] == '/' {
|
||||
clean = clean[1:]
|
||||
}
|
||||
return clean
|
||||
}
|
25
internal/ctlsock/sanitize_test.go
Normal file
25
internal/ctlsock/sanitize_test.go
Normal file
@ -0,0 +1,25 @@
|
||||
package ctlsock
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSanitizePath(t *testing.T) {
|
||||
testCases := [][]string{
|
||||
{"", ""},
|
||||
{".", ""},
|
||||
{"/", ""},
|
||||
{"foo", "foo"},
|
||||
{"/foo", "foo"},
|
||||
{"foo/", "foo"},
|
||||
{"/foo/", "foo"},
|
||||
{"/foo/./foo", "foo/foo"},
|
||||
{"./", ""},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
res := SanitizePath(tc[0])
|
||||
if res != tc[1] {
|
||||
t.Errorf("%q: got %q, want %q", tc[0], res, tc[1])
|
||||
}
|
||||
}
|
||||
}
|
@ -57,6 +57,13 @@ func TestCtlSock(t *testing.T) {
|
||||
if response.ErrNo != int32(syscall.ENOENT) || response.Result != "" {
|
||||
t.Errorf("incorrect error handling: %+v", response)
|
||||
}
|
||||
// Strange paths should not cause a crash
|
||||
crashers := []string{"/foo", "foo/", "/foo/", ".", "/////", "/../../."}
|
||||
for _, c := range crashers {
|
||||
req.EncryptPath = c
|
||||
// QueryCtlSock calls t.Fatal if it gets EOF when gocryptfs panics
|
||||
test_helpers.QueryCtlSock(t, sock, req)
|
||||
}
|
||||
}
|
||||
|
||||
// In gocryptfs before v1.2, the file header was only read once for each
|
||||
|
Loading…
Reference in New Issue
Block a user