From 84ea673c632dea0bc9af61c371f71ca86cc19e1a Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Mon, 15 Oct 2012 23:46:41 -0700 Subject: egencache --update-manifests: re-sign when needed 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. --- .../ebuild/_parallel_manifest/ManifestScheduler.py | 6 +- .../ebuild/_parallel_manifest/ManifestTask.py | 86 +++++++++++++++++++++- 2 files changed, 88 insertions(+), 4 deletions(-) (limited to 'pym') diff --git a/pym/portage/package/ebuild/_parallel_manifest/ManifestScheduler.py b/pym/portage/package/ebuild/_parallel_manifest/ManifestScheduler.py index 77d41339a..3dc955684 100644 --- a/pym/portage/package/ebuild/_parallel_manifest/ManifestScheduler.py +++ b/pym/portage/package/ebuild/_parallel_manifest/ManifestScheduler.py @@ -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): diff --git a/pym/portage/package/ebuild/_parallel_manifest/ManifestTask.py b/pym/portage/package/ebuild/_parallel_manifest/ManifestTask.py index 1b954f0f3..fbb3c8856 100644 --- a/pym/portage/package/ebuild/_parallel_manifest/ManifestTask.py +++ b/pym/portage/package/ebuild/_parallel_manifest/ManifestTask.py @@ -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): -- cgit v1.2.3-1-g7c22