libzypp 17.37.17
keyringwf.cc
Go to the documentation of this file.
1/*---------------------------------------------------------------------\
2| ____ _ __ __ ___ |
3| |__ / \ / / . \ . \ |
4| / / \ V /| _/ _/ |
5| / /__ | | | | | | |
6| /_____||_| |_| |_| |
7| |
8\---------------------------------------------------------------------*/
9
10#include "keyringwf.h"
11#include "logichelpers.h"
14#include <zypp/RepoInfo.h>
15#include <zypp/ZConfig.h>
16#include <zypp/Pathname.h>
17#include <zypp-common/PublicKey.h>
18
20#include <utility>
21#include <zypp-core/zyppng/pipelines/Expected>
23#include <zypp/ng/Context>
25#include <zypp/ng/UserRequest>
26
28
29 template <class Executor, class OpType>
30 struct ImportKeyFromRepoLogic : public LogicBase<Executor, OpType> {
31
34 using ProvideType = typename ZyppContextType::ProvideType;
35 using MediaHandle = typename ProvideType::MediaHandle;
36 using ProvideRes = typename ProvideType::Res;
37
38 ZYPP_ENABLE_LOGIC_BASE(Executor, OpType);
39
40 public:
41 ImportKeyFromRepoLogic( ZyppContextRefType context, std::string &&keyId, zypp::RepoInfo &&info )
42 : _context( std::move(context) ), _keyId(std::move(keyId)), _repo( std::move(info) )
43 { }
44
46
47 using namespace zyppng::operators;
48 using zyppng::operators::operator|;
49 using zyppng::expected;
50
51 if ( _keyId.empty() || !_context )
52 return makeReadyResult(false);
53
54 if ( _context->keyRing()->isKeyKnown(_keyId) ){
55 if ( _context->keyRing()->isKeyTrusted(_keyId) )
56 return makeReadyResult(true);
57
59 }
60
61 const zypp::ZConfig &conf = _context->config();
62 zypp::Pathname cacheDir = conf.repoManagerRoot() / conf.pubkeyCachePath();
63
64 if ( _repo.gpgKeyUrlsEmpty() ) {
65 // translator: %1% is a repositories name
66 JobReportHelper(_context).info( zypp::str::Format(_("Repository %1% does not define 'gpgkey=' URLs.") ) % _repo.asUserString() );
67 }
68
70 | [this, cacheDir]( expected<void> res ) {
71 if ( !res ) return false;
72 if ( !_context->keyRing()->isKeyKnown(_keyId) )
73 // if we did not find any keys, there is no point in checking again, break
74 return false;
75
77 };
78 }
79
81 zypp::PublicKeyData keyData ( _context->keyRing()->publicKeyData(_keyId) );
82 if ( !keyData )
83 return false;
84
85 zypp::PublicKey key;
86 try {
87 key = zypp::PublicKey( _context->keyRing()->exportPublicKey( keyData ) );
88 } catch ( const zypp::Exception &e ) {
89 ZYPP_CAUGHT(e);
90 return false;
91 }
92
93 if ( !key.isValid() ) {
94 ERR << "Key [" << _keyId << "] from known keyring is not valid" << std::endl;
95 return false;
96 }
97
98 MIL << "Key [" << _keyId << "] " << key.name() << " loaded from cache" << std::endl;
99
100 zypp::KeyContext context;
101 context.setRepoInfo( _repo );
102 if ( !KeyRingReportHelper(_context).askUserToAcceptPackageKey( key, context ) ) {
103 return false;
104 }
105
106 MIL << "User wants to import key [" << _keyId << "] " << key.name() << " from cache" << std::endl;
107 try {
108 _context->keyRing()->importKey( key, true );
109 } catch ( const zypp::KeyRingException &e ) {
110 ZYPP_CAUGHT(e);
111 ERR << "Failed to import key: "<<_keyId;
112 return false;
113 }
114 return true;
115 }
116
118 std::string _keyId;
120 };
121
122 bool provideAndImportKeyFromRepository( SyncContextRef ctx, std::string id_r, zypp::RepoInfo info_r )
123 {
124 return SimpleExecutor<ImportKeyFromRepoLogic, SyncOp<bool>>::run( ctx, std::move(id_r), std::move(info_r) );
125 }
126
127 AsyncOpRef<bool> provideAndImportKeyFromRepository( ContextRef ctx, std::string id_r, zypp::RepoInfo info_r)
128 {
129 return SimpleExecutor<ImportKeyFromRepoLogic, AsyncOp<bool>>::run( ctx, std::move(id_r), std::move(info_r) );
130 }
131
132 namespace {
133
138 template <class Executor, class OpType>
139 struct VerifyFileSignatureLogic : public LogicBase<Executor, OpType>
140 {
141 ZYPP_ENABLE_LOGIC_BASE(Executor, OpType);
142
143 using ZyppContextRefType = MaybeAsyncContextRef<OpType>;
145
146 VerifyFileSignatureLogic( ZyppContextRefType zyppContext, KeyRingRef &&keyRing, zypp::keyring::VerifyFileContext &&ctx )
147 : _zyppContext( std::move(zyppContext) )
148 , _keyringReport( _zyppContext )
149 , _keyRing( std::move(keyRing) )
150 , _verifyContext( std::move(ctx) )
151 { }
152
153 struct FoundKeyData {
154 zypp::PublicKeyData _foundKey;
155 zypp::Pathname _whichKeyRing;
156 bool trusted = false;
157 };
158
159 MaybeAsyncRef<FoundKeyData> findKey ( const std::string &id ) {
160
161 using zyppng::operators::operator|;
162
163 if ( id.empty() )
164 return makeReadyResult(FoundKeyData{zypp::PublicKeyData(), zypp::Pathname()});
165
166 // does key exists in trusted keyring
167 zypp::PublicKeyData trustedKeyData( _keyRing->pimpl().publicKeyExists( id, _keyRing->pimpl().trustedKeyRing() ) );
168 if ( trustedKeyData )
169 {
170 MIL << "Key is trusted: " << trustedKeyData << std::endl;
171
172 // lets look if there is an updated key in the
173 // general keyring
174 zypp::PublicKeyData generalKeyData( _keyRing->pimpl().publicKeyExists( id, _keyRing->pimpl().generalKeyRing() ) );
175 if ( generalKeyData )
176 {
177 // bnc #393160: Comment #30: Compare at least the fingerprint
178 // in case an attacker created a key the the same id.
179 //
180 // FIXME: bsc#1008325: For keys using subkeys, we'd actually need
181 // to compare the subkey sets, to tell whether a key was updated.
182 // because created() remains unchanged if the primary key is not touched.
183 // For now we wait until a new subkey signs the data and treat it as a
184 // new key (else part below).
185 if ( trustedKeyData.fingerprint() == generalKeyData.fingerprint()
186 && trustedKeyData.created() < generalKeyData.created() )
187 {
188 MIL << "Key was updated. Saving new version into trusted keyring: " << generalKeyData << std::endl;
189 _keyRing->importKey( _keyRing->pimpl().exportKey( generalKeyData, _keyRing->pimpl().generalKeyRing() ), true );
190 trustedKeyData = _keyRing->pimpl().publicKeyExists( id, _keyRing->pimpl().trustedKeyRing() ); // re-read: invalidated by import?
191 }
192 }
193
194 return makeReadyResult( FoundKeyData{ trustedKeyData, _keyRing->pimpl().trustedKeyRing(), true } );
195 }
196 else
197 {
198 zypp::PublicKeyData generalKeyData( _keyRing->pimpl().publicKeyExists( id, _keyRing->pimpl().generalKeyRing() ) );
199 if ( generalKeyData )
200 {
201 zypp::PublicKey key( _keyRing->pimpl().exportKey( generalKeyData, _keyRing->pimpl().generalKeyRing() ) );
202 MIL << "Key [" << id << "] " << key.name() << " is not trusted" << std::endl;
203
204 // ok the key is not trusted, ask the user to trust it or not
205 zypp::KeyRingReport::KeyTrust reply = _keyringReport.askUserToAcceptKey( key, _verifyContext.keyContext() );
208 {
209 zypp::Pathname whichKeyring;
210
211 MIL << "User wants to trust key [" << id << "] " << key.name() << std::endl;
212
214 {
215 MIL << "User wants to import key [" << id << "] " << key.name() << std::endl;
216 _keyRing->importKey( key, true );
217 whichKeyring = _keyRing->pimpl().trustedKeyRing();
218 }
219 else
220 whichKeyring = _keyRing->pimpl().generalKeyRing();
221
222 return makeReadyResult(FoundKeyData { std::move(generalKeyData), std::move(whichKeyring), true });
223 }
224 else
225 {
226 MIL << "User does not want to trust key [" << id << "] " << key.name() << std::endl;
227 return makeReadyResult(FoundKeyData { std::move(generalKeyData), _keyRing->pimpl().generalKeyRing(), false });
228 }
229 }
230 else if ( ! _verifyContext.keyContext().empty() )
231 {
232 // try to find the key in the repository info
233 return provideAndImportKeyFromRepository ( _zyppContext, id, _verifyContext.keyContext().repoInfo() )
234 | [this, id]( bool success ) {
235 if ( !success ) {
236 return FoundKeyData{ zypp::PublicKeyData(), zypp::Pathname() };
237 }
238 return FoundKeyData{ _keyRing->pimpl().publicKeyExists( id, _keyRing->pimpl().trustedKeyRing() ), _keyRing->pimpl().trustedKeyRing(), true };
239 };
240 }
241 }
242 return makeReadyResult(FoundKeyData{ zypp::PublicKeyData(), zypp::Pathname() });
243 }
244
245 // returns std::pair<bool, zypp::keyring::VerifyFileContext>
246 auto execute () {
247
248 _verifyContext.resetResults();
249 const zypp::Pathname & file { _verifyContext.file() };
250 const zypp::Pathname & signature { _verifyContext.signature() };
251 const std::string & filedesc { _verifyContext.shortFile() };
252
253 MIL << "Going to verify signature for " << filedesc << " ( " << file << " ) with " << signature << std::endl;
254
255 // if signature does not exists, ask user if they want to accept unsigned file.
256 if( signature.empty() || (!zypp::PathInfo( signature ).isExist()) )
257 {
258 bool res = _keyringReport.askUserToAcceptUnsignedFile( filedesc, _verifyContext.keyContext() );
259 MIL << "askUserToAcceptUnsignedFile: " << res << std::endl;
260 return makeReadyResult( makeReturn(res) );
261 }
262
263 // get the id of the signature (it might be a subkey id!)
264 try {
265 _verifyContext.signatureId( _keyRing->readSignatureKeyId( signature ) ); //throws !
266 } catch ( const zypp::Exception &e ) {
267 MIL << "Failed to read the signature from " << signature << std::endl;
268 ZYPP_CAUGHT(e);
269 return makeReadyResult( makeReturn(false) );
270 }
271
272 const std::string & id = _verifyContext.signatureId();
273
274 // collect the buddies
275 std::list<zypp::PublicKeyData> buddies; // Could be imported IFF the file is validated by a trusted key
276 for ( const auto & sid : _verifyContext.buddyKeys() ) {
277 if ( not zypp::PublicKeyData::isSafeKeyId( sid ) ) {
278 WAR << "buddy " << sid << ": key id is too short to safely identify a gpg key. Skipping it." << std::endl;
279 continue;
280 }
281 if ( _keyRing->pimpl().trustedPublicKeyExists( sid ) ) {
282 MIL << "buddy " << sid << ": already in trusted key ring. Not needed." << std::endl;
283 continue;
284 }
285 auto pk = _keyRing->pimpl().publicKeyExists( sid );
286 if ( not pk ) {
287 WAR << "buddy " << sid << ": not available in the public key ring. Skipping it." << std::endl;
288 continue;
289 }
290 if ( pk.providesKey(id) ) {
291 MIL << "buddy " << sid << ": is the signing key. Handled separately." << std::endl;
292 continue;
293 }
294 MIL << "buddy " << sid << ": candidate for auto import. Remeber it." << std::endl;
295 buddies.push_back( pk );
296 }
297
298 using zyppng::operators::operator|;
299 return findKey( id ) | [this, id, buddies=std::move(buddies)]( FoundKeyData res ) {
300
301 const zypp::Pathname & file { _verifyContext.file() };
302 const zypp::KeyContext & keyContext { _verifyContext.keyContext() };
303 const zypp::Pathname & signature { _verifyContext.signature() };
304 const std::string & filedesc { _verifyContext.shortFile() };
305
306 if ( res._foundKey ) {
307
308 // we found a key but it is not trusted ( e.g. user did not want to trust it )
309 if ( !res.trusted )
310 return makeReturn(false);
311
312 // it exists, is trusted, does it validate?
313 _verifyContext.signatureIdTrusted( res._whichKeyRing == _keyRing->pimpl().trustedKeyRing() );
314 _keyringReport.infoVerify( filedesc, res._foundKey, keyContext );
315 if ( _keyRing->pimpl().verifyFile( file, signature, res._whichKeyRing ) )
316 {
317 _verifyContext.fileValidated( true );
318 if ( _verifyContext.signatureIdTrusted() && not buddies.empty() ) {
319 // Check for buddy keys to be imported...
320 MIL << "Validated with trusted key: importing buddy list..." << std::endl;
321 _keyringReport.reportAutoImportKey( buddies, res._foundKey, keyContext );
322 for ( const auto & kd : buddies ) {
323 _keyRing->importKey( _keyRing->pimpl().exportKey( kd, _keyRing->pimpl().generalKeyRing() ), true );
324 }
325 }
326 return makeReturn(_verifyContext.fileValidated()); // signature is actually successfully validated!
327 }
328 else
329 {
330 bool userAnswer = _keyringReport.askUserToAcceptVerificationFailed( filedesc, _keyRing->pimpl().exportKey( res._foundKey, res._whichKeyRing ), keyContext );
331 MIL << "askUserToAcceptVerificationFailed: " << userAnswer << std::endl;
332 return makeReturn(userAnswer);
333 }
334 } else {
335 // signed with an unknown key...
336 MIL << "File [" << file << "] ( " << filedesc << " ) signed with unknown key [" << id << "]" << std::endl;
337 bool res = _keyringReport.askUserToAcceptUnknownKey( filedesc, id, _verifyContext.keyContext() );
338 MIL << "askUserToAcceptUnknownKey: " << res << std::endl;
339 return makeReturn(res);
340 }
341
342 return makeReturn(false);
343 };
344 }
345
346 protected:
347 ZyppContextRefType _zyppContext;
348 KeyRingReportHelper<ZyppContextRefType> _keyringReport;
349 KeyRingRef _keyRing;
350 zypp::keyring::VerifyFileContext _verifyContext;
351
352 private:
353 inline std::pair<bool, zypp::keyring::VerifyFileContext> makeReturn( bool res ){
354 _verifyContext.fileAccepted( res );
355 return std::make_pair( res, std::move(_verifyContext) ) ;
356 }
357 };
358 }
359
360 std::pair<bool,zypp::keyring::VerifyFileContext>
361 verifyFileSignature( SyncContextRef zyppContext, zypp::keyring::VerifyFileContext &&context_r )
362 {
363 auto kr = zyppContext->keyRing();
364 return SimpleExecutor<VerifyFileSignatureLogic, SyncOp<std::pair<bool,zypp::keyring::VerifyFileContext> >>::run( std::move(zyppContext), std::move(kr), std::move(context_r) );
365 }
366
368 {
369 auto kr = zyppContext->keyRing();
370 return SimpleExecutor<VerifyFileSignatureLogic, AsyncOp<std::pair<bool,zypp::keyring::VerifyFileContext> >>::run( std::move(zyppContext), std::move(kr), std::move(context_r) );
371 }
372
373 std::pair<bool,zypp::keyring::VerifyFileContext> verifyFileSignature( SyncContextRef zyppContext, zypp::KeyRing_Ptr keyRing, zypp::keyring::VerifyFileContext &&context_r )
374 {
375 return SimpleExecutor<VerifyFileSignatureLogic, SyncOp<std::pair<bool,zypp::keyring::VerifyFileContext> >>::run( std::move(zyppContext), std::move(keyRing), std::move(context_r) );
376 }
377
379 {
380 return SimpleExecutor<VerifyFileSignatureLogic, AsyncOp<std::pair<bool,zypp::keyring::VerifyFileContext> >>::run( std::move(zyppContext), std::move(keyRing), std::move(context_r) );
381 }
382
383}
Base class for Exception.
Definition Exception.h:153
What is known about a repository.
Definition RepoInfo.h:72
Interim helper class to collect global options and settings.
Definition ZConfig.h:69
Pathname repoManagerRoot() const
The RepoManager root directory.
Definition ZConfig.cc:980
Pathname pubkeyCachePath() const
Path where the pubkey caches.
Definition ZConfig.cc:1075
bool empty() const
Test for an empty path.
Definition Pathname.h:116
I/O context for KeyRing::verifyFileSignatureWorkflow.
bool info(std::string msg_r, UserData userData_r=UserData())
send message text
#define ZYPP_ENABLE_LOGIC_BASE(Executor, OpType)
Definition Arch.h:364
bool empty() const
Whether neither idents nor provides are set.
bool provideAndImportKeyFromRepository(SyncContextRef 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:122
std::pair< bool, zypp::keyring::VerifyFileContext > verifyFileSignature(SyncContextRef zyppContext, zypp::keyring::VerifyFileContext &&context_r)
Follows a signature verification interacting with the user.
Definition keyringwf.cc:361
expected< void > fetchGpgKeys(SyncContextRef ctx, zypp::RepoInfo info)
std::conditional_t< isAsync, AsyncOpRef< T >, T > makeReadyResult(T &&result)
Definition asyncop.h:297
std::shared_ptr< AsyncOp< T > > AsyncOpRef
Definition asyncop.h:255
typename remove_smart_ptr< T >::type remove_smart_ptr_t
zypp::KeyRing_Ptr KeyRingRef
Definition context.h:29
std::conditional_t< detail::is_async_op_v< OpType >, ContextRef, SyncContextRef > MaybeAsyncContextRef
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
remove_smart_ptr_t< ZyppContextRefType > ZyppContextType
Definition keyringwf.cc:33
ImportKeyFromRepoLogic(ZyppContextRefType context, std::string &&keyId, zypp::RepoInfo &&info)
Definition keyringwf.cc:41
typename ProvideType::MediaHandle MediaHandle
Definition keyringwf.cc:35
MaybeAsyncContextRef< OpType > ZyppContextRefType
Definition keyringwf.cc:32
typename ZyppContextType::ProvideType ProvideType
Definition keyringwf.cc:34
std::conditional_t< isAsync, AsyncOpRef< Type >, Type > MaybeAsyncRef
#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