- implement signature and checksum checker in
authorDuncan Mac-Vicar P <dmacvicar@suse.de>
Mon, 21 May 2007 11:34:12 +0000 (11:34 +0000)
committerDuncan Mac-Vicar P <dmacvicar@suse.de>
Mon, 21 May 2007 11:34:12 +0000 (11:34 +0000)
  the fetcher layer

  also support for any kind of check mechanism.
  See fetcher documentation.

zypp/Fetcher.cc
zypp/Fetcher.h
zypp/MediaSetAccess.cc
zypp/MediaSetAccess.h
zypp/source/yum/YUMDownloader.cc

index bc34c77..6e27232 100644 (file)
@@ -14,8 +14,9 @@
 
 #include "zypp/base/Logger.h"
 #include "zypp/base/DefaultIntegral.h"
+#include "zypp/ZYppFactory.h"
 #include "zypp/Fetcher.h"
-
+#include "zypp/KeyRing.h"
 
 using namespace std;
 
@@ -23,6 +24,97 @@ using namespace std;
 namespace zypp
 { /////////////////////////////////////////////////////////////////
 
+  Fetcher::ChecksumFileChecker::ChecksumFileChecker( const CheckSum &checksum )
+    : _checksum(checksum)
+  {
+  }
+
+  bool Fetcher::ChecksumFileChecker::operator()( const Pathname &file )
+  {
+    callback::SendReport<DigestReport> report;
+    CheckSum real_checksum( _checksum.type(), filesystem::checksum( file, _checksum.type() ));
+    
+    if ( _checksum.empty() )
+    {
+      MIL << "File " <<  file << " has no checksum available." << std::endl;
+      if ( report->askUserToAcceptNoDigest(file) )
+      {
+        MIL << "User accepted " <<  file << " with no checksum." << std::endl;
+        return true;
+      }
+      else
+      {
+        return false;
+      }
+    }
+    else
+    {
+      if ( (real_checksum == _checksum) )
+      {
+        if ( report->askUserToAcceptWrongDigest( file, _checksum.checksum(), real_checksum.checksum() ) )
+        {
+          WAR << "User accepted " <<  file << " with WRONG CHECKSUM." << std::endl;
+          return true;
+        }
+        else
+        {
+          return false;
+        }
+      }
+      else
+      {
+        return true;
+      }
+    }
+  }
+
+  bool Fetcher::NullFileChecker::operator()(const Pathname &file )
+  {
+    return true;
+  }
+
+  bool Fetcher::CompositeFileChecker::operator()(const Pathname &file )
+  {
+    bool result = true;
+    for ( list<Fetcher::FileChecker>::iterator it = _checkers.begin(); it != _checkers.end(); ++it )
+    {
+      result = result && (*it)(file);
+    }
+    return result;
+  }
+  
+  void Fetcher::CompositeFileChecker::add( const FileChecker &checker )
+  {
+    _checkers.push_back(checker);
+  }
+
+  Fetcher::SignatureFileChecker::SignatureFileChecker( const Pathname &signature )
+    : _signature(signature)
+  {
+  }
+  
+  Fetcher::SignatureFileChecker::SignatureFileChecker()
+  {
+  }
+  
+  void Fetcher::SignatureFileChecker::addPublicKey( const Pathname &publickey )
+  {
+    ZYpp::Ptr z = getZYpp();
+    z->keyRing()->importKey(publickey, false);
+  }
+  
+  bool Fetcher::SignatureFileChecker::operator()(const Pathname &file )
+  {
+    ZYpp::Ptr z = getZYpp();
+    MIL << "checking " << file << " file vailidity using digital signature.." << endl;
+    bool valid = z->keyRing()->verifyFileSignatureWorkflow( file, string(), _signature);
+    return valid;
+  }
+  
+  /**
+   * Class to encapsulate the \ref OnMediaLocation object
+   * and the \ref Fetcher::FileChcker together
+   */
   struct FetcherJob
   {
     FetcherJob( const OnMediaLocation &loc )
@@ -32,6 +124,7 @@ namespace zypp
     }
 
     OnMediaLocation location;
+    Fetcher::CompositeFileChecker checkers;
   };
 
   ///////////////////////////////////////////////////////////////////
@@ -44,7 +137,8 @@ namespace zypp
 
   public:
 
-    void enqueue( const OnMediaLocation &resource );    
+    void enqueue( const OnMediaLocation &resource, const Fetcher::FileChecker &checker  );
+    void enqueueDigested( const OnMediaLocation &resource, const Fetcher::FileChecker &checker );
     void addCachePath( const Pathname &cache_dir );
     void reset();
     void start( const Pathname &dest_dir, MediaSetAccess &media );
@@ -68,11 +162,21 @@ namespace zypp
   ///////////////////////////////////////////////////////////////////
 
 
-  void Fetcher::Impl::enqueue( const OnMediaLocation &resource )
+  void Fetcher::Impl::enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker )
   {
-    _resources.push_back(FetcherJob(resource));
+    CompositeFileChecker composite;
+    composite.add(ChecksumFileChecker(resource.checksum()));
+    composite.add(checker);
+    enqueue(resource, composite);
   }
   
+  void Fetcher::Impl::enqueue( const OnMediaLocation &resource, const FileChecker &checker )
+  {
+    FetcherJob job(resource);
+    job.checkers.add(checker);
+    _resources.push_back(resource);
+  }
+
   void Fetcher::Impl::reset()
   {
     _resources.clear();
@@ -197,9 +301,14 @@ namespace zypp
   Fetcher::~Fetcher()
   {}
 
-  void Fetcher::enqueue( const OnMediaLocation &resource )
+  void Fetcher::enqueueDigested( const OnMediaLocation &resource, const Fetcher::FileChecker &checker )
   {
-    _pimpl->enqueue(resource);
+    _pimpl->enqueue(resource, checker);
+  }
+  
+  void Fetcher::enqueue( const OnMediaLocation &resource, const Fetcher::FileChecker &checker  )
+  {
+    _pimpl->enqueue(resource, checker);
   }
   
   void Fetcher::addCachePath( const Pathname &cache_dir )
@@ -232,23 +341,3 @@ namespace zypp
 } // namespace zypp
 ///////////////////////////////////////////////////////////////////
 
-//       callback::SendReport<DigestReport> report;
-//       if ( checksum.empty() )
-//       {
-//         MIL << "File " <<  file_url << " has no checksum available." << std::endl;
-//         if ( report->askUserToAcceptNoDigest(file_to_download) )
-//         {
-//           MIL << "User accepted " <<  file_url << " with no checksum." << std::endl;
-//           return;
-//         }
-//         else
-//         {
-//           ZYPP_THROW(SourceMetadataException( file_url.asString() + " " + N_(" miss checksum.") ));
-//         }
-//       }
-//       else
-//       {
-//         if (! is_checksum( destination, checksum))
-//           ZYPP_THROW(SourceMetadataException( file_url.asString() + " " + N_(" fails checksum verification.") ));
-//       }
-
index 7a8ada9..2fbe0d9 100644 (file)
 #define ZYPP_FETCHER_H
 
 #include <iosfwd>
+#include <list>
 
 #include "zypp/base/PtrTypes.h"
 #include "zypp/Pathname.h"
 #include "zypp/Url.h"
 #include "zypp/OnMediaLocation.h"
+#include "zypp/Digest.h"
 #include "zypp/MediaSetAccess.h"
 
 ///////////////////////////////////////////////////////////////////
@@ -25,8 +27,14 @@ namespace zypp
 { /////////////////////////////////////////////////////////////////
 
   /**
-  * This class allows to retrieve a group of files which can
-  * be cached already on the local disk.
+  * This class allows to retrieve a group of files in a confortable
+  * way, providing some smartness that does not belong to the
+  * media layer like:
+  *
+  * \li Configurable local caches to retrieve already
+  *     donwloaded files.
+  * \li File checkers that can check for right checksums
+  *     digital signatures, etc.
   *
   * \code
   * MediaSetAccess access(url, path);
@@ -36,6 +44,20 @@ namespace zypp
   * fetcher.start( "/download-dir, access );
   * fetcher.reset();
   * \endcode
+  *
+  * To use the checkers. just create a functor implementing
+  * bool operator()(const Pathname &file) \see FileChecker.
+  * Pass the necessary validation data in the constructor
+  * of the functor, and pass the object to the \ref enqueue
+  * method.
+  *
+  * \code
+  * ChecksumFileChecker checker(CheckSum("sha1", "....");
+  * fetcher.enqueue( location, checker);
+  * \endcode
+  *
+  * If you need to use more than one checker
+  * \see CompositeFileChecker
   */
   class Fetcher
   {
@@ -45,6 +67,105 @@ namespace zypp
     /** Implementation  */
     class Impl;
 
+  /**
+   * Functor signature used to check files.
+   * \param file File to check.
+   */
+  typedef boost::function<bool ( const Pathname &file )> FileChecker;
+  
+  /**
+   * Built in file checkers
+   */
+  
+  /**
+   * \short Checks for a valid checksum and interacts with the user.
+   */
+   class ChecksumFileChecker
+   {
+   public:
+     /**
+      * Constructor.
+      * \param checksum Checksum that validates the file
+      */
+     ChecksumFileChecker( const CheckSum &checksum );
+     /**
+      * \short Try to validate the file
+      * \param file File to validate.
+      */
+     bool operator()( const Pathname &file );
+     
+   private:
+     CheckSum _checksum;
+   };
+   
+   /**
+    * \short Checks for the validity of a signature
+    */
+   class SignatureFileChecker
+   {
+     public:
+      /**
+      * Constructor.
+      * \param signature Signature that validates the file
+      */
+      SignatureFileChecker( const Pathname &signature );
+      
+      /**
+      * Default Constructor.
+      * \short Signature for unsigned files
+      * Use it when you dont have a signature but you want
+      * to check the user to accept an unsigned file.
+      */
+      SignatureFileChecker();
+      
+      
+      /**
+       * add a public key to the list of known keys
+       */
+      void addPublicKey( const Pathname &publickey );
+      /**
+      * \short Try to validate the file
+      * \param file File to validate.
+      */
+      bool operator()( const Pathname &file );
+     
+     private:
+      Pathname _signature;
+   };
+   
+   /**
+   * \short Checks for nothing
+   * Used as the default checker
+   */
+   class NullFileChecker
+   {
+   public:
+     bool operator()( const Pathname &file );
+   };
+    
+   /**
+    * \short Checker composed of more checkers.
+    * 
+    * Allows to create a checker composed of various
+    * checkers altothether. It will only
+    * validate if all the checkers validate.
+    *
+    * \code
+    * CompositeFileChecker com;
+    * com.add(checker1);
+    * com.add(checker2);
+    * fetcher.enqueue(location, com);
+    * \endcode
+    */
+   class CompositeFileChecker
+   {
+   public:
+     void add( const FileChecker &checker );
+     bool operator()( const Pathname &file );
+   private:
+     std::list<FileChecker> _checkers;
+   };
+   
   public:
     /** Default ctor */
     Fetcher();
@@ -55,8 +176,23 @@ namespace zypp
    /**
     * Enqueue a object for transferal, they will not
     * be transfered until \ref start() is called
+    *
+    */
+    void enqueue( const OnMediaLocation &resource, const FileChecker &checker = NullFileChecker() );
+    
+    /**
+    * Enqueue a object for transferal, they will not
+    * be transfered until \ref start() is called
+    *
+    * \note As \ref OnMediaLocation contains the digest information,
+    * a \ref ChecksumFileChecker is automatically added to the
+    * transfer job, so make sure you don't add another one or
+    * the user could be asked twice.
+    *
+    * \todo FIXME implement checker == operator to avoid this.
     */
-    void enqueue( const OnMediaLocation &resource );
+    void enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker = NullFileChecker() );
+    
     /**
     * adds a directory to the list of directories
     * where to look for cached files
index e102963..d478b75 100644 (file)
@@ -23,32 +23,6 @@ namespace zypp
 { /////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////
 
-  ChecksumFileChecker::ChecksumFileChecker( const CheckSum &checksum )
-    : _checksum(checksum)
-  {
-  }
-
-  bool ChecksumFileChecker::operator()( const Pathname &file )
-  {
-    // FIXME probably this funcionality should be in CheckSum itself
-    CheckSum real_checksum( _checksum.type(), filesystem::checksum( file, _checksum.type() ));
-    if ( real_checksum == _checksum )
-    {
-      return true;
-    }
-    else
-    {
-      ERR << "Got " << real_checksum << ", expected " << _checksum << std::endl;
-      return false;
-    }
-  }
-
-  bool NullFileChecker::operator()(const Pathname &file )
-  {
-    return true;
-  }
-
-
   MediaSetAccess::MediaSetAccess(  const Url &url, const Pathname &path )
       : _url(url),
         _path(path)
@@ -212,17 +186,6 @@ namespace zypp
     return exists;
   }
 
-  Pathname  MediaSetAccess::provideFile(const Pathname & file, unsigned media_nr, FileChecker checker )
-  {
-    Pathname p = provideFileInternal( file, media_nr, false, false);
-    
-    if ( ! checker(p) )
-    {
-      ZYPP_THROW(Exception("Error checker"));
-    }
-    return p;
-  }
-
   Pathname MediaSetAccess::provideFileInternal(const Pathname & file, unsigned media_nr, bool cached, bool checkonly )
   {
     callback::SendReport<media::MediaChangeReport> report;
index 9f880d2..ae21b41 100644 (file)
@@ -29,23 +29,6 @@ namespace zypp
 
     DEFINE_PTR_TYPE(MediaSetAccess);
 
-    typedef boost::function<bool ( const Pathname &file )> FileChecker;
-
-    class NullFileChecker
-    {
-      public:
-      bool operator()( const Pathname &file );
-    };
-
-    class ChecksumFileChecker
-    {
-      public:
-      ChecksumFileChecker( const CheckSum &checksum );
-      bool operator()( const Pathname &file );
-      private:
-      CheckSum _checksum;
-    };
-
     ///////////////////////////////////////////////////////////////////
     //
     // CLASS NAME : MediaSetAccess
@@ -120,20 +103,12 @@ namespace zypp
       Pathname provideFile(const Pathname & file, unsigned media_nr = 1 );
 
       /**
-       * Provides \a file from media \a media_nr and use \a checker on provided
-       * file.
-       * 
-       * \throws Exception if \a checker returns false. TODO use ProvideFilePolicy
-       */
-      Pathname provideFile(const Pathname & file, unsigned media_nr, const FileChecker checker );
-
-      /**
        * check if a file exists on the specified media
        *
        * \param file file to check
        * \param media_nr Media number
        */
-      bool doesFileExist(const Pathname & file, unsigned media_nr );
+      bool doesFileExist(const Pathname & file, unsigned media_nr = 1 );
 
       /**
        * Replaces media number in specified url with given \a medianr.
index 5a79351..d4ec95f 100644 (file)
@@ -37,7 +37,7 @@ YUMDownloader::YUMDownloader( const Url &url, const Pathname &path )
 bool YUMDownloader::patches_Callback( const OnMediaLocation &loc, const string &id )
 {
   MIL << id << " : " << loc << endl;
-  _fetcher.enqueue(loc);
+  _fetcher.enqueueDigested(loc);
   return true;
 }
 
@@ -45,7 +45,7 @@ bool YUMDownloader::patches_Callback( const OnMediaLocation &loc, const string &
 bool YUMDownloader::repomd_Callback( const OnMediaLocation &loc, const YUMResourceType &dtype )
 {
   MIL << dtype << " : " << loc << endl;
-  _fetcher.enqueue(loc);
+  _fetcher.enqueueDigested(loc);
   
   // We got a patches file we need to read, to add patches listed
   // there, so we transfer what we have in the queue, and 
@@ -61,12 +61,32 @@ bool YUMDownloader::repomd_Callback( const OnMediaLocation &loc, const YUMResour
 
 void YUMDownloader::download( const Pathname &dest_dir )
 {
+  Pathname repomdpath =  "/repodata/repomd.xml";
+  Pathname keypath =  "/repodata/repomd.xml.key";
+  Pathname sigpath =  "/repodata/repomd.xml.asc";
+  
+  
   _dest_dir = dest_dir;
-  _fetcher.enqueue( OnMediaLocation().filename("/repodata/repomd.xml") );
-  _fetcher.start( dest_dir, _media);
-
-  //if ( _media.doesFile
+  if ( _media.doesFileExist(keypath) )
+    _fetcher.enqueue( OnMediaLocation().filename(keypath) );
 
+  if ( _media.doesFileExist(sigpath) )
+     _fetcher.enqueue( OnMediaLocation().filename(sigpath) );
+  
+  _fetcher.start( dest_dir, _media );
+  
+  Fetcher::SignatureFileChecker sigchecker;
+  
+  if ( PathInfo( dest_dir + sigpath ).isExist() )
+    sigchecker = Fetcher::SignatureFileChecker(dest_dir + sigpath);
+  
+  if ( PathInfo( dest_dir + keypath ).isExist() )
+    sigchecker.addPublicKey(dest_dir + keypath );
+  
+  _fetcher.enqueue( OnMediaLocation().filename(repomdpath), sigchecker );
+  _fetcher.start( dest_dir, _media);
+  
+  
   _fetcher.reset();
 
   Reader reader( dest_dir + "/repodata/repomd.xml" );
@@ -81,3 +101,4 @@ void YUMDownloader::download( const Pathname &dest_dir )
 } // ns zypp
 
 
+