egencache --update-manifests: re-sign when needed
authorZac Medico <zmedico@gentoo.org>
Tue, 16 Oct 2012 06:46:41 +0000 (23:46 -0700)
committerZac Medico <zmedico@gentoo.org>
Tue, 16 Oct 2012 06:47:09 +0000 (23:47 -0700)
If the Manifest is signed with a different key from the one specified
by --gpg-key or PORTAGE_GPG_KEY, strip the existing signature and
re-sign the Manifest.

bin/egencache
pym/portage/package/ebuild/_parallel_manifest/ManifestScheduler.py
pym/portage/package/ebuild/_parallel_manifest/ManifestTask.py

index 15571cbdccc181c71cd52cd4cf5e5b1c0cb65a5b..9d88237a31d09bce7c8a1e7ab6d35af7c5230079 100755 (executable)
@@ -969,6 +969,7 @@ def egencache_main(args):
 
        gpg_cmd = None
        gpg_vars = None
+       force_sign_key = None
 
        if options.update_manifests:
                if repo_config.sign_manifest:
@@ -1024,6 +1025,8 @@ def egencache_main(args):
                                if v is not None:
                                        gpg_vars[k] = v
 
+                       force_sign_key = gpg_vars.get("PORTAGE_GPG_KEY")
+
        ret = [os.EX_OK]
 
        if options.update:
@@ -1050,6 +1053,7 @@ def egencache_main(args):
                event_loop = global_event_loop()
                scheduler = ManifestScheduler(portdb, cp_iter=cp_iter,
                        gpg_cmd=gpg_cmd, gpg_vars=gpg_vars,
+                       force_sign_key=force_sign_key,
                        max_jobs=options.jobs,
                        max_load=options.load_average,
                        event_loop=event_loop)
index 77d41339a586f57fb7462f77b26d0b072169e9ed..3dc955684efbcea0ee0fb914a7c06a51a372746e 100644 (file)
@@ -12,7 +12,7 @@ from .ManifestTask import ManifestTask
 class ManifestScheduler(AsyncScheduler):
 
        def __init__(self, portdb, cp_iter=None,
-               gpg_cmd=None, gpg_vars=None, **kwargs):
+               gpg_cmd=None, gpg_vars=None, force_sign_key=None, **kwargs):
 
                AsyncScheduler.__init__(self, **kwargs)
 
@@ -23,6 +23,7 @@ class ManifestScheduler(AsyncScheduler):
                self._cp_iter = cp_iter
                self._gpg_cmd = gpg_cmd
                self._gpg_vars = gpg_vars
+               self._force_sign_key = force_sign_key
                self._task_iter = self._iter_tasks()
 
        def _next_task(self):
@@ -76,7 +77,8 @@ class ManifestScheduler(AsyncScheduler):
 
                                yield ManifestTask(cp=cp, distdir=distdir,
                                        fetchlist_dict=fetchlist_dict, repo_config=repo_config,
-                                       gpg_cmd=self._gpg_cmd, gpg_vars=self._gpg_vars)
+                                       gpg_cmd=self._gpg_cmd, gpg_vars=self._gpg_vars,
+                                       force_sign_key=self._force_sign_key)
 
        def _task_exit(self, task):
 
index 1b954f0f3fac12f71c7ec697aa40eefae2647c11..fbb3c88567f4bc44306a5a933d924137c92ef949 100644 (file)
@@ -2,20 +2,28 @@
 # Distributed under the terms of the GNU General Public License v2
 
 import errno
+import re
+import subprocess
 
 from portage import os
 from portage import _unicode_encode, _encodings
-from portage.util import shlex_split, varexpand, writemsg
+from portage.const import MANIFEST2_IDENTIFIERS
+from portage.util import (atomic_ofstream, grablines,
+       shlex_split, varexpand, writemsg)
+from portage.util._async.PopenProcess import PopenProcess
 from _emerge.CompositeTask import CompositeTask
+from _emerge.PipeReader import PipeReader
 from _emerge.SpawnProcess import SpawnProcess
 from .ManifestProcess import ManifestProcess
 
 class ManifestTask(CompositeTask):
 
        __slots__ = ("cp", "distdir", "fetchlist_dict", "gpg_cmd",
-               "gpg_vars", "repo_config", "_manifest_path")
+               "gpg_vars", "repo_config", "force_sign_key", "_manifest_path",
+               "_proc")
 
        _PGP_HEADER = b"BEGIN PGP SIGNED MESSAGE"
+       _manifest_line_re = re.compile(r'^(%s) ' % "|".join(MANIFEST2_IDENTIFIERS))
 
        def _start(self):
                self._manifest_path = os.path.join(self.repo_config.location,
@@ -25,6 +33,16 @@ class ManifestTask(CompositeTask):
                        scheduler=self.scheduler)
                self._start_task(manifest_proc, self._manifest_proc_exit)
 
+       def _cancel(self):
+               if self._proc is not None:
+                       self._proc.cancel()
+               CompositeTask._cancel(self)
+
+       def _proc_wait(self):
+               if self._proc is not None:
+                       self._proc.wait()
+                       self._proc = None
+
        def _manifest_proc_exit(self, manifest_proc):
                self._assert_current(manifest_proc)
                if manifest_proc.returncode not in (os.EX_OK, manifest_proc.MODIFIED):
@@ -38,6 +56,10 @@ class ManifestTask(CompositeTask):
 
                if not modified and sign:
                        sign = self._need_signature()
+                       if not sign and self.force_sign_key is not None \
+                               and os.path.exists(self._manifest_path):
+                               self._check_sig_key()
+                               return
 
                if not sign or not os.path.exists(self._manifest_path):
                        self.returncode = os.EX_OK
@@ -47,6 +69,64 @@ class ManifestTask(CompositeTask):
 
                self._start_gpg_proc()
 
+       def _check_sig_key(self):
+               self._proc = PopenProcess(proc=subprocess.Popen(
+                       ["gpg", "--verify", self._manifest_path],
+                       stdout=subprocess.PIPE, stderr=subprocess.STDOUT),
+                       scheduler=self.scheduler)
+               pipe_reader = PipeReader(
+                       input_files={"producer" : self._proc.proc.stdout},
+                       scheduler=self.scheduler)
+               self._start_task(pipe_reader, self._check_sig_key_exit)
+
+       @staticmethod
+       def _parse_gpg_key(output):
+               """
+               Returns the last token of the first line, or None if there
+               is no such token.
+               """
+               output = output.splitlines()
+               if output:
+                       output = output[0].split()
+                       if output:
+                               return output[-1]
+               return None
+
+       def _check_sig_key_exit(self, pipe_reader):
+               self._assert_current(pipe_reader)
+
+               parsed_key = self._parse_gpg_key(
+                       pipe_reader.getvalue().decode('utf_8', 'replace'))
+               if parsed_key is not None and \
+                       parsed_key.lower() in self.force_sign_key.lower():
+                       self.returncode = os.EX_OK
+                       self._current_task = None
+                       self._proc_wait()
+                       self.wait()
+                       return
+
+               self._strip_sig(self._manifest_path)
+               self._start_gpg_proc()
+
+       @staticmethod
+       def _strip_sig(manifest_path):
+               """
+               Strip an existing signature from a Manifest file.
+               """
+               line_re = ManifestTask._manifest_line_re
+               lines = grablines(manifest_path)
+               f = None
+               try:
+                       f = atomic_ofstream(manifest_path)
+                       for line in lines:
+                               if line_re.match(line) is not None:
+                                       f.write(line)
+                       f.close()
+                       f = None
+               finally:
+                       if f is not None:
+                               f.abort()
+
        def _start_gpg_proc(self):
                gpg_vars = self.gpg_vars
                if gpg_vars is None:
@@ -62,6 +142,7 @@ class ManifestTask(CompositeTask):
 
        def _gpg_proc_exit(self, gpg_proc):
                if self._default_exit(gpg_proc) != os.EX_OK:
+                       self._proc_wait()
                        self.wait()
                        return
 
@@ -80,6 +161,7 @@ class ManifestTask(CompositeTask):
                        self.returncode = os.EX_OK
 
                self._current_task = None
+               self._proc_wait()
                self.wait()
 
        def _need_signature(self):