summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apt/auth.py262
1 files changed, 186 insertions, 76 deletions
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 <mvo@debian.org>
-#
+# Sebastian Heinlein <devel@glatzor.de>
+#
# 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 <ftpmaster@ubuntu.com>")
-N_("Ubuntu CD Image Automatic Signing Key <cdimage@ubuntu.com>")
-
-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 <ftpmaster@ubuntu.com>")
+ lambda: _("Ubuntu CD Image Automatic Signing Key <cdimage@ubuntu.com>")
+
+ apt_pkg.init()
+ for trusted_key in list_keys():
+ print(trusted_key)