Repository: freedomofpress/securedrop

Issue:
SecureDrop Updater Tails Admin Password Prompt Appears to Fail When Clicking OK
## Description

When using the SecureDrop updater GUI tool to update SecureDrop, correctly entering the Tails admin password and clicking OK with a trackpad fails and invokes a "Administrator password is incorrect" error, while hitting the Enter key instead of clicking OK works.

## Steps to Reproduce

After a successful upgrade to SecureDrop 0.7.0, the SecureDrop Updater appeared to inform me I can upgrade to 0.8.0. Once I began that upgrade, I was prompted for the Tails admin password, which I entered and used the journalist workstation's built-in trackpad to click the OK button on the prompt, after which it failed and I had to click Update Now again to restart the upgrade process, only to have it fail the same way a few times. On the final try, I used the Enter key instead of clicking OK and it accepted the admin password and completed the upgrade.

## Expected Behavior

Clicking OK after entering a correct Tails admin password should accept the password and continue the upgrade.

## Actual Behavior

Clicking OK after entering a correct Tails admin password fails and tells me the password is wrong.

## Comments

This was on a Tails laptop that had only just been upgraded from 0.6.0 to 0.7.0 via the command line. Once the 0.7.0 upgrade was complete, the more modern SecureDrop updater prompted me to go to the shiny chrome future that is 0.8.0 and I very much wanted to.



Discussion:
thanks for filing this @huertanix! unfortunately I was not able to reproduce this in Tails 3.8, can you confirm that was the version of Tails you used?

And can you provide the error message, i.e. what happened when it failed? If you are able to provide it, the full output of the details tab in the GUI is very helpful since that output is very verbose (please be sure to grep through the output and remove any onion addresses or HidServAuth cookies)
I could not reproduce it for `0.7.0` on Tails 3.6. 
closing this, but let us know if you have more information or see it again! 
I ran into this again but this time I was already at 0.8.0 (just cloned the repo today but the GUI updater popped up anyway after running `./securedrop-admin tailsconfig`) for a journalist (not admin) Tails drive. I had a very easy and specific Tails admin password and it would fail every time I tried it, whether with a mouse click or enter key this time. Here's the tab output:

> When the update begins, this area will populate with output.
> Fetching and verifying latest update... (5 mins remaining)
> INFO: Applying SecureDrop updates...
> INFO: Checking for SecureDrop updates...
> Fetching origin
> INFO: Update needed
> INFO: Verifying signature on latest update...
> gpg: key 0x310F561200F4AD77: public key "SecureDrop Release Signing Key" imported
> gpg: no ultimately trusted keys found
> gpg: Total number processed: 1
> gpg:               imported: 1
> INFO: Signature verification successful.
> Note: checking out '0.8.0'.
> 
> You are in 'detached HEAD' state. You can look around, make experimental
> changes and commit them, and you can discard any commits you make in this
> state without impacting any branches by performing another checkout.
> 
> If you want to create a new branch to retain commits you create, you may
> do so (now or later) by using -b with the checkout command again. Example:
> 
>   git checkout -b <new-branch-name>
> 
> HEAD is now at e2c2f07d... SecureDrop 0.8.0
> INFO: Updated to SecureDrop 0.8.0.
> Checking dependencies are up to date... (2 mins remaining)
> INFO: Virtualenv already exists, not creating
> INFO: Checking Python dependencies for securedrop-admin
> INFO: Python dependencies for securedrop-admin upgraded
> INFO: Finished installing SecureDrop dependencies
> Configuring local Tails environment... (1 min remaining)
> INFO: Configuring Tails workstation environment
> INFO: You'll be prompted for the temporary Tails admin password, which was set on Tails login screen
> Administrator password incorrect. Exiting upgrade - click Update Now to try again.
> Fetching and verifying latest update... (5 mins remaining)
> INFO: Applying SecureDrop updates...
> INFO: Checking for SecureDrop updates...
> Fetching origin
> INFO: All updates applied
> Checking dependencies are up to date... (2 mins remaining)
> INFO: Virtualenv already exists, not creating
> INFO: Checking Python dependencies for securedrop-admin
> INFO: Python dependencies for securedrop-admin are up-to-date
> INFO: Finished installing SecureDrop dependencies
> Configuring local Tails environment... (1 min remaining)
> INFO: Configuring Tails workstation environment
> INFO: You'll be prompted for the temporary Tails admin password, which was set on Tails login screen
> INFO: Configuring Tails workstation environment
> INFO: You'll be prompted for the temporary Tails admin password, which was set on Tails login screen
> Administrator password incorrect. Exiting upgrade - click Update Now to try again.
> Fetching and verifying latest update... (5 mins remaining)
> INFO: Applying SecureDrop updates...
> INFO: Checking for SecureDrop updates...
> Fetching origin
> INFO: All updates applied
> Checking dependencies are up to date... (2 mins remaining)
> INFO: Virtualenv already exists, not creating
> INFO: Checking Python dependencies for securedrop-admin
> INFO: Python dependencies for securedrop-admin are up-to-date
> INFO: Finished installing SecureDrop dependencies
> Configuring local Tails environment... (1 min remaining)
> INFO: Configuring Tails workstation environment
> INFO: You'll be prompted for the temporary Tails admin password, which was set on Tails login screen
> INFO: Configuring Tails workstation environment
> INFO: You'll be prompted for the temporary Tails admin password, which was set on Tails login screen
> INFO: Configuring Tails workstation environment
> INFO: You'll be prompted for the temporary Tails admin password, which was set on Tails login screen
> Administrator password incorrect. Exiting upgrade - click Update Now to try again.
> Fetching and verifying latest update... (5 mins remaining)
> INFO: Applying SecureDrop updates...
> INFO: Checking for SecureDrop updates...
> Fetching origin
> INFO: All updates applied
> Checking dependencies are up to date... (2 mins remaining)
> INFO: Virtualenv already exists, not creating
> INFO: Checking Python dependencies for securedrop-admin
> INFO: Python dependencies for securedrop-admin are up-to-date
> INFO: Finished installing SecureDrop dependencies
> Configuring local Tails environment... (1 min remaining)
> INFO: Configuring Tails workstation environment
> INFO: You'll be prompted for the temporary Tails admin password, which was set on Tails login screen
> INFO: Configuring Tails workstation environment
> INFO: You'll be prompted for the temporary Tails admin password, which was set on Tails login screen
> INFO: Configuring Tails workstation environment
> INFO: You'll be prompted for the temporary Tails admin password, which was set on Tails login screen
> INFO: Configuring Tails workstation environment
> INFO: You'll be prompted for the temporary Tails admin password, which was set on Tails login screen
> Administrator password incorrect. Exiting upgrade - click Update Now to try again.
> Fetching and verifying latest update... (5 mins remaining)
> INFO: Applying SecureDrop updates...
> INFO: Checking for SecureDrop updates...
> Fetching origin
> INFO: All updates applied
> Checking dependencies are up to date... (2 mins remaining)
> INFO: Virtualenv already exists, not creating
> INFO: Checking Python dependencies for securedrop-admin
> INFO: Python dependencies for securedrop-admin are up-to-date
> INFO: Finished installing SecureDrop dependencies
> Configuring local Tails environment... (1 min remaining)
> INFO: Configuring Tails workstation environment
> INFO: You'll be prompted for the temporary Tails admin password, which was set on Tails login screen
> INFO: Configuring Tails workstation environment
> INFO: You'll be prompted for the temporary Tails admin password, which was set on Tails login screen
> INFO: Configuring Tails workstation environment
> INFO: You'll be prompted for the temporary Tails admin password, which was set on Tails login screen
> INFO: Configuring Tails workstation environment
> INFO: You'll be prompted for the temporary Tails admin password, which was set on Tails login screen
> INFO: Configuring Tails workstation environment
> INFO: You'll be prompted for the temporary Tails admin password, which was set on Tails login screen
> Administrator password incorrect. Exiting upgrade - click Update Now to try again.
> Fetching and verifying latest update... (5 mins remaining)
> INFO: Applying SecureDrop updates...
> INFO: Checking for SecureDrop updates...
> Fetching origin
> INFO: All updates applied
> Checking dependencies are up to date... (2 mins remaining)
> INFO: Virtualenv already exists, not creating
> INFO: Checking Python dependencies for securedrop-admin
> INFO: Python dependencies for securedrop-admin are up-to-date
> INFO: Finished installing SecureDrop dependencies
> Configuring local Tails environment... (1 min remaining)
> INFO: Configuring Tails workstation environment
> INFO: You'll be prompted for the temporary Tails admin password, which was set on Tails login screen
> INFO: Configuring Tails workstation environment
> INFO: You'll be prompted for the temporary Tails admin password, which was set on Tails login screen
> INFO: Configuring Tails workstation environment
> INFO: You'll be prompted for the temporary Tails admin password, which was set on Tails login screen
> INFO: Configuring Tails workstation environment
> INFO: You'll be prompted for the temporary Tails admin password, which was set on Tails login screen
> INFO: Configuring Tails workstation environment
> INFO: You'll be prompted for the temporary Tails admin password, which was set on Tails login screen
> INFO: Configuring Tails workstation environment
> INFO: You'll be prompted for the temporary Tails admin password, which was set on Tails login screen
> Administrator password incorrect. Exiting upgrade - click Update Now to try again.
I still can not reproduce it :( I installed fresh Tails 3.8, and there I moved to `develop` and then did tailsconfig first. I moved to `0.7.0` and then disconnected and connected back the network, that fired up the updater, and typed the sudo password manually. If I am on `0.8.0`, I could not manage to get the application up automatically.
Reopening for further investigation based on @huertanix 's report.
We should see if we can get a repro or root cause on this ahead of the next release. Making a note to set aside some time for additional investigation in the next sprint. 
Sadly, we're having to bump this again. On the upside, we've not seen any reports in support about this issue, but we should still make one more attempt to repro before the next release.
Cannot reproduce on current Tails (3.10.1).

Downgraded to 0.7.0, bumped the network connection, the updater appears. 
Clicked *Update Now*, the admin password comes up. 
Put in admin password, clicked *OK*. Update completes without error to 0.9.1.
Bumped network again, updater appears, repeat update process, clicking *OK* instead of  hitting Return. Update completes without error to 0.10.0

Repeated the above 3 times. No errors.
Given multiple unsuccessful repro attempts, no other reports, and no obvious potential causes in the code itself, we'll have to close this for now. @huertanix, thanks for reporting -- next time you encounter this issue, if you can get a clean repro, let's sync up in real-time to see if we can get to the bottom of this.
During QA for 1.2.1 today I encountered this issue: despite my very carefully typing in the Tails admin password, the graphical updater told me it was incorrect. Confirmed I was using the correct pw by running `sudo echo hello` in the terminal, no problems. 
Got a report of this from a production user so bumping in priority.
Saw this myself today with first attempt failing (though password was correct), second attempt working. Poked a bit at the Qt input but isolating that I don't see any issues at least in my Ubuntu dev env -- e.g., it ignores control characters and only outputs what I input. My hypothesis, FWIW, is that the logic for setting the password via a child process sometimes doesn't work (some timing issue).

Resolving patch:
diff --git a/journalist_gui/journalist_gui/SecureDropUpdater.py b/journalist_gui/journalist_gui/SecureDropUpdater.py
--- a/journalist_gui/journalist_gui/SecureDropUpdater.py
+++ b/journalist_gui/journalist_gui/SecureDropUpdater.py
@@ -128,12 +128,13 @@ def run(self):
         tailsconfig_command = ("/home/amnesia/Persistent/"
                                "securedrop/securedrop-admin "
                                "tailsconfig")
+        self.failure_reason = ""
         try:
             child = pexpect.spawn(tailsconfig_command)
             child.expect('SUDO password:')
             self.output += child.before.decode('utf-8')
             child.sendline(self.sudo_password)
-            child.expect(pexpect.EOF)
+            child.expect(pexpect.EOF, timeout=120)
             self.output += child.before.decode('utf-8')
             child.close()
 
@@ -141,13 +142,15 @@ def run(self):
             # failures in the Ansible output.
             if child.exitstatus:
                 self.update_success = False
-                self.failure_reason = strings.tailsconfig_failed_generic_reason  # noqa
+                if "[sudo via ansible" in self.output:
+                    self.failure_reason = strings.tailsconfig_failed_sudo_password
+                else:
+                    self.failure_reason = strings.tailsconfig_failed_generic_reason
             else:
                 self.update_success = True
         except pexpect.exceptions.TIMEOUT:
             self.update_success = False
-            self.failure_reason = strings.tailsconfig_failed_sudo_password
-
+            self.failure_reason = strings.tailsconfig_failed_timeout
         except subprocess.CalledProcessError:
             self.update_success = False
             self.failure_reason = strings.tailsconfig_failed_generic_reason
diff --git a/journalist_gui/journalist_gui/strings.py b/journalist_gui/journalist_gui/strings.py
--- a/journalist_gui/journalist_gui/strings.py
+++ b/journalist_gui/journalist_gui/strings.py
@@ -25,12 +25,15 @@
                              "Contact your SecureDrop administrator "
                              "or securedrop@freedom.press immediately.")
 tailsconfig_failed_sudo_password = ('Administrator password incorrect. '
-                                    'Exiting upgrade - '
-                                    'click Update Now to try again.')
+                                    'Click Update Now to try again.')
 tailsconfig_failed_generic_reason = ("Tails workstation configuration failed. "
                                      "Contact your administrator. "
                                      "If you are an administrator, contact "
                                      "securedrop@freedom.press.")
+tailsconfig_failed_timeout = ("Tails workstation configuration took too long. "
+                              "Contact your administrator. "
+                              "If you are an administrator, contact "
+                              "securedrop@freedom.press.")
 install_update_button = 'Update Now'
 install_later_button = 'Update Later'
 sudo_password_text = ("Enter the Tails Administrator password you "


Test patch:
diff --git a/journalist_gui/test_gui.py b/journalist_gui/test_gui.py
--- a/journalist_gui/test_gui.py
+++ b/journalist_gui/test_gui.py
@@ -204,6 +204,18 @@ def test_tailsconfigThread_generic_failure(self, pt):
 
     @mock.patch('pexpect.spawn')
     def test_tailsconfigThread_sudo_password_is_wrong(self, pt):
+        child = pt()
+        before = MagicMock()
+        before.decode.return_value = "stuff[sudo via ansible, key=blahblahblah"
+        child.before = before
+        self.window.tails_thread.run()
+        self.assertNotIn("failed=0", self.window.output)
+        self.assertEqual(self.window.update_success, False)
+        self.assertEqual(self.window.failure_reason,
+                         strings.tailsconfig_failed_sudo_password)
+
+    @mock.patch('pexpect.spawn')
+    def test_tailsconfigThread_timeout(self, pt):
         child = pt()
         before = MagicMock()
         before.decode.side_effect = ["some data",
@@ -213,7 +225,7 @@ def test_tailsconfigThread_sudo_password_is_wrong(self, pt):
         self.assertNotIn("failed=0", self.window.output)
         self.assertEqual(self.window.update_success, False)
         self.assertEqual(self.window.failure_reason,
-                         strings.tailsconfig_failed_sudo_password)
+                         strings.tailsconfig_failed_timeout)
 
     @mock.patch('pexpect.spawn')
     def test_tailsconfigThread_some_other_subprocess_error(self, pt):