aboutsummaryrefslogtreecommitdiff
path: root/cmd/fscrypt/commands.go
diff options
context:
space:
mode:
authorEric Biggers <ebiggers@google.com>2019-12-15 19:31:39 -0800
committerEric Biggers <ebiggers@google.com>2020-01-05 10:02:13 -0800
commit9003a0331a112e8901fae8279f4897a825ee8069 (patch)
tree37f6341d853346489f69ebd8b671b8133e69dfd5 /cmd/fscrypt/commands.go
parent6ffc9457945a9484d2757cc4b01de35426502d0a (diff)
cmd/fscrypt: add 'fscrypt lock' command
Add support for 'fscrypt lock'. This command "locks" a directory, undoing 'fscrypt unlock'. When the filesystem keyring is used, 'fscrypt lock' also detects when a directory wasn't fully locked due to some files still being in-use. It can then be run again later to try to finish locking the files.
Diffstat (limited to 'cmd/fscrypt/commands.go')
-rw-r--r--cmd/fscrypt/commands.go95
1 files changed, 86 insertions, 9 deletions
diff --git a/cmd/fscrypt/commands.go b/cmd/fscrypt/commands.go
index a3bfef2..8f2d21b 100644
--- a/cmd/fscrypt/commands.go
+++ b/cmd/fscrypt/commands.go
@@ -281,8 +281,8 @@ var Unlock = cli.Command{
appropriate key into the keyring. This requires unlocking one of
the protectors protecting this directory (either by selecting a
protector or specifying one with %s). This directory will be
- locked again upon reboot, or after running "fscrypt purge" and
- unmounting the corresponding filesystem.`, directoryArg,
+ locked again upon reboot, or after running "fscrypt lock" or
+ "fscrypt purge".`, directoryArg,
shortDisplay(unlockWithFlag)),
Flags: []cli.Flag{unlockWithFlag, keyFileFlag, userFlag},
Action: unlockAction,
@@ -328,6 +328,88 @@ func unlockAction(c *cli.Context) error {
return nil
}
+func dropCachesIfRequested(c *cli.Context, ctx *actions.Context) error {
+ if dropCachesFlag.Value {
+ if err := security.DropFilesystemCache(); err != nil {
+ return err
+ }
+ fmt.Fprintf(c.App.Writer, "Encrypted data removed from filesystem cache.\n")
+ } else {
+ fmt.Fprintf(c.App.Writer, "Filesystem %q should now be unmounted.\n", ctx.Mount.Path)
+ }
+ return nil
+}
+
+// Lock takes an encrypted directory and locks it, undoing Unlock.
+var Lock = cli.Command{
+ Name: "lock",
+ ArgsUsage: directoryArg,
+ Usage: "lock an encrypted directory",
+ Description: fmt.Sprintf(`This command takes %s, an encrypted directory
+ which has been unlocked by fscrypt, and locks the directory by
+ removing the encryption key from the kernel. I.e., it undoes the
+ effect of 'fscrypt unlock'.
+
+ For this to be effective, all files in the directory must first
+ be closed.
+
+ The %s=true option may be needed to properly lock the directory.
+ Root is required for this.
+
+ WARNING: even after the key has been removed, decrypted data may
+ still be present in freed memory, where it may still be
+ recoverable by an attacker who compromises system memory. To be
+ fully safe, you must reboot with a power cycle.`,
+ directoryArg, shortDisplay(dropCachesFlag)),
+ Flags: []cli.Flag{dropCachesFlag, userFlag},
+ Action: lockAction,
+}
+
+func lockAction(c *cli.Context) error {
+ if c.NArg() != 1 {
+ return expectedArgsErr(c, 1, false)
+ }
+
+ targetUser, err := parseUserFlag(true)
+ if err != nil {
+ return newExitError(c, err)
+ }
+ path := c.Args().Get(0)
+ ctx, err := actions.NewContextFromPath(path, targetUser)
+ if err != nil {
+ return newExitError(c, err)
+ }
+
+ log.Printf("performing sanity checks")
+ // Ensure path is encrypted and filesystem is using fscrypt.
+ policy, err := actions.GetPolicyFromPath(ctx, path)
+ if err != nil {
+ return newExitError(c, err)
+ }
+ // Check if directory is already locked
+ if policy.IsFullyDeprovisioned() {
+ log.Printf("policy %s is already fully deprovisioned", policy.Descriptor())
+ return newExitError(c, errors.Wrapf(ErrPolicyLocked, path))
+ }
+ // Check for permission to drop caches, if it will be needed.
+ if policy.NeedsUserKeyring() && dropCachesFlag.Value && !util.IsUserRoot() {
+ return newExitError(c, ErrDropCachesPerm)
+ }
+
+ if err = policy.Deprovision(); err != nil {
+ return newExitError(c, err)
+ }
+
+ if policy.NeedsUserKeyring() {
+ if err = dropCachesIfRequested(c, ctx); err != nil {
+ return newExitError(c, err)
+ }
+ }
+
+ fmt.Fprintf(c.App.Writer, "%q is now locked.\n", path)
+ return nil
+}
+
// Purge removes all the policy keys from the keyring (also need unmount).
var Purge = cli.Command{
Name: "purge",
@@ -401,13 +483,8 @@ func purgeAction(c *cli.Context) error {
}
fmt.Fprintf(c.App.Writer, "Policies purged for %q.\n", ctx.Mount.Path)
- if dropCachesFlag.Value {
- if err = security.DropFilesystemCache(); err != nil {
- return newExitError(c, err)
- }
- fmt.Fprintf(c.App.Writer, "Encrypted data removed from filesystem cache.\n")
- } else {
- fmt.Fprintf(c.App.Writer, "Filesystem %q should now be unmounted.\n", ctx.Mount.Path)
+ if err = dropCachesIfRequested(c, ctx); err != nil {
+ return newExitError(c, err)
}
return nil
}