Skip to content
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

mfa: cancel TOTP prompt if U2F was used #6542

Merged
merged 1 commit into from
Apr 29, 2021
Merged

mfa: cancel TOTP prompt if U2F was used #6542

merged 1 commit into from
Apr 29, 2021

Conversation

awly
Copy link
Contributor

@awly awly commented Apr 21, 2021

Implement context-based cancellation in /lib/utils/prompt, for MFA
prompts.

This fixes the following scenario:

# User has both OTP and U2F devices registered.
$ tsh mfa ls
Name  Type Added at                      Last used
----- ---- ----------------------------- -----------------------------
otp   TOTP Wed, 21 Apr 2021 19:41:44 UTC Wed, 21 Apr 2021 19:44:32 UTC
usb-a U2F  Wed, 21 Apr 2021 19:44:34 UTC Wed, 21 Apr 2021 19:44:34 UTC

# Add a new OTP device, using existing U2F device:
$ tsh mfa add
Choose device type [TOTP, U2F]: totp
Enter device name: otp2
Tap any *registered* security key or enter a code from a *registered* OTP device: <tap> # <- First OTP prompt here
Open your TOTP app and create a new manual entry with these fields:
Name: awly@localhost:3080
Issuer: Teleport
Algorithm: SHA1
Number of digits: 6
Period: 30s
Secret: 3UD42X2NN7EEZ6LUPG6NFMNOLDY6AJTS

Once created, enter an OTP code generated by the app: 607738 # <- Second OTP prompt here
MFA device "otp2" added.

Before this PR, the first OTP prompt (for *registered* device) would
hang in the background. The OTP code from the newly-registered device is
prompted later, but any text written ends up going to the first prompt and lost.

After this PR, the first prompt is canceled and the code from a new
device goes to the second prompt as intended.

Note: this is implemented using pure Go code (background goroutine
consuming os.Stdin) rather than syscalls (e.g. poll or select)
for portability.

Fixes #5804
Updates #5878

@awly awly requested a review from Joerger April 21, 2021 20:26
@awly awly force-pushed the andrew/totp-polish branch from e6951d8 to 92d61ca Compare April 21, 2021 20:27
@awly awly marked this pull request as ready for review April 21, 2021 20:51
@awly awly force-pushed the andrew/totp-polish branch from 92d61ca to 2841f63 Compare April 27, 2021 22:11
lib/utils/prompt/stdin.go Outdated Show resolved Hide resolved
return false, trace.WrapWithMessage(scan.Err(), "failed reading prompt response")
answer, err := in.ReadContext(ctx)
if err != nil {
return false, trace.WrapWithMessage(err, "failed reading prompt response")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This code is quite old but I think all "WrapWithMessage" calls here can be replaced with just regular "Wrap".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrap just calls WrapWithMessage if there are any extra arguments.
I don't see any difference between them, other than a bit of overhead

lib/utils/prompt/stdin.go Outdated Show resolved Hide resolved
@awly awly force-pushed the andrew/totp-polish branch from 2841f63 to 19b482f Compare April 29, 2021 18:06
Implement context-based cancellation in `/lib/utils/prompt`, for MFA
prompts.

This fixes the following scenario:
```sh
User has both OTP and U2F devices registered.
$ tsh mfa ls
Name  Type Added at                      Last used
----- ---- ----------------------------- -----------------------------
otp   TOTP Wed, 21 Apr 2021 19:41:44 UTC Wed, 21 Apr 2021 19:44:32 UTC
usb-a U2F  Wed, 21 Apr 2021 19:44:34 UTC Wed, 21 Apr 2021 19:44:34 UTC

Add a new OTP device, using existing U2F device:
$ tsh mfa add
Choose device type [TOTP, U2F]: totp
Enter device name: otp2
Tap any *registered* security key or enter a code from a *registered* OTP device: <tap> # <- First OTP prompt here
Open your TOTP app and create a new manual entry with these fields:
Name: awly@localhost:3080
Issuer: Teleport
Algorithm: SHA1
Number of digits: 6
Period: 30s
Secret: 3UD42X2NN7EEZ6LUPG6NFMNOLDY6AJTS

Once created, enter an OTP code generated by the app: 607738 # <- Second OTP prompt here
MFA device "otp2" added.
```

Before this PR, the first OTP prompt (for `*registered* device`) would
hang in the background. The OTP code from the newly-registered device is
prompted later, but any text written ends up going to the first prompt.

After this PR, the first prompt is canceled and the code from a new
device goes to the second prompt as intended.

Note: this is implemented using pure Go code (background goroutine
consuming `os.Stdin`) rather than syscalls (e.g. `poll` or `select`)
for portability.
@awly awly force-pushed the andrew/totp-polish branch from 19b482f to e9b36be Compare April 29, 2021 18:07
@awly awly enabled auto-merge (squash) April 29, 2021 18:11
@awly awly merged commit b8fbb2d into master Apr 29, 2021
@awly awly deleted the andrew/totp-polish branch April 29, 2021 18:22
awly pushed a commit that referenced this pull request Apr 29, 2021
Implement context-based cancellation in `/lib/utils/prompt`, for MFA
prompts.

This fixes the following scenario:
```sh
User has both OTP and U2F devices registered.
$ tsh mfa ls
Name  Type Added at                      Last used
----- ---- ----------------------------- -----------------------------
otp   TOTP Wed, 21 Apr 2021 19:41:44 UTC Wed, 21 Apr 2021 19:44:32 UTC
usb-a U2F  Wed, 21 Apr 2021 19:44:34 UTC Wed, 21 Apr 2021 19:44:34 UTC

Add a new OTP device, using existing U2F device:
$ tsh mfa add
Choose device type [TOTP, U2F]: totp
Enter device name: otp2
Tap any *registered* security key or enter a code from a *registered* OTP device: <tap> # <- First OTP prompt here
Open your TOTP app and create a new manual entry with these fields:
Name: awly@localhost:3080
Issuer: Teleport
Algorithm: SHA1
Number of digits: 6
Period: 30s
Secret: 3UD42X2NN7EEZ6LUPG6NFMNOLDY6AJTS

Once created, enter an OTP code generated by the app: 607738 # <- Second OTP prompt here
MFA device "otp2" added.
```

Before this PR, the first OTP prompt (for `*registered* device`) would
hang in the background. The OTP code from the newly-registered device is
prompted later, but any text written ends up going to the first prompt.

After this PR, the first prompt is canceled and the code from a new
device goes to the second prompt as intended.

Note: this is implemented using pure Go code (background goroutine
consuming `os.Stdin`) rather than syscalls (e.g. `poll` or `select`)
for portability.
awly pushed a commit that referenced this pull request May 3, 2021
Implement context-based cancellation in `/lib/utils/prompt`, for MFA
prompts.

This fixes the following scenario:
```sh
User has both OTP and U2F devices registered.
$ tsh mfa ls
Name  Type Added at                      Last used
----- ---- ----------------------------- -----------------------------
otp   TOTP Wed, 21 Apr 2021 19:41:44 UTC Wed, 21 Apr 2021 19:44:32 UTC
usb-a U2F  Wed, 21 Apr 2021 19:44:34 UTC Wed, 21 Apr 2021 19:44:34 UTC

Add a new OTP device, using existing U2F device:
$ tsh mfa add
Choose device type [TOTP, U2F]: totp
Enter device name: otp2
Tap any *registered* security key or enter a code from a *registered* OTP device: <tap> # <- First OTP prompt here
Open your TOTP app and create a new manual entry with these fields:
Name: awly@localhost:3080
Issuer: Teleport
Algorithm: SHA1
Number of digits: 6
Period: 30s
Secret: 3UD42X2NN7EEZ6LUPG6NFMNOLDY6AJTS

Once created, enter an OTP code generated by the app: 607738 # <- Second OTP prompt here
MFA device "otp2" added.
```

Before this PR, the first OTP prompt (for `*registered* device`) would
hang in the background. The OTP code from the newly-registered device is
prompted later, but any text written ends up going to the first prompt.

After this PR, the first prompt is canceled and the code from a new
device goes to the second prompt as intended.

Note: this is implemented using pure Go code (background goroutine
consuming `os.Stdin`) rather than syscalls (e.g. `poll` or `select`)
for portability.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

mfa: failed registering multiple OTP devices
3 participants