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
parent
7779a87fac
commit
e4f2d533b1
|
@ -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,
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue