libzypp 17.38.15
keyring_p.cc
Go to the documentation of this file.
1/*---------------------------------------------------------------------\
2| ____ _ __ __ ___ |
3| |__ / \ / / . \ . \ |
4| / / \ V /| _/ _/ |
5| / /__ | | | | | | |
6| /_____||_| |_| |_| |
7| |
8\---------------------------------------------------------------------*/
12
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
23#include <zypp/ZYppFactory.h>
24#include <zypp/ZYpp.h>
25
31#include <zypp-core/fs/WatchFile>
37
38using std::endl;
39
40#undef ZYPP_BASE_LOGGER_LOGGROUP
41#define ZYPP_BASE_LOGGER_LOGGROUP "zypp::KeyRing"
42
44namespace zypp
45{
47 : _cache { cache_r }
48 , _keyring { std::move(keyring_r) }
49 {}
50
52 if ( not _context ) {
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 : _source( KeyManagerCtx::createForOpenPGP( source ) )
114 , _target( keyRing.keyRingManip( target ) )
115 {}
116
118 : SendKeys( keyRing, keyRing.keyRingPath( source ), keyRing.keyRingPath( target ) )
119 {}
120
121 void KeyRingImpl::SendKeys::operator()( const std::string & id )
122 {
123 ByteArray keydata;
124 if ( ! _source.exportKey( id, keydata ) )
125 ZYPP_THROW(KeyRingException(_("Failed to export key.")));
126
127 if ( ! _target.keyManagerCtx().importKey( keydata ) )
128 ZYPP_THROW(KeyRingException(_("Failed to import key.")));
129 }
130
131
133 : _trusted_tmp_dir( baseTmpDir, "zypp-trusted-kr" )
134 , _general_tmp_dir( baseTmpDir, "zypp-general-kr" )
135 , _base_dir( baseTmpDir )
136 {
137 }
138
139
140 void KeyRingImpl::importKey( const PublicKey & key, const Ring ring )
141 {
142 if ( not key.isValid() ) {
143 pDBG( "Import empty key to", ring, "skipped" );
144 return;
145 }
146 // bsc#1259706: When importing keys to the general ring also update
147 // renewed trusted keys. Previously this was done only if the renewed
148 // trusted key was used to sign repo metadata.
149 if ( ring == Ring::Trusted )
150 {
151 auto myMustUpdateData = [this]( std::string_view prefix, const PublicKeyData & keyData ) -> bool {
152 MustUpdate fate = this->mustUpdateData( keyData, Ring::Trusted );
153 pMIL( prefix, fate, keyData, "to", Ring::Trusted );
154 if ( fate == MustUpdate::Present ) {
155 return false; // already in Ring::Trusted
156 }
157 return true; // new or update in Ring::Trusted
158 };
159
160 bool mustUpdate = myMustUpdateData( "Import Tkey", key.keyData() );
161 for ( const PublicKeyData & hkeyData : key.hiddenKeys() ) {
162 mustUpdate |= myMustUpdateData( " ", hkeyData );
163 }
164
165 if ( mustUpdate ) {
166 importKey( key.path(), keyRingPath( Ring::Trusted ) ); // this imports all in the file
167
168 if ( key.hiddenKeys().empty() ) {
169 _sigTrustedKeyAdded.emit( key );
170 } else {
171 // multiple keys: Export individual keys ascii armored to import in rpmdb
172 _sigTrustedKeyAdded.emit( exportKey( key, Ring::Trusted ) );
173 for ( const PublicKeyData & hkey : key.hiddenKeys() )
174 _sigTrustedKeyAdded.emit( exportKey( hkey, Ring::Trusted ) );
175 }
176 }
177 }
178 else // ring == Ring::General
179 {
180 // If all included keydata (incl. hidden keys) are already in the ring
181 // we can skip an update. Otherwise import the key into the general ring.
182 // On the fly check for each key whether it updates a trusted key and remember
183 // the id for a later update. To have proper data in the log, we report
184 // the fate of all hidden keys.
185 std::vector<PublicKeyData> trustedToUpdate;
186 auto myMustUpdateData = [this,&trustedToUpdate]( std::string_view prefix, const PublicKeyData & keyData ) -> bool {
187 MustUpdate fate = this->mustUpdateData( keyData, Ring::General );
188 if ( fate == MustUpdate::Present ) {
189 pMIL( prefix, fate, keyData, "to", Ring::General );
190 return false; // already in Ring::General
191 } else if ( this->mustUpdateData( keyData, Ring::Trusted ) == MustUpdate::Update ) {
192 pMIL( prefix, "U", keyData, "to", Ring::General ); // and later to trusted
193 trustedToUpdate.push_back( keyData );
194 } else {
195 pMIL( prefix, fate, keyData, "to", Ring::General );
196 }
197 return true; // new or update in Ring::General
198 };
199
200 bool mustUpdate = myMustUpdateData( "Import Gkey", key.keyData() );
201 for ( const PublicKeyData & hkeyData : key.hiddenKeys() ) {
202 mustUpdate |= myMustUpdateData( " ", hkeyData );
203 }
204
205 if ( mustUpdate ) {
206 importKey( key.path(), keyRingPath( Ring::General ) ); // this imports all in the file
207 SendKeys toTrusted { *this, Ring::General, Ring::Trusted };
208 for ( const PublicKeyData & keyData : trustedToUpdate ) {
209 // transfer the individual keys only! Never the complete file.
210 toTrusted( keyData.id() );
211 }
212 }
213 }
214 }
215
216 // TODO: (ma) check for a workflow where one context imports multiple keys.
217 void KeyRingImpl::importKeys( const std::list<PublicKey> & keys, const Ring ring )
218 {
219 pDBG( "Import", keys.size(), "keys to", ring );
220 for ( const PublicKey & key : keys )
221 importKey( key, ring );
222 }
223
224 void KeyRingImpl::multiKeyImport( const Pathname & keyfile_r, const Ring ring )
225 {
226 importKey( keyfile_r, keyRingPath( ring ) );
227 }
228
229
230 void KeyRingImpl::deleteKey( const std::string & id, const Ring ring )
231 {
232 PublicKeyData keyDataToDel( publicKeyData( id, ring ) );
233 if ( ! keyDataToDel )
234 {
235 WAR << "Key to delete [" << id << "] is not in " << ring << endl;
236 return;
237 }
238
239 deleteKey( id, keyRingPath( ring ) );
240 MIL << "Deleted key [" << id << "] from " << ring << endl;
241
242 if ( ring == Ring::Trusted ) {
243 _sigTrustedKeyAdded.emit ( PublicKey( keyDataToDel ) );
244 }
245 }
246
247 void KeyRingImpl::importKey( const Pathname & keyfile, const Pathname & keyring )
248 {
249 if ( ! PathInfo( keyfile ).isExist() )
250 // TranslatorExplanation first %s is key name, second is keyring name
251 ZYPP_THROW(KeyRingException( str::Format(_("Tried to import not existent key %s into keyring %s"))
252 % keyfile.asString()
253 % keyring.asString() ));
254
255 CachedPublicKeyData::Manip manip { keyRingManip( keyring ) }; // Provides the context if we want to manip a cached keyring.
256 if ( ! manip.keyManagerCtx().importKey( keyfile ) )
257 ZYPP_THROW(KeyRingException(_("Failed to import key.")));
258 }
259
261 {
262 return PublicKey( dumpPublicKeyToTmp( keyData.id(), keyring ), keyData );
263 }
264
265 // TODO: (ma) not obvious why "exportKey id" checks validity, but "exportKey keyData" does not
266 PublicKey KeyRingImpl::exportKey( const std::string & id, const Pathname & keyring ) const
267 {
268 PublicKeyData keyData( publicKeyData( id, keyring ) );
269 if ( keyData )
270 return PublicKey( dumpPublicKeyToTmp( keyData.id(), keyring ), keyData );
271
272 // Here: key not found
273 WAR << "No key [" << id << "] to export from " << keyring << endl;
274 return PublicKey();
275 }
276
277 void KeyRingImpl::deleteKey( const std::string & id, const Pathname & keyring )
278 {
279 CachedPublicKeyData::Manip manip { keyRingManip( keyring ) }; // Provides the context if we want to manip a cached keyring.
280 if ( ! manip.keyManagerCtx().deleteKey( id ) )
281 ZYPP_THROW(KeyRingException(_("Failed to delete key.")));
282 }
283
284
285 PublicKeyData KeyRingImpl::publicKeyData( const std::string & id, const Pathname & keyring ) const
286 {
287 PublicKeyData ret;
288 for ( const PublicKeyData & key : publicKeyData( keyring ) )
289 {
290 if ( key.providesKey( id ) )
291 {
292 ret = key;
293 break;
294 }
295 }
296 //DBG << (ret ? "Found" : "No") << " key [" << id << "] in keyring " << keyring << endl;
297 return ret;
298 }
299
300 std::list<PublicKey> KeyRingImpl::publicKeys( const Pathname & keyring ) const
301 {
302 const std::list<PublicKeyData> & keys( publicKeyData( keyring ) );
303 std::list<PublicKey> ret;
304
305 for ( const PublicKeyData& keyData : keys )
306 {
307 PublicKey key( exportKey( keyData, keyring ) );
308 ret.push_back( key );
309 MIL << "Found key " << key << endl;
310 }
311 return ret;
312 }
313
314
315 void KeyRingImpl::dumpPublicKey( const std::string & id, const Pathname & keyring, std::ostream & stream ) const
316 {
318 }
319
321 {
322 filesystem::TmpFile tmpFile( _base_dir, "pubkey-"+id+"-" );
323 MIL << "Going to export key [" << id << "] from " << keyring << " to " << tmpFile.path() << endl;
324
325 std::ofstream os( tmpFile.path().c_str() );
326 dumpPublicKey( id, keyring, os );
327 os.close();
328 return tmpFile;
329 }
330
331 std::string KeyRingImpl::readSignatureKeyId( const Pathname & signature )
332 {
333 if ( ! PathInfo( signature ).isFile() )
334 ZYPP_THROW(KeyRingException( str::Format(_("Signature file %s not found")) % signature.asString() ));
335
336 MIL << "Determining key id of signature " << signature << endl;
337
338 std::list<std::string> fprs = KeyManagerCtx::createForOpenPGP().readSignatureFingerprints( signature );
339 if ( ! fprs.empty() ) {
340 std::string &id = fprs.back();
341 MIL << "Determined key id [" << id << "] for signature " << signature << endl;
342 return id;
343 }
344 return std::string();
345 }
346
347 bool KeyRingImpl::verifyFile( const Pathname & file, const Pathname & signature, const Pathname & keyring )
348 { return KeyManagerCtx::createForOpenPGP( keyring ).verify( file, signature ); }
349
350
352 {
353 BLOCKTRACE("preloadCachedKeys");
354 // For now just load the 'gpg-pubkey-*.{asc,key}' files into the general keyring.
355 // TODO: Head for a persistent general keyring.
356 std::set<Pathname> cachedirs;
357 ZConfig & conf { ZConfig::instance() };
358 cachedirs.insert( conf.pubkeyCachePath() );
359 cachedirs.insert( "/usr/lib/rpm/gnupg/keys" );
360 if ( Pathname r = conf.systemRoot(); r != "/" && not r.empty() ) {
361 cachedirs.insert( r / conf.pubkeyCachePath() );
362 cachedirs.insert( r / "/usr/lib/rpm/gnupg/keys" );
363 }
364 if ( Pathname r = conf.repoManagerRoot(); r != "/" && not r.empty() ) {
365 cachedirs.insert( r / conf.pubkeyCachePath() );
366 cachedirs.insert( r / "/usr/lib/rpm/gnupg/keys" );
367 }
368
369 // We load all the matching files. Although the key-id embedded in the filename
370 // suggests there's just one key inside, this must not be true. There may be
371 // more keys hidden in the file (see PublicKey::hiddenKeys). And more important,
372 // there my be trusted keys with with an extended lifetime. They need to be updated
373 // on the fly in the trusteddb.
374 std::list<PublicKey> newkeys;
375 for ( const auto & cache : cachedirs ) {
376 dirForEach( cache,
377 [&newkeys]( const Pathname & dir_r, const char *const file_r )->bool {
378 static const str::regex rx { "^gpg-pubkey-([[:xdigit:]]{8,})(-[[:xdigit:]]{8,})?\\.(asc|key)$" };
379 str::smatch what;
380 if ( str::regex_match( file_r, what, rx ) ) {
381 newkeys.push_back( PublicKey( dir_r / file_r ) );
382 }
383 return true;
384 }
385 );
386 }
387 if ( not newkeys.empty() ) {
388 MIL << "Preload cached keys..." << endl;
389 importKeys( newkeys, Ring::General );
390 }
391 }
392
393} // namespace zypp
394
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition Exception.h:459
#define _(MSG)
Definition Gettext.h:39
#define pMIL
Definition LogTools.h:315
#define pDBG
Definition LogTools.h:314
#define MIL
Definition Logger.h:130
#define WAR
Definition Logger.h:131
#define BLOCKTRACE(M)
Definition Logger.h:51
bool exportKey(const std::string &id, std::ostream &stream)
Exports the key with id into the given stream, returns true on success.
std::list< PublicKeyData > listKeys()
Returns a list of all public keys found in the current keyring.
bool verify(const Pathname &file, const Pathname &signature)
Tries to verify file using signature, returns true on success.
static KeyManagerCtx createForOpenPGP()
Creates a new KeyManagerCtx for PGP using a volatile temp.
std::list< std::string > readSignatureFingerprints(const Pathname &signature)
Reads all fingerprints from the signature file , returns a list of all found fingerprints.
bool deleteKey(const std::string &id)
Tries to delete a key specified by id, returns true on success.
bool importKey(const Pathname &keyfile)
Tries to import a key from keyfile, returns true on success.
KeyRingException()
Ctor taking message.
Helper for in-memory key transfer between two keyrings.
Definition keyring_p.h:184
void operator()(const std::string &id)
Transfer key with id from source to target keyring.
Definition keyring_p.cc:121
SendKeys(KeyRingImpl &keyRing, const Pathname &source, const Pathname &target)
Ctor creates the context for reading source and writing target.
Definition keyring_p.cc:112
CachedPublicKeyData::Manip _target
Definition keyring_p.h:202
zyppng::Signal< void(const PublicKey &)> _sigTrustedKeyAdded
Definition keyring_p.h:257
std::list< PublicKey > publicKeys(const Ring ring) const
Definition keyring_p.h:177
@ Update
old version of Key is in Ring
Definition keyring_p.h:146
void importKey(const PublicKey &key, const Ring ring)
Import PublicKeys into a Ring.
Definition keyring_p.cc:140
filesystem::TmpFile dumpPublicKeyToTmp(const std::string &id, const Pathname &keyring) const
Definition keyring_p.cc:320
MustUpdate mustUpdateData(const PublicKeyData &keyData, const Ring ring) const
Helper computing PublicKeyData's status in a Ring.
Definition keyring_p.h:149
filesystem::TmpDir _general_tmp_dir
Definition keyring_p.h:246
bool verifyFile(const Pathname &file, const Pathname &signature, const Ring ring)
Definition keyring_p.h:130
KeyRingImpl(const Pathname &baseTmpDir)
Definition keyring_p.cc:132
PublicKeyData publicKeyData(const std::string &id, const Ring ring) const
Definition keyring_p.h:167
void preloadCachedKeys()
Load key files cached on the system into the generalKeyRing.
Definition keyring_p.cc:351
void multiKeyImport(const Pathname &keyfile_r, const Ring ring)
Used by RpmDB to import the trusted keys.
Definition keyring_p.cc:224
const Pathname keyRingPath(const Ring ring) const
Definition keyring_p.h:206
void deleteKey(const std::string &id, const Ring ring)
Definition keyring_p.cc:230
PublicKey exportKey(const std::string &id, const Ring ring) const
Definition keyring_p.h:118
Pathname _base_dir
Definition keyring_p.h:247
std::string readSignatureKeyId(const Pathname &signature)
Definition keyring_p.cc:331
void importKeys(const std::list< PublicKey > &keys, const Ring ring)
Definition keyring_p.cc:217
CachedPublicKeyData::Manip keyRingManip(const Pathname &keyring)
Impl helper providing on demand a KeyManagerCtx to manip a cached keyring.
Definition keyring_p.h:240
void dumpPublicKey(const std::string &id, const Ring ring, std::ostream &stream)
Definition keyring_p.h:127
filesystem::TmpDir _trusted_tmp_dir
Definition keyring_p.h:245
Class representing one GPG Public Keys data.
Definition PublicKey.h:201
std::string id() const
Key ID.
Definition PublicKey.cc:412
Class representing one GPG Public Key (PublicKeyData + ASCII armored in a tempfile).
Definition PublicKey.h:378
Pathname path() const
File containing the ASCII armored key.
Definition PublicKey.cc:643
PublicKey()
Default ctor.
Definition PublicKey.cc:614
const std::list< PublicKeyData > & hiddenKeys() const
Additional keys data in case the ASCII armored blob contains multiple keys.
Definition PublicKey.cc:646
const PublicKeyData & keyData() const
The public keys data (.
Definition PublicKey.cc:640
bool isValid() const
Definition PublicKey.h:416
WatchFile(const Pathname &path_r=Pathname(), Initial mode=INIT)
Definition watchfile.h:56
Interim helper class to collect global options and settings.
Definition ZConfig.h:82
Pathname repoManagerRoot() const
The RepoManager root directory.
Definition ZConfig.cc:834
Pathname systemRoot() const
The target root directory.
Definition ZConfig.cc:831
static ZConfig & instance()
Singleton ctor.
Definition ZConfig.cc:794
Pathname pubkeyCachePath() const
Path where the pubkey caches.
Definition ZConfig.cc:928
const char * c_str() const
String representation.
Definition Pathname.h:113
const std::string & asString() const
String representation.
Definition Pathname.h:94
bool empty() const
Test for an empty path.
Definition Pathname.h:117
Provide a new empty temporary file and delete it when no longer needed.
Definition TmpPath.h:118
Pathname path() const
Definition TmpPath.cc:124
Regular expression.
Definition Regex.h:95
Regular expression match result.
Definition Regex.h:168
bool regex_match(const char *s, smatch &matches, const regex &regex) ZYPP_API
Regular expression matching.
Definition Regex.cc:80
Definition ansi.h:855
Easy-to use interface to the ZYPP dependency resolver.
scoped_ptr< WatchFile > _keyringP
Definition keyring_p.h:75
void assertCache(const Pathname &keyring_r)
Definition keyring_p.cc:70
std::list< PublicKeyData > _data
Definition keyring_p.h:70
scoped_ptr< WatchFile > _keyringK
Definition keyring_p.h:74
Helper providing on demand a KeyManagerCtx to manip the cached keyring.
Definition keyring_p.h:44
std::optional< KeyManagerCtx > _context
Definition keyring_p.h:52
KeyManagerCtx & keyManagerCtx()
Definition keyring_p.cc:51
Manip(CachedPublicKeyData &cache_r, Pathname keyring_r)
Definition keyring_p.cc:46
CachedPublicKeyData & _cache
Definition keyring_p.h:50
Functor returning the keyrings data (cached).
Definition keyring_p.h:33
void setDirty(const Pathname &keyring_r)
Definition keyring_p.cc:89
const std::list< PublicKeyData > & operator()(const Pathname &keyring_r) const
Definition keyring_p.cc:86
const std::list< PublicKeyData > & getData(const Pathname &keyring_r) const
Definition keyring_p.cc:94
Manip manip(Pathname keyring_r)
Helper providing on demand a KeyManagerCtx to manip the cached keyring.
Definition keyring_p.cc:92
Convenient building of std::string with boost::format.
Definition String.h:254