diff options
| author | Julian Andres Klode <jak@debian.org> | 2024-07-30 04:18:02 +0000 |
|---|---|---|
| committer | Julian Andres Klode <jak@debian.org> | 2024-07-30 04:18:02 +0000 |
| commit | d0e8f39437afdd557a8712047e1fc494fde9209e (patch) | |
| tree | 990fb521980b0fc7a005cadb0f2384b1dab4c52b | |
| parent | 847c60af9eaa4b1dc3c6c3afa94bf494b721a1b7 (diff) | |
| parent | 47deb766c0a11c54d122197531279677545459af (diff) | |
Merge branch 'deprecate-pubkey-algos' into 'main'
Only revoke weak RSA keys now, add 'next' and 'future' levels
See merge request apt-team/apt!365
| -rw-r--r-- | apt-pkg/acquire-worker.cc | 5 | ||||
| -rw-r--r-- | apt-pkg/contrib/gpgv.cc | 32 | ||||
| -rw-r--r-- | apt-pkg/contrib/gpgv.h | 1 | ||||
| -rw-r--r-- | apt-pkg/init.cc | 4 | ||||
| -rw-r--r-- | doc/examples/configure-index | 2 | ||||
| -rw-r--r-- | methods/aptmethod.h | 12 | ||||
| -rw-r--r-- | methods/gpgv.cc | 21 | ||||
| -rwxr-xr-x | test/integration/test-method-gpgv | 43 | ||||
| -rw-r--r-- | test/libapt/assert_pubkeyalgo_test.cc | 56 |
9 files changed, 173 insertions, 3 deletions
diff --git a/apt-pkg/acquire-worker.cc b/apt-pkg/acquire-worker.cc index 4f247cf84..696a3e0b8 100644 --- a/apt-pkg/acquire-worker.cc +++ b/apt-pkg/acquire-worker.cc @@ -200,6 +200,7 @@ enum class APT_HIDDEN MessageType STATUS = 102, REDIRECT = 103, WARNING = 104, + AUDIT = 105, URI_START = 200, URI_DONE = 201, AUX_REQUEST = 351, @@ -388,6 +389,10 @@ bool pkgAcquire::Worker::RunMessages() _error->Warning("%s: %s", Itm ? Itm->Owner ? Itm->Owner->DescURI().c_str() : Access.c_str() : Access.c_str(), LookupTag(Message, "Message").c_str()); break; + case MessageType::AUDIT: + _error->Audit("%s: %s", Itm ? Itm->Owner ? Itm->Owner->DescURI().c_str() : Access.c_str() : Access.c_str(), LookupTag(Message, "Message").c_str()); + break; + case MessageType::URI_START: { if (Itm == nullptr) diff --git a/apt-pkg/contrib/gpgv.cc b/apt-pkg/contrib/gpgv.cc index 2fa5b0c30..225acae88 100644 --- a/apt-pkg/contrib/gpgv.cc +++ b/apt-pkg/contrib/gpgv.cc @@ -566,3 +566,35 @@ bool OpenMaybeClearSignedFile(std::string const &ClearSignedFileName, FileFd &Me return not MessageFile.Failed(); } /*}}}*/ +bool IsAssertedPubKeyAlgo(std::string const &pkstr, std::string const &option) /*{{{*/ +{ + auto fullAss = APT::String::Startswith(option, "APT::Key") ? _config->Find(option) : option; + for (auto &ass : VectorizeString(fullAss, ',')) + { + if (ass == pkstr) + return true; + // We only implement >= for rsa + if (APT::String::Startswith(ass, ">=rsa")) + { + if (not APT::String::Startswith(pkstr, "rsa")) + continue; + if (not std::all_of(ass.begin() + 5, ass.end(), isdigit)) + return _error->Error("Unrecognized public key specification '%s' in option %s: expect only digits after >=rsa", ass.c_str(), option.c_str()); + + int assBits = std::stoi(ass.substr(5)); + int pkBits = std::stoi(pkstr.substr(3)); + + if (pkBits >= assBits) + return true; + + continue; + } + if (ass.empty()) + return _error->Error("Empty item in public key assertion string option %s", option.c_str()); + if (not std::all_of(ass.begin(), ass.end(), [](char c) + { return isalpha(c) || isdigit(c); })) + return _error->Error("Unrecognized public key specification '%s' in option %s", ass.c_str(), option.c_str()); + } + return false; +} + /*}}}*/ diff --git a/apt-pkg/contrib/gpgv.h b/apt-pkg/contrib/gpgv.h index 1cabed4e6..1f3ef26f9 100644 --- a/apt-pkg/contrib/gpgv.h +++ b/apt-pkg/contrib/gpgv.h @@ -86,4 +86,5 @@ APT_PUBLIC bool SplitClearSignedFile(std::string const &InFile, FileFd * const C */ APT_PUBLIC bool OpenMaybeClearSignedFile(std::string const &ClearSignedFileName, FileFd &MessageFile); +APT_PUBLIC bool IsAssertedPubKeyAlgo(std::string const &pkstr, std::string const &option); #endif diff --git a/apt-pkg/init.cc b/apt-pkg/init.cc index 487f94f20..d71d9541f 100644 --- a/apt-pkg/init.cc +++ b/apt-pkg/init.cc @@ -131,7 +131,9 @@ bool pkgInitConfig(Configuration &Cnf) Cnf.Set("APT::Build-Essential::", "build-essential"); Cnf.CndSet("APT::Install-Recommends", true); Cnf.CndSet("APT::Install-Suggests", false); - Cnf.CndSet("APT::Key::Assert-Pubkey-Algo", ">=rsa2048,ed25519,ed448"); + Cnf.CndSet("APT::Key::Assert-Pubkey-Algo", ">=rsa2048,ed25519,ed448,nistp256,nistp384,nistp512,brainpoolP256r1,brainpoolP320r1,brainpoolP384r1,brainpoolP512r1,secp256k1"); + Cnf.CndSet("APT::Key::Assert-Pubkey-Algo::Next", ">=rsa2048,ed25519,ed448,nistp256,nistp384,nistp512"); + Cnf.CndSet("APT::Key::Assert-Pubkey-Algo::Future", ">=rsa3072,ed25519,ed448"); Cnf.CndSet("Dir","/"); // State diff --git a/doc/examples/configure-index b/doc/examples/configure-index index 6bcf8e354..72ce1fe3a 100644 --- a/doc/examples/configure-index +++ b/doc/examples/configure-index @@ -776,6 +776,8 @@ apt::key::masterkeyring "<STRING>"; apt::key::archivekeyringuri "<STRING>"; apt::key::net-update-enabled "<STRING>"; apt::key::assert-pubkey-algo "<STRING>"; +apt::key::assert-pubkey-algo::next "<STRING>"; +apt::key::assert-pubkey-algo::future "<STRING>"; apt::ftparchive::release::patterns "<LIST>"; apt::ftparchive::release::validtime "<INT>"; diff --git a/methods/aptmethod.h b/methods/aptmethod.h index 26b8c0b55..1c24f3a98 100644 --- a/methods/aptmethod.h +++ b/methods/aptmethod.h @@ -448,7 +448,7 @@ protected: return true; } - void Warning(std::string &&msg) + void Message(std::string &&msg, std::string code) { std::unordered_map<std::string, std::string> fields; if (Queue != 0) @@ -458,7 +458,15 @@ protected: if (not UsedMirror.empty()) fields.emplace("UsedMirror", UsedMirror); fields.emplace("Message", std::move(msg)); - SendMessage("104 Warning", std::move(fields)); + SendMessage(code, std::move(fields)); + } + void Warning(std::string &&msg) + { + return Message(std::move(msg), "104 Warning"); + } + void Audit(std::string &&msg) + { + return Message(std::move(msg), "105 Audit"); } bool TransferModificationTimes(char const * const From, char const * const To, time_t &LastModified) APT_NONNULL(2, 3) diff --git a/methods/gpgv.cc b/methods/gpgv.cc index 20ef2861d..4a0866555 100644 --- a/methods/gpgv.cc +++ b/methods/gpgv.cc @@ -112,6 +112,7 @@ struct APT_HIDDEN SignersStorage { std::vector<Signer> Worthless; // a worthless signature is a expired or revoked one std::vector<Signer> SoonWorthless; + std::vector<Signer> LaterWorthless; std::vector<std::string> NoPubKey; std::vector<std::string> Valid; std::vector<std::string> SignedBy; @@ -261,6 +262,17 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, { return IsTheSameKey(fpr, goodsig); }), Signers.Good.end()); } + else if (not IsAssertedPubKeyAlgo(pkstr, "APT::Key::Assert-Pubkey-Algo::Next")) + { + std::string reason; + Signers.SoonWorthless.push_back({fpr, pkstr}); + } + else if (not IsAssertedPubKeyAlgo(pkstr, "APT::Key::Assert-Pubkey-Algo::Future")) + { + std::string reason; + strprintf(reason, _("%s will be deprecated in a future release"), pkstr.c_str()); + Signers.LaterWorthless.push_back({fpr, reason}); + } } else if (strncmp(buffer, GNUPGGOODSIG, sizeof(GNUPGGOODSIG)-1) == 0) PushEntryWithKeyID(Signers.Good, buffer, Debug); @@ -420,6 +432,8 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, std::for_each(Signers.Worthless.begin(), Signers.Worthless.end(), [](Signer const &sig) { std::cerr << sig.key << ", "; }); std::cerr << "\n SoonWorthless: "; std::for_each(Signers.SoonWorthless.begin(), Signers.SoonWorthless.end(), [](Signer const &sig) { std::cerr << sig.key << ", "; }); + std::cerr << "\n LaterWorthless: "; + std::for_each(Signers.LaterWorthless.begin(), Signers.LaterWorthless.end(), [](Signer const &sig) { std::cerr << sig.key << ", "; }); std::cerr << "\n NoPubKey: "; implodeVector(Signers.NoPubKey, std::cerr, ", "); std::cerr << "\n Signed-By: "; @@ -565,6 +579,13 @@ bool GPGVMethod::URIAcquire(std::string const &Message, FetchItem *Itm) strprintf(msg, _("Signature by key %s uses weak algorithm (%s)"), Signer.key.c_str(), Signer.note.c_str()); Warning(std::move(msg)); } + for (auto const &Signer : Signers.LaterWorthless) + { + std::string msg; + // TRANSLATORS: The second %s is the reason and is untranslated for repository owners. + strprintf(msg, _("Signature by key %s uses weak algorithm (%s)"), Signer.key.c_str(), Signer.note.c_str()); + Audit(std::move(msg)); + } } if (Signers.Good.empty() || !Signers.Bad.empty() || !Signers.NoPubKey.empty()) diff --git a/test/integration/test-method-gpgv b/test/integration/test-method-gpgv index 0f014e3d1..ffaa72c8f 100755 --- a/test/integration/test-method-gpgv +++ b/test/integration/test-method-gpgv @@ -48,6 +48,14 @@ testrun() { [GNUPG:] VALIDSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE 2016-09-01 1472742625 0 4 0 1 11 00 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE [GNUPG:] ASSERT_PUBKEY_ALGO 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE 1 rsa1024' + testgpgv 'Not asserted in the next level' 'SoonWorthless: 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE, ' '34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE!' '[GNUPG:] GOODSIG 5A90D141DBAC8DAE Joe Sixpack (APT Testcases Dummy) <joe@example.org> +[GNUPG:] VALIDSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE 2016-09-01 1472742625 0 4 0 1 11 00 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE +[GNUPG:] ASSERT_PUBKEY_ALGO 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE 1 brainpoolP256r1' + + testgpgv 'Not asserted in the future level' 'LaterWorthless: 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE, ' '34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE!' '[GNUPG:] GOODSIG 5A90D141DBAC8DAE Joe Sixpack (APT Testcases Dummy) <joe@example.org> +[GNUPG:] VALIDSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE 2016-09-01 1472742625 0 4 0 1 11 00 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE +[GNUPG:] ASSERT_PUBKEY_ALGO 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE 1 nistp256' + testgpgv 'Good subkey signed with long keyid' 'Good: GOODSIG 5B6896415D44C43E' '34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE, 4281DEDBD466EAE8C1F4157E5B6896415D44C43E!' '[GNUPG:] GOODSIG 5B6896415D44C43E Sebastian Subkey <subkey@example.org> [GNUPG:] VALIDSIG 4281DEDBD466EAE8C1F4157E5B6896415D44C43E 2018-08-16 1534459673 0 4 0 1 11 00 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE' testgpgv 'Good subkey signed with fingerprint' 'Good: GOODSIG 4281DEDBD466EAE8C1F4157E5B6896415D44C43E' '34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE, 4281DEDBD466EAE8C1F4157E5B6896415D44C43E!' '[GNUPG:] GOODSIG 4281DEDBD466EAE8C1F4157E5B6896415D44C43E Sebastian Subkey <subkey@example.org> @@ -108,6 +116,9 @@ gpgvmethod() { Config-Item: Debug::Acquire::gpgv=1 Config-Item: Dir::Bin::apt-key=./faked-apt-key Config-Item: APT::Hashes::SHA1::Weak=true +Config-Item: APT::Key::Assert-Pubkey-Algo=>=rsa2048,nistp256,brainpoolP256r1 +Config-Item: APT::Key::Assert-Pubkey-Algo::Next=>=rsa2048,nistp256 +Config-Item: APT::Key::Assert-Pubkey-Algo::Future=>=rsa2048 600 URI Acquire URI: file://${TMPWORKINGDIRECTORY}/message.sig @@ -121,6 +132,9 @@ gpgvmethod() { Config-Item: Debug::Acquire::gpgv=1 Config-Item: Dir::Bin::apt-key=./faked-apt-key Config-Item: APT::Hashes::SHA1::Weak=true +Config-Item: APT::Key::Assert-Pubkey-Algo=>=rsa2048,nistp256,brainpoolP256r1 +Config-Item: APT::Key::Assert-Pubkey-Algo::Next=>=rsa2048,nistp256 +Config-Item: APT::Key::Assert-Pubkey-Algo::Future=>=rsa2048 600 URI Acquire URI: file://${TMPWORKINGDIRECTORY}/message.sig @@ -135,6 +149,9 @@ gpgvmethod() { Config-Item: Debug::Acquire::gpgv=1 Config-Item: Dir::Bin::apt-key=./faked-apt-key Config-Item: APT::Hashes::SHA1::Weak=true +Config-Item: APT::Key::Assert-Pubkey-Algo=>=rsa2048,nistp256,brainpoolP256r1 +Config-Item: APT::Key::Assert-Pubkey-Algo::Next=>=rsa2048,nistp256 +Config-Item: APT::Key::Assert-Pubkey-Algo::Future=>=rsa2048 600 URI Acquire URI: file://${TMPWORKINGDIRECTORY}/message.sig @@ -158,6 +175,9 @@ gpgvmethod() { Config-Item: Debug::Acquire::gpgv=1 Config-Item: Dir::Bin::apt-key=./faked-apt-key Config-Item: APT::Hashes::SHA1::Weak=true +Config-Item: APT::Key::Assert-Pubkey-Algo=>=rsa2048,nistp256,brainpoolP256r1 +Config-Item: APT::Key::Assert-Pubkey-Algo::Next=>=rsa2048,nistp256 +Config-Item: APT::Key::Assert-Pubkey-Algo::Future=>=rsa2048 600 URI Acquire URI: file://${TMPWORKINGDIRECTORY}/message.sig @@ -199,3 +219,26 @@ echo '[GNUPG:] GOODSIG 5A90D141DBAC8DAE Sebastian Subkey <subkey@example.org> [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 + +gpgvmethod() { + echo "601 Configuration +Config-Item: Debug::Acquire::gpgv=1 +Config-Item: Dir::Bin::apt-key=./faked-apt-key +Config-Item: APT::Hashes::SHA1::Weak=true +Config-Item: APT::Key::Assert-Pubkey-Algo::Next=>=invalid + +600 URI Acquire +URI: file://${TMPWORKINGDIRECTORY}/message.sig +Filename: ${TMPWORKINGDIRECTORY}/message.data +Signed-By: 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE! +" | runapt "${METHODSDIR}/gpgv" +} + + +echo '[GNUPG:] GOODSIG 5A90D141DBAC8DAE Joe Sixpack (APT Testcases Dummy) <joe@example.org> +[GNUPG:] VALIDSIG 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE 2016-09-01 1472742625 0 4 0 1 11 00 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE +[GNUPG:] ASSERT_PUBKEY_ALGO 34A8E9D18DB320F367E8EAA05A90D141DBAC8DAE 1 brainpoolP256r1' > gpgv.output + +msgtest "Warns about invalid specification" ">=invalid" +gpgvmethod >method.output 2>&1 || true +testsuccess --nomsg grep "Message: Unrecognized public key specification '>=invalid' in option APT::Key::Assert-Pubkey-Algo::Next" method.output diff --git a/test/libapt/assert_pubkeyalgo_test.cc b/test/libapt/assert_pubkeyalgo_test.cc new file mode 100644 index 000000000..88a070bbe --- /dev/null +++ b/test/libapt/assert_pubkeyalgo_test.cc @@ -0,0 +1,56 @@ +#include <config.h> + +#include <apt-pkg/error.h> +#include <apt-pkg/gpgv.h> + +#include "common.h" + +TEST(AssertPubKeyAlgo_Test, test) +{ + EXPECT_TRUE(IsAssertedPubKeyAlgo("rsa2048", ">=rsa2048")); + _error->DumpErrors(); + EXPECT_TRUE(_error->empty()); + + EXPECT_TRUE(IsAssertedPubKeyAlgo("rsa2048", "another,>=rsa2048")); + EXPECT_TRUE(_error->empty()); + + EXPECT_FALSE(IsAssertedPubKeyAlgo("rsa2048", ">=rsa2049")); + EXPECT_TRUE(_error->empty()); + + EXPECT_TRUE(IsAssertedPubKeyAlgo("ed25519", ">=rsa2048,ed25519")); + EXPECT_TRUE(_error->empty()); +} + +TEST(AssertPubKeyAlgo_Test, CanOnlyCompareRSA) +{ + std::string msg; + EXPECT_FALSE(IsAssertedPubKeyAlgo("ed25519", ">=ed25519")); + EXPECT_TRUE(_error->PopMessage(msg)); + EXPECT_EQ("Unrecognized public key specification '>=ed25519' in option >=ed25519", msg); + EXPECT_TRUE(_error->empty()); +} + +TEST(AssertPubKeyAlgo_Test, EmptyOption) +{ + std::string msg; + EXPECT_FALSE(IsAssertedPubKeyAlgo("ed25519", "")); + EXPECT_TRUE(_error->empty()); + + EXPECT_FALSE(IsAssertedPubKeyAlgo("ed25519", ",")); + EXPECT_TRUE(_error->PopMessage(msg)); + EXPECT_EQ("Empty item in public key assertion string option ,", msg); + EXPECT_TRUE(_error->empty()); + + EXPECT_FALSE(IsAssertedPubKeyAlgo("ed25519", "moo,")); + EXPECT_TRUE(_error->empty()); + + EXPECT_FALSE(IsAssertedPubKeyAlgo("ed25519", "moo,,")); + EXPECT_TRUE(_error->PopMessage(msg)); + EXPECT_EQ("Empty item in public key assertion string option moo,,", msg); + EXPECT_TRUE(_error->empty()); + + EXPECT_FALSE(IsAssertedPubKeyAlgo("ed25519", ",moo")); + EXPECT_TRUE(_error->PopMessage(msg)); + EXPECT_EQ("Empty item in public key assertion string option ,moo", msg); + EXPECT_TRUE(_error->empty()); +} |
