Skip to content

Commit

Permalink
Factor prompting for secrets into Prompter
Browse files Browse the repository at this point in the history
so that it can be passed to KeyUnsealer, and be passed from outside
later to possibly match the passed KeyUnsealer as well
  • Loading branch information
pedronis committed Sep 21, 2020
1 parent 6a5392b commit 40263fb
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 15 deletions.
50 changes: 39 additions & 11 deletions crypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,35 @@ func activate(volumeName, sourceDevicePath string, key []byte, options []string)
return wrapExecError(cmd, cmd.Wait())
}

// A Prompter can be used to prompt the user for secrets necessary
// to unlock a disk.
type Prompter interface {
PromptFor2FA(sourceDevicePath, description string) (string, error)

PromptForRecoveryKey(sourceDevicePath string) (string, error)
}

// SystemPrompter implements Prompter reading once if set from the
// dedicated io.Reader or otherwise using systemd-ask-password.
type SystemPrompter struct {
ReaderFor2FA io.Reader
ReaderForRecoveryKey io.Reader
}

func (p *SystemPrompter) PromptFor2FA(sourceDevicePath, description string) (string, error) {
// consumed once
r := p.ReaderFor2FA
p.ReaderFor2FA = nil
return getPassword(sourceDevicePath, description, r)
}

func (p *SystemPrompter) PromptForRecoveryKey(sourceDevicePath string) (string, error) {
// consumed once
r := p.ReaderForRecoveryKey
p.ReaderForRecoveryKey = nil
return getPassword(sourceDevicePath, "recovery key", r)
}

func askPassword(sourceDevicePath, msg string) (string, error) {
cmd := exec.Command(
"systemd-ask-password",
Expand Down Expand Up @@ -280,7 +309,7 @@ const (
RecoveryKeyUsageReasonPINFail
)

func activateWithRecoveryKey(volumeName, sourceDevicePath string, keyReader io.Reader, tries int, reason RecoveryKeyUsageReason, activateOptions []string, keyringPrefix string) error {
func activateWithRecoveryKey(volumeName, sourceDevicePath string, p Prompter, tries int, reason RecoveryKeyUsageReason, activateOptions []string, keyringPrefix string) error {
if tries == 0 {
return errors.New("no recovery key tries permitted")
}
Expand All @@ -290,10 +319,7 @@ func activateWithRecoveryKey(volumeName, sourceDevicePath string, keyReader io.R
for ; tries > 0; tries-- {
lastErr = nil

r := keyReader
keyReader = nil

passphrase, err := getPassword(sourceDevicePath, "recovery key", r)
passphrase, err := p.PromptForRecoveryKey(sourceDevicePath)
if err != nil {
return xerrors.Errorf("cannot obtain recovery key: %w", err)
}
Expand Down Expand Up @@ -369,11 +395,11 @@ type ActivateWithTPMSealedKeyOptions struct {
}

type KeyUnsealer interface {
UnsealKey(volumeName, sourceDevicePath string) (key, resealToken []byte, err error)
UnsealKey(volumeName, sourceDevicePath string, p Prompter) (key, resealToken []byte, err error)
UnderstoodError(e error, isCryptsetupError bool) (ok bool, reason RecoveryKeyUsageReason, err error)
}

func ActivateVolumeWithKeyUnsealer(volumeName, sourceDevicePath string, unsealer KeyUnsealer, options *ActivateWithTPMSealedKeyOptions) (bool, error) {
func ActivateVolumeWithKeyUnsealer(volumeName, sourceDevicePath string, unsealer KeyUnsealer, p Prompter, options *ActivateWithTPMSealedKeyOptions) (bool, error) {
if options.RecoveryKeyTries < 0 {
return false, errors.New("invalid RecoveryKeyTries")
}
Expand All @@ -383,7 +409,7 @@ func ActivateVolumeWithKeyUnsealer(volumeName, sourceDevicePath string, unsealer
return false, err
}

key, resealToken, err := unsealer.UnsealKey(volumeName, sourceDevicePath)
key, resealToken, err := unsealer.UnsealKey(volumeName, sourceDevicePath, p)
if err == nil {
err = activate(volumeName, sourceDevicePath, key, activateOptions)
if err != nil {
Expand All @@ -399,7 +425,7 @@ func ActivateVolumeWithKeyUnsealer(volumeName, sourceDevicePath string, unsealer
if !ok {
reason = RecoveryKeyUsageReasonUnexpectedError
}
rErr := activateWithRecoveryKey(volumeName, sourceDevicePath, nil, options.RecoveryKeyTries, reason, activateOptions, options.KeyringPrefix)
rErr := activateWithRecoveryKey(volumeName, sourceDevicePath, p, options.RecoveryKeyTries, reason, activateOptions, options.KeyringPrefix)
return rErr == nil, &ActivateWithTPMSealedKeyError{err, rErr}
}

Expand Down Expand Up @@ -464,7 +490,8 @@ func ActivateVolumeWithTPMSealedKey(tpm *TPMConnection, volumeName, sourceDevice
return false, err
}

return ActivateVolumeWithKeyUnsealer(volumeName, sourceDevicePath, keyUnsealer, options)
p := &SystemPrompter{ReaderFor2FA: pinReader}
return ActivateVolumeWithKeyUnsealer(volumeName, sourceDevicePath, keyUnsealer, p, options)
}

// ActivateWithRecoveryKeyOptions provides options to ActivateVolumeWithRecoveryKey.
Expand Down Expand Up @@ -504,7 +531,8 @@ func ActivateVolumeWithRecoveryKey(volumeName, sourceDevicePath string, keyReade
return err
}

return activateWithRecoveryKey(volumeName, sourceDevicePath, keyReader, options.Tries, RecoveryKeyUsageReasonRequested, activateOptions, options.KeyringPrefix)
p := &SystemPrompter{ReaderForRecoveryKey: keyReader}
return activateWithRecoveryKey(volumeName, sourceDevicePath, p, options.Tries, RecoveryKeyUsageReasonRequested, activateOptions, options.KeyringPrefix)
}

// ActivationData corresponds to some data added to the user keyring by one of the ActivateVolume functions.
Expand Down
6 changes: 2 additions & 4 deletions crypt_tpm.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func NewTPMKeyUnsealer(tpm *TPMConnection, keyPath string, pinReader io.Reader,
}, nil
}

func (u *TPMKeyUnsealer) UnsealKey(volumeName, sourceDevicePath string) (key, resealAuthKey []byte, err error) {
func (u *TPMKeyUnsealer) UnsealKey(volumeName, sourceDevicePath string, p Prompter) (key, resealAuthKey []byte, err error) {
var lockErr error
tpm := u.tpm
sealedKey, authPrivateKey, err := func() ([]byte, TPMPolicyAuthKey, error) {
Expand Down Expand Up @@ -78,9 +78,7 @@ func (u *TPMKeyUnsealer) UnsealKey(volumeName, sourceDevicePath string) (key, re
for ; pinTries > 0; pinTries-- {
var pin string
if k.AuthMode2F() == AuthModePIN {
r := u.pinReader
u.pinReader = nil
pin, err = getPassword(sourceDevicePath, "PIN", r)
pin, err = p.PromptFor2FA(sourceDevicePath, "PIN")
if err != nil {
return nil, nil, xerrors.Errorf("cannot obtain PIN: %w", err)
}
Expand Down

0 comments on commit 40263fb

Please sign in to comment.