libzypp 17.38.3
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>
16
18#include <utility>
19#include <zypp-core/ng/pipelines/Expected>
21#include <zypp/ng/context.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
70 };
71 }
72
74 zypp::PublicKeyData keyData ( _context->keyRing()->publicKeyData(_keyId) );
75 if ( !keyData )
76 return false;
77
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 {
122
127 struct VerifyFileSignatureLogic
128 {
129 using ZyppContextRefType = ContextRef;
130 using KeyTrust = zypp::KeyRingReport::KeyTrust;
131
132 VerifyFileSignatureLogic( ZyppContextRefType zyppContext, KeyRingRef &&keyRing, zypp::keyring::VerifyFileContext &&ctx )
133 : _zyppContext( std::move(zyppContext) )
134 , _keyringReport( _zyppContext )
135 , _keyRing( std::move(keyRing) )
136 , _verifyContext( std::move(ctx) )
137 { }
138
139 struct FoundKeyData {
140 zypp::PublicKeyData _foundKey;
141 zypp::Pathname _whichKeyRing;
142 bool trusted = false;
143 };
144
145 MaybeAwaitable<FoundKeyData> findKey ( const std::string &id ) {
146
147 using zyppng::operators::operator|;
148
149 if ( id.empty() )
150 return makeReadyTask(FoundKeyData{zypp::PublicKeyData(), zypp::Pathname()});
151
152 // does key exists in trusted keyring
153 zypp::PublicKeyData trustedKeyData( _keyRing->pimpl().publicKeyExists( id, _keyRing->pimpl().trustedKeyRing() ) );
154 if ( trustedKeyData )
155 {
156 MIL << "Key is trusted: " << trustedKeyData << std::endl;
157
158 // lets look if there is an updated key in the
159 // general keyring
160 zypp::PublicKeyData generalKeyData( _keyRing->pimpl().publicKeyExists( id, _keyRing->pimpl().generalKeyRing() ) );
161 if ( generalKeyData )
162 {
163 // bnc #393160: Comment #30: Compare at least the fingerprint
164 // in case an attacker created a key the the same id.
165 //
166 // FIXME: bsc#1008325: For keys using subkeys, we'd actually need
167 // to compare the subkey sets, to tell whether a key was updated.
168 // because created() remains unchanged if the primary key is not touched.
169 // For now we wait until a new subkey signs the data and treat it as a
170 // new key (else part below).
171 if ( trustedKeyData.fingerprint() == generalKeyData.fingerprint()
172 && trustedKeyData.created() < generalKeyData.created() )
173 {
174 MIL << "Key was updated. Saving new version into trusted keyring: " << generalKeyData << std::endl;
175 _keyRing->importKey( _keyRing->pimpl().exportKey( generalKeyData, _keyRing->pimpl().generalKeyRing() ), true );
176 trustedKeyData = _keyRing->pimpl().publicKeyExists( id, _keyRing->pimpl().trustedKeyRing() ); // re-read: invalidated by import?
177 }
178 }
179
180 return makeReadyTask( FoundKeyData{ trustedKeyData, _keyRing->pimpl().trustedKeyRing(), true } );
181 }
182 else
183 {
184 zypp::PublicKeyData generalKeyData( _keyRing->pimpl().publicKeyExists( id, _keyRing->pimpl().generalKeyRing() ) );
185 if ( generalKeyData )
186 {
187 zypp::PublicKey key( _keyRing->pimpl().exportKey( generalKeyData, _keyRing->pimpl().generalKeyRing() ) );
188 MIL << "Key [" << id << "] " << key.name() << " is not trusted" << std::endl;
189
190 // ok the key is not trusted, ask the user to trust it or not
191 zypp::KeyRingReport::KeyTrust reply = _keyringReport.askUserToAcceptKey( key, _verifyContext.keyContext() );
194 {
195 zypp::Pathname whichKeyring;
196
197 MIL << "User wants to trust key [" << id << "] " << key.name() << std::endl;
198
200 {
201 MIL << "User wants to import key [" << id << "] " << key.name() << std::endl;
202 _keyRing->importKey( key, true );
203 whichKeyring = _keyRing->pimpl().trustedKeyRing();
204 }
205 else
206 whichKeyring = _keyRing->pimpl().generalKeyRing();
207
208 return makeReadyTask(FoundKeyData { std::move(generalKeyData), std::move(whichKeyring), true });
209 }
210 else
211 {
212 MIL << "User does not want to trust key [" << id << "] " << key.name() << std::endl;
213 return makeReadyTask(FoundKeyData { std::move(generalKeyData), _keyRing->pimpl().generalKeyRing(), false });
214 }
215 }
216 else if ( ! _verifyContext.keyContext().empty() )
217 {
218 // try to find the key in the repository info
219 return provideAndImportKeyFromRepository ( _zyppContext, id, _verifyContext.keyContext().repoInfo() )
220 | [this, id]( bool success ) {
221 if ( !success ) {
222 return FoundKeyData{ zypp::PublicKeyData(), zypp::Pathname() };
223 }
224 return FoundKeyData{ _keyRing->pimpl().publicKeyExists( id, _keyRing->pimpl().trustedKeyRing() ), _keyRing->pimpl().trustedKeyRing(), true };
225 };
226 }
227 }
228 return makeReadyTask(FoundKeyData{ zypp::PublicKeyData(), zypp::Pathname() });
229 }
230
231 // returns std::pair<bool, zypp::keyring::VerifyFileContext>
232 auto execute () {
233
234 _verifyContext.resetResults();
235 const zypp::Pathname & file { _verifyContext.file() };
236 const zypp::Pathname & signature { _verifyContext.signature() };
237 const std::string & filedesc { _verifyContext.shortFile() };
238
239 MIL << "Going to verify signature for " << filedesc << " ( " << file << " ) with " << signature << std::endl;
240
241 // if signature does not exists, ask user if they want to accept unsigned file.
242 if( signature.empty() || (!zypp::PathInfo( signature ).isExist()) )
243 {
244 bool res = _keyringReport.askUserToAcceptUnsignedFile( filedesc, _verifyContext.keyContext() );
245 MIL << "askUserToAcceptUnsignedFile: " << res << std::endl;
246 return makeReadyTask( makeReturn(res) );
247 }
248
249 // get the id of the signature (it might be a subkey id!)
250 try {
251 _verifyContext.signatureId( _keyRing->readSignatureKeyId( signature ) ); //throws !
252 } catch ( const zypp::Exception &e ) {
253 MIL << "Failed to read the signature from " << signature << std::endl;
254 ZYPP_CAUGHT(e);
255 return makeReadyTask( makeReturn(false) );
256 }
257
258 const std::string & id = _verifyContext.signatureId();
259
260 // collect the buddies
261 std::list<zypp::PublicKeyData> buddies; // Could be imported IFF the file is validated by a trusted key
262 for ( const auto & sid : _verifyContext.buddyKeys() ) {
263 if ( not zypp::PublicKeyData::isSafeKeyId( sid ) ) {
264 WAR << "buddy " << sid << ": key id is too short to safely identify a gpg key. Skipping it." << std::endl;
265 continue;
266 }
267 if ( _keyRing->pimpl().trustedPublicKeyExists( sid ) ) {
268 MIL << "buddy " << sid << ": already in trusted key ring. Not needed." << std::endl;
269 continue;
270 }
271 auto pk = _keyRing->pimpl().publicKeyExists( sid );
272 if ( not pk ) {
273 WAR << "buddy " << sid << ": not available in the public key ring. Skipping it." << std::endl;
274 continue;
275 }
276 if ( pk.providesKey(id) ) {
277 MIL << "buddy " << sid << ": is the signing key. Handled separately." << std::endl;
278 continue;
279 }
280 MIL << "buddy " << sid << ": candidate for auto import. Remeber it." << std::endl;
281 buddies.push_back( pk );
282 }
283
284 using zyppng::operators::operator|;
285 return findKey( id ) | [this, id, buddies=std::move(buddies)]( FoundKeyData res ) {
286
287 const zypp::Pathname & file { _verifyContext.file() };
288 const zypp::KeyContext & keyContext { _verifyContext.keyContext() };
289 const zypp::Pathname & signature { _verifyContext.signature() };
290 const std::string & filedesc { _verifyContext.shortFile() };
291
292 if ( res._foundKey ) {
293
294 // we found a key but it is not trusted ( e.g. user did not want to trust it )
295 if ( !res.trusted )
296 return makeReturn(false);
297
298 // it exists, is trusted, does it validate?
299 _verifyContext.signatureIdTrusted( res._whichKeyRing == _keyRing->pimpl().trustedKeyRing() );
300 _keyringReport.infoVerify( filedesc, res._foundKey, keyContext );
301 if ( _keyRing->pimpl().verifyFile( file, signature, res._whichKeyRing ) )
302 {
303 _verifyContext.fileValidated( true );
304 if ( _verifyContext.signatureIdTrusted() && not buddies.empty() ) {
305 // Check for buddy keys to be imported...
306 MIL << "Validated with trusted key: importing buddy list..." << std::endl;
307 _keyringReport.reportAutoImportKey( buddies, res._foundKey, keyContext );
308 for ( const auto & kd : buddies ) {
309 _keyRing->importKey( _keyRing->pimpl().exportKey( kd, _keyRing->pimpl().generalKeyRing() ), true );
310 }
311 }
312 return makeReturn(_verifyContext.fileValidated()); // signature is actually successfully validated!
313 }
314 else
315 {
316 bool userAnswer = _keyringReport.askUserToAcceptVerificationFailed( filedesc, _keyRing->pimpl().exportKey( res._foundKey, res._whichKeyRing ), keyContext );
317 MIL << "askUserToAcceptVerificationFailed: " << userAnswer << std::endl;
318 return makeReturn(userAnswer);
319 }
320 } else {
321 // signed with an unknown key...
322 MIL << "File [" << file << "] ( " << filedesc << " ) signed with unknown key [" << id << "]" << std::endl;
323 bool res = _keyringReport.askUserToAcceptUnknownKey( filedesc, id, _verifyContext.keyContext() );
324 MIL << "askUserToAcceptUnknownKey: " << res << std::endl;
325 return makeReturn(res);
326 }
327
328 return makeReturn(false);
329 };
330 }
331
332 protected:
333 ZyppContextRefType _zyppContext;
334 KeyRingReportHelper _keyringReport;
335 KeyRingRef _keyRing;
336 zypp::keyring::VerifyFileContext _verifyContext;
337
338 private:
339 inline std::pair<bool, zypp::keyring::VerifyFileContext> makeReturn( bool res ){
340 _verifyContext.fileAccepted( res );
341 return std::make_pair( res, std::move(_verifyContext) ) ;
342 }
343 };
344 }
345
346 MaybeAwaitable<std::pair<bool,zypp::keyring::VerifyFileContext>> verifyFileSignature( ContextRef zyppContext, zypp::keyring::VerifyFileContext &&context_r )
347 {
348 auto kr = zyppContext->keyRing();
349 VerifyFileSignatureLogic impl( std::move(zyppContext), std::move(kr), std::move(context_r) );
350 zypp_co_return zypp_co_await( impl.execute () );
351 }
352
353 MaybeAwaitable<std::pair<bool,zypp::keyring::VerifyFileContext>> verifyFileSignature(ContextRef zyppContext, zypp::KeyRing_Ptr keyRing, zypp::keyring::VerifyFileContext &&context_r )
354 {
355 VerifyFileSignatureLogic impl( std::move(zyppContext), std::move(keyRing), std::move(context_r) );
356 zypp_co_return zypp_co_await( impl.execute () );
357 }
358
359}
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition Exception.h:475
#define _(MSG)
Definition Gettext.h:39
#define MIL
Definition Logger.h:100
#define ERR
Definition Logger.h:102
#define WAR
Definition Logger.h:101
Base class for Exception.
Definition Exception.h:153
Class representing one GPG Public Keys data.
Definition PublicKey.h:201
static bool isSafeKeyId(const std::string &id_r)
Whether this is a long id (64bit/16byte) or even better a fingerprint.
Definition PublicKey.h:301
Class representing one GPG Public Key (PublicKeyData + ASCII armored in a tempfile).
Definition PublicKey.h:358
std::string name() const
Definition PublicKey.cc:660
bool isValid() const
Definition PublicKey.h:396
What is known about a repository.
Definition RepoInfo.h:72
Interim helper class to collect global options and settings.
Definition ZConfig.h:82
Pathname repoManagerRoot() const
The RepoManager root directory.
Definition ZConfig.cc:1011
Pathname pubkeyCachePath() const
Path where the pubkey caches.
Definition ZConfig.cc:1106
bool empty() const
Test for an empty path.
Definition Pathname.h:117
I/O context for KeyRing::verifyFileSignatureWorkflow.
Provide ProvideType
Definition context.h:34
bool info(std::string msg_r, UserData userData_r=UserData())
send message text
Definition ansi.h:855
bool empty() const
Whether neither idents nor provides are set.
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
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:346
MaybeAwaitable< expected< void > > fetchGpgKeys(ContextRef ctx, zypp::RepoInfo info)
zypp::KeyRing_Ptr KeyRingRef
Definition context.h:25
void setRepoInfo(const RepoInfo &repoinfo)
Definition KeyContext.h:19
KeyTrust
User reply options for the askUserToTrustKey callback.
Definition KeyRing.h:54
@ KEY_TRUST_AND_IMPORT
Import the key.
Definition KeyRing.h:72
@ KEY_TRUST_TEMPORARILY
This basically means, we knew the key, but it was not trusted.
Definition KeyRing.h:63
Convenient building of std::string with boost::format.
Definition String.h:254
ImportKeyFromRepoLogic(ContextRef context, std::string &&keyId, zypp::RepoInfo &&info)
Definition keyringwf.cc:34
typename Context::ProvideType ProvideType
Definition keyringwf.cc:29
typename ProvideType::MediaHandle MediaHandle
Definition keyringwf.cc:30