libzypp  11.13.5
PublicKey.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
12 #include <climits>
13 
14 #include <iostream>
15 #include <vector>
16 
17 #include "zypp/base/Gettext.h"
18 #include "zypp/base/String.h"
19 #include "zypp/base/Regex.h"
20 #include "zypp/PublicKey.h"
21 #include "zypp/ExternalProgram.h"
22 #include "zypp/TmpPath.h"
23 #include "zypp/PathInfo.h"
24 #include "zypp/base/Exception.h"
25 #include "zypp/base/LogTools.h"
26 #include "zypp/Date.h"
27 #include "zypp/TmpPath.h"
28 
29 #include <ctime>
30 
31 using std::endl;
32 
34 namespace zypp
35 {
36 
42  {
43  std::string _id;
44  std::string _name;
45  std::string _fingerprint;
48 
49  public:
51  static shared_ptr<Impl> nullimpl()
52  {
53  static shared_ptr<Impl> _nullimpl( new Impl );
54  return _nullimpl;
55  }
56 
57  private:
58  friend Impl * rwcowClone<Impl>( const Impl * rhs );
60  Impl * clone() const
61  { return new Impl( *this ); }
62  };
64 
68 
70  : _pimpl( Impl::nullimpl() )
71  {}
72 
74  {}
75 
76  PublicKeyData::operator bool() const
77  { return !_pimpl->_fingerprint.empty(); }
78 
79  std::string PublicKeyData::id() const
80  { return _pimpl->_id; }
81 
82  std::string PublicKeyData::name() const
83  { return _pimpl->_name; }
84 
85  std::string PublicKeyData::fingerprint() const
86  { return _pimpl->_fingerprint; }
87 
89  { return _pimpl->_created; }
90 
92  { return _pimpl->_expires; }
93 
95  { return( _pimpl->_expires && _pimpl->_expires < Date::now() ); }
96 
98  {
99  if ( _pimpl->_expires )
100  {
101  Date exp( _pimpl->_expires - Date::now() );
102  return exp < 0 ? exp / Date::day - 1 : exp / Date::day;
103  }
104  return INT_MAX;
105  }
106 
107  std::string PublicKeyData::expiresAsString() const
108  {
109  if ( !_pimpl->_expires )
110  { // translators: an annotation to a gpg keys expiry date
111  return _("(does not expire)");
112  }
113  std::string ret( _pimpl->_expires.asString() );
114  int ttl( daysToLive() );
115  if ( ttl <= 90 )
116  {
117  ret += " ";
118  if ( ttl < 0 )
119  { // translators: an annotation to a gpg keys expiry date
120  ret += _("(EXPIRED)");
121  }
122  else if ( ttl == 0 )
123  { // translators: an annotation to a gpg keys expiry date
124  ret += _("(expires within 24h)");
125  }
126  else
127  { // translators: an annotation to a gpg keys expiry date
128  ret += str::form( _PL("(expires in %d day)", "(expires in %d days)", ttl ), ttl );
129  }
130  }
131  return ret;
132  }
133 
135  { return _pimpl->_id.empty() ? _pimpl->_id : str::toLower( _pimpl->_id.substr(8,8) ); }
136 
138  { return _pimpl->_created ? str::hexstring( _pimpl->_created ).substr(2) : std::string(); }
139 
140  std::string PublicKeyData::asString() const
141  {
142  return str::form( "[%s-%s] [%s] [%s] [TTL %d]",
143  _pimpl->_id.c_str(),
144  gpgPubkeyRelease().c_str(),
145  _pimpl->_name.c_str(),
146  _pimpl->_fingerprint.c_str(),
147  daysToLive() );
148  }
149 
150  std::ostream & dumpOn( std::ostream & str, const PublicKeyData & obj )
151  {
152  str << "[" << obj.name() << "]" << endl;
153  str << " fpr " << obj.fingerprint() << endl;
154  str << " id " << obj.id() << endl;
155  str << " cre " << Date::ValueType(obj.created()) << ' ' << obj.created() << endl;
156  str << " exp " << Date::ValueType(obj.expires()) << ' ' << obj.expiresAsString() << endl;
157  str << " ttl " << obj.daysToLive() << endl;
158  str << " rpm " << obj.gpgPubkeyVersion() << "-" << obj.gpgPubkeyRelease() << endl;
159  str << "]";
160  return str;
161  }
162 
163  bool operator==( const PublicKeyData & lhs, const PublicKeyData & rhs )
164  { return ( lhs.fingerprint() == rhs.fingerprint() && lhs.created() == rhs.created() ); }
165 
166 
172  {
173  std::vector<std::string> _words;
175 
177  : _parseEntry( pNONE )
178  {}
179 
180  void scan( std::string & line_r, std::list<PublicKeyData> & keys_r )
181  {
182  // pub:-:1024:17:A84EDAE89C800ACA:971961473:1214043198::-:SuSE Package Signing Key <build@suse.de>:
183  // fpr:::::::::79C179B2E1C820C1890F9994A84EDAE89C800ACA:
184  // sig:::17:A84EDAE89C800ACA:1087899198:::::[selfsig]::13x:
185  // sig:::17:9E40E310000AABA4:980442706::::[User ID not found]:10x:
186  // sig:::1:77B2E6003D25D3D9:980443247::::[User ID not found]:10x:
187  // sig:::17:A84EDAE89C800ACA:1318348291:::::[selfsig]::13x:
188  // sub:-:2048:16:197448E88495160C:971961490:1214043258::: [expires: 2008-06-21]
189  // sig:::17:A84EDAE89C800ACA:1087899258:::::[keybind]::18x:
190  if ( line_r.empty() )
191  return;
192 
193  // quick check for interesting entries
194  _parseEntry = pNONE;
195  switch ( line_r[0] )
196  {
197  #define DOTEST( C1, C2, C3, E ) case C1: if ( line_r[1] == C2 && line_r[2] == C3 && line_r[3] == ':' ) _parseEntry = E; break
198  DOTEST( 'p', 'u', 'b', pPUB );
199  DOTEST( 's', 'i', 'g', pSIG );
200  DOTEST( 'f', 'p', 'r', pFPR );
201  DOTEST( 'u', 'i', 'd', pUID );
202  #undef DOTEST
203  }
204  if ( _parseEntry == pNONE )
205  return;
206 
207  if ( line_r[line_r.size()-1] == '\n' )
208  line_r.erase( line_r.size()-1 );
209  // DBG << line_r << endl;
210 
211  _words.clear();
212  str::splitFields( line_r, std::back_inserter(_words), ":" );
213 
214  PublicKeyData * key( &keys_r.back() );
215 
216  switch ( _parseEntry )
217  {
218  case pPUB:
219  keys_r.push_back( PublicKeyData() ); // reset upon new key
220  key = &keys_r.back();
221  key->_pimpl->_id = _words[4];
222  key->_pimpl->_name = str::replaceAll( _words[9], "\\x3a", ":" );
223  key->_pimpl->_created = Date(str::strtonum<Date::ValueType>(_words[5]));
224  key->_pimpl->_expires = Date(str::strtonum<Date::ValueType>(_words[6]));
225  break;
226 
227  case pSIG:
228  // Update creation/modification date from signatures type "13x".
229  if ( _words[_words.size()-2] == "13x" )
230  {
231  Date cdate(str::strtonum<Date::ValueType>(_words[5]));
232  if ( key->_pimpl->_created < cdate )
233  key->_pimpl->_created = cdate;
234  }
235  break;
236 
237  case pFPR:
238  if ( key->_pimpl->_fingerprint.empty() )
239  key->_pimpl->_fingerprint = _words[9];
240  break;
241 
242  case pUID:
243  if ( ! _words[9].empty() )
244  key->_pimpl->_name = str::replaceAll( _words[9], "\\x3a", ":" );
245  break;
246 
247  case pNONE:
248  break;
249  }
250  }
251  };
253 
255  // class PublicKeyScanner
257 
259  : _pimpl( new Impl )
260  {}
261 
263  {}
264 
265  void PublicKeyScanner::scan( std::string line_r )
266  { _pimpl->scan( line_r, _keys ); }
267 
268 
274  {
276  {}
277 
278  Impl( const Pathname & keyFile_r )
279  {
280  PathInfo info( keyFile_r );
281  MIL << "Taking pubkey from " << keyFile_r << " of size " << info.size() << " and sha1 " << filesystem::checksum(keyFile_r, "sha1") << endl;
282 
283  if ( !info.isExist() )
284  ZYPP_THROW(Exception("Can't read public key from " + keyFile_r.asString() + ", file not found"));
285 
286  if ( filesystem::hardlinkCopy( keyFile_r, _dataFile.path() ) != 0 )
287  ZYPP_THROW(Exception("Can't copy public key data from " + keyFile_r.asString() + " to " + _dataFile.path().asString() ));
288 
289  readFromFile();
290  }
291 
292  Impl( const filesystem::TmpFile & sharedFile_r )
293  : _dataFile( sharedFile_r )
294  { readFromFile(); }
295 
296  Impl( const filesystem::TmpFile & sharedFile_r, const PublicKeyData & keyData_r )
297  : _dataFile( sharedFile_r )
298  , _keyData( keyData_r )
299  {
300  if ( ! keyData_r )
301  {
302  WAR << "Invalid PublicKeyData supplied: scanning from file" << endl;
303  readFromFile();
304  }
305  }
306 
307  public:
308  const PublicKeyData & keyData() const
309  { return _keyData; }
310 
311  Pathname path() const
312  { return _dataFile.path(); }
313 
314  const std::list<PublicKeyData> & hiddenKeys() const
315  { return _hiddenKeys; }
316 
317  protected:
319  {
320  PathInfo info( _dataFile.path() );
321  MIL << "Reading pubkey from " << info.path() << " of size " << info.size() << " and sha1 " << filesystem::checksum(info.path(), "sha1") << endl;
322 
323  static filesystem::TmpDir dir;
324  const char* argv[] =
325  {
326  "gpg",
327  "-v",
328  "--no-default-keyring",
329  "--fixed-list-mode",
330  "--with-fingerprint",
331  "--with-colons",
332  "--homedir",
333  dir.path().asString().c_str(),
334  "--quiet",
335  "--no-tty",
336  "--no-greeting",
337  "--batch",
338  "--status-fd",
339  "1",
340  _dataFile.path().asString().c_str(),
341  NULL
342  };
343  ExternalProgram prog( argv, ExternalProgram::Discard_Stderr, false, -1, true );
344 
345  PublicKeyScanner scanner;
346  for ( std::string line = prog.receiveLine(); !line.empty(); line = prog.receiveLine() )
347  {
348  scanner.scan( line );
349  }
350  prog.close();
351 
352  switch ( scanner._keys.size() )
353  {
354  case 0:
355  ZYPP_THROW( BadKeyException( "File " + _dataFile.path().asString() + " doesn't contain public key data" , _dataFile.path() ) );
356  break;
357 
358  case 1:
359  // ok.
360  _keyData = scanner._keys.back();
361  _hiddenKeys.clear();
362  break;
363 
364  default:
365  WAR << "File " << _dataFile.path().asString() << " contains multiple keys: " << scanner._keys << endl;
366  _keyData = scanner._keys.back();
367  scanner._keys.pop_back();
368  _hiddenKeys.swap( scanner._keys );
369  break;
370  }
371 
372  MIL << "Read pubkey from " << info.path() << ": " << _keyData << endl;
373  }
374 
375  private:
378  std::list<PublicKeyData> _hiddenKeys;
379 
380  public:
382  static shared_ptr<Impl> nullimpl()
383  {
384  static shared_ptr<Impl> _nullimpl( new Impl );
385  return _nullimpl;
386  }
387 
388  private:
389  friend Impl * rwcowClone<Impl>( const Impl * rhs );
391  Impl * clone() const
392  { return new Impl( *this ); }
393  };
395 
397  // class PublicKey
400  : _pimpl( Impl::nullimpl() )
401  {}
402 
403  PublicKey::PublicKey( const Pathname & file )
404  : _pimpl( new Impl( file ) )
405  {}
406 
408  : _pimpl( new Impl( sharedfile ) )
409  {}
410 
411  PublicKey::PublicKey( const filesystem::TmpFile & sharedfile, const PublicKeyData & keydata )
412  : _pimpl( new Impl( sharedfile, keydata ) )
413  {}
414 
416  {}
417 
419  { return _pimpl->keyData(); }
420 
421  Pathname PublicKey::path() const
422  { return _pimpl->path(); }
423 
424  const std::list<PublicKeyData> & PublicKey::hiddenKeys() const
425  { return _pimpl->hiddenKeys(); }
426 
427  std::string PublicKey::id() const
428  { return keyData().id(); }
429 
430  std::string PublicKey::name() const
431  { return keyData().name(); }
432 
433  std::string PublicKey::fingerprint() const
434  { return keyData().fingerprint(); }
435 
437  { return keyData().created(); }
438 
440  { return keyData().expires(); }
441 
442  bool PublicKey::expired() const
443  { return keyData().expired(); }
444 
446  { return keyData().daysToLive(); }
447 
448  std::string PublicKey::expiresAsString() const
449  { return keyData().expiresAsString(); }
450 
451  std::string PublicKey::gpgPubkeyVersion() const
452  { return keyData().gpgPubkeyVersion(); }
453 
454  std::string PublicKey::gpgPubkeyRelease() const
455  { return keyData().gpgPubkeyRelease(); }
456 
457  std::string PublicKey::asString() const
458  { return keyData().asString(); }
459 
461  { return rhs.keyData() == keyData(); }
462 
463  bool PublicKey::operator==( std::string sid ) const
464  { return sid == id(); }
465 
466  std::ostream & dumpOn( std::ostream & str, const PublicKey & obj )
467  { return dumpOn( str, obj.keyData() ); }
468 
470 } // namespace zypp