git credential helper - getting user input from the console

Hi,

We use a git credential helper that get credentials from a keystore which works fine but to unlock it the first time it pops-up a XWindow asking the unlock password. The problem is that if we have a ssh session without a X server, the credential helper cannot pop a XWindow for getting the unlock password. So we would like to modify the credential helper to ask the unlock password on the command line; but we do not see a way to do this as the standard input and output serve as data input and output (see INPUT/OUTPUT FORMAT on the git-credential documentation) to the credential helper.

  1. Is there a way to acheve this?

  2. If not, maybe we could modify git (the git code and rebuild it) to support special attributes that the credential subsystem and credential helper could use for the purpose: 

  3. Attribute “queryuser=<question>” which the credential helper could return to the credential subsystem which will, in turn, ask the <question> to the user (on git’s standard input); a <question> starting with an asterisk (*) would mean that the entered answer must not be echoed back (like with passwords).

  4. Attribute “queryresponse=<answer>” which the credential susbsystem can pass to the (recalled) credential helper along with the original “queryuser=<question>” attribute (for validation purpose);

  5. Any attribute in capital letters given by the credential helper would be passed back to recalls of this helper.

  6. This way, the credential helper could query the user as many times as required (ex: username and password to unlock the keystore) by querying the user through git with the “queryuser” attribute, getting responses with the “queryresponse” attribute and using capital attributes to retain previous answers. This solution would theoretically be (backward) compatible with any git version and any credential helper (ignored attributes) and could possibly be a feature request for git. Please feel free to comment on this solution.

Thanks,

d-gagnon

Interesting question … while I can’t answer any questions about your organization’s credential helper, I’ve taken a look at the documentation and recalled some conversations with others who know much more about the subject than I do.

First, rather than modifying a credential helper that works, you may want to investigate creating an _additional _helper, since you can configure more than one. Your first helper would be the one you already have and then the new one would be the fallback. See the documentation on credential helper configuration options for more details.

Second, I’m not sure I understand why your credential helper couldn’t prompt the user for information? Yes, Git uses standard input and output to communicate with the credential helper process. On the other hand, using standard output is not necessary to display text on the screen. You can send text directly to the TTY device and it will be displayed on the screen but not be sent to the standard output stream. You could also use the standard error stream, which is typically displayed on the TTY device. Additionally, in the INPUT/OUTPUT FORMAT documentation you link to it states:

> The list of attributes is terminated by a blank line or end-of-file.

This means that you know when Git is done talking to your credential helper and any further data coming in from the standard input stream will be from the user, in response to your prompt.

I hope that helps!

1 Like

Hi lee-dohm,

It does not seem that the credential helper can get any user input from the standard input after git has finished talking by sending the end-of-file. I tested this with this script used as credential helper:

#!/bin/bash

NL=$'\n'
attrib=""

while read line
do
  attrib="$attrib$line$NL"
done <&0

echo -n "Enter password for $USER: ">&2

read -s password

attrib="$attrib""username=$USER$NL""password=$password"

# Echo attributes to STDERR for debugging.
echo "$NL$-$attrib">&2
# Echo attributes to STDIN as response to git.
echo "$attrib"

Explanation: The while loop reads the standard input until the end-of-file so the attrib variable contains what git has given. Then the script queries the user for password on the standard input with “read -s password”. It then adds the user and password attributes with the current user and the queried password to the other attributes in the attrib variable. For debugging purposes, the script outputs to the standard error the shell’s current set of options ($-) followed by the content of the attrib variable before giving the resulting attributes to git through the standard output.

When doing a git fetch, there is no query to the user and the password is blank as nothing was got from the standard input. Here is the result:

user@pc:~/repo$ git fetch
hB
Enter password for user:
protocol=https
host=gitrepos.fromsomesite.com
username=user
password=
hB
Enter password for user:
protocol=https
host=gitrepos.fromsomesite.com
username=user
password=
username=user
password=
fatal: Authentication failed for 'https://gitrepos.fromsomesite.com/somerepo/'
user@pc:~/repo$

Here we can see that the shell’s current set of options do not include “i” (interactive) for user interaction. If this script was executed by itself, it would get everything from the standard input until the end-of-file (CTRL-D) then query the user, etc.; and the “i” option would appear. As another test, piping something to it would also fail to get user interaction.

Note that user and host posted here are fictional; the real output has a real user and host.

Tell me if my investigation was correct or if there is something wrong with the test.

Thanks,

d-gagnon

I ran into the same issue as you today @d-gagnon, and I wanted to share the solution I ultimately found in case others come across it as well :slight_smile:

What’s happening here is that git-credential executes the helper script in a pipeline, feeding it data over stdin. The consequence is that the stdin of the helper script is not attached to the user’s terminal any more, and when you call read -s password, read is trying to get more input from stdin but the EOF has already occurred so it exits immediately.

The solution is to point the terminal back into read, with the line

read -s password </dev/tty

And that should do it!

Incidentally, for the benefit of anyone writing a Python credential helper and getting the same issue, the same solution can be effected with

sys.stdin = open("/dev/tty", "r")

before the call to input(), etc.

1 Like