diff options
author | Jakob Unterwurzacher | 2020-05-09 17:36:41 +0200 |
---|---|---|
committer | Jakob Unterwurzacher | 2020-05-09 17:36:41 +0200 |
commit | 16221facb9066ccf03015ccfe9e7ca784b0d2099 (patch) | |
tree | 46abce23a8592542563deb463a98d4318deb24af /internal/ctlsock | |
parent | 3ef563493a9d0774513ec0d6aab4cfbc0f6334e5 (diff) |
ctlsock: create exported ctlsock client library
The former interal ctlsock server package is renamed
to ctlsocksrv.
Diffstat (limited to 'internal/ctlsock')
-rw-r--r-- | internal/ctlsock/ctlsock_serve.go | 183 | ||||
-rw-r--r-- | internal/ctlsock/sanitize.go | 32 | ||||
-rw-r--r-- | internal/ctlsock/sanitize_test.go | 30 |
3 files changed, 0 insertions, 245 deletions
diff --git a/internal/ctlsock/ctlsock_serve.go b/internal/ctlsock/ctlsock_serve.go deleted file mode 100644 index 8b19e8a..0000000 --- a/internal/ctlsock/ctlsock_serve.go +++ /dev/null @@ -1,183 +0,0 @@ -// Package ctlsock implements the control socket interface that can be -// activated by passing "-ctlsock" on the command line. -package ctlsock - -import ( - "encoding/json" - "errors" - "fmt" - "io" - "net" - "os" - "syscall" - - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// Interface should be implemented by fusefrontend[_reverse] -type Interface interface { - EncryptPath(string) (string, error) - DecryptPath(string) (string, error) -} - -// RequestStruct is sent by a client -type RequestStruct struct { - EncryptPath string - DecryptPath string -} - -// ResponseStruct is sent by us as response to a request -type ResponseStruct struct { - // Result is the resulting decrypted or encrypted path. Empty on error. - Result string - // ErrNo is the error number as defined in errno.h. - // 0 means success and -1 means that the error number is not known - // (look at ErrText in this case). - 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 { - fs Interface - socket *net.UnixListener -} - -// Serve serves incoming connections on "sock". This call blocks so you -// probably want to run it in a new goroutine. -func Serve(sock net.Listener, fs Interface) { - handler := ctlSockHandler{ - fs: fs, - socket: sock.(*net.UnixListener), - } - handler.acceptLoop() -} - -func (ch *ctlSockHandler) acceptLoop() { - for { - conn, err := ch.socket.Accept() - if err != nil { - // This can trigger on program exit with "use of closed network connection". - // Special-casing this is hard due to https://github.com/golang/go/issues/4373 - // so just don't use tlog.Warn to not cause panics in the tests. - tlog.Info.Printf("ctlsock: Accept error: %v", err) - break - } - go ch.handleConnection(conn.(*net.UnixConn)) - } -} - -// ReadBufSize is the size of the request read buffer. -// The longest possible path is 4096 bytes on Linux and 1024 on Mac OS X so -// 5000 bytes should be enough to hold the whole JSON request. This -// assumes that the path does not contain too many characters that had to be -// be escaped in JSON (for example, a null byte blows up to "\u0000"). -// We abort the connection if the request is bigger than this. -const ReadBufSize = 5000 - -// handleConnection reads and parses JSON requests from "conn" -func (ch *ctlSockHandler) handleConnection(conn *net.UnixConn) { - buf := make([]byte, ReadBufSize) - for { - n, err := conn.Read(buf) - if err == io.EOF { - conn.Close() - return - } else if err != nil { - tlog.Warn.Printf("ctlsock: Read error: %#v", err) - conn.Close() - return - } - if n == ReadBufSize { - tlog.Warn.Printf("ctlsock: request too big (max = %d bytes)", ReadBufSize-1) - conn.Close() - return - } - data := buf[:n] - var in RequestStruct - err = json.Unmarshal(data, &in) - if err != nil { - tlog.Warn.Printf("ctlsock: JSON Unmarshal error: %#v", err) - err = errors.New("JSON Unmarshal error: " + err.Error()) - sendResponse(conn, err, "", "") - continue - } - ch.handleRequest(&in, conn) - } -} - -// handleRequest handles an already-unmarshaled JSON request -func (ch *ctlSockHandler) handleRequest(in *RequestStruct, conn *net.UnixConn) { - var err error - var inPath, outPath, clean, warnText string - // You cannot perform both decryption and encryption in one request - if in.DecryptPath != "" && in.EncryptPath != "" { - err = errors.New("Ambiguous") - sendResponse(conn, err, "", "") - return - } - // Neither encryption nor encryption has been requested, makes no sense - if in.DecryptPath == "" && in.EncryptPath == "" { - err = errors.New("Empty input") - sendResponse(conn, err, "", "") - return - } - // Canonicalize input path - if in.EncryptPath != "" { - inPath = in.EncryptPath - } else { - inPath = in.DecryptPath - } - clean = SanitizePath(inPath) - // Warn if a non-canonical path was passed - if inPath != clean { - warnText = fmt.Sprintf("Non-canonical input path '%s' has been interpreted as '%s'.", inPath, clean) - } - // Error out if the canonical path is now empty - if clean == "" { - err = errors.New("Empty input after canonicalization") - sendResponse(conn, err, "", warnText) - return - } - // Actual encrypt or decrypt operation - if in.EncryptPath != "" { - outPath, err = ch.fs.EncryptPath(clean) - } else { - outPath, err = ch.fs.DecryptPath(clean) - } - sendResponse(conn, err, outPath, warnText) -} - -// sendResponse sends a JSON response message -func sendResponse(conn *net.UnixConn, err error, result string, warnText string) { - msg := ResponseStruct{ - Result: result, - WarnText: warnText, - } - if err != nil { - msg.ErrText = err.Error() - msg.ErrNo = -1 - // Try to extract the actual error number - if pe, ok := err.(*os.PathError); ok { - if se, ok := pe.Err.(syscall.Errno); ok { - msg.ErrNo = int32(se) - } - } else if err == syscall.ENOENT { - msg.ErrNo = int32(syscall.ENOENT) - } - } - jsonMsg, err := json.Marshal(msg) - if err != nil { - 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 deleted file mode 100644 index 7cf77a5..0000000 --- a/internal/ctlsock/sanitize.go +++ /dev/null @@ -1,32 +0,0 @@ -package ctlsock - -import ( - "path/filepath" - "strings" -) - -// SanitizePath adapts filepath.Clean for FUSE paths. -// 1) Leading slash(es) are dropped -// 2) It returns "" instead of "." -// 3) If the cleaned path points above CWD (start with ".."), an empty string -// is returned -// See the TestSanitizePath testcases for examples. -func SanitizePath(path string) string { - // (1) - for len(path) > 0 && path[0] == '/' { - path = path[1:] - } - if len(path) == 0 { - return "" - } - clean := filepath.Clean(path) - // (2) - if clean == "." { - return "" - } - // (3) - if clean == ".." || strings.HasPrefix(clean, "../") { - return "" - } - return clean -} diff --git a/internal/ctlsock/sanitize_test.go b/internal/ctlsock/sanitize_test.go deleted file mode 100644 index d79fa7c..0000000 --- a/internal/ctlsock/sanitize_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package ctlsock - -import ( - "testing" -) - -func TestSanitizePath(t *testing.T) { - testCases := [][]string{ - {"", ""}, - {".", ""}, - {"/", ""}, - {"foo", "foo"}, - {"/foo", "foo"}, - {"foo/", "foo"}, - {"/foo/", "foo"}, - {"/foo/./foo", "foo/foo"}, - {"./", ""}, - {"..", ""}, - {"foo/../..", ""}, - {"foo/../../aaaaaa", ""}, - {"/foo/../../aaaaaa", ""}, - {"/////", ""}, - } - for _, tc := range testCases { - res := SanitizePath(tc[0]) - if res != tc[1] { - t.Errorf("%q: got %q, want %q", tc[0], res, tc[1]) - } - } -} |