libzypp  17.38.7
keyringwf.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
9 
10 #include "keyringwf.h"
12 #include <zypp/RepoInfo.h>
13 #include <zypp/ZConfig.h>
14 #include <zypp-core/Pathname.h>
15 #include <zypp-common/PublicKey.h>
16 
17 #include <zypp-core/base/Gettext.h>
18 #include <utility>
19 #include <zypp-core/ng/pipelines/Expected>
21 #include <zypp/ng/context.h>
22 #include <zypp/ng/reporthelper.h>
23 #include <zypp/ng/UserRequest>
24 
26 
28 
30  using MediaHandle = typename ProvideType::MediaHandle;
31  using ProvideRes = typename ProvideType::Res;
32 
33  public:
34  ImportKeyFromRepoLogic( ContextRef context, std::string &&keyId, zypp::RepoInfo &&info )
35  : _context( std::move(context) ), _keyId(std::move(keyId)), _repo( std::move(info) )
36  { }
37 
38  MaybeAwaitable<bool> execute () {
39 
40  using namespace zyppng::operators;
41  using zyppng::operators::operator|;
42  using zyppng::expected;
43 
44  if ( _keyId.empty() || !_context )
45  return makeReadyTask(false);
46 
47  if ( _context->keyRing()->isKeyKnown(_keyId) ){
48  if ( _context->keyRing()->isKeyTrusted(_keyId) )
49  return makeReadyTask(true);
50 
51  return makeReadyTask(importFromKnownKeyring ());
52  }
53 
54  const zypp::ZConfig &conf = _context->config();
55  zypp::Pathname cacheDir = conf.repoManagerRoot() / conf.pubkeyCachePath();
56 
57  if ( _repo.gpgKeyUrlsEmpty() ) {
58  // translator: %1% is a repositories name
59  JobReportHelper(_context).info( zypp::str::Format(_("Repository %1% does not define 'gpgkey=' URLs.") ) % _repo.asUserString() );
60  }
61 
63  | [this, cacheDir]( expected<void> res ) {
64  if ( !res ) return false;
65  if ( !_context->keyRing()->isKeyKnown(_keyId) )
66  // if we did not find any keys, there is no point in checking again, break
67  return false;
68 
69  return importFromKnownKeyring();
70  };
71  }
72 
74  zypp::PublicKeyData keyData ( _context->keyRing()->publicKeyData(_keyId) );
75  if ( !keyData )
76  return false;
77 
78  zypp::PublicKey key;
79  try {
80  key = zypp::PublicKey( _context->keyRing()->exportPublicKey( keyData ) );
81  } catch ( const zypp::Exception &e ) {
82  ZYPP_CAUGHT(e);
83  return false;
84  }
85 
86  if ( !key.isValid() ) {
87  ERR << "Key [" << _keyId << "] from known keyring is not valid" << std::endl;
88  return false;
89  }
90 
91  MIL << "Key [" << _keyId << "] " << key.name() << " loaded from cache" << std::endl;
92 
93  zypp::KeyContext context;
94  context.setRepoInfo( _repo );
95  if ( !KeyRingReportHelper(_context).askUserToAcceptPackageKey( key, context ) ) {
96  return false;
97  }
98 
99  MIL << "User wants to import key [" << _keyId << "] " << key.name() << " from cache" << std::endl;
100  try {
101  _context->keyRing()->importKey( key, true );
102  } catch ( const zypp::KeyRingException &e ) {
103  ZYPP_CAUGHT(e);
104  ERR << "Failed to import key: "<<_keyId;
105  return false;
106  }
107  return true;
108  }
109 
110  ContextRef _context;
111  std::string _keyId;
113  };
114 
115  MaybeAwaitable<bool> provideAndImportKeyFromRepository( ContextRef ctx, std::string id_r, zypp::RepoInfo info_r)
116  {
117  ImportKeyFromRepoLogic impl( ctx, std::move(id_r), std::move(info_r) );
118  zypp_co_return zypp_co_await( impl.execute () );
119  }
120 
121  namespace {
123 
128  struct VerifyFileSignatureLogic
129  {
130  using ZyppContextRefType = ContextRef;
132 
133  VerifyFileSignatureLogic( ZyppContextRefType zyppContext, KeyRingRef &&keyRing, zypp::keyring::VerifyFileContext &&ctx )
134  : _zyppContext( std::move(zyppContext) )
136  , _keyRing( std::move(keyRing) )
137  , _verifyContext( std::move(ctx) )
138  { }
139 
140  struct FoundKeyData {
143  bool trusted = false;
144  };
145 
146  MaybeAwaitable<FoundKeyData> findKey ( const std::string &id ) {
147 
148  using zyppng::operators::operator|;
149 
150  if ( id.empty() )
151  return makeReadyTask(FoundKeyData());
152 
153  // does key exists in trusted keyring
154  zypp::PublicKeyData trustedKeyData { _keyRing->pimpl().publicKeyData( id, Ring::Trusted ) };
155  if ( trustedKeyData )
156  {
157  MIL << "Key is trusted: " << trustedKeyData << std::endl;
158  return makeReadyTask( FoundKeyData{ trustedKeyData, Ring::Trusted, true } );
159  }
160  else
161  {
162  zypp::PublicKeyData generalKeyData { _keyRing->pimpl().publicKeyData( id, Ring::General ) };
163  if ( generalKeyData )
164  {
165  zypp::PublicKey key( _keyRing->pimpl().exportKey( generalKeyData, Ring::General ) );
166  MIL << "Key [" << id << "] " << key.name() << " is not trusted" << std::endl;
167 
168  // ok the key is not trusted, ask the user to trust it or not
169  zypp::KeyRingReport::KeyTrust reply = _keyringReport.askUserToAcceptKey( key, _verifyContext.keyContext() );
172  {
173  Ring whichKeyring;
174 
175  MIL << "User wants to trust key [" << id << "] " << key.name() << std::endl;
176 
178  {
179  MIL << "User wants to import key [" << id << "] " << key.name() << std::endl;
180  _keyRing->importKey( key, true );
181  whichKeyring = Ring::Trusted;
182  }
183  else
184  whichKeyring = Ring::General;
185 
186  return makeReadyTask(FoundKeyData { std::move(generalKeyData), std::move(whichKeyring), true });
187  }
188  else
189  {
190  MIL << "User does not want to trust key [" << id << "] " << key.name() << std::endl;
191  return makeReadyTask(FoundKeyData { std::move(generalKeyData), Ring::General, false });
192  }
193  }
194  else if ( ! _verifyContext.keyContext().empty() )
195  {
196  // try to find the key in the repository info
198  | [this, id]( bool success ) {
199  if ( !success ) {
200  return FoundKeyData();
201  }
202  return FoundKeyData{ _keyRing->pimpl().publicKeyData( id, Ring::Trusted ), Ring::Trusted, true };
203  };
204  }
205  }
206  return makeReadyTask(FoundKeyData());
207  }
208 
209  // returns std::pair<bool, zypp::keyring::VerifyFileContext>
210  auto execute () {
211 
213  const zypp::Pathname & file { _verifyContext.file() };
214  const zypp::Pathname & signature { _verifyContext.signature() };
215  const std::string & filedesc { _verifyContext.shortFile() };
216 
217  MIL << "Going to verify signature for " << filedesc << " ( " << file << " ) with " << signature << std::endl;
218 
219  // if signature does not exists, ask user if they want to accept unsigned file.
220  if( signature.empty() || (!zypp::PathInfo( signature ).isExist()) )
221  {
222  bool res = _keyringReport.askUserToAcceptUnsignedFile( filedesc, _verifyContext.keyContext() );
223  MIL << "askUserToAcceptUnsignedFile: " << res << std::endl;
224  return makeReadyTask( makeReturn(res) );
225  }
226 
227  // get the id of the signature (it might be a subkey id!)
228  try {
229  _verifyContext.signatureId( _keyRing->readSignatureKeyId( signature ) ); //throws !
230  } catch ( const zypp::Exception &e ) {
231  MIL << "Failed to read the signature from " << signature << std::endl;
232  ZYPP_CAUGHT(e);
233  return makeReadyTask( makeReturn(false) );
234  }
235 
236  const std::string & id = _verifyContext.signatureId();
237 
238  // collect the buddies
239  std::list<zypp::PublicKeyData> buddies; // Could be imported IFF the file is validated by a trusted key
240  for ( const auto & sid : _verifyContext.buddyKeys() ) {
241  if ( not zypp::PublicKeyData::isSafeKeyId( sid ) ) {
242  WAR << "buddy " << sid << ": key id is too short to safely identify a gpg key. Skipping it." << std::endl;
243  continue;
244  }
245  if ( _keyRing->pimpl().publicKeyData( sid, Ring::Trusted ) ) {
246  MIL << "buddy " << sid << ": already in trusted key ring. Not needed." << std::endl;
247  continue;
248  }
249  auto pk = _keyRing->pimpl().publicKeyData( sid, Ring::General );
250  if ( not pk ) {
251  WAR << "buddy " << sid << ": not available in the public key ring. Skipping it." << std::endl;
252  continue;
253  }
254  if ( pk.providesKey(id) ) {
255  MIL << "buddy " << sid << ": is the signing key. Handled separately." << std::endl;
256  continue;
257  }
258  MIL << "buddy " << sid << ": candidate for auto import. Remeber it." << std::endl;
259  buddies.push_back( pk );
260  }
261 
262  using zyppng::operators::operator|;
263  return findKey( id ) | [this, id, buddies=std::move(buddies)]( FoundKeyData res ) {
264 
265  const zypp::Pathname & file { _verifyContext.file() };
266  const zypp::KeyContext & keyContext { _verifyContext.keyContext() };
267  const zypp::Pathname & signature { _verifyContext.signature() };
268  const std::string & filedesc { _verifyContext.shortFile() };
269 
270  if ( res._foundKey ) {
271 
272  // we found a key but it is not trusted ( e.g. user did not want to trust it )
273  if ( !res.trusted )
274  return makeReturn(false);
275 
276  // it exists, is trusted, does it validate?
277  _verifyContext.signatureIdTrusted( res._whichKeyRing == Ring::Trusted );
278  _keyringReport.infoVerify( filedesc, res._foundKey, keyContext );
279  if ( _keyRing->pimpl().verifyFile( file, signature, res._whichKeyRing ) )
280  {
282  if ( _verifyContext.signatureIdTrusted() && not buddies.empty() ) {
283  // Check for buddy keys to be imported...
284  MIL << "Validated with trusted key: importing buddy list..." << std::endl;
285  _keyringReport.reportAutoImportKey( buddies, res._foundKey, keyContext );
286  for ( const auto & kd : buddies ) {
287  _keyRing->importKey( _keyRing->pimpl().exportKey( kd, Ring::General ), true );
288  }
289  }
290  return makeReturn(_verifyContext.fileValidated()); // signature is actually successfully validated!
291  }
292  else
293  {
294  bool userAnswer = _keyringReport.askUserToAcceptVerificationFailed( filedesc, _keyRing->pimpl().exportKey( res._foundKey, res._whichKeyRing ), keyContext );
295  MIL << "askUserToAcceptVerificationFailed: " << userAnswer << std::endl;
296  return makeReturn(userAnswer);
297  }
298  } else {
299  // signed with an unknown key...
300  MIL << "File [" << file << "] ( " << filedesc << " ) signed with unknown key [" << id << "]" << std::endl;
301  bool res = _keyringReport.askUserToAcceptUnknownKey( filedesc, id, _verifyContext.keyContext() );
302  MIL << "askUserToAcceptUnknownKey: " << res << std::endl;
303  return makeReturn(res);
304  }
305 
306  return makeReturn(false);
307  };
308  }
309 
310  protected:
311  ZyppContextRefType _zyppContext;
312  KeyRingReportHelper _keyringReport;
315 
316  private:
317  inline std::pair<bool, zypp::keyring::VerifyFileContext> makeReturn( bool res ){
319  return std::make_pair( res, std::move(_verifyContext) ) ;
320  }
321  };
322  }
323 
324  MaybeAwaitable<std::pair<bool,zypp::keyring::VerifyFileContext>> verifyFileSignature( ContextRef zyppContext, zypp::keyring::VerifyFileContext &&context_r )
325  {
326  auto kr = zyppContext->keyRing();
327  VerifyFileSignatureLogic impl( std::move(zyppContext), std::move(kr), std::move(context_r) );
328  zypp_co_return zypp_co_await( impl.execute () );
329  }
330 
331  MaybeAwaitable<std::pair<bool,zypp::keyring::VerifyFileContext>> verifyFileSignature(ContextRef zyppContext, zypp::KeyRing_Ptr keyRing, zypp::keyring::VerifyFileContext &&context_r )
332  {
333  VerifyFileSignatureLogic impl( std::move(zyppContext), std::move(keyRing), std::move(context_r) );
334  zypp_co_return zypp_co_await( impl.execute () );
335  }
336 
337 }
bool fileValidated() const
Whether the signature was actually successfully verified.
#define MIL
Definition: Logger.h:103
static bool isSafeKeyId(const std::string &id_r)
Whether this is a long id (64bit/16byte) or even better a fingerprint.
Definition: PublicKey.h:318
zypp::PublicKeyData _foundKey
The key we found or false.
Definition: keyringwf.cc:141
Provide ProvideType
Definition: context.h:34
MaybeAwaitable< bool > provideAndImportKeyFromRepository(ContextRef 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:115
std::string name() const
Definition: PublicKey.cc:660
MaybeAwaitable< expected< void > > fetchGpgKeys(ContextRef ctx, zypp::RepoInfo info)
Definition: repoinfowf.cc:131
const std::string & signatureId() const
The id of the gpg key which signed the file.
zypp::keyring::VerifyFileContext _verifyContext
Definition: keyringwf.cc:314
This basically means, we knew the key, but it was not trusted.
Definition: KeyRing.h:63
Class representing one GPG Public Keys data.
Definition: PublicKey.h:200
Pathname pubkeyCachePath() const
Path where the pubkey caches.
Definition: ZConfig.cc:890
Definition: ansi.h:854
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
Convenient building of std::string with boost::format.
Definition: String.h:253
#define ERR
Definition: Logger.h:105
const KeyContext & keyContext() const
KeyContext passed to callbacks
zypp::KeyRing_Ptr KeyRingRef
Definition: context.h:25
bool signatureIdTrusted() const
Whether the SignatureId is in the trusted keyring (not temp.
void resetResults()
Reset all result values to safe defaults.
Ring _whichKeyRing
The keyring in which we found the key. Valid only if the key is valid.
Definition: keyringwf.cc:142
Pathname repoManagerRoot() const
The RepoManager root directory.
Definition: ZConfig.cc:796
KeyTrust
User reply options for the askUserToTrustKey callback.
Definition: KeyRing.h:53
bool trusted
Whether the key may validate data. Keys in Ring::General may if the user temp. trusted.
Definition: keyringwf.cc:143
const Pathname & signature() const
Detached signature or empty.
bool gpgKeyUrlsEmpty() const
Whether gpgkey URLs are defined.
Definition: RepoInfo.cc:800
typename Context::ProvideType ProvideType
Definition: keyringwf.cc:29
KeyRingRef _keyRing
Definition: keyringwf.cc:313
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:81
#define WAR
Definition: Logger.h:104
KeyRingReportHelper _keyringReport
Definition: keyringwf.cc:312
#define _(MSG)
Definition: Gettext.h:39
const Pathname & file() const
File to verify.
const BuddyKeys & buddyKeys() const
bool info(std::string msg_r, UserData userData_r=UserData())
send message text
#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
Class representing one GPG Public Key (PublicKeyData + ASCII armored in a tempfile).
Definition: PublicKey.h:374
Base class for Exception.
Definition: Exception.h:152
bool empty() const
Is the context unknown?
Definition: KeyContext.h:15
ImportKeyFromRepoLogic(ContextRef context, std::string &&keyId, zypp::RepoInfo &&info)
Definition: keyringwf.cc:34
KeyRingImpl::Ring Ring
Definition: KeyRing.cc:172
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:225
typename ProvideType::MediaHandle MediaHandle
Definition: keyringwf.cc:30
bool fileAccepted() const
May return true due to user interaction or global defaults even if the signature was not actually ver...
MaybeAwaitable< std::pair< bool, zypp::keyring::VerifyFileContext > > verifyFileSignature(ContextRef zyppContext, zypp::keyring::VerifyFileContext &&context_r)
Follows a signature verification interacting with the user.
Definition: keyringwf.cc:324
bool isValid() const
Definition: PublicKey.h:413
void setRepoInfo(const RepoInfo &repoinfo)
Definition: KeyContext.h:19
ZyppContextRefType _zyppContext
Definition: keyringwf.cc:311
std::string shortFile() const
Short name for file (default: basename).