1
votes

In GPG, there is no proper documentation how to check a valid passphrase via bash code so, this is a hack. Based on the following example code is use to check whether GPG password that is cached in gpg-agent is valid or not:

#!/bin/bash
KEY_ID=YOUR_KEY_ID
echo "1234" | gpg -q --batch --status-fd 1 --sign --local-user $KEY_ID --passphrase-fd 0 --output /dev/null
return_code=$?
if [ "$return_code" = 0 ]; then
 echo "Valid passphrase has been set in gpg-agent"
   else
 echo "Invalid passphrase or no passphrase is set in gpg-agent"
fi

If a valid passphrase is set, and when I run this bash script, the return value is 0. This is correct

But

If no passphrase or invalid passphrase is set, I can see that the command is waiting for some unknown input or processes and it does not exit (blinking cursor until I terminate with CTRL+C). But this is a good signal to show that invalid passphrase is supplied.

My question is, if invalid passphrase is supplied, how do I force the command to exit and get return value of 1 so I can use the if else conditional correctly ?

NOTE AND INFO TO REPRODUCE THE PROBLEM FROM THE CODE (THIS IS NOT A QUESTION):

to set gpg password there are 2 ways:

  1. gpg --export-secret-keys -a <KEY_ID> (this can validate the passphrase) or
  2. without prompt in bash: /usr/libexec/gpg-preset-passphrase -c $KEY_GRIP <<< $PASSPHRASE (this does not validate the passphrase). I need to use command for cron. Why? Read below.

to clear the password I do this: echo RELOADAGENT | gpg-connect-agent

KEY_ID - you got when you first create the cert

KEY_GRIP - can be obtain with this command: gpg --with-keygrip --list-secret-keys $KEY_ID.

PASSPHRASE - is your passphrase / password for your cert to cache in gpg-agent.

Note that, If you are using this method to cache password in gpg-agent: /usr/libexec/gpg-preset-passphrase -c $KEY_GRIP <<< $PASSPHRASE, it won't validate the passphrase because wrong passphrase can also be cached in gpg-agent. This method is the only way to skip interactive input when run as cron to prevent script error because no input from user. This is the reason I use the hacked code.

3
Err. 1 is what you're supposed to get for an error, 0 is what you're supposed to get for success; just as a matter of how standard UNIX exit status values are defined.Charles Duffy
Bigger picture, though... if gpg doesn't treat an EOF from the passphrase FD as an exit condition, I'd suggest bringing that as a bug to its authors -- or doing as I've done, and switching away to a better-written OpenPGP implementation (I use the golang x/crypto/openpgp one).Charles Duffy
Speaking to the title alone, though: A command that doesn't exit doesn't have a return value. If you want to force it to exit, consider running it under timeout or some equivalent. timeout a command in bash without unnecessary delay provides one way to get there.Charles Duffy
Just coming back to this, and revisiting what you're trying to do... you're trying to determine whether the agent already has a key loaded? You do realize you can query the agent directly, right? gpg-connect-agent is a thing.Charles Duffy
Have you dug into the codepaths to determine what gpg is waiting for? If it's waiting for the agent to query the user for a new passphrase, for example, then one ugly-yet-workable hack might be to temporarily reconfigure the executable the agent uses to request a passphrase from the user to instead be something that immediately returns an error. A stacktrace from wherever it's sitting while it hangs would be useful for root-cause analysis as to the why of that hang, and thus to determining what to do about it.Charles Duffy

3 Answers

1
votes

I have found the answer for this problem. The return code did not output correctly because of bug. I just need to modify the code so that it won't use default pin-entry passphrase prompt. Using the pin-entry passphrase can cause return code stuck at random time based on this related bug (https://dev.gnupg.org/T5076). So in brief, the return code was stuck at random time but this can be solved using this option:

--pinentry-mode=loopback

So, the above option is to disable pin-entry prompt for passphrase and we can use gpg-preset-passphrase utility instead to set the passphrase in terminal. So, using this method I can get the correct return code.

I found this thread talking about how to disable pin entry in gpg-agent such as this: here

So this is the working code with the correct return code when validating passphrase:

#!/bin/bash
gpg-agent --daemon
KEY_ID=ABC12321SS
GPG_PRESET_PASS="/usr/libexec/gpg-preset-passphrase" # This utility came with gpg
KEY_GRIP=$(gpg --with-keygrip --list-secret-keys "$KEY_ID" | grep -Pom1 '^ *Keygrip += +\K.*') # Get they key grip
# Ask passphrase
read -s -p "Enter a passphrase to cache into a running gpg-agent: " PASSPHRASE; echo
# Cache RAW passphrase here. RAW passphrase means un-validated passphrase. Passphrase can be valid or invalid cached in gpg-agent.
$GPG_PRESET_PASS -c $KEY_GRIP <<< $PASSPHRASE 
# AND NOW WE check if a RAW passphrase is cached:
RET_VAL=$?
if [ $RET_VAL = 0 ]; then
        echo "Un-validated (RAW) passphrase is cached"
        # This is the part that I must use --pinentry-mode=loopback, so I will get correct return code
        echo "Now validating a RAW cached passphrase from gpg-agent ..."
        test=$(echo "1234" | gpg -q --pinentry-mode=loopback --status-fd 1 --sign --local-user "$KEY_ID" --passphrase-fd 0 > /dev/null)
        RET_VAL=$?
        if [ $RET_VAL = 0 ]; then
                echo "Passphrase is valid"
        else
                echo "Passphrase is invalid"
                # Can clear the passphrase and ask again for RAW passphrase
        fi

else
        echo "Error when trying to cache RAW passphrase"
        exit 1
        # Run again the script to cache RAW passphrase
fi
0
votes

If I may, you can start another script that could time this script's execution time. If the actual script exits then correct password, else, if the set time reaches, you kill the script's PID and the script ends.

-1
votes

you can try something like:

echo "1234" | gpg -q --batch --status-fd 1 --sign --local-user <KEY_ID> --passphrase-fd 0 --output /dev/null ||
{
    echo "Invalid passphrase or no passphrase is set in gpg-agent"
    exit
}
echo "Valid passphrase has been set in gpg-agent"

I am not sure whether this can deal with what looks like waiting for input.