From 75ebb28a625bc16d145f5acd9e0cc1d305716afe Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Thu, 10 Nov 2016 00:27:08 +0100 Subject: ctlsock: add initial limited implementation At the moment, in forward mode you can only encrypt paths and in reverse mode you can only decrypt paths. --- internal/ctlsock/ctlsock_serve.go | 137 +++++++++++++++++++++ internal/fusefrontend/ctlsock_interface.go | 19 +++ internal/fusefrontend/write_lock.go | 2 +- internal/fusefrontend_reverse/ctlsock_interface.go | 19 +++ internal/fusefrontend_reverse/rfs.go | 2 +- 5 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 internal/ctlsock/ctlsock_serve.go create mode 100644 internal/fusefrontend/ctlsock_interface.go create mode 100644 internal/fusefrontend_reverse/ctlsock_interface.go (limited to 'internal') diff --git a/internal/ctlsock/ctlsock_serve.go b/internal/ctlsock/ctlsock_serve.go new file mode 100644 index 0000000..571260d --- /dev/null +++ b/internal/ctlsock/ctlsock_serve.go @@ -0,0 +1,137 @@ +// Package ctlsock implementes the control socket interface that can be +// activated by passing "-ctlsock" on the command line. +package ctlsock + +import ( + "encoding/json" + "errors" + "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 +} + +type ctlSockHandler struct { + fs Interface + socket *net.UnixListener +} + +// CreateAndServe creates an unix socket at "path" and serves incoming +// connections in a new goroutine. +func CreateAndServe(path string, fs Interface) error { + sock, err := net.Listen("unix", path) + if err != nil { + return err + } + handler := ctlSockHandler{ + fs: fs, + socket: sock.(*net.UnixListener), + } + go handler.acceptLoop() + return nil +} + +func (ch *ctlSockHandler) acceptLoop() { + for { + conn, err := ch.socket.Accept() + if err != nil { + tlog.Warn.Printf("ctlsock: Accept error: %v", err) + break + } + go ch.handleConnection(conn.(*net.UnixConn)) + } +} + +func (ch *ctlSockHandler) handleConnection(conn *net.UnixConn) { + // 2*PATH_MAX is definitely big enough for requests to decrypt or + // encrypt paths. + buf := make([]byte, 2*syscall.PathMax) + 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 + } + buf = buf[:n] + var in RequestStruct + err = json.Unmarshal(buf, &in) + if err != nil { + tlog.Warn.Printf("ctlsock: Unmarshal error: %#v", err) + errorMsg := ResponseStruct{ + ErrNo: int32(syscall.EINVAL), + ErrText: err.Error(), + } + sendResponse(&errorMsg, conn) + } + ch.handleRequest(&in, conn) + // Restore original size. + buf = buf[:cap(buf)] + } +} + +func (ch *ctlSockHandler) handleRequest(in *RequestStruct, conn *net.UnixConn) { + var err error + var out ResponseStruct + 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) + } else if in.EncryptPath != "" { + out.Result, err = ch.fs.EncryptPath(in.EncryptPath) + } + if err != nil { + out.ErrText = err.Error() + out.ErrNo = -1 + // Try to extract the actual error number + if pe, ok := err.(*os.PathError); ok { + if se, ok := pe.Err.(syscall.Errno); ok { + out.ErrNo = int32(se) + } + } + } + sendResponse(&out, conn) +} + +func sendResponse(msg *ResponseStruct, conn *net.UnixConn) { + jsonMsg, err := json.Marshal(msg) + if err != nil { + tlog.Warn.Printf("ctlsock: Marshal failed: %v", err) + return + } + _, err = conn.Write(jsonMsg) + if err != nil { + tlog.Warn.Printf("ctlsock: Write failed: %v", err) + } +} diff --git a/internal/fusefrontend/ctlsock_interface.go b/internal/fusefrontend/ctlsock_interface.go new file mode 100644 index 0000000..c22dce6 --- /dev/null +++ b/internal/fusefrontend/ctlsock_interface.go @@ -0,0 +1,19 @@ +package fusefrontend + +import ( + "errors" + + "github.com/rfjakob/gocryptfs/internal/ctlsock" +) + +var _ ctlsock.Interface = &FS{} // Verify that interface is implemented. + +// EncryptPath implements ctlsock.Backend +func (fs *FS) EncryptPath(plainPath string) (string, error) { + return fs.encryptPath(plainPath) +} + +// DecryptPath implements ctlsock.Backend +func (fs *FS) DecryptPath(plainPath string) (string, error) { + return "", errors.New("Not implemented") +} diff --git a/internal/fusefrontend/write_lock.go b/internal/fusefrontend/write_lock.go index 7394994..3addfd6 100644 --- a/internal/fusefrontend/write_lock.go +++ b/internal/fusefrontend/write_lock.go @@ -21,7 +21,7 @@ var wlock wlockMap // 2) lock ... unlock ... // 3) unregister type wlockMap struct { - // Counts lock() calls. As every operation that modifies a file should + // opCount counts lock() calls. As every operation that modifies a file should // call it, this effectively serves as a write-operation counter. // The variable is accessed without holding any locks so atomic operations // must be used. It must be the first element of the struct to guarantee diff --git a/internal/fusefrontend_reverse/ctlsock_interface.go b/internal/fusefrontend_reverse/ctlsock_interface.go new file mode 100644 index 0000000..448663f --- /dev/null +++ b/internal/fusefrontend_reverse/ctlsock_interface.go @@ -0,0 +1,19 @@ +package fusefrontend_reverse + +import ( + "errors" + + "github.com/rfjakob/gocryptfs/internal/ctlsock" +) + +var _ ctlsock.Interface = &reverseFS{} // Verify that interface is implemented. + +// EncryptPath implements ctlsock.Backend +func (rfs *reverseFS) EncryptPath(plainPath string) (string, error) { + return "", errors.New("Not implemented") +} + +// DecryptPath implements ctlsock.Backend +func (rfs *reverseFS) DecryptPath(plainPath string) (string, error) { + return rfs.decryptPath(plainPath) +} diff --git a/internal/fusefrontend_reverse/rfs.go b/internal/fusefrontend_reverse/rfs.go index 87a2602..84348f7 100644 --- a/internal/fusefrontend_reverse/rfs.go +++ b/internal/fusefrontend_reverse/rfs.go @@ -49,7 +49,7 @@ var _ pathfs.FileSystem = &reverseFS{} // NewFS returns an encrypted FUSE overlay filesystem. // In this case (reverse mode) the backing directory is plain-text and // reverseFS provides an encrypted view. -func NewFS(args fusefrontend.Args) pathfs.FileSystem { +func NewFS(args fusefrontend.Args) *reverseFS { if args.CryptoBackend != cryptocore.BackendAESSIV { panic("reverse mode must use AES-SIV, everything else is insecure") } -- cgit v1.2.3