2020-05-09 17:36:41 +02:00
|
|
|
// Package ctlsocksrv implements the control socket interface that can be
|
2016-11-10 00:27:08 +01:00
|
|
|
// activated by passing "-ctlsock" on the command line.
|
2020-05-09 17:36:41 +02:00
|
|
|
package ctlsocksrv
|
2016-11-10 00:27:08 +01:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
2016-12-10 12:59:54 +01:00
|
|
|
"fmt"
|
2016-11-10 00:27:08 +01:00
|
|
|
"io"
|
|
|
|
"net"
|
|
|
|
"os"
|
|
|
|
"syscall"
|
|
|
|
|
2020-05-09 17:36:41 +02:00
|
|
|
"github.com/rfjakob/gocryptfs/ctlsock"
|
2016-11-10 00:27:08 +01:00
|
|
|
"github.com/rfjakob/gocryptfs/internal/tlog"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Interface should be implemented by fusefrontend[_reverse]
|
|
|
|
type Interface interface {
|
|
|
|
EncryptPath(string) (string, error)
|
|
|
|
DecryptPath(string) (string, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
type ctlSockHandler struct {
|
|
|
|
fs Interface
|
|
|
|
socket *net.UnixListener
|
|
|
|
}
|
|
|
|
|
2016-12-10 14:54:06 +01:00
|
|
|
// 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) {
|
2016-11-10 00:27:08 +01:00
|
|
|
handler := ctlSockHandler{
|
|
|
|
fs: fs,
|
|
|
|
socket: sock.(*net.UnixListener),
|
|
|
|
}
|
2016-12-10 14:54:06 +01:00
|
|
|
handler.acceptLoop()
|
2016-11-10 00:27:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (ch *ctlSockHandler) acceptLoop() {
|
|
|
|
for {
|
|
|
|
conn, err := ch.socket.Accept()
|
|
|
|
if err != nil {
|
2018-02-27 09:58:14 +01:00
|
|
|
// 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)
|
2016-11-10 00:27:08 +01:00
|
|
|
break
|
|
|
|
}
|
|
|
|
go ch.handleConnection(conn.(*net.UnixConn))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-29 19:01:16 +01:00
|
|
|
// ReadBufSize is the size of the request read buffer.
|
2016-12-10 20:41:40 +01:00
|
|
|
// 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
|
|
|
|
|
2017-02-05 18:07:14 +01:00
|
|
|
// handleConnection reads and parses JSON requests from "conn"
|
2016-11-10 00:27:08 +01:00
|
|
|
func (ch *ctlSockHandler) handleConnection(conn *net.UnixConn) {
|
2016-12-10 20:41:40 +01:00
|
|
|
buf := make([]byte, ReadBufSize)
|
2016-11-10 00:27:08 +01:00
|
|
|
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
|
|
|
|
}
|
2016-12-10 20:41:40 +01:00
|
|
|
if n == ReadBufSize {
|
|
|
|
tlog.Warn.Printf("ctlsock: request too big (max = %d bytes)", ReadBufSize-1)
|
|
|
|
conn.Close()
|
|
|
|
return
|
|
|
|
}
|
2019-01-20 12:13:49 +01:00
|
|
|
data := buf[:n]
|
2020-05-09 17:36:41 +02:00
|
|
|
var in ctlsock.RequestStruct
|
2019-01-20 12:13:49 +01:00
|
|
|
err = json.Unmarshal(data, &in)
|
2016-11-10 00:27:08 +01:00
|
|
|
if err != nil {
|
2017-01-29 18:24:47 +01:00
|
|
|
tlog.Warn.Printf("ctlsock: JSON Unmarshal error: %#v", err)
|
2017-02-05 18:07:14 +01:00
|
|
|
err = errors.New("JSON Unmarshal error: " + err.Error())
|
|
|
|
sendResponse(conn, err, "", "")
|
2017-01-29 18:24:47 +01:00
|
|
|
continue
|
2016-11-10 00:27:08 +01:00
|
|
|
}
|
|
|
|
ch.handleRequest(&in, conn)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-05 18:07:14 +01:00
|
|
|
// handleRequest handles an already-unmarshaled JSON request
|
2020-05-09 17:36:41 +02:00
|
|
|
func (ch *ctlSockHandler) handleRequest(in *ctlsock.RequestStruct, conn *net.UnixConn) {
|
2016-11-10 00:27:08 +01:00
|
|
|
var err error
|
2017-02-05 18:07:14 +01:00
|
|
|
var inPath, outPath, clean, warnText string
|
|
|
|
// You cannot perform both decryption and encryption in one request
|
2016-11-10 00:27:08 +01:00
|
|
|
if in.DecryptPath != "" && in.EncryptPath != "" {
|
2018-02-04 20:38:22 +01:00
|
|
|
err = errors.New("Ambiguous")
|
2017-02-05 18:07:14 +01:00
|
|
|
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 != "" {
|
2016-12-10 12:59:54 +01:00
|
|
|
inPath = in.EncryptPath
|
2017-02-05 18:07:14 +01:00
|
|
|
} 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) {
|
2020-05-09 17:36:41 +02:00
|
|
|
msg := ctlsock.ResponseStruct{
|
2017-02-05 18:07:14 +01:00
|
|
|
Result: result,
|
|
|
|
WarnText: warnText,
|
2016-11-10 00:27:08 +01:00
|
|
|
}
|
|
|
|
if err != nil {
|
2017-02-05 18:07:14 +01:00
|
|
|
msg.ErrText = err.Error()
|
|
|
|
msg.ErrNo = -1
|
2016-11-10 00:27:08 +01:00
|
|
|
// Try to extract the actual error number
|
|
|
|
if pe, ok := err.(*os.PathError); ok {
|
|
|
|
if se, ok := pe.Err.(syscall.Errno); ok {
|
2017-02-05 18:07:14 +01:00
|
|
|
msg.ErrNo = int32(se)
|
2016-11-10 00:27:08 +01:00
|
|
|
}
|
2018-04-02 18:32:30 +02:00
|
|
|
} else if err == syscall.ENOENT {
|
|
|
|
msg.ErrNo = int32(syscall.ENOENT)
|
2016-11-10 00:27:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
jsonMsg, err := json.Marshal(msg)
|
|
|
|
if err != nil {
|
|
|
|
tlog.Warn.Printf("ctlsock: Marshal failed: %v", err)
|
|
|
|
return
|
|
|
|
}
|
2016-12-10 12:59:54 +01:00
|
|
|
// For convenience for the user, add a newline at the end.
|
|
|
|
jsonMsg = append(jsonMsg, '\n')
|
2016-11-10 00:27:08 +01:00
|
|
|
_, err = conn.Write(jsonMsg)
|
|
|
|
if err != nil {
|
|
|
|
tlog.Warn.Printf("ctlsock: Write failed: %v", err)
|
|
|
|
}
|
|
|
|
}
|