-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Have a single ActivateVolumeOptions struct for the ActivateVolumeWith* family #116
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -275,9 +275,10 @@ const ( | |||||||||||||||||||||||
// is also not correctly provisioned. | ||||||||||||||||||||||||
RecoveryKeyUsageReasonInvalidKeyFile | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
// RecoveryKeyUsageReasonPINFail indicates that a volume had to be activated with the fallback recovery key because the correct PIN | ||||||||||||||||||||||||
// was not provided. | ||||||||||||||||||||||||
RecoveryKeyUsageReasonPINFail | ||||||||||||||||||||||||
// RecoveryKeyUsageReasonUserPassphraseFail indicates that a | ||||||||||||||||||||||||
// volume had to be activated with the fallback recovery key | ||||||||||||||||||||||||
// because the correct user passphrase/PIN was not provided. | ||||||||||||||||||||||||
RecoveryKeyUsageReasonUserPassphraseFail | ||||||||||||||||||||||||
) | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
func activateWithRecoveryKey(volumeName, sourceDevicePath string, keyReader io.Reader, tries int, reason RecoveryKeyUsageReason, activateOptions []string, keyringPrefix string) error { | ||||||||||||||||||||||||
|
@@ -367,7 +368,7 @@ func isLockAccessError(err error) bool { | |||||||||||||||||||||||
return xerrors.As(err, &e) | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
func activateWithTPMKey(tpm *TPMConnection, volumeName, sourceDevicePath, keyPath string, pinReader io.Reader, pinTries int, lock bool, activateOptions []string, keyringPrefix string) error { | ||||||||||||||||||||||||
func activateWithTPMKey(tpm *TPMConnection, volumeName, sourceDevicePath, keyPath string, passphraseReader io.Reader, passphraseTries int, lock bool, activateOptions []string, keyringPrefix string) error { | ||||||||||||||||||||||||
var lockErr error | ||||||||||||||||||||||||
sealedKey, authPrivateKey, err := func() ([]byte, TPMPolicyAuthKey, error) { | ||||||||||||||||||||||||
defer func() { | ||||||||||||||||||||||||
|
@@ -383,20 +384,20 @@ func activateWithTPMKey(tpm *TPMConnection, volumeName, sourceDevicePath, keyPat | |||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
switch { | ||||||||||||||||||||||||
case pinTries == 0 && k.AuthMode2F() == AuthModePIN: | ||||||||||||||||||||||||
case passphraseTries == 0 && k.AuthMode2F() == AuthModePIN: | ||||||||||||||||||||||||
return nil, nil, requiresPinErr | ||||||||||||||||||||||||
case pinTries == 0: | ||||||||||||||||||||||||
pinTries = 1 | ||||||||||||||||||||||||
case passphraseTries == 0: | ||||||||||||||||||||||||
passphraseTries = 1 | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
var sealedKey []byte | ||||||||||||||||||||||||
var authPrivateKey []byte | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
for ; pinTries > 0; pinTries-- { | ||||||||||||||||||||||||
for ; passphraseTries > 0; passphraseTries-- { | ||||||||||||||||||||||||
var pin string | ||||||||||||||||||||||||
if k.AuthMode2F() == AuthModePIN { | ||||||||||||||||||||||||
r := pinReader | ||||||||||||||||||||||||
pinReader = nil | ||||||||||||||||||||||||
r := passphraseReader | ||||||||||||||||||||||||
passphraseReader = nil | ||||||||||||||||||||||||
pin, err = getPassword(sourceDevicePath, "PIN", r) | ||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||
return nil, nil, xerrors.Errorf("cannot obtain PIN: %w", err) | ||||||||||||||||||||||||
|
@@ -454,40 +455,55 @@ func makeActivateOptions(in []string) ([]string, error) { | |||||||||||||||||||||||
return append(out, "tries=1"), nil | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
// ActivateWithTPMSealedKeyOptions provides options to ActivateVolumeWtthTPMSealedKey. | ||||||||||||||||||||||||
type ActivateWithTPMSealedKeyOptions struct { | ||||||||||||||||||||||||
// PINTries specifies the maximum number of times that unsealing with a PIN should be attempted before failing with an error and | ||||||||||||||||||||||||
// falling back to activating with the recovery key if RecoveryKeyTries is greater than zero. Setting this to zero disables unsealing | ||||||||||||||||||||||||
// with a PIN - in this case, an error will be returned if the sealed key object indicates that a PIN has been set. Attempts to | ||||||||||||||||||||||||
// unseal with a PIN will stop if the TPM enters dictionary attack lockout mode before this limit is reached. | ||||||||||||||||||||||||
PINTries int | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
// RecoveryKeyTries specifies the maximum number of times that activation with the fallback recovery key should be attempted | ||||||||||||||||||||||||
// if activation with the TPM sealed key fails, before failing with an error. Setting this to zero will disable attempts to activate | ||||||||||||||||||||||||
// with the fallback recovery key. | ||||||||||||||||||||||||
// ActivateVolumeOptions provides options to the ActivateVolumeWith* | ||||||||||||||||||||||||
// family of functions. | ||||||||||||||||||||||||
type ActivateVolumeOptions struct { | ||||||||||||||||||||||||
// UserPassphraseTries specifies the maximum number of times | ||||||||||||||||||||||||
// that unsealing with a user passphrase should be attempted | ||||||||||||||||||||||||
// before failing with an error and falling back to activating | ||||||||||||||||||||||||
// with the recovery key if RecoveryKeyTries is greater than | ||||||||||||||||||||||||
// zero. Setting this to zero disables unsealing with a user | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||
// passphrase - in this case, an error will be returned if the | ||||||||||||||||||||||||
// sealed key object indicates that a user passphrase has been | ||||||||||||||||||||||||
// set. With a TPM attempts to unseal with a user passphrase | ||||||||||||||||||||||||
// will stop if the TPM enters dictionary attack lockout mode | ||||||||||||||||||||||||
// before this limit is reached. | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||
// It is unused by ActivateWithRecoveryKey. | ||||||||||||||||||||||||
UserPassphraseTries int | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. UserPassphraseMaxTries? |
||||||||||||||||||||||||
|
||||||||||||||||||||||||
// RecoveryKeyTries specifies the maximum number of times that | ||||||||||||||||||||||||
// activation with the fallback recovery key should be | ||||||||||||||||||||||||
// attempted. It is considered directly by | ||||||||||||||||||||||||
// ActivateWithRecoveryKey and when falling in the other | ||||||||||||||||||||||||
// cases, for example failed TPM unsealing. Setting this to | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||
// zero will disable attempts to activate with the fallback | ||||||||||||||||||||||||
// recovery key. | ||||||||||||||||||||||||
RecoveryKeyTries int | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. RecoveryKeyMaxTries? |
||||||||||||||||||||||||
|
||||||||||||||||||||||||
// ActivateOptions provides a mechanism to pass additional options to systemd-cryptsetup. | ||||||||||||||||||||||||
// ActivateOptions provides a mechanism to pass additional | ||||||||||||||||||||||||
// options to systemd-cryptsetup. | ||||||||||||||||||||||||
ActivateOptions []string | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
or maybe
Suggested change
or maybe even
Suggested change
(I'm not familiar enough with the pkg to know how specific we ought to be about using systemd-cryptsetup references) |
||||||||||||||||||||||||
|
||||||||||||||||||||||||
// LockSealedKeyAccess controls whether LockAccessToSealedKeys should be called after unsealing the TPM sealed key. It is called if | ||||||||||||||||||||||||
// this is set to true, and not called if this is set to false. | ||||||||||||||||||||||||
LockSealedKeyAccess bool | ||||||||||||||||||||||||
// LockSealedKeys controls whether access to the keys locked | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||
// after unsealing. That means calling LockAccessToSealedKeys | ||||||||||||||||||||||||
// in TPM case. | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||
LockSealedKeys bool | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
// KeyringPrefix is the prefix used for the description of any kernel keys created during activation. | ||||||||||||||||||||||||
// KeyringPrefix is the prefix used for the description of any | ||||||||||||||||||||||||
// kernel keys created during activation. | ||||||||||||||||||||||||
KeyringPrefix string | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
// ActivateVolumeWithTPMSealedKey attempts to activate the LUKS encrypted volume at sourceDevicePath and create a mapping with the | ||||||||||||||||||||||||
// name volumeName, using the TPM sealed key object at the specified keyPath. This makes use of systemd-cryptsetup. | ||||||||||||||||||||||||
// | ||||||||||||||||||||||||
// If the TPM sealed key object has a PIN defined, then this function will use systemd-ask-password to request it. If pinReader is not | ||||||||||||||||||||||||
// nil, then an attempt to read the PIN from this will be made instead by reading all characters until the first newline. The PINTries | ||||||||||||||||||||||||
// field of options defines how many attempts should be made to obtain the correct PIN before failing. | ||||||||||||||||||||||||
// If the TPM sealed key object has a user passphrase/PIN defined, then this function will use systemd-ask-password to request it. If passphraseReader is not | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a long comment, but I see it's consistent with the rest of the comments in the package. Nevertheless, it does not fit into a file view in a github review page, making it a bit hard to follow. Could we unify the comments style with snapd at some point? |
||||||||||||||||||||||||
// nil, then an attempt to read the user passphrase/PIN from this will be made instead by reading all characters until the first newline. The UserPassphraseTries | ||||||||||||||||||||||||
// field of options defines how many attempts should be made to obtain the correct passphrase before failing. | ||||||||||||||||||||||||
// | ||||||||||||||||||||||||
// The ActivateOptions field of options can be used to specify additional options to pass to systemd-cryptsetup. | ||||||||||||||||||||||||
// | ||||||||||||||||||||||||
// If the LockSealedKeyAccess field of options is true, then this function will call LockAccessToSealedKeys after unsealing the key | ||||||||||||||||||||||||
// If the LockSealedKeys field of options is true, then this function will call LockAccessToSealedKeys after unsealing the key | ||||||||||||||||||||||||
// and before activating the LUKS volume. | ||||||||||||||||||||||||
// | ||||||||||||||||||||||||
// If activation with the TPM sealed key object fails, this function will attempt to activate it with the fallback recovery key | ||||||||||||||||||||||||
|
@@ -497,7 +513,7 @@ type ActivateWithTPMSealedKeyOptions struct { | |||||||||||||||||||||||
// calling GetActivationDataFromKernel will return a *RecoveryActivationData containing the recovery key and the reason that the | ||||||||||||||||||||||||
// recovery key was requested. | ||||||||||||||||||||||||
// | ||||||||||||||||||||||||
// If either the PINTries or RecoveryKeyTries fields of options are less than zero, an error will be returned. If the ActivateOptions | ||||||||||||||||||||||||
// If either the UserPassphraseTries or RecoveryKeyTries fields of options are less than zero, an error will be returned. If the ActivateOptions | ||||||||||||||||||||||||
// field of options contains the "tries=" option, then an error will be returned. This option cannot be used with this function. | ||||||||||||||||||||||||
// | ||||||||||||||||||||||||
// If the LockSealedKeyAccess field of options is true and the call to LockAccessToSealedKeys fails, a LockAccessToSealedKeysError | ||||||||||||||||||||||||
|
@@ -514,9 +530,9 @@ type ActivateWithTPMSealedKeyOptions struct { | |||||||||||||||||||||||
// | ||||||||||||||||||||||||
// If the volume is successfully activated, either with the TPM sealed key or the fallback recovery key, this function returns true. | ||||||||||||||||||||||||
// If it is not successfully activated, then this function returns false. | ||||||||||||||||||||||||
func ActivateVolumeWithTPMSealedKey(tpm *TPMConnection, volumeName, sourceDevicePath, keyPath string, pinReader io.Reader, options *ActivateWithTPMSealedKeyOptions) (bool, error) { | ||||||||||||||||||||||||
if options.PINTries < 0 { | ||||||||||||||||||||||||
return false, errors.New("invalid PINTries") | ||||||||||||||||||||||||
func ActivateVolumeWithTPMSealedKey(tpm *TPMConnection, volumeName, sourceDevicePath, keyPath string, passphraseReader io.Reader, options *ActivateVolumeOptions) (bool, error) { | ||||||||||||||||||||||||
if options.UserPassphraseTries < 0 { | ||||||||||||||||||||||||
return false, errors.New("invalid UserPassphraseTries") | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
if options.RecoveryKeyTries < 0 { | ||||||||||||||||||||||||
return false, errors.New("invalid RecoveryKeyTries") | ||||||||||||||||||||||||
|
@@ -527,7 +543,7 @@ func ActivateVolumeWithTPMSealedKey(tpm *TPMConnection, volumeName, sourceDevice | |||||||||||||||||||||||
return false, err | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
if err := activateWithTPMKey(tpm, volumeName, sourceDevicePath, keyPath, pinReader, options.PINTries, options.LockSealedKeyAccess, activateOptions, options.KeyringPrefix); err != nil { | ||||||||||||||||||||||||
if err := activateWithTPMKey(tpm, volumeName, sourceDevicePath, keyPath, passphraseReader, options.UserPassphraseTries, options.LockSealedKeys, activateOptions, options.KeyringPrefix); err != nil { | ||||||||||||||||||||||||
reason := RecoveryKeyUsageReasonUnexpectedError | ||||||||||||||||||||||||
switch { | ||||||||||||||||||||||||
case isLockAccessError(err): | ||||||||||||||||||||||||
|
@@ -539,9 +555,9 @@ func ActivateVolumeWithTPMSealedKey(tpm *TPMConnection, volumeName, sourceDevice | |||||||||||||||||||||||
case isInvalidKeyFileError(err): | ||||||||||||||||||||||||
reason = RecoveryKeyUsageReasonInvalidKeyFile | ||||||||||||||||||||||||
case xerrors.Is(err, requiresPinErr): | ||||||||||||||||||||||||
reason = RecoveryKeyUsageReasonPINFail | ||||||||||||||||||||||||
reason = RecoveryKeyUsageReasonUserPassphraseFail | ||||||||||||||||||||||||
case xerrors.Is(err, ErrPINFail): | ||||||||||||||||||||||||
reason = RecoveryKeyUsageReasonPINFail | ||||||||||||||||||||||||
reason = RecoveryKeyUsageReasonUserPassphraseFail | ||||||||||||||||||||||||
case isExecError(err, systemdCryptsetupPath): | ||||||||||||||||||||||||
// systemd-cryptsetup only provides 2 exit codes - success or fail - so we don't know the reason it failed yet. If activation | ||||||||||||||||||||||||
// with the recovery key is successful, then it's safe to assume that it failed because the key unsealed from the TPM is incorrect. | ||||||||||||||||||||||||
|
@@ -554,44 +570,31 @@ func ActivateVolumeWithTPMSealedKey(tpm *TPMConnection, volumeName, sourceDevice | |||||||||||||||||||||||
return true, nil | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
// ActivateWithRecoveryKeyOptions provides options to ActivateVolumeWithRecoveryKey. | ||||||||||||||||||||||||
type ActivateWithRecoveryKeyOptions struct { | ||||||||||||||||||||||||
// Tries specifies the maximum number of times that activation with the fallback recovery key should be attempted before failing | ||||||||||||||||||||||||
// with an error. | ||||||||||||||||||||||||
Tries int | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
// ActivateOptions provides a mechanism to pass additional options to systemd-cryptsetup. | ||||||||||||||||||||||||
ActivateOptions []string | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
// KeyringPrefix is the prefix used for the description of any kernel keys created during activation. | ||||||||||||||||||||||||
KeyringPrefix string | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
// ActivateVolumeWithRecoveryKey attempts to activate the LUKS encrypted volume at sourceDevicePath and create a mapping with the | ||||||||||||||||||||||||
// name volumeName, using the fallback recovery key. This makes use of systemd-cryptsetup. | ||||||||||||||||||||||||
// | ||||||||||||||||||||||||
// This function will use systemd-ask-password to request the recovery key. If keyReader is not nil, then an attempt to read the key | ||||||||||||||||||||||||
// from this will be made instead by reading all characters until the first newline. The Tries field of options defines how many | ||||||||||||||||||||||||
// from this will be made instead by reading all characters until the first newline. The RecoveryKeyTries field of options defines how many | ||||||||||||||||||||||||
// attempts should be made to activate the volume with the recovery key before failing. | ||||||||||||||||||||||||
// | ||||||||||||||||||||||||
// The ActivateOptions field of options can be used to specify additional options to pass to systemd-cryptsetup. | ||||||||||||||||||||||||
// | ||||||||||||||||||||||||
// If activation with the recovery key is successful, calling GetActivationDataFromKernel will return a *RecoveryActivationData | ||||||||||||||||||||||||
// containing the recovery key and RecoveryKeyUsageReasonRequested as the recovery reason. | ||||||||||||||||||||||||
// | ||||||||||||||||||||||||
// If the Tries field of options is less than zero, an error will be returned. If the ActivateOptions field of options contains the | ||||||||||||||||||||||||
// If the RecoveryKeyTries field of options is less than zero, an error will be returned. If the ActivateOptions field of options contains the | ||||||||||||||||||||||||
// "tries=" option, then an error will be returned. This option cannot be used with this function. | ||||||||||||||||||||||||
func ActivateVolumeWithRecoveryKey(volumeName, sourceDevicePath string, keyReader io.Reader, options *ActivateWithRecoveryKeyOptions) error { | ||||||||||||||||||||||||
if options.Tries < 0 { | ||||||||||||||||||||||||
return errors.New("invalid Tries") | ||||||||||||||||||||||||
func ActivateVolumeWithRecoveryKey(volumeName, sourceDevicePath string, keyReader io.Reader, options *ActivateVolumeOptions) error { | ||||||||||||||||||||||||
if options.RecoveryKeyTries < 0 { | ||||||||||||||||||||||||
return errors.New("invalid RecoveryKeyTries") | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
activateOptions, err := makeActivateOptions(options.ActivateOptions) | ||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||
return err | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
return activateWithRecoveryKey(volumeName, sourceDevicePath, keyReader, options.Tries, RecoveryKeyUsageReasonRequested, activateOptions, options.KeyringPrefix) | ||||||||||||||||||||||||
return activateWithRecoveryKey(volumeName, sourceDevicePath, keyReader, options.RecoveryKeyTries, RecoveryKeyUsageReasonRequested, activateOptions, options.KeyringPrefix) | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
// ActivationData corresponds to some data added to the user keyring by one of the ActivateVolume functions. | ||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated to renames, is this a precaution that the reader wouldn't be used again by accident?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not quite sure what was the original intention here. It's meant to fall back to use systemd-ask-password after consuming one passphrase. But it's very specific behavior that's another reason to have some more control later via the Prompter if needed.