Blame dnf/crypto.py

Packit 6f3914
# crypto.py
Packit 6f3914
# Keys and signatures.
Packit 6f3914
#
Packit 6f3914
# Copyright (C) 2014  Red Hat, Inc.
Packit 6f3914
#
Packit 6f3914
# This copyrighted material is made available to anyone wishing to use,
Packit 6f3914
# modify, copy, or redistribute it subject to the terms and conditions of
Packit 6f3914
# the GNU General Public License v.2, or (at your option) any later version.
Packit 6f3914
# This program is distributed in the hope that it will be useful, but WITHOUT
Packit 6f3914
# ANY WARRANTY expressed or implied, including the implied warranties of
Packit 6f3914
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
Packit 6f3914
# Public License for more details.  You should have received a copy of the
Packit 6f3914
# GNU General Public License along with this program; if not, write to the
Packit 6f3914
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit 6f3914
# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
Packit 6f3914
# source code or documentation are not subject to the GNU General Public
Packit 6f3914
# License and may only be used or replicated with the express permission of
Packit 6f3914
# Red Hat, Inc.
Packit 6f3914
#
Packit 6f3914
Packit 6f3914
from __future__ import print_function
Packit 6f3914
from __future__ import absolute_import
Packit 6f3914
from __future__ import unicode_literals
Packit 6f3914
from dnf.i18n import _
Packit 6f3914
import contextlib
Packit 6f3914
import dnf.pycomp
Packit 6f3914
import dnf.util
Packit 6f3914
import dnf.yum.misc
Packit 6f3914
import io
Packit 6f3914
import logging
Packit 6f3914
import os
Packit 6f3914
import tempfile
Packit 6f3914
Packit 6f3914
try:
Packit 6f3914
    from gpg import Context
Packit 6f3914
    from gpg import Data
Packit 6f3914
except ImportError:
Packit 6f3914
    import gpgme
Packit 6f3914
Packit 6f3914
Packit 6f3914
    class Context(object):
Packit 6f3914
        def __init__(self):
Packit 6f3914
            self.__dict__["ctx"] = gpgme.Context()
Packit 6f3914
Packit 6f3914
        def __enter__(self):
Packit 6f3914
            return self
Packit 6f3914
Packit 6f3914
        def __exit__(self, type, value, tb):
Packit 6f3914
            pass
Packit 6f3914
Packit 6f3914
        @property
Packit 6f3914
        def armor(self):
Packit 6f3914
            return self.ctx.armor
Packit 6f3914
Packit 6f3914
        @armor.setter
Packit 6f3914
        def armor(self, value):
Packit 6f3914
            self.ctx.armor = value
Packit 6f3914
Packit 6f3914
        def op_import(self, key_fo):
Packit 6f3914
            if isinstance(key_fo, basestring):
Packit 6f3914
                key_fo = io.BytesIO(key_fo)
Packit 6f3914
            self.ctx.import_(key_fo)
Packit 6f3914
Packit 6f3914
        def op_export(self, pattern, mode, keydata):
Packit 6f3914
            self.ctx.export(pattern, keydata)
Packit 6f3914
Packit 6f3914
        def __getattr__(self, name):
Packit 6f3914
            return getattr(self.ctx, name)
Packit 6f3914
Packit 6f3914
Packit 6f3914
    class Data(object):
Packit 6f3914
        def __init__(self):
Packit 6f3914
            self.__dict__["buf"] = io.BytesIO()
Packit 6f3914
Packit 6f3914
        def __enter__(self):
Packit 6f3914
            return self
Packit 6f3914
Packit 6f3914
        def __exit__(self, type, value, tb):
Packit 6f3914
            pass
Packit 6f3914
Packit 6f3914
        def read(self):
Packit 6f3914
            return self.buf.getvalue()
Packit 6f3914
Packit 6f3914
        def __getattr__(self, name):
Packit 6f3914
            return getattr(self.buf, name)
Packit 6f3914
Packit 6f3914
Packit 6f3914
GPG_HOME_ENV = 'GNUPGHOME'
Packit 6f3914
logger = logging.getLogger('dnf')
Packit 6f3914
Packit 6f3914
Packit 6f3914
def _extract_signing_subkey(key):
Packit 6f3914
    return dnf.util.first(subkey for subkey in key.subkeys if subkey.can_sign)
Packit 6f3914
Packit 6f3914
Packit 6f3914
def _printable_fingerprint(fpr_hex):
Packit 6f3914
    segments = (fpr_hex[i:i + 4] for i in range(0, len(fpr_hex), 4))
Packit 6f3914
    return " ".join(segments)
Packit 6f3914
Packit 6f3914
Packit 6f3914
def import_repo_keys(repo):
Packit 6f3914
    gpgdir = repo._pubring_dir
Packit 6f3914
    known_keys = keyids_from_pubring(gpgdir)
Packit 6f3914
    for keyurl in repo.gpgkey:
Packit 6f3914
        for keyinfo in retrieve(keyurl, repo):
Packit 6f3914
            keyid = keyinfo.id_
Packit 6f3914
            if keyid in known_keys:
Packit 6f3914
                logger.debug(_('repo %s: 0x%s already imported'), repo.id, keyid)
Packit 6f3914
                continue
Packit 6f3914
            if not repo._key_import._confirm(keyinfo):
Packit 6f3914
                continue
Packit 6f3914
            dnf.yum.misc.import_key_to_pubring(
Packit 6f3914
                keyinfo.raw_key, keyinfo.short_id, gpgdir=gpgdir,
Packit 6f3914
                make_ro_copy=False)
Packit 6f3914
            logger.debug(_('repo %s: imported key 0x%s.'), repo.id, keyid)
Packit 6f3914
Packit 6f3914
Packit 6f3914
def keyids_from_pubring(gpgdir):
Packit 6f3914
    if not os.path.exists(gpgdir):
Packit 6f3914
        return []
Packit 6f3914
Packit 6f3914
    with pubring_dir(gpgdir), Context() as ctx:
Packit 6f3914
        keyids = []
Packit 6f3914
        for k in ctx.keylist():
Packit 6f3914
            subkey = _extract_signing_subkey(k)
Packit 6f3914
            if subkey is not None:
Packit 6f3914
                keyids.append(subkey.keyid)
Packit 6f3914
        return keyids
Packit 6f3914
Packit 6f3914
Packit 6f3914
def log_key_import(keyinfo):
Packit 6f3914
    msg = (_('Importing GPG key 0x%s:\n'
Packit 6f3914
             ' Userid     : "%s"\n'
Packit 6f3914
             ' Fingerprint: %s\n'
Packit 6f3914
             ' From       : %s') %
Packit 6f3914
           (keyinfo.short_id, keyinfo.userid,
Packit 6f3914
            _printable_fingerprint(keyinfo.fingerprint),
Packit 6f3914
            keyinfo.url.replace("file://", "")))
Packit 6f3914
    logger.critical("%s", msg)
Packit 6f3914
Packit 6f3914
Packit 6f3914
@contextlib.contextmanager
Packit 6f3914
def pubring_dir(pubring_dir):
Packit 6f3914
    orig = os.environ.get(GPG_HOME_ENV, None)
Packit 6f3914
    os.environ[GPG_HOME_ENV] = pubring_dir
Packit 6f3914
    try:
Packit 6f3914
        yield
Packit 6f3914
    finally:
Packit 6f3914
        if orig is None:
Packit 6f3914
            del os.environ[GPG_HOME_ENV]
Packit 6f3914
        else:
Packit 6f3914
            os.environ[GPG_HOME_ENV] = orig
Packit 6f3914
Packit 6f3914
Packit 6f3914
def rawkey2infos(key_fo):
Packit 6f3914
    pb_dir = tempfile.mkdtemp()
Packit 6f3914
    keyinfos = []
Packit 6f3914
    with pubring_dir(pb_dir), Context() as ctx:
Packit 6f3914
        ctx.op_import(key_fo)
Packit 6f3914
        for key in ctx.keylist():
Packit 6f3914
            subkey = _extract_signing_subkey(key)
Packit 6f3914
            if subkey is None:
Packit 6f3914
                continue
Packit 6f3914
            keyinfos.append(Key(key, subkey))
Packit 6f3914
        ctx.armor = True
Packit 6f3914
        for info in keyinfos:
Packit 6f3914
            with Data() as sink:
Packit 6f3914
                ctx.op_export(info.id_, 0, sink)
Packit 6f3914
                sink.seek(0, os.SEEK_SET)
Packit 6f3914
                info.raw_key = sink.read()
Packit 6f3914
    dnf.util.rm_rf(pb_dir)
Packit 6f3914
    return keyinfos
Packit 6f3914
Packit 6f3914
Packit 6f3914
def retrieve(keyurl, repo=None):
Packit 6f3914
    with dnf.util._urlopen(keyurl, repo=repo) as handle:
Packit 6f3914
        keyinfos = rawkey2infos(handle)
Packit 6f3914
    for keyinfo in keyinfos:
Packit 6f3914
        keyinfo.url = keyurl
Packit 6f3914
    return keyinfos
Packit 6f3914
Packit 6f3914
Packit 6f3914
class Key(object):
Packit 6f3914
    def __init__(self, key, subkey):
Packit 6f3914
        self.id_ = subkey.keyid
Packit 6f3914
        self.fingerprint = subkey.fpr
Packit 6f3914
        self.raw_key = None
Packit 6f3914
        self.timestamp = subkey.timestamp
Packit 6f3914
        self.url = None
Packit 6f3914
        self.userid = key.uids[0].uid
Packit 6f3914
Packit 6f3914
    @property
Packit 6f3914
    def short_id(self):
Packit 6f3914
        rj = '0' if dnf.pycomp.PY3 else b'0'
Packit 6f3914
        return self.id_[-8:].rjust(8, rj)
Packit 6f3914
Packit 6f3914
    @property
Packit 6f3914
    def rpm_id(self):
Packit 6f3914
        return self.short_id.lower()