summaryrefslogtreecommitdiffstats
path: root/tests/test_auth.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_auth.py')
-rw-r--r--tests/test_auth.py314
1 files changed, 314 insertions, 0 deletions
diff --git a/tests/test_auth.py b/tests/test_auth.py
new file mode 100644
index 0000000..811cbb9
--- /dev/null
+++ b/tests/test_auth.py
@@ -0,0 +1,314 @@
+#!/usr/bin/python3
+
+import contextlib
+import errno
+import itertools
+import os
+import shutil
+import sys
+import tempfile
+import time
+import unittest
+from http.server import HTTPServer
+from http.server import SimpleHTTPRequestHandler as HTTPRequestHandler
+
+import apt_pkg
+import testcommon
+
+import apt.auth
+
+WHEEZY_KEYID = "8B48AD6246925553"
+WHEEZY_KEYDATE = "1335553717"
+WHEEZY_KEY = """-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.12 (GNU/Linux)
+
+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
+DHcut3Yey8o=
+=id4q
+-----END PGP PUBLIC KEY BLOCK-----"""
+
+
+def normalize_key(keystr):
+ """Remove the Version: header from a key block"""
+ lines = keystr.split("\n")
+ if lines[1].startswith("Version:"):
+ return lines[:1] + lines[2:]
+ return lines
+
+
+class TestAuthKeys(testcommon.TestCase):
+
+ """Test handling of keys for signed repositories."""
+
+ def setUp(self):
+ testcommon.TestCase.setUp(self)
+
+ self.tmpdir = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.tmpdir)
+ apt_pkg.config.set("Dir", self.tmpdir)
+ apt_pkg.config.set("Dir::Bin::Apt-key", "fakeroot-apt-key")
+ apt_pkg.config.set("Dir::Etc", "etc/apt/")
+ trustedparts_dir = apt_pkg.config.find_dir("Dir::Etc::Trustedparts")
+ confparts_dir = apt_pkg.config.find_dir("Dir::Etc::parts")
+ self.assertTrue(trustedparts_dir.startswith(self.tmpdir))
+ os.makedirs(trustedparts_dir)
+ os.makedirs(confparts_dir)
+ shutil.copy("fakeroot-apt-key", self.tmpdir)
+
+ @contextlib.contextmanager
+ def _discard_stderr(self):
+ stderr_fd = sys.stderr.fileno()
+ stderr_save = os.dup(stderr_fd)
+ try:
+ devnull = os.open("/dev/null", os.O_WRONLY)
+ try:
+ os.dup2(devnull, stderr_fd)
+ yield
+ finally:
+ os.close(devnull)
+ finally:
+ os.dup2(stderr_save, stderr_fd)
+ os.close(stderr_save)
+
+ def testAddAndExportKey(self):
+ """Add an example key."""
+ apt.auth.add_key(WHEEZY_KEY)
+ # Strip the headers from the keys to avoid test errors because
+ # the exported key used a differenct GnuPG version than the
+ # original example key
+ self.assertEqual(
+ normalize_key(apt.auth.export_key(WHEEZY_KEYID)), normalize_key(WHEEZY_KEY)
+ )
+
+ def testAddAndListKey(self):
+ """Add an example key and test if it is correctly returned by
+ list_keys()
+ """
+ apt.auth.add_key(WHEEZY_KEY)
+ ret = apt.auth.list_keys()
+ self.assertEqual(len(ret), 1)
+ key = ret[0]
+ self.assertEqual(
+ key.name,
+ "Debian Archive Automatic Signing Key (7.0/wheezy) "
+ "<ftpmaster@debian.org>",
+ )
+ self.assertEqual(key.keyid, WHEEZY_KEYID)
+ self.assertEqual(key.date, WHEEZY_KEYDATE)
+
+ def testAddKeyFromFile(self):
+ """Test adding a key from file."""
+ keyfd, keyname = tempfile.mkstemp()
+ self.addCleanup(os.close, keyfd)
+ os.write(keyfd, WHEEZY_KEY.encode("UTF-8"))
+
+ apt.auth.add_key_from_file(keyname)
+
+ ret = apt.auth.list_keys()
+ self.assertEqual(len(ret), 1)
+ key = ret[0]
+ self.assertEqual(
+ key.name,
+ "Debian Archive Automatic Signing Key (7.0/wheezy) "
+ "<ftpmaster@debian.org>",
+ )
+ self.assertEqual(key.keyid, WHEEZY_KEYID)
+ self.assertEqual(key.date, WHEEZY_KEYDATE)
+
+ def test_add_key_from_keyserver_too_short(self):
+ """Ensure that short keyids are not imported"""
+ with self.assertRaises(apt.auth.AptKeyIDTooShortError):
+ apt.auth.add_key_from_keyserver(WHEEZY_KEYID, "hkp://localhost:19191")
+ with self.assertRaises(apt.auth.AptKeyIDTooShortError):
+ apt.auth.add_key_from_keyserver(
+ "0101010178F7FE5C3E65D8AF8B48AD624692555", "hkp://localhost:19191"
+ )
+ with self.assertRaises(apt.auth.AptKeyIDTooShortError):
+ apt.auth.add_key_from_keyserver(
+ "0x0101010178F7FE5C3E65D8AF8B48AD624692555", "hkp://localhost:19191"
+ )
+ with self.assertRaises(apt.auth.AptKeyIDTooShortError):
+ apt.auth.add_key_from_keyserver(
+ "0101 0101 78F7 FE5C 3E65 D8AF 8B48 AD62 4692 555",
+ "hkp://localhost:19191",
+ )
+
+ def test_add_key_from_server_mitm(self):
+ """Verify that the key fingerprint is verified after download"""
+ self._start_keyserver()
+ self.addCleanup(self._stop_keyserver)
+ with self.assertRaises(apt.auth.AptKeyError) as cm:
+ with self._discard_stderr():
+ apt.auth.add_key_from_keyserver(
+ "0101010178F7FE5C3E65D8AF8B48AD6246925553",
+ "hkp://localhost:%d" % self.keyserver_port,
+ )
+ self.assertTrue(
+ str(cm.exception).startswith(
+ "recv from 'hkp://localhost:%d' failed for '%s'"
+ % (self.keyserver_port, "0101010178F7FE5C3E65D8AF8B48AD6246925553")
+ ),
+ cm.exception,
+ )
+
+ def testAddKeyFromServer(self):
+ """Install a GnuPG key from a remote server."""
+ self._start_keyserver()
+ self.addCleanup(self._stop_keyserver)
+
+ with self._discard_stderr():
+ apt.auth.add_key_from_keyserver(
+ "0xa1bD8E9D78F7FE5C3E65D8AF8B48AD6246925553",
+ "hkp://localhost:%d" % self.keyserver_port,
+ )
+
+ ret = apt.auth.list_keys()
+ self.assertEqual(len(ret), 1)
+ key = ret[0]
+ self.assertEqual(
+ key.name,
+ "Debian Archive Automatic Signing Key (7.0/wheezy) "
+ "<ftpmaster@debian.org>",
+ )
+ self.assertEqual(key.keyid, WHEEZY_KEYID)
+ self.assertEqual(key.date, WHEEZY_KEYDATE)
+
+ def _start_keyserver(self):
+ """Start a fake keyserver on http://localhost:19191
+ If port 19191 is unavailable, try successive ports until one is.
+ Store the port actually in use in self.keyserver_port.
+ Thanks pitti.
+ """
+ dir = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, dir)
+ os.mkdir(os.path.join(dir, "pks"))
+ with open(os.path.join(dir, "pks", "lookup"), "w") as key_file:
+ key_file.write(WHEEZY_KEY)
+
+ keyserver_pipe = os.pipe()
+ self.keyserver_pid = os.fork()
+ if self.keyserver_pid == 0:
+ os.close(keyserver_pipe[0])
+ # quiesce server log
+ os.dup2(os.open("/dev/null", os.O_WRONLY), sys.stderr.fileno())
+ os.chdir(dir)
+ for port in itertools.count(19191):
+ try:
+ httpd = HTTPServer(("localhost", port), HTTPRequestHandler)
+ break
+ except OSError as e:
+ if e.errno != errno.EADDRINUSE:
+ raise
+ keyserver_write = os.fdopen(keyserver_pipe[1], "w")
+ print(port, file=keyserver_write)
+ keyserver_write.close()
+ httpd.serve_forever()
+ os._exit(0)
+
+ os.close(keyserver_pipe[1])
+ keyserver_read = os.fdopen(keyserver_pipe[0])
+ self.keyserver_port = int(keyserver_read.readline())
+ keyserver_read.close()
+
+ # temporarily disable proxy, as gnupg does not get along with that
+ # (LP #789049)
+ self.orig_proxy = os.environ.get("http_proxy")
+ try:
+ del os.environ["http_proxy"]
+ except KeyError:
+ pass
+
+ # wait a bit until server is ready
+ time.sleep(0.5)
+
+ def _stop_keyserver(self):
+ """Stop fake keyserver"""
+ assert self.keyserver_pid
+
+ os.kill(self.keyserver_pid, 15)
+ os.wait()
+
+ # restore proxy
+ if self.orig_proxy is not None:
+ os.environ["http_proxy"] = self.orig_proxy
+
+
+if __name__ == "__main__":
+ unittest.main()