Imported Upstream version 15.2.0 61/94661/1
authorDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 1 Nov 2016 01:43:13 +0000 (10:43 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 1 Nov 2016 01:43:14 +0000 (10:43 +0900)
Change-Id: Ibb673e635f3537b7ff5d8ad4865efb2df38f4f22
Signed-off-by: DongHun Kwak <dh0128.kwak@samsung.com>
27 files changed:
VERSION.cmake
package/libzypp.changes
tests/zypp/CMakeLists.txt
tests/zypp/UserData_test.cc [new file with mode: 0644]
zypp.conf
zypp/Callback.h
zypp/ContentType.h
zypp/Fetcher.cc
zypp/FileChecker.cc
zypp/FileChecker.h
zypp/KeyRing.cc
zypp/KeyRing.h
zypp/RepoInfo.cc
zypp/RepoInfo.h
zypp/Resolvable.h
zypp/TriBool.h
zypp/UserData.h
zypp/ZConfig.cc
zypp/ZConfig.h
zypp/ZYppCallbacks.h
zypp/base/String.cc
zypp/base/String.h
zypp/parser/RepoFileReader.cc
zypp/repo/Downloader.cc
zypp/repo/PackageProvider.cc
zypp/target/rpm/RpmDb.cc
zypp/target/rpm/RpmDb.h

index 083325c..7872ad4 100644 (file)
 #   See './mkChangelog -h' for help.
 #
 SET(LIBZYPP_MAJOR "15")
-SET(LIBZYPP_COMPATMINOR "1")
-SET(LIBZYPP_MINOR "1")
-SET(LIBZYPP_PATCH "3")
+SET(LIBZYPP_COMPATMINOR "2")
+SET(LIBZYPP_MINOR "2")
+SET(LIBZYPP_PATCH "0")
 #
-# LAST RELEASED: 15.1.3 (1)
+# LAST RELEASED: 15.2.0 (2)
 # (The number in parenthesis is LIBZYPP_COMPATMINOR)
 #=======
index dfe542d..9495785 100644 (file)
@@ -1,4 +1,18 @@
 -------------------------------------------------------------------
+Mon May 18 10:46:10 CEST 2015 - ma@suse.de
+
+- zypp.conf: Add config values for gpgcheck, repo_gpgcheck 
+  and pkg_gpgcheck. The default behavior 'gpgcheck=On' will
+  automatically turn on the gpg signature check for packages 
+  downloaded from repository with unsigned metadata. If the
+  repo metadata are signed, a faster comparison via checksums
+  is done. By explicitly setting repo_gpgcheck or pkg_gpgcheck
+  you can enforce the signature check of repository metadata
+  or downloaded packages to be always performed. Those defaults 
+  can be overwritten per repository. (FATE#314603) 
+- version 15.2.0 (2)
+
+-------------------------------------------------------------------
 Wed May 13 14:04:04 CEST 2015 - ma@suse.de
 
 - Write solv.idx to speed up bash tab completion (bnc#928650)
index acafee6..a2d5d9c 100644 (file)
@@ -41,6 +41,7 @@ ADD_TESTS(
   StrMatcher
   Target
   Url
+  UserData
   Vendor
   Vendor2
 )
diff --git a/tests/zypp/UserData_test.cc b/tests/zypp/UserData_test.cc
new file mode 100644 (file)
index 0000000..41ab3bb
--- /dev/null
@@ -0,0 +1,87 @@
+#include <boost/test/auto_unit_test.hpp>
+#include <iostream>
+#include <set>
+#include "zypp/UserData.h"
+
+using std::cout;
+using std::endl;
+
+using zypp::callback::UserData;
+const std::string key( "key" );
+
+#define checkIsEmpty(v)        \
+  BOOST_CHECK( !v );   \
+  BOOST_CHECK( v.empty() );    \
+  BOOST_CHECK_EQUAL( v.size(), 0 );    \
+  BOOST_CHECK_EQUAL( v.haskey( key ), false ); \
+  BOOST_CHECK_EQUAL( v.hasvalue( key ), false );       \
+  BOOST_CHECK_EQUAL( v.getvalue( key ).empty(), true );
+
+#define checkIsNotEmpty(v,s)   \
+  BOOST_CHECK( v );    \
+  BOOST_CHECK( !v.empty() );   \
+  if ( s )     \
+  { BOOST_CHECK_EQUAL( v.size(), s ); }        \
+  else \
+  { BOOST_CHECK( v.size() ); } \
+  BOOST_CHECK_EQUAL( v.haskey( key ), true );
+
+
+BOOST_AUTO_TEST_CASE(useruata_default)
+{
+  UserData v;
+  checkIsEmpty( v );
+
+  // set key with empty value
+  v.reset( key );
+  checkIsNotEmpty( v, 1 );
+  BOOST_CHECK_EQUAL( v.hasvalue( key ), false );
+  BOOST_CHECK_EQUAL( v.getvalue( key ).empty(), true );
+
+  std::string rs;
+  unsigned    ru = 0;
+  int         ri = 0;
+  char        rc = 0;
+
+  // set key with value
+  v.set( key, 42 );
+  BOOST_CHECK_EQUAL( v.hasvalue( key ), true );
+  BOOST_CHECK_EQUAL( v.getvalue( key ).empty(), false );
+
+  // get back data
+  BOOST_CHECK_EQUAL( v.get( key, rs ), false );
+  BOOST_CHECK_EQUAL( v.get( key, ru ), false );
+  BOOST_CHECK_EQUAL( v.get( key, ri ), true );
+  BOOST_CHECK_EQUAL( v.get( key, rc ), false );
+  BOOST_CHECK_EQUAL( ru, 0 );
+  BOOST_CHECK_EQUAL( ri, 42 );
+  BOOST_CHECK_EQUAL( rc, 0 );
+
+  v.set( key, 43U );
+  BOOST_CHECK_EQUAL( v.get( key, rs ), false );
+  BOOST_CHECK_EQUAL( v.get( key, ru ), true );
+  BOOST_CHECK_EQUAL( v.get( key, ri ), false );
+  BOOST_CHECK_EQUAL( v.get( key, rc ), false );
+  BOOST_CHECK_EQUAL( ru, 43 );
+  BOOST_CHECK_EQUAL( ri, 42 );
+  BOOST_CHECK_EQUAL( rc, 0 );
+
+  // set key with empty value
+  v.reset( key );
+  BOOST_CHECK_EQUAL( v.hasvalue( key ), false );
+  BOOST_CHECK_EQUAL( v.getvalue( key ).empty(), true );
+  checkIsNotEmpty( v, 1 );
+
+  // erase key
+  v.erase( key );
+  BOOST_CHECK_EQUAL( v.hasvalue( key ), false );
+  BOOST_CHECK_EQUAL( v.getvalue( key ).empty(), true );
+  checkIsEmpty( v );
+
+  // const may add but not manip non-empty values
+  const UserData & cv( v );
+  BOOST_CHECK_EQUAL( cv.reset( key ), true );  // add new key: ok
+  BOOST_CHECK_EQUAL( cv.set( key, 42 ), true );        // empty -> non-empty: ok
+  BOOST_CHECK_EQUAL( cv.set( key, 43 ), false );// change non-empty: not ok
+  BOOST_CHECK_EQUAL( cv.reset( key ), false ); // change non-empty: not ok
+}
index dfe7f64..b23b8a5 100644 (file)
--- a/zypp.conf
+++ b/zypp.conf
 ## download.media_preference = download
 
 ##
+## Signature checking (repodata and rpm packages)
+##
+##   boolean   gpgcheck        (default: on)
+##   boolean   repo_gpgcheck   (default: unset -> according to gpgcheck)
+##   boolean   pkg_gpgcheck    (default: unset -> according to gpgcheck)
+##
+## If 'gpgcheck' is 'on' we will either check the signature of repo metadata
+## (packages are secured via checksum in the metadata), or the signature of
+## an rpm package to install if it's repo metadata are not signed or not
+## checked.
+##
+## The default behavior can be altered by explicitly setting 'repo_gpgcheck' and/or
+## 'pkg_gpgcheck' to perform those checks always (if 'on') or never (if 'off').
+##
+## Explicitly setting 'gpgcheck', 'repo_gpgcheck' 'pkg_gpgcheck' in a
+## repositories .repo file will overwrite the defaults here.
+##
+##   DISABLING GPG CHECKS IS NOT RECOMMENDED.
+##   Signing data enables the recipient to verify that no modifications
+##   occurred after the data were signed. Accepting data with no, wrong
+##   or unknown signature can lead to a corrupted system and in extreme
+##   cases even to a system compromise.
+##
+# repo_gpgcheck = unset -> according to gpgcheck
+# pkg_gpgcheck =  unset -> according to gpgcheck
+
+##
 ## Commit download policy to use as default.
 ##
 ##  DownloadOnly,      Just download all packages to the local cache.
index 54cf219..3956a0b 100644 (file)
@@ -13,6 +13,7 @@
 #define ZYPP_CALLBACK_H
 
 #include "zypp/base/NonCopyable.h"
+#include "zypp/UserData.h"
 
 ///////////////////////////////////////////////////////////////////
 namespace zypp
@@ -136,6 +137,7 @@ namespace zypp
     /**  */
     struct ReportBase
     {
+      typedef callback::UserData UserData;
       virtual ~ReportBase()
       {}
     };
@@ -149,6 +151,7 @@ namespace zypp
       struct ReceiveReport : public _Report
       {
        typedef _Report                   ReportType;
+       typedef typename ReportType::UserData UserData;
        typedef ReceiveReport<_Report>    Receiver;
         typedef DistributeReport<_Report> Distributor;
 
@@ -179,6 +182,7 @@ namespace zypp
       {
        public:
        typedef _Report                   ReportType;
+       typedef typename ReportType::UserData UserData;
        typedef ReceiveReport<_Report>    Receiver;
        typedef DistributeReport<_Report> Distributor;
 
@@ -217,6 +221,7 @@ namespace zypp
       struct SendReport : private zypp::base::NonCopyable
       {
        typedef _Report                   ReportType;
+       typedef typename ReportType::UserData UserData;
         typedef ReceiveReport<_Report>    Receiver;
         typedef DistributeReport<_Report> Distributor;
 
@@ -265,6 +270,7 @@ namespace zypp
       struct TempConnect
       {
        typedef _Report                   ReportType;
+       typedef typename ReportType::UserData UserData;
         typedef ReceiveReport<_Report>    Receiver;
         typedef DistributeReport<_Report> Distributor;
 
index 4254d06..eaf99c6 100644 (file)
@@ -36,27 +36,24 @@ namespace zypp
     /** Ctor taking <tt>"type[/subtype]"</tt>
      * \throws std::invalid_argument if string is malformed
      */
-    explicit ContentType( const std::string & type_r )
+    explicit ContentType( std::string type_r )
     {
       std::string::size_type pos = type_r.find( "/" );
-      if ( pos == std::string::npos )
+      if ( pos != std::string::npos )
       {
-       testAndSet( _type, type_r );
-      }
-      else
-      {
-       testAndSet( _type,    type_r.substr( 0, pos ) );
        testAndSet( _subtype, type_r.substr( pos+1 ) );
+       type_r.erase( pos );
       }
+      testAndSet( _type, std::move(type_r) );
     }
 
     /** Ctor taking type and subtype
      * \throws std::invalid_argument if string is malformed
      */
-    ContentType( const std::string & type_r, const std::string & subtype_r )
+    ContentType( std::string type_r, std::string subtype_r )
     {
-      testAndSet( _type,    type_r );
-      testAndSet( _subtype, subtype_r );
+      testAndSet( _type,    std::move(type_r) );
+      testAndSet( _subtype, std::move(subtype_r) );
     }
 
   public:
@@ -67,8 +64,8 @@ namespace zypp
     /** Set type
      * \throws std::invalid_argument if string is malformed
      */
-    void type( const std::string & type_r )
-    { _type = type_r; }
+    void type( std::string type_r )
+    { _type = std::move(type_r); }
 
     /**  Get subtype */
     const std::string & subtype() const
@@ -77,8 +74,8 @@ namespace zypp
     /**  Set subtype
      * \throws std::invalid_argument if string is malformed
      */
-    void subtype( const std::string & subtype_r )
-    { _subtype = subtype_r; }
+    void subtype( std::string subtype_r )
+    { _subtype = std::move(subtype_r); }
 
   public:
     /** Whether type and subtype are empty */
@@ -100,11 +97,11 @@ namespace zypp
     { std::string ret( type() ); if ( ! emptySubtype() ) { ret += "/"; ret += subtype(); } return ret; }
 
   private:
-    void testAndSet( std::string & var_r, const std::string & val_r )
+    void testAndSet( std::string & var_r, std::string val_r )
     {
       if ( val_r.find_first_of( "/ \t\r\n" ) != std::string::npos )
        throw std::invalid_argument( "ContentType: illegal char in '" + val_r + "'" );
-      var_r = val_r;
+      var_r = std::move(val_r);
     }
   private:
     std::string        _type;
index 03924d6..ef8e95b 100644 (file)
@@ -801,7 +801,7 @@ namespace zypp
       // if the file was not transferred, and no exception, just
       // return, as it was an optional file
       if ( ! PathInfo(dest_dir + (*it_res)->location.filename()).isExist() )
-          return;
+          continue;
 
       // if the checksum is empty, but the checksum is in one of the
       // indexes checksum, then add a checker
index f6b7abc..1d21137 100644 (file)
 
 using namespace std;
 
+#undef ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "FileChecker"
+
 ///////////////////////////////////////////////////////////////////
 namespace zypp
 { /////////////////////////////////////////////////////////////////
 
   ChecksumFileChecker::ChecksumFileChecker( const CheckSum &checksum )
     : _checksum(checksum)
-  {
-  }
+  {}
 
   void ChecksumFileChecker::operator()( const Pathname &file ) const
   {
-      //MIL << "checking " << file << " file against checksum '" << _checksum << "'" << endl;
+    //MIL << "checking " << file << " file against checksum '" << _checksum << "'" << endl;
     callback::SendReport<DigestReport> report;
 
     if ( _checksum.empty() )
@@ -42,7 +44,7 @@ namespace zypp
       }
       else
       {
-        ZYPP_THROW( FileCheckException( file.basename() + " has no checksum" ) );
+        ZYPP_THROW( ExceptionType( file.basename() + " has no checksum" ) );
       }
     }
     else
@@ -57,7 +59,7 @@ namespace zypp
         }
         else
         {
-          ZYPP_THROW( FileCheckException( file.basename() + " has wrong checksum" ) );
+          ZYPP_THROW( ExceptionType( file.basename() + " has wrong checksum" ) );
         }
       }
     }
@@ -87,23 +89,15 @@ namespace zypp
   }
 
   void CompositeFileChecker::add( const FileChecker &checker )
-  {
-    //MIL << "||# " << _checkers.size() << endl;
-    _checkers.push_back(checker);
-    //MIL << "||* " << _checkers.size() << endl;
+  { _checkers.push_back(checker); }
 
-  }
 
-   SignatureFileChecker::SignatureFileChecker( const Pathname &signature )
+  SignatureFileChecker::SignatureFileChecker( const Pathname & signature )
        : _signature(signature)
-  {
-
-  }
-
+  {}
 
   SignatureFileChecker::SignatureFileChecker()
-  {
-  }
+  {}
 
   void SignatureFileChecker::setKeyContext(const KeyContext & keycontext)
   { _context = keycontext; }
@@ -119,19 +113,18 @@ namespace zypp
 
   void SignatureFileChecker::operator()(const Pathname &file ) const
   {
-    ZYpp::Ptr z = getZYpp();
-
-    if ( (! PathInfo(_signature).isExist()) && (!_signature.empty()))
+    if ( (! PathInfo(_signature).isExist()) && (!_signature.empty()) )
     {
-      ZYPP_THROW(FileCheckException("Signature " + _signature.asString() + " not found."));
+      ZYPP_THROW( ExceptionType("Signature " + _signature.asString() + " not found.") );
     }
 
     MIL << "checking " << file << " file validity using digital signature.." << endl;
-    bool valid = z->keyRing()->verifyFileSignatureWorkflow( file, file.basename(), _signature, _context);
+    _fileValidated = false;
+    _fileAccepted = getZYpp()->keyRing()->verifyFileSignatureWorkflow( file, file.basename(), _signature, _fileValidated, _context );
 
-    if (!valid)
-      ZYPP_THROW( FileCheckException( "Signature verification failed for "  + file.basename() ) );
 }
+    if ( !_fileAccepted )
+      ZYPP_THROW( ExceptionType( "Signature verification failed for "  + file.basename() ) );
+ }
 
   /******************************************************************
   **
index 579a627..d3ddf2d 100644 (file)
@@ -14,6 +14,7 @@
 
 #include <iosfwd>
 #include <list>
+#include "zypp/base/DefaultIntegral.h"
 #include "zypp/base/Exception.h"
 #include "zypp/base/Function.h"
 #include "zypp/PathInfo.h"
@@ -45,12 +46,18 @@ namespace zypp
 
   class CheckSumCheckException : public FileCheckException
   {
-    //TODO
+  public:
+    CheckSumCheckException(const std::string &msg)
+      : FileCheckException(msg)
+    {}
   };
 
   class SignatureCheckException : public FileCheckException
   {
-    //TODO
+  public:
+    SignatureCheckException(const std::string &msg)
+      : FileCheckException(msg)
+    {}
   };
 
   /**
@@ -63,6 +70,7 @@ namespace zypp
    class ChecksumFileChecker
    {
    public:
+     typedef CheckSumCheckException ExceptionType;
      /**
       * Constructor.
       * \param checksum Checksum that validates the file
@@ -85,6 +93,10 @@ namespace zypp
    class SignatureFileChecker
    {
      public:
+       typedef SignatureCheckException ExceptionType;
+       typedef function<void ( const SignatureFileChecker & checker,  const Pathname & file )> OnSigValidated;
+
+     public:
       /**
       * Constructor.
       * \param signature Signature that validates the file
@@ -108,6 +120,24 @@ namespace zypp
        */
       void setKeyContext(const KeyContext & keycontext);
 
+      /** Return the current context */
+      const KeyContext & keyContext() const
+      { return _context; }
+
+      /** Return whether the last file passed to \ref operator() was accepted.
+       * If this is \ref false \ref operator() was not invoked or threw a
+       * \ref SignatureCheckException.
+       */
+      bool fileAccepted() const
+      { return _fileAccepted; }
+
+      /** Return whether the last file passed to \ref operator() was actually sucessfully verified.
+       * If this is \c false but \ref fileAccepted, the file was accepted due to user interaction or
+       * global settings, but the signature was not verified.
+       */
+      bool fileValidated() const
+      { return _fileValidated; }
+
       /**
        * add a public key to the list of known keys
        */
@@ -116,16 +146,24 @@ namespace zypp
       void addPublicKey( const Pathname & publickey, const KeyContext & keycontext = KeyContext());
 
       /**
-      * \short Try to validate the file
-      * \param file File to validate.
-      *
-      * \throws SignatureCheckException if validation fails
-      */
+       * Calls \ref KeyRing::verifyFileSignatureWorkflow to verify the file.
+       *
+       * Keep in mind the the workflow may return \c true (file accepted) due to user interaction
+       * or global defaults even if a signature was not actually sucessfully verified. Whether a
+       * signature was actually sucessfully verified can be determined by checking \ref fileValidated
+       * which is invokes IFF a signature for this file actually validated.
+       *
+       * \param file File to validate.fileValidated
+       *
+       * \throws SignatureCheckException if validation fails
+       */
       void operator()( const Pathname &file ) const;
 
      protected:
       Pathname _signature;
       KeyContext _context;
+      mutable DefaultIntegral<bool,false> _fileAccepted;
+      mutable DefaultIntegral<bool,false> _fileValidated;
    };
 
    /**
index 797f0e7..9c3f39b 100644 (file)
@@ -222,11 +222,7 @@ namespace zypp
     PublicKey exportTrustedPublicKey( const PublicKeyData & keyData )
     { return exportKey( keyData, trustedKeyRing() ); }
 
-    bool verifyFileSignatureWorkflow(
-        const Pathname & file,
-        const std::string & filedesc,
-        const Pathname & signature,
-        const KeyContext & keycontext = KeyContext());
+    bool verifyFileSignatureWorkflow( const Pathname & file, const std::string & filedesc, const Pathname & signature, bool & sigValid_r, const KeyContext & keycontext = KeyContext());
 
     bool verifyFileSignature( const Pathname & file, const Pathname & signature )
     { return verifyFile( file, signature, generalKeyRing() ); }
@@ -380,12 +376,10 @@ namespace zypp
     return tmpFile;
   }
 
-  bool KeyRing::Impl::verifyFileSignatureWorkflow(
-      const Pathname & file,
-      const std::string & filedesc,
-      const Pathname & signature,
-      const KeyContext & context )
+  bool KeyRing::Impl::verifyFileSignatureWorkflow( const Pathname & file, const std::string & filedesc, const Pathname & signature, bool & sigValid_r, const KeyContext & context )
   {
+    sigValid_r = false;        // set true if signature is actually successfully validated!
+
     callback::SendReport<KeyRingReport> report;
     MIL << "Going to verify signature for " << filedesc << " ( " << file << " ) with " << signature << endl;
 
@@ -428,7 +422,9 @@ namespace zypp
 
       // it exists, is trusted, does it validates?
       if ( verifyFile( file, signature, trustedKeyRing() ) )
-        return true;
+      {
+        return (sigValid_r=true);      // signature is actually successfully validated!
+      }
       else
       {
         return report->askUserToAcceptVerificationFailed( filedesc, exportKey( trustedKeyData, trustedKeyRing() ), context );
@@ -465,7 +461,7 @@ namespace zypp
           if ( verifyFile( file, signature, whichKeyring ) )
           {
             MIL << "File signature is verified" << endl;
-            return true;
+           return (sigValid_r=true);   // signature is actually successfully validated!
           }
           else
           {
@@ -705,12 +701,11 @@ namespace zypp
   std::list<PublicKeyData> KeyRing::trustedPublicKeyData()
   { return _pimpl->trustedPublicKeyData(); }
 
-  bool KeyRing::verifyFileSignatureWorkflow(
-      const Pathname & file,
-      const std::string filedesc,
-      const Pathname & signature,
-      const KeyContext & keycontext )
-  { return _pimpl->verifyFileSignatureWorkflow( file, filedesc, signature, keycontext ); }
+  bool KeyRing::verifyFileSignatureWorkflow( const Pathname & file, const std::string & filedesc, const Pathname & signature, bool & sigValid_r, const KeyContext & keycontext )
+  { return _pimpl->verifyFileSignatureWorkflow( file, filedesc, signature, sigValid_r, keycontext ); }
+
+  bool KeyRing::verifyFileSignatureWorkflow( const Pathname & file, const std::string filedesc, const Pathname & signature, const KeyContext & keycontext )
+  { bool unused; return _pimpl->verifyFileSignatureWorkflow( file, filedesc, signature, unused, keycontext ); }
 
   bool KeyRing::verifyFileSignature( const Pathname & file, const Pathname & signature )
   { return _pimpl->verifyFileSignature( file, signature ); }
index f193ad6..fdf18f8 100644 (file)
@@ -246,6 +246,11 @@ namespace zypp
      * To propagate user decisions, either connect to the \ref KeyRingReport
      * or use its static methods to set the desired defaults.
      *
+     * A second bool passed as reference arg \a sigValid_r tells whether the
+     * signature was actually successfully verified. If \a sigValid_r returns
+     * \c false, but the method \c true, you know it's due to user callback or
+     * defaults.
+     *
      * \code
      * struct KeyRingReportReceive : public callback::ReceiveReport<KeyRingReport>
      * {
@@ -260,15 +265,13 @@ namespace zypp
      * \param file Path of the file to be verified
      * \param filedesc Description of the file (to give the user some context)
      * \param signature Signature to verify the file against
+     * \param sigValid_r Returns whether signature was successfully verified
      *
      * \see \ref KeyRingReport
      */
-    bool verifyFileSignatureWorkflow(
-        const Pathname &file,
-        const std::string filedesc,
-        const Pathname &signature,
-        const KeyContext &keycontext = KeyContext());
-
+    bool verifyFileSignatureWorkflow( const Pathname &file, const std::string &filedesc, const Pathname &signature, bool & sigValid_r, const KeyContext &keycontext = KeyContext());
+    /** \overload legacy version without 'bool & sigValid_r' */
+    bool verifyFileSignatureWorkflow( const Pathname &file, const std::string filedesc, const Pathname &signature, const KeyContext &keycontext = KeyContext());
 
     /**
      * Verifies a file against a signature, with no user interaction
index 3e8f523..47ec974 100644 (file)
@@ -19,6 +19,7 @@
 #include "zypp/RepoInfo.h"
 #include "zypp/TriBool.h"
 #include "zypp/Pathname.h"
+#include "zypp/ZConfig.h"
 #include "zypp/repo/RepoMirrorList.h"
 #include "zypp/ExternalProgram.h"
 #include "zypp/media/MediaAccess.h"
@@ -42,7 +43,10 @@ namespace zypp
   struct RepoInfo::Impl
   {
     Impl()
-      : gpgcheck(indeterminate)
+      : _gpgCheck( indeterminate )
+      , _repoGpgCheck( indeterminate )
+      , _pkgGpgCheck( indeterminate )
+      , _validRepoSignature( indeterminate )
       ,        keeppackages(indeterminate)
       , _mirrorListForceMetalink(false)
       , type(repo::RepoType::NONE_e)
@@ -137,8 +141,67 @@ namespace zypp
       return( _keywords.find( keyword_r ) != _keywords.end() );
     }
 
+    /** Signature check result needs to be stored/retrieved from _metadatapath.
+     * Don't call them from outside validRepoSignature/setValidRepoSignature
+     */
+    //@{
+    TriBool internalValidRepoSignature() const
+    {
+      if ( ! indeterminate(_validRepoSignature) )              return _validRepoSignature;
+      // check metadata:
+      if ( ! metadatapath.empty() )
+      {
+       //TODO: a missing ".repo_gpgcheck" might be plaindir(no Downloader) or not yet refreshed signed repo!
+       TriBool linkval = triBoolFromPath( metadatapath / ".repo_gpgcheck" );
+       return linkval;
+      }
+      return indeterminate;
+    }
+
+    void internalSetValidRepoSignature( TriBool value_r )
+    {
+      if ( PathInfo(metadatapath).isDir() )
+      {
+       TriBool linkval( indeterminate );
+       if ( triBoolFromPath( metadatapath / ".repo_gpgcheck", linkval ) && linkval == value_r )
+         return;       // existing symlink fits value_r
+
+       filesystem::unlink( metadatapath / ".repo_gpgcheck" );
+       filesystem::symlink( asString(value_r), metadatapath / ".repo_gpgcheck" );
+      }
+      _validRepoSignature = value_r;
+    }
+
+    bool triBoolFromPath( const Pathname & path_r, TriBool & ret_r ) const
+    {
+      static const Pathname truePath( "true" );
+      static const Pathname falsePath( "false" );
+      static const Pathname indeterminatePath( "indeterminate" );
+      Pathname linkval( filesystem::readlink( path_r ) );
+      bool known = true;
+      if ( linkval == truePath )
+       ret_r = true;
+      else if ( linkval == falsePath )
+       ret_r = false;
+      else if ( linkval == indeterminatePath )
+       ret_r = indeterminate;
+      else
+       known = false;
+      return known;
+    }
+
+    TriBool triBoolFromPath( const Pathname & path_r ) const
+    { TriBool ret(indeterminate); triBoolFromPath( path_r, ret ); return ret; }
+
+    //@}
+
+  public:
+    TriBool _gpgCheck;         ///< default gpgcheck behavior: Y/N/ZConf
+    TriBool _repoGpgCheck;     ///< need to check repo sign.: Y/N/(ZConf(Y/N/gpgCheck))
+    TriBool _pkgGpgCheck;      ///< need to check pkg sign.: Y/N/(ZConf(Y/N/gpgCheck && no valid repo sign.))
+  private:
+    TriBool _validRepoSignature;///< have  signed and valid repo metadata
   public:
-    TriBool gpgcheck;
     TriBool keeppackages;
     RepoVariablesReplacedUrl _gpgKeyUrl;
     RepoVariablesReplacedUrl _mirrorListUrl;
@@ -178,24 +241,12 @@ namespace zypp
 
   const RepoInfo RepoInfo::noRepo;
 
-  ///////////////////////////////////////////////////////////////////
-  //
-  //   METHOD NAME : RepoInfo::RepoInfo
-  //   METHOD TYPE : Ctor
-  //
   RepoInfo::RepoInfo()
   : _pimpl( new Impl() )
   {}
 
-  ///////////////////////////////////////////////////////////////////
-  //
-  //   METHOD NAME : RepoInfo::~RepoInfo
-  //   METHOD TYPE : Dtor
-  //
   RepoInfo::~RepoInfo()
-  {
-    //MIL << std::endl;
-  }
+  {}
 
   unsigned RepoInfo::priority() const
   { return _pimpl->priority; }
@@ -206,8 +257,52 @@ namespace zypp
   void RepoInfo::setPriority( unsigned newval_r )
   { _pimpl->priority = newval_r ? newval_r : Impl::defaultPriority; }
 
-  void RepoInfo::setGpgCheck( bool check )
-  { _pimpl->gpgcheck = check; }
+
+  bool RepoInfo::gpgCheck() const
+  { return indeterminate(_pimpl->_gpgCheck) ? ZConfig::instance().gpgCheck() : (bool)_pimpl->_gpgCheck; }
+
+  void RepoInfo::setGpgCheck( TriBool value_r )
+  { _pimpl->_gpgCheck = value_r; }
+
+  void RepoInfo::setGpgCheck( bool value_r ) // deprecated legacy and for squid
+  { setGpgCheck( TriBool(value_r) ); }
+
+
+  bool RepoInfo::repoGpgCheck() const
+  {
+    if ( ! indeterminate(_pimpl->_repoGpgCheck) )              return _pimpl->_repoGpgCheck;
+    if ( ! indeterminate(ZConfig::instance().repoGpgCheck()) ) return ZConfig::instance().repoGpgCheck();
+    return gpgCheck(); // no preference: follow gpgCheck
+  }
+
+  void RepoInfo::setRepoGpgCheck( TriBool value_r )
+  { _pimpl->_repoGpgCheck = value_r; }
+
+
+  bool RepoInfo::pkgGpgCheck() const
+  {
+    if ( ! indeterminate(_pimpl->_pkgGpgCheck) )               return _pimpl->_pkgGpgCheck;
+    if ( ! indeterminate(ZConfig::instance().pkgGpgCheck()) )  return ZConfig::instance().pkgGpgCheck();
+    // no preference: follow gpgCheck and check package if repo signature not available or not checked
+    return gpgCheck() && ( !repoGpgCheck() || !(bool)validRepoSignature() );   // !(bool)TriBool ==> false or indeterminate
+  }
+
+  void RepoInfo::setPkgGpgCheck( TriBool value_r )
+  { _pimpl->_pkgGpgCheck = value_r; }
+
+
+  TriBool RepoInfo::validRepoSignature() const
+  {
+    TriBool ret = _pimpl->internalValidRepoSignature();
+    // keep indeterminate(=unsigned) but invalidate any signature if !repoGpgCheck
+    if ( !indeterminate(ret) && !repoGpgCheck() )
+      ret = false;
+    return ret;
+  }
+
+  void RepoInfo::setValidRepoSignature( TriBool value_r )
+  { _pimpl->internalSetValidRepoSignature( value_r ); }
+
 
   void RepoInfo::setMirrorListUrl( const Url & url_r ) // Raw
   { _pimpl->_mirrorListUrl.raw() = url_r; _pimpl->_mirrorListForceMetalink = false; }
@@ -257,9 +352,6 @@ namespace zypp
   void RepoInfo::setTargetDistribution( const std::string & targetDistribution )
   { _pimpl->targetDistro = targetDistribution; }
 
-  bool RepoInfo::gpgCheck() const
-  { return indeterminate(_pimpl->gpgcheck) ? true : (bool)_pimpl->gpgcheck; }
-
   bool RepoInfo::keepPackages() const
   { return indeterminate(_pimpl->keeppackages) ? false : (bool)_pimpl->keeppackages; }
 
@@ -459,7 +551,16 @@ namespace zypp
     strif( "- path        : ", path().asString() );
     str << "- type        : " << type() << std::endl;
     str << "- priority    : " << priority() << std::endl;
-    str << "- gpgcheck    : " << gpgCheck() << std::endl;
+
+    // Yes No Default(Y) Default(N)
+#define OUTS(T,B) ( indeterminate(T) ? (std::string("D(")+(B?"Y":"N")+")") : ((bool)T?"Y":"N") )
+    str << "- gpgcheck    : " << OUTS(_pimpl->_gpgCheck,gpgCheck())
+                              << " repo" << OUTS(_pimpl->_repoGpgCheck,repoGpgCheck())
+                             << " sig" << asString( validRepoSignature(), "?", "Y", "N" )
+                             << " pkg" << OUTS(_pimpl->_pkgGpgCheck,pkgGpgCheck())
+                             << std::endl;
+#undef OUTS
+
     strif( "- gpgkey      : ", rawGpgKeyUrl().asString() );
 
     if ( ! indeterminate(_pimpl->keeppackages) )
@@ -499,8 +600,14 @@ namespace zypp
     if ( priority() != defaultPriority() )
       str << "priority=" << priority() << endl;
 
-    if (!indeterminate(_pimpl->gpgcheck))
-      str << "gpgcheck=" << (gpgCheck() ? "1" : "0") << endl;
+    if ( ! indeterminate(_pimpl->_gpgCheck) )
+      str << "gpgcheck=" << (_pimpl->_gpgCheck ? "1" : "0") << endl;
+
+    if ( ! indeterminate(_pimpl->_repoGpgCheck) )
+      str << "repo_gpgcheck=" << (_pimpl->_repoGpgCheck ? "1" : "0") << endl;
+
+    if ( ! indeterminate(_pimpl->_pkgGpgCheck) )
+      str << "pkg_gpgcheck=" << (_pimpl->_pkgGpgCheck ? "1" : "0") << endl;
 
     if ( ! (rawGpgKeyUrl().asString().empty()) )
       str << "gpgkey=" << rawGpgKeyUrl() << endl;
@@ -527,7 +634,9 @@ namespace zypp
       << " priority=\"" << priority() << "\""
       << " enabled=\"" << enabled() << "\""
       << " autorefresh=\"" << autorefresh() << "\""
-      << " gpgcheck=\"" << gpgCheck() << "\"";
+      << " gpgcheck=\"" << gpgCheck() << "\""
+      << " repo_gpgcheck=\"" << repoGpgCheck() << "\""
+      << " pkg_gpgcheck=\"" << pkgGpgCheck() << "\"";
     if (!(tmpstr = gpgKeyUrl().asString()).empty())
       str << " gpgkey=\"" << escape(tmpstr) << "\"";
     if (!(tmpstr = mirrorListUrl().asString()).empty())
index bd12e4a..6397759 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "zypp/Url.h"
 #include "zypp/Locale.h"
+#include "zypp/TriBool.h"
 #include "zypp/repo/RepoType.h"
 #include "zypp/repo/RepoVariables.h"
 
@@ -246,24 +247,40 @@ namespace zypp
        */
       void setPackagesPath( const Pathname &path );
 
-      /**
-       * \short Whether to check or not this repository with gpg
+
+      /** Whether default signature checking should be performed for this repo.
        *
-       * \note This is a just a hint to the application and can
-       * be ignored.
+       * This will turn on \ref repoGpgCheck for signed repos and
+       * \ref pkgGpgCheck for unsigned ones or if \ref repoGpgCheck is off.
        *
+       * The default is \c true but may be overwritten by \c zypp.conf or a \ref .repo file.
        */
       bool gpgCheck() const;
-      /**
-       * \short Whether to check or not this repository with gpg
-       *
-       * \param check true (check) or false (dont'check)
-       *
-       * \note This is a just a hint to the application and can
-       * be ignored.
-       *
+      /** Set the value for \ref gpgCheck (or \c indeterminate to use the default). */
+      void setGpgCheck( TriBool value_r );
+      /** \overload \deprecated legacy and for squid */
+      void setGpgCheck( bool value_r );
+
+      /** Whether the signature of repo metadata should be checked for this repo.
+       * The default is defined by \ref gpgCheck but may be overwritten by \c zypp.conf or a \ref .repo file.
        */
-      void setGpgCheck( bool check );
+      bool repoGpgCheck() const;
+      /** Set the value for \ref repoGpgCheck (or \c indeterminate to use the default). */
+      void setRepoGpgCheck( TriBool value_r );
+
+      /** Whether the signature of rpm packages should be checked for this repo.
+       * The default is defined by \ref gpgCheck but may be overwritten by \c zypp.conf or a \ref .repo file.
+       */
+      bool pkgGpgCheck() const;
+      /** Set the value for \ref pkgGpgCheck (or \c indeterminate to use the default). */
+      void setPkgGpgCheck( TriBool value_r );
+
+      /** Whether the repo metadata are signed and successfully validated or \c indeterminate if unsigned.
+       * The value is usually set by \ref repo::Downloader when retrieving the metadata.
+       */
+      TriBool validRepoSignature() const;
+      /** Set the value for \ref validRepoSignature (or \c indeterminate if unsigned). */
+      void setValidRepoSignature( TriBool value_r );
 
       /**
        * \short Key to use for gpg checking of this repository
@@ -383,13 +400,6 @@ namespace zypp
       LocaleSet getLicenseLocales() const;
       //@}
 
-      /** \name Repository global unique id
-       *
-       *
-       */
-      //@{
-      //@}
-
     public:
       /**
        * Write a human-readable representation of this RepoInfo object
index 907a17f..a27a5cb 100644 (file)
@@ -85,6 +85,9 @@ namespace zypp
     bool multiversionInstall() const
     { return sat::Solvable::multiversionInstall(); }
 
+    using sat::Solvable::asString;
+    using sat::Solvable::asUserString;
+
     /** \name Dependencies. */
     //@{
     /** Select by Dep. */
index 64488d1..4f6a0ae 100644 (file)
@@ -13,6 +13,7 @@
 #define ZYPP_TRIBOOL_H
 
 #include <iosfwd>
+#include <string>
 #include <boost/logic/tribool.hpp>
 
 ///////////////////////////////////////////////////////////////////
@@ -40,6 +41,20 @@ namespace zypp
   using   boost::logic::tribool;
   using   boost::logic::indeterminate;
 
+  inline std::string asString( const TriBool & val_r, const std::string & istr_r = std::string(),
+                                                     const std::string & tstr_r = std::string(),
+                                                     const std::string & fstr_r = std::string() )
+  {
+    std::string ret;
+    if (indeterminate(val_r))
+      ret = ( istr_r.empty() ? "indeterminate" : istr_r );
+    else if (val_r)
+      ret = ( tstr_r.empty() ? "true" : tstr_r );
+    else
+      ret = ( fstr_r.empty() ? "false" : fstr_r );
+    return ret;
+  }
+
   /////////////////////////////////////////////////////////////////
 } // namespace zypp
 ///////////////////////////////////////////////////////////////////
@@ -49,15 +64,7 @@ namespace boost
     {
       /** \relates TriBool stream output */
       inline std::ostream & operator<<(std::ostream & s, const tribool & obj)
-      {
-        if (indeterminate(obj))
-          s << "indeterminate";
-        else if (obj)
-          s << "true";
-        else
-          s << "false";
-        return s;
-      }
+      { return s << zypp::asString( obj ); }
     }
 }
 #endif // ZYPP_TRIBOOL_H
index acb5526..a52fea6 100644 (file)
@@ -31,37 +31,196 @@ namespace zypp
     ///
     /// Basically a <tt>std::map<std::string,boost::any></tt> plus
     /// associated \ref ContentType.
+    ///
+    /// Constness protects non-empty values from being modified.
+    /// It is possible to overwrite empty values or to add new ones.
     ///////////////////////////////////////////////////////////////////
     class UserData
     {
       typedef std::map<std::string,boost::any> DataType;
+      typedef DataType::size_type size_type;
+      typedef DataType::key_type key_type;
+      typedef DataType::value_type value_type;
+      typedef DataType::const_iterator const_iterator;
     public:
-      /** Default ctor */
+      /** Default ctor. */
       UserData()
       {}
 
+      /** Ctor taking ContentType. */
+      explicit UserData( ContentType type_r )
+      : _type( std::move(type_r) )
+      {}
+      /** Ctor taking ContentType. */
+      explicit UserData( std::string type_r )
+      : UserData( ContentType( std::move(type_r) ) )
+      {}
+      /** Ctor taking ContentType. */
+      UserData( std::string type_r, std::string subtype_r )
+      : UserData( ContentType( std::move(type_r), std::move(subtype_r) ) )
+      {}
+
     public:
-      /** Get type */
+      /** Get type. */
       const ContentType & type() const
       { return _type; }
 
-      /** Set type */
-      void type( const ContentType & type_r )
-      { _type = type_r; }
+      /** Set type. */
+      void type( ContentType type_r )
+      { _type = std::move(type_r); }
 
     public:
-      /**  Validate object in a boolean context: has data */
+       /**  Validate object in a boolean context: has data */
       explicit operator bool() const
-      { return ! ( _dataP == nullptr || _dataP->empty() ); }
+      { return !empty(); }
+
+      /** Whether \ref data is empty. */
+      bool empty() const
+      { return !_dataP || _dataP->empty(); }
+
+      /** Size of \ref data. */
+      size_type size() const
+      { return _dataP ? _dataP->size() : 0;  }
+
+      /** The \ref data. */
+      const DataType & data() const
+      { return dataRef(); }
+
+      /** Whether \a key_r is in \ref data. */
+      bool haskey( const std::string & key_r ) const
+      { return _dataP && _dataP->find( key_r ) != _dataP->end(); }
+
+      /** Whether \a key_r is in \ref data and value is not empty. */
+      bool hasvalue( const std::string & key_r ) const
+      {
+       bool ret = false;
+       if ( _dataP )
+       {
+         const_iterator it = _dataP->find( key_r );
+         if ( it != _dataP->end() && ! it->second.empty() )
+         {
+           ret = true;
+         }
+       }
+       return ret;
+      }
+
+      /** Set the value for key (nonconst version always returns true).
+       * Const version is allowed to set empty values or to add new ones only.
+       */
+      bool set( const std::string & key_r, boost::any val_r )
+      { dataRef()[key_r] = std::move(val_r); return true; }
+      /** \overload const version */
+      bool set( const std::string & key_r, boost::any val_r ) const
+      {
+       bool ret = false;
+       boost::any & val( dataRef()[key_r] );
+       if ( val.empty() )
+       {
+         val = std::move(val_r);
+         ret = true;
+       }
+       return ret;
+      }
+
+      /** Set an empty value for \a key_r (if possible). */
+      bool reset( const std::string & key_r )
+      { return set( key_r, boost::any() ); }
+      /** \overload const version */
+      bool reset( const std::string & key_r ) const
+      { return set( key_r, boost::any() ); }
+
+      /** Remove key from data.*/
+      void erase( const std::string & key_r )
+      { if ( _dataP ) _dataP->erase( key_r ); }
+
+      /** Return the keys boost::any value or an empty value if key does not exist. */
+      const boost::any & getvalue( const std::string & key_r ) const
+      {
+       if ( _dataP )
+       {
+         const_iterator it = _dataP->find( key_r );
+         if ( it != _dataP->end() )
+         {
+           return it->second;
+         }
+       }
+       static const boost::any none;
+       return none;
+      }
+
+      /** Pass back a <tt>const Tp &</tt> reference to \a key_r value.
+       * \throws boost::bad_any_cast if key is not set or value is not of appropriate type
+       * \code
+       *   UserData data;
+       *   std::string value( "defaultvalue" );
+       *   try
+       *   {
+       *     value = data.get<std::string>( "mykey" );
+       *   }
+       *   catch ( const boost::bad_any_cast & )
+       *   {
+       *     // no "mykey" or not a std::sting
+       *   }
+       * \endcode
+       */
+      template <class Tp>
+      const Tp & get( const std::string & key_r ) const
+      { return boost::any_cast<const Tp &>( getvalue( key_r ) ); }
+
+      /** Pass back a \a Tp copy of \a key_r value.
+       * \throws boost::bad_any_cast if key is not set or value is not of appropriate type
+       * \code
+       *   UserData data;
+       *   std::string value = data.get<std::string>( "mykey", "defaultvalue" );
+       * \endcode
+       */
+      template <class Tp>
+      Tp get( const std::string & key_r, const Tp & default_r ) const
+      { Tp ret( default_r ); get( key_r, ret ); return ret; }
+
+      /** If the value for \a key_r is of the same type as \a ret_r, pass it back in \a ret_r and return \c true;.
+       * \code
+       *   UserData data;
+       *   std::string value( "defaultvalue" );
+       *   if ( ! data.get<std::string>( "mykey", value )
+       *   {
+       *      // no "mykey" or not a std::sting
+       *   }
+       * \endcode
+       */
+      template <class Tp>
+      bool get( const std::string & key_r, Tp & ret_r ) const
+      {
+       bool ret = false;
+       if ( _dataP )
+       {
+         const_iterator it = _dataP->find( key_r );
+         if ( it != _dataP->end() )
+         {
+           auto ptr = boost::any_cast<const Tp>(&it->second);
+           if ( ptr )
+           {
+             ret_r = *ptr;
+             ret = true;
+           }
+         }
+       }
+       return ret;
+      }
+
+    private:
+      DataType & dataRef() const
+      { if ( ! _dataP ) _dataP.reset( new DataType ); return *_dataP; }
 
     private:
-      ContentType              _type;
-      shared_ptr<DataType>     _dataP;
+      ContentType _type;
+      mutable shared_ptr<DataType> _dataP;
     };
 
     /** \relates UserData Stream output */
     inline std::ostream & operator<<( std::ostream & str, const UserData & obj )
-    { return str << "UserData(" << obj.type() << ")";}
+    { return str << "UserData(" << obj.type() << ":" << obj.size() << ")";}
 
   } // namespace callback
   ///////////////////////////////////////////////////////////////////
index ef6edc8..857f0c3 100644 (file)
@@ -305,6 +305,9 @@ namespace zypp
         , download_max_silent_tries    ( 5 )
         , download_transfer_timeout    ( 180 )
         , commit_downloadMode          ( DownloadDefault )
+       , gpgCheck                      ( true )
+       , repoGpgCheck                  ( indeterminate )
+       , pkgGpgCheck                   ( indeterminate )
         , solver_onlyRequires          ( false )
         , solver_allowVendorChange     ( false )
         , solver_cleandepsOnRemove     ( false )
@@ -440,6 +443,18 @@ namespace zypp
                 {
                   commit_downloadMode.set( deserializeDownloadMode( value ) );
                 }
+                else if ( entry == "gpgcheck" )
+               {
+                 gpgCheck.set( str::strToBool( value, gpgCheck ) );
+               }
+               else if ( entry == "repo_gpgcheck" )
+               {
+                 repoGpgCheck.set( str::strToTriBool( value ) );
+               }
+               else if ( entry == "pkg_gpgcheck" )
+               {
+                 pkgGpgCheck.set( str::strToTriBool( value ) );
+               }
                 else if ( entry == "vendordir" )
                 {
                   cfg_vendor_path = Pathname(value);
@@ -585,6 +600,10 @@ namespace zypp
 
     Option<DownloadMode> commit_downloadMode;
 
+    Option<bool>       gpgCheck;
+    Option<TriBool>    repoGpgCheck;
+    Option<TriBool>    pkgGpgCheck;
+
     Option<bool>       solver_onlyRequires;
     Option<bool>       solver_allowVendorChange;
     Option<bool>       solver_cleandepsOnRemove;
@@ -866,6 +885,15 @@ namespace zypp
   DownloadMode ZConfig::commit_downloadMode() const
   { return _pimpl->commit_downloadMode; }
 
+  bool ZConfig::gpgCheck() const
+  { return _pimpl->gpgCheck; }
+
+  TriBool ZConfig::repoGpgCheck() const
+  { return _pimpl->repoGpgCheck; }
+
+  TriBool ZConfig::pkgGpgCheck() const
+  { return _pimpl->pkgGpgCheck; }
+
   bool ZConfig::solver_onlyRequires() const
   { return _pimpl->solver_onlyRequires; }
 
index b380138..84b3905 100644 (file)
@@ -24,6 +24,7 @@
 #include "zypp/Locale.h"
 #include "zypp/Pathname.h"
 #include "zypp/IdString.h"
+#include "zypp/TriBool.h"
 
 #include "zypp/DownloadMode.h"
 #include "zypp/target/rpm/RpmFlags.h"
@@ -262,6 +263,24 @@ namespace zypp
        */
       DownloadMode commit_downloadMode() const;
 
+      /** \name Signature checking (repodata and packages)
+       * If \ref gpgcheck is \c on (the default) we will either check the signature
+       * of repo metadata (packages are secured via checksum in the metadata), or the
+       * signature of an rpm package to install if it's repo metadata are not signed
+       * or not checked. If \ref gpgcheck is \c off, no checks are performed.
+       *
+       * The default behavior can be altered by explicitly setting \ref repo_gpgcheck and/or
+       * \ref pkg_gpgcheck to perform those checks always (\c on) or never (\c off).
+       *
+       * Explicitly setting \c gpgcheck, \c repo_gpgcheck or \c pkg_gpgcheck in a
+       * repositories \a .repo file will overwrite the defaults here.
+       */
+      //@{
+      bool gpgCheck() const;           ///< Turn signature checking on/off (on)
+      TriBool repoGpgCheck() const;    ///< Check repo matadata signatures (indeterminate - according to gpgcheck)
+      TriBool pkgGpgCheck() const;     ///< Check rpm package signatures (indeterminate - according to gpgcheck)
+      //@}
+      //
       /**
        * Directory for equivalent vendor definitions  (configPath()/vendors.d)
        * \ingroup g_ZC_CONFIGFILES
index 9727486..82f63f9 100644 (file)
@@ -167,6 +167,21 @@ namespace zypp
        , const std::string &/*description*/
       ) { return ABORT; }
 
+
+      /** Detail information about the result of a performed pkgGpgCheck.
+       *
+       * Userdata sent:
+       * \param "Package"      Package::constPtr of the package
+       * \param "Localpath"    Pathname to downloaded package on disk
+       * \param "CheckPackageResult"   RpmDb::checkPackageResult of signature check
+       * \param "CheckPackageDetail"   RpmDb::CheckPackageDetail logmessages of rpm signature check
+       *
+       *  Userdata accepted:
+       * \param "Action"       DownloadResolvableReport::Action user advice how to behave on error (ABORT).
+       */
+      virtual void pkgGpgCheck( const UserData & userData_r = UserData() )
+      {}
+
       virtual void finish(Resolvable::constPtr /*resolvable_ptr*/
         , Error /*error*/
         , const std::string &/*reason*/
index 666e79a..fb7bd35 100644 (file)
@@ -17,6 +17,8 @@
 #include "zypp/base/String.h"
 #include "zypp/base/LogTools.h"
 
+#include "zypp/TriBool.h"
+
 using std::string;
 
 ///////////////////////////////////////////////////////////////////
@@ -86,6 +88,13 @@ namespace zypp
                );
     }
 
+    TriBool strToTriBool( const C_Str & str )  // from TriBool.h
+    {
+      if ( strToTrue( str ) )  return true;
+      if ( !strToFalse( str ) )        return false;
+      return indeterminate;
+    }
+
     ///////////////////////////////////////////////////////////////////
     // Hexencode
     ///////////////////////////////////////////////////////////////////
index e69a106..c496554 100644 (file)
 #include "zypp/base/PtrTypes.h"
 #include "zypp/base/Function.h"
 
+///////////////////////////////////////////////////////////////////
+namespace boost { namespace logic { class tribool; } }
+namespace zypp { typedef boost::logic::tribool TriBool; }
+///////////////////////////////////////////////////////////////////
 
 ///////////////////////////////////////////////////////////////////
 namespace boost
@@ -404,6 +408,9 @@ namespace zypp
       return return_r;
     }
 
+    /** Parse \c str into a bool if it's a legal \c true or \c false string; else \c indterminate. */
+    TriBool strToTriBool( const C_Str & str );
+
     //@}
 
     /**
index 10d4d91..ff0309c 100644 (file)
@@ -121,7 +121,11 @@ namespace zypp
               info.setGpgKeyUrl( Url(*keys.begin()) );
           }
           else if ( it->first == "gpgcheck" )
-            info.setGpgCheck( str::strToTrue( it->second ) );
+            info.setGpgCheck( str::strToTriBool( it->second ) );
+          else if ( it->first == "repo_gpgcheck" )
+            info.setRepoGpgCheck( str::strToTrue( it->second ) );
+          else if ( it->first == "pkg_gpgcheck" )
+            info.setPkgGpgCheck( str::strToTrue( it->second ) );
          else if ( it->first == "keeppackages" )
            info.setKeepPackages( str::strToTrue( it->second ) );
          else if ( it->first == "service" )
index 2bf752c..137a1ab 100644 (file)
@@ -51,38 +51,49 @@ void Downloader::defaultDownloadMasterIndex( MediaSetAccess & media_r, const Pat
   Pathname sigpath = masterIndex_r.extend( ".asc" );
   Pathname keypath = masterIndex_r.extend( ".key" );
 
-  SignatureFileChecker sigchecker;
-
+  // always download them, even if repoGpgCheck is disabled
   enqueue( OnMediaLocation( sigpath, 1 ).setOptional( true ) );
-  start( destdir_r, media_r );
-  reset();
-
-  // only add the signature if it exists
-  if ( PathInfo(destdir_r / sigpath).isExist() )
-    sigchecker = SignatureFileChecker( destdir_r / sigpath );
-
   enqueue( OnMediaLocation( keypath, 1 ).setOptional( true ) );
   start( destdir_r, media_r );
   reset();
 
-  KeyContext context;
-  context.setRepoInfo( repoInfo() );
-  // only add the key if it exists
-  if ( PathInfo(destdir_r / keypath).isExist() )
-    sigchecker.addPublicKey( destdir_r / keypath, context );
-  else
-    // set the checker context even if the key is not known (unsigned repo, key
-    // file missing; bnc #495977)
-    sigchecker.setKeyContext( context );
+  FileChecker checker; // set to sigchecker if appropriate, else Null.
+  SignatureFileChecker sigchecker;
+  bool isSigned = PathInfo(destdir_r / sigpath).isExist();
 
-  if ( ! repoInfo().gpgCheck() )
+  if ( repoInfo().repoGpgCheck() )
+  {
+    // only add the signature if it exists
+    if ( isSigned )
+      sigchecker = SignatureFileChecker( destdir_r / sigpath );
+
+    KeyContext context;
+    context.setRepoInfo( repoInfo() );
+    // only add the key if it exists
+    if ( PathInfo(destdir_r / keypath).isExist() )
+      sigchecker.addPublicKey( destdir_r / keypath, context );
+    else
+      // set the checker context even if the key is not known (unsigned repo, key
+      // file missing; bnc #495977)
+      sigchecker.setKeyContext( context );
+
+    checker = FileChecker( ref(sigchecker) );  // ref() to the local sigchecker is important as we want back fileValidated!
+  }
+  else
   {
     WAR << "Signature checking disabled in config of repository " << repoInfo().alias() << endl;
   }
-  enqueue( OnMediaLocation( masterIndex_r, 1 ),
-          repoInfo().gpgCheck() ? FileChecker(sigchecker) : FileChecker(NullFileChecker()) );
+
+  enqueue( OnMediaLocation( masterIndex_r, 1 ), checker ? checker : FileChecker(NullFileChecker()) );
   start( destdir_r, media_r );
   reset();
+
+  // Accepted!
+  _repoinfo.setMetadataPath( destdir_r );
+  if ( isSigned )
+    _repoinfo.setValidRepoSignature( sigchecker.fileValidated() );
+  else
+    _repoinfo.setValidRepoSignature( indeterminate );
 }
 
 
index 0557284..a8b5a11 100644 (file)
 #include "zypp/RepoInfo.h"
 #include "zypp/RepoManager.h"
 
+#include "zypp/ZYppFactory.h"
+#include "zypp/Target.h"
+#include "zypp/target/rpm/RpmDb.h"
+#include "zypp/FileChecker.h"
+
 using std::endl;
 
 ///////////////////////////////////////////////////////////////////
@@ -54,6 +59,7 @@ namespace zypp
     ///////////////////////////////////////////////////////////////////
     class PackageProvider::Impl : private base::NonCopyable
     {
+      typedef callback::UserData UserData;
     public:
       /** Ctor taking the Package to provide. */
       Impl( RepoMediaAccess & access_r,
@@ -165,6 +171,51 @@ namespace zypp
        return true; // anyway a failure
       }
 
+      typedef target::rpm::RpmDb RpmDb;
+
+      RpmDb::checkPackageResult packageSigCheck( const Pathname & path_r, UserData & userData ) const
+      {
+       if ( !_target )
+         _target = getZYpp()->getTarget();
+
+       RpmDb::checkPackageResult ret = RpmDb::CHK_ERROR;
+       RpmDb::CheckPackageDetail detail;
+       if ( _target )
+         ret = _target->rpmDb().checkPackage( path_r, detail );
+       else
+         detail.push_back( RpmDb::CheckPackageDetail::value_type( ret, "OOps. Target is not initialized!" ) );
+
+       userData.set( "CheckPackageResult", ret );
+       userData.set( "CheckPackageDetail", std::move(detail) );
+       return ret;
+      }
+
+      /** React on signature verrification error user action */
+      void resolveSignatureErrorAction( repo::DownloadResolvableReport::Action action_r ) const
+      {
+       // TranslatorExplanation %s = package being checked for integrity
+       switch ( action_r )
+       {
+         case repo::DownloadResolvableReport::RETRY:
+           _retry = true;
+           break;
+         case repo::DownloadResolvableReport::IGNORE:
+           WAR << _package->asUserString() << ": " << "User requested skip of insecure file" << endl;
+           break;
+         default:
+         case repo::DownloadResolvableReport::ABORT:
+           ZYPP_THROW(AbortRequestException("User requested to abort"));
+           break;
+       }
+      }
+
+      /** Default signature verrification error handling. */
+      void defaultReportSignatureError( RpmDb::checkPackageResult ret ) const
+      {
+       std::string msg( str::Str() << _package->asUserString() << ": " << _("Signature verification failed") << " " << ret );
+       resolveSignatureErrorAction( report()->problem( _package, repo::DownloadResolvableReport::INVALID, msg ) );
+      }
+
     protected:
       PackageProviderPolicy    _policy;
       Package::constPtr                _package;
@@ -187,6 +238,7 @@ namespace zypp
 
       mutable bool               _retry;
       mutable shared_ptr<Report> _report;
+      mutable Target_Ptr         _target;
     };
     ///////////////////////////////////////////////////////////////////
 
@@ -260,10 +312,53 @@ namespace zypp
       Url url = * info.baseUrlsBegin();
       do {
         _retry = false;
+       if ( ! ret->empty() )
+       {
+         ret.setDispose( filesystem::unlink );
+         ret.reset();
+       }
         report()->start( _package, url );
         try  // ELIMINATE try/catch by providing a log-guard
           {
             ret = doProvidePackage();
+
+           if ( info.pkgGpgCheck() )
+           {
+             UserData userData( "pkgGpgCheck" );
+             userData.set( "Package", _package );
+             userData.set( "Localpath", ret.value() );
+             RpmDb::checkPackageResult res = packageSigCheck( ret, userData );
+             // publish the checkresult, even if it is OK. Apps may want to report something...
+             report()->pkgGpgCheck( userData );
+
+             if ( res != RpmDb::CHK_OK )
+             {
+               if ( userData.hasvalue( "Action" ) )    // pkgGpgCheck provided an user error action
+               {
+                 resolveSignatureErrorAction( userData.get( "Action", repo::DownloadResolvableReport::ABORT ) );
+               }
+               else                                    // no advice from user => usedefaults
+               {
+                 switch ( res )
+                 {
+                   case RpmDb::CHK_OK:         // Signature is OK
+                     break;
+
+                   case RpmDb::CHK_NOKEY:      // Public key is unavailable
+                   case RpmDb::CHK_NOTFOUND:   // Signature is unknown type
+                   case RpmDb::CHK_NOTTRUSTED: // Signature is OK, but key is not trusted
+                     // should fail in future versions.
+                     break;
+
+                   case RpmDb::CHK_FAIL:       // Signature does not verify
+                   case RpmDb::CHK_ERROR:      // File does not exist or can't be opened
+                     // report problem, throw if to abort, else retry/ignore
+                     defaultReportSignatureError( res );
+                     break;
+                 }
+               }
+             }
+           }
           }
         catch ( const UserRequestException & excpt )
           {
index 67b47e1..1b1af08 100644 (file)
  *
 */
 #include "librpm.h"
-
+extern "C"
+{
+#include <rpm/rpmcli.h>
+#include <rpm/rpmlog.h>
+}
 #include <cstdlib>
 #include <cstdio>
 #include <ctime>
@@ -1440,11 +1444,56 @@ void RpmDb::getData( const string & name_r, const Edition & ed_r,
 }
 
 ///////////////////////////////////////////////////////////////////
+namespace
+{
+  struct RpmlogCapture : public std::string
+  {
+    RpmlogCapture()
+    { rpmlog()._cap = this; }
+
+    ~RpmlogCapture()
+    { rpmlog()._cap = nullptr; }
+
+  private:
+    struct Rpmlog
+    {
+      Rpmlog()
+      : _cap( nullptr )
+      {
+       rpmlogSetCallback( rpmLogCB, this );
+       rpmSetVerbosity( RPMLOG_INFO );
+       _f = ::fopen( "/dev/null","w");
+       rpmlogSetFile( _f );
+      }
+
+      ~Rpmlog()
+      { if ( _f ) ::fclose( _f ); }
+
+      static int rpmLogCB( rpmlogRec rec_r, rpmlogCallbackData data_r )
+      { return reinterpret_cast<Rpmlog*>(data_r)->rpmLog( rec_r ); }
+
+      int rpmLog( rpmlogRec rec_r )
+      {
+       if ( _cap ) (*_cap) = rpmlogRecMessage( rec_r );
+       return RPMLOG_DEFAULT;
+      }
+
+      FILE * _f;
+      std::string * _cap;
+    };
+
+    static Rpmlog & rpmlog()
+    { static Rpmlog _rpmlog; return _rpmlog; }
+  };
+
+
+} // namespace
+///////////////////////////////////////////////////////////////////
 //
 //     METHOD NAME : RpmDb::checkPackage
 //     METHOD TYPE : RpmDb::checkPackageResult
 //
-RpmDb::checkPackageResult RpmDb::checkPackage( const Pathname & path_r )
+RpmDb::checkPackageResult RpmDb::checkPackage( const Pathname & path_r, CheckPackageDetail & detail_r )
 {
   PathInfo file( path_r );
   if ( ! file.isFile() )
@@ -1461,41 +1510,72 @@ RpmDb::checkPackageResult RpmDb::checkPackage( const Pathname & path_r )
       ::Fclose( fd );
     return CHK_ERROR;
   }
-
   rpmts ts = ::rpmtsCreate();
   ::rpmtsSetRootDir( ts, root().asString().c_str() );
   ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
-  int res = ::rpmReadPackageFile( ts, fd, path_r.asString().c_str(), NULL );
-  ts = rpmtsFree(ts);
 
+  rpmQVKArguments_s qva;
+  memset( &qva, 0, sizeof(rpmQVKArguments_s) );
+  qva.qva_flags = (VERIFY_DIGEST|VERIFY_SIGNATURE);
+
+  RpmlogCapture vresult;
+  int res = ::rpmVerifySignatures( &qva, ts, fd, path_r.basename().c_str() );
+
+  ts = rpmtsFree(ts);
   ::Fclose( fd );
 
-  switch ( res )
+
+  if ( res == 0 )
   {
-  case RPMRC_OK:
+    detail_r.push_back( CheckPackageDetail::value_type( CHK_OK, std::move(vresult) ) );
     return CHK_OK;
-    break;
-  case RPMRC_NOTFOUND:
-    WAR << "Signature is unknown type. " << file << endl;
-    return CHK_NOTFOUND;
-    break;
-  case RPMRC_FAIL:
-    WAR << "Signature does not verify. " << file << endl;
-    return CHK_FAIL;
-    break;
-  case RPMRC_NOTTRUSTED:
-    WAR << "Signature is OK, but key is not trusted. " << file << endl;
-    return CHK_NOTTRUSTED;
-    break;
-  case RPMRC_NOKEY:
-    WAR << "Public key is unavailable. " << file << endl;
-    return CHK_NOKEY;
-    break;
   }
-  ERR << "Error reading header." << file << endl;
-  return CHK_ERROR;
+
+  // results per line...
+  WAR << vresult;
+  std::vector<std::string> lines;
+  str::split( vresult, std::back_inserter(lines), "\n" );
+  unsigned count[6] = { 0, 0, 0, 0, 0, 0 };
+
+  for ( unsigned i = 1; i < lines.size(); ++i )
+  {
+    std::string & line( lines[i] );
+    checkPackageResult lineres = CHK_ERROR;
+    if ( line.find( ": OK" ) != std::string::npos )
+    { lineres = CHK_OK; }
+    else if ( line.find( ": NOKEY" ) != std::string::npos )
+    { lineres = CHK_NOKEY; }
+    else if ( line.find( ": BAD" ) != std::string::npos )
+    { lineres = CHK_FAIL; }
+    else if ( line.find( ": UNKNOWN" ) != std::string::npos )
+    { lineres = CHK_NOTFOUND; }
+    else if ( line.find( ": NOTRUSTED" ) != std::string::npos )
+    { lineres = CHK_NOTTRUSTED; }
+
+    ++count[lineres];
+    detail_r.push_back( CheckPackageDetail::value_type( lineres, std::move(line) ) );
+  }
+
+  checkPackageResult ret = CHK_ERROR;
+  if ( count[CHK_FAIL] )
+    ret = CHK_FAIL;
+
+  else if ( count[CHK_NOTFOUND] )
+    ret = CHK_NOTFOUND;
+
+  else if ( count[CHK_NOKEY] )
+    ret = CHK_NOKEY;
+
+  else if ( count[CHK_NOTTRUSTED] )
+    ret = CHK_NOTTRUSTED;
+
+  return ret;
 }
 
+RpmDb::checkPackageResult RpmDb::checkPackage( const Pathname & path_r )
+{ CheckPackageDetail dummy; return checkPackage( path_r, dummy ); }
+
+
 // determine changed files of installed package
 bool
 RpmDb::queryChangedFiles(FileList & fileList, const string& packageName)
@@ -2279,6 +2359,35 @@ void RpmDb::setBackupPath(const Pathname& path)
   _backuppath = path;
 }
 
+std::ostream & operator<<( std::ostream & str, RpmDb::checkPackageResult obj )
+{
+  switch ( obj )
+  {
+#define OUTS(E,S) case RpmDb::E: return str << "["<< (unsigned)obj << "-"<< S << "]"; break
+    // translators: possible rpm package signature check result [brief]
+    OUTS( CHK_OK,              _("Signature is OK") );
+    // translators: possible rpm package signature check result [brief]
+    OUTS( CHK_NOTFOUND,                _("Unknown type of signature") );
+    // translators: possible rpm package signature check result [brief]
+    OUTS( CHK_FAIL,            _("Signature does not verify") );
+    // translators: possible rpm package signature check result [brief]
+    OUTS( CHK_NOTTRUSTED,      _("Signature is OK, but key is not trusted") );
+    // translators: possible rpm package signature check result [brief]
+    OUTS( CHK_NOKEY,           _("Signatures public key is not available") );
+    // translators: possible rpm package signature check result [brief]
+    OUTS( CHK_ERROR,           _("File does not exist or signature can't be checked") );
+#undef OUTS
+  }
+  return str << "UnknowSignatureCheckError("+str::numstring(obj)+")";
+}
+
+std::ostream & operator<<( std::ostream & str, const RpmDb::CheckPackageDetail & obj )
+{
+  for ( const auto & el : obj )
+    str << el.second << endl;
+  return str;
+}
+
 } // namespace rpm
 } // namespace target
 } // namespace zypp
index 956ba25..69ae574 100644 (file)
@@ -436,13 +436,23 @@ public:
     CHK_ERROR         = 5  /*!< File does not exist or can't be opened. */
   };
 
+  /** Detailed rpm signature check log messages
+   * A single multiline message if \ref CHK_OK. Otherwise each message line
+   * together with it's \ref checkPackageResult.
+   */
+  struct CheckPackageDetail : std::vector<std::pair<checkPackageResult,std::string>>
+  {};
+
   /**
    * Check signature of rpm file on disk.
    *
-   * @param filename which file to check
+   * @param path_r which file to check
+   * @param detail_r Return detailed rpm log messages
    *
    * @return checkPackageResult
   */
+  checkPackageResult checkPackage( const Pathname & path_r, CheckPackageDetail & detail_r );
+  /** \overload Ignoring the \a datails_r */
   checkPackageResult checkPackage( const Pathname & path_r );
 
   /** install rpm package
@@ -539,6 +549,12 @@ protected:
   void doRebuildDatabase(callback::SendReport<RebuildDBReport> & report);
 };
 
+/** \relates RpmDb::checkPackageResult Stream output */
+std::ostream & operator<<( std::ostream & str, RpmDb::checkPackageResult obj );
+
+/** \relates RpmDb::checkPackageDetail Stream output */
+std::ostream & operator<<( std::ostream & str, const RpmDb::CheckPackageDetail & obj );
+
 } // namespace rpm
 } // namespace target
 } // namespace zypp