From e9d03de8f3cb573a6f2d29fcb11fff73736ab44e Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 7 Nov 2020 14:20:45 -0800 Subject: [PATCH] cmd/fscrypt: fix isDirUnlockedHeuristic() on latest kernels On an "incompletely locked" directory, isDirUnlockedHeuristic() is supposed to return true, but on Linux v5.10-rc1 and later it returns false since now creating a subdirectory fails rather than succeeds. This change was intentional, so make isDirUnlockedHeuristic() apply a second heuristic too: also return true if any filenames in the directory don't appear to be valid no-key names. This fixes cli-tests/t_v1_encrypt on Linux v5.10-rc1 and later. --- cmd/fscrypt/commands.go | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/cmd/fscrypt/commands.go b/cmd/fscrypt/commands.go index 7c123563..e6c8ecc3 100644 --- a/cmd/fscrypt/commands.go +++ b/cmd/fscrypt/commands.go @@ -25,6 +25,7 @@ import ( "log" "os" "path/filepath" + "strings" "github.com/pkg/errors" "github.com/urfave/cli" @@ -532,8 +533,25 @@ func lockAction(c *cli.Context) error { return nil } -// isDirUnlockedHeuristic returns true if we can create a subdirectory of the -// given directory and therefore it is definitely still unlocked. It returns +func isPossibleNoKeyName(filename string) bool { + // No-key names are at least 22 bytes long, since they are + // base64-encoded and ciphertext filenames are at least 16 bytes. + if len(filename) < 22 { + return false + } + // No-key names contain only base64 characters and underscore. + validChars := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,_" + for _, char := range filename { + if !strings.ContainsRune(validChars, char) { + return false + } + } + return true +} + +// isDirUnlockedHeuristic returns true if the directory is definitely still +// unlocked. This is the case if we can create a subdirectory or if the +// directory contains filenames that aren't valid no-key names. It returns // false if the directory is probably locked (though it could also be unlocked). // // This is only useful if the directory's policy uses the user keyring, since @@ -544,6 +562,21 @@ func isDirUnlockedHeuristic(dirPath string) bool { os.Remove(subdirPath) return true } + dir, err := os.Open(dirPath) + if err != nil { + return false + } + defer dir.Close() + + names, err := dir.Readdirnames(-1) + if err != nil { + return false + } + for _, name := range names { + if !isPossibleNoKeyName(name) { + return true + } + } return false }