]> git.apps.os.sepia.ceph.com Git - fscrypt.git/commitdiff
filesystem: preserve metadata file permissions on updates
authorEric Biggers <ebiggers@google.com>
Wed, 23 Feb 2022 20:35:04 +0000 (12:35 -0800)
committerEric Biggers <ebiggers@google.com>
Wed, 23 Feb 2022 20:35:04 +0000 (12:35 -0800)
Since fscrypt replaces metadata files rather than overwrites them (to
get atomicity), their owner will change to root if root makes a change.
That isn't too much of an issue when the files have mode 0644.  However,
it will become a much bigger issue when the files have mode 0600,
especially because existing files with mode 0644 would also get changed
to have mode 0600.

In preparation for this, start preserving the previous owner and mode of
policy and protector files when they are updated.

filesystem/filesystem.go

index 1877b1b2b5743088b72b0effc75b7e770aa7750a..70076b72980e7f3bf36af49b9f9e8db4ead0442c 100644 (file)
@@ -254,7 +254,7 @@ const (
        basePermissions = 0755
        // The metadata files are globally visible, but can only be deleted by
        // the user that created them
-       filePermissions = 0644
+       filePermissions = os.FileMode(0644)
 
        // Maximum size of a metadata file.  This value is arbitrary, and it can
        // be changed.  We just set a reasonable limit that shouldn't be reached
@@ -626,7 +626,7 @@ func (m *Mount) overwriteDataNonAtomic(path string, data []byte) error {
 // However, if the process doesn't have write permission to the directory but
 // does have write permission to the file itself, then as a fallback the file is
 // overwritten in-place rather than replaced.  Note that this may be non-atomic.
-func (m *Mount) writeData(path string, data []byte, owner *user.User) error {
+func (m *Mount) writeData(path string, data []byte, owner *user.User, mode os.FileMode) error {
        // Write the data to a temporary file, sync it, then rename into place
        // so that the operation will be atomic.
        dirPath := filepath.Dir(path)
@@ -644,8 +644,8 @@ func (m *Mount) writeData(path string, data []byte, owner *user.User) error {
        }
        defer os.Remove(tempFile.Name())
 
-       // TempFile() creates the file with mode 0600.  Change it to 0644.
-       if err = tempFile.Chmod(filePermissions); err != nil {
+       // Ensure the new file has the right permissions mask.
+       if err = tempFile.Chmod(mode); err != nil {
                tempFile.Close()
                return err
        }
@@ -690,8 +690,29 @@ func (m *Mount) addMetadata(path string, md metadata.Metadata, owner *user.User)
                return err
        }
 
-       log.Printf("writing metadata to %q", path)
-       return m.writeData(path, data, owner)
+       mode := filePermissions
+       // If the file already exists, then preserve its owner and mode if
+       // possible.  This is necessary because by default, for atomicity
+       // reasons we'll replace the file rather than overwrite it.
+       info, err := os.Lstat(path)
+       if err == nil {
+               if owner == nil && util.IsUserRoot() {
+                       uid := info.Sys().(*syscall.Stat_t).Uid
+                       if owner, err = util.UserFromUID(int64(uid)); err != nil {
+                               log.Print(err)
+                       }
+               }
+               mode = info.Mode() & 0777
+       } else if !os.IsNotExist(err) {
+               log.Print(err)
+       }
+
+       if owner != nil {
+               log.Printf("writing metadata to %q and setting owner to %s", path, owner.Username)
+       } else {
+               log.Printf("writing metadata to %q", path)
+       }
+       return m.writeData(path, data, owner, mode)
 }
 
 // readMetadataFileSafe gets the contents of a metadata file extra-carefully,
@@ -838,7 +859,7 @@ func (m *Mount) AddLinkedProtector(descriptor string, dest *Mount, trustedUser *
        if err != nil {
                return false, err
        }
-       return true, m.writeData(linkPath, []byte(newLink), ownerIfCreating)
+       return true, m.writeData(linkPath, []byte(newLink), ownerIfCreating, filePermissions)
 }
 
 // GetRegularProtector looks up the protector metadata by descriptor. This will