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