Merge lp:~mvo/click/debsigs-verify into lp:click/devel

Proposed by Michael Vogt
Status: Merged
Approved by: Colin Watson
Approved revision: 503
Merged at revision: 496
Proposed branch: lp:~mvo/click/debsigs-verify
Merge into: lp:click/devel
Diff against target: 566 lines (+438/-6)
7 files modified
click/commands/install.py (+6/-1)
click/commands/verify.py (+6/-1)
click/install.py (+52/-1)
click/tests/test_install.py (+23/-0)
debian/tests/control (+1/-1)
tests/integration/test_signatures.py (+346/-0)
tests/integration/test_verify.py (+4/-2)
To merge this branch: bzr merge lp:~mvo/click/debsigs-verify
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Colin Watson Approve
Review via email: mp+226652@code.launchpad.net

Commit message

Add support for click package gpg signatures (LP: #1330770)

Description of the change

This branch adds signature checking via debsigs/debsig-verfiy to click.

Source package for the new default policy at: lp:~mvo/click/click-ubuntu-policy (needs the real store pubkey)

It needs a MIR for debsigs/debsig-verify before we can activate it as it requires both as test dependencies for the integration test.

(Unrelated) A fix so that "debsigs --delete" produces the exactly same deb/click as before the "debsigs --sign": https://gitorious.org/debsigs/debsigs/merge_requests/1

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:498
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https://code.launchpad.net/~mvo/click/debsigs-verify/+merge/226652/+edit-commit-message

http://jenkins.qa.ubuntu.com/job/click-devel-ci/37/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/click-devel-utopic-amd64-ci/39/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/click-devel-utopic-armhf-ci/37/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/click-devel-utopic-i386-ci/37/console

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/click-devel-ci/37/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mvo/click/debsigs-verify updated
499. By Michael Vogt

click/tests/test_install.py: fix test when no debsig-verify is installed

500. By Michael Vogt

tests/integration/test_signatures.py: cleanup

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Colin Watson (cjwatson) wrote :

This looks generally great, thanks. It would be good if you could fix up the various nitpicks here; once you've done that, feel free to merge to lp:click/devel.

Note that the autopkgtest infrastructure doesn't restrict packages in main to be tested using only packages in main, so there's no need to deal with MIRs for debsigs or debsig-verify before landing this.

review: Approve
Revision history for this message
Colin Watson (cjwatson) wrote :

Argh, the previous review ate my inline comments.

lp:~mvo/click/debsigs-verify updated
501. By Michael Vogt

address Colins review comments (thanks!)

502. By Michael Vogt

fix integration tests and add --allow-unauthenticated to click audit too

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mvo/click/debsigs-verify updated
503. By Michael Vogt

merged with lp:click/devel

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'click/commands/install.py'
--- click/commands/install.py 2014-04-03 08:52:02 +0000
+++ click/commands/install.py 2014-08-08 05:55:46 +0000
@@ -43,6 +43,9 @@
43 parser.add_option(43 parser.add_option(
44 "--all-users", default=False, action="store_true",44 "--all-users", default=False, action="store_true",
45 help="register package for all users")45 help="register package for all users")
46 parser.add_option(
47 "--allow-unauthenticated", default=False, action="store_true",
48 help="allow installing packages with no sigantures")
46 options, args = parser.parse_args(argv)49 options, args = parser.parse_args(argv)
47 if len(args) < 1:50 if len(args) < 1:
48 parser.error("need package file name")51 parser.error("need package file name")
@@ -51,7 +54,9 @@
51 if options.root is not None:54 if options.root is not None:
52 db.add(options.root)55 db.add(options.root)
53 package_path = args[0]56 package_path = args[0]
54 installer = ClickInstaller(db, options.force_missing_framework)57 installer = ClickInstaller(
58 db=db, force_missing_framework=options.force_missing_framework,
59 allow_unauthenticated=options.allow_unauthenticated)
55 try:60 try:
56 installer.install(61 installer.install(
57 package_path, user=options.user, all_users=options.all_users)62 package_path, user=options.user, all_users=options.all_users)
5863
=== modified file 'click/commands/verify.py'
--- click/commands/verify.py 2013-09-23 21:24:37 +0000
+++ click/commands/verify.py 2014-08-08 05:55:46 +0000
@@ -29,10 +29,15 @@
29 parser.add_option(29 parser.add_option(
30 "--force-missing-framework", action="store_true", default=False,30 "--force-missing-framework", action="store_true", default=False,
31 help="ignore missing system framework")31 help="ignore missing system framework")
32 parser.add_option(
33 "--allow-unauthenticated", action="store_true", default=False,
34 help="allow installing packages with no sigantures")
32 options, args = parser.parse_args(argv)35 options, args = parser.parse_args(argv)
33 if len(args) < 1:36 if len(args) < 1:
34 parser.error("need package file name")37 parser.error("need package file name")
35 package_path = args[0]38 package_path = args[0]
36 installer = ClickInstaller(None, options.force_missing_framework)39 installer = ClickInstaller(
40 db=None, force_missing_framework=options.force_missing_framework,
41 allow_unauthenticated=options.allow_unauthenticated)
37 installer.audit(package_path, slow=True)42 installer.audit(package_path, slow=True)
38 return 043 return 0
3944
=== modified file 'click/install.py'
--- click/install.py 2014-05-05 13:10:19 +0000
+++ click/install.py 2014-08-08 05:55:46 +0000
@@ -30,6 +30,7 @@
30import grp30import grp
31import inspect31import inspect
32import json32import json
33import logging
33import os34import os
34import pwd35import pwd
35import shutil36import shutil
@@ -74,6 +75,44 @@
74apt_pkg.init_system()75apt_pkg.init_system()
7576
7677
78class DebsigVerifyError(Exception):
79 pass
80
81
82class DebsigVerify:
83 """Tiny wrapper around the debsig-verify commandline"""
84 # from debsig-verify-0.9/debsigs.h
85 DS_SUCCESS = 0
86 DS_FAIL_NOSIGS = 10
87 DS_FAIL_UNKNOWN_ORIGIN = 11
88 DS_FAIL_NOPOLICIES = 12
89 DS_FAIL_BADSIG = 13
90 DS_FAIL_INTERNAL = 14
91
92 @classmethod
93 @property
94 def available(cls):
95 return Click.find_on_path("debsig-verify")
96
97 @classmethod
98 def verify(cls, path, allow_unauthenticated):
99 command = ["debsig-verify"] + [path]
100 try:
101 subprocess.check_output(command, universal_newlines=True)
102 except subprocess.CalledProcessError as e:
103 if (allow_unauthenticated and
104 e.returncode in (DebsigVerify.DS_FAIL_NOSIGS,
105 DebsigVerify.DS_FAIL_UNKNOWN_ORIGIN,
106 DebsigVerify.DS_FAIL_NOPOLICIES)):
107 logging.warning(
108 "Signature check failed, but installing anyway "
109 "as requested")
110 else:
111 raise DebsigVerifyError(
112 "Signature verification error: %s" % e.output)
113 return True
114
115
77class ClickInstallerError(Exception):116class ClickInstallerError(Exception):
78 pass117 pass
79118
@@ -87,9 +126,11 @@
87126
88127
89class ClickInstaller:128class ClickInstaller:
90 def __init__(self, db, force_missing_framework=False):129 def __init__(self, db, force_missing_framework=False,
130 allow_unauthenticated=False):
91 self.db = db131 self.db = db
92 self.force_missing_framework = force_missing_framework132 self.force_missing_framework = force_missing_framework
133 self.allow_unauthenticated = allow_unauthenticated
93134
94 def _preload_path(self):135 def _preload_path(self):
95 if "CLICK_PACKAGE_PRELOAD" in os.environ:136 if "CLICK_PACKAGE_PRELOAD" in os.environ:
@@ -125,6 +166,16 @@
125 subprocess.check_call(command, env=env, **kwargs)166 subprocess.check_call(command, env=env, **kwargs)
126167
127 def audit(self, path, slow=False, check_arch=False):168 def audit(self, path, slow=False, check_arch=False):
169 # always do the signature check first
170 if DebsigVerify.available:
171 try:
172 DebsigVerify.verify(path, self.allow_unauthenticated)
173 except DebsigVerifyError as e:
174 raise ClickInstallerAuditError(str(e))
175 else:
176 logging.warning(
177 "debsig-verify not available; cannot check signatures")
178
128 with closing(DebFile(filename=path)) as package:179 with closing(DebFile(filename=path)) as package:
129 control_fields = package.control.debcontrol()180 control_fields = package.control.debcontrol()
130181
131182
=== modified file 'click/tests/test_install.py'
--- click/tests/test_install.py 2014-05-19 13:08:57 +0000
+++ click/tests/test_install.py 2014-08-08 05:55:46 +0000
@@ -75,6 +75,12 @@
75 self.use_temp_dir()75 self.use_temp_dir()
76 self.db = Click.DB()76 self.db = Click.DB()
77 self.db.add(self.temp_dir)77 self.db.add(self.temp_dir)
78 # mock signature checks during the tests
79 self.debsig_patcher = mock.patch("click.install.DebsigVerify")
80 self.debsig_patcher.start()
81
82 def tearDown(self):
83 self.debsig_patcher.stop()
7884
79 def make_fake_package(self, control_fields=None, manifest=None,85 def make_fake_package(self, control_fields=None, manifest=None,
80 control_scripts=None, data_files=None):86 control_scripts=None, data_files=None):
@@ -232,6 +238,23 @@
232 'Framework "missing" not present on system.*',238 'Framework "missing" not present on system.*',
233 ClickInstaller(self.db).audit, path)239 ClickInstaller(self.db).audit, path)
234240
241 # FIXME: we really want a unit test with a valid signature too
242 def test_audit_no_signature(self):
243 if not Click.find_on_path("debsig-verify"):
244 self.skipTest("this test needs debsig-verify")
245 path = self.make_fake_package(
246 control_fields={"Click-Version": "0.4"},
247 manifest={
248 "name": "test-package",
249 "version": "1.0",
250 "framework": "",
251 })
252 self.debsig_patcher.stop()
253 self.assertRaisesRegex(
254 ClickInstallerAuditError, "Signature verification failed",
255 ClickInstaller(self.db).audit, path)
256 self.debsig_patcher.start()
257
235 @disable_logging258 @disable_logging
236 def test_audit_missing_framework_force(self):259 def test_audit_missing_framework_force(self):
237 with self.run_in_subprocess(260 with self.run_in_subprocess(
238261
=== modified file 'debian/tests/control'
--- debian/tests/control 2014-06-30 14:03:36 +0000
+++ debian/tests/control 2014-08-08 05:55:46 +0000
@@ -1,3 +1,3 @@
1Tests: run-tests.sh1Tests: run-tests.sh
2Depends: @, iputils-ping, click-dev, schroot, debootstrap, sudo, bzr2Depends: @, iputils-ping, click-dev, schroot, debootstrap, sudo, bzr, debsigs, debsig-verify
3Restrictions: needs-root allow-stderr3Restrictions: needs-root allow-stderr
44
=== added directory 'tests/integration/data'
=== added directory 'tests/integration/data/evil-keyring'
=== added file 'tests/integration/data/evil-keyring/pubring.gpg'
5Binary files tests/integration/data/evil-keyring/pubring.gpg 1970-01-01 00:00:00 +0000 and tests/integration/data/evil-keyring/pubring.gpg 2014-08-08 05:55:46 +0000 differ5Binary files tests/integration/data/evil-keyring/pubring.gpg 1970-01-01 00:00:00 +0000 and tests/integration/data/evil-keyring/pubring.gpg 2014-08-08 05:55:46 +0000 differ
=== added file 'tests/integration/data/evil-keyring/secring.gpg'
6Binary files tests/integration/data/evil-keyring/secring.gpg 1970-01-01 00:00:00 +0000 and tests/integration/data/evil-keyring/secring.gpg 2014-08-08 05:55:46 +0000 differ6Binary files tests/integration/data/evil-keyring/secring.gpg 1970-01-01 00:00:00 +0000 and tests/integration/data/evil-keyring/secring.gpg 2014-08-08 05:55:46 +0000 differ
=== added file 'tests/integration/data/evil-keyring/trustdb.gpg'
7Binary files tests/integration/data/evil-keyring/trustdb.gpg 1970-01-01 00:00:00 +0000 and tests/integration/data/evil-keyring/trustdb.gpg 2014-08-08 05:55:46 +0000 differ7Binary files tests/integration/data/evil-keyring/trustdb.gpg 1970-01-01 00:00:00 +0000 and tests/integration/data/evil-keyring/trustdb.gpg 2014-08-08 05:55:46 +0000 differ
=== added directory 'tests/integration/data/origin-keyring'
=== added file 'tests/integration/data/origin-keyring/pubring.gpg'
8Binary files tests/integration/data/origin-keyring/pubring.gpg 1970-01-01 00:00:00 +0000 and tests/integration/data/origin-keyring/pubring.gpg 2014-08-08 05:55:46 +0000 differ8Binary files tests/integration/data/origin-keyring/pubring.gpg 1970-01-01 00:00:00 +0000 and tests/integration/data/origin-keyring/pubring.gpg 2014-08-08 05:55:46 +0000 differ
=== added file 'tests/integration/data/origin-keyring/secring.gpg'
9Binary files tests/integration/data/origin-keyring/secring.gpg 1970-01-01 00:00:00 +0000 and tests/integration/data/origin-keyring/secring.gpg 2014-08-08 05:55:46 +0000 differ9Binary files tests/integration/data/origin-keyring/secring.gpg 1970-01-01 00:00:00 +0000 and tests/integration/data/origin-keyring/secring.gpg 2014-08-08 05:55:46 +0000 differ
=== added file 'tests/integration/data/origin-keyring/trustdb.gpg'
10Binary files tests/integration/data/origin-keyring/trustdb.gpg 1970-01-01 00:00:00 +0000 and tests/integration/data/origin-keyring/trustdb.gpg 2014-08-08 05:55:46 +0000 differ10Binary files tests/integration/data/origin-keyring/trustdb.gpg 1970-01-01 00:00:00 +0000 and tests/integration/data/origin-keyring/trustdb.gpg 2014-08-08 05:55:46 +0000 differ
=== added file 'tests/integration/test_signatures.py'
--- tests/integration/test_signatures.py 1970-01-01 00:00:00 +0000
+++ tests/integration/test_signatures.py 2014-08-08 05:55:46 +0000
@@ -0,0 +1,346 @@
1# Copyright (C) 2014 Canonical Ltd.
2# Author: Michael Vogt <michael.vogt@ubuntu.com>
3
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; version 3 of the License.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program. If not, see <http://www.gnu.org/licenses/>.
15
16"""Integration tests for the click signature checking."""
17
18import copy
19import os
20import shutil
21import subprocess
22import tarfile
23import unittest
24from textwrap import dedent
25
26from .helpers import (
27 is_root,
28 ClickTestCase,
29)
30
31def makedirs(path):
32 try:
33 os.makedirs(path)
34 except OSError:
35 pass
36
37def get_keyid_from_gpghome(gpg_home):
38 """Return the public keyid of a given gpg home dir"""
39 output = subprocess.check_output(
40 ["gpg", "--home", gpg_home, "--list-keys", "--with-colons"],
41 universal_newlines=True)
42 for line in output.splitlines():
43 if not line.startswith("pub:"):
44 continue
45 return line.split(":")[4]
46 raise ValueError("Cannot find public key in output: '%s'" % output)
47
48
49class Debsigs:
50 """Tiny wrapper around the debsigs CLI"""
51 def __init__(self, gpghome, keyid):
52 self.keyid = keyid
53 self.gpghome = gpghome
54 self.policy = "/etc/debsig/policies/%s/generic.pol" % self.keyid
55
56 def sign(self, filepath, signature_type="origin"):
57 """Sign the click at filepath"""
58 env = copy.copy(os.environ)
59 env["GNUPGHOME"] = os.path.abspath(self.gpghome)
60 subprocess.check_call(
61 ["debsigs",
62 "--sign=%s" % signature_type,
63 "--default-key=%s" % self.keyid,
64 filepath], env=env)
65
66 def install_signature_policy(self):
67 """Install/update the system-wide signature policy"""
68 xmls = dedent("""\
69 <?xml version="1.0"?>
70 <!DOCTYPE Policy SYSTEM "http://www.debian.org/debsig/1.0/policy.dtd">
71 <Policy xmlns="http://www.debian.org/debsig/1.0/">
72
73 <Origin Name="test-origin" id="{keyid}" Description="Example policy"/>
74 <Selection>
75 <Required Type="origin" File="{filename}" id="{keyid}"/>
76 </Selection>
77
78 <Verification>
79 <Required Type="origin" File="{filename}" id="{keyid}"/>
80 </Verification>
81 </Policy>
82 """.format(keyid=self.keyid, filename="origin.pub"))
83 makedirs(os.path.dirname(self.policy))
84 with open(self.policy, "w") as f:
85 f.write(xmls)
86 self.pubkey_path = (
87 "/usr/share/debsig/keyrings/%s/origin.pub" % self.keyid)
88 makedirs(os.path.dirname(self.pubkey_path))
89 shutil.copy(os.path.join(self.gpghome, "pubring.gpg"), self.pubkey_path)
90
91 def uninstall_signature_policy(self):
92 # FIXME: update debsig-verify so that it can work from a different
93 # root than "/" so that the tests do not have to use the
94 # system root
95 os.remove(self.policy)
96 os.remove(self.pubkey_path)
97
98
99@unittest.skipIf(not is_root(), "This tests needs to run as root")
100class ClickSignaturesTestCase(ClickTestCase):
101 def assertClickNoSignatureError(self, cmd_args):
102 with self.assertRaises(subprocess.CalledProcessError) as cm:
103 output = subprocess.check_output(
104 [self.click_binary] + cmd_args,
105 stderr=subprocess.STDOUT, universal_newlines=True)
106 output = cm.exception.output
107 expected_error_message = ("debsig: Origin Signature check failed. "
108 "This deb might not be signed.")
109 self.assertIn(expected_error_message, output)
110
111 def assertClickInvalidSignatureError(self, cmd_args):
112 with self.assertRaises(subprocess.CalledProcessError) as cm:
113 output = subprocess.check_output(
114 [self.click_binary] + cmd_args,
115 stderr=subprocess.STDOUT, universal_newlines=True)
116 output = cm.exception.output
117 expected_error_message = "Signature verification error: "
118 self.assertIn(expected_error_message, output)
119
120
121@unittest.skipIf(not is_root(), "This tests needs to run as root")
122class TestSignatureVerificationNoSignature(ClickSignaturesTestCase):
123 def test_debsig_verify_no_sig(self):
124 name = "org.example.debsig-no-sig"
125 path_to_click = self._make_click(name, framework="")
126 self.assertClickNoSignatureError(["verify", path_to_click])
127
128 def test_debsig_install_no_sig(self):
129 name = "org.example.debsig-no-sig"
130 path_to_click = self._make_click(name, framework="")
131 self.assertClickNoSignatureError(["install", path_to_click])
132
133 def test_debsig_install_can_install_with_sig_override(self):
134 name = "org.example.debsig-no-sig"
135 path_to_click = self._make_click(name, framework="")
136 user = os.environ.get("USER", "root")
137 subprocess.check_call(
138 [self.click_binary, "install",
139 "--allow-unauthenticated", "--user=%s" % user,
140 path_to_click])
141 self.addCleanup(
142 subprocess.call, [self.click_binary, "unregister",
143 "--user=%s" % user, name])
144
145
146@unittest.skipIf(not is_root(), "This tests needs to run as root")
147class TestSignatureVerification(ClickSignaturesTestCase):
148 def setUp(self):
149 super(TestSignatureVerification, self).setUp()
150 self.user = os.environ.get("USER", "root")
151 # the valid origin keyring
152 self.datadir = os.path.join(os.path.dirname(__file__), "data")
153 origin_keyring_dir = os.path.abspath(
154 os.path.join(self.datadir, "origin-keyring"))
155 keyid = get_keyid_from_gpghome(origin_keyring_dir)
156 self.debsigs = Debsigs(origin_keyring_dir, keyid)
157 self.debsigs.install_signature_policy()
158
159 def tearDown(self):
160 self.debsigs.uninstall_signature_policy()
161
162 def test_debsig_install_valid_signature(self):
163 name = "org.example.debsig-valid-sig"
164 path_to_click = self._make_click(name, framework="")
165 self.debsigs.sign(path_to_click)
166 subprocess.check_call(
167 [self.click_binary, "install",
168 "--user=%s" % self.user,
169 path_to_click])
170 self.addCleanup(
171 subprocess.call, [self.click_binary, "unregister",
172 "--user=%s" % self.user, name])
173 output = subprocess.check_output(
174 [self.click_binary, "list", "--user=%s" % self.user],
175 universal_newlines=True)
176 self.assertIn(name, output)
177
178 def test_debsig_install_signature_not_in_keyring(self):
179 name = "org.example.debsig-no-keyring-sig"
180 path_to_click = self._make_click(name, framework="")
181 evil_keyring_dir = os.path.join(self.datadir, "evil-keyring")
182 keyid = get_keyid_from_gpghome(evil_keyring_dir)
183 debsig_bad = Debsigs(evil_keyring_dir, keyid)
184 debsig_bad.sign(path_to_click)
185 # and ensure its really not there
186 self.assertClickInvalidSignatureError(["install", path_to_click])
187 output = subprocess.check_output(
188 [self.click_binary, "list", "--user=%s" % self.user],
189 universal_newlines=True)
190 self.assertNotIn(name, output)
191
192 def test_debsig_install_not_a_signature(self):
193 name = "org.example.debsig-invalid-sig"
194 path_to_click = self._make_click(name, framework="")
195 invalid_sig = os.path.join(self.temp_dir, "_gpgorigin")
196 with open(invalid_sig, "w") as f:
197 f.write("no-valid-signature")
198 # add a invalid sig
199 subprocess.check_call(["ar", "-r", path_to_click, invalid_sig])
200 self.assertClickInvalidSignatureError(["install", path_to_click])
201 output = subprocess.check_output(
202 [self.click_binary, "list", "--user=%s" % self.user],
203 universal_newlines=True)
204 self.assertNotIn(name, output)
205
206 def test_debsig_install_signature_altered_click(self):
207 def modify_ar_member(member):
208 subprocess.check_call(
209 ["ar", "-x", path_to_click, "control.tar.gz"],
210 cwd=self.temp_dir)
211 altered_member = os.path.join(self.temp_dir, member)
212 with open(altered_member, "ba") as f:
213 f.write(b"\0")
214 subprocess.check_call(["ar", "-r", path_to_click, altered_member])
215
216 # ensure that all members we care about are checked by debsig-verify
217 for member in ["control.tar.gz", "data.tar.gz", "debian-binary"]:
218 name = "org.example.debsig-altered-click"
219 path_to_click = self._make_click(name, framework="")
220 self.debsigs.sign(path_to_click)
221 modify_ar_member(member)
222 self.assertClickInvalidSignatureError(["install", path_to_click])
223 output = subprocess.check_output(
224 [self.click_binary, "list", "--user=%s" % self.user],
225 universal_newlines=True)
226 self.assertNotIn(name, output)
227
228 def make_nasty_data_tar(self, compression):
229 new_data_tar = os.path.join(self.temp_dir, "data.tar." + compression)
230 evilfile = os.path.join(self.temp_dir, "README.evil")
231 with open(evilfile, "w") as f:
232 f.write("I am a nasty README")
233 with tarfile.open(new_data_tar, "w:"+compression) as tar:
234 tar.add(evilfile)
235 return new_data_tar
236
237 def test_debsig_install_signature_injected_data_tar(self):
238 name = "org.example.debsig-injected-data-click"
239 path_to_click = self._make_click(name, framework="")
240 self.debsigs.sign(path_to_click)
241 new_data = self.make_nasty_data_tar("bz2")
242 # insert before the real data.tar.gz and ensure this is caught
243 # NOTE: that right now this will not be caught by debsig-verify
244 # but later in audit() by debian.debfile.DebFile()
245 subprocess.check_call(["ar",
246 "-r",
247 "-b", "data.tar.gz",
248 path_to_click,
249 new_data])
250 output = subprocess.check_output(
251 ["ar", "-t", path_to_click], universal_newlines=True)
252 self.assertEqual(output.splitlines(),
253 ["debian-binary",
254 "_click-binary",
255 "control.tar.gz",
256 "data.tar.bz2",
257 "data.tar.gz",
258 "_gpgorigin"])
259 with self.assertRaises(subprocess.CalledProcessError):
260 output = subprocess.check_output(
261 [self.click_binary, "install", path_to_click],
262 stderr=subprocess.STDOUT, universal_newlines=True)
263 output = subprocess.check_output(
264 [self.click_binary, "list", "--user=%s" % self.user],
265 universal_newlines=True)
266 self.assertNotIn(name, output)
267
268 def test_debsig_install_signature_replaced_data_tar(self):
269 name = "org.example.debsig-replaced-data-click"
270 path_to_click = self._make_click(name, framework="")
271 self.debsigs.sign(path_to_click)
272 new_data = self.make_nasty_data_tar("bz2")
273 # replace data.tar.gz with data.tar.bz2 and ensure this is caught
274 subprocess.check_call(["ar",
275 "-d",
276 path_to_click,
277 "data.tar.gz",
278 ])
279 subprocess.check_call(["ar",
280 "-r",
281 path_to_click,
282 new_data])
283 output = subprocess.check_output(
284 ["ar", "-t", path_to_click], universal_newlines=True)
285 self.assertEqual(output.splitlines(),
286 ["debian-binary",
287 "_click-binary",
288 "control.tar.gz",
289 "_gpgorigin",
290 "data.tar.bz2",
291 ])
292 with self.assertRaises(subprocess.CalledProcessError) as cm:
293 output = subprocess.check_output(
294 [self.click_binary, "install", path_to_click],
295 stderr=subprocess.STDOUT, universal_newlines=True)
296 self.assertIn("Signature verification error", cm.exception.output)
297 output = subprocess.check_output(
298 [self.click_binary, "list", "--user=%s" % self.user],
299 universal_newlines=True)
300 self.assertNotIn(name, output)
301
302 def test_debsig_install_signature_prepend_sig(self):
303 # this test is probably not really needed, it tries to trick
304 # the system by prepending a valid signature that is not
305 # in the keyring. But given that debsig-verify only reads
306 # the first packet of any given _gpg$foo signature its
307 # equivalent to test_debsig_install_signature_not_in_keyring test
308 name = "org.example.debsig-replaced-data-prepend-sig-click"
309 path_to_click = self._make_click(name, framework="")
310 self.debsigs.sign(path_to_click)
311 new_data = self.make_nasty_data_tar("gz")
312 # replace data.tar.gz
313 subprocess.check_call(["ar",
314 "-r",
315 path_to_click,
316 new_data,
317 ])
318 # get previous good _gpgorigin for the old data
319 subprocess.check_call(
320 ["ar", "-x", path_to_click, "_gpgorigin"], cwd=self.temp_dir)
321 with open(os.path.join(self.temp_dir, "_gpgorigin"), "br") as f:
322 good_gpg_origin = f.read()
323 # and append a valid signature from a non-keyring key
324 evil_keyring_dir = os.path.join(self.datadir, "evil-keyring")
325 debsig_bad = Debsigs(evil_keyring_dir, "18B38B9AC1B67A0D")
326 debsig_bad.sign(path_to_click)
327 subprocess.check_call(
328 ["ar", "-x", path_to_click, "_gpgorigin"], cwd=self.temp_dir)
329 with open(os.path.join(self.temp_dir, "_gpgorigin"), "br") as f:
330 evil_gpg_origin = f.read()
331 with open(os.path.join(self.temp_dir, "_gpgorigin"), "wb") as f:
332 f.write(evil_gpg_origin)
333 f.write(good_gpg_origin)
334 subprocess.check_call(
335 ["ar", "-r", path_to_click, "_gpgorigin"], cwd=self.temp_dir)
336 # now ensure that the verification fails as well
337 with self.assertRaises(subprocess.CalledProcessError) as cm:
338 output = subprocess.check_output(
339 [self.click_binary, "install", path_to_click],
340 stderr=subprocess.STDOUT, universal_newlines=True)
341 self.assertIn("Signature verification error", cm.exception.output)
342 output = subprocess.check_output(
343 [self.click_binary, "list", "--user=%s" % self.user],
344 universal_newlines=True)
345 self.assertNotIn(name, output)
346
0347
=== modified file 'tests/integration/test_verify.py'
--- tests/integration/test_verify.py 2014-06-26 12:00:09 +0000
+++ tests/integration/test_verify.py 2014-08-08 05:55:46 +0000
@@ -22,9 +22,11 @@
2222
23class TestVerify(ClickTestCase):23class TestVerify(ClickTestCase):
24 def test_verify_ok(self):24 def test_verify_ok(self):
25 name = "com.ubuntu.verify-ok"25 name = "com.example.verify-ok"
26 path_to_click = self._make_click(name)26 path_to_click = self._make_click(name)
27 output = subprocess.check_output([27 output = subprocess.check_output([
28 self.click_binary, "verify", "--force-missing-framework",28 self.click_binary, "verify",
29 "--force-missing-framework",
30 "--allow-unauthenticated",
29 path_to_click], universal_newlines=True)31 path_to_click], universal_newlines=True)
30 self.assertEqual(output, "")32 self.assertEqual(output, "")

Subscribers

People subscribed via source and target branches

to all changes: