[yum-git] Branch 'yum-3_2_X' - 3 commits - cli.py output.py yum/__init__.py yum/misc.py yum/repos.py yum/yumRepo.py
Seth Vidal
skvidal at linux.duke.edu
Wed Aug 6 04:40:57 UTC 2008
cli.py | 2
output.py | 12 ++++
yum/__init__.py | 135 +++++++++++++++++++++++++++++++++++++++++---------------
yum/misc.py | 49 +++++++++++++++++---
yum/repos.py | 10 ++--
yum/yumRepo.py | 41 ++++++++++++++++-
6 files changed, 202 insertions(+), 47 deletions(-)
New commits:
commit 835e52c879c1155d2ec3ab2811c1b93b7cfd7706
Author: Seth Vidal <skvidal at fedoraproject.org>
Date: Wed Aug 6 00:36:31 2008 -0400
clean up output garbage and behavior when user will not allow gpg key import to repo-local pubrin
diff --git a/yum/__init__.py b/yum/__init__.py
index 58f2011..5c21db5 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -2845,8 +2845,6 @@ class YumBase(depsolve.Depsolve):
"""
keyurls = repo.gpgkey
key_installed = False
- if not callback:
- callback = self._confirmGpgKeyImport
for keyurl in keyurls:
keys = self._retrievePublicKey(keyurl)
diff --git a/yum/yumRepo.py b/yum/yumRepo.py
index 9bab1b4..970b30b 100644
--- a/yum/yumRepo.py
+++ b/yum/yumRepo.py
@@ -1126,11 +1126,15 @@ class YumRepository(Repository, config.RepoConf):
if self.gpg_import_func:
#FIXME probably should have an else off of this to
# complain if there is no import function
- self.gpg_import_func(self, self.confirm_func)
+ try:
+ self.gpg_import_func(self, self.confirm_func)
+ except Errors.YumBaseError, e:
+ raise URLGrabError(-1, 'Gpg Keys not imported, cannot verify repomd.xml for repo %s' % (self))
+
# FIXME if we get the okay here to import the key then
# we should set an option so that future key imports for this
# repo will be allowed w/o question
-
+
if not misc.valid_detached_sig(result, filepath, self.gpgdir):
raise URLGrabError(-1, 'repomd.xml signature could not be verified for %s' % (self))
commit ba177bbf285067d1eec24c10f2e0246d3a5d5d23
Merge: 122a950... 33abf35...
Author: Seth Vidal <skvidal at fedoraproject.org>
Date: Wed Aug 6 00:22:21 2008 -0400
Merge branch 'yum-3_2_X' of ssh://login.linux.duke.edu/home/groups/yum/git/yum into yum-3_2_X
* 'yum-3_2_X' of ssh://login.linux.duke.edu/home/groups/yum/git/yum:
Add failing test case for non-latest update/install with dep. -- update fails
Don't display download count if 1 pkg to download
Don't display download total if <= 1 pkgs downloaded
Return [] instead of None in named get_notices, to match non-named path
Return the get_applicable_notices() result in the right order (descending)
Don't show ppc packages on .i386 in UpdateNotice.__str__
Fix bad format string in UpdateNotice.__str__
Add get_applicable_notices() call to UpdateMetadata class
Fix UpdateNotice.__str__ when it's None, add name only lookups for notices
Add total download stats. line
Don't give up if PATTERNS_MAX reached, just do the operation multiple times
Make gpgcheck true/false work again
Remove the assoc. of gpgcheck=1/true meaning gpgcheck=all
Give more info. when we fail the disk space checks
commit 122a9503506f88f3a45b3f51a67429c723ab687a
Author: Seth Vidal <skvidal at fedoraproject.org>
Date: Wed Aug 6 00:22:08 2008 -0400
merge changes for repomd.xml signature checking and key import
diff --git a/cli.py b/cli.py
index 4dda42c..e8458b4 100644
--- a/cli.py
+++ b/cli.py
@@ -1060,6 +1060,8 @@ class YumOptionParser(OptionParser):
# setup the progress bars/callbacks
self.base.setupProgressCallbacks()
+ # setup the callbacks to import gpg pubkeys and confirm them
+ self.base.setupKeyImportCallbacks()
# Process repo enables and disables in order
for opt, repoexp in opts.repos:
diff --git a/output.py b/output.py
index c55e5d7..a2c70aa 100644
--- a/output.py
+++ b/output.py
@@ -386,7 +386,11 @@ class YumOutput:
return False
else:
return True
-
+
+ def _cli_confirm_gpg_key_import(self, keydict):
+ # FIXME what should we be printing here?
+ return self.userconfirm()
+
def _group_names2pkgs(self, pkg_names):
# Convert pkg_names to installed pkgs and available pkgs
ipkgs = self.rpmdb.searchNames(pkg_names)
@@ -699,7 +703,11 @@ Remove %5.5s Package(s)
def setupProgessCallbacks(self):
# api purposes only to protect the typo
self.setupProgressCallbacks()
-
+
+ def setupKeyImportCallbacks(self):
+ self.repos.confirm_func = self._cli_confirm_gpg_key_import
+ self.repos.gpg_import_func = self.getKeyForRepo
+
def interrupt_callback(self, cbobj):
'''Handle CTRL-C's during downloads
diff --git a/yum/__init__.py b/yum/__init__.py
index b2dc186..066e3dd 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -2711,6 +2711,38 @@ class YumBase(depsolve.Depsolve):
return returndict
+ def _retrievePublicKey(self, keyurl):
+ """
+ Retrieve a key file
+ @param keyurl: url to the key to retrieve
+ Returns a list of dicts with all the keyinfo
+ """
+ key_installed = False
+
+ self.logger.info(_('Retrieving GPG key from %s') % keyurl)
+
+ # Go get the GPG key from the given URL
+ try:
+ rawkey = urlgrabber.urlread(keyurl, limit=9999)
+ except urlgrabber.grabber.URLGrabError, e:
+ raise Errors.YumBaseError(_('GPG key retrieval failed: ') +
+ to_unicode(str(e)))
+ # Parse the key
+ keys_info = misc.getgpgkeyinfo(rawkey, multiple=True)
+ keys = []
+ for keyinfo in keys_info:
+ thiskey = {}
+ for info in ('keyid', 'timestamp', 'userid',
+ 'fingerprint', 'raw_key'):
+ if not keyinfo.has_key(info):
+ raise Errors.YumBaseError, \
+ _('GPG key parsing failed: key does not have value %s') + info
+ thiskey[info] = keyinfo[info]
+ thiskey['hexkeyid'] = misc.keyIdToRPMVer(keyinfo['keyid']).upper()
+ keys.append(thiskey)
+
+ return keys
+
def getKeyForPackage(self, po, askcb = None, fullaskcb = None):
"""
Retrieve a key for a package. If needed, prompt for if the key should
@@ -2731,60 +2763,40 @@ class YumBase(depsolve.Depsolve):
ts = rpmUtils.transaction.TransactionWrapper(self.conf.installroot)
for keyurl in keyurls:
- self.logger.info(_('Retrieving GPG key from %s') % keyurl)
-
- # Go get the GPG key from the given URL
- try:
- rawkey = urlgrabber.urlread(keyurl, limit=9999)
- except urlgrabber.grabber.URLGrabError, e:
- raise Errors.YumBaseError(_('GPG key retrieval failed: ') +
- to_unicode(str(e)))
-
- # Parse the key
- keys_info = misc.getgpgkeyinfo(rawkey, multiple=True)
-
- for keyinfo in keys_info:
- try:
- keyid = keyinfo['keyid']
- hexkeyid = misc.keyIdToRPMVer(keyid).upper()
- timestamp = keyinfo['timestamp']
- userid = keyinfo['userid']
- fingerprint = keyinfo['fingerprint']
- raw_key = keyinfo['raw_key']
- except ValueError, e:
- raise Errors.YumBaseError, \
- _('GPG key parsing failed: ') + str(e)
+ keys = self._retrievePublicKey(keyurl)
+ for info in keys:
# Check if key is already installed
- if misc.keyInstalled(ts, keyid, timestamp) >= 0:
+ if misc.keyInstalled(ts, info['keyid'], info['timestamp']) >= 0:
self.logger.info(_('GPG key at %s (0x%s) is already installed') % (
- keyurl, hexkeyid))
+ keyurl, info['hexkeyid']))
continue
# Try installing/updating GPG key
self.logger.critical(_('Importing GPG key 0x%s "%s" from %s') %
- (hexkeyid, to_unicode(userid),
+ (info['hexkeyid'],
+ to_unicode(info['userid']),
keyurl.replace("file://","")))
rc = False
if self.conf.assumeyes:
rc = True
elif fullaskcb:
- rc = fullaskcb({"po": po, "userid": userid,
- "hexkeyid": hexkeyid, "keyurl": keyurl,
- "fingerprint": fingerprint, "timestamp": timestamp})
+ rc = fullaskcb({"po": po, "userid": info['userid'],
+ "hexkeyid": info['hexkeyid'],
+ "keyurl": keyurl,
+ "fingerprint": info['fingerprint'],
+ "timestamp": info['timestamp']})
elif askcb:
- rc = askcb(po, userid, hexkeyid)
+ rc = askcb(po, info['userid'], info['hexkeyid'])
if not rc:
raise Errors.YumBaseError, _("Not installing key")
# Import the key
- result = ts.pgpImportPubkey(misc.procgpgkey(raw_key))
+ result = ts.pgpImportPubkey(misc.procgpgkey(info['raw_key']))
if result != 0:
raise Errors.YumBaseError, \
_('Key import failed (code %d)') % result
- misc.import_key_to_pubring(rawkey, po.repo.cachedir)
-
self.logger.info(_('Key imported successfully'))
key_installed = True
@@ -2801,6 +2813,63 @@ class YumBase(depsolve.Depsolve):
if result != 0:
self.logger.info(_("Import of key(s) didn't help, wrong key(s)?"))
raise Errors.YumBaseError, errmsg
+
+ def getKeyForRepo(self, repo, callback=None):
+ """
+ Retrieve a key for a repository If needed, prompt for if the key should
+ be imported using callback
+
+ @param repo: Repository object to retrieve the key of.
+ @param callback: Callback function to use for asking for verification
+ of a key. Takes a dictionary of key info.
+ """
+ keyurls = repo.gpgkey
+ key_installed = False
+ if not callback:
+ callback = self._confirmGpgKeyImport
+
+ for keyurl in keyurls:
+ keys = self._retrievePublicKey(keyurl)
+ for info in keys:
+ # Check if key is already installed
+ if info['keyid'] in misc.return_keyids_from_pubring(repo.gpgdir):
+ self.logger.info(_('GPG key at %s (0x%s) is already imported') % (
+ keyurl, info['hexkeyid']))
+ continue
+
+ # Try installing/updating GPG key
+ self.logger.critical(_('Importing GPG key 0x%s "%s" from %s') %
+ (info['hexkeyid'],
+ to_unicode(info['userid']),
+ keyurl.replace("file://","")))
+ rc = False
+ if self.conf.assumeyes:
+ rc = True
+ elif callback:
+ rc = callback({"repo": repo, "userid": info['userid'],
+ "hexkeyid": info['hexkeyid'], "keyurl": keyurl,
+ "fingerprint": info['fingerprint'],
+ "timestamp": info['timestamp']})
+
+
+ if not rc:
+ raise Errors.YumBaseError, _("Not installing key for repo %s") % repo
+
+ # Import the key
+ result = misc.import_key_to_pubring(info['raw_key'], info['hexkeyid'], gpgdir=repo.gpgdir)
+ if not result:
+ raise Errors.YumBaseError, _('Key import failed')
+ self.logger.info(_('Key imported successfully'))
+ key_installed = True
+
+ if not key_installed:
+ raise Errors.YumBaseError, \
+ _('The GPG keys listed for the "%s" repository are ' \
+ 'already installed but they are not correct for this ' \
+ 'package.\n' \
+ 'Check that the correct key URLs are configured for ' \
+ 'this repository.') % (repo.name)
+
def _limit_installonly_pkgs(self):
if self.conf.installonly_limit < 1 :
@@ -2912,7 +2981,7 @@ class YumBase(depsolve.Depsolve):
This need to be overloaded in a subclass to make GPG Key import work
'''
return False
-
+
def _doTestTransaction(self,callback,display=None):
''' Do the RPM test transaction '''
# This can be overloaded by a subclass.
diff --git a/yum/misc.py b/yum/misc.py
index 3224a8e..1a3f33d 100644
--- a/yum/misc.py
+++ b/yum/misc.py
@@ -18,6 +18,7 @@ import bz2
from stat import *
try:
import gpgme
+ import gpgme.editutil
except ImportError:
gpgme = None
try:
@@ -339,30 +340,37 @@ def keyInstalled(ts, keyid, timestamp):
return -1
-def import_key_to_pubring(rawkey, repo_cachedir):
+def import_key_to_pubring(rawkey, keyid, cachedir=None, gpgdir=None):
+ # FIXME - cachedir can be removed from this method when we break api
if gpgme is None:
return False
-
- gpgdir = '%s/gpgdir' % repo_cachedir
+
+ if not gpgdir:
+ gpgdir = '%s/gpgdir' % cachedir
+
if not os.path.exists(gpgdir):
os.makedirs(gpgdir)
key_fo = StringIO(rawkey)
- ctx = gpgme.Context()
os.environ['GNUPGHOME'] = gpgdir
+ # import the key
+ ctx = gpgme.Context()
fp = open(os.path.join(gpgdir, 'gpg.conf'), 'wb')
fp.write('')
fp.close()
ctx.import_(key_fo)
key_fo.close()
+ # ultimately trust the key or pygpgme is definitionally stupid
+ k = ctx.get_key(keyid)
+ gpgme.editutil.edit_trust(ctx, k, gpgme.VALIDITY_ULTIMATE)
return True
def return_keyids_from_pubring(gpgdir):
if gpgme is None or not os.path.exists(gpgdir):
return []
-
- ctx = gpgme.Context()
+
os.environ['GNUPGHOME'] = gpgdir
+ ctx = gpgme.Context()
keyids = []
for k in ctx.keylist():
for subkey in k.subkeys:
@@ -370,7 +378,34 @@ def return_keyids_from_pubring(gpgdir):
keyids.append(subkey.keyid)
return keyids
-
+
+def valid_detached_sig(sig_file, signed_file, gpghome=None):
+ """takes signature , file that was signed and an optional gpghomedir"""
+
+ if gpghome and os.path.exists(gpghome):
+ os.environ['GNUPGHOME'] = gpghome
+
+ sig = open(sig_file, 'r')
+ signed_text = open(signed_file, 'r')
+ plaintext = None
+ ctx = gpgme.Context()
+
+ try:
+ sigs = ctx.verify(sig, signed_text, plaintext)
+ except gpgme.GpgmeError, e:
+ return False
+ else:
+ thissig = sigs[0] # is there ever a case where we care about a sig beyond the first one?
+ if thissig:
+ if thissig.validity in (gpgme.VALIDITY_FULL,
+ gpgme.VALIDITY_MARGINAL,
+ gpgme.VALIDITY_ULTIMATE):
+ return True
+ else:
+ return False
+
+ return False
+
def getCacheDir(tmpdir='/var/tmp'):
"""return a path to a valid and safe cachedir - only used when not running
as root or when --tempcache is set"""
diff --git a/yum/repos.py b/yum/repos.py
index 9c0960b..d290520 100644
--- a/yum/repos.py
+++ b/yum/repos.py
@@ -40,10 +40,14 @@ class RepoStorage:
self._setup = False
self.ayum = weakref(ayum)
+ # callbacks for handling gpg key imports for repomd.xml sig checks
+ # need to be set from outside of the repos object to do anything
+ # even quasi-useful
+ self.gpg_import_func = self.ayum.getKeyForRepo # defaults to what is probably sane-ish
+ self.confirm_func = None
def doSetup(self, thisrepo = None):
-
self.ayum.plugins.run('prereposetup')
if thisrepo is None:
@@ -56,10 +60,10 @@ class RepoStorage:
num = 1
for repo in repos:
- repo.setup(self.ayum.conf.cache, self.ayum.mediagrabber)
+ repo.setup(self.ayum.conf.cache, self.ayum.mediagrabber,
+ gpg_import_func = self.gpg_import_func, confirm_func=self.confirm_func)
num += 1
-
if self.callback and len(repos) > 0:
self.callback.progressbar(num, len(repos), repo.id)
diff --git a/yum/yumRepo.py b/yum/yumRepo.py
index 7723534..9bab1b4 100644
--- a/yum/yumRepo.py
+++ b/yum/yumRepo.py
@@ -251,6 +251,10 @@ class YumRepository(Repository, config.RepoConf):
# callback function for handling media
self.mediafunc = None
+ # callbacks for gpg key importing and confirmation
+ self.gpg_import_func = None
+ self.confirm_func = None
+
self._sack = None
self._grabfunc = None
@@ -474,6 +478,7 @@ class YumRepository(Repository, config.RepoConf):
self.setAttribute('cachedir', cachedir)
self.setAttribute('pkgdir', pkgdir)
self.setAttribute('hdrdir', hdrdir)
+ self.setAttribute('gpgdir', self.cachedir + '/gpgdir')
cookie = self.cachedir + '/' + self.metadata_cookie_fn
self.setAttribute('metadata_cookie', cookie)
@@ -751,10 +756,12 @@ class YumRepository(Repository, config.RepoConf):
del fo
- def setup(self, cache, mediafunc = None):
+ def setup(self, cache, mediafunc = None, gpg_import_func=None, confirm_func=None):
try:
self.cache = cache
self.mediafunc = mediafunc
+ self.gpg_import_func = gpg_import_func
+ self.confirm_func = confirm_func
self.dirSetup()
except Errors.RepoError, e:
raise
@@ -789,6 +796,8 @@ class YumRepository(Repository, config.RepoConf):
if grab_can_fail:
return None
raise Errors.RepoError, 'Error downloading file %s: %s' % (local, e)
+
+
return result
def _parseRepoXML(self, local, parse_can_fail=None):
@@ -1098,7 +1107,33 @@ class YumRepository(Repository, config.RepoConf):
filepath = fo.filename
else:
filepath = fo
+
+ if self.gpgcheck in ('repo'): # or whatever FIXME
+ sigfile = self.cachedir + '/repomd.xml.asc'
+ try:
+ result = self._getFile(relative='repodata/repomd.xml.asc',
+ copy_local=1,
+ local = sigfile,
+ text='%s repo signature' % self.id,
+ reget=None,
+ checkfunc=None,
+ cache=self.http_caching == 'all')
+ except URLGrabError, e:
+ raise URLGrabError(-1, 'Error finding signature for repomd.xml for %s: %s' % (self, e))
+
+ if not os.path.exists(self.gpgdir):
+ if self.gpg_import_func:
+ #FIXME probably should have an else off of this to
+ # complain if there is no import function
+ self.gpg_import_func(self, self.confirm_func)
+ # FIXME if we get the okay here to import the key then
+ # we should set an option so that future key imports for this
+ # repo will be allowed w/o question
+
+ if not misc.valid_detached_sig(result, filepath, self.gpgdir):
+ raise URLGrabError(-1, 'repomd.xml signature could not be verified for %s' % (self))
+
try:
repoMDObject.RepoMD(self.id, filepath)
except Errors.RepoMDError, e:
More information about the Yum-cvs-commits
mailing list