[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