libzypp  17.38.7
keyring_p.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
13 #include "keyring_p.h"
14 #include "zypp/ZConfig.h"
15 
16 #include <iostream>
17 #include <fstream>
18 #include <optional>
19 #include <sys/file.h>
20 #include <unistd.h>
21 
22 #include <zypp-core/fs/TmpPath.h>
23 #include <zypp/ZYppFactory.h>
24 #include <zypp/ZYpp.h>
25 
28 #include <zypp-core/base/String.h>
29 #include <zypp-core/base/Regex.h>
30 #include <zypp-core/base/Gettext.h>
31 #include <zypp-core/fs/WatchFile>
32 #include <zypp-core/fs/PathInfo.h>
34 #include <zypp-core/fs/TmpPath.h>
35 #include <zypp-common/KeyManager.h>
37 
38 using std::endl;
39 
40 #undef ZYPP_BASE_LOGGER_LOGGROUP
41 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::KeyRing"
42 
44 namespace zypp
45 {
47  : _cache { cache_r }
48  , _keyring { std::move(keyring_r) }
49  {}
50 
52  if ( not _context ) {
53  _context = KeyManagerCtx::createForOpenPGP( _keyring );
54  }
55  // frankly: don't remember why an explicit setDirty was introduced and
56  // why WatchFile was not enough. Maybe some corner case when the keyrings
57  // are created?
58  _cache.setDirty( _keyring );
59  return _context.value();
60  }
61 
63 
65  {
66  _keyringK.reset();
67  _keyringP.reset();
68  }
69 
71  {
72  // .kbx since gpg2-2.1
73  if ( !_keyringK )
74  _keyringK.reset( new WatchFile( keyring_r/"pubring.kbx", WatchFile::NO_INIT ) );
75  if ( !_keyringP )
76  _keyringP.reset( new WatchFile( keyring_r/"pubring.gpg", WatchFile::NO_INIT ) );
77  }
78 
80  {
81  bool k = _keyringK->hasChanged(); // be sure both files are checked
82  bool p = _keyringP->hasChanged();
83  return k || p;
84  }
85 
86  const std::list<PublicKeyData> &CachedPublicKeyData::operator()(const filesystem::Pathname &keyring_r) const
87  { return getData( keyring_r ); }
88 
90  { _cacheMap[keyring_r].setDirty(); }
91 
92  CachedPublicKeyData::Manip CachedPublicKeyData::manip(filesystem::Pathname keyring_r) { return Manip( *this, std::move(keyring_r) ); }
93 
94  const std::list<PublicKeyData> &CachedPublicKeyData::getData(const filesystem::Pathname &keyring_r) const
95  {
96  Cache & cache( _cacheMap[keyring_r] );
97  // init new cache entry
98  cache.assertCache( keyring_r );
99  return getData( keyring_r, cache );
100  }
101 
102  const std::list<PublicKeyData> &CachedPublicKeyData::getData(const filesystem::Pathname &keyring_r, Cache &cache_r) const
103  {
104  if ( cache_r.hasChanged() ) {
105  cache_r._data = KeyManagerCtx::createForOpenPGP( keyring_r ).listKeys();
106  pMIL( keyring_r, cache_r._data );
107  }
108  return cache_r._data;
109  }
110 
111 
113  : _trusted_tmp_dir( baseTmpDir, "zypp-trusted-kr" )
114  , _general_tmp_dir( baseTmpDir, "zypp-general-kr" )
115  , _base_dir( baseTmpDir )
116  {
117  }
118 
119 
120  void KeyRingImpl::importKey( const PublicKey & key, const Ring ring )
121  {
122  if ( not key.isValid() ) {
123  pDBG( "Import empty key to", ring, "skipped" );
124  return;
125  }
126  // bsc#1259706: When importing keys to the general ring also update
127  // renewed trusted keys. Previously this was done only if the renewed
128  // trusted key was used to sign repo metadata.
129  if ( ring == Ring::Trusted )
130  {
131  auto myMustUpdateData = [this]( std::string_view prefix, const PublicKeyData & keyData ) -> bool {
132  MustUpdate fate = this->mustUpdateData( keyData, Ring::Trusted );
133  pMIL( prefix, fate, keyData, "to", Ring::Trusted );
134  if ( fate == MustUpdate::Present ) {
135  return false; // already in Ring::Trusted
136  }
137  return true; // new or update in Ring::Trusted
138  };
139 
140  bool mustUpdate = myMustUpdateData( "Import Tkey", key.keyData() );
141  for ( const PublicKeyData & hkeyData : key.hiddenKeys() ) {
142  mustUpdate |= myMustUpdateData( " ", hkeyData );
143  }
144 
145  if ( mustUpdate ) {
146  importKey( key.path(), keyRingPath( Ring::Trusted ) ); // this imports all in the file
147 
148  if ( key.hiddenKeys().empty() ) {
149  _sigTrustedKeyAdded.emit( key );
150  } else {
151  // multiple keys: Export individual keys ascii armored to import in rpmdb
153  for ( const PublicKeyData & hkey : key.hiddenKeys() )
155  }
156  }
157  }
158  else // ring == Ring::General
159  {
160  // If all included keydata (incl. hidden keys) are already in the ring
161  // we can skip an update. Otherwise import the key into the general ring.
162  // On the fly check for each key whether it updates a trusted key and remember
163  // the id for a later update. To have proper data in the log, we report
164  // the fate of all hidden keys.
165  std::vector<PublicKeyData> trustedToUpdate;
166  auto myMustUpdateData = [this,&trustedToUpdate]( std::string_view prefix, const PublicKeyData & keyData ) -> bool {
167  MustUpdate fate = this->mustUpdateData( keyData, Ring::General );
168  if ( fate == MustUpdate::Present ) {
169  pMIL( prefix, fate, keyData, "to", Ring::General );
170  return false; // already in Ring::General
171  } else if ( this->mustUpdateData( keyData, Ring::Trusted ) == MustUpdate::Update ) {
172  pMIL( prefix, "U", keyData, "to", Ring::General ); // and later to trusted
173  trustedToUpdate.push_back( keyData );
174  } else {
175  pMIL( prefix, fate, keyData, "to", Ring::General );
176  }
177  return true; // new or update in Ring::General
178  };
179 
180  bool mustUpdate = myMustUpdateData( "Import Gkey", key.keyData() );
181  for ( const PublicKeyData & hkeyData : key.hiddenKeys() ) {
182  mustUpdate |= myMustUpdateData( " ", hkeyData );
183  }
184 
185  if ( mustUpdate ) {
186  importKey( key.path(), keyRingPath( Ring::General ) ); // this imports all in the file
187  for ( const PublicKeyData & keyData : trustedToUpdate ) {
188  // export-import the individual keys! Never the complete file.
190  }
191  }
192  }
193  }
194 
195  // TODO: (ma) check for a workflow where one context imports multiple keys.
196  void KeyRingImpl::importKeys( const std::list<PublicKey> & keys, const Ring ring )
197  {
198  pDBG( "Import", keys.size(), "keys to", ring );
199  for ( const PublicKey & key : keys )
200  importKey( key, ring );
201  }
202 
203  void KeyRingImpl::multiKeyImport( const Pathname & keyfile_r, const Ring ring )
204  {
205  importKey( keyfile_r, keyRingPath( ring ) );
206  }
207 
208 
209  void KeyRingImpl::deleteKey( const std::string & id, const Ring ring )
210  {
211  PublicKeyData keyDataToDel( publicKeyData( id, ring ) );
212  if ( ! keyDataToDel )
213  {
214  WAR << "Key to delete [" << id << "] is not in " << ring << endl;
215  return;
216  }
217 
218  deleteKey( id, keyRingPath( ring ) );
219  MIL << "Deleted key [" << id << "] from " << ring << endl;
220 
221  if ( ring == Ring::Trusted ) {
222  _sigTrustedKeyAdded.emit ( PublicKey( keyDataToDel ) );
223  }
224  }
225 
226  void KeyRingImpl::importKey( const Pathname & keyfile, const Pathname & keyring )
227  {
228  if ( ! PathInfo( keyfile ).isExist() )
229  // TranslatorExplanation first %s is key name, second is keyring name
230  ZYPP_THROW(KeyRingException( str::Format(_("Tried to import not existent key %s into keyring %s"))
231  % keyfile.asString()
232  % keyring.asString() ));
233 
234  CachedPublicKeyData::Manip manip { keyRingManip( keyring ) }; // Provides the context if we want to manip a cached keyring.
235  if ( ! manip.keyManagerCtx().importKey( keyfile ) )
236  ZYPP_THROW(KeyRingException(_("Failed to import key.")));
237  }
238 
239  PublicKey KeyRingImpl::exportKey( const PublicKeyData & keyData, const Pathname & keyring ) const
240  {
241  return PublicKey( dumpPublicKeyToTmp( keyData.id(), keyring ), keyData );
242  }
243 
244  // TODO: (ma) not obvious why "exportKey id" checks validity, but "exportKey keyData" does not
245  PublicKey KeyRingImpl::exportKey( const std::string & id, const Pathname & keyring ) const
246  {
247  PublicKeyData keyData( publicKeyData( id, keyring ) );
248  if ( keyData )
249  return PublicKey( dumpPublicKeyToTmp( keyData.id(), keyring ), keyData );
250 
251  // Here: key not found
252  WAR << "No key [" << id << "] to export from " << keyring << endl;
253  return PublicKey();
254  }
255 
256  void KeyRingImpl::deleteKey( const std::string & id, const Pathname & keyring )
257  {
258  CachedPublicKeyData::Manip manip { keyRingManip( keyring ) }; // Provides the context if we want to manip a cached keyring.
259  if ( ! manip.keyManagerCtx().deleteKey( id ) )
260  ZYPP_THROW(KeyRingException(_("Failed to delete key.")));
261  }
262 
263 
264  PublicKeyData KeyRingImpl::publicKeyData( const std::string & id, const Pathname & keyring ) const
265  {
266  PublicKeyData ret;
267  for ( const PublicKeyData & key : publicKeyData( keyring ) )
268  {
269  if ( key.providesKey( id ) )
270  {
271  ret = key;
272  break;
273  }
274  }
275  //DBG << (ret ? "Found" : "No") << " key [" << id << "] in keyring " << keyring << endl;
276  return ret;
277  }
278 
279  std::list<PublicKey> KeyRingImpl::publicKeys( const Pathname & keyring ) const
280  {
281  const std::list<PublicKeyData> & keys( publicKeyData( keyring ) );
282  std::list<PublicKey> ret;
283 
284  for ( const PublicKeyData& keyData : keys )
285  {
286  PublicKey key( exportKey( keyData, keyring ) );
287  ret.push_back( key );
288  MIL << "Found key " << key << endl;
289  }
290  return ret;
291  }
292 
293 
294  void KeyRingImpl::dumpPublicKey( const std::string & id, const Pathname & keyring, std::ostream & stream ) const
295  {
296  KeyManagerCtx::createForOpenPGP( keyring ).exportKey(id, stream);
297  }
298 
299  filesystem::TmpFile KeyRingImpl::dumpPublicKeyToTmp( const std::string & id, const Pathname & keyring ) const
300  {
301  filesystem::TmpFile tmpFile( _base_dir, "pubkey-"+id+"-" );
302  MIL << "Going to export key [" << id << "] from " << keyring << " to " << tmpFile.path() << endl;
303 
304  std::ofstream os( tmpFile.path().c_str() );
305  dumpPublicKey( id, keyring, os );
306  os.close();
307  return tmpFile;
308  }
309 
310  std::string KeyRingImpl::readSignatureKeyId( const Pathname & signature )
311  {
312  if ( ! PathInfo( signature ).isFile() )
313  ZYPP_THROW(KeyRingException( str::Format(_("Signature file %s not found")) % signature.asString() ));
314 
315  MIL << "Determining key id of signature " << signature << endl;
316 
317  std::list<std::string> fprs = KeyManagerCtx::createForOpenPGP().readSignatureFingerprints( signature );
318  if ( ! fprs.empty() ) {
319  std::string &id = fprs.back();
320  MIL << "Determined key id [" << id << "] for signature " << signature << endl;
321  return id;
322  }
323  return std::string();
324  }
325 
326  bool KeyRingImpl::verifyFile( const Pathname & file, const Pathname & signature, const Pathname & keyring )
327  { return KeyManagerCtx::createForOpenPGP( keyring ).verify( file, signature ); }
328 
329 
331  {
332  // For now just load the 'gpg-pubkey-*.{asc,key}' files into the general keyring.
333  // TODO: Head for a persistent general keyring.
334  std::set<Pathname> cachedirs;
335  ZConfig & conf { ZConfig::instance() };
336  cachedirs.insert( conf.pubkeyCachePath() );
337  cachedirs.insert( "/usr/lib/rpm/gnupg/keys" );
338  if ( Pathname r = conf.systemRoot(); r != "/" && not r.empty() ) {
339  cachedirs.insert( r / conf.pubkeyCachePath() );
340  cachedirs.insert( r / "/usr/lib/rpm/gnupg/keys" );
341  }
342  if ( Pathname r = conf.repoManagerRoot(); r != "/" && not r.empty() ) {
343  cachedirs.insert( r / conf.pubkeyCachePath() );
344  cachedirs.insert( r / "/usr/lib/rpm/gnupg/keys" );
345  }
346 
347  // We load all the matching files. Although the key-id embedded in the filename
348  // suggests there's just one key inside, this must not be true. There may be
349  // more keys hidden in the file (see PublicKey::hiddenKeys). And more important,
350  // there my be trusted keys with with an extended lifetime. They need to be updated
351  // on the fly in the trusteddb.
352  std::list<PublicKey> newkeys;
353  for ( const auto & cache : cachedirs ) {
354  dirForEach( cache,
355  [&newkeys]( const Pathname & dir_r, const char *const file_r )->bool {
356  static const str::regex rx { "^gpg-pubkey-([[:xdigit:]]{8,})(-[[:xdigit:]]{8,})?\\.(asc|key)$" };
357  str::smatch what;
358  if ( str::regex_match( file_r, what, rx ) ) {
359  newkeys.push_back( PublicKey( dir_r / file_r ) );
360  }
361  return true;
362  }
363  );
364  }
365  if ( not newkeys.empty() ) {
366  MIL << "Preload cached keys..." << endl;
367  importKeys( newkeys, Ring::General );
368  }
369  }
370 
371 } // namespace zypp
372 
bool verifyFile(const Pathname &file, const Pathname &signature, const Ring ring)
Definition: keyring_p.h:130
#define MIL
Definition: Logger.h:103
Pathname _base_dir
Definition: keyring_p.h:223
const std::list< PublicKeyData > & hiddenKeys() const
Additional keys data in case the ASCII armored blob contains multiple keys.
Definition: PublicKey.cc:643
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:459
const PublicKeyData & keyData() const
The public keys data (.
Definition: PublicKey.cc:637
Regular expression.
Definition: Regex.h:94
Functor returning the keyrings data (cached).
Definition: keyring_p.h:32
static ZConfig & instance()
Singleton ctor.
Definition: ZConfig.cc:756
Manip(CachedPublicKeyData &cache_r, Pathname keyring_r)
Definition: keyring_p.cc:46
Class representing one GPG Public Keys data.
Definition: PublicKey.h:200
void preloadCachedKeys()
Load key files cached on the system into the generalKeyRing.
Definition: keyring_p.cc:330
int dirForEach(const Pathname &dir_r, const StrMatcher &matcher_r, function< bool(const Pathname &, const char *const)> fnc_r)
Definition: PathInfo.cc:32
const char * c_str() const
String representation.
Definition: Pathname.h:113
void importKeys(const std::list< PublicKey > &keys, const Ring ring)
Definition: keyring_p.cc:196
Pathname path() const
Definition: TmpPath.cc:124
Manip manip(Pathname keyring_r)
Helper providing on demand a KeyManagerCtx to manip the cached keyring.
Definition: keyring_p.cc:92
bool verify(const Pathname &file, const Pathname &signature)
Tries to verify file using signature, returns true on success.
Definition: KeyManager.cc:428
Convenient building of std::string with boost::format.
Definition: String.h:253
static KeyManagerCtx createForOpenPGP()
Creates a new KeyManagerCtx for PGP using a volatile temp.
Definition: KeyManager.cc:320
Provide a new empty temporary file and delete it when no longer needed.
Definition: TmpPath.h:117
bool exportKey(const std::string &id, std::ostream &stream)
Exports the key with id into the given stream, returns true on success.
Definition: KeyManager.cc:433
Remember a files attributes to detect content changes.
Definition: watchfile.h:49
std::list< PublicKey > publicKeys(const Ring ring) const
Definition: keyring_p.h:177
zyppng::Signal< void(const PublicKey &)> _sigTrustedKeyAdded
Definition: keyring_p.h:233
bool empty() const
Test for an empty path.
Definition: Pathname.h:117
std::string readSignatureKeyId(const Pathname &signature)
Definition: keyring_p.cc:310
const std::string & asString() const
String representation.
Definition: Pathname.h:94
KeyRingImpl(const Pathname &baseTmpDir)
Definition: keyring_p.cc:112
MustUpdate mustUpdateData(const PublicKeyData &keyData, const Ring ring) const
Helper computing PublicKeyData&#39;s status in a Ring.
Definition: keyring_p.h:149
Interim helper class to collect global options and settings.
Definition: ZConfig.h:81
#define WAR
Definition: Logger.h:104
old version of Key is in Ring
const std::list< PublicKeyData > & getData(const Pathname &keyring_r) const
Definition: keyring_p.cc:94
KeyManagerCtx & keyManagerCtx()
Definition: keyring_p.cc:51
const Pathname keyRingPath(const Ring ring) const
Definition: keyring_p.h:182
void multiKeyImport(const Pathname &keyfile_r, const Ring ring)
Used by RpmDB to import the trusted keys.
Definition: keyring_p.cc:203
filesystem::TmpFile dumpPublicKeyToTmp(const std::string &id, const Pathname &keyring) const
Definition: keyring_p.cc:299
#define _(MSG)
Definition: Gettext.h:39
PublicKeyData publicKeyData(const std::string &id, const Ring ring) const
Definition: keyring_p.h:167
void assertCache(const Pathname &keyring_r)
Definition: keyring_p.cc:70
std::list< PublicKeyData > listKeys()
Returns a list of all public keys found in the current keyring.
Definition: KeyManager.cc:375
#define pMIL
Definition: LogTools.h:305
Regular expression match result.
Definition: Regex.h:167
Class representing one GPG Public Key (PublicKeyData + ASCII armored in a tempfile).
Definition: PublicKey.h:374
Pathname path() const
File containing the ASCII armored key.
Definition: PublicKey.cc:640
std::string id() const
Key ID.
Definition: PublicKey.cc:412
void deleteKey(const std::string &id, const Ring ring)
Definition: keyring_p.cc:209
const std::list< PublicKeyData > & operator()(const Pathname &keyring_r) const
Definition: keyring_p.cc:86
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:225
bool regex_match(const char *s, smatch &matches, const regex &regex) ZYPP_API
Regular expression matching.
Definition: Regex.cc:80
std::list< std::string > readSignatureFingerprints(const Pathname &signature)
Reads all fingerprints from the signature file , returns a list of all found fingerprints.
Definition: KeyManager.cc:588
std::list< PublicKeyData > _data
Definition: keyring_p.h:70
void setDirty(const Pathname &keyring_r)
Definition: keyring_p.cc:89
CachedPublicKeyData::Manip keyRingManip(const Pathname &keyring)
Impl helper providing on demand a KeyManagerCtx to manip a cached keyring.
Definition: keyring_p.h:216
#define pDBG
Definition: LogTools.h:304
bool isValid() const
Definition: PublicKey.h:413
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:1
PublicKey exportKey(const std::string &id, const Ring ring) const
Definition: keyring_p.h:118
void importKey(const PublicKey &key, const Ring ring)
Import PublicKeys into a Ring.
Definition: keyring_p.cc:120
Helper providing on demand a KeyManagerCtx to manip the cached keyring.
Definition: keyring_p.h:44
void dumpPublicKey(const std::string &id, const Ring ring, std::ostream &stream)
Definition: keyring_p.h:127