aboutsummaryrefslogtreecommitdiff
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
commitd0ac36dcea341ff000aca983dd80e7bef9fc30ec (patch)
tree673eef073fd0646bbc16bc088a315d0dfe68dfb7
parent0829eb74863bd279ae012779e52040ecc7f7178e (diff)
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.
-rw-r--r--actions/policy.go6
-rw-r--r--pam/pam.go20
-rw-r--r--pam_fscrypt/pam_fscrypt.go71
3 files changed, 84 insertions, 13 deletions
diff --git a/actions/policy.go b/actions/policy.go
index 2d8c521..b9cd88c 100644
--- a/actions/policy.go
+++ b/actions/policy.go
@@ -412,6 +412,12 @@ func (policy *Policy) NeedsUserKeyring() bool {
return !policy.Context.Config.GetUseFsKeyringForV1Policies()
}
+// NeedsRootToProvision returns true if Provision and Deprovision will require
+// root for this policy in the current configuration.
+func (policy *Policy) NeedsRootToProvision() bool {
+ return policy.Context.Config.GetUseFsKeyringForV1Policies()
+}
+
// commitData writes the Policy's current data to the filesystem.
func (policy *Policy) commitData() error {
return policy.Context.Mount.AddPolicy(policy.data)
diff --git a/pam/pam.go b/pam/pam.go
index ece6bda..54a60e2 100644
--- a/pam/pam.go
+++ b/pam/pam.go
@@ -34,7 +34,6 @@ import (
"os/user"
"unsafe"
- "github.com/google/fscrypt/keyring"
"github.com/google/fscrypt/security"
)
@@ -128,26 +127,31 @@ func (h *Handle) GetItem(i Item) (unsafe.Pointer, error) {
return data, nil
}
-// StartAsPamUser sets the effective privileges to that of the PAM user, and
-// configures the PAM user's keyrings to be properly linked.
+// StartAsPamUser sets the effective privileges to that of the PAM user.
func (h *Handle) StartAsPamUser() error {
- if _, err := keyring.UserKeyringID(h.PamUser, true); err != nil {
- log.Printf("Setting up keyrings in PAM: %v", err)
- }
userPrivs, err := security.UserPrivileges(h.PamUser)
if err != nil {
return err
}
- if h.origPrivs, err = security.ProcessPrivileges(); err != nil {
+ origPrivs, err := security.ProcessPrivileges()
+ if err != nil {
+ return err
+ }
+ if err = security.SetProcessPrivileges(userPrivs); err != nil {
return err
}
- return security.SetProcessPrivileges(userPrivs)
+ h.origPrivs = origPrivs
+ return nil
}
// StopAsPamUser restores the original privileges that were running the
// PAM module (this is usually root).
func (h *Handle) StopAsPamUser() error {
+ if h.origPrivs == nil {
+ return nil
+ }
err := security.SetProcessPrivileges(h.origPrivs)
+ h.origPrivs = nil
if err != nil {
log.Print(err)
}
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())