From 6b01cd087e6f92c5511fe6eea73699e075aa699a Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Tue, 11 Sep 2018 16:45:06 +0200 Subject: Refactor internal Signers information storage in gpgv Having a method take a bunch of string vectors is bad style, so we change this to a wrapping struct and adapt the rest of the code brushing it up slightly in the process, which results even in a slightly "better" debug output, no practical change otherwise. Gbp-Dch: Ignore --- methods/gpgv.cc | 140 +++++++++++++++++++++++++++----------------------------- 1 file changed, 67 insertions(+), 73 deletions(-) (limited to 'methods') diff --git a/methods/gpgv.cc b/methods/gpgv.cc index 9135b49c5..1b9f6dd64 100644 --- a/methods/gpgv.cc +++ b/methods/gpgv.cc @@ -102,17 +102,21 @@ static bool IsTheSameKey(std::string const &validsig, std::string const &goodsig return validsig.compare(24, 16, goodsig, strlen("GOODSIG "), 16) == 0; } +struct APT_HIDDEN SignersStorage { + std::vector Good; + std::vector Bad; + std::vector Worthless; + // a worthless signature is a expired or revoked one + std::vector SoonWorthless; + std::vector NoPubKey; +}; class GPGVMethod : public aptMethod { private: string VerifyGetSigners(const char *file, const char *outfile, vector const &keyFpts, vector const &keyFiles, - vector &GoodSigners, - vector &BadSigners, - vector &WorthlessSigners, - vector &SoonWorthlessSigners, - vector &NoPubKeySigners); + SignersStorage &Signers); protected: virtual bool URIAcquire(std::string const &Message, FetchItem *Itm) APT_OVERRIDE; public: @@ -146,14 +150,18 @@ static void PushEntryWithUID(std::vector &Signers, char * const buf std::clog << "Got " << msg << " !" << std::endl; Signers.push_back(msg); } +static void implodeVector(std::vector const &vec, std::ostream &out, char const * const sep) +{ + if (vec.empty()) + return; + std::copy(vec.begin(), std::prev(vec.end()), std::ostream_iterator(out, sep)); + out << *vec.rbegin(); + return; +} string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, vector const &keyFpts, vector const &keyFiles, - vector &GoodSigners, - vector &BadSigners, - vector &WorthlessSigners, - vector &SoonWorthlessSigners, - vector &NoPubKeySigners) + SignersStorage &Signers) { bool const Debug = DebugEnabled(); @@ -171,11 +179,7 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, else if (pid == 0) { std::ostringstream keys; - if (keyFiles.empty() == false) - { - std::copy(keyFiles.begin(), keyFiles.end()-1, std::ostream_iterator(keys, ",")); - keys << *keyFiles.rbegin(); - } + implodeVector(keyFiles, keys, ","); ExecGPGV(outfile, file, 3, fd, keys.str()); } close(fd[1]); @@ -201,25 +205,25 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, // if we improve the apt method communication stuff later // it will be better. if (strncmp(buffer, GNUPGBADSIG, sizeof(GNUPGBADSIG)-1) == 0) - PushEntryWithUID(BadSigners, buffer, Debug); + PushEntryWithUID(Signers.Bad, buffer, Debug); else if (strncmp(buffer, GNUPGERRSIG, sizeof(GNUPGERRSIG)-1) == 0) PushEntryWithKeyID(ErrSigners, buffer, Debug); else if (strncmp(buffer, GNUPGNOPUBKEY, sizeof(GNUPGNOPUBKEY)-1) == 0) { - PushEntryWithKeyID(NoPubKeySigners, buffer, Debug); + PushEntryWithKeyID(Signers.NoPubKey, buffer, Debug); ErrSigners.erase(std::remove_if(ErrSigners.begin(), ErrSigners.end(), [&](std::string const &errsig) { return errsig.compare(strlen("ERRSIG "), 16, buffer, sizeof(GNUPGNOPUBKEY), 16) == 0; }), ErrSigners.end()); } else if (strncmp(buffer, GNUPGNODATA, sizeof(GNUPGNODATA)-1) == 0) gotNODATA = true; else if (strncmp(buffer, GNUPGEXPKEYSIG, sizeof(GNUPGEXPKEYSIG)-1) == 0) - PushEntryWithUID(WorthlessSigners, buffer, Debug); + PushEntryWithUID(Signers.Worthless, buffer, Debug); else if (strncmp(buffer, GNUPGEXPSIG, sizeof(GNUPGEXPSIG)-1) == 0) - PushEntryWithUID(WorthlessSigners, buffer, Debug); + PushEntryWithUID(Signers.Worthless, buffer, Debug); else if (strncmp(buffer, GNUPGREVKEYSIG, sizeof(GNUPGREVKEYSIG)-1) == 0) - PushEntryWithUID(WorthlessSigners, buffer, Debug); + PushEntryWithUID(Signers.Worthless, buffer, Debug); else if (strncmp(buffer, GNUPGGOODSIG, sizeof(GNUPGGOODSIG)-1) == 0) - PushEntryWithKeyID(GoodSigners, buffer, Debug); + PushEntryWithKeyID(Signers.Good, buffer, Debug); else if (strncmp(buffer, GNUPGVALIDSIG, sizeof(GNUPGVALIDSIG)-1) == 0) { std::istringstream iss(buffer + sizeof(GNUPGVALIDSIG)); @@ -232,16 +236,16 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, case Digest::State::Weak: // Treat them like an expired key: For that a message about expiry // is emitted, a VALIDSIG, but no GOODSIG. - SoonWorthlessSigners.push_back({sig, digest.name}); + Signers.SoonWorthless.push_back({sig, digest.name}); if (Debug == true) std::clog << "Got weak VALIDSIG, key ID: " << sig << std::endl; break; case Digest::State::Untrusted: // Treat them like an expired key: For that a message about expiry // is emitted, a VALIDSIG, but no GOODSIG. - WorthlessSigners.push_back(sig); - GoodSigners.erase(std::remove_if(GoodSigners.begin(), GoodSigners.end(), [&](std::string const &goodsig) { - return IsTheSameKey(sig, goodsig); }), GoodSigners.end()); + Signers.Worthless.push_back(sig); + Signers.Good.erase(std::remove_if(Signers.Good.begin(), Signers.Good.end(), [&](std::string const &goodsig) { + return IsTheSameKey(sig, goodsig); }), Signers.Good.end()); if (Debug == true) std::clog << "Got untrusted VALIDSIG, key ID: " << sig << std::endl; break; @@ -264,7 +268,7 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, } fclose(pipein); free(buffer); - std::move(ErrSigners.begin(), ErrSigners.end(), std::back_inserter(WorthlessSigners)); + std::move(ErrSigners.begin(), ErrSigners.end(), std::back_inserter(Signers.Worthless)); // apt-key has a --keyid parameter, but this requires gpg, so we call it without it // and instead check after the fact which keyids where used for verification @@ -273,11 +277,11 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, if (Debug == true) { std::clog << "GoodSigs needs to be limited to keyid(s): "; - std::copy(keyFpts.begin(), keyFpts.end(), std::ostream_iterator(std::clog, ", ")); + implodeVector(keyFpts, std::clog, ", "); std::clog << "\n"; } std::vector filteredGood; - for (auto &&good: GoodSigners) + for (auto &&good: Signers.Good) { if (Debug == true) std::clog << "Key " << good << " is good sig, is it also a valid and allowed one? "; @@ -320,9 +324,9 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, if (found) filteredGood.emplace_back(std::move(good)); else - NoPubKeySigners.emplace_back(std::move(good)); + Signers.NoPubKey.emplace_back(std::move(good)); } - GoodSigners = std::move(filteredGood); + Signers.Good= std::move(filteredGood); } int status; @@ -334,16 +338,16 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, if (Debug) { - std::cerr << "Summary:" << std::endl << " Good: "; - std::copy(GoodSigners.begin(), GoodSigners.end(), std::ostream_iterator(std::cerr, ", ")); - std::cerr << std::endl << " Bad: "; - std::copy(BadSigners.begin(), BadSigners.end(), std::ostream_iterator(std::cerr, ", ")); - std::cerr << std::endl << " Worthless: "; - std::copy(WorthlessSigners.begin(), WorthlessSigners.end(), std::ostream_iterator(std::cerr, ", ")); - std::cerr << std::endl << " SoonWorthless: "; - std::for_each(SoonWorthlessSigners.begin(), SoonWorthlessSigners.end(), [](Signer const &sig) { std::cerr << sig.key << ", "; }); - std::cerr << std::endl << " NoPubKey: "; - std::copy(NoPubKeySigners.begin(), NoPubKeySigners.end(), std::ostream_iterator(std::cerr, ", ")); + std::cerr << "Summary:\n Good: "; + implodeVector(Signers.Good, std::cerr, ", "); + std::cerr << "\n Bad: "; + implodeVector(Signers.Bad, std::cerr, ", "); + std::cerr << "\n Worthless: "; + implodeVector(Signers.Worthless, std::cerr, ", "); + std::cerr << "\n SoonWorthless: "; + std::for_each(Signers.SoonWorthless.begin(), Signers.SoonWorthless.end(), [](Signer const &sig) { std::cerr << sig.key << ", "; }); + std::cerr << "\n NoPubKey: "; + implodeVector(Signers.NoPubKey, std::cerr, ", "); std::cerr << std::endl << " NODATA: " << (gotNODATA ? "yes" : "no") << std::endl; } @@ -369,12 +373,12 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, { // gpgv will report success, but we want to enforce a certain keyring // so if we haven't found the key the valid we found is in fact invalid - if (GoodSigners.empty()) + if (Signers.Good.empty()) return _("At least one invalid signature was encountered."); } else { - if (GoodSigners.empty()) + if (Signers.Good.empty()) return _("Internal error: Good signature, but could not determine key fingerprint?!"); } return ""; @@ -391,13 +395,8 @@ bool GPGVMethod::URIAcquire(std::string const &Message, FetchItem *Itm) { URI const Get = Itm->Uri; string const Path = Get.Host + Get.Path; // To account for relative paths - vector GoodSigners; - vector BadSigners; - // a worthless signature is a expired or revoked one - vector WorthlessSigners; - vector SoonWorthlessSigners; - vector NoPubKeySigners; - + SignersStorage Signers; + FetchResult Res; Res.Filename = Itm->DestFile; URIStart(Res); @@ -410,68 +409,63 @@ bool GPGVMethod::URIAcquire(std::string const &Message, FetchItem *Itm) keyFpts.emplace_back(std::move(key)); // Run apt-key on file, extract contents and get the key ID of the signer - string const msg = VerifyGetSigners(Path.c_str(), Itm->DestFile.c_str(), keyFpts, keyFiles, - GoodSigners, BadSigners, WorthlessSigners, - SoonWorthlessSigners, NoPubKeySigners); + string const msg = VerifyGetSigners(Path.c_str(), Itm->DestFile.c_str(), keyFpts, keyFiles, Signers); if (_error->PendingError()) return false; // Check if all good signers are soon worthless and warn in that case - if (std::all_of(GoodSigners.begin(), GoodSigners.end(), [&](std::string const &good) { - return std::any_of(SoonWorthlessSigners.begin(), SoonWorthlessSigners.end(), [&](Signer const &weak) { + if (std::all_of(Signers.Good.begin(), Signers.Good.end(), [&](std::string const &good) { + return std::any_of(Signers.SoonWorthless.begin(), Signers.SoonWorthless.end(), [&](Signer const &weak) { return IsTheSameKey(weak.key, good); }); })) { - for (auto const & Signer : SoonWorthlessSigners) + for (auto const & Signer : Signers.SoonWorthless) // TRANSLATORS: The second %s is the reason and is untranslated for repository owners. Warning(_("Signature by key %s uses weak digest algorithm (%s)"), Signer.key.c_str(), Signer.note.c_str()); } - if (GoodSigners.empty() || !BadSigners.empty() || !NoPubKeySigners.empty()) + if (Signers.Good.empty() || !Signers.Bad.empty() || !Signers.NoPubKey.empty()) { string errmsg; // In this case, something bad probably happened, so we just go // with what the other method gave us for an error message. - if (BadSigners.empty() && WorthlessSigners.empty() && NoPubKeySigners.empty()) + if (Signers.Bad.empty() && Signers.Worthless.empty() && Signers.NoPubKey.empty()) errmsg = msg; else { - if (!BadSigners.empty()) + if (!Signers.Bad.empty()) { errmsg += _("The following signatures were invalid:\n"); - for (vector::iterator I = BadSigners.begin(); - I != BadSigners.end(); ++I) - errmsg += (*I + "\n"); + for (auto const &I : Signers.Bad) + errmsg.append(I).append("\n"); } - if (!WorthlessSigners.empty()) + if (!Signers.Worthless.empty()) { errmsg += _("The following signatures were invalid:\n"); - for (vector::iterator I = WorthlessSigners.begin(); - I != WorthlessSigners.end(); ++I) - errmsg += (*I + "\n"); + for (auto const &I : Signers.Worthless) + errmsg.append(I).append("\n"); } - if (!NoPubKeySigners.empty()) + if (!Signers.NoPubKey.empty()) { errmsg += _("The following signatures couldn't be verified because the public key is not available:\n"); - for (vector::iterator I = NoPubKeySigners.begin(); - I != NoPubKeySigners.end(); ++I) - errmsg += (*I + "\n"); + for (auto const &I : Signers.NoPubKey) + errmsg.append(I).append("\n"); } } // this is only fatal if we have no good sigs or if we have at // least one bad signature. good signatures and NoPubKey signatures // happen easily when a file is signed with multiple signatures - if(GoodSigners.empty() or !BadSigners.empty()) + if(Signers.Good.empty() or !Signers.Bad.empty()) return _error->Error("%s", errmsg.c_str()); } // Just pass the raw output up, because passing it as a real data // structure is too difficult with the method stuff. We keep it // as three separate vectors for future extensibility. - Res.GPGVOutput = GoodSigners; - std::move(BadSigners.begin(), BadSigners.end(), std::back_inserter(Res.GPGVOutput)); - std::move(NoPubKeySigners.begin(), NoPubKeySigners.end(), std::back_inserter(Res.GPGVOutput)); + Res.GPGVOutput = Signers.Good; + std::move(Signers.Bad.begin(), Signers.Bad.end(), std::back_inserter(Res.GPGVOutput)); + std::move(Signers.NoPubKey.begin(), Signers.NoPubKey.end(), std::back_inserter(Res.GPGVOutput)); URIDone(Res); if (DebugEnabled()) -- cgit v1.2.3-70-g09d2 From 7bf533967fb385b9625a1ee4dd7c6542a84b489c Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Wed, 12 Sep 2018 01:44:18 +0200 Subject: Communicate back which key(s) were used for signing Telling the acquire system which keys caused the gpgv method to succeed allows us for now just a casual check if the gpgv method really executed catching bugs like CVE-2018-0501, but we will make use of the information for better features in the following commits. --- apt-pkg/acquire-item.cc | 18 +++++++-- apt-pkg/acquire-item.h | 3 +- methods/gpgv.cc | 79 +++++++++++++++++++++++++++++---------- test/integration/test-method-gpgv | 62 ++++++++++++++++++++---------- 4 files changed, 119 insertions(+), 43 deletions(-) (limited to 'methods') diff --git a/apt-pkg/acquire-item.cc b/apt-pkg/acquire-item.cc index 83c793093..755e1fb59 100644 --- a/apt-pkg/acquire-item.cc +++ b/apt-pkg/acquire-item.cc @@ -1464,8 +1464,20 @@ bool pkgAcqMetaBase::CheckDownloadDone(pkgAcqTransactionItem * const I, const st return true; } /*}}}*/ -bool pkgAcqMetaBase::CheckAuthDone(string const &Message) /*{{{*/ +bool pkgAcqMetaBase::CheckAuthDone(string const &Message, pkgAcquire::MethodConfig const *const Cnf) /*{{{*/ { + /* If we work with a recent version of our gpgv method, we expect that it tells us + which key(s) have signed the file so stuff like CVE-2018-0501 is harder in the future */ + if (Cnf->Version != "1.0" && LookupTag(Message, "Signed-By").empty()) + { + std::string errmsg; + strprintf(errmsg, "Internal Error: Signature on %s seems good, but expected details are missing! (%s)", Target.URI.c_str(), "Signed-By"); + if (ErrorText.empty()) + ErrorText = errmsg; + Status = StatAuthError; + return _error->Error("%s", errmsg.c_str()); + } + // At this point, the gpgv method has succeeded, so there is a // valid signature from a key in the trusted keyring. We // perform additional verification of its contents, and use them @@ -1946,7 +1958,7 @@ void pkgAcqMetaClearSig::Done(std::string const &Message, QueueForSignatureVerify(this, DestFile, DestFile); return; } - else if(CheckAuthDone(Message) == true) + else if (CheckAuthDone(Message, Cnf) == true) { if (TransactionManager->IMSHit == false) TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename()); @@ -2190,7 +2202,7 @@ void pkgAcqMetaSig::Done(string const &Message, HashStringList const &Hashes, } return; } - else if(MetaIndex->CheckAuthDone(Message) == true) + else if (MetaIndex->CheckAuthDone(Message, Cfg) == true) { auto const Releasegpg = GetFinalFilename(); auto const Release = MetaIndex->GetFinalFilename(); diff --git a/apt-pkg/acquire-item.h b/apt-pkg/acquire-item.h index 4a1378922..70651d9e3 100644 --- a/apt-pkg/acquire-item.h +++ b/apt-pkg/acquire-item.h @@ -451,8 +451,9 @@ class APT_HIDDEN pkgAcqMetaBase : public pkgAcqTransactionItem /*{{{*/ * * \param Message The message block received from the fetch * subprocess. + * \param Cnf The method and its configuration which handled the request */ - bool CheckAuthDone(std::string const &Message); + bool CheckAuthDone(std::string const &Message, pkgAcquire::MethodConfig const *const Cnf); /** Check if the current item should fail at this point */ bool CheckStopAuthentication(pkgAcquire::Item * const I, const std::string &Message); diff --git a/methods/gpgv.cc b/methods/gpgv.cc index 1b9f6dd64..f66e3356f 100644 --- a/methods/gpgv.cc +++ b/methods/gpgv.cc @@ -109,6 +109,8 @@ struct APT_HIDDEN SignersStorage { // a worthless signature is a expired or revoked one std::vector SoonWorthless; std::vector NoPubKey; + std::vector Valid; + std::vector SignedBy; }; class GPGVMethod : public aptMethod { @@ -120,7 +122,7 @@ class GPGVMethod : public aptMethod protected: virtual bool URIAcquire(std::string const &Message, FetchItem *Itm) APT_OVERRIDE; public: - GPGVMethod() : aptMethod("gpgv","1.0",SingleInstance | SendConfig) {}; + GPGVMethod() : aptMethod("gpgv", "1.1", SingleInstance | SendConfig){}; }; static void PushEntryWithKeyID(std::vector &Signers, char * const buffer, bool const Debug) { @@ -187,7 +189,6 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, FILE *pipein = fdopen(fd[0], "r"); // Loop over the output of apt-key (which really is gnupg), and check the signatures. - std::vector ValidSigners; std::vector ErrSigners; std::map> SubKeyMapping; size_t buffersize = 0; @@ -256,7 +257,7 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, break; } - ValidSigners.push_back(sig); + Signers.Valid.push_back(sig); if (tokens.size() > 9 && sig != tokens[9]) SubKeyMapping[tokens[9]].emplace_back(sig); @@ -297,9 +298,10 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, if (IsTheSameKey(l, good)) { // GOODSIG might be "just" a longid, so we check VALIDSIG which is always a fingerprint - if (std::find(ValidSigners.cbegin(), ValidSigners.cend(), l) == ValidSigners.cend()) + if (std::find(Signers.Valid.cbegin(), Signers.Valid.cend(), l) == Signers.Valid.cend()) continue; found = true; + Signers.SignedBy.push_back(l + "!"); break; } else if (exactKey == false) @@ -310,9 +312,11 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, for (auto const &sub : master->second) if (IsTheSameKey(sub, good)) { - if (std::find(ValidSigners.cbegin(), ValidSigners.cend(), sub) == ValidSigners.cend()) + if (std::find(Signers.Valid.cbegin(), Signers.Valid.cend(), sub) == Signers.Valid.cend()) continue; found = true; + Signers.SignedBy.push_back(l); + Signers.SignedBy.push_back(sub + "!"); break; } if (found) @@ -328,6 +332,24 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, } Signers.Good= std::move(filteredGood); } + else + { + // for gpg an expired key is valid, too, but we want only the valid & good ones + for (auto const &v : Signers.Valid) + if (std::any_of(Signers.Good.begin(), Signers.Good.end(), + [&v](std::string const &g) { return IsTheSameKey(v, g); })) + Signers.SignedBy.push_back(v + "!"); + for (auto sub : SubKeyMapping) + if (std::any_of(sub.second.begin(), sub.second.end(), + [&](std::string const &s) { + if (std::find(Signers.Valid.begin(), Signers.Valid.end(), s) == Signers.Valid.end()) + return false; + return std::any_of(Signers.Good.begin(), Signers.Good.end(), + [&s](std::string const &g) { return IsTheSameKey(s, g); }); + })) + Signers.SignedBy.push_back(sub.first); + } + std::sort(Signers.SignedBy.begin(), Signers.SignedBy.end()); int status; waitpid(pid, &status, 0); @@ -340,6 +362,8 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, { std::cerr << "Summary:\n Good: "; implodeVector(Signers.Good, std::cerr, ", "); + std::cerr << "\n Valid: "; + implodeVector(Signers.Valid, std::cerr, ", "); std::cerr << "\n Bad: "; implodeVector(Signers.Bad, std::cerr, ", "); std::cerr << "\n Worthless: "; @@ -348,6 +372,8 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, std::for_each(Signers.SoonWorthless.begin(), Signers.SoonWorthless.end(), [](Signer const &sig) { std::cerr << sig.key << ", "; }); std::cerr << "\n NoPubKey: "; implodeVector(Signers.NoPubKey, std::cerr, ", "); + std::cerr << "\n Signed-By: "; + implodeVector(Signers.SignedBy, std::cerr, ", "); std::cerr << std::endl << " NODATA: " << (gotNODATA ? "yes" : "no") << std::endl; } @@ -397,10 +423,6 @@ bool GPGVMethod::URIAcquire(std::string const &Message, FetchItem *Itm) string const Path = Get.Host + Get.Path; // To account for relative paths SignersStorage Signers; - FetchResult Res; - Res.Filename = Itm->DestFile; - URIStart(Res); - std::vector keyFpts, keyFiles; for (auto &&key : VectorizeString(LookupTag(Message, "Signed-By"), ',')) if (key.empty() == false && key[0] == '/') @@ -456,17 +478,36 @@ bool GPGVMethod::URIAcquire(std::string const &Message, FetchItem *Itm) // this is only fatal if we have no good sigs or if we have at // least one bad signature. good signatures and NoPubKey signatures // happen easily when a file is signed with multiple signatures - if(Signers.Good.empty() or !Signers.Bad.empty()) - return _error->Error("%s", errmsg.c_str()); + if (Signers.Good.empty() or !Signers.Bad.empty()) + return _error->Error("%s", errmsg.c_str()); + } + + std::unordered_map fields; + fields.emplace("URI", Itm->Uri); + fields.emplace("Filename", Itm->DestFile); + if (Signers.SignedBy.empty() == false) + { + std::ostringstream out; + implodeVector(Signers.SignedBy, out, "\n"); + fields.emplace("Signed-By", out.str()); + } + { + // Just pass the raw output up, because passing it as a real data + // structure is too difficult with the method stuff. We keep it + // as three separate vectors for future extensibility. + std::vector gpgvoutput; + std::move(Signers.Good.begin(), Signers.Good.end(), std::back_inserter(gpgvoutput)); + std::move(Signers.Bad.begin(), Signers.Bad.end(), std::back_inserter(gpgvoutput)); + std::move(Signers.NoPubKey.begin(), Signers.NoPubKey.end(), std::back_inserter(gpgvoutput)); + if (gpgvoutput.empty() == false) + { + std::ostringstream out; + implodeVector(gpgvoutput, out, "\n"); + fields.emplace("GPGVOutput", out.str()); + } } - - // Just pass the raw output up, because passing it as a real data - // structure is too difficult with the method stuff. We keep it - // as three separate vectors for future extensibility. - Res.GPGVOutput = Signers.Good; - std::move(Signers.Bad.begin(), Signers.Bad.end(), std::back_inserter(Res.GPGVOutput)); - std::move(Signers.NoPubKey.begin(), Signers.NoPubKey.end(), std::back_inserter(Res.GPGVOutput)); - URIDone(Res); + SendMessage("201 URI Done", std::move(fields)); + Dequeue(); if (DebugEnabled()) std::clog << "apt-key succeeded\n"; diff --git a/test/integration/test-method-gpgv b/test/integration/test-method-gpgv index fedcea63a..70521881d 100755 --- a/test/integration/test-method-gpgv +++ b/test/integration/test-method-gpgv @@ -10,7 +10,6 @@ configarchitecture 'i386' cat > faked-apt-key < gpgv.output + echo "$4" > gpgv.output msgtest "$1" "$2" gpgvmethod >method.output 2>&1 || true testsuccess --nomsg grep "^ $2\$" method.output + msgtest 'The reported signedby key is' "${3:-empty}" + testsuccess --nomsg grep "^ Signed-By:\s\+$3\$" method.output } testrun() { - testgpgv 'Good signed with long keyid' 'Good: GOODSIG 5A90D141DBAC8DAE' '[GNUPG:] GOODSIG 5A90D141DBAC8DAE Joe Sixpack (APT Testcases Dummy) + testgpgv 'Good signed with long keyid' 'Good: GOODSIG 5A90D141DBAC8DAE' '34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE!' '[GNUPG:] GOODSIG 5A90D141DBAC8DAE Joe Sixpack (APT Testcases Dummy) [GNUPG:] VALIDSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE 2016-09-01 1472742625 0 4 0 1 11 00 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE' - testgpgv 'Good signed with fingerprint' 'Good: GOODSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE' '[GNUPG:] GOODSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE Joe Sixpack (APT Testcases Dummy) + testgpgv 'Good signed with fingerprint' 'Good: GOODSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE' '34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE!' '[GNUPG:] GOODSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE Joe Sixpack (APT Testcases Dummy) [GNUPG:] VALIDSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE 2016-09-01 1472742625 0 4 0 1 11 00 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE' - testgpgv 'Good subkey signed with long keyid' 'Good: GOODSIG 5B6896415D44C43E' '[GNUPG:] GOODSIG 5B6896415D44C43E Sebastian Subkey + testgpgv 'Good subkey signed with long keyid' 'Good: GOODSIG 5B6896415D44C43E' '34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE, 4281DEDBD466EAE8C1F4157E5B6896415D44C43E!' '[GNUPG:] GOODSIG 5B6896415D44C43E Sebastian Subkey [GNUPG:] VALIDSIG 4281DEDBD466EAE8C1F4157E5B6896415D44C43E 2018-08-16 1534459673 0 4 0 1 11 00 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE' - testgpgv 'Good subkey signed with fingerprint' 'Good: GOODSIG 4281DEDBD466EAE8C1F4157E5B6896415D44C43E' '[GNUPG:] GOODSIG 4281DEDBD466EAE8C1F4157E5B6896415D44C43E Sebastian Subkey + testgpgv 'Good subkey signed with fingerprint' 'Good: GOODSIG 4281DEDBD466EAE8C1F4157E5B6896415D44C43E' '34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE, 4281DEDBD466EAE8C1F4157E5B6896415D44C43E!' '[GNUPG:] GOODSIG 4281DEDBD466EAE8C1F4157E5B6896415D44C43E Sebastian Subkey [GNUPG:] VALIDSIG 4281DEDBD466EAE8C1F4157E5B6896415D44C43E 2018-08-16 1534459673 0 4 0 1 11 00 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE' - testgpgv 'Untrusted signed with long keyid' 'Worthless: 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE' '[GNUPG:] GOODSIG 5A90D141DBAC8DAE Joe Sixpack (APT Testcases Dummy) + testgpgv 'Untrusted signed with long keyid' 'Worthless: 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE' '' '[GNUPG:] GOODSIG 5A90D141DBAC8DAE Joe Sixpack (APT Testcases Dummy) [GNUPG:] VALIDSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE 2016-09-01 1472742625 0 4 0 1 1 00 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE' testsuccess grep '^\s\+Good:\s\+$' method.output - testgpgv 'Untrusted signed with fingerprint' 'Worthless: 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE' '[GNUPG:] GOODSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE Joe Sixpack (APT Testcases Dummy) + testgpgv 'Untrusted signed with fingerprint' 'Worthless: 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE' '' '[GNUPG:] GOODSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE Joe Sixpack (APT Testcases Dummy) [GNUPG:] VALIDSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE 2016-09-01 1472742625 0 4 0 1 1 00 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE' testsuccess grep '^\s\+Good:\s\+$' method.output - testgpgv 'Weak signed with long keyid' 'Good: GOODSIG 5A90D141DBAC8DAE' '[GNUPG:] GOODSIG 5A90D141DBAC8DAE Joe Sixpack (APT Testcases Dummy) + testgpgv 'Weak signed with long keyid' 'Good: GOODSIG 5A90D141DBAC8DAE' '34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE!' '[GNUPG:] GOODSIG 5A90D141DBAC8DAE Joe Sixpack (APT Testcases Dummy) [GNUPG:] VALIDSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE 2016-09-01 1472742625 0 4 0 1 2 00 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE' testsuccess grep '^Message: Signature by key 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE uses weak digest algorithm (SHA1)$' method.output - testgpgv 'Weak signed with fingerprint' 'Good: GOODSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE' '[GNUPG:] GOODSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE Joe Sixpack (APT Testcases Dummy) + testgpgv 'Weak signed with fingerprint' 'Good: GOODSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE' '34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE!' '[GNUPG:] GOODSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE Joe Sixpack (APT Testcases Dummy) [GNUPG:] VALIDSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE 2016-09-01 1472742625 0 4 0 1 2 00 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE' testsuccess grep '^Message: Signature by key 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE uses weak digest algorithm (SHA1)$' method.output - testgpgv 'No Pubkey with long keyid' 'NoPubKey: NO_PUBKEY E8525D47528144E2' '[GNUPG:] ERRSIG E8525D47528144E2 1 11 00 1472744666 9 + testgpgv 'No Pubkey with long keyid' 'NoPubKey: NO_PUBKEY E8525D47528144E2' '' '[GNUPG:] ERRSIG E8525D47528144E2 1 11 00 1472744666 9 [GNUPG:] NO_PUBKEY E8525D47528144E2' - testgpgv 'No Pubkey with fingerprint' 'NoPubKey: NO_PUBKEY DE66AECA9151AFA1877EC31DE8525D47528144E2' '[GNUPG:] ERRSIG DE66AECA9151AFA1877EC31DE8525D47528144E2 1 11 00 1472744666 9 + testgpgv 'No Pubkey with fingerprint' 'NoPubKey: NO_PUBKEY DE66AECA9151AFA1877EC31DE8525D47528144E2' '' '[GNUPG:] ERRSIG DE66AECA9151AFA1877EC31DE8525D47528144E2 1 11 00 1472744666 9 [GNUPG:] NO_PUBKEY DE66AECA9151AFA1877EC31DE8525D47528144E2' - testgpgv 'Expired key with long keyid' 'Worthless: EXPKEYSIG 4BC0A39C27CE74F9 Rex Expired ' '[GNUPG:] EXPKEYSIG 4BC0A39C27CE74F9 Rex Expired + testgpgv 'Expired key with long keyid' 'Worthless: EXPKEYSIG 4BC0A39C27CE74F9 Rex Expired ' '' '[GNUPG:] EXPKEYSIG 4BC0A39C27CE74F9 Rex Expired [GNUPG:] VALIDSIG 891CC50E605796A0C6E733F74BC0A39C27CE74F9 2016-09-01 1472742629 0 4 0 1 11 00 891CC50E605796A0C6E733F74BC0A39C27CE74F9' - testgpgv 'Expired key with fingerprint' 'Worthless: EXPKEYSIG 891CC50E605796A0C6E733F74BC0A39C27CE74F9 Rex Expired ' '[GNUPG:] EXPKEYSIG 891CC50E605796A0C6E733F74BC0A39C27CE74F9 Rex Expired + testgpgv 'Expired key with fingerprint' 'Worthless: EXPKEYSIG 891CC50E605796A0C6E733F74BC0A39C27CE74F9 Rex Expired ' '' '[GNUPG:] EXPKEYSIG 891CC50E605796A0C6E733F74BC0A39C27CE74F9 Rex Expired [GNUPG:] VALIDSIG 891CC50E605796A0C6E733F74BC0A39C27CE74F9 2016-09-01 1472742629 0 4 0 1 11 00 891CC50E605796A0C6E733F74BC0A39C27CE74F9' } @@ -111,11 +112,11 @@ Signed-By: 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE,/dev/null } testrun -testgpgv 'Good signed with long keyid but not signed-by key' 'NoPubKey: GOODSIG 4BC0A39C27CE74F9' '[GNUPG:] GOODSIG 4BC0A39C27CE74F9 Rex Expired +testgpgv 'Good signed with long keyid but not signed-by key' 'NoPubKey: GOODSIG 4BC0A39C27CE74F9' '' '[GNUPG:] GOODSIG 4BC0A39C27CE74F9 Rex Expired [GNUPG:] VALIDSIG 891CC50E605796A0C6E733F74BC0A39C27CE74F9 2016-09-01 1472742625 0 4 0 1 11 00 891CC50E605796A0C6E733F74BC0A39C27CE74F9' testsuccess grep '^\s\+Good:\s\+$' method.output testsuccess grep 'verified because the public key is not available: GOODSIG' method.output -testgpgv 'Good signed with fingerprint but not signed-by key' 'NoPubKey: GOODSIG 891CC50E605796A0C6E733F74BC0A39C27CE74F9' '[GNUPG:] GOODSIG 891CC50E605796A0C6E733F74BC0A39C27CE74F9 Rex Expired +testgpgv 'Good signed with fingerprint but not signed-by key' 'NoPubKey: GOODSIG 891CC50E605796A0C6E733F74BC0A39C27CE74F9' '' '[GNUPG:] GOODSIG 891CC50E605796A0C6E733F74BC0A39C27CE74F9 Rex Expired [GNUPG:] VALIDSIG 891CC50E605796A0C6E733F74BC0A39C27CE74F9 2016-09-01 1472742625 0 4 0 1 11 00 891CC50E605796A0C6E733F74BC0A39C27CE74F9' testsuccess grep '^\s\+Good:\s\+$' method.output testsuccess grep 'verified because the public key is not available: GOODSIG' method.output @@ -132,16 +133,37 @@ Filename: /dev/zero Signed-By: 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE! ' | runapt "${METHODSDIR}/gpgv" } -testgpgv 'Exact matched subkey signed with long keyid' 'Good: GOODSIG 5A90D141DBAC8DAE' '[GNUPG:] GOODSIG 5A90D141DBAC8DAE Sebastian Subkey +testgpgv 'Exact matched subkey signed with long keyid' 'Good: GOODSIG 5A90D141DBAC8DAE' '34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE!' '[GNUPG:] GOODSIG 5A90D141DBAC8DAE Sebastian Subkey [GNUPG:] VALIDSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE 2018-08-16 1534459673 0 4 0 1 11 00 4281DEDBD466EAE8C1F4157E5B6896415D44C43E' -testgpgv 'Exact matched subkey signed with fingerprint' 'Good: GOODSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE' '[GNUPG:] GOODSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE Sebastian Subkey +testgpgv 'Exact matched subkey signed with fingerprint' 'Good: GOODSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE' '34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE!' '[GNUPG:] GOODSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE Sebastian Subkey [GNUPG:] VALIDSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE 2018-08-16 1534459673 0 4 0 1 11 00 4281DEDBD466EAE8C1F4157E5B6896415D44C43E' -testgpgv 'Exact unmatched subkey signed with long keyid' 'NoPubKey: GOODSIG 5B6896415D44C43E' '[GNUPG:] GOODSIG 5B6896415D44C43E Sebastian Subkey +testgpgv 'Exact unmatched subkey signed with long keyid' 'NoPubKey: GOODSIG 5B6896415D44C43E' '' '[GNUPG:] GOODSIG 5B6896415D44C43E Sebastian Subkey [GNUPG:] VALIDSIG 4281DEDBD466EAE8C1F4157E5B6896415D44C43E 2018-08-16 1534459673 0 4 0 1 11 00 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE' testsuccess grep '^\s\+Good:\s\+$' method.output testsuccess grep 'verified because the public key is not available: GOODSIG' method.output -testgpgv 'Exact unmatched subkey signed with fingerprint' 'NoPubKey: GOODSIG 4281DEDBD466EAE8C1F4157E5B6896415D44C43E' '[GNUPG:] GOODSIG 4281DEDBD466EAE8C1F4157E5B6896415D44C43E Sebastian Subkey +testgpgv 'Exact unmatched subkey signed with fingerprint' 'NoPubKey: GOODSIG 4281DEDBD466EAE8C1F4157E5B6896415D44C43E' '' '[GNUPG:] GOODSIG 4281DEDBD466EAE8C1F4157E5B6896415D44C43E Sebastian Subkey [GNUPG:] VALIDSIG 4281DEDBD466EAE8C1F4157E5B6896415D44C43E 2018-08-16 1534459673 0 4 0 1 11 00 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE' testsuccess grep '^\s\+Good:\s\+$' method.output testsuccess grep 'verified because the public key is not available: GOODSIG' method.output + +insertpackage 'unstable' 'foo' 'all' '1' +setupaptarchive --no-update + +echo '[GNUPG:] GOODSIG 5A90D141DBAC8DAE Sebastian Subkey +[GNUPG:] VALIDSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE 2018-08-16 1534459673 0 4 0 1 11 00 4281DEDBD466EAE8C1F4157E5B6896415D44C43E' > gpgv.output +testsuccess apt update -o Dir::Bin::apt-key="./faked-apt-key" -o Debug::pkgAcquire::Worker=1 -o Debug::Acquire::gpgv=1 +rm -rf rootdir/var/lib/apt/lists + +echo '[GNUPG:] GOODSIG 5A90D141DBAC8DAE Sebastian Subkey ' > gpgv.output +testfailure apt update -o Dir::Bin::apt-key="./faked-apt-key" -o Debug::pkgAcquire::Worker=1 -o Debug::Acquire::gpgv=1 +rm -rf rootdir/var/lib/apt/lists + +echo '[GNUPG:] VALIDSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE 2018-08-16 1534459673 0 4 0 1 11 00 4281DEDBD466EAE8C1F4157E5B6896415D44C43E' > gpgv.output +testfailure apt update -o Dir::Bin::apt-key="./faked-apt-key" -o Debug::pkgAcquire::Worker=1 -o Debug::Acquire::gpgv=1 +rm -rf rootdir/var/lib/apt/lists + +echo '[GNUPG:] GOODSIG 5A90D141DBAC8DAE Sebastian Subkey +[GNUPG:] VALIDSIG 0000000000000000000000000000000000000000 2018-08-16 1534459673 0 4 0 1 11 00 4281DEDBD466EAE8C1F4157E5B6896415D44C43E' > gpgv.output +testfailure apt update -o Dir::Bin::apt-key="./faked-apt-key" -o Debug::pkgAcquire::Worker=1 -o Debug::Acquire::gpgv=1 +rm -rf rootdir/var/lib/apt/lists -- cgit v1.2.3-70-g09d2