From d0ac36dcea341ff000aca983dd80e7bef9fc30ec Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 15 Dec 2019 19:31:39 -0800 Subject: [PATCH] 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. --- actions/policy.go | 6 ++++ pam/pam.go | 20 ++++++----- pam_fscrypt/pam_fscrypt.go | 71 +++++++++++++++++++++++++++++++++++--- 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()) -- 2.39.5