summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Andres Klode <jak@debian.org>2024-07-30 04:18:02 +0000
committerJulian Andres Klode <jak@debian.org>2024-07-30 04:18:02 +0000
commitd0e8f39437afdd557a8712047e1fc494fde9209e (patch)
tree990fb521980b0fc7a005cadb0f2384b1dab4c52b
parent847c60af9eaa4b1dc3c6c3afa94bf494b721a1b7 (diff)
parent47deb766c0a11c54d122197531279677545459af (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.cc5
-rw-r--r--apt-pkg/contrib/gpgv.cc32
-rw-r--r--apt-pkg/contrib/gpgv.h1
-rw-r--r--apt-pkg/init.cc4
-rw-r--r--doc/examples/configure-index2
-rw-r--r--methods/aptmethod.h12
-rw-r--r--methods/gpgv.cc21
-rwxr-xr-xtest/integration/test-method-gpgv43
-rw-r--r--test/libapt/assert_pubkeyalgo_test.cc56
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());
+}