# 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)
#=======
-------------------------------------------------------------------
+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)
StrMatcher
Target
Url
+ UserData
Vendor
Vendor2
)
--- /dev/null
+#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
+}
## 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.
#define ZYPP_CALLBACK_H
#include "zypp/base/NonCopyable.h"
+#include "zypp/UserData.h"
///////////////////////////////////////////////////////////////////
namespace zypp
/** */
struct ReportBase
{
+ typedef callback::UserData UserData;
virtual ~ReportBase()
{}
};
struct ReceiveReport : public _Report
{
typedef _Report ReportType;
+ typedef typename ReportType::UserData UserData;
typedef ReceiveReport<_Report> Receiver;
typedef DistributeReport<_Report> Distributor;
{
public:
typedef _Report ReportType;
+ typedef typename ReportType::UserData UserData;
typedef ReceiveReport<_Report> Receiver;
typedef DistributeReport<_Report> Distributor;
struct SendReport : private zypp::base::NonCopyable
{
typedef _Report ReportType;
+ typedef typename ReportType::UserData UserData;
typedef ReceiveReport<_Report> Receiver;
typedef DistributeReport<_Report> Distributor;
struct TempConnect
{
typedef _Report ReportType;
+ typedef typename ReportType::UserData UserData;
typedef ReceiveReport<_Report> Receiver;
typedef DistributeReport<_Report> Distributor;
/** 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:
/** 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
/** 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 */
{ 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;
// 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
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() )
}
else
{
- ZYPP_THROW( FileCheckException( file.basename() + " has no checksum" ) );
+ ZYPP_THROW( ExceptionType( file.basename() + " has no checksum" ) );
}
}
else
}
else
{
- ZYPP_THROW( FileCheckException( file.basename() + " has wrong checksum" ) );
+ ZYPP_THROW( ExceptionType( file.basename() + " has wrong checksum" ) );
}
}
}
}
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; }
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() ) );
+ }
/******************************************************************
**
#include <iosfwd>
#include <list>
+#include "zypp/base/DefaultIntegral.h"
#include "zypp/base/Exception.h"
#include "zypp/base/Function.h"
#include "zypp/PathInfo.h"
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)
+ {}
};
/**
class ChecksumFileChecker
{
public:
+ typedef CheckSumCheckException ExceptionType;
/**
* Constructor.
* \param checksum Checksum that validates the file
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
*/
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
*/
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;
};
/**
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() ); }
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;
// 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 );
if ( verifyFile( file, signature, whichKeyring ) )
{
MIL << "File signature is verified" << endl;
- return true;
+ return (sigValid_r=true); // signature is actually successfully validated!
}
else
{
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 ); }
* 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>
* {
* \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
#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"
struct RepoInfo::Impl
{
Impl()
- : gpgcheck(indeterminate)
+ : _gpgCheck( indeterminate )
+ , _repoGpgCheck( indeterminate )
+ , _pkgGpgCheck( indeterminate )
+ , _validRepoSignature( indeterminate )
, keeppackages(indeterminate)
, _mirrorListForceMetalink(false)
, type(repo::RepoType::NONE_e)
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;
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; }
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; }
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; }
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) )
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;
<< " 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())
#include "zypp/Url.h"
#include "zypp/Locale.h"
+#include "zypp/TriBool.h"
#include "zypp/repo/RepoType.h"
#include "zypp/repo/RepoVariables.h"
*/
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
LocaleSet getLicenseLocales() const;
//@}
- /** \name Repository global unique id
- *
- *
- */
- //@{
- //@}
-
public:
/**
* Write a human-readable representation of this RepoInfo object
bool multiversionInstall() const
{ return sat::Solvable::multiversionInstall(); }
+ using sat::Solvable::asString;
+ using sat::Solvable::asUserString;
+
/** \name Dependencies. */
//@{
/** Select by Dep. */
#define ZYPP_TRIBOOL_H
#include <iosfwd>
+#include <string>
#include <boost/logic/tribool.hpp>
///////////////////////////////////////////////////////////////////
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
///////////////////////////////////////////////////////////////////
{
/** \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
///
/// 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
///////////////////////////////////////////////////////////////////
, 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 )
{
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);
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;
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; }
#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"
*/
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
, 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*/
#include "zypp/base/String.h"
#include "zypp/base/LogTools.h"
+#include "zypp/TriBool.h"
+
using std::string;
///////////////////////////////////////////////////////////////////
);
}
+ TriBool strToTriBool( const C_Str & str ) // from TriBool.h
+ {
+ if ( strToTrue( str ) ) return true;
+ if ( !strToFalse( str ) ) return false;
+ return indeterminate;
+ }
+
///////////////////////////////////////////////////////////////////
// Hexencode
///////////////////////////////////////////////////////////////////
#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
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 );
+
//@}
/**
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" )
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 );
}
#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;
///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
class PackageProvider::Impl : private base::NonCopyable
{
+ typedef callback::UserData UserData;
public:
/** Ctor taking the Package to provide. */
Impl( RepoMediaAccess & access_r,
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;
mutable bool _retry;
mutable shared_ptr<Report> _report;
+ mutable Target_Ptr _target;
};
///////////////////////////////////////////////////////////////////
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 )
{
*
*/
#include "librpm.h"
-
+extern "C"
+{
+#include <rpm/rpmcli.h>
+#include <rpm/rpmlog.h>
+}
#include <cstdlib>
#include <cstdio>
#include <ctime>
}
///////////////////////////////////////////////////////////////////
+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() )
::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)
_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
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
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