libzypp  17.37.5
keyringwf.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
9 
10 #include "keyringwf.h"
11 #include "logichelpers.h"
14 #include <zypp/RepoInfo.h>
15 #include <zypp/ZConfig.h>
16 #include <zypp/Pathname.h>
17 #include <zypp-common/PublicKey.h>
18 
19 #include <zypp-core/base/Gettext.h>
20 #include <utility>
21 #include <zypp-core/zyppng/pipelines/Expected>
23 #include <zypp/ng/Context>
24 #include <zypp/ng/reporthelper.h>
25 #include <zypp/ng/UserRequest>
26 
28 
29  template <class Executor, class OpType>
30  struct ImportKeyFromRepoLogic : public LogicBase<Executor, OpType> {
31 
34  using ProvideType = typename ZyppContextType::ProvideType;
35  using MediaHandle = typename ProvideType::MediaHandle;
36  using ProvideRes = typename ProvideType::Res;
37 
38  ZYPP_ENABLE_LOGIC_BASE(Executor, OpType);
39 
40  public:
41  ImportKeyFromRepoLogic( ZyppContextRefType context, std::string &&keyId, zypp::RepoInfo &&info )
42  : _context( std::move(context) ), _keyId(std::move(keyId)), _repo( std::move(info) )
43  { }
44 
46 
47  using namespace zyppng::operators;
48  using zyppng::operators::operator|;
49  using zyppng::expected;
50 
51  if ( _keyId.empty() || !_context )
52  return makeReadyResult(false);
53 
54  if ( _context->keyRing()->isKeyKnown(_keyId) ){
55  if ( _context->keyRing()->isKeyTrusted(_keyId) )
56  return makeReadyResult(true);
57 
59  }
60 
61  const zypp::ZConfig &conf = _context->config();
62  zypp::Pathname cacheDir = conf.repoManagerRoot() / conf.pubkeyCachePath();
63 
64  if ( _repo.gpgKeyUrlsEmpty() ) {
65  // translator: %1% is a repositories name
66  JobReportHelper(_context).info( zypp::str::Format(_("Repository %1% does not define 'gpgkey=' URLs.") ) % _repo.asUserString() );
67  }
68 
70  | [this, cacheDir]( expected<void> res ) {
71  if ( !res ) return false;
72  if ( !_context->keyRing()->isKeyKnown(_keyId) )
73  // if we did not find any keys, there is no point in checking again, break
74  return false;
75 
76  return importFromKnownKeyring();
77  };
78  }
79 
81  zypp::PublicKeyData keyData ( _context->keyRing()->publicKeyData(_keyId) );
82  if ( !keyData )
83  return false;
84 
85  zypp::PublicKey key;
86  try {
87  key = zypp::PublicKey( _context->keyRing()->exportPublicKey( keyData ) );
88  } catch ( const zypp::Exception &e ) {
89  ZYPP_CAUGHT(e);
90  return false;
91  }
92 
93  if ( !key.isValid() ) {
94  ERR << "Key [" << _keyId << "] from known keyring is not valid" << std::endl;
95  return false;
96  }
97 
98  MIL << "Key [" << _keyId << "] " << key.name() << " loaded from cache" << std::endl;
99 
100  zypp::KeyContext context;
101  context.setRepoInfo( _repo );
102  if ( !KeyRingReportHelper(_context).askUserToAcceptPackageKey( key, context ) ) {
103  return false;
104  }
105 
106  MIL << "User wants to import key [" << _keyId << "] " << key.name() << " from cache" << std::endl;
107  try {
108  _context->keyRing()->importKey( key, true );
109  } catch ( const zypp::KeyRingException &e ) {
110  ZYPP_CAUGHT(e);
111  ERR << "Failed to import key: "<<_keyId;
112  return false;
113  }
114  return true;
115  }
116 
118  std::string _keyId;
120  };
121 
122  bool provideAndImportKeyFromRepository( SyncContextRef ctx, std::string id_r, zypp::RepoInfo info_r )
123  {
124  return SimpleExecutor<ImportKeyFromRepoLogic, SyncOp<bool>>::run( ctx, std::move(id_r), std::move(info_r) );
125  }
126 
127  AsyncOpRef<bool> provideAndImportKeyFromRepository( ContextRef ctx, std::string id_r, zypp::RepoInfo info_r)
128  {
129  return SimpleExecutor<ImportKeyFromRepoLogic, AsyncOp<bool>>::run( ctx, std::move(id_r), std::move(info_r) );
130  }
131 
132  namespace {
133 
138  template <class Executor, class OpType>
139  struct VerifyFileSignatureLogic : public LogicBase<Executor, OpType>
140  {
141  ZYPP_ENABLE_LOGIC_BASE(Executor, OpType);
142 
145 
146  VerifyFileSignatureLogic( ZyppContextRefType zyppContext, KeyRingRef &&keyRing, zypp::keyring::VerifyFileContext &&ctx )
147  : _zyppContext( std::move(zyppContext) )
149  , _keyRing( std::move(keyRing) )
150  , _verifyContext( std::move(ctx) )
151  { }
152 
153  struct FoundKeyData {
154  zypp::PublicKeyData _foundKey;
156  bool trusted = false;
157  };
158 
159  MaybeAsyncRef<FoundKeyData> findKey ( const std::string &id ) {
160 
161  using zyppng::operators::operator|;
162 
163  if ( id.empty() )
164  return makeReadyResult(FoundKeyData{zypp::PublicKeyData(), zypp::Pathname()});
165 
166  // does key exists in trusted keyring
167  zypp::PublicKeyData trustedKeyData( _keyRing->pimpl().publicKeyExists( id, _keyRing->pimpl().trustedKeyRing() ) );
168  if ( trustedKeyData )
169  {
170  MIL << "Key is trusted: " << trustedKeyData << std::endl;
171 
172  // lets look if there is an updated key in the
173  // general keyring
174  zypp::PublicKeyData generalKeyData( _keyRing->pimpl().publicKeyExists( id, _keyRing->pimpl().generalKeyRing() ) );
175  if ( generalKeyData )
176  {
177  // bnc #393160: Comment #30: Compare at least the fingerprint
178  // in case an attacker created a key the the same id.
179  //
180  // FIXME: bsc#1008325: For keys using subkeys, we'd actually need
181  // to compare the subkey sets, to tell whether a key was updated.
182  // because created() remains unchanged if the primary key is not touched.
183  // For now we wait until a new subkey signs the data and treat it as a
184  // new key (else part below).
185  if ( trustedKeyData.fingerprint() == generalKeyData.fingerprint()
186  && trustedKeyData.created() < generalKeyData.created() )
187  {
188  MIL << "Key was updated. Saving new version into trusted keyring: " << generalKeyData << std::endl;
189  _keyRing->importKey( _keyRing->pimpl().exportKey( generalKeyData, _keyRing->pimpl().generalKeyRing() ), true );
190  trustedKeyData = _keyRing->pimpl().publicKeyExists( id, _keyRing->pimpl().trustedKeyRing() ); // re-read: invalidated by import?
191  }
192  }
193 
194  return makeReadyResult( FoundKeyData{ trustedKeyData, _keyRing->pimpl().trustedKeyRing(), true } );
195  }
196  else
197  {
198  zypp::PublicKeyData generalKeyData( _keyRing->pimpl().publicKeyExists( id, _keyRing->pimpl().generalKeyRing() ) );
199  if ( generalKeyData )
200  {
201  zypp::PublicKey key( _keyRing->pimpl().exportKey( generalKeyData, _keyRing->pimpl().generalKeyRing() ) );
202  MIL << "Key [" << id << "] " << key.name() << " is not trusted" << std::endl;
203 
204  // ok the key is not trusted, ask the user to trust it or not
205  zypp::KeyRingReport::KeyTrust reply = _keyringReport.askUserToAcceptKey( key, _verifyContext.keyContext() );
208  {
209  zypp::Pathname whichKeyring;
210 
211  MIL << "User wants to trust key [" << id << "] " << key.name() << std::endl;
212 
214  {
215  MIL << "User wants to import key [" << id << "] " << key.name() << std::endl;
216  _keyRing->importKey( key, true );
217  whichKeyring = _keyRing->pimpl().trustedKeyRing();
218  }
219  else
220  whichKeyring = _keyRing->pimpl().generalKeyRing();
221 
222  return makeReadyResult(FoundKeyData { std::move(generalKeyData), std::move(whichKeyring), true });
223  }
224  else
225  {
226  MIL << "User does not want to trust key [" << id << "] " << key.name() << std::endl;
227  return makeReadyResult(FoundKeyData { std::move(generalKeyData), _keyRing->pimpl().generalKeyRing(), false });
228  }
229  }
230  else if ( ! _verifyContext.keyContext().empty() )
231  {
232  // try to find the key in the repository info
234  | [this, id]( bool success ) {
235  if ( !success ) {
236  return FoundKeyData{ zypp::PublicKeyData(), zypp::Pathname() };
237  }
238  return FoundKeyData{ _keyRing->pimpl().publicKeyExists( id, _keyRing->pimpl().trustedKeyRing() ), _keyRing->pimpl().trustedKeyRing(), true };
239  };
240  }
241  }
242  return makeReadyResult(FoundKeyData{ zypp::PublicKeyData(), zypp::Pathname() });
243  }
244 
245  // returns std::pair<bool, zypp::keyring::VerifyFileContext>
246  auto execute () {
247 
249  const zypp::Pathname & file { _verifyContext.file() };
250  const zypp::Pathname & signature { _verifyContext.signature() };
251  const std::string & filedesc { _verifyContext.shortFile() };
252 
253  MIL << "Going to verify signature for " << filedesc << " ( " << file << " ) with " << signature << std::endl;
254 
255  // if signature does not exists, ask user if they want to accept unsigned file.
256  if( signature.empty() || (!zypp::PathInfo( signature ).isExist()) )
257  {
258  bool res = _keyringReport.askUserToAcceptUnsignedFile( filedesc, _verifyContext.keyContext() );
259  MIL << "askUserToAcceptUnsignedFile: " << res << std::endl;
260  return makeReadyResult( makeReturn(res) );
261  }
262 
263  // get the id of the signature (it might be a subkey id!)
264  try {
265  _verifyContext.signatureId( _keyRing->readSignatureKeyId( signature ) ); //throws !
266  } catch ( const zypp::Exception &e ) {
267  MIL << "Failed to read the signature from " << signature << std::endl;
268  ZYPP_CAUGHT(e);
269  return makeReadyResult( makeReturn(false) );
270  }
271 
272  const std::string & id = _verifyContext.signatureId();
273 
274  // collect the buddies
275  std::list<zypp::PublicKeyData> buddies; // Could be imported IFF the file is validated by a trusted key
276  for ( const auto & sid : _verifyContext.buddyKeys() ) {
277  if ( not zypp::PublicKeyData::isSafeKeyId( sid ) ) {
278  WAR << "buddy " << sid << ": key id is too short to safely identify a gpg key. Skipping it." << std::endl;
279  continue;
280  }
281  if ( _keyRing->pimpl().trustedPublicKeyExists( sid ) ) {
282  MIL << "buddy " << sid << ": already in trusted key ring. Not needed." << std::endl;
283  continue;
284  }
285  auto pk = _keyRing->pimpl().publicKeyExists( sid );
286  if ( not pk ) {
287  WAR << "buddy " << sid << ": not available in the public key ring. Skipping it." << std::endl;
288  continue;
289  }
290  if ( pk.providesKey(id) ) {
291  MIL << "buddy " << sid << ": is the signing key. Handled separately." << std::endl;
292  continue;
293  }
294  MIL << "buddy " << sid << ": candidate for auto import. Remeber it." << std::endl;
295  buddies.push_back( pk );
296  }
297 
298  using zyppng::operators::operator|;
299  return findKey( id ) | [this, id, buddies=std::move(buddies)]( FoundKeyData res ) {
300 
301  const zypp::Pathname & file { _verifyContext.file() };
302  const zypp::KeyContext & keyContext { _verifyContext.keyContext() };
303  const zypp::Pathname & signature { _verifyContext.signature() };
304  const std::string & filedesc { _verifyContext.shortFile() };
305 
306  if ( res._foundKey ) {
307 
308  // we found a key but it is not trusted ( e.g. user did not want to trust it )
309  if ( !res.trusted )
310  return makeReturn(false);
311 
312  // it exists, is trusted, does it validate?
313  _verifyContext.signatureIdTrusted( res._whichKeyRing == _keyRing->pimpl().trustedKeyRing() );
314  _keyringReport.infoVerify( filedesc, res._foundKey, keyContext );
315  if ( _keyRing->pimpl().verifyFile( file, signature, res._whichKeyRing ) )
316  {
318  if ( _verifyContext.signatureIdTrusted() && not buddies.empty() ) {
319  // Check for buddy keys to be imported...
320  MIL << "Validated with trusted key: importing buddy list..." << std::endl;
321  _keyringReport.reportAutoImportKey( buddies, res._foundKey, keyContext );
322  for ( const auto & kd : buddies ) {
323  _keyRing->importKey( _keyRing->pimpl().exportKey( kd, _keyRing->pimpl().generalKeyRing() ), true );
324  }
325  }
326  return makeReturn(_verifyContext.fileValidated()); // signature is actually successfully validated!
327  }
328  else
329  {
330  bool userAnswer = _keyringReport.askUserToAcceptVerificationFailed( filedesc, _keyRing->pimpl().exportKey( res._foundKey, res._whichKeyRing ), keyContext );
331  MIL << "askUserToAcceptVerificationFailed: " << userAnswer << std::endl;
332  return makeReturn(userAnswer);
333  }
334  } else {
335  // signed with an unknown key...
336  MIL << "File [" << file << "] ( " << filedesc << " ) signed with unknown key [" << id << "]" << std::endl;
337  bool res = _keyringReport.askUserToAcceptUnknownKey( filedesc, id, _verifyContext.keyContext() );
338  MIL << "askUserToAcceptUnknownKey: " << res << std::endl;
339  return makeReturn(res);
340  }
341 
342  return makeReturn(false);
343  };
344  }
345 
346  protected:
348  KeyRingReportHelper<ZyppContextRefType> _keyringReport;
351 
352  private:
353  inline std::pair<bool, zypp::keyring::VerifyFileContext> makeReturn( bool res ){
355  return std::make_pair( res, std::move(_verifyContext) ) ;
356  }
357  };
358  }
359 
360  std::pair<bool,zypp::keyring::VerifyFileContext>
361  verifyFileSignature( SyncContextRef zyppContext, zypp::keyring::VerifyFileContext &&context_r )
362  {
363  auto kr = zyppContext->keyRing();
364  return SimpleExecutor<VerifyFileSignatureLogic, SyncOp<std::pair<bool,zypp::keyring::VerifyFileContext> >>::run( std::move(zyppContext), std::move(kr), std::move(context_r) );
365  }
366 
368  {
369  auto kr = zyppContext->keyRing();
370  return SimpleExecutor<VerifyFileSignatureLogic, AsyncOp<std::pair<bool,zypp::keyring::VerifyFileContext> >>::run( std::move(zyppContext), std::move(kr), std::move(context_r) );
371  }
372 
373  std::pair<bool,zypp::keyring::VerifyFileContext> verifyFileSignature( SyncContextRef zyppContext, zypp::KeyRing_Ptr keyRing, zypp::keyring::VerifyFileContext &&context_r )
374  {
375  return SimpleExecutor<VerifyFileSignatureLogic, SyncOp<std::pair<bool,zypp::keyring::VerifyFileContext> >>::run( std::move(zyppContext), std::move(keyRing), std::move(context_r) );
376  }
377 
379  {
380  return SimpleExecutor<VerifyFileSignatureLogic, AsyncOp<std::pair<bool,zypp::keyring::VerifyFileContext> >>::run( std::move(zyppContext), std::move(keyRing), std::move(context_r) );
381  }
382 
383 }
bool fileValidated() const
Whether the signature was actually successfully verified.
#define MIL
Definition: Logger.h:100
zypp::PublicKeyData _foundKey
Definition: keyringwf.cc:154
#define _(MSG)
Definition: Gettext.h:39
const std::string & signatureId() const
The id of the gpg key which signed the file.
zypp::keyring::VerifyFileContext _verifyContext
Definition: keyringwf.cc:350
This basically means, we knew the key, but it was not trusted.
Definition: KeyRing.h:63
Pathname pubkeyCachePath() const
Path where the pubkey caches.
Definition: ZConfig.cc:1075
zypp::Pathname _whichKeyRing
Definition: keyringwf.cc:155
bool provideAndImportKeyFromRepository(SyncContextRef ctx, std::string id_r, zypp::RepoInfo info_r)
Try to find the id in key cache or repository specified in info.
Definition: keyringwf.cc:122
Definition: Arch.h:363
What is known about a repository.
Definition: RepoInfo.h:71
I/O context for KeyRing::verifyFileSignatureWorkflow.
std::string asUserString() const
User string: label (alias or name)
Definition: RepoInfoBase.h:87
std::pair< bool, zypp::keyring::VerifyFileContext > verifyFileSignature(SyncContextRef zyppContext, zypp::keyring::VerifyFileContext &&context_r)
Follows a signature verification interacting with the user.
Definition: keyringwf.cc:361
Convenient building of std::string with boost::format.
Definition: String.h:253
zypp::KeyRing_Ptr KeyRingRef
Definition: context.h:29
#define ERR
Definition: Logger.h:102
const KeyContext & keyContext() const
KeyContext passed to callbacks
bool signatureIdTrusted() const
Whether the SignatureId is in the trusted keyring (not temp.
void resetResults()
Reset all result values to safe defaults.
expected< void > fetchGpgKeys(SyncContextRef ctx, zypp::RepoInfo info)
Definition: repoinfowf.cc:136
Pathname repoManagerRoot() const
The RepoManager root directory.
Definition: ZConfig.cc:980
bool info(std::string msg_r, UserData userData_r=UserData())
send message text
KeyTrust
User reply options for the askUserToTrustKey callback.
Definition: KeyRing.h:53
bool trusted
Definition: keyringwf.cc:156
static std::enable_if_t< detail::is_async_op_v< FOpType >, AsyncOpRef< Result > > run(Args &&...args)
Definition: logichelpers.h:170
const Pathname & signature() const
Detached signature or empty.
bool gpgKeyUrlsEmpty() const
Whether gpgkey URLs are defined.
Definition: RepoInfo.cc:768
KeyRingRef _keyRing
Definition: keyringwf.cc:349
bool isExist() const
Return whether valid stat info exists.
Definition: PathInfo.h:286
Interim helper class to collect global options and settings.
Definition: ZConfig.h:68
#define WAR
Definition: Logger.h:101
ImportKeyFromRepoLogic(ZyppContextRefType context, std::string &&keyId, zypp::RepoInfo &&info)
Definition: keyringwf.cc:41
typename ProvideType::MediaHandle MediaHandle
Definition: keyringwf.cc:35
const Pathname & file() const
File to verify.
KeyRingReportHelper< ZyppContextRefType > _keyringReport
Definition: keyringwf.cc:348
const BuddyKeys & buddyKeys() const
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition: Exception.h:475
const RepoInfo repoInfo() const
Definition: KeyContext.h:18
std::shared_ptr< AsyncOp< T > > AsyncOpRef
Definition: asyncop.h:255
typename ZyppContextType::ProvideType ProvideType
Definition: keyringwf.cc:34
Base class for Exception.
Definition: Exception.h:152
bool empty() const
Is the context unknown?
Definition: KeyContext.h:15
remove_smart_ptr_t< ZyppContextRefType > ZyppContextType
Definition: keyringwf.cc:33
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:225
MaybeAsyncContextRef< OpType > ZyppContextRefType
Definition: keyringwf.cc:32
std::conditional_t< isAsync, AsyncOpRef< Type >, Type > MaybeAsyncRef
Definition: logichelpers.h:44
bool fileAccepted() const
May return true due to user interaction or global defaults even if the signature was not actually ver...
std::conditional_t< detail::is_async_op_v< OpType >, ContextRef, SyncContextRef > MaybeAsyncContextRef
Definition: contextfacade.h:51
void setRepoInfo(const RepoInfo &repoinfo)
Definition: KeyContext.h:19
ZyppContextRefType _zyppContext
Definition: keyringwf.cc:347
typename remove_smart_ptr< T >::type remove_smart_ptr_t
Definition: type_traits.h:133
std::string shortFile() const
Short name for file (default: basename).