Blame dnf/crypto.py

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