7e11753bfa96f76acd0bac9d77c1e992b15d5fd4
[platform/upstream/libzypp.git] / tests / repo / RepoSigcheck_test.cc
1 #include <iostream>
2
3 #include <boost/test/auto_unit_test.hpp>
4
5 #include "zypp/ZYppFactory.h"
6 #include "zypp/RepoManager.h"
7 #include "TestSetup.h"
8
9 using namespace boost::unit_test;
10 using namespace zypp;
11 using std::cout;
12 using std::endl;
13
14 #define TC_VERBOSE 0
15
16 #define COUT if ( TC_VERBOSE ) std::cout
17 #define TAG COUT << "*** " << __PRETTY_FUNCTION__ << endl
18
19 TestSetup test( Arch_x86_64, TSO_REPO_DEFAULT_GPG );
20 const Pathname DATADIR( TESTS_SRC_DIR "/repo/RepoSigcheck" );
21
22 ///////////////////////////////////////////////////////////////////
23
24 struct KeyRingReceiver : public callback::ReceiveReport<KeyRingReport>
25 {
26   typedef callback::ReceiveReport<KeyRingReport> Base;
27
28   KeyRingReceiver()             { TAG; connect(); }
29   ~KeyRingReceiver()            { TAG; }
30
31   virtual void reportbegin()    { TAG; cblistCheck( __FUNCTION__ ); }
32   virtual void reportend()      { TAG; cblistCheck( __FUNCTION__ ); }
33
34   virtual KeyTrust askUserToAcceptKey( const PublicKey &key, const KeyContext &keycontext = KeyContext() )
35   {
36     TAG; cblistCheck( __FUNCTION__ );
37     return Base::askUserToAcceptKey( key , keycontext );
38   }
39
40   virtual void infoVerify( const std::string & file_r, const PublicKeyData & keyData_r, const KeyContext &keycontext = KeyContext() )
41   {
42     TAG; cblistCheck( __FUNCTION__ );
43     return Base::infoVerify( file_r, keyData_r, keycontext );
44   }
45
46   virtual bool askUserToAcceptUnsignedFile( const std::string &file, const KeyContext &keycontext = KeyContext() )
47   {
48     TAG; cblistCheck( __FUNCTION__ );
49     return Base::askUserToAcceptUnsignedFile( file, keycontext );
50   }
51
52   virtual bool askUserToAcceptUnknownKey( const std::string &file, const std::string &id, const KeyContext &keycontext = KeyContext() )
53   {
54     TAG;  cblistCheck( __FUNCTION__ );
55     return Base::askUserToAcceptUnknownKey( file, id, keycontext );
56   }
57
58   virtual bool askUserToAcceptVerificationFailed( const std::string &file, const PublicKey &key, const KeyContext &keycontext = KeyContext() )
59   {
60     TAG;  cblistCheck( __FUNCTION__ );
61     return Base::askUserToAcceptVerificationFailed( file, key, keycontext );
62   }
63
64 public:
65   typedef std::list<std::string> CallbackList;
66
67   void cblistCheck( const std::string & cb_r )
68   {
69     BOOST_CHECK_EQUAL( _cblist.empty(), false );
70     if ( !_cblist.empty() )
71     {
72       BOOST_CHECK_EQUAL( _cblist.front(), cb_r );
73       _cblist.pop_front();
74     }
75   }
76
77   CallbackList _cblist; // expected callback sequence list
78
79 } krCallback;
80
81 inline std::string chr( const bool & v )
82 { return v ? "1" : "0"; }
83
84 inline std::string chr( const TriBool & v )
85 {
86   return indeterminate(v) ? "*" : chr( bool(v) );
87 }
88
89 ///////////////////////////////////////////////////////////////////
90 /*
91  * Check the RepoInfo methods returning which checks to perform
92  * based on the global (zypp.conf) and local (.repo file) settings.
93  *
94  * *** See table in RepoInfo.h:'Repository gpgchecks'
95  */
96 BOOST_AUTO_TEST_CASE(init)
97 {
98   ZConfig & zcfg( ZConfig::instance() );
99
100   RepoInfo repo;
101   std::initializer_list<TriBool> tribools( { TriBool(indeterminate), TriBool(true), TriBool(false) } );
102
103   // global zconfig values...
104   for ( bool g_GpgCheck : { true, false } )
105   {
106     zcfg.setGpgCheck( g_GpgCheck );
107     for ( TriBool g_RepoGpgCheck : tribools )
108     {
109       zcfg.setRepoGpgCheck( g_RepoGpgCheck );
110       for ( TriBool g_PkgGpgCheck : tribools )
111       {
112         zcfg.setPkgGpgCheck( g_PkgGpgCheck );
113
114         // .repo values
115         for ( TriBool r_GpgCheck : tribools )
116         {
117           repo.setGpgCheck( r_GpgCheck );
118           for ( TriBool r_RepoGpgCheck : tribools )
119           {
120             repo.setRepoGpgCheck( r_RepoGpgCheck );
121             for ( TriBool r_PkgGpgCheck : tribools )
122             {
123               repo.setPkgGpgCheck( r_PkgGpgCheck );
124
125               // check the repo methods returning what to do:
126               bool      cfgGpgCheck     = indeterminate(r_GpgCheck)                                  ? g_GpgCheck     : bool(r_GpgCheck);
127               TriBool   cfgRepoGpgCheck = indeterminate(r_GpgCheck) && indeterminate(r_RepoGpgCheck) ? g_RepoGpgCheck : r_RepoGpgCheck;
128               TriBool   cfgPkgGpgCheck  = indeterminate(r_GpgCheck) && indeterminate(r_PkgGpgCheck)  ? g_PkgGpgCheck  : r_PkgGpgCheck;
129 #if ( TC_VERBOSE )
130               COUT << chr(cfgGpgCheck) << "\t" << chr(cfgRepoGpgCheck) << "\t" << chr(cfgPkgGpgCheck)
131                    << "\t(" << chr(r_GpgCheck)     << "," << chr(g_GpgCheck)     << ")"
132                    << "\t(" << chr(r_RepoGpgCheck) << "," << chr(g_RepoGpgCheck) << ")"
133                    << "\t(" << chr(r_PkgGpgCheck)  << "," << chr(g_PkgGpgCheck)  << ")"
134                    << flush;
135 #endif
136
137               // default gpgCeck follows config
138               BOOST_CHECK_EQUAL( repo.gpgCheck(), cfgGpgCheck );
139
140
141               // repoGpgCheck follows gpgCeck
142               // explicitly defined it alters mandatory check
143               bool willCheckRepo  = repo.repoGpgCheck();
144               bool mandatoryCheck = repo.repoGpgCheckIsMandatory();
145 #if ( TC_VERBOSE )
146               COUT << "\t" << ( willCheckRepo ? ( mandatoryCheck ? "!" : "+" ) : "-" ) << flush;
147 #endif
148               if ( mandatoryCheck )     // be a subset of willCheckRepo!
149                 BOOST_CHECK_EQUAL( willCheckRepo, mandatoryCheck );
150
151               if ( cfgGpgCheck )
152               {
153                 BOOST_CHECK_EQUAL( willCheckRepo,  true );
154                 BOOST_CHECK_EQUAL( mandatoryCheck, !bool(!cfgRepoGpgCheck) );   // TriBool: !false = true or indeterminate
155               }
156               else
157               {
158                 BOOST_CHECK_EQUAL( willCheckRepo,  bool(cfgRepoGpgCheck) );
159                 BOOST_CHECK_EQUAL( mandatoryCheck, bool(cfgRepoGpgCheck) );
160               }
161
162
163               // pkgGpgCheck may depend on the repoGpgCheck result
164               for ( TriBool r_validSignature : tribools )       // indeterminate <==> unsigned repo
165               {
166                 repo.setValidRepoSignature( r_validSignature );
167
168                 if ( r_validSignature && !willCheckRepo )
169                   // RepoInfo must invalidate any valid (old) signature as soon as the repo check
170                   // is turned off. This prevents showing 'valid sig' for not yet refreshed repos.
171                   // Instead show 'won't be checked' immediately.
172                   BOOST_CHECK( bool(!repo.validRepoSignature()) );
173                 else
174                   BOOST_CHECK( sameTriboolState( repo.validRepoSignature(), r_validSignature ) );
175
176                 bool willCheckPkg   = repo.pkgGpgCheck();
177                 bool mandatoryCheck = repo.pkgGpgCheckIsMandatory();
178 #if ( TC_VERBOSE )
179                 COUT << "\t" << chr(r_validSignature) << ( willCheckPkg ? ( mandatoryCheck ? "!" : "+" ) : "-" ) << flush;
180 #endif
181                 if ( mandatoryCheck )   // be a subset of willCheckPkg!
182                   BOOST_CHECK_EQUAL( willCheckPkg, mandatoryCheck );
183
184                 if ( cfgPkgGpgCheck )
185                 {
186                   BOOST_CHECK_EQUAL( willCheckPkg,   true );
187                   BOOST_CHECK_EQUAL( mandatoryCheck, true );
188                 }
189                 else if ( cfgGpgCheck )
190                 {
191                   if ( r_validSignature )
192                   {
193                     BOOST_CHECK_EQUAL( willCheckPkg,   false );
194                     BOOST_CHECK_EQUAL( mandatoryCheck, false );
195                   }
196                   else // TriBool: !true = false or indeterminate/unsigned
197                   {
198                     BOOST_CHECK_EQUAL( willCheckPkg,   true );
199                     BOOST_CHECK_EQUAL( mandatoryCheck, !bool(!cfgPkgGpgCheck) ); // TriBool: !false = true or indeterminate/unsigned
200                   }
201                 }
202                 else
203                 {
204                   BOOST_CHECK_EQUAL( willCheckPkg,   false );
205                   BOOST_CHECK_EQUAL( mandatoryCheck, false );
206                 }
207               }
208 #if ( TC_VERBOSE )
209               COUT << endl;
210 #endif
211
212             }
213           }
214         }
215       }
216     }
217   }
218   // reset to defaults:
219   zcfg.setGpgCheck      ( true );
220   zcfg.setRepoGpgCheck  ( indeterminate );
221   zcfg.setPkgGpgCheck   ( indeterminate );
222 }
223
224 // RAII: Protect ZConfig value changes from escaping the block scope
225 struct ZConfigGuard
226 {
227   ZConfigGuard()
228   : _zcfg( ZConfig::instance() )
229   {
230     _g = _zcfg.gpgCheck();
231     _r = _zcfg.repoGpgCheck();
232     _p = _zcfg.pkgGpgCheck();
233   }
234
235   ~ZConfigGuard()
236   {
237     _zcfg.setGpgCheck    ( _g );
238     _zcfg.setRepoGpgCheck( _r );
239     _zcfg.setPkgGpgCheck ( _p );
240   }
241
242   ZConfig * operator->() { return &_zcfg; }
243
244   ZConfig &     _zcfg;
245   bool          _g;
246   TriBool       _r;
247   TriBool       _p;
248 };
249
250
251 // RAII: Set and reset KeyRingReceiver callback list and response bits for new testcase
252 struct KeyRingGuard
253 {
254   KeyRingGuard ( KeyRing::DefaultAccept accept_r = KeyRing::ACCEPT_NOTHING )
255   {
256     KeyRing::setDefaultAccept( accept_r );
257     krCallback._cblist.clear();
258 #if ( TC_VERBOSE )
259     COUT << "================================================================================" << endl;
260     KeyRing & keyRing( *getZYpp()->keyRing() );
261     COUT << "K " << keyRing.publicKeys() << endl;
262     COUT << "T " << keyRing.trustedPublicKeys() << endl;
263     COUT << KeyRing::defaultAccept() << endl;
264
265     ZConfig & zcfg( ZConfig::instance() );
266     COUT << "ZConf " << chr( zcfg.gpgCheck() ) << chr( zcfg.repoGpgCheck() ) << chr( zcfg.pkgGpgCheck() ) << endl;
267 #endif
268   }
269
270   ~KeyRingGuard()
271   {
272     BOOST_CHECK_EQUAL( krCallback._cblist.empty(), true );
273     KeyRing::setDefaultAccept( KeyRing::ACCEPT_NOTHING );
274     krCallback._cblist.clear();
275   }
276 };
277
278 void testLoadRepo( bool succeed_r,                              // whether loadRepos should succeed or fail with RepoException
279                    const std::string & repo_r,                  // name of the test repo to load
280                    KeyRing::DefaultAccept accept_r,             // Callback response bits to set (mimics user input)
281                    KeyRingReceiver::CallbackList cblist_r )     // Callback sequence list expected
282 {
283   KeyRingGuard _guard( accept_r );
284   krCallback._cblist = std::move(cblist_r);
285   if ( succeed_r )
286     test.loadRepo( DATADIR/repo_r, repo_r );
287   else
288     BOOST_CHECK_THROW( test.loadRepo( DATADIR/repo_r, repo_r ), repo::RepoException );
289 }
290
291 // ACCEPT_NOTHING             = 0x0000,
292 // ACCEPT_UNSIGNED_FILE       = 0x0001,
293 // ACCEPT_UNKNOWNKEY          = 0x0002,
294 // TRUST_KEY_TEMPORARILY      = 0x0004,
295 // TRUST_AND_IMPORT_KEY       = 0x0008,
296 // ACCEPT_VERIFICATION_FAILED = 0x0010,
297
298 BOOST_AUTO_TEST_CASE(unsigned_repo)
299 {
300   // askUserToAcceptUnsignedFile actually depends on the gpgcheck settings.
301   // Mandatory on 'R' cases. Otherwise an unsigend repo is accepted but 'pkggpg on'
302   //  is enforced.
303   ZConfigGuard zcfg;
304   zcfg->setRepoGpgCheck( false );       // unsafe
305
306   std::string repo( "unsigned_repo" );
307   testLoadRepo( true, repo, KeyRing::ACCEPT_NOTHING,
308                 { } );
309
310   zcfg->setRepoGpgCheck( indeterminate );       // the default
311
312   testLoadRepo( false, repo, KeyRing::ACCEPT_NOTHING,
313                 { "reportbegin", "askUserToAcceptUnsignedFile", "reportend" } );
314   testLoadRepo( true, repo, KeyRing::ACCEPT_UNSIGNED_FILE,
315                 { "reportbegin", "askUserToAcceptUnsignedFile", "reportend" } );
316 }
317
318 BOOST_AUTO_TEST_CASE(unknownkey_repo)
319 {
320   std::string repo( "unknownkey_repo" );
321   testLoadRepo( false, repo, KeyRing::ACCEPT_NOTHING,
322                 { "reportbegin", "askUserToAcceptUnknownKey", "reportend" } );
323   testLoadRepo( true, repo, KeyRing::ACCEPT_UNKNOWNKEY,
324                 { "reportbegin", "askUserToAcceptUnknownKey", "reportend" } );
325 }
326
327
328 BOOST_AUTO_TEST_CASE(wrongsig_repo)
329 {
330   std::string repo( "wrongsig_repo" );
331   // IMPORTED KEYS WILL STAY IN KEYRING! FIXIT if it disturbs subsequent tests
332
333   // 1st testcase with a key, so on the fly check askUserToAcceptKey
334   // being called unless the key is imported in the trusted ring
335   testLoadRepo( false, repo, KeyRing::ACCEPT_NOTHING,
336                 { "reportbegin", "askUserToAcceptKey", "reportend" } );
337   testLoadRepo( false, repo, KeyRing::TRUST_KEY_TEMPORARILY,
338                 { "reportbegin", "askUserToAcceptKey", "infoVerify", "askUserToAcceptVerificationFailed", "reportend" } );
339   testLoadRepo( false, repo, KeyRing::ACCEPT_NOTHING,
340                 { "reportbegin", "askUserToAcceptKey", "reportend" } );
341   testLoadRepo( false, repo, KeyRing::TRUST_AND_IMPORT_KEY,
342                 { "reportbegin", "askUserToAcceptKey", "infoVerify", "askUserToAcceptVerificationFailed", "reportend" } );
343
344   // Now the key is in the trusted ring (no more askUserToAcceptKey)
345   testLoadRepo( false, repo, KeyRing::ACCEPT_NOTHING,
346                 { "reportbegin", "infoVerify", "askUserToAcceptVerificationFailed", "reportend" } );
347   testLoadRepo( true, repo, KeyRing::KeyRing::ACCEPT_VERIFICATION_FAILED,
348                 { "reportbegin", "infoVerify", "askUserToAcceptVerificationFailed", "reportend" } );
349 }
350
351 BOOST_AUTO_TEST_CASE(signed_repo)
352 {
353   std::string repo( "signed_repo" );
354   testLoadRepo( true, repo, KeyRing::KeyRing::ACCEPT_NOTHING,   // relies on wrongsig_repo having accepted the key! (already in trusted ring)
355                 { "reportbegin", "infoVerify", "reportend" } );
356 }
357
358
359 BOOST_AUTO_TEST_CASE(summary)
360 {
361   KeyRingGuard _guard;
362   KeyRing & keyRing( *getZYpp()->keyRing() );
363   BOOST_CHECK_EQUAL( keyRing.publicKeys().size(),               1 );
364   BOOST_CHECK_EQUAL( keyRing.trustedPublicKeys().size(),        1 );
365   BOOST_CHECK_EQUAL( KeyRing::defaultAccept(),                  KeyRing::ACCEPT_NOTHING );
366   BOOST_CHECK_EQUAL( test.satpool().reposSize(),                5 );    //
367 }