From 89e6a9be049641d13ff1959fd5319ad96dd23b03 Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Tue, 1 Nov 2016 10:43:13 +0900 Subject: [PATCH] Imported Upstream version 15.2.0 Change-Id: Ibb673e635f3537b7ff5d8ad4865efb2df38f4f22 Signed-off-by: DongHun Kwak --- VERSION.cmake | 8 +- package/libzypp.changes | 14 ++++ tests/zypp/CMakeLists.txt | 1 + tests/zypp/UserData_test.cc | 87 ++++++++++++++++++++ zypp.conf | 27 +++++++ zypp/Callback.h | 6 ++ zypp/ContentType.h | 29 +++---- zypp/Fetcher.cc | 2 +- zypp/FileChecker.cc | 43 +++++----- zypp/FileChecker.h | 52 ++++++++++-- zypp/KeyRing.cc | 31 +++----- zypp/KeyRing.h | 15 ++-- zypp/RepoInfo.cc | 157 ++++++++++++++++++++++++++++++------ zypp/RepoInfo.h | 50 +++++++----- zypp/Resolvable.h | 3 + zypp/TriBool.h | 25 +++--- zypp/UserData.h | 179 +++++++++++++++++++++++++++++++++++++++--- zypp/ZConfig.cc | 28 +++++++ zypp/ZConfig.h | 19 +++++ zypp/ZYppCallbacks.h | 15 ++++ zypp/base/String.cc | 9 +++ zypp/base/String.h | 7 ++ zypp/parser/RepoFileReader.cc | 6 +- zypp/repo/Downloader.cc | 53 ++++++++----- zypp/repo/PackageProvider.cc | 95 ++++++++++++++++++++++ zypp/target/rpm/RpmDb.cc | 161 +++++++++++++++++++++++++++++++------ zypp/target/rpm/RpmDb.h | 18 ++++- 27 files changed, 951 insertions(+), 189 deletions(-) create mode 100644 tests/zypp/UserData_test.cc diff --git a/VERSION.cmake b/VERSION.cmake index 083325c..7872ad4 100644 --- a/VERSION.cmake +++ b/VERSION.cmake @@ -59,10 +59,10 @@ # 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) #======= diff --git a/package/libzypp.changes b/package/libzypp.changes index dfe542d..9495785 100644 --- a/package/libzypp.changes +++ b/package/libzypp.changes @@ -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) diff --git a/tests/zypp/CMakeLists.txt b/tests/zypp/CMakeLists.txt index acafee6..a2d5d9c 100644 --- a/tests/zypp/CMakeLists.txt +++ b/tests/zypp/CMakeLists.txt @@ -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 index 0000000..41ab3bb --- /dev/null +++ b/tests/zypp/UserData_test.cc @@ -0,0 +1,87 @@ +#include +#include +#include +#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 +} diff --git a/zypp.conf b/zypp.conf index dfe7f64..b23b8a5 100644 --- a/zypp.conf +++ b/zypp.conf @@ -228,6 +228,33 @@ ## 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. diff --git a/zypp/Callback.h b/zypp/Callback.h index 54cf219..3956a0b 100644 --- a/zypp/Callback.h +++ b/zypp/Callback.h @@ -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; diff --git a/zypp/ContentType.h b/zypp/ContentType.h index 4254d06..eaf99c6 100644 --- a/zypp/ContentType.h +++ b/zypp/ContentType.h @@ -36,27 +36,24 @@ namespace zypp /** Ctor taking "type[/subtype]" * \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; diff --git a/zypp/Fetcher.cc b/zypp/Fetcher.cc index 03924d6..ef8e95b 100644 --- a/zypp/Fetcher.cc +++ b/zypp/Fetcher.cc @@ -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 diff --git a/zypp/FileChecker.cc b/zypp/FileChecker.cc index f6b7abc..1d21137 100644 --- a/zypp/FileChecker.cc +++ b/zypp/FileChecker.cc @@ -18,18 +18,20 @@ 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 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() ) ); + } /****************************************************************** ** diff --git a/zypp/FileChecker.h b/zypp/FileChecker.h index 579a627..d3ddf2d 100644 --- a/zypp/FileChecker.h +++ b/zypp/FileChecker.h @@ -14,6 +14,7 @@ #include #include +#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 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 _fileAccepted; + mutable DefaultIntegral _fileValidated; }; /** diff --git a/zypp/KeyRing.cc b/zypp/KeyRing.cc index 797f0e7..9c3f39b 100644 --- a/zypp/KeyRing.cc +++ b/zypp/KeyRing.cc @@ -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 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 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 ); } diff --git a/zypp/KeyRing.h b/zypp/KeyRing.h index f193ad6..fdf18f8 100644 --- a/zypp/KeyRing.h +++ b/zypp/KeyRing.h @@ -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 * { @@ -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 diff --git a/zypp/RepoInfo.cc b/zypp/RepoInfo.cc index 3e8f523..47ec974 100644 --- a/zypp/RepoInfo.cc +++ b/zypp/RepoInfo.cc @@ -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()) diff --git a/zypp/RepoInfo.h b/zypp/RepoInfo.h index bd12e4a..6397759 100644 --- a/zypp/RepoInfo.h +++ b/zypp/RepoInfo.h @@ -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 diff --git a/zypp/Resolvable.h b/zypp/Resolvable.h index 907a17f..a27a5cb 100644 --- a/zypp/Resolvable.h +++ b/zypp/Resolvable.h @@ -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. */ diff --git a/zypp/TriBool.h b/zypp/TriBool.h index 64488d1..4f6a0ae 100644 --- a/zypp/TriBool.h +++ b/zypp/TriBool.h @@ -13,6 +13,7 @@ #define ZYPP_TRIBOOL_H #include +#include #include /////////////////////////////////////////////////////////////////// @@ -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 diff --git a/zypp/UserData.h b/zypp/UserData.h index acb5526..a52fea6 100644 --- a/zypp/UserData.h +++ b/zypp/UserData.h @@ -31,37 +31,196 @@ namespace zypp /// /// Basically a std::map 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 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 const Tp & 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( "mykey" ); + * } + * catch ( const boost::bad_any_cast & ) + * { + * // no "mykey" or not a std::sting + * } + * \endcode + */ + template + const Tp & get( const std::string & key_r ) const + { return boost::any_cast( 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( "mykey", "defaultvalue" ); + * \endcode + */ + template + 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( "mykey", value ) + * { + * // no "mykey" or not a std::sting + * } + * \endcode + */ + template + 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(&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 _dataP; + ContentType _type; + mutable shared_ptr _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 /////////////////////////////////////////////////////////////////// diff --git a/zypp/ZConfig.cc b/zypp/ZConfig.cc index ef6edc8..857f0c3 100644 --- a/zypp/ZConfig.cc +++ b/zypp/ZConfig.cc @@ -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 commit_downloadMode; + Option gpgCheck; + Option repoGpgCheck; + Option pkgGpgCheck; + Option solver_onlyRequires; Option solver_allowVendorChange; Option 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; } diff --git a/zypp/ZConfig.h b/zypp/ZConfig.h index b380138..84b3905 100644 --- a/zypp/ZConfig.h +++ b/zypp/ZConfig.h @@ -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 diff --git a/zypp/ZYppCallbacks.h b/zypp/ZYppCallbacks.h index 9727486..82f63f9 100644 --- a/zypp/ZYppCallbacks.h +++ b/zypp/ZYppCallbacks.h @@ -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*/ diff --git a/zypp/base/String.cc b/zypp/base/String.cc index 666e79a..fb7bd35 100644 --- a/zypp/base/String.cc +++ b/zypp/base/String.cc @@ -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 /////////////////////////////////////////////////////////////////// diff --git a/zypp/base/String.h b/zypp/base/String.h index e69a106..c496554 100644 --- a/zypp/base/String.h +++ b/zypp/base/String.h @@ -24,6 +24,10 @@ #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 ); + //@} /** diff --git a/zypp/parser/RepoFileReader.cc b/zypp/parser/RepoFileReader.cc index 10d4d91..ff0309c 100644 --- a/zypp/parser/RepoFileReader.cc +++ b/zypp/parser/RepoFileReader.cc @@ -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" ) diff --git a/zypp/repo/Downloader.cc b/zypp/repo/Downloader.cc index 2bf752c..137a1ab 100644 --- a/zypp/repo/Downloader.cc +++ b/zypp/repo/Downloader.cc @@ -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 ); } diff --git a/zypp/repo/PackageProvider.cc b/zypp/repo/PackageProvider.cc index 0557284..a8b5a11 100644 --- a/zypp/repo/PackageProvider.cc +++ b/zypp/repo/PackageProvider.cc @@ -26,6 +26,11 @@ #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; + 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 ) { diff --git a/zypp/target/rpm/RpmDb.cc b/zypp/target/rpm/RpmDb.cc index 67b47e1..1b1af08 100644 --- a/zypp/target/rpm/RpmDb.cc +++ b/zypp/target/rpm/RpmDb.cc @@ -10,7 +10,11 @@ * */ #include "librpm.h" - +extern "C" +{ +#include +#include +} #include #include #include @@ -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(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 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 diff --git a/zypp/target/rpm/RpmDb.h b/zypp/target/rpm/RpmDb.h index 956ba25..69ae574 100644 --- a/zypp/target/rpm/RpmDb.h +++ b/zypp/target/rpm/RpmDb.h @@ -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> + {}; + /** * 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 & 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 -- 2.7.4