apt  2.2.4
About: Apt (Advanced Package Tool) is a management system for software packages (Debian/Ubuntu). Release series 2.2.
  Fossies Dox: apt-2.2.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

gpgv.cc
Go to the documentation of this file.
1 #include <config.h>
2 
3 #include "aptmethod.h"
4 #include <apt-pkg/configuration.h>
5 #include <apt-pkg/error.h>
6 #include <apt-pkg/fileutl.h>
7 #include <apt-pkg/gpgv.h>
8 #include <apt-pkg/strutl.h>
9 
10 #include <ctype.h>
11 #include <errno.h>
12 #include <stddef.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/wait.h>
17 #include <unistd.h>
18 
19 #include <algorithm>
20 #include <array>
21 #include <iostream>
22 #include <iterator>
23 #include <map>
24 #include <sstream>
25 #include <string>
26 #include <vector>
27 
28 #include <apti18n.h>
29 
30 using std::string;
31 using std::vector;
32 
33 #define GNUPGPREFIX "[GNUPG:]"
34 #define GNUPGBADSIG "[GNUPG:] BADSIG"
35 #define GNUPGERRSIG "[GNUPG:] ERRSIG"
36 #define GNUPGNOPUBKEY "[GNUPG:] NO_PUBKEY"
37 #define GNUPGVALIDSIG "[GNUPG:] VALIDSIG"
38 #define GNUPGGOODSIG "[GNUPG:] GOODSIG"
39 #define GNUPGEXPKEYSIG "[GNUPG:] EXPKEYSIG"
40 #define GNUPGEXPSIG "[GNUPG:] EXPSIG"
41 #define GNUPGREVKEYSIG "[GNUPG:] REVKEYSIG"
42 #define GNUPGNODATA "[GNUPG:] NODATA"
43 #define APTKEYWARNING "[APTKEY:] WARNING"
44 #define APTKEYERROR "[APTKEY:] ERROR"
45 
46 struct Digest {
47  enum class State {
48  Untrusted,
49  Weak,
50  Trusted,
51  } state;
52  char name[32];
53 
54  State getState() const {
55  std::string optionUntrusted;
56  std::string optionWeak;
57  strprintf(optionUntrusted, "APT::Hashes::%s::Untrusted", name);
58  strprintf(optionWeak, "APT::Hashes::%s::Weak", name);
59  if (_config->FindB(optionUntrusted, false) == true)
60  return State::Untrusted;
61  if (_config->FindB(optionWeak, false) == true)
62  return State::Weak;
63 
64  return state;
65  }
66 };
67 
68 static constexpr Digest Digests[] = {
69  {Digest::State::Untrusted, "Invalid digest"},
70  {Digest::State::Untrusted, "MD5"},
71  {Digest::State::Untrusted, "SHA1"},
72  {Digest::State::Untrusted, "RIPE-MD/160"},
73  {Digest::State::Trusted, "Reserved digest"},
74  {Digest::State::Trusted, "Reserved digest"},
75  {Digest::State::Trusted, "Reserved digest"},
76  {Digest::State::Trusted, "Reserved digest"},
77  {Digest::State::Trusted, "SHA256"},
78  {Digest::State::Trusted, "SHA384"},
79  {Digest::State::Trusted, "SHA512"},
80  {Digest::State::Trusted, "SHA224"},
81 };
82 
83 static Digest FindDigest(std::string const & Digest)
84 {
85  int id = atoi(Digest.c_str());
86  if (id >= 0 && static_cast<unsigned>(id) < APT_ARRAY_SIZE(Digests))
87  {
88  return Digests[id];
89  } else {
90  return Digests[0];
91  }
92 }
93 
94 struct Signer {
95  std::string key;
96  std::string note;
97 };
98 static bool IsTheSameKey(std::string const &validsig, std::string const &goodsig) {
99  // VALIDSIG reports a fingerprint (40 = 24 + 16), GOODSIG can be longid (16) or
100  // fingerprint according to documentation in DETAILS.gz
101  if (goodsig.length() == 40 + strlen("GOODSIG "))
102  return validsig.compare(0, 40, goodsig, strlen("GOODSIG "), 40) == 0;
103  return validsig.compare(24, 16, goodsig, strlen("GOODSIG "), 16) == 0;
104 }
105 
107  std::vector<std::string> Good;
108  std::vector<std::string> Bad;
109  std::vector<std::string> Worthless;
110  // a worthless signature is a expired or revoked one
111  std::vector<Signer> SoonWorthless;
112  std::vector<std::string> NoPubKey;
113  std::vector<std::string> Valid;
114  std::vector<std::string> SignedBy;
115 };
116 class GPGVMethod : public aptMethod
117 {
118  private:
119  string VerifyGetSigners(const char *file, const char *outfile,
120  vector<string> const &keyFpts,
121  vector<string> const &keyFiles,
122  SignersStorage &Signers);
123  protected:
124  virtual bool URIAcquire(std::string const &Message, FetchItem *Itm) APT_OVERRIDE;
125  public:
127 };
128 static void PushEntryWithKeyID(std::vector<std::string> &Signers, char * const buffer, bool const Debug)
129 {
130  char * const msg = buffer + sizeof(GNUPGPREFIX);
131  char *p = msg;
132  // skip the message
133  while (*p && !isspace(*p))
134  ++p;
135  // skip the separator whitespace
136  ++p;
137  // skip the hexdigit fingerprint
138  while (*p && isxdigit(*p))
139  ++p;
140  // cut the rest from the message
141  *p = '\0';
142  if (Debug == true)
143  std::clog << "Got " << msg << " !" << std::endl;
144  Signers.push_back(msg);
145 }
146 static void PushEntryWithUID(std::vector<std::string> &Signers, char * const buffer, bool const Debug)
147 {
148  std::string msg = buffer + sizeof(GNUPGPREFIX);
149  auto const nuke = msg.find_last_not_of("\n\t\r");
150  if (nuke != std::string::npos)
151  msg.erase(nuke + 1);
152  if (Debug == true)
153  std::clog << "Got " << msg << " !" << std::endl;
154  Signers.push_back(msg);
155 }
156 static void implodeVector(std::vector<std::string> const &vec, std::ostream &out, char const * const sep)
157 {
158  if (vec.empty())
159  return;
160  std::copy(vec.begin(), std::prev(vec.end()), std::ostream_iterator<std::string>(out, sep));
161  out << *vec.rbegin();
162  return;
163 }
164 string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile,
165  vector<string> const &keyFpts,
166  vector<string> const &keyFiles,
167  SignersStorage &Signers)
168 {
169  bool const Debug = DebugEnabled();
170 
171  if (Debug == true)
172  std::clog << "inside VerifyGetSigners" << std::endl;
173 
174  int fd[2];
175 
176  if (pipe(fd) < 0)
177  return "Couldn't create pipe";
178 
179  pid_t pid = fork();
180  if (pid < 0)
181  return string("Couldn't spawn new process") + strerror(errno);
182  else if (pid == 0)
183  {
184  std::ostringstream keys;
185  implodeVector(keyFiles, keys, ",");
186  ExecGPGV(outfile, file, 3, fd, keys.str());
187  }
188  close(fd[1]);
189 
190  FILE *pipein = fdopen(fd[0], "r");
191 
192  // Loop over the output of apt-key (which really is gnupg), and check the signatures.
193  std::vector<std::string> ErrSigners;
194  std::map<std::string, std::vector<std::string>> SubKeyMapping;
195  size_t buffersize = 0;
196  char *buffer = NULL;
197  bool gotNODATA = false;
198  while (1)
199  {
200  if (getline(&buffer, &buffersize, pipein) == -1)
201  break;
202  if (Debug == true)
203  std::clog << "Read: " << buffer << std::endl;
204 
205  // Push the data into three separate vectors, which
206  // we later concatenate. They're kept separate so
207  // if we improve the apt method communication stuff later
208  // it will be better.
209  if (strncmp(buffer, GNUPGBADSIG, sizeof(GNUPGBADSIG)-1) == 0)
210  PushEntryWithUID(Signers.Bad, buffer, Debug);
211  else if (strncmp(buffer, GNUPGERRSIG, sizeof(GNUPGERRSIG)-1) == 0)
212  PushEntryWithKeyID(ErrSigners, buffer, Debug);
213  else if (strncmp(buffer, GNUPGNOPUBKEY, sizeof(GNUPGNOPUBKEY)-1) == 0)
214  {
215  PushEntryWithKeyID(Signers.NoPubKey, buffer, Debug);
216  ErrSigners.erase(std::remove_if(ErrSigners.begin(), ErrSigners.end(), [&](std::string const &errsig) {
217  return errsig.compare(strlen("ERRSIG "), 16, buffer, sizeof(GNUPGNOPUBKEY), 16) == 0; }), ErrSigners.end());
218  }
219  else if (strncmp(buffer, GNUPGNODATA, sizeof(GNUPGNODATA)-1) == 0)
220  gotNODATA = true;
221  else if (strncmp(buffer, GNUPGEXPKEYSIG, sizeof(GNUPGEXPKEYSIG)-1) == 0)
222  PushEntryWithUID(Signers.Worthless, buffer, Debug);
223  else if (strncmp(buffer, GNUPGEXPSIG, sizeof(GNUPGEXPSIG)-1) == 0)
224  PushEntryWithUID(Signers.Worthless, buffer, Debug);
225  else if (strncmp(buffer, GNUPGREVKEYSIG, sizeof(GNUPGREVKEYSIG)-1) == 0)
226  PushEntryWithUID(Signers.Worthless, buffer, Debug);
227  else if (strncmp(buffer, GNUPGGOODSIG, sizeof(GNUPGGOODSIG)-1) == 0)
228  PushEntryWithKeyID(Signers.Good, buffer, Debug);
229  else if (strncmp(buffer, GNUPGVALIDSIG, sizeof(GNUPGVALIDSIG)-1) == 0)
230  {
231  std::istringstream iss(buffer + sizeof(GNUPGVALIDSIG));
232  vector<string> tokens{std::istream_iterator<string>{iss},
233  std::istream_iterator<string>{}};
234  auto const sig = tokens[0];
235  // Reject weak digest algorithms
236  Digest digest = FindDigest(tokens[7]);
237  switch (digest.getState()) {
238  case Digest::State::Weak:
239  // Treat them like an expired key: For that a message about expiry
240  // is emitted, a VALIDSIG, but no GOODSIG.
241  Signers.SoonWorthless.push_back({sig, digest.name});
242  if (Debug == true)
243  std::clog << "Got weak VALIDSIG, key ID: " << sig << std::endl;
244  break;
246  // Treat them like an expired key: For that a message about expiry
247  // is emitted, a VALIDSIG, but no GOODSIG.
248  Signers.Worthless.push_back(sig);
249  Signers.Good.erase(std::remove_if(Signers.Good.begin(), Signers.Good.end(), [&](std::string const &goodsig) {
250  return IsTheSameKey(sig, goodsig); }), Signers.Good.end());
251  if (Debug == true)
252  std::clog << "Got untrusted VALIDSIG, key ID: " << sig << std::endl;
253  break;
254 
256  if (Debug == true)
257  std::clog << "Got trusted VALIDSIG, key ID: " << sig << std::endl;
258  break;
259  }
260 
261  Signers.Valid.push_back(sig);
262 
263  if (tokens.size() > 9 && sig != tokens[9])
264  SubKeyMapping[tokens[9]].emplace_back(sig);
265  }
266  else if (strncmp(buffer, APTKEYWARNING, sizeof(APTKEYWARNING)-1) == 0)
267  Warning(buffer + sizeof(APTKEYWARNING));
268  else if (strncmp(buffer, APTKEYERROR, sizeof(APTKEYERROR)-1) == 0)
269  _error->Error("%s", buffer + sizeof(APTKEYERROR));
270  }
271  fclose(pipein);
272  free(buffer);
273  std::move(ErrSigners.begin(), ErrSigners.end(), std::back_inserter(Signers.Worthless));
274 
275  // apt-key has a --keyid parameter, but this requires gpg, so we call it without it
276  // and instead check after the fact which keyids where used for verification
277  if (keyFpts.empty() == false)
278  {
279  if (Debug == true)
280  {
281  std::clog << "GoodSigs needs to be limited to keyid(s): ";
282  implodeVector(keyFpts, std::clog, ", ");
283  std::clog << "\n";
284  }
285  std::vector<std::string> filteredGood;
286  for (auto &&good: Signers.Good)
287  {
288  if (Debug == true)
289  std::clog << "Key " << good << " is good sig, is it also a valid and allowed one? ";
290  bool found = false;
291  for (auto l : keyFpts)
292  {
293  bool exactKey = false;
294  if (APT::String::Endswith(l, "!"))
295  {
296  exactKey = true;
297  l.erase(l.length() - 1);
298  }
299  if (IsTheSameKey(l, good))
300  {
301  // GOODSIG might be "just" a longid, so we check VALIDSIG which is always a fingerprint
302  if (std::find(Signers.Valid.cbegin(), Signers.Valid.cend(), l) == Signers.Valid.cend())
303  continue;
304  found = true;
305  Signers.SignedBy.push_back(l + "!");
306  break;
307  }
308  else if (exactKey == false)
309  {
310  auto const primary = SubKeyMapping.find(l);
311  if (primary == SubKeyMapping.end())
312  continue;
313  auto const validsubkeysig = std::find_if(primary->second.cbegin(), primary->second.cend(), [&](auto const subkey) {
314  return IsTheSameKey(subkey, good) && std::find(Signers.Valid.cbegin(), Signers.Valid.cend(), subkey) != Signers.Valid.cend();
315  });
316  if (validsubkeysig != primary->second.cend())
317  {
318  found = true;
319  Signers.SignedBy.push_back(l);
320  Signers.SignedBy.push_back(*validsubkeysig + "!");
321  break;
322  }
323  }
324  }
325  if (Debug)
326  std::clog << (found ? "yes" : "no") << "\n";
327  if (found)
328  filteredGood.emplace_back(std::move(good));
329  else
330  Signers.NoPubKey.emplace_back(std::move(good));
331  }
332  Signers.Good= std::move(filteredGood);
333  }
334  else
335  {
336  // for gpg an expired key is valid, too, but we want only the valid & good ones
337  for (auto const &v : Signers.Valid)
338  if (std::any_of(Signers.Good.begin(), Signers.Good.end(),
339  [&v](std::string const &g) { return IsTheSameKey(v, g); }))
340  Signers.SignedBy.push_back(v + "!");
341  for (auto sub : SubKeyMapping)
342  if (std::any_of(sub.second.begin(), sub.second.end(),
343  [&](std::string const &s) {
344  if (std::find(Signers.Valid.begin(), Signers.Valid.end(), s) == Signers.Valid.end())
345  return false;
346  return std::any_of(Signers.Good.begin(), Signers.Good.end(),
347  [&s](std::string const &g) { return IsTheSameKey(s, g); });
348  }))
349  Signers.SignedBy.push_back(sub.first);
350  }
351  std::sort(Signers.SignedBy.begin(), Signers.SignedBy.end());
352 
353  int status;
354  waitpid(pid, &status, 0);
355  if (Debug == true)
356  {
357  ioprintf(std::clog, "gpgv exited with status %i\n", WEXITSTATUS(status));
358  }
359 
360  if (Debug)
361  {
362  std::cerr << "Summary:\n Good: ";
363  implodeVector(Signers.Good, std::cerr, ", ");
364  std::cerr << "\n Valid: ";
365  implodeVector(Signers.Valid, std::cerr, ", ");
366  std::cerr << "\n Bad: ";
367  implodeVector(Signers.Bad, std::cerr, ", ");
368  std::cerr << "\n Worthless: ";
369  implodeVector(Signers.Worthless, std::cerr, ", ");
370  std::cerr << "\n SoonWorthless: ";
371  std::for_each(Signers.SoonWorthless.begin(), Signers.SoonWorthless.end(), [](Signer const &sig) { std::cerr << sig.key << ", "; });
372  std::cerr << "\n NoPubKey: ";
373  implodeVector(Signers.NoPubKey, std::cerr, ", ");
374  std::cerr << "\n Signed-By: ";
375  implodeVector(Signers.SignedBy, std::cerr, ", ");
376  std::cerr << std::endl << " NODATA: " << (gotNODATA ? "yes" : "no") << std::endl;
377  }
378 
379  if (WEXITSTATUS(status) == 112)
380  {
381  // acquire system checks for "NODATA" to generate GPG errors (the others are only warnings)
382  std::string errmsg;
383  //TRANSLATORS: %s is a single techy word like 'NODATA'
384  strprintf(errmsg, _("Clearsigned file isn't valid, got '%s' (does the network require authentication?)"), "NODATA");
385  return errmsg;
386  }
387  else if (gotNODATA)
388  {
389  // acquire system checks for "NODATA" to generate GPG errors (the others are only warnings)
390  std::string errmsg;
391  //TRANSLATORS: %s is a single techy word like 'NODATA'
392  strprintf(errmsg, _("Signed file isn't valid, got '%s' (does the network require authentication?)"), "NODATA");
393  return errmsg;
394  }
395  else if (WEXITSTATUS(status) == 0)
396  {
397  if (keyFpts.empty() == false)
398  {
399  // gpgv will report success, but we want to enforce a certain keyring
400  // so if we haven't found the key the valid we found is in fact invalid
401  if (Signers.Good.empty())
402  return _("At least one invalid signature was encountered.");
403  }
404  else
405  {
406  if (Signers.Good.empty())
407  return _("Internal error: Good signature, but could not determine key fingerprint?!");
408  }
409  return "";
410  }
411  else if (WEXITSTATUS(status) == 1)
412  return _("At least one invalid signature was encountered.");
413  else if (WEXITSTATUS(status) == 111)
414  return _("Could not execute 'apt-key' to verify signature (is gnupg installed?)");
415  else
416  return _("Unknown error executing apt-key");
417 }
418 
419 bool GPGVMethod::URIAcquire(std::string const &Message, FetchItem *Itm)
420 {
421  URI const Get(Itm->Uri);
422  std::string const Path = DecodeSendURI(Get.Host + Get.Path); // To account for relative paths
423  SignersStorage Signers;
424 
425  std::vector<std::string> keyFpts, keyFiles;
426  for (auto &&key : VectorizeString(LookupTag(Message, "Signed-By"), ','))
427  if (key.empty() == false && key[0] == '/')
428  keyFiles.emplace_back(std::move(key));
429  else
430  keyFpts.emplace_back(std::move(key));
431 
432  // Run apt-key on file, extract contents and get the key ID of the signer
433  string const msg = VerifyGetSigners(Path.c_str(), Itm->DestFile.c_str(), keyFpts, keyFiles, Signers);
434  if (_error->PendingError())
435  return false;
436 
437  // Check if all good signers are soon worthless and warn in that case
438  if (std::all_of(Signers.Good.begin(), Signers.Good.end(), [&](std::string const &good) {
439  return std::any_of(Signers.SoonWorthless.begin(), Signers.SoonWorthless.end(), [&](Signer const &weak) {
440  return IsTheSameKey(weak.key, good);
441  });
442  }))
443  {
444  for (auto const & Signer : Signers.SoonWorthless)
445  {
446  std::string msg;
447  // TRANSLATORS: The second %s is the reason and is untranslated for repository owners.
448  strprintf(msg, _("Signature by key %s uses weak digest algorithm (%s)"), Signer.key.c_str(), Signer.note.c_str());
449  Warning(std::move(msg));
450  }
451  }
452 
453  if (Signers.Good.empty() || !Signers.Bad.empty() || !Signers.NoPubKey.empty())
454  {
455  string errmsg;
456  // In this case, something bad probably happened, so we just go
457  // with what the other method gave us for an error message.
458  if (Signers.Bad.empty() && Signers.Worthless.empty() && Signers.NoPubKey.empty())
459  errmsg = msg;
460  else
461  {
462  if (!Signers.Bad.empty())
463  {
464  errmsg += _("The following signatures were invalid:\n");
465  for (auto const &I : Signers.Bad)
466  errmsg.append(I).append("\n");
467  }
468  if (!Signers.Worthless.empty())
469  {
470  errmsg += _("The following signatures were invalid:\n");
471  for (auto const &I : Signers.Worthless)
472  errmsg.append(I).append("\n");
473  }
474  if (!Signers.NoPubKey.empty())
475  {
476  errmsg += _("The following signatures couldn't be verified because the public key is not available:\n");
477  for (auto const &I : Signers.NoPubKey)
478  errmsg.append(I).append("\n");
479  }
480  }
481  // this is only fatal if we have no good sigs or if we have at
482  // least one bad signature. good signatures and NoPubKey signatures
483  // happen easily when a file is signed with multiple signatures
484  if (Signers.Good.empty() or !Signers.Bad.empty())
485  return _error->Error("%s", errmsg.c_str());
486  }
487 
488  std::unordered_map<std::string, std::string> fields;
489  fields.emplace("URI", Itm->Uri);
490  fields.emplace("Filename", Itm->DestFile);
491  if (Signers.SignedBy.empty() == false)
492  {
493  std::ostringstream out;
494  implodeVector(Signers.SignedBy, out, "\n");
495  fields.emplace("Signed-By", out.str());
496  }
497  {
498  // Just pass the raw output up, because passing it as a real data
499  // structure is too difficult with the method stuff. We keep it
500  // as three separate vectors for future extensibility.
501  std::vector<std::string> gpgvoutput;
502  std::move(Signers.Good.begin(), Signers.Good.end(), std::back_inserter(gpgvoutput));
503  std::move(Signers.Bad.begin(), Signers.Bad.end(), std::back_inserter(gpgvoutput));
504  std::move(Signers.NoPubKey.begin(), Signers.NoPubKey.end(), std::back_inserter(gpgvoutput));
505  if (gpgvoutput.empty() == false)
506  {
507  std::ostringstream out;
508  implodeVector(gpgvoutput, out, "\n");
509  fields.emplace("GPGVOutput", out.str());
510  }
511  }
512  SendMessage("201 URI Done", std::move(fields));
513  Dequeue();
514 
515  if (DebugEnabled())
516  std::clog << "apt-key succeeded\n";
517 
518  return true;
519 }
520 
521 
522 int main()
523 {
524  return GPGVMethod().Run();
525 }
strprintf(m, msg, repo.c_str())
static bool std::string const metaIndex const *const pkgAcqMetaClearSig *const pkgAcquire::Item *const I
static char const *const msg
void ExecGPGV(std::string const &File, std::string const &FileGPG, int const &statusfd, int fd[2], std::string const &key)
generates and run the command to verify a file with gpgv
Definition: gpgv.cc:148
bool FindB(const char *Name, bool const &Default=false) const
string VerifyGetSigners(const char *file, const char *outfile, vector< string > const &keyFpts, vector< string > const &keyFiles, SignersStorage &Signers)
Definition: gpgv.cc:164
GPGVMethod()
Definition: gpgv.cc:126
virtual bool URIAcquire(std::string const &Message, FetchItem *Itm) APT_OVERRIDE
Definition: gpgv.cc:419
Definition: strutl.h:193
std::string Path
Definition: strutl.h:202
std::string Host
Definition: strutl.h:201
void Warning(std::string &&msg)
Definition: aptmethod.h:387
bool DebugEnabled() const
Definition: aptmethod.h:409
static std::string DecodeSendURI(std::string const &part)
Definition: aptmethod.h:493
int Run(bool Single=false)
FILE
Configuration * _config
#define APT_ARRAY_SIZE(a)
Definition: macros.h:18
#define APT_OVERRIDE
Definition: macros.h:111
#define APT_HIDDEN
Definition: macros.h:78
static bool IsTheSameKey(std::string const &validsig, std::string const &goodsig)
Definition: gpgv.cc:98
#define GNUPGBADSIG
Definition: gpgv.cc:34
#define GNUPGEXPSIG
Definition: gpgv.cc:40
#define GNUPGVALIDSIG
Definition: gpgv.cc:37
static void PushEntryWithUID(std::vector< std::string > &Signers, char *const buffer, bool const Debug)
Definition: gpgv.cc:146
static void implodeVector(std::vector< std::string > const &vec, std::ostream &out, char const *const sep)
Definition: gpgv.cc:156
#define GNUPGERRSIG
Definition: gpgv.cc:35
static void PushEntryWithKeyID(std::vector< std::string > &Signers, char *const buffer, bool const Debug)
Definition: gpgv.cc:128
#define GNUPGEXPKEYSIG
Definition: gpgv.cc:39
#define GNUPGNODATA
Definition: gpgv.cc:42
static Digest FindDigest(std::string const &Digest)
Definition: gpgv.cc:83
#define GNUPGREVKEYSIG
Definition: gpgv.cc:41
#define APTKEYERROR
Definition: gpgv.cc:44
#define GNUPGNOPUBKEY
Definition: gpgv.cc:36
#define APTKEYWARNING
Definition: gpgv.cc:43
#define GNUPGGOODSIG
Definition: gpgv.cc:38
int main()
Definition: gpgv.cc:522
static constexpr Digest Digests[]
Definition: gpgv.cc:68
#define GNUPGPREFIX
Definition: gpgv.cc:33
bool Endswith(const std::string &s, const std::string &end)
Definition: strutl.cc:77
Definition: gpgv.cc:46
enum Digest::State state
State
Definition: gpgv.cc:47
State getState() const
Definition: gpgv.cc:54
char name[32]
Definition: gpgv.cc:52
Definition: gpgv.cc:94
std::string key
Definition: gpgv.cc:95
std::string note
Definition: gpgv.cc:96
std::vector< std::string > SignedBy
Definition: gpgv.cc:114
std::vector< std::string > NoPubKey
Definition: gpgv.cc:112
std::vector< std::string > Bad
Definition: gpgv.cc:108
std::vector< Signer > SoonWorthless
Definition: gpgv.cc:111
std::vector< std::string > Valid
Definition: gpgv.cc:113
std::vector< std::string > Worthless
Definition: gpgv.cc:109
std::vector< std::string > Good
Definition: gpgv.cc:107
void ioprintf(ostream &out, const char *format,...)
Definition: strutl.cc:1433
vector< string > VectorizeString(string const &haystack, char const &split)
Definition: strutl.cc:1308
std::string LookupTag(const std::string &Message, const char *TagC, const char *Default)
Definition: strutl.cc:742