libzypp 17.38.7
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 : _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
152 _sigTrustedKeyAdded.emit( exportKey( key, Ring::Trusted ) );
153 for ( const PublicKeyData & hkey : key.hiddenKeys() )
154 _sigTrustedKeyAdded.emit( exportKey( hkey, Ring::Trusted ) );
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.
189 importKey( exportKey( keyData, Ring::General ), Ring::Trusted );
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
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 {
297 }
298
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
#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:305
#define pDBG
Definition LogTools.h:304
#define MIL
Definition Logger.h:103
#define WAR
Definition Logger.h:104
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.
zyppng::Signal< void(const PublicKey &)> _sigTrustedKeyAdded
Definition keyring_p.h:233
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:120
filesystem::TmpFile dumpPublicKeyToTmp(const std::string &id, const Pathname &keyring) const
Definition keyring_p.cc:299
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:222
bool verifyFile(const Pathname &file, const Pathname &signature, const Ring ring)
Definition keyring_p.h:130
KeyRingImpl(const Pathname &baseTmpDir)
Definition keyring_p.cc:112
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:330
void multiKeyImport(const Pathname &keyfile_r, const Ring ring)
Used by RpmDB to import the trusted keys.
Definition keyring_p.cc:203
const Pathname keyRingPath(const Ring ring) const
Definition keyring_p.h:182
void deleteKey(const std::string &id, const Ring ring)
Definition keyring_p.cc:209
PublicKey exportKey(const std::string &id, const Ring ring) const
Definition keyring_p.h:118
Pathname _base_dir
Definition keyring_p.h:223
std::string readSignatureKeyId(const Pathname &signature)
Definition keyring_p.cc:310
void importKeys(const std::list< PublicKey > &keys, const Ring ring)
Definition keyring_p.cc:196
CachedPublicKeyData::Manip keyRingManip(const Pathname &keyring)
Impl helper providing on demand a KeyManagerCtx to manip a cached keyring.
Definition keyring_p.h:216
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:221
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:375
Pathname path() const
File containing the ASCII armored key.
Definition PublicKey.cc:640
const std::list< PublicKeyData > & hiddenKeys() const
Additional keys data in case the ASCII armored blob contains multiple keys.
Definition PublicKey.cc:643
const PublicKeyData & keyData() const
The public keys data (.
Definition PublicKey.cc:637
bool isValid() const
Definition PublicKey.h:413
Remember a files attributes to detect content changes.
Definition watchfile.h:50
Interim helper class to collect global options and settings.
Definition ZConfig.h:82
Pathname repoManagerRoot() const
The RepoManager root directory.
Definition ZConfig.cc:796
Pathname systemRoot() const
The target root directory.
Definition ZConfig.cc:793
static ZConfig & instance()
Singleton ctor.
Definition ZConfig.cc:756
Pathname pubkeyCachePath() const
Path where the pubkey caches.
Definition ZConfig.cc:890
Wrapper class for stat/lstat.
Definition PathInfo.h:226
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