From d0ac36dcea341ff000aca983dd80e7bef9fc30ec Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 15 Dec 2019 19:31:39 -0800 Subject: pam_fscrypt: update to handle filesystem keyring FS_IOC_ADD_ENCRYPTION_KEY and FS_IOC_REMOVE_ENCRYPTION_KEY require root for v1 policy keys, so update the PAM module to re-acquire root privileges while provisioning/deprovisioning policies that need this. Also, only set up the user keyring if it will actually be used. --- pam_fscrypt/pam_fscrypt.go | 71 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 5 deletions(-) (limited to 'pam_fscrypt') diff --git a/pam_fscrypt/pam_fscrypt.go b/pam_fscrypt/pam_fscrypt.go index c7f9931..b3c7a0e 100644 --- a/pam_fscrypt/pam_fscrypt.go +++ b/pam_fscrypt/pam_fscrypt.go @@ -36,6 +36,7 @@ import ( "github.com/google/fscrypt/actions" "github.com/google/fscrypt/crypto" + "github.com/google/fscrypt/keyring" "github.com/google/fscrypt/pam" "github.com/google/fscrypt/security" ) @@ -81,6 +82,43 @@ func Authenticate(handle *pam.Handle, _ map[string]bool) error { return errors.Wrap(err, "could not set AUTHTOK data") } +func beginProvisioningOp(handle *pam.Handle, policy *actions.Policy) error { + if policy.NeedsRootToProvision() { + return handle.StopAsPamUser() + } + return nil +} + +func endProvisioningOp(handle *pam.Handle, policy *actions.Policy) error { + if policy.NeedsRootToProvision() { + return handle.StartAsPamUser() + } + return nil +} + +// Set up the PAM user's keyring if needed by any encryption policies. +func setupUserKeyringIfNeeded(handle *pam.Handle, policies []*actions.Policy) error { + needed := false + for _, policy := range policies { + if policy.NeedsUserKeyring() { + needed = true + break + } + } + if !needed { + return nil + } + err := handle.StopAsPamUser() + if err != nil { + return err + } + _, err = keyring.UserKeyringID(handle.PamUser, true) + if err != nil { + log.Printf("Setting up keyrings in PAM: %v", err) + } + return handle.StartAsPamUser() +} + // OpenSession provisions any policies protected with the login protector. func OpenSession(handle *pam.Handle, _ map[string]bool) error { // We will always clear the AUTHTOK data @@ -107,6 +145,10 @@ func OpenSession(handle *pam.Handle, _ map[string]bool) error { return nil } + if err = setupUserKeyringIfNeeded(handle, policies); err != nil { + return errors.Wrapf(err, "setting up user keyring") + } + log.Printf("unlocking %d policies protected with AUTHTOK", len(policies)) keyFn := func(_ actions.ProtectorInfo, retry bool) (*crypto.Key, error) { if retry { @@ -144,8 +186,15 @@ func OpenSession(handle *pam.Handle, _ map[string]bool) error { } defer policy.Lock() - if err := policy.Provision(); err != nil { - log.Printf("provisioning policy %s: %s", policy.Descriptor(), err) + if err := beginProvisioningOp(handle, policy); err != nil { + return err + } + provisionErr := policy.Provision() + if err := endProvisioningOp(handle, policy); err != nil { + return err + } + if provisionErr != nil { + log.Printf("provisioning policy %s: %s", policy.Descriptor(), provisionErr) continue } log.Printf("policy %s provisioned", policy.Descriptor()) @@ -163,7 +212,8 @@ func CloseSession(handle *pam.Handle, args map[string]bool) error { } var errLock, errCache error - // Don't automatically drop privileges, we may need them to drop caches. + // Don't automatically drop privileges, since we may need them to + // deprovision policies or to drop caches. if args[lockFlag] { log.Print("locking polices protected with login protector") errLock = lockLoginPolicies(handle) @@ -200,14 +250,25 @@ func lockLoginPolicies(handle *pam.Handle) error { return nil } + if err = setupUserKeyringIfNeeded(handle, policies); err != nil { + return errors.Wrapf(err, "setting up user keyring") + } + // We will try to deprovision all of the policies. for _, policy := range policies { if !policy.IsProvisioned() { log.Printf("policy %s not provisioned", policy.Descriptor()) continue } - if err := policy.Deprovision(); err != nil { - log.Printf("deprovisioning policy %s: %s", policy.Descriptor(), err) + if err := beginProvisioningOp(handle, policy); err != nil { + return err + } + deprovisionErr := policy.Deprovision() + if err := endProvisioningOp(handle, policy); err != nil { + return err + } + if deprovisionErr != nil { + log.Printf("deprovisioning policy %s: %s", policy.Descriptor(), deprovisionErr) continue } log.Printf("policy %s deprovisioned", policy.Descriptor()) -- cgit v1.2.3 From 42e0dfe85ec7a75a2fa30c417d57eae60b5a881d Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 15 Dec 2019 19:31:39 -0800 Subject: Keyring support for v2 encryption policies Implement adding/removing v2 encryption policy keys to/from the kernel. The kernel requires that the new ioctls FS_IOC_ADD_ENCRYPTION_KEY and FS_IOC_REMOVE_ENCRYPTION_KEY be used for this. Root is not required. However, non-root support brings an extra complication: the kernel keeps track of which users have called FS_IOC_ADD_ENCRYPTION_KEY for the same key. FS_IOC_REMOVE_ENCRYPTION_KEY only works as one of these users, and it only removes the calling user's claim to the key; the key is only truly removed when the last claim is removed. Implement the following behavior: - 'fscrypt unlock' and pam_fscrypt add the key for the user, even if other user(s) have it added already. This behavior is needed so that another user can't remove the key out from under the user. - 'fscrypt lock' and pam_fscrypt remove the key for the user. However, if the key wasn't truly removed because other users still have it added, 'fscrypt lock' prints a warning. - 'fscrypt status' shows whether the directory is unlocked for anyone. --- pam_fscrypt/pam_fscrypt.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'pam_fscrypt') diff --git a/pam_fscrypt/pam_fscrypt.go b/pam_fscrypt/pam_fscrypt.go index b3c7a0e..5f573e3 100644 --- a/pam_fscrypt/pam_fscrypt.go +++ b/pam_fscrypt/pam_fscrypt.go @@ -176,8 +176,9 @@ func OpenSession(handle *pam.Handle, _ map[string]bool) error { // We don't stop provisioning polices on error, we try all of them. for _, policy := range policies { - if policy.IsProvisioned() { - log.Printf("policy %s already provisioned", policy.Descriptor()) + if policy.IsProvisionedByTargetUser() { + log.Printf("policy %s already provisioned by %v", + policy.Descriptor(), handle.PamUser.Username) continue } if err := policy.UnlockWithProtector(protector); err != nil { @@ -197,7 +198,8 @@ func OpenSession(handle *pam.Handle, _ map[string]bool) error { log.Printf("provisioning policy %s: %s", policy.Descriptor(), provisionErr) continue } - log.Printf("policy %s provisioned", policy.Descriptor()) + log.Printf("policy %s provisioned by %v", policy.Descriptor(), + handle.PamUser.Username) } return nil } @@ -256,8 +258,9 @@ func lockLoginPolicies(handle *pam.Handle) error { // We will try to deprovision all of the policies. for _, policy := range policies { - if !policy.IsProvisioned() { - log.Printf("policy %s not provisioned", policy.Descriptor()) + if !policy.IsProvisionedByTargetUser() { + log.Printf("policy %s not provisioned by %v", + policy.Descriptor(), handle.PamUser.Username) continue } if err := beginProvisioningOp(handle, policy); err != nil { @@ -271,7 +274,7 @@ func lockLoginPolicies(handle *pam.Handle) error { log.Printf("deprovisioning policy %s: %s", policy.Descriptor(), deprovisionErr) continue } - log.Printf("policy %s deprovisioned", policy.Descriptor()) + log.Printf("policy %s deprovisioned by %v", policy.Descriptor(), handle.PamUser.Username) } return nil } -- cgit v1.2.3 From 068879664efd8a0f983cbc3e8115571047fe9edd Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 15 Dec 2019 19:31:39 -0800 Subject: cmd/fscrypt, keyring: add --all-users option to 'fscrypt lock' Allow root to provide the --all-users option to 'fscrypt lock' to force an encryption key to be removed from the filesystem (i.e., force an encrypted directory to be locked), even if other users have added it. To implement this option, we just need to use the FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS ioctl rather than FS_IOC_REMOVE_ENCRYPTION_KEY. In theory this option could be implemented for the user keyrings case too, but it would be difficult and the user keyrings are being deprecated for fscrypt, so don't bother. --- pam_fscrypt/pam_fscrypt.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pam_fscrypt') 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 } -- cgit v1.2.3