From 8ab2e648e11c321c31d88e4c135066bf85816160 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Wed, 30 May 2012 10:15:35 +0200 Subject: Import AptAuth.py from software-properites as auth.py --- apt/auth.py | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 apt/auth.py (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py new file mode 100644 index 00000000..04ee8b76 --- /dev/null +++ b/apt/auth.py @@ -0,0 +1,98 @@ +# dialog_apt_key.py.in - edit the apt keys +# +# Copyright (c) 2004 Canonical +# +# Author: Michael Vogt +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA + +import atexit +import gettext +import os +import shutil +import subprocess +import tempfile + +from subprocess import PIPE + +# gettext convenient +_ = gettext.gettext +def dummy(e): return e +N_ = dummy + +# some known keys +N_("Ubuntu Archive Automatic Signing Key ") +N_("Ubuntu CD Image Automatic Signing Key ") + +class AptAuth: + def __init__(self, rootdir="/"): + self.gpg = ["/usr/bin/gpg"] + self.base_opt = self.gpg + [ + "--no-options", + "--no-default-keyring", + "--no-auto-check-trustdb", + "--trust-model", "always", + "--keyring", os.path.join(rootdir, "etc/apt/trusted.gpg"), + ] + self.tmpdir = tempfile.mkdtemp() + self.base_opt += ["--secret-keyring", + os.path.join(self.tmpdir, "secring.gpg")] + self.base_opt += ["--trustdb-name", + os.path.join(self.tmpdir, "trustdb.gpg")] + self.list_opt = self.base_opt + ["--with-colons", + "--batch", + "--list-keys"] + self.rm_opt = self.base_opt + ["--quiet", + "--batch", + "--delete-key", + "--yes"] + self.add_opt = self.base_opt + ["--quiet", + "--batch", + "--import"] + atexit.register(self._cleanup_tmpdir) + + def _cleanup_tmpdir(self): + shutil.rmtree(self.tmpdir) + + def list(self): + res = [] + #print self.list_opt + p = subprocess.Popen(self.list_opt,stdout=PIPE).stdout + for line in p.readlines(): + fields = line.split(":") + if fields[0] == "pub": + name = fields[9] + res.append("%s %s\n%s" %((fields[4])[-8:],fields[5], _(name))) + return res + + def add(self, filename): + #print "request to add " + filename + cmd = self.add_opt[:] + cmd.append(filename) + p = subprocess.Popen(cmd) + return (p.wait() == 0) + + def update(self): + cmd = ["/usr/bin/apt-key", "update"] + p = subprocess.Popen(cmd) + return (p.wait() == 0) + + def rm(self, key): + #print "request to remove " + key + cmd = self.rm_opt[:] + cmd.append(key) + p = subprocess.Popen(cmd) + return (p.wait() == 0) -- cgit v1.2.3 From 2fa1eb3cfd05e981e1ab6daf2589f8d6e92c4bf6 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Thu, 31 May 2012 08:45:24 +0200 Subject: A lot of refactoring --- apt/auth.py | 262 ++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 186 insertions(+), 76 deletions(-) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index 04ee8b76..97f9d578 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -1,9 +1,12 @@ -# dialog_apt_key.py.in - edit the apt keys -# +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# auth - authentication key management +# # Copyright (c) 2004 Canonical -# +# # Author: Michael Vogt -# +# Sebastian Heinlein +# # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the @@ -13,86 +16,193 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA +"""Handle GnuPG keys used to trust signed repositories.""" import atexit -import gettext +import glob import os +import os.path import shutil import subprocess import tempfile -from subprocess import PIPE - -# gettext convenient -_ = gettext.gettext -def dummy(e): return e -N_ = dummy - -# some known keys -N_("Ubuntu Archive Automatic Signing Key ") -N_("Ubuntu CD Image Automatic Signing Key ") - -class AptAuth: - def __init__(self, rootdir="/"): - self.gpg = ["/usr/bin/gpg"] - self.base_opt = self.gpg + [ - "--no-options", - "--no-default-keyring", - "--no-auto-check-trustdb", - "--trust-model", "always", - "--keyring", os.path.join(rootdir, "etc/apt/trusted.gpg"), - ] - self.tmpdir = tempfile.mkdtemp() - self.base_opt += ["--secret-keyring", - os.path.join(self.tmpdir, "secring.gpg")] - self.base_opt += ["--trustdb-name", - os.path.join(self.tmpdir, "trustdb.gpg")] - self.list_opt = self.base_opt + ["--with-colons", - "--batch", - "--list-keys"] - self.rm_opt = self.base_opt + ["--quiet", - "--batch", - "--delete-key", - "--yes"] - self.add_opt = self.base_opt + ["--quiet", - "--batch", - "--import"] - atexit.register(self._cleanup_tmpdir) - - def _cleanup_tmpdir(self): - shutil.rmtree(self.tmpdir) - - def list(self): - res = [] - #print self.list_opt - p = subprocess.Popen(self.list_opt,stdout=PIPE).stdout - for line in p.readlines(): - fields = line.split(":") - if fields[0] == "pub": - name = fields[9] - res.append("%s %s\n%s" %((fields[4])[-8:],fields[5], _(name))) - return res - - def add(self, filename): - #print "request to add " + filename - cmd = self.add_opt[:] - cmd.append(filename) - p = subprocess.Popen(cmd) - return (p.wait() == 0) - - def update(self): - cmd = ["/usr/bin/apt-key", "update"] - p = subprocess.Popen(cmd) - return (p.wait() == 0) - - def rm(self, key): - #print "request to remove " + key - cmd = self.rm_opt[:] - cmd.append(key) - p = subprocess.Popen(cmd) - return (p.wait() == 0) +import apt_pkg +from apt_pkg import gettext as _ + +# Create a temporary dir to store secret keying and trust database. +# APT doesn't use a secrect key ring but GnuPG fails without it. +_TMPDIR = tempfile.mkdtemp() +atexit.register(shutil.rmtree, _TMPDIR) + + +class TrustedKey(object): + + """Represents a trusted key.""" + + def __init__(self, name, keyid, date): + self.raw_name = name + # Allow to translated some known keys + self.name = _(name) + self.keyid = keyid + self.date = date + + def __str__(self): + return "%s\n%s %s" % (self.name, self.keyid, self.date) + + +def _get_gpg_command(keyring=None): + """Return the gpg command""" + cmd = [apt_pkg.config.find_file("Dir::Bin::Gpg", "/usr/bin/gpg"), + "--ignore-time-conflict", + "--no-default-keyring", + "--trust-model", "always", + "--no-options", + "--secret-keyring", os.path.join(_TMPDIR, "secring.gpg")] + if keyring is None: + # Add the public keyring + cmd.extend(["--keyring", + apt_pkg.config.find_file("Dir::Etc::Trusted"), + "--primary-keyring", + apt_pkg.config.find_file("Dir::Etc::Trusted")]) + # Add the public keyring parts + trusted_parts_dir = apt_pkg.config.find_dir("Dir::Etc::TrustedParts") + for part_name in glob.glob(os.path.join(trusted_parts_dir, "*.gpg")): + part_path = os.path.join(trusted_parts_dir, part_name) + if os.access(part_path, os.R_OK): + cmd.extend(["--keyring", part_path]) + # TrustDB + trustdb_path = os.path.join(apt_pkg.config.find_dir("Dir::Etc"), + "trustdb.gpg") + cmd.extend(["--trustdb-name", trustdb_path]) + else: + cmd.extend(["--keyring", os.path.abspath(keyring), + "--primary-keyring", os.path.abspath(keyring), + "--trustdb-name", os.path.join(_TMPDIR, "trustdb.gpg")]) + return cmd + +def _wait_and_raise(proc): + """Wait until the given subprocess is completed and raise a + SystemError if it failed. + """ + if proc.wait() != 0: + output = proc.stdout.read() + raise SystemError("GnuPG command failed: %s" % output) + +def add_key_from_file(filename, wait=True): + """Import a GnuPG key file to trust repositores signed by it. + + Keyword arguments: + filename -- the absolute path to the public GnuPG key file + wait -- if the system should be blocked until the internal GnuPG call is + completed. Otherwise the subprocess.Popen() instance will be + returned. By default the call will be blocking. + """ + if not os.path.abspath(filename): + raise SystemError("An absolute path is required: %s" % filename) + if not os.access(os.R_OK): + raise SystemError("Key file cannot be accessed: %s" % filename) + cmd = _get_gpg_command() + cmd.extend(["--quiet", "--batch", "--import", filename]) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + if wait: + _wait_and_raise(proc) + else: + return proc + +def add_key_from_keyserver(keyid, keyserver, wait=True): + """Import a GnuPG key file to trust repositores signed by it. + + Keyword arguments: + keyid -- the identifier of the key, e.g. 0x0EB12DSA + keyserver -- the URL or hostname of the key server + wait -- if the system should be blocked until the internal GnuPG call is + completed. Otherwise the subprocess.Popen() instance will be + returned. By default the call will be blocking. + """ + cmd = _get_gpg_command() + cmd.extend(["--quiet", "--batch", + "--keyserver", keyserver, + "--recv", keyid]) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + if wait: + _wait_and_raise(proc) + else: + return proc + +def add_key(content, wait=True): + """Import a GnuPG key to trust repositores signed by it. + + Keyword arguments: + content -- the content of the GnuPG public key + wait -- if the system should be blocked until the internal GnuPG call is + completed. Otherwise the subprocess.Popen() instance will be + returned. By default the call will be blocking. + """ + cmd = _get_gpg_command() + cmd.extend(["--quiet", "--batch", "--import", "-"]) + proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + proc = subprocess.Popen(cmd) + proc.stdin.write(content) + if wait: + _wait_and_raise(proc) + else: + return proc + +def remove_key(fingerprint, wait=True): + """Remove a GnuPG key to no longer trust repositores signed by it. + + Keyword arguments: + fingerprint -- the fingerprint identifying the key + wait -- if the system should be blocked until the internal GnuPG is + completed. Otherwise the subprocess.Popen() instance will be + returned. By default the call will be blocking. + """ + cmd = _get_gpg_command() + cmd.extend(["--quiet", "--batch", "--delete-key", "--yes", fingerprint]) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + if wait: + _wait_and_raise(proc) + else: + return proc + +def list_keys(): + """Returns a list of TrustedKey instances for each key which is + used to trust repositories. + """ + cmd = _get_gpg_command() + cmd.extend(["--with-colons", "--batch", "--list-keys"]) + res = [] + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + _wait_and_raise(proc) + for line in proc.stdout.readlines(): + fields = line.split(":") + if fields[0] == "pub": + key = TrustedKey(fields[9], fields[4][-8:], fields[5]) + res.append(key) + return res + +if __name__ == "__main__": + # Add some known keys we would like to see translated so that they get + # picked up by gettext + lambda: _("Ubuntu Archive Automatic Signing Key ") + lambda: _("Ubuntu CD Image Automatic Signing Key ") + + apt_pkg.init() + for trusted_key in list_keys(): + print(trusted_key) -- cgit v1.2.3 From 1b18197f00a0f8b230c11dd90b6f9799ac114d55 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Thu, 31 May 2012 19:18:46 +0200 Subject: Don't overwrite proc twice --- apt/auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index 97f9d578..d6b1d97f 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -153,8 +153,8 @@ def add_key(content, wait=True): stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) - proc = subprocess.Popen(cmd) proc.stdin.write(content) + proc.stdin.close() if wait: _wait_and_raise(proc) else: -- cgit v1.2.3 From fdae874a77c122a72d283fc13c5494b0359f6b67 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Fri, 1 Jun 2012 06:37:51 +0200 Subject: Add an export_key method --- apt/auth.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index d6b1d97f..3b15b256 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -179,6 +179,26 @@ def remove_key(fingerprint, wait=True): else: return proc +def export_key(fingerprint, wait=True): + """Return the GnuPG key in text format. + + Keyword arguments: + fingerprint -- the fingerprint identifying the key + wait -- if the system should be blocked until the internal GnuPG is + completed. Otherwise the subprocess.Popen() instance will be + returned. By default the call will be blocking. + """ + cmd = _get_gpg_command() + cmd.extend(["--armor", "--export", fingerprint]) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + if wait: + _wait_and_raise(proc) + return proc.stdout.read() + else: + return proc + def list_keys(): """Returns a list of TrustedKey instances for each key which is used to trust repositories. -- cgit v1.2.3 From 207b245b2ce5a9a04b187f5001b99093c660701d Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Fri, 1 Jun 2012 06:38:33 +0200 Subject: Don't set the trust model to always --- apt/auth.py | 1 - 1 file changed, 1 deletion(-) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index 3b15b256..1d018f8b 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -60,7 +60,6 @@ def _get_gpg_command(keyring=None): cmd = [apt_pkg.config.find_file("Dir::Bin::Gpg", "/usr/bin/gpg"), "--ignore-time-conflict", "--no-default-keyring", - "--trust-model", "always", "--no-options", "--secret-keyring", os.path.join(_TMPDIR, "secring.gpg")] if keyring is None: -- cgit v1.2.3 From f968fba66c7b540bb34417e9675299e283b926d1 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Fri, 1 Jun 2012 07:23:30 +0200 Subject: Strip exported key --- apt/auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index 1d018f8b..8663a043 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -194,7 +194,7 @@ def export_key(fingerprint, wait=True): universal_newlines=True) if wait: _wait_and_raise(proc) - return proc.stdout.read() + return proc.stdout.read().strip() else: return proc -- cgit v1.2.3 From 18fa7a01ebb31f5bfa465bf191989eb060713248 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Fri, 1 Jun 2012 08:18:24 +0200 Subject: Small fix --- apt/auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index 8663a043..024f01da 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -103,7 +103,7 @@ def add_key_from_file(filename, wait=True): """ if not os.path.abspath(filename): raise SystemError("An absolute path is required: %s" % filename) - if not os.access(os.R_OK): + if not os.access(filename, os.R_OK): raise SystemError("Key file cannot be accessed: %s" % filename) cmd = _get_gpg_command() cmd.extend(["--quiet", "--batch", "--import", filename]) -- cgit v1.2.3 From 621372f543a4d0547f3637889da11665b628df14 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 5 Jun 2012 11:39:40 +0200 Subject: pep8 fixes --- apt/auth.py | 14 +++++++++++--- tests/test_auth.py | 6 +++--- 2 files changed, 14 insertions(+), 6 deletions(-) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index 024f01da..04aae908 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -7,11 +7,11 @@ # Author: Michael Vogt # Sebastian Heinlein # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -84,6 +84,7 @@ def _get_gpg_command(keyring=None): "--trustdb-name", os.path.join(_TMPDIR, "trustdb.gpg")]) return cmd + def _wait_and_raise(proc): """Wait until the given subprocess is completed and raise a SystemError if it failed. @@ -92,6 +93,7 @@ def _wait_and_raise(proc): output = proc.stdout.read() raise SystemError("GnuPG command failed: %s" % output) + def add_key_from_file(filename, wait=True): """Import a GnuPG key file to trust repositores signed by it. @@ -115,6 +117,7 @@ def add_key_from_file(filename, wait=True): else: return proc + def add_key_from_keyserver(keyid, keyserver, wait=True): """Import a GnuPG key file to trust repositores signed by it. @@ -137,6 +140,7 @@ def add_key_from_keyserver(keyid, keyserver, wait=True): else: return proc + def add_key(content, wait=True): """Import a GnuPG key to trust repositores signed by it. @@ -159,6 +163,7 @@ def add_key(content, wait=True): else: return proc + def remove_key(fingerprint, wait=True): """Remove a GnuPG key to no longer trust repositores signed by it. @@ -178,6 +183,7 @@ def remove_key(fingerprint, wait=True): else: return proc + def export_key(fingerprint, wait=True): """Return the GnuPG key in text format. @@ -198,6 +204,7 @@ def export_key(fingerprint, wait=True): else: return proc + def list_keys(): """Returns a list of TrustedKey instances for each key which is used to trust repositories. @@ -216,6 +223,7 @@ def list_keys(): res.append(key) return res + if __name__ == "__main__": # Add some known keys we would like to see translated so that they get # picked up by gettext diff --git a/tests/test_auth.py b/tests/test_auth.py index e9906f56..7ecebb7b 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -125,9 +125,9 @@ class TestAuthKeys(unittest.TestCase): os.makedirs(trustedparts_dir) def _restore_apt_config(self, cnf): - """Restore previous apt configuration.""" - for item in cnf: - apt_pkg.config.set(item, cnf[item]) + """Restore previous apt configuration.""" + for item in cnf: + apt_pkg.config.set(item, cnf[item]) def testAddAndExportKey(self): """Add an example key.""" -- cgit v1.2.3 From a8ec7c4c6430e69953ea8f8f3925d1b0fb4ac63a Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 5 Jun 2012 11:43:43 +0200 Subject: apt/auth.py: create _TMPDIR on demand --- apt/auth.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index 04aae908..d8c602fa 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -36,8 +36,7 @@ from apt_pkg import gettext as _ # Create a temporary dir to store secret keying and trust database. # APT doesn't use a secrect key ring but GnuPG fails without it. -_TMPDIR = tempfile.mkdtemp() -atexit.register(shutil.rmtree, _TMPDIR) +_TMPDIR = None class TrustedKey(object): @@ -57,6 +56,10 @@ class TrustedKey(object): def _get_gpg_command(keyring=None): """Return the gpg command""" + global _TMPDIR + if _TMPDIR is None: + _TMPDIR = tempfile.mkdtemp() + atexit.register(shutil.rmtree, _TMPDIR) cmd = [apt_pkg.config.find_file("Dir::Bin::Gpg", "/usr/bin/gpg"), "--ignore-time-conflict", "--no-default-keyring", -- cgit v1.2.3 From b6dbdb941109b5bbfbb5042ad49e1353602240d2 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Wed, 6 Jun 2012 12:18:16 +0200 Subject: Call apt-key instead of gnupg --- apt/auth.py | 160 ++++++++++++++++++------------------------------------------ 1 file changed, 48 insertions(+), 112 deletions(-) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index d8c602fa..3a1d132e 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -24,20 +24,14 @@ """Handle GnuPG keys used to trust signed repositories.""" import atexit -import glob import os import os.path -import shutil import subprocess import tempfile import apt_pkg from apt_pkg import gettext as _ -# Create a temporary dir to store secret keying and trust database. -# APT doesn't use a secrect key ring but GnuPG fails without it. -_TMPDIR = None - class TrustedKey(object): @@ -54,48 +48,42 @@ class TrustedKey(object): return "%s\n%s %s" % (self.name, self.keyid, self.date) -def _get_gpg_command(keyring=None): - """Return the gpg command""" - global _TMPDIR - if _TMPDIR is None: - _TMPDIR = tempfile.mkdtemp() - atexit.register(shutil.rmtree, _TMPDIR) - cmd = [apt_pkg.config.find_file("Dir::Bin::Gpg", "/usr/bin/gpg"), - "--ignore-time-conflict", - "--no-default-keyring", - "--no-options", - "--secret-keyring", os.path.join(_TMPDIR, "secring.gpg")] - if keyring is None: - # Add the public keyring - cmd.extend(["--keyring", - apt_pkg.config.find_file("Dir::Etc::Trusted"), - "--primary-keyring", - apt_pkg.config.find_file("Dir::Etc::Trusted")]) - # Add the public keyring parts - trusted_parts_dir = apt_pkg.config.find_dir("Dir::Etc::TrustedParts") - for part_name in glob.glob(os.path.join(trusted_parts_dir, "*.gpg")): - part_path = os.path.join(trusted_parts_dir, part_name) - if os.access(part_path, os.R_OK): - cmd.extend(["--keyring", part_path]) - # TrustDB - trustdb_path = os.path.join(apt_pkg.config.find_dir("Dir::Etc"), - "trustdb.gpg") - cmd.extend(["--trustdb-name", trustdb_path]) - else: - cmd.extend(["--keyring", os.path.abspath(keyring), - "--primary-keyring", os.path.abspath(keyring), - "--trustdb-name", os.path.join(_TMPDIR, "trustdb.gpg")]) - return cmd - - -def _wait_and_raise(proc): - """Wait until the given subprocess is completed and raise a - SystemError if it failed. - """ - if proc.wait() != 0: - output = proc.stdout.read() - raise SystemError("GnuPG command failed: %s" % output) - +def _call_apt_key_script(*args, **kwargs): + """Run the apt-key script with the given arguments.""" + cmd = [apt_pkg.config.find_file("Dir::Bin::Apt-Key", "/usr/bin/apt-key")] + cmd.extend(args) + if os.getuid() != 0: + cmd.insert(0, "fakeroot") + env = os.environ.copy() + env["LANG"] = "C" + if apt_pkg.config.find_dir("Dir") != "/": + # If the key is to be installed into a chroot we have to export the + # configuration from the chroot to the apt-key script by using + # a temporary APT_CONFIG file. The apt-key script uses apt-config shell + # internally + conf_fd, conf_name = tempfile.mkstemp(prefix="apt-key", suffix="conf") + atexit.register(os.remove, conf_name) + try: + os.write(conf_fd, apt_pkg.config.dump().encode("UTF-8")) + finally: + os.close(conf_fd) + env["APT_CONFIG"] = conf_name + proc = subprocess.Popen(cmd, env=env, universal_newlines=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + try: + proc.stdin.write(kwargs["stdin"]) + except KeyError: + pass + finally: + proc.stdin.close() + return_code = proc.wait() + output = proc.stdout.read() + if return_code: + raise SystemError("The apt-key script failed with return code %s:\n" + "%s\n%s" % (return_code, " ".join(cmd), output)) + return output.strip() def add_key_from_file(filename, wait=True): """Import a GnuPG key file to trust repositores signed by it. @@ -110,16 +98,7 @@ def add_key_from_file(filename, wait=True): raise SystemError("An absolute path is required: %s" % filename) if not os.access(filename, os.R_OK): raise SystemError("Key file cannot be accessed: %s" % filename) - cmd = _get_gpg_command() - cmd.extend(["--quiet", "--batch", "--import", filename]) - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) - if wait: - _wait_and_raise(proc) - else: - return proc - + _call_apt_key_script("add", filename) def add_key_from_keyserver(keyid, keyserver, wait=True): """Import a GnuPG key file to trust repositores signed by it. @@ -131,18 +110,8 @@ def add_key_from_keyserver(keyid, keyserver, wait=True): completed. Otherwise the subprocess.Popen() instance will be returned. By default the call will be blocking. """ - cmd = _get_gpg_command() - cmd.extend(["--quiet", "--batch", - "--keyserver", keyserver, - "--recv", keyid]) - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) - if wait: - _wait_and_raise(proc) - else: - return proc - + _call_apt_key_script("adv", "--quiet", "--keyserver", keyserver, + "--recv", keyid) def add_key(content, wait=True): """Import a GnuPG key to trust repositores signed by it. @@ -153,19 +122,8 @@ def add_key(content, wait=True): completed. Otherwise the subprocess.Popen() instance will be returned. By default the call will be blocking. """ - cmd = _get_gpg_command() - cmd.extend(["--quiet", "--batch", "--import", "-"]) - proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) - proc.stdin.write(content) - proc.stdin.close() - if wait: - _wait_and_raise(proc) - else: - return proc - + _call_apt_key_script("adv", "--quiet", "--batch", + "--import", "-", stdin=content) def remove_key(fingerprint, wait=True): """Remove a GnuPG key to no longer trust repositores signed by it. @@ -176,16 +134,7 @@ def remove_key(fingerprint, wait=True): completed. Otherwise the subprocess.Popen() instance will be returned. By default the call will be blocking. """ - cmd = _get_gpg_command() - cmd.extend(["--quiet", "--batch", "--delete-key", "--yes", fingerprint]) - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) - if wait: - _wait_and_raise(proc) - else: - return proc - + _call_apt_key_script("rm", fingerprint) def export_key(fingerprint, wait=True): """Return the GnuPG key in text format. @@ -196,37 +145,24 @@ def export_key(fingerprint, wait=True): completed. Otherwise the subprocess.Popen() instance will be returned. By default the call will be blocking. """ - cmd = _get_gpg_command() - cmd.extend(["--armor", "--export", fingerprint]) - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) - if wait: - _wait_and_raise(proc) - return proc.stdout.read().strip() - else: - return proc - + return _call_apt_key_script("export", fingerprint) def list_keys(): """Returns a list of TrustedKey instances for each key which is used to trust repositories. """ - cmd = _get_gpg_command() - cmd.extend(["--with-colons", "--batch", "--list-keys"]) + # The output of `apt-key list` is difficult to parse since the + # --with-colons parameter isn't user + output = _call_apt_key_script("adv", "--with-colons", "--batch", + "--list-keys") res = [] - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) - _wait_and_raise(proc) - for line in proc.stdout.readlines(): + for line in output.split("\n"): fields = line.split(":") if fields[0] == "pub": key = TrustedKey(fields[9], fields[4][-8:], fields[5]) res.append(key) return res - if __name__ == "__main__": # Add some known keys we would like to see translated so that they get # picked up by gettext -- cgit v1.2.3 From 1b765003316443169b626874c50b98e0f91efb88 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Wed, 6 Jun 2012 12:19:45 +0200 Subject: Remove the wait statement to get a cleaner API --- apt/auth.py | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index 3a1d132e..27365678 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -85,14 +85,11 @@ def _call_apt_key_script(*args, **kwargs): "%s\n%s" % (return_code, " ".join(cmd), output)) return output.strip() -def add_key_from_file(filename, wait=True): +def add_key_from_file(filename): """Import a GnuPG key file to trust repositores signed by it. Keyword arguments: filename -- the absolute path to the public GnuPG key file - wait -- if the system should be blocked until the internal GnuPG call is - completed. Otherwise the subprocess.Popen() instance will be - returned. By default the call will be blocking. """ if not os.path.abspath(filename): raise SystemError("An absolute path is required: %s" % filename) @@ -100,50 +97,38 @@ def add_key_from_file(filename, wait=True): raise SystemError("Key file cannot be accessed: %s" % filename) _call_apt_key_script("add", filename) -def add_key_from_keyserver(keyid, keyserver, wait=True): +def add_key_from_keyserver(keyid, keyserver): """Import a GnuPG key file to trust repositores signed by it. Keyword arguments: keyid -- the identifier of the key, e.g. 0x0EB12DSA keyserver -- the URL or hostname of the key server - wait -- if the system should be blocked until the internal GnuPG call is - completed. Otherwise the subprocess.Popen() instance will be - returned. By default the call will be blocking. """ _call_apt_key_script("adv", "--quiet", "--keyserver", keyserver, "--recv", keyid) -def add_key(content, wait=True): +def add_key(content): """Import a GnuPG key to trust repositores signed by it. Keyword arguments: content -- the content of the GnuPG public key - wait -- if the system should be blocked until the internal GnuPG call is - completed. Otherwise the subprocess.Popen() instance will be - returned. By default the call will be blocking. """ _call_apt_key_script("adv", "--quiet", "--batch", "--import", "-", stdin=content) -def remove_key(fingerprint, wait=True): +def remove_key(fingerprint): """Remove a GnuPG key to no longer trust repositores signed by it. Keyword arguments: fingerprint -- the fingerprint identifying the key - wait -- if the system should be blocked until the internal GnuPG is - completed. Otherwise the subprocess.Popen() instance will be - returned. By default the call will be blocking. """ _call_apt_key_script("rm", fingerprint) -def export_key(fingerprint, wait=True): +def export_key(fingerprint): """Return the GnuPG key in text format. Keyword arguments: fingerprint -- the fingerprint identifying the key - wait -- if the system should be blocked until the internal GnuPG is - completed. Otherwise the subprocess.Popen() instance will be - returned. By default the call will be blocking. """ return _call_apt_key_script("export", fingerprint) -- cgit v1.2.3 From c1abd02805bf19dab6183b1f22900877fd62f1e8 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Wed, 6 Jun 2012 12:20:33 +0200 Subject: Don't call fakeroot by default - this should be done by an alternate Dir::Bin::Apt-key script --- apt/auth.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index 27365678..255d7fd4 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -52,8 +52,6 @@ def _call_apt_key_script(*args, **kwargs): """Run the apt-key script with the given arguments.""" cmd = [apt_pkg.config.find_file("Dir::Bin::Apt-Key", "/usr/bin/apt-key")] cmd.extend(args) - if os.getuid() != 0: - cmd.insert(0, "fakeroot") env = os.environ.copy() env["LANG"] = "C" if apt_pkg.config.find_dir("Dir") != "/": -- cgit v1.2.3 From 8d592cbbc8cab3407aa628579ed002ef8d7b8c4a Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Wed, 6 Jun 2012 12:26:44 +0200 Subject: Add support for update and net-update --- apt/auth.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index 255d7fd4..a999a7cf 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -130,6 +130,24 @@ def export_key(fingerprint): """ return _call_apt_key_script("export", fingerprint) +def update(): + """Update the local keyring with the archive keyring and remove from + the local keyring the archive keys which are no longer valid. The + archive keyring is shipped in the archive-keyring package of your + distribution, e.g. the debian-archive-keyring package in Debian. + """ + return _call_apt_key_script("update") + +def net_update(): + """Work similar to the update command above, but get the archive + keyring from an URI instead and validate it against a master key. + This requires an installed wget(1) and an APT build configured to + have a server to fetch from and a master keyring to validate. APT + in Debian does not support this command and relies on update + instead, but Ubuntu's APT does. + """ + return _call_apt_key_script("net-update") + def list_keys(): """Returns a list of TrustedKey instances for each key which is used to trust repositories. -- cgit v1.2.3 From b1d3ecfe422383a641512779fdb0084b397fd7e6 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Wed, 6 Jun 2012 12:27:18 +0200 Subject: Add to copyright --- apt/auth.py | 1 + 1 file changed, 1 insertion(+) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index a999a7cf..38c4bdc6 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -3,6 +3,7 @@ # auth - authentication key management # # Copyright (c) 2004 Canonical +# Copyright (c) 2012 Sebastian Heinlein # # Author: Michael Vogt # Sebastian Heinlein -- cgit v1.2.3