Add cipher, compress, and digest preferences and rename parameters.

* Rename gpghome to homedir.
 * Rename gpgbinary to binary.
 * Add setting to append '--no-use-agent' to the command string in an attempt
   to overcome bugs resulting on systems where the user has gpg-agent running
   in the background (with some configurations, this is run before X is
   started, and killing the agent can result in X dying) and GnuPG tries to
   call the program specified by the symlink at /usr/bin/pinentry, result in
   encryption and decryption failing due to the '--batch' option blasting
   through pinentry without input. This results in a complaint from GnuPG:

       gpg: Sorry, no terminal at all requested - can't get input

   This bug also prevents symmetric encryption/decryption from working in a
   similar manner.
       Daniel Kahn Gilmor's monkeysphere, if I am recalling correctly, has a
   hack to remove the $DISPLAY variable from the users environment, and then
   add it back in, but frankly this option frightens me, as unsetting the
   display could result in all X applications failing.
       Werner Koch's suggestions, from the gnupg-devel mailing list are:
   http://lists.gnupg.org/pipermail/gnupg-users/2007-April/030927.html
       And, for the record, '--no-use-agent' doesn't disable pinentry.
testing/mmn/mktime_takes_localtime_not_gmtime
Isis Lovecruft 2013-05-11 17:48:04 +00:00
parent 7779a87fac
commit e4f2d533b1
No known key found for this signature in database
GPG Key ID: A3ADB67A2CDB8B35
3 changed files with 83 additions and 72 deletions

View File

@ -122,27 +122,27 @@ class GPG(object):
'sign': Sign,
'verify': Verify,}
def __init__(self, gpgbinary=None, gpghome=None, verbose=False,
use_agent=False, keyring=None, secring=None, pubring=None,
options=None):
def __init__(self, binary=None, homedir=None, verbose=False,
use_agent=False, keyring=None, secring=None,
default_preference_list=None, options=None):
"""Initialize a GnuPG process wrapper.
:param str gpgbinary: Name for GnuPG binary executable. If the absolute
:param str binary: Name for GnuPG binary executable. If the absolute
path is not given, the evironment variable $PATH
is searched for the executable and checked that
the real uid/gid of the user has sufficient
permissions.
:param str gpghome: Full pathname to directory containing the public
:param str homedir: Full pathname to directory containing the public
and private keyrings. Default is whatever GnuPG
defaults to.
:param str keyring: raises :exc:DeprecationWarning. Use :param:pubring.
:param str secring: Name of alternative secret keyring file to use. If
left unspecified, this will default to using
'secring.gpg' in the :param:gpghome directory, and
'secring.gpg' in the :param:homedir directory, and
create that file if it does not exist.
:param str pubring: Name of alternative public keyring file to use. If
left unspecified, this will default to using
'pubring.gpg' in the :param:gpghome directory, and
'pubring.gpg' in the :param:homedir directory, and
create that file if it does not exist.
:param list options: A list of additional options to pass to the GPG
binary.
@ -150,24 +150,28 @@ class GPG(object):
problem invoking gpg.
"""
if not gpghome:
gpghome = _conf
self.gpghome = _fix_unsafe(gpghome)
if self.gpghome:
_util._create_gpghome(self.gpghome)
if not homedir:
homedir = _conf
self.homedir = _fix_unsafe(homedir)
if self.homedir:
_util._create_homedir(self.homedir)
else:
message = ("Unsuitable gpg home dir: %s" % gpghome)
message = ("Unsuitable gpg home dir: %s" % homedir)
logger.debug("GPG.__init__(): %s" % message)
self.gpgbinary = _util._find_gpgbinary(gpgbinary)
self.binary = _util._find_binary(binary)
if keyring is not None:
raise DeprecationWarning("Option 'keyring' changing to 'secring'")
if default_preference_list is None:
prefs = 'SHA512 SHA384 SHA256 AES256 CAMELLIA256 TWOFISH ZLIB ZIP'
else:
## xxx implement me, should return None on error
prefs = check_preference_list(default_preference_list)
self.default_preference_list = prefs
secring = 'secring.gpg' if secring is None else _fix_unsafe(secring)
pubring = 'pubring.gpg' if pubring is None else _fix_unsafe(pubring)
self.secring = os.path.join(self.gpghome, secring)
self.pubring = os.path.join(self.gpghome, pubring)
keyring = 'pubring.gpg' if keyring is None else _fix_unsafe(keyring)
self.secring = os.path.join(self.homedir, secring)
self.keyring = os.path.join(self.homedir, keyring)
self.options = _sanitise(options) if options else None
@ -176,10 +180,10 @@ class GPG(object):
self.encoding = sys.stdin.encoding
try:
assert self.gpghome is not None, "Got None for self.gpghome"
assert _util._has_readwrite(self.gpghome), ("Home dir %s needs r+w"
% self.gpghome)
assert self.gpgbinary, "Could not find gpgbinary %s" % full
assert self.homedir is not None, "Got None for self.homedir"
assert _util._has_readwrite(self.homedir), ("Home dir %s needs r+w"
% self.homedir)
assert self.binary, "Could not find binary %s" % full
assert isinstance(verbose, bool), "'verbose' must be boolean"
assert isinstance(use_agent, bool), "'use_agent' must be boolean"
if self.options:
@ -205,17 +209,19 @@ class GPG(object):
:func:parsers._sanitise. The ``passphrase`` argument needs to be True
if a passphrase will be sent to GPG, else False.
"""
cmd = [self.gpgbinary, '--status-fd 2 --no-tty --no-emit-version']
if self.gpghome:
cmd.append('--homedir "%s"' % self.gpghome)
if self.pubring:
cmd.append('--no-default-keyring --keyring %s' % self.pubring)
cmd = [self.binary, '--status-fd 2 --no-tty --no-emit-version']
if self.homedir:
cmd.append('--homedir "%s"' % self.homedir)
if self.keyring:
cmd.append('--no-default-keyring --keyring %s' % self.keyring)
if self.secring:
cmd.append('--secret-keyring %s' % self.secring)
if passphrase:
cmd.append('--batch --passphrase-fd 0')
if self.use_agent:
cmd.append('--use-agent')
else:
cmd.append('--no-use-agent')
if self.options:
[cmd.append(opt) for opt in iter(_sanitise_list(self.options))]
if args:
@ -375,7 +381,7 @@ class GPG(object):
def verify(self, data):
"""Verify the signature on the contents of the string ``data``.
>>> gpg = GPG(gpghome="keys")
>>> gpg = GPG(homedir="keys")
>>> input = gpg.gen_key_input(Passphrase='foo')
>>> key = gpg.gen_key(input)
>>> assert key
@ -440,7 +446,7 @@ class GPG(object):
>>> import shutil
>>> shutil.rmtree("keys")
>>> gpg = GPG(gpghome="keys")
>>> gpg = GPG(homedir="keys")
>>> input = gpg.gen_key_input()
>>> result = gpg.gen_key(input)
>>> print1 = result.fingerprint
@ -495,7 +501,7 @@ class GPG(object):
>>> import shutil
>>> shutil.rmtree("keys")
>>> gpg = GPG(gpghome="keys")
>>> gpg = GPG(homedir="keys")
>>> result = gpg.recv_keys('pgp.mit.edu', '3FF0DB166A7476EA')
>>> assert result
@ -583,7 +589,7 @@ class GPG(object):
>>> import shutil
>>> shutil.rmtree("keys")
>>> gpg = GPG(gpghome="keys")
>>> gpg = GPG(homedir="keys")
>>> input = gpg.gen_key_input()
>>> result = gpg.gen_key(input)
>>> print1 = result.fingerprint
@ -639,7 +645,7 @@ class GPG(object):
"""Generate a GnuPG key through batch file key generation. See
:meth:`GPG.gen_key_input()` for creating the control input.
>>> gpg = GPG(gpghome="keys")
>>> gpg = GPG(homedir="keys")
>>> input = gpg.gen_key_input()
>>> result = gpg.gen_key(input)
>>> assert result
@ -687,7 +693,7 @@ class GPG(object):
%secring foo.sec
%commit
>>> gpg = GPG(gpghome="keys")
>>> gpg = GPG(homedir="keys")
>>> params = {'name_real':'python-gnupg tester', 'name_email':'test@ing'}
>>> key_input = gpg.gen_key_input(**params)
>>> result = gpg.gen_key(input)
@ -850,7 +856,7 @@ class GPG(object):
for key, val in list(parms.items()):
out += "%s: %s\n" % (key, val)
out += "%%pubring %s\n" % self.pubring
out += "%%pubring %s\n" % self.keyring
out += "%%secring %s\n" % self.secring
if testing:
@ -922,7 +928,7 @@ class GPG(object):
>>> import shutil
>>> if os.path.exists("keys"):
... shutil.rmtree("keys")
>>> gpg = GPG(gpghome="keys")
>>> gpg = GPG(homedir="keys")
>>> input = gpg.gen_key_input(passphrase='foo')
>>> result = gpg.gen_key(input)
>>> print1 = result.fingerprint
@ -1001,10 +1007,10 @@ class GPGWrapper(GPG):
replaced by a more general class used throughout the project.
"""
def __init__(self, gpgbinary=None, gnupghome=_conf,
def __init__(self, binary=None, homedir=_conf,
verbose=False, use_agent=False, keyring=None, options=None):
super(GPGWrapper, self).__init__(gnupghome=gnupghome,
gpgbinary=gpgbinary,
binary=binary,
verbose=verbose,
use_agent=use_agent,
keyring=keyring,
@ -1036,13 +1042,14 @@ class GPGWrapper(GPG):
raise LookupError(
"GnuPG public key for subkey %s not found!" % subkey)
def encrypt(self, data, recipient, sign=None, always_trust=True,
def encrypt(self, data, recipient, sign_with=None, always_trust=True,
passphrase=None, symmetric=False):
"""
Encrypt data using GPG.
"""
# TODO: devise a way so we don't need to "always trust".
return super(GPGWrapper, self).encrypt(data, recipient, sign=sign,
return super(GPGWrapper, self).encrypt(data, recipient,
sign_with=sign,
always_trust=always_trust,
passphrase=passphrase,
symmetric=symmetric,

View File

@ -142,8 +142,8 @@ class GPGTestCase(unittest.TestCase):
self.assertTrue(os.path.isdir(hd), "Not a directory: %s" % hd)
shutil.rmtree(hd)
self.homedir = hd
self.gpg = gnupg.GPG(gpghome=hd, gpgbinary='gpg')
self.pubring = os.path.join(self.homedir, 'pubring.gpg')
self.gpg = gnupg.GPG(homedir=hd, binary='gpg')
self.keyring = os.path.join(self.homedir, 'keyring.gpg')
self.secring = os.path.join(self.homedir, 'secring.gpg')
def test_parsers_fix_unsafe(self):
@ -198,24 +198,24 @@ class GPGTestCase(unittest.TestCase):
env_copy = os.environ
path_copy = os.environ.pop('PATH')
with self.assertRaises(RuntimeError):
gnupg.GPG(gpghome=self.homedir)
gnupg.GPG(homedir=self.homedir)
os.environ = env_copy
os.environ.update({'PATH': path_copy})
def test_gpg_binary_not_abs(self):
"""Test that a non-absolute path to gpg results in a full path."""
self.assertTrue(os.path.isabs(self.gpg.gpgbinary))
self.assertTrue(os.path.isabs(self.gpg.binary))
def test_make_args_drop_protected_options(self):
"""Test that unsupported gpg options are dropped."""
self.gpg.options = ['--tyrannosaurus-rex', '--stegosaurus']
self.gpg.keyring = self.secring
cmd = self.gpg._make_args(None, False)
expected = ['/usr/bin/gpg',
'--status-fd 2 --no-tty --no-emit-version',
'--homedir "%s"' % HOME_DIR,
'--no-default-keyring --keyring %s' % self.pubring,
'--secret-keyring %s' % self.secring]
'--homedir "%s"' % self.homedir,
'--no-default-keyring --keyring %s' % self.keyring,
'--secret-keyring %s' % self.secring,
'--no-use-agent',]
self.assertListEqual(cmd, expected)
def test_make_args(self):
@ -223,7 +223,7 @@ class GPGTestCase(unittest.TestCase):
not_allowed = ['--bicycle', '--zeppelin', 'train', 'flying-carpet']
self.gpg.options = not_allowed[:-2]
args = self.gpg._make_args(not_allowed[2:], False)
self.assertTrue(len(args) == 5)
self.assertTrue(len(args) == 6)
for na in not_allowed:
self.assertNotIn(na, args)
@ -410,13 +410,15 @@ class GPGTestCase(unittest.TestCase):
def test_public_keyring(self):
"""Test that the public keyring is found in the gpg home directory."""
self.gpg.keyring = self.pubring
self.assertTrue(os.path.isfile(self.pubring))
## we have to use the keyring for GnuPG to create it:
keys = self.gpg.list_keys()
self.assertTrue(os.path.isfile(self.gpg.keyring))
def test_secret_keyring(self):
"""Test that the secret keyring is found in the gpg home directory."""
self.gpg.keyring = self.secring
self.assertTrue(os.path.isfile(self.secring))
## we have to use the secring for GnuPG to create it:
keys = self.gpg.list_keys(secret=True)
self.assertTrue(os.path.isfile(self.gpg.secring))
def test_import_and_export(self):
"""Test that key import and export works."""
@ -725,7 +727,8 @@ suites = { 'parsers': set(['test_parsers_fix_unsafe',
'test_parsers_is_hex_valid',
'test_parsers_is_hex_invalid',
'test_copy_data_bytesio',]),
'basic': set(['test_gpghome_creation',
'basic': set(['test_homedir_creation',
'test_binary_discovery',
'test_gpg_binary',
'test_gpg_binary_not_abs',
'test_gpg_binary_version_str',

View File

@ -111,28 +111,29 @@ def _copy_data(instream, outstream):
try:
outstream.close()
except IOError:
logger.exception('_copy_data(): Got IOError while closing %s' % outstream)
logger.exception('_copy_data(): Got IOError while closing %s'
% outstream)
else:
logger.debug("_copy_data(): Closed output, %d bytes sent." % sent)
def _create_gpghome(gpghome):
def _create_homedir(homedir):
"""Create the specified GnuPG home directory, if necessary.
:param str gpghome: The directory to use.
:param str homedir: The directory to use.
:rtype: bool
:returns: True if no errors occurred and the directory was created or
existed beforehand, False otherwise.
"""
## xxx how will this work in a virtualenv?
if not os.path.isabs(gpghome):
message = ("Got non-abs gpg home dir path: %s" % gpghome)
logger.warn("util._create_gpghome(): %s" % message)
gpghome = os.path.abspath(gpghome)
if not os.path.isdir(gpghome):
message = ("Creating gpg home dir: %s" % gpghome)
logger.warn("util._create_gpghome(): %s" % message)
if not os.path.isabs(homedir):
message = ("Got non-abs gpg home dir path: %s" % homedir)
logger.warn("util._create_homedir(): %s" % message)
homedir = os.path.abspath(homedir)
if not os.path.isdir(homedir):
message = ("Creating gpg home dir: %s" % homedir)
logger.warn("util._create_homedir(): %s" % message)
try:
os.makedirs(gpghome, 0x1C0)
os.makedirs(homedir, 0x1C0)
except OSError as ose:
logger.error(ose, exc_info=1)
return False
@ -141,22 +142,22 @@ def _create_gpghome(gpghome):
else:
return True
def _find_gpgbinary(gpgbinary=None):
def _find_binary(binary=None):
"""Find the absolute path to the GnuPG binary.
Also run checks that the binary is not a symlink, and check that
our process real uid has exec permissions.
:param str gpgbinary: The path to the GnuPG binary.
:param str binary: The path to the GnuPG binary.
:raises: :exc:RuntimeError if it appears that GnuPG is not installed.
:rtype: str
:returns: The absolute path to the GnuPG binary to use, if no exceptions
occur.
"""
binary = None
if gpgbinary is not None:
if not os.path.isabs(gpgbinary):
try: binary = _which(gpgbinary)[0]
gpg_binary = None
if binary is not None:
if not os.path.isabs(binary):
try: binary = _which(binary)[0]
except IndexError as ie: logger.debug(ie.message)
if binary is None:
try: binary = _which('gpg')[0]
@ -166,7 +167,7 @@ def _find_gpgbinary(gpgbinary=None):
assert not os.path.islink(binary), "Path to gpg binary is symlink"
assert os.access(binary, os.X_OK), "Lacking +x perms for gpg binary"
except (AssertionError, AttributeError) as ae:
logger.debug("util._find_gpgbinary(): %s" % ae.message)
logger.debug("util._find_binary(): %s" % ae.message)
else:
return binary