I'm struggling with some issues while scripting gpg with bash
on a Debian 6.0.6 box. I have a script that does a batch of operations and wants to make sure that a gpg-agent is available before it attempts to proceed.
Since gpg-agent will take no action and return success if launched when already running, ensuring the agent is present is as simple as:
eval $(gpg-agent --daemon)
gpg-agent
start, or will report:
gpg-agent[21927]: a gpg-agent is already running - not starting a new one
and return 0 (success) if already running.
The problem arises when an agent is already running in another session. gpg-agent
says that it's already running ... but gpg
its self then claims that it's unavailable.
$ gpg-agent --version
gpg-agent (GnuPG) 2.0.19
libgcrypt 1.5.0
$ gpg --version
gpg (GnuPG) 1.4.13
$ eval $(gpg-agent --daemon)
gpg-agent[21927]: a gpg-agent is already running - not starting a new one
$ gpg -d demo-file.asc
gpg: gpg-agent is not available in this session
This leaves me frustrated and confused. It appears that gpg-agent
is detecting the agent a different way to gpg its self. Worse, gpg
offers no way to ask if the agent is available in a scriptable way, much as it likes to silently ignore recipients with unusable keys and still return success, so it's very hard to detect this problem before beginning the batch. I don't want to get into parsing gpg's output for i18n reasons among others.
You can reproduce this by ensuring you don't have a gpg-agent running or have GPG_AGENT_INFO
set, then in one terminal running eval $(gpg-agent --daemon)
and in another terminal running the above. You'll note that gpg-agent says it's already running, but gpg fails to connect to the agent.
Ideas?
UPDATE: gpg-agent
detects another agent by looking for a socket file in a well-known location and writing to it to test for aliveness, a per this strace
:
socket(PF_FILE, SOCK_STREAM, 0) = 5
connect(5, {sa_family=AF_FILE, sun_path="/home/craig/.gnupg/S.gpg-agent"}, 32) = 0
fcntl(5, F_GETFL) = 0x2 (flags O_RDWR)
fcntl(5, F_GETFL) = 0x2 (flags O_RDWR)
select(6, [5], NULL, NULL, {0, 0}) = 1 (in [5], left {0, 0})
read(5, "OK Pleased to meet you, process "..., 1002) = 38
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f41a3e61000
write(2, "gpg-agent: gpg-agent running and"..., 43gpg-agent: gpg-agent running and available
) = 43
while GnuPG seems to look only at the environment, ignoring the well-known socket location. In common/simple-pwquery.c
:
/* Try to open a connection to the agent, send all options and return
the file descriptor for the connection. Return -1 in case of
error. */
static int
agent_open (int *rfd)
{
int rc;
int fd;
char *infostr, *p;
struct sockaddr_un client_addr;
size_t len;
int prot;
char line[200];
int nread;
*rfd = -1;
infostr = getenv ( "GPG_AGENT_INFO" );
if ( !infostr || !*infostr )
infostr = default_gpg_agent_info;
if ( !infostr || !*infostr )
{
#ifdef SPWQ_USE_LOGGING
log_error (_("gpg-agent is not available in this session\n"));
#endif
return SPWQ_NO_AGENT;
}
/* blah blah blah truncated blah */
}
I don't really want to kill the agent just to make sure I can start it again, and there's no standard place where the user's agent might write an environment file. Worse, I can't even test for the presence of GPG_AGENT_INFO
in the environment since that could refer to a stale (dead) agent that's since been replaced ... and neither gpg
nor gpg-agent
provide a command line option to ping the agent and return true if it's ok.
gpg-connect-agent /bye
The running gpg agent's major version is 2. You should invoke gpg2 rather than gpg as answered here: https://unix.stackexchange.com/questions/231386/how-to-make-gpg-find-gpg-agent
So far the best workaround I have is the following hideous mess:
This will check for
GPG_AGENT_INFO
in the environment and if it's set, make sure gpg-agent is actually running. (I'm not yet sure how this interacts with other gpg-agent implementations like GNOME's agent). If the agent info is set but the agent is not running it doesn't know how to cope and gives up.If the agent info isn't set it checks to see if the agent is running. If it is, it looks for the env info in a couple of well known locations and if it fails to find it, gives up.
If the agent isn't running and the agent info is unset, it starts an agent, writes the env file to a private location, and proceeds.
To say that I'm unhappy with this horrible, user-hostile and unreliable hack is an understatement.
It's very surprising that
gpg
, a security/crypto tool, will ignore arguments and proceed.--use-agent
should be a fatal error if an agent is not running, at least optionally, much as specifying-r
with an invalid recipient should be an error rather than ignored. The fact thatgpg
finds its agent a different way to thegpg-agent
command is bewildering.On my Ubuntu system
gpg-agent
is configured to write its environment file to~/.gnupg/gpg-agent-info-$(hostname)
(which is done by/etc/X11/Xsession.d/90gpg-agent
). If your system doesn't do this you could modify the way the agent is started to write an environment file in a well known location which can later be sourced. For example: