aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--actions/policy.go6
-rw-r--r--cmd/fscrypt/commands.go8
-rw-r--r--cmd/fscrypt/errors.go3
-rw-r--r--cmd/fscrypt/flags.go10
-rw-r--r--keyring/fs_keyring.go29
-rw-r--r--keyring/keyring.go8
-rw-r--r--keyring/keyring_test.go50
-rw-r--r--pam_fscrypt/pam_fscrypt.go2
8 files changed, 82 insertions, 34 deletions
diff --git a/actions/policy.go b/actions/policy.go
index f448620..41e108e 100644
--- a/actions/policy.go
+++ b/actions/policy.go
@@ -56,7 +56,7 @@ func PurgeAllPolicies(ctx *Context) error {
}
for _, policyDescriptor := range policies {
- err = keyring.RemoveEncryptionKey(policyDescriptor, ctx.getKeyringOptions())
+ err = keyring.RemoveEncryptionKey(policyDescriptor, ctx.getKeyringOptions(), false)
switch errors.Cause(err) {
case nil, keyring.ErrKeyNotPresent:
// We don't care if the key has already been removed
@@ -416,9 +416,9 @@ func (policy *Policy) Provision() error {
// Deprovision removes the Policy key from the kernel keyring. This prevents
// reading and writing to the directory --- unless the target keyring is a user
// keyring, in which case caches must be dropped too.
-func (policy *Policy) Deprovision() error {
+func (policy *Policy) Deprovision(allUsers bool) error {
return keyring.RemoveEncryptionKey(policy.Descriptor(),
- policy.Context.getKeyringOptions())
+ policy.Context.getKeyringOptions(), allUsers)
}
// NeedsUserKeyring returns true if Provision and Deprovision for this policy
diff --git a/cmd/fscrypt/commands.go b/cmd/fscrypt/commands.go
index 0bf0a4c..41009b0 100644
--- a/cmd/fscrypt/commands.go
+++ b/cmd/fscrypt/commands.go
@@ -232,7 +232,7 @@ func encryptPath(path string) (err error) {
defer func() {
policy.Lock()
if err != nil {
- policy.Deprovision()
+ policy.Deprovision(false)
policy.Revert()
}
}()
@@ -248,7 +248,7 @@ func encryptPath(path string) (err error) {
return
}
if skipUnlockFlag.Value {
- defer policy.Deprovision()
+ defer policy.Deprovision(false)
}
}
if err = policy.Apply(path); os.IsPermission(errors.Cause(err)) {
@@ -426,7 +426,7 @@ var Lock = cli.Command{
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},
+ Flags: []cli.Flag{dropCachesFlag, userFlag, allUsersFlag},
Action: lockAction,
}
@@ -465,7 +465,7 @@ func lockAction(c *cli.Context) error {
return newExitError(c, ErrDropCachesPerm)
}
- if err = policy.Deprovision(); err != nil {
+ if err = policy.Deprovision(allUsersFlag.Value); err != nil {
return newExitError(c, err)
}
diff --git a/cmd/fscrypt/errors.go b/cmd/fscrypt/errors.go
index ba9ec7a..5239155 100644
--- a/cmd/fscrypt/errors.go
+++ b/cmd/fscrypt/errors.go
@@ -103,7 +103,8 @@ func getErrorSuggestions(err error) string {
re-running 'fscrypt lock'.`
case keyring.ErrKeyAddedByOtherUsers:
return `Directory couldn't be fully locked because other user(s)
- have unlocked it.`
+ have unlocked it. If you want to force the directory to
+ be locked, use 'sudo fscrypt lock --all-users DIR'.`
case keyring.ErrSessionUserKeying:
return `This is usually the result of a bad PAM configuration.
Either correct the problem in your PAM stack, enable
diff --git a/cmd/fscrypt/flags.go b/cmd/fscrypt/flags.go
index a22ec05..b7933c9 100644
--- a/cmd/fscrypt/flags.go
+++ b/cmd/fscrypt/flags.go
@@ -116,7 +116,7 @@ var (
allFlags = []prettyFlag{helpFlag, versionFlag, verboseFlag, quietFlag,
forceFlag, legacyFlag, skipUnlockFlag, timeTargetFlag,
sourceFlag, nameFlag, keyFileFlag, protectorFlag,
- unlockWithFlag, policyFlag}
+ unlockWithFlag, policyFlag, allUsersFlag}
// universalFlags contains flags that should be on every command
universalFlags = []cli.Flag{verboseFlag, quietFlag, helpFlag}
)
@@ -170,6 +170,14 @@ var (
privileges.`,
Default: true,
}
+ allUsersFlag = &boolFlag{
+ Name: "all-users",
+ Usage: `Lock the directory no matter which user(s) have unlocked
+ it. Requires root privileges. This flag is only
+ necessary if the directory was unlocked by a user
+ different from the one you're locking it as. This flag
+ is only implemented for v2 encryption policies.`,
+ }
)
// Option flags: used to specify options instead of being prompted for them
diff --git a/keyring/fs_keyring.go b/keyring/fs_keyring.go
index 970105e..42c1648 100644
--- a/keyring/fs_keyring.go
+++ b/keyring/fs_keyring.go
@@ -228,16 +228,23 @@ func fsRemoveEncryptionKey(descriptor string, mount *filesystem.Mount,
return err
}
- savedPrivs, err := dropPrivsIfNeeded(user, &arg.Key_spec)
- if err != nil {
- return err
+ ioc := unix.FS_IOC_REMOVE_ENCRYPTION_KEY
+ iocName := "FS_IOC_REMOVE_ENCRYPTION_KEY"
+ var savedPrivs *savedPrivs
+ if user == nil {
+ ioc = unix.FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS
+ iocName = "FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS"
+ } else {
+ savedPrivs, err = dropPrivsIfNeeded(user, &arg.Key_spec)
+ if err != nil {
+ return err
+ }
}
- _, _, errno := unix.Syscall(unix.SYS_IOCTL, dir.Fd(),
- unix.FS_IOC_REMOVE_ENCRYPTION_KEY, uintptr(unsafe.Pointer(&arg)))
+ _, _, errno := unix.Syscall(unix.SYS_IOCTL, dir.Fd(), uintptr(ioc), uintptr(unsafe.Pointer(&arg)))
restorePrivs(savedPrivs)
- log.Printf("FS_IOC_REMOVE_ENCRYPTION_KEY(%q, %s) = %v, removal_status_flags=0x%x",
- mount.Path, descriptor, errno, arg.Removal_status_flags)
+ log.Printf("%s(%q, %s) = %v, removal_status_flags=0x%x",
+ iocName, mount.Path, descriptor, errno, arg.Removal_status_flags)
switch errno {
case 0:
switch {
@@ -251,9 +258,11 @@ func fsRemoveEncryptionKey(descriptor string, mount *filesystem.Mount,
// ENOKEY means either the key is completely missing or that the
// current user doesn't have a claim to it. Distinguish between
// these two cases by getting the key status.
- status, _ := fsGetEncryptionKeyStatus(descriptor, mount, user)
- if status == KeyPresentButOnlyOtherUsers {
- return ErrKeyAddedByOtherUsers
+ if user != nil {
+ status, _ := fsGetEncryptionKeyStatus(descriptor, mount, user)
+ if status == KeyPresentButOnlyOtherUsers {
+ return ErrKeyAddedByOtherUsers
+ }
}
return ErrKeyNotPresent
default:
diff --git a/keyring/keyring.go b/keyring/keyring.go
index 925d059..5a75153 100644
--- a/keyring/keyring.go
+++ b/keyring/keyring.go
@@ -100,9 +100,13 @@ func AddEncryptionKey(key *crypto.Key, descriptor string, options *Options) erro
// RemoveEncryptionKey removes an encryption policy key from a kernel keyring.
// It uses either the filesystem keyring for the target Mount or the user
// keyring for the target User.
-func RemoveEncryptionKey(descriptor string, options *Options) error {
+func RemoveEncryptionKey(descriptor string, options *Options, allUsers bool) error {
if shouldUseFsKeyring(descriptor, options) {
- return fsRemoveEncryptionKey(descriptor, options.Mount, options.User)
+ user := options.User
+ if allUsers {
+ user = nil
+ }
+ return fsRemoveEncryptionKey(descriptor, options.Mount, user)
}
return userRemoveKey(options.Service+descriptor, options.User)
}
diff --git a/keyring/keyring_test.go b/keyring/keyring_test.go
index a675a70..8912556 100644
--- a/keyring/keyring_test.go
+++ b/keyring/keyring_test.go
@@ -139,11 +139,11 @@ func testAddAndRemoveKey(t *testing.T, descriptor string, options *Options) {
t.Error(err)
}
assertKeyStatus(t, descriptor, options, KeyPresent)
- if err := RemoveEncryptionKey(descriptor, options); err != nil {
+ if err := RemoveEncryptionKey(descriptor, options, false); err != nil {
t.Error(err)
}
assertKeyStatus(t, descriptor, options, KeyAbsent)
- err := RemoveEncryptionKey(descriptor, options)
+ err := RemoveEncryptionKey(descriptor, options, false)
if err != ErrKeyNotPresent {
t.Error(err)
}
@@ -155,12 +155,12 @@ func testAddAndRemoveKey(t *testing.T, descriptor string, options *Options) {
if err := AddEncryptionKey(fakeValidPolicyKey, descriptor, options); err != nil {
t.Error("AddEncryptionKey should not fail if key already exists")
}
- RemoveEncryptionKey(descriptor, options)
+ RemoveEncryptionKey(descriptor, options, false)
assertKeyStatus(t, descriptor, options, KeyAbsent)
// Adding a key with wrong length should fail
if err := AddEncryptionKey(fakeInvalidPolicyKey, descriptor, options); err == nil {
- RemoveEncryptionKey(descriptor, options)
+ RemoveEncryptionKey(descriptor, options, false)
t.Error("AddEncryptionKey should fail with wrong-length key")
}
assertKeyStatus(t, descriptor, options, KeyAbsent)
@@ -227,14 +227,14 @@ func TestV2PolicyKeyCannotBeRemovedByAnotherUser(t *testing.T) {
assertKeyStatus(t, fakeV2Descriptor, rootOptions, KeyPresentButOnlyOtherUsers)
// Key shouldn't be removable by another user, even root.
- err := RemoveEncryptionKey(fakeV2Descriptor, user2Options)
+ err := RemoveEncryptionKey(fakeV2Descriptor, user2Options, false)
if err != ErrKeyAddedByOtherUsers {
t.Error(err)
}
assertKeyStatus(t, fakeV2Descriptor, user1Options, KeyPresent)
assertKeyStatus(t, fakeV2Descriptor, user2Options, KeyPresentButOnlyOtherUsers)
assertKeyStatus(t, fakeV2Descriptor, rootOptions, KeyPresentButOnlyOtherUsers)
- err = RemoveEncryptionKey(fakeV2Descriptor, rootOptions)
+ err = RemoveEncryptionKey(fakeV2Descriptor, rootOptions, false)
if err != ErrKeyAddedByOtherUsers {
t.Error(err)
}
@@ -242,7 +242,7 @@ func TestV2PolicyKeyCannotBeRemovedByAnotherUser(t *testing.T) {
assertKeyStatus(t, fakeV2Descriptor, user2Options, KeyPresentButOnlyOtherUsers)
assertKeyStatus(t, fakeV2Descriptor, rootOptions, KeyPresentButOnlyOtherUsers)
- if err := RemoveEncryptionKey(fakeV2Descriptor, user1Options); err != nil {
+ if err := RemoveEncryptionKey(fakeV2Descriptor, user1Options, false); err != nil {
t.Error(err)
}
assertKeyStatus(t, fakeV2Descriptor, user1Options, KeyAbsent)
@@ -267,7 +267,7 @@ func TestV2PolicyKeyMultipleUsers(t *testing.T) {
assertKeyStatus(t, fakeV2Descriptor, rootOptions, KeyPresentButOnlyOtherUsers)
// Remove key as one user.
- err := RemoveEncryptionKey(fakeV2Descriptor, user1Options)
+ err := RemoveEncryptionKey(fakeV2Descriptor, user1Options, false)
if err != ErrKeyAddedByOtherUsers {
t.Error(err)
}
@@ -276,7 +276,7 @@ func TestV2PolicyKeyMultipleUsers(t *testing.T) {
assertKeyStatus(t, fakeV2Descriptor, rootOptions, KeyPresentButOnlyOtherUsers)
// Remove key as the other user.
- err = RemoveEncryptionKey(fakeV2Descriptor, user2Options)
+ err = RemoveEncryptionKey(fakeV2Descriptor, user2Options, false)
if err != nil {
t.Error(err)
}
@@ -296,7 +296,7 @@ func TestV2PolicyKeyWrongDescriptor(t *testing.T) {
for _, desc := range wrongV2Descriptors {
if err := AddEncryptionKey(fakeValidPolicyKey, desc, options); err == nil {
- RemoveEncryptionKey(desc, options)
+ RemoveEncryptionKey(desc, options, false)
t.Error("For v2 policy keys, AddEncryptionKey should fail if the descriptor is wrong")
}
}
@@ -308,10 +308,10 @@ func TestV2PolicyKeyBadMount(t *testing.T) {
User: testUser,
}
if err := AddEncryptionKey(fakeValidPolicyKey, fakeV2Descriptor, options); err == nil {
- RemoveEncryptionKey(fakeV2Descriptor, options)
+ RemoveEncryptionKey(fakeV2Descriptor, options, false)
t.Error("AddEncryptionKey should have failed with bad mount!")
}
- if err := RemoveEncryptionKey(fakeV2Descriptor, options); err == nil {
+ if err := RemoveEncryptionKey(fakeV2Descriptor, options, false); err == nil {
t.Error("RemoveEncryptionKey should have failed with bad mount!")
}
status, err := GetEncryptionKeyStatus(fakeV2Descriptor, options)
@@ -322,3 +322,29 @@ func TestV2PolicyKeyBadMount(t *testing.T) {
t.Error("GetEncryptionKeyStatus should have returned unknown status!")
}
}
+
+func TestV2PolicyKeyRemoveForAllUsers(t *testing.T) {
+ rootOptions, userOptions := getOptionsForFsKeyringUsers(t, 2)
+ user1Options := userOptions[0]
+ user2Options := userOptions[1]
+
+ // Add key as two non-root users.
+ if err := AddEncryptionKey(fakeValidPolicyKey, fakeV2Descriptor, user1Options); err != nil {
+ t.Error(err)
+ }
+ if err := AddEncryptionKey(fakeValidPolicyKey, fakeV2Descriptor, user2Options); err != nil {
+ t.Error(err)
+ }
+ assertKeyStatus(t, fakeV2Descriptor, user1Options, KeyPresent)
+ assertKeyStatus(t, fakeV2Descriptor, user2Options, KeyPresent)
+ assertKeyStatus(t, fakeV2Descriptor, rootOptions, KeyPresentButOnlyOtherUsers)
+
+ // Remove key for all users as root.
+ err := RemoveEncryptionKey(fakeV2Descriptor, rootOptions, true)
+ if err != nil {
+ t.Error(err)
+ }
+ assertKeyStatus(t, fakeV2Descriptor, user1Options, KeyAbsent)
+ assertKeyStatus(t, fakeV2Descriptor, user2Options, KeyAbsent)
+ assertKeyStatus(t, fakeV2Descriptor, rootOptions, KeyAbsent)
+}
diff --git a/pam_fscrypt/pam_fscrypt.go b/pam_fscrypt/pam_fscrypt.go
index 5f573e3..a7582cc 100644
--- a/pam_fscrypt/pam_fscrypt.go
+++ b/pam_fscrypt/pam_fscrypt.go
@@ -266,7 +266,7 @@ func lockLoginPolicies(handle *pam.Handle) error {
if err := beginProvisioningOp(handle, policy); err != nil {
return err
}
- deprovisionErr := policy.Deprovision()
+ deprovisionErr := policy.Deprovision(false)
if err := endProvisioningOp(handle, policy); err != nil {
return err
}