aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--actions/protector.go10
-rw-r--r--keyring/user_keyring.go75
-rw-r--r--pam_fscrypt/pam_fscrypt.go61
3 files changed, 114 insertions, 32 deletions
diff --git a/actions/protector.go b/actions/protector.go
index 3278e63..7d51f79 100644
--- a/actions/protector.go
+++ b/actions/protector.go
@@ -260,6 +260,16 @@ func (protector *Protector) Unlock(keyFn KeyFunc) (err error) {
return
}
+// RawKey returns the internal key of an unlocked protector.
+func (protector *Protector) InternalKey() *crypto.Key {
+ return protector.key
+}
+
+// UnlockFromRawKey unlocks the protector directly from the internal key.
+func (protector *Protector) UnlockFromInternalKey(key *crypto.Key) {
+ protector.key = key
+}
+
// Lock wipes a Protector's internal Key. It should always be called after using
// an unlocked Protector. This is often done with a defer statement. There is
// no effect if called multiple times.
diff --git a/keyring/user_keyring.go b/keyring/user_keyring.go
index 0ea4689..416872f 100644
--- a/keyring/user_keyring.go
+++ b/keyring/user_keyring.go
@@ -32,6 +32,7 @@ import (
"log"
"github.com/google/fscrypt/crypto"
+ "github.com/google/fscrypt/metadata"
"github.com/google/fscrypt/security"
"github.com/google/fscrypt/util"
)
@@ -181,6 +182,80 @@ func UserKeyringID(targetUser *user.User, checkSession bool) (int, error) {
return targetKeyring, nil
}
+func protectorKeyDescription(user *user.User) string {
+ return fmt.Sprintf("fscrypt-protector-key.uid.%s", user.Uid)
+}
+
+func SaveProtectorKey(key *crypto.Key, user *user.User) error {
+ runtime.LockOSThread() // ensure the thread keyring doesn't change
+ defer runtime.UnlockOSThread()
+
+ keyringID, err := userKeyringIDLookup(0)
+ if err != nil {
+ return errors.Wrap(err, "could not find root user keyring")
+ }
+
+ description := protectorKeyDescription(user)
+ _, err = unix.AddKey("user", description, key.Data(), keyringID)
+ if err != nil {
+ return err
+ }
+ log.Printf("saved protector key for %s", user.Username)
+ return nil
+}
+
+func RestoreProtectorKey(user *user.User) (*crypto.Key, error) {
+ runtime.LockOSThread() // ensure the thread keyring doesn't change
+ defer runtime.UnlockOSThread()
+
+ keyringID, err := userKeyringIDLookup(0)
+ if err != nil {
+ return nil, errors.Wrap(err, "could not find root user keyring")
+ }
+
+ description := protectorKeyDescription(user)
+ keyID, err := unix.KeyctlSearch(keyringID, "user", description, 0)
+ if err != nil {
+ return nil, errors.Wrap(err, "could not find saved protector key")
+ }
+
+ key, err := crypto.NewBlankKey(metadata.InternalKeyLen)
+ if err != nil {
+ return nil, err
+ }
+ ret, err := unix.KeyctlBuffer(unix.KEYCTL_READ, keyID, key.Data(), key.Len())
+ if err != nil {
+ return nil, errors.Wrap(err, "could not read saved protector key")
+ }
+ if ret != metadata.InternalKeyLen {
+ return nil, errors.New("bad saved protector key")
+ }
+ log.Printf("loaded saved protector key for %s", user.Username)
+ return key, nil
+}
+
+func DeleteSavedProtectorKey(user *user.User) error {
+ runtime.LockOSThread() // ensure the thread keyring doesn't change
+ defer runtime.UnlockOSThread()
+
+ keyringID, err := userKeyringIDLookup(0)
+ if err != nil {
+ return nil
+ }
+
+ description := protectorKeyDescription(user)
+ keyID, err := unix.KeyctlSearch(keyringID, "user", description, 0)
+ if err != nil {
+ return nil
+ }
+
+ if _, err := unix.KeyctlInt(unix.KEYCTL_UNLINK, keyID, keyringID, 0, 0); err != nil {
+ return errors.Wrap(err, "could not delete saved protector key")
+ }
+ log.Printf("deleted saved protector key for %s", user.Username)
+ return nil
+}
+
func userKeyringIDLookup(uid int) (keyringID int, err error) {
// Our goals here are to:
diff --git a/pam_fscrypt/pam_fscrypt.go b/pam_fscrypt/pam_fscrypt.go
index 2e31af9..7a3f25c 100644
--- a/pam_fscrypt/pam_fscrypt.go
+++ b/pam_fscrypt/pam_fscrypt.go
@@ -43,8 +43,6 @@ import (
const (
moduleName = "pam_fscrypt"
- // authtokLabel tags the AUTHTOK in the PAM data.
- authtokLabel = "fscrypt_authtok"
// These flags are used to toggle behavior of the PAM module.
debugFlag = "debug"
@@ -76,18 +74,31 @@ func Authenticate(handle *pam.Handle, _ map[string]bool) error {
defer handle.StopAsPamUser()
// If this user doesn't have a login protector, no unlocking is needed.
- if _, err := loginProtector(handle); err != nil {
+ protector, err := loginProtector(handle)
+ if err != nil {
log.Printf("no protector, no need for AUTHTOK: %s", err)
return nil
}
- log.Print("copying AUTHTOK for use in the session open")
- authtok, err := handle.GetItem(pam.Authtok)
- if err != nil {
- return errors.Wrap(err, "could not get AUTHTOK")
+ log.Print("unlocking user's login protector")
+ keyFn := func(_ actions.ProtectorInfo, retry bool) (*crypto.Key, error) {
+ if retry {
+ return nil, pam.ErrPassphrase
+ }
+ authtok, err := handle.GetItem(pam.Authtok)
+ if err != nil {
+ return nil, errors.Wrap(err, "could not get AUTHTOK")
+ }
+ return crypto.NewKeyFromCString(authtok)
+ }
+ if err := protector.Unlock(keyFn); err != nil {
+ return errors.Wrap(err, "could not unlock login protector")
}
- err = handle.SetSecret(authtokLabel, authtok)
- return errors.Wrap(err, "could not set AUTHTOK data")
+ handle.StopAsPamUser()
+ if err := keyring.SaveProtectorKey(protector.InternalKey(), handle.PamUser); err != nil {
+ return errors.Wrap(err, "could not save protector key")
+ }
+ return nil
}
func beginProvisioningOp(handle *pam.Handle, policy *actions.Policy) error {
@@ -129,13 +140,16 @@ func setupUserKeyringIfNeeded(handle *pam.Handle, policies []*actions.Policy) er
// 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
- defer handle.ClearData(authtokLabel)
+ // We will always delete the saved protector key
+ defer keyring.DeleteSavedProtectorKey(handle.PamUser)
// Increment the count as we add a session
if _, err := AdjustCount(handle, +1); err != nil {
return err
}
+ protectorKey, protectorKeyErr := keyring.RestoreProtectorKey(handle.PamUser)
+ defer protectorKey.Wipe()
+
if err := handle.StartAsPamUser(); err != nil {
return err
}
@@ -157,29 +171,12 @@ func OpenSession(handle *pam.Handle, _ map[string]bool) error {
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 {
- // Login passphrase and login protector have diverged.
- // We could prompt the user for the old passphrase and
- // rewrap, but we currently don't.
- return nil, pam.ErrPassphrase
- }
+ log.Printf("unlocking %d policies protected by login protector", len(policies))
- authtok, err := handle.GetSecret(authtokLabel)
- if err != nil {
- // pam_sm_authenticate was not run before the session is
- // opened. This can happen when a user does something
- // like "sudo su <user>". We could prompt for the
- // login passphrase here, but we currently don't.
- return nil, errors.Wrap(err, "AUTHTOK data missing")
- }
-
- return crypto.NewKeyFromCString(authtok)
- }
- if err := protector.Unlock(keyFn); err != nil {
- return errors.Wrapf(err, "unlocking protector %s", protector.Descriptor())
+ if protectorKeyErr != nil {
+ return protectorKeyErr
}
+ protector.UnlockFromInternalKey(protectorKey)
defer protector.Lock()
// We don't stop provisioning polices on error, we try all of them.