diff options
| author | Jakob Unterwurzacher | 2016-12-10 12:59:54 +0100 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2016-12-10 12:59:54 +0100 | 
| commit | 2758c75cae2896b7f1327fe00f60a1c017c0e0d1 (patch) | |
| tree | 79b3639b9341e7c88cff50b0baba003edbeca5ed /internal | |
| parent | 21904cd5f03f853ea6ceccbb414a5070c1f96324 (diff) | |
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
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/ctlsock/ctlsock_serve.go | 18 | ||||
| -rw-r--r-- | internal/ctlsock/sanitize.go | 20 | ||||
| -rw-r--r-- | internal/ctlsock/sanitize_test.go | 25 | 
3 files changed, 61 insertions, 2 deletions
| diff --git a/internal/ctlsock/ctlsock_serve.go b/internal/ctlsock/ctlsock_serve.go index 571260d..63515c0 100644 --- a/internal/ctlsock/ctlsock_serve.go +++ b/internal/ctlsock/ctlsock_serve.go @@ -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) diff --git a/internal/ctlsock/sanitize.go b/internal/ctlsock/sanitize.go new file mode 100644 index 0000000..5bc3706 --- /dev/null +++ b/internal/ctlsock/sanitize.go @@ -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 +} diff --git a/internal/ctlsock/sanitize_test.go b/internal/ctlsock/sanitize_test.go new file mode 100644 index 0000000..dfcb62c --- /dev/null +++ b/internal/ctlsock/sanitize_test.go @@ -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]) +		} +	} +} | 
