Commit Graph
4 Commits
Author SHA1 Message Date
Alan Parra 0ecf31de0e Restore terminal state on interrupt or exit (#13382)
Fixes an issue with ContextReader on `bash` where abandoned password reads cause
the terminal to remain "locked" even after `tsh` exits. This happens on
interrupts but also on regular exit if the user picks a security key in the dual
security key/OTP login prompt.

Doesn't seem to affect shells like `zsh` or `fish`.

Repro steps:

```shell
$ bash

# repro #1
$ ./tsh login --proxy=example.com
> Enter password for Teleport user llama: <CTRL-C>
# shell is now locked

# repro #2
# (Use an account with security key and OTP MFA.)
$ ./tsh login --proxy=example.com
> Enter password for Teleport user llama: <enters password>
> Tap any security key or enter a code from a OTP device <taps security key>
# shell is now locked
```

* Restore terminal state on interrupt or exit
* Notify prompt on tsh exit
* Review: context.WithTimeout and Notify on os.Exit
2022-06-10 14:44:18 -03:00
Alan Parra 70a0ad132e Consistently prompt for OTPs as password (#11346)
Introduce the prompt.Password function and use it consistently whenever we read
a password, OTP or PIN.

The PR makes it easy to support PINs during MFA authentication, so now we do so.
It also adds the capability to mock prompt.Stdin() for tests, adding a uniform
way to fake user input in tests without having to swap functions.

Complements #10953 and #9160.

* Prompt for OTPs as passwords (take 1), read PINs on MFA authentication
* Add the prompt.Password method
* Add Stdin mocking capabilities to the prompt package
* Delegate password reads to prompt.Stdin().ReadPassword()
* Remove stdinHijack from PromptMFAChallenge
* Change api_login_test.go to FakeReader
* Change tsh_test.go to FakeReader
* Retire client.ReadPassword in favor of prompt.Password
2022-03-28 17:16:00 +00:00
Alan Parra 5a11006f81 Add ReadPassword functionality to ContextReader (#11436)
This changes prompt.ContextReader in the following ways:

Reads only happen as a response to Read methods being called. This allows
ContextReader to coexist with other readers as long as no reads are abandoned.
ReadPassword is now available, the underlying implementation being
term.ReadPassword. An abandoned password read may be turned into a clean read.
This gives us some UX flexibility when callers abandon password reads (looking
at you, PromptMFAChallenge). Turning clean reads into password reads is not
supported. It's tricky and I have a few ideas, but it's not paramount at this
moment.

This solves the woes caused by abandoned OTP reads followed by PIN reads in
different packages, such as client.PromptMFAChallenge followed by tsh mfa add's
implementation.

#9160

* Move ContextReader to its own file
* Refactor ContextReader and implement ReadPassword
* Test ReadPassword
* Fix typos
* Remove prompt.StdinSync()

prompt.Stdin() has the same behavior for non-abandoned reads.

* Group /x/term methods under a type
2022-03-25 17:17:20 +00:00
Andrew Lytvynov b8fbb2d1e9 mfa: cancel TOTP prompt if U2F was used (#6542)
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.
2021-04-29 11:22:11 -07:00