mirror of
https://git.launchpad.net/ubuntu-dev-tools
synced 2025-05-13 18:01:28 +00:00
ubuntutools: add pull-* --no-verify-signature option, don't fail if no pub key
Change dsc verification to fail only if the public key was available, but signature verification failed. If no public key is available for the dsc, print warning only. (LP: #1700846) Also add pull-* parameter --no-verify-signature to manually prevent failure when signature verification fails.
This commit is contained in:
parent
41a6c47ac2
commit
3a413760f3
@ -129,7 +129,7 @@ class SourcePackage(object):
|
|||||||
|
|
||||||
def __init__(self, package=None, version=None, component=None,
|
def __init__(self, package=None, version=None, component=None,
|
||||||
dscfile=None, lp=None, mirrors=(), workdir='.', quiet=False,
|
dscfile=None, lp=None, mirrors=(), workdir='.', quiet=False,
|
||||||
series=None, pocket=None):
|
series=None, pocket=None, verify_signature=False):
|
||||||
"""Can be initialised using either package or dscfile.
|
"""Can be initialised using either package or dscfile.
|
||||||
If package is specified, either the version or series can also be
|
If package is specified, either the version or series can also be
|
||||||
specified; using version will get the specific package version,
|
specified; using version will get the specific package version,
|
||||||
@ -149,6 +149,7 @@ class SourcePackage(object):
|
|||||||
self._use_series = True
|
self._use_series = True
|
||||||
self._pocket = pocket
|
self._pocket = pocket
|
||||||
self._dsc_source = dscfile
|
self._dsc_source = dscfile
|
||||||
|
self._verify_signature = verify_signature
|
||||||
|
|
||||||
# Cached values:
|
# Cached values:
|
||||||
self._distribution = None
|
self._distribution = None
|
||||||
@ -321,7 +322,7 @@ class SourcePackage(object):
|
|||||||
continue
|
continue
|
||||||
yield (bpph.getFileName(), bpph.getUrl(), 0)
|
yield (bpph.getFileName(), bpph.getUrl(), 0)
|
||||||
|
|
||||||
def pull_dsc(self, verify_signature=False):
|
def pull_dsc(self):
|
||||||
"Retrieve dscfile and parse"
|
"Retrieve dscfile and parse"
|
||||||
if self._dsc_source:
|
if self._dsc_source:
|
||||||
parsed = urlparse(self._dsc_source)
|
parsed = urlparse(self._dsc_source)
|
||||||
@ -333,16 +334,18 @@ class SourcePackage(object):
|
|||||||
url = self._lp_url(self.dsc_name)
|
url = self._lp_url(self.dsc_name)
|
||||||
self._download_dsc(url)
|
self._download_dsc(url)
|
||||||
|
|
||||||
self._check_dsc(verify_signature=verify_signature)
|
self._check_dsc()
|
||||||
|
|
||||||
def _download_dsc(self, url):
|
def _download_dsc(self, url):
|
||||||
"Download specified dscfile and parse"
|
"Download specified dscfile and parse"
|
||||||
parsed = urlparse(url)
|
parsed = urlparse(url)
|
||||||
if parsed.scheme == 'file':
|
if parsed.scheme == 'file':
|
||||||
with open(parsed.path, 'r') as f:
|
Logger.debug("Using dsc file '%s'" % parsed.path)
|
||||||
|
with open(parsed.path, 'rb') as f:
|
||||||
body = f.read()
|
body = f.read()
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
|
Logger.debug("Trying dsc url '%s'" % url)
|
||||||
response, body = httplib2.Http().request(url)
|
response, body = httplib2.Http().request(url)
|
||||||
except httplib2.HttpLib2Error as e:
|
except httplib2.HttpLib2Error as e:
|
||||||
raise DownloadError(e)
|
raise DownloadError(e)
|
||||||
@ -351,13 +354,14 @@ class SourcePackage(object):
|
|||||||
response.reason))
|
response.reason))
|
||||||
self._dsc = Dsc(body)
|
self._dsc = Dsc(body)
|
||||||
|
|
||||||
def _check_dsc(self, verify_signature=False):
|
def _check_dsc(self):
|
||||||
"Check that the dsc matches what we are expecting"
|
"Check that the dsc matches what we are expecting"
|
||||||
assert self._dsc is not None
|
assert self._dsc is not None
|
||||||
self.source = self.dsc['Source']
|
self.source = self.dsc['Source']
|
||||||
self._version = Version(self.dsc['Version'])
|
self._version = Version(self.dsc['Version'])
|
||||||
|
|
||||||
valid = False
|
valid = False
|
||||||
|
no_pub_key = False
|
||||||
message = None
|
message = None
|
||||||
gpg_info = None
|
gpg_info = None
|
||||||
try:
|
try:
|
||||||
@ -381,19 +385,24 @@ class SourcePackage(object):
|
|||||||
% (gpg_info['GOODSIG'][1], gpg_info['GOODSIG'][0]))
|
% (gpg_info['GOODSIG'][1], gpg_info['GOODSIG'][0]))
|
||||||
elif 'VALIDSIG' in gpg_info:
|
elif 'VALIDSIG' in gpg_info:
|
||||||
message = 'Valid signature by 0x%s' % gpg_info['VALIDSIG'][0]
|
message = 'Valid signature by 0x%s' % gpg_info['VALIDSIG'][0]
|
||||||
if verify_signature:
|
elif 'NO_PUBKEY' in gpg_info:
|
||||||
|
no_pub_key = True
|
||||||
|
message = 'Public key not found, could not verify signature'
|
||||||
|
if self._verify_signature:
|
||||||
if valid:
|
if valid:
|
||||||
Logger.normal(message)
|
Logger.normal(message)
|
||||||
|
elif no_pub_key:
|
||||||
|
Logger.warn(message)
|
||||||
else:
|
else:
|
||||||
Logger.error(message)
|
Logger.error(message)
|
||||||
raise DownloadError(message)
|
raise DownloadError(message)
|
||||||
else:
|
else:
|
||||||
Logger.info(message)
|
Logger.info(message)
|
||||||
|
|
||||||
def _write_dsc(self, verify_signature=True):
|
def _write_dsc(self):
|
||||||
"Write dsc file to workdir"
|
"Write dsc file to workdir"
|
||||||
if self._dsc is None:
|
if self._dsc is None:
|
||||||
self.pull_dsc(verify_signature=verify_signature)
|
self.pull_dsc()
|
||||||
with open(self.dsc_pathname, 'wb') as f:
|
with open(self.dsc_pathname, 'wb') as f:
|
||||||
f.write(self.dsc.raw_text)
|
f.write(self.dsc.raw_text)
|
||||||
|
|
||||||
@ -477,9 +486,9 @@ class SourcePackage(object):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def pull(self, verify_signature=False):
|
def pull(self):
|
||||||
"Pull into workdir"
|
"Pull into workdir"
|
||||||
self._write_dsc(verify_signature=verify_signature)
|
self._write_dsc()
|
||||||
for entry in self.dsc['Files']:
|
for entry in self.dsc['Files']:
|
||||||
name = entry['name']
|
name = entry['name']
|
||||||
for url in self._source_urls(name):
|
for url in self._source_urls(name):
|
||||||
@ -648,10 +657,10 @@ class DebianSourcePackage(SourcePackage):
|
|||||||
continue
|
continue
|
||||||
yield (f.name, f.getUrl(), f.size)
|
yield (f.name, f.getUrl(), f.size)
|
||||||
|
|
||||||
def pull_dsc(self, verify_signature=True):
|
def pull_dsc(self):
|
||||||
"Retrieve dscfile and parse"
|
"Retrieve dscfile and parse"
|
||||||
try:
|
try:
|
||||||
super(DebianSourcePackage, self).pull_dsc(verify_signature)
|
super(DebianSourcePackage, self).pull_dsc()
|
||||||
return
|
return
|
||||||
except DownloadError:
|
except DownloadError:
|
||||||
pass
|
pass
|
||||||
@ -666,7 +675,7 @@ class DebianSourcePackage(SourcePackage):
|
|||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
raise DownloadError('dsc could not be found anywhere')
|
raise DownloadError('dsc could not be found anywhere')
|
||||||
self._check_dsc(verify_signature=verify_signature)
|
self._check_dsc()
|
||||||
|
|
||||||
# Local methods:
|
# Local methods:
|
||||||
@property
|
@property
|
||||||
|
@ -83,6 +83,8 @@ def create_argparser(default_pull=None, default_distro=None, default_arch=None):
|
|||||||
help='Preferred mirror(s)')
|
help='Preferred mirror(s)')
|
||||||
parser.add_argument('--no-conf', action='store_true',
|
parser.add_argument('--no-conf', action='store_true',
|
||||||
help="Don't read config files or environment variables")
|
help="Don't read config files or environment variables")
|
||||||
|
parser.add_argument('--no-verify-signature', action='store_true',
|
||||||
|
help="Don't fail if dsc signature can't be verified")
|
||||||
parser.add_argument('-a', '--arch', default=default_arch,
|
parser.add_argument('-a', '--arch', default=default_arch,
|
||||||
help=help_default_arch)
|
help=help_default_arch)
|
||||||
parser.add_argument('-p', '--pull', default=default_pull,
|
parser.add_argument('-p', '--pull', default=default_pull,
|
||||||
@ -177,6 +179,7 @@ def pull(options):
|
|||||||
assert hasattr(options, 'verbose')
|
assert hasattr(options, 'verbose')
|
||||||
assert hasattr(options, 'download_only')
|
assert hasattr(options, 'download_only')
|
||||||
assert hasattr(options, 'no_conf')
|
assert hasattr(options, 'no_conf')
|
||||||
|
assert hasattr(options, 'no_verify_signature')
|
||||||
# these are type string
|
# these are type string
|
||||||
assert hasattr(options, 'arch')
|
assert hasattr(options, 'arch')
|
||||||
assert hasattr(options, 'pull')
|
assert hasattr(options, 'pull')
|
||||||
@ -234,7 +237,8 @@ def pull(options):
|
|||||||
pkgcls = DISTRO_PKG_CLASS[distro]
|
pkgcls = DISTRO_PKG_CLASS[distro]
|
||||||
srcpkg = pkgcls(package=package, version=version,
|
srcpkg = pkgcls(package=package, version=version,
|
||||||
series=release, pocket=pocket,
|
series=release, pocket=pocket,
|
||||||
mirrors=mirrors, dscfile=dscfile)
|
mirrors=mirrors, dscfile=dscfile,
|
||||||
|
verify_signature=(not options.no_verify_signature))
|
||||||
spph = srcpkg.lp_spph
|
spph = srcpkg.lp_spph
|
||||||
except PackageNotFoundException as e:
|
except PackageNotFoundException as e:
|
||||||
Logger.error(str(e))
|
Logger.error(str(e))
|
||||||
|
@ -145,11 +145,14 @@ class LocalSourcePackageTestCase(unittest.TestCase):
|
|||||||
return self.request_404(url)
|
return self.request_404(url)
|
||||||
|
|
||||||
def test_local_copy(self):
|
def test_local_copy(self):
|
||||||
pkg = self.SourcePackage('example', '1.0-1', 'main',
|
pkg = self.SourcePackage(package='example',
|
||||||
|
version='1.0-1',
|
||||||
|
component='main',
|
||||||
dscfile='test-data/example_1.0-1.dsc',
|
dscfile='test-data/example_1.0-1.dsc',
|
||||||
workdir=self.workdir)
|
workdir=self.workdir,
|
||||||
|
verify_signature=False)
|
||||||
pkg.quiet = True
|
pkg.quiet = True
|
||||||
pkg.pull(verify_signature=False)
|
pkg.pull()
|
||||||
pkg.unpack()
|
pkg.unpack()
|
||||||
|
|
||||||
def test_workdir_srcpkg_noinfo(self):
|
def test_workdir_srcpkg_noinfo(self):
|
||||||
@ -159,9 +162,10 @@ class LocalSourcePackageTestCase(unittest.TestCase):
|
|||||||
|
|
||||||
pkg = self.SourcePackage(dscfile=os.path.join(self.workdir,
|
pkg = self.SourcePackage(dscfile=os.path.join(self.workdir,
|
||||||
'example_1.0-1.dsc'),
|
'example_1.0-1.dsc'),
|
||||||
workdir=self.workdir)
|
workdir=self.workdir,
|
||||||
|
verify_signature=False)
|
||||||
pkg.quiet = True
|
pkg.quiet = True
|
||||||
pkg.pull(verify_signature=False)
|
pkg.pull()
|
||||||
pkg.unpack()
|
pkg.unpack()
|
||||||
|
|
||||||
def test_workdir_srcpkg_info(self):
|
def test_workdir_srcpkg_info(self):
|
||||||
@ -169,12 +173,14 @@ class LocalSourcePackageTestCase(unittest.TestCase):
|
|||||||
shutil.copy2('test-data/example_1.0.orig.tar.gz', self.workdir)
|
shutil.copy2('test-data/example_1.0.orig.tar.gz', self.workdir)
|
||||||
shutil.copy2('test-data/example_1.0-1.debian.tar.xz', self.workdir)
|
shutil.copy2('test-data/example_1.0-1.debian.tar.xz', self.workdir)
|
||||||
|
|
||||||
pkg = self.SourcePackage('example', '1.0-1', 'main',
|
pkg = self.SourcePackage(package='example', version='1.0-1',
|
||||||
|
component='main',
|
||||||
dscfile=os.path.join(self.workdir,
|
dscfile=os.path.join(self.workdir,
|
||||||
'example_1.0-1.dsc'),
|
'example_1.0-1.dsc'),
|
||||||
workdir=self.workdir)
|
workdir=self.workdir,
|
||||||
|
verify_signature=False)
|
||||||
pkg.quiet = True
|
pkg.quiet = True
|
||||||
pkg.pull(verify_signature=False)
|
pkg.pull()
|
||||||
pkg.unpack()
|
pkg.unpack()
|
||||||
|
|
||||||
def test_verification(self):
|
def test_verification(self):
|
||||||
@ -185,19 +191,25 @@ class LocalSourcePackageTestCase(unittest.TestCase):
|
|||||||
'r+b') as f:
|
'r+b') as f:
|
||||||
f.write(b'CORRUPTION')
|
f.write(b'CORRUPTION')
|
||||||
|
|
||||||
pkg = self.SourcePackage('example', '1.0-1', 'main',
|
pkg = self.SourcePackage(package='example',
|
||||||
|
version='1.0-1',
|
||||||
|
component='main',
|
||||||
dscfile='test-data/example_1.0-1.dsc',
|
dscfile='test-data/example_1.0-1.dsc',
|
||||||
workdir=self.workdir)
|
workdir=self.workdir,
|
||||||
|
verify_signature=False)
|
||||||
pkg.quiet = True
|
pkg.quiet = True
|
||||||
pkg.pull(verify_signature=False)
|
pkg.pull()
|
||||||
|
|
||||||
def test_pull(self):
|
def test_pull(self):
|
||||||
pkg = self.SourcePackage('example', '1.0-1', 'main',
|
pkg = self.SourcePackage(package='example',
|
||||||
workdir=self.workdir)
|
version='1.0-1',
|
||||||
|
component='main',
|
||||||
|
workdir=self.workdir,
|
||||||
|
verify_signature=False)
|
||||||
|
|
||||||
pkg.url_opener = self.url_opener
|
pkg.url_opener = self.url_opener
|
||||||
pkg.quiet = True
|
pkg.quiet = True
|
||||||
pkg.pull(verify_signature=False)
|
pkg.pull()
|
||||||
|
|
||||||
def test_mirrors(self):
|
def test_mirrors(self):
|
||||||
mirror = 'http://mirror'
|
mirror = 'http://mirror'
|
||||||
@ -209,15 +221,21 @@ class LocalSourcePackageTestCase(unittest.TestCase):
|
|||||||
url_opener = mock.MagicMock(spec=OpenerDirector)
|
url_opener = mock.MagicMock(spec=OpenerDirector)
|
||||||
url_opener.open.side_effect = _callable_iter
|
url_opener.open.side_effect = _callable_iter
|
||||||
|
|
||||||
pkg = self.SourcePackage('example', '1.0-1', 'main',
|
pkg = self.SourcePackage(package='example',
|
||||||
workdir=self.workdir, mirrors=[mirror])
|
version='1.0-1',
|
||||||
|
component='main',
|
||||||
|
workdir=self.workdir,
|
||||||
|
mirrors=[mirror],
|
||||||
|
verify_signature=False)
|
||||||
pkg.url_opener = url_opener
|
pkg.url_opener = url_opener
|
||||||
pkg.quiet = True
|
pkg.quiet = True
|
||||||
pkg.pull(verify_signature=False)
|
pkg.pull()
|
||||||
|
|
||||||
def test_dsc_missing(self):
|
def test_dsc_missing(self):
|
||||||
self.mock_http.side_effect = self.request_404
|
self.mock_http.side_effect = self.request_404
|
||||||
pkg = self.SourcePackage('example', '1.0-1', 'main',
|
pkg = self.SourcePackage(package='example',
|
||||||
|
version='1.0-1',
|
||||||
|
component='main',
|
||||||
workdir=self.workdir)
|
workdir=self.workdir)
|
||||||
pkg.quiet = True
|
pkg.quiet = True
|
||||||
self.assertRaises(ubuntutools.archive.DownloadError, pkg.pull)
|
self.assertRaises(ubuntutools.archive.DownloadError, pkg.pull)
|
||||||
@ -243,12 +261,15 @@ class DebianLocalSourcePackageTestCase(LocalSourcePackageTestCase):
|
|||||||
url_opener = mock.MagicMock(spec=OpenerDirector)
|
url_opener = mock.MagicMock(spec=OpenerDirector)
|
||||||
url_opener.open.side_effect = _callable_iter
|
url_opener.open.side_effect = _callable_iter
|
||||||
|
|
||||||
pkg = self.SourcePackage('example', '1.0-1', 'main',
|
pkg = self.SourcePackage(package='example',
|
||||||
workdir=self.workdir, mirrors=[debian_mirror,
|
version='1.0-1',
|
||||||
debsec_mirror])
|
component='main',
|
||||||
|
workdir=self.workdir,
|
||||||
|
mirrors=[debian_mirror, debsec_mirror],
|
||||||
|
verify_signature=False)
|
||||||
pkg.quiet = True
|
pkg.quiet = True
|
||||||
pkg.url_opener = url_opener
|
pkg.url_opener = url_opener
|
||||||
pkg.pull(verify_signature=False)
|
pkg.pull()
|
||||||
pkg.unpack()
|
pkg.unpack()
|
||||||
|
|
||||||
def test_dsc_missing(self):
|
def test_dsc_missing(self):
|
||||||
@ -262,10 +283,14 @@ class DebianLocalSourcePackageTestCase(LocalSourcePackageTestCase):
|
|||||||
'[GNUPG:] GOODSIG DEADBEEF Joe Developer '
|
'[GNUPG:] GOODSIG DEADBEEF Joe Developer '
|
||||||
'<joe@example.net>')
|
'<joe@example.net>')
|
||||||
|
|
||||||
pkg = self.SourcePackage('example', '1.0-1', 'main',
|
pkg = self.SourcePackage(package='example',
|
||||||
workdir=self.workdir, mirrors=[mirror])
|
version='1.0-1',
|
||||||
|
component='main',
|
||||||
|
workdir=self.workdir,
|
||||||
|
mirrors=[mirror],
|
||||||
|
verify_signature=False)
|
||||||
pkg.url_opener = self.url_opener
|
pkg.url_opener = self.url_opener
|
||||||
pkg.pull(verify_signature=False)
|
pkg.pull()
|
||||||
|
|
||||||
def test_dsc_badsig(self):
|
def test_dsc_badsig(self):
|
||||||
mirror = 'http://mirror'
|
mirror = 'http://mirror'
|
||||||
@ -277,8 +302,11 @@ class DebianLocalSourcePackageTestCase(LocalSourcePackageTestCase):
|
|||||||
mock_gpg_info.return_value = debian.deb822.GpgInfo.from_output(
|
mock_gpg_info.return_value = debian.deb822.GpgInfo.from_output(
|
||||||
'[GNUPG:] ERRSIG DEADBEEF')
|
'[GNUPG:] ERRSIG DEADBEEF')
|
||||||
|
|
||||||
pkg = self.SourcePackage('example', '1.0-1', 'main',
|
pkg = self.SourcePackage(package='example',
|
||||||
workdir=self.workdir, mirrors=[mirror])
|
version='1.0-1',
|
||||||
|
component='main',
|
||||||
|
workdir=self.workdir,
|
||||||
|
mirrors=[mirror])
|
||||||
try:
|
try:
|
||||||
self.assertRaises(ubuntutools.archive.DownloadError, pkg.pull)
|
self.assertRaises(ubuntutools.archive.DownloadError, pkg.pull)
|
||||||
except URLError:
|
except URLError:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user