From eaca820e876bfcdc67323eac6dd43ecc420968f2 Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Sun, 7 Feb 2021 20:01:16 +0100 Subject: fusefrontend: do not encrypt ACLs Pass through system.posix_acl_access and system.posix_acl_default unencrypted to fix "cp -a" problems. "cp -a" uses "setxattr" even to set normal permissions, see https://www.spinics.net/lists/linux-nfs/msg63986.html . Fixes https://github.com/rfjakob/gocryptfs/issues/543 --- internal/fusefrontend/node_xattr.go | 74 ++++++++++++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 9 deletions(-) (limited to 'internal') diff --git a/internal/fusefrontend/node_xattr.go b/internal/fusefrontend/node_xattr.go index cbc5804..3855b55 100644 --- a/internal/fusefrontend/node_xattr.go +++ b/internal/fusefrontend/node_xattr.go @@ -10,6 +10,9 @@ import ( "github.com/rfjakob/gocryptfs/internal/tlog" ) +// -1 as uint32 +const minus1 = ^uint32(0) + // xattr names are encrypted like file names, but with a fixed IV. // Padded with "_xx" for length 16. var xattrNameIV = []byte("xattr_name_iv_xx") @@ -22,6 +25,13 @@ var xattrStorePrefix = "user.gocryptfs." // see https://github.com/rfjakob/gocryptfs/issues/515 for details. var xattrCapability = "security.capability" +// isAcl returns true if the attribute name is for storing ACLs +// +// ACLs are passed through without encryption +func isAcl(attr string) bool { + return attr == "system.posix_acl_access" || attr == "system.posix_acl_default" +} + // GetXAttr - FUSE call. Reads the value of extended attribute "attr". // // This function is symlink-safe through Fgetxattr. @@ -36,15 +46,34 @@ func (n *Node) Getxattr(ctx context.Context, attr string, dest []byte) (uint32, // and it did not cause trouble. Seems cleaner than saying ENODATA. return 0, syscall.EOPNOTSUPP } - cAttr := rn.encryptXattrName(attr) - cData, errno := n.getXAttr(cAttr) - if errno != 0 { - return 0, errno + var data []byte + // ACLs are passed through without encryption + if isAcl(attr) { + var errno syscall.Errno + data, errno = n.getXAttr(attr) + if errno != 0 { + return minus1, errno + } + } else { + // encrypted user xattr + cAttr := rn.encryptXattrName(attr) + cData, errno := n.getXAttr(cAttr) + if errno != 0 { + return 0, errno + } + var err error + data, err = rn.decryptXattrValue(cData) + if err != nil { + tlog.Warn.Printf("GetXAttr: %v", err) + return minus1, syscall.EIO + } + } + // Caller passes size zero to find out how large their buffer should be + if len(dest) == 0 { + return uint32(len(data)), 0 } - data, err := rn.decryptXattrValue(cData) - if err != nil { - tlog.Warn.Printf("GetXAttr: %v", err) - return ^uint32(0), syscall.EIO + if len(dest) < len(data) { + return minus1, syscall.ERANGE } l := copy(dest, data) return uint32(l), 0 @@ -56,6 +85,12 @@ func (n *Node) Getxattr(ctx context.Context, attr string, dest []byte) (uint32, func (n *Node) Setxattr(ctx context.Context, attr string, data []byte, flags uint32) syscall.Errno { rn := n.rootNode() flags = uint32(filterXattrSetFlags(int(flags))) + + // ACLs are passed through without encryption + if isAcl(attr) { + return n.setXAttr(attr, data, flags) + } + cAttr := rn.encryptXattrName(attr) cData := rn.encryptXattrValue(data) return n.setXAttr(cAttr, cData, flags) @@ -66,6 +101,12 @@ func (n *Node) Setxattr(ctx context.Context, attr string, data []byte, flags uin // This function is symlink-safe through Fremovexattr. func (n *Node) Removexattr(ctx context.Context, attr string) syscall.Errno { rn := n.rootNode() + + // ACLs are passed through without encryption + if isAcl(attr) { + return n.removeXAttr(attr) + } + cAttr := rn.encryptXattrName(attr) return n.removeXAttr(cAttr) } @@ -81,6 +122,11 @@ func (n *Node) Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errn rn := n.rootNode() var buf bytes.Buffer for _, curName := range cNames { + // ACLs are passed through without encryption + if isAcl(curName) { + buf.WriteString(curName + "\000") + continue + } if !strings.HasPrefix(curName, xattrStorePrefix) { continue } @@ -90,10 +136,20 @@ func (n *Node) Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errn rn.reportMitigatedCorruption(curName) continue } + // We *used to* encrypt ACLs, which caused a lot of problems. + if isAcl(name) { + tlog.Warn.Printf("ListXAttr: ignoring deprecated encrypted ACL %q = %q", curName, name) + rn.reportMitigatedCorruption(curName) + continue + } buf.WriteString(name + "\000") } + // Caller passes size zero to find out how large their buffer should be + if len(dest) == 0 { + return uint32(buf.Len()), 0 + } if buf.Len() > len(dest) { - return ^uint32(0), syscall.ERANGE + return minus1, syscall.ERANGE } return uint32(copy(dest, buf.Bytes())), 0 } -- cgit v1.2.3