Page MenuHome GnuPG

All of max-cache-ttl, default-cache-ttl, and no-allow-external-cache are ignored
Closed, InvalidPublic

Description

Our application uses GPGME to encrypt files with a public key, and decrypt them with
a private key, from the user's keyring. Because the private key is protected with a
password, GPGME invokes the gpg-agent (which in turn invokes the pinentry program) to
retrieve it. (Please correct me if I am wrong.)

Ever since gpg-agent switched to sockets from pipes for its communication, we started
seeing issues with password caching. Specifically, a new gpg-agent daemon process
would start and stay for every different $GNUPGHOME value, and, what is more
concerning, the password returned by the pinentry program would remain cached
forever, making it effectively impossible to "forget" it.

Initially, we were able to work around those issues by specifying no-use-standard-
socket in gpg-agent.conf. However, in our latest deployment (and according to your
documentation) that no longer has any effect.

Since we do not want to cache passwords, we tried including max-cache-ttl, default-
cache-ttl, and no-allow-external-cache options in gpg-agent.conf, with values varying
from -1 to 0 to bigger positive integers for the first two of these options. The only
change in behavior we observed was when the value of -1 was used, in which case the
pinentry program (and gpg-agent as far as we can tell) was not invoked at all.

So, please help us understand how we can prevent gpg-agent from caching passwords,
and whether it is possible to avoid multiple long-lived gpg-agent processes when
$GNUPGHOME changes. If you are willing to consider software patches from us, perhaps,
we can implement the necessary changes to make the agent honor the max-cache-ttl and
default-cache-ttl options.

Ivan

Details

Version
1.4.19

Event Timeline

Hi,

I think having a different agent for different values of GNUPGHOME is correct
behavior. It is desirable as it increases isolation.

What version of GnuPG are you using? (You filed this bug report against
libgcrypt.) Did you build from source? What distribution?

Thanks.

werner removed a project: libgcrypt.
werner added a subscriber: werner.

I changed the category to gnupg because of version 1.4.19 which is the latest
GnuPG-1 release.

Which version of GPGME are you using? Is a GnuPG-2 version also installed (run
"gpg2 --version")?

GnuPG version is 1.4.19, as reported by gpg --version, but gpg2 is, in fact, also installed:

  $ gpg2 --version
  gpg (GnuPG) 2.1.4
  libgcrypt 1.6.3
  ...

We have seen this problem on various OSs, but in this particular case it is Fedora 22. And we always install GnuPG from package
managers.

Here are all the gpgme files I see:

$ rpm -ql gpgme | grep \.so

/usr/lib64/libgpgme-pthread.so.11

/usr/lib64/libgpgme-pthread.so.11.11.0

/usr/lib64/libgpgme.so.11

/usr/lib64/libgpgme.so.11.11.0

The SO number is unfortunately not helpful to show the GPGME version.

/usr/bin/gpgme-config --version

displays the gpgme version but that assumes that the shared library
matches the version of the installed development files. You may copy

#include <stdio.h>
#include <gpgme.h>

int
main (void)
{

  printf ("Version from header: %s (0x%06x)\n",
          GPGME_VERSION, GPGME_VERSION_NUMBER);
  printf ("Version from binary: %s\n", gpgme_check_version (NULL));
  printf ("Copyright blurb ...:%s\n", gpgme_check_version ("\x01\x01"));

  return 0;

}

into a file gpgme-version.c and run

  cc gpgme-version.c -o gpgme-version $(gpgme-config --cflags --libs)
  ./gpgme-version

to display the real version info.

OK, here are the outputs:

  $ /usr/bin/gpgme-config --version
  1.4.3
  $ ./gpgme-version
  Version from header: 1.4.3 (0x010403)
  Version from binary: 1.4.3
  Copyright blurb ...:

  This is GPGME 1.4.3 - The GnuPG Made Easy library
  Copyright (C) 2000 Werner Koch
  Copyright (C) 2001--2013 g10 Code GmbH

  (d788c35 2014-12-06T04:06+0000)

I've tried to reproduce this issue by setting max-cache-ttl and default-
cache-ttl to 0 in my gpg-agent.conf and running echo | gpg2 -s -a multiple times
in a row. The behavior is such that I have to enter the password each time. I
also traced the control flow in gpg-agent using gdb. The point where the
password is cached is in agent/cache.c:agent_put_cache. The passed ttl is 0 and
opt.def.cache_ttl is also 0. Thus, the password is not cached in my experiments.

Can you please confirm using the most recent version of GnuPG (2.1.9 as of
today) that a gpg-agent.conf with just the above options set caches the password
for you? Does the problem only occur when you are using your gpgme-based
application?

Thanks!

OK, based on my findings, I have to clarify that it is the environment of the
agent program that is cached, thus rendering the pinentry program oblivious to
the further changes of the environment, even though it is those changes that
"guide" it in the retrieval of the correct passphrase.

Attached is a test program. I apologize that it is TCSH, but that is what we use
in our shop (for portability). Running test.csh should produce an output like
this:

    > test.csh < /dev/null
    ---------------------------------------------------
     GPG version:
    ---------------------------------------------------
    gpg (GnuPG) 2.1.9
    libgcrypt 1.6.3
    Copyright (C) 2015 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.
    
    Home: /tmp/gpg-caching/.gnupg
    Supported algorithms:
    Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA
    Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
            CAMELLIA128, CAMELLIA192, CAMELLIA256
    Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
    Compression: Uncompressed, ZIP, ZLIB, BZIP2
    
    ---------------------------------------------------
     Available keys:
    ---------------------------------------------------
    /tmp/gpg-caching/.gnupg/pubring.kbx
    -----------------------------------
    pub   dsa1024/E65831AC 2015-11-24
    uid         [ultimate] user1 <user1@email.com>
    sub   rsa2048/95F85202 2015-11-24
    
    pub   dsa1024/B0731F16 2015-11-24
    uid         [ultimate] user2 <user2@email.com>
    sub   rsa2048/67841B64 2015-11-24
    
    
    ---------------------------------------------------
     Key 1 decrypted using gpg2 directly:
    ---------------------------------------------------
    gpg: encrypted with 2048-bit RSA key, ID 95F85202, created 2015-11-24
          "user1 <user1@email.com>"
    32-bit-long key for user1 (ONE).
    
    ---------------------------------------------------
     Key 2 decrypted using gpg2 directly:
    ---------------------------------------------------
    gpg: encrypted with 2048-bit RSA key, ID 67841B64, created 2015-11-24
          "user2 <user2@email.com>"
    gpg: public key decryption failed: Broken pipe  
    gpg: decryption failed: No secret key
    
    
    ---------------------------------------------------
     Key 1 decrypted using gpgme:
    ---------------------------------------------------
    32-bit-long key for user1 (ONE).
    
    ---------------------------------------------------
     Key 2 decrypted using gpgme:
    ---------------------------------------------------
    Error while accessing key file user2.key: Decryption failed

This test creates two private/public key pairs in the same keyring; the password
to the private key of the first is "password1"; that of the second is
"password2". It then generates two files (user1.key and user2.key), one encrypted
with the first public key, the other with the second. The contents of those files
are "32-bit-long key for user1 (ONE)." and "32-bit-long key for user2 (TWO).",
respectively. The test also involves a simplistic pinentry program that returns
the value of $password to the agent. The point is to demonstrate that once the
gpg agent is started, it caches the environment, such that even updating
$password to the right value is of no use. And that is why only user1.key is
decrypted successfully (the agent was started to decrypt it).

marcus added a subscriber: marcus.

It is not supported to pass arbitrary information through gpg and gpg-agent to pinentry via environment variables. You will probably find good use of the pinentry-mode=loopback option.