diff options
| -rw-r--r-- | actions/config.go | 9 | ||||
| -rw-r--r-- | actions/config_test.go | 26 | ||||
| -rw-r--r-- | actions/context_test.go | 2 | ||||
| -rw-r--r-- | cmd/fscrypt/setup.go | 16 | ||||
| -rw-r--r-- | util/util.go | 23 | ||||
| -rw-r--r-- | util/util_test.go | 7 |
6 files changed, 78 insertions, 5 deletions
diff --git a/actions/config.go b/actions/config.go index 3433438..2463b95 100644 --- a/actions/config.go +++ b/actions/config.go @@ -58,8 +58,9 @@ var ( // CreateConfigFile creates a new config file at the appropriate location with // the appropriate hashing costs and encryption parameters. The hashing will be -// configured to take as long as the specified time target. -func CreateConfigFile(target time.Duration) error { +// configured to take as long as the specified time target. In addition, the +// version of encryption policy to use may be overridden from the default of v1. +func CreateConfigFile(target time.Duration, policyVersion int64) error { // Create the config file before computing the hashing costs, so we fail // immediately if the program has insufficient permissions. configFile, err := filesystem.OpenFileOverridingUmask(ConfigFileLocation, @@ -77,6 +78,10 @@ func CreateConfigFile(target time.Duration) error { Options: metadata.DefaultOptions, } + if policyVersion != 0 { + config.Options.PolicyVersion = policyVersion + } + if config.HashCosts, err = getHashingCosts(target); err != nil { return err } diff --git a/actions/config_test.go b/actions/config_test.go index 02c89e6..3599667 100644 --- a/actions/config_test.go +++ b/actions/config_test.go @@ -26,6 +26,8 @@ import ( "time" "golang.org/x/sys/unix" + + "github.com/google/fscrypt/metadata" ) // Test that the global config file is created with mode 0644, regardless of the @@ -42,7 +44,7 @@ func TestConfigFileIsCreatedWithCorrectMode(t *testing.T) { defer os.RemoveAll(tempDir) ConfigFileLocation = filepath.Join(tempDir, "test.conf") - if err = CreateConfigFile(time.Millisecond); err != nil { + if err = CreateConfigFile(time.Millisecond, 0); err != nil { t.Fatal(err) } fileInfo, err := os.Stat(ConfigFileLocation) @@ -53,3 +55,25 @@ func TestConfigFileIsCreatedWithCorrectMode(t *testing.T) { t.Error("Expected newly created config file to have mode 0644") } } + +func TestCreateConfigFileV2Policy(t *testing.T) { + tempDir, err := ioutil.TempDir("", "fscrypt") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tempDir) + ConfigFileLocation = filepath.Join(tempDir, "test.conf") + + if err = CreateConfigFile(time.Millisecond, 2); err != nil { + t.Fatal(err) + } + + var config *metadata.Config + config, err = getConfig() + if err != nil { + t.Fatal(err) + } + if config.Options.PolicyVersion != 2 { + t.Error("Expected PolicyVersion 2") + } +} diff --git a/actions/context_test.go b/actions/context_test.go index 4f93776..4488a6b 100644 --- a/actions/context_test.go +++ b/actions/context_test.go @@ -52,7 +52,7 @@ func setupContext() (ctx *Context, err error) { return nil, fmt.Errorf("created context at %q without config file", badCtx.Mount.Path) } - if err = CreateConfigFile(testTime); err != nil { + if err = CreateConfigFile(testTime, 0); err != nil { return nil, err } defer func() { diff --git a/cmd/fscrypt/setup.go b/cmd/fscrypt/setup.go index 328788a..7b9bebb 100644 --- a/cmd/fscrypt/setup.go +++ b/cmd/fscrypt/setup.go @@ -50,8 +50,22 @@ func createGlobalConfig(w io.Writer, path string) error { return err } + // v2 encryption policies are recommended, so set policy_version 2 when + // the kernel supports it. v2 policies are supported by upstream Linux + // v5.4 and later. For now we simply check the kernel version. Ideally + // we'd instead check whether setting a v2 policy actually works, in + // order to also detect backports of the kernel patches. However, that's + // hard because from this context (creating /etc/fscrypt.conf) we may + // not yet have access to a filesystem that supports encryption. + var policyVersion int64 + if util.IsKernelVersionAtLeast(5, 4) { + fmt.Fprintln(w, "Defaulting to policy_version 2 because kernel supports it.") + policyVersion = 2 + } else { + fmt.Fprintln(w, "Defaulting to policy_version 1 because kernel doesn't support v2.") + } fmt.Fprintln(w, "Customizing passphrase hashing difficulty for this system...") - err = actions.CreateConfigFile(timeTargetFlag.Value) + err = actions.CreateConfigFile(timeTargetFlag.Value, policyVersion) if err != nil { return err } diff --git a/util/util.go b/util/util.go index 97ee33c..d97a7ae 100644 --- a/util/util.go +++ b/util/util.go @@ -25,10 +25,14 @@ package util import ( "bufio" + "fmt" + "log" "os" "os/user" "strconv" "unsafe" + + "golang.org/x/sys/unix" ) // Ptr converts a Go byte array to a pointer to the start of the array. @@ -126,3 +130,22 @@ func EffectiveUser() (*user.User, error) { func IsUserRoot() bool { return os.Geteuid() == 0 } + +// IsKernelVersionAtLeast returns true if the Linux kernel version is at least +// major.minor. If something goes wrong it assumes false. +func IsKernelVersionAtLeast(major, minor int) bool { + var uname unix.Utsname + if err := unix.Uname(&uname); err != nil { + log.Printf("Uname failed [%v], assuming old kernel", err) + return false + } + release := string(uname.Release[:]) + log.Printf("Kernel version is %s", release) + var actualMajor, actualMinor int + if n, _ := fmt.Sscanf(release, "%d.%d", &actualMajor, &actualMinor); n != 2 { + log.Printf("Unrecognized uname format %q, assuming old kernel", release) + return false + } + return actualMajor > major || + (actualMajor == major && actualMinor >= minor) +} diff --git a/util/util_test.go b/util/util_test.go index 7739edd..70e7070 100644 --- a/util/util_test.go +++ b/util/util_test.go @@ -91,3 +91,10 @@ func TestNeverErrorNoPanic(t *testing.T) { NeverError(nil) } + +func TestIsKernelVersionAtLeast(t *testing.T) { + // Even just running Go requires at least v2.6.23, so... + if !IsKernelVersionAtLeast(2, 6) { + t.Error("IsKernelVersionAtLeast() is broken") + } +} |