|
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()
|