#define GNUPGPREFIX "[GNUPG:]"
#define GNUPGBADSIG "[GNUPG:] BADSIG"
+#define GNUPGERRSIG "[GNUPG:] ERRSIG"
#define GNUPGNOPUBKEY "[GNUPG:] NO_PUBKEY"
#define GNUPGVALIDSIG "[GNUPG:] VALIDSIG"
#define GNUPGGOODSIG "[GNUPG:] GOODSIG"
Untrusted,
Weak,
Trusted,
+ Configureable
} state;
char name[32];
+
+ State getState() const {
+ if (state != Digest::State::Configureable)
+ return state;
+ std::string const digestconfig = _config->Find("Debug::Acquire::gpgv::configdigest::truststate", "trusted");
+ if (digestconfig == "weak")
+ return State::Weak;
+ else if (digestconfig == "untrusted")
+ return State::Untrusted;
+ return State::Trusted;
+ }
};
static constexpr Digest Digests[] = {
{Digest::State::Trusted, "SHA256"},
{Digest::State::Trusted, "SHA384"},
{Digest::State::Trusted, "SHA512"},
- {Digest::State::Trusted, "SHA224"},
+ {Digest::State::Configureable, "SHA224"},
};
+static_assert(Digests[_count(Digests) - 1].state == Digest::State::Configureable, "the last digest algo isn't the configurable one which we expect for tests");
static Digest FindDigest(std::string const & Digest)
{
std::string key;
std::string note;
};
+static bool IsTheSameKey(std::string const &validsig, std::string const &goodsig) {
+ // VALIDSIG reports a keyid (40 = 24 + 16), GOODSIG is a longid (16) only
+ return validsig.compare(24, 16, goodsig, strlen("GOODSIG "), 16) == 0;
+}
class GPGVMethod : public aptMethod
{
protected:
virtual bool URIAcquire(std::string const &Message, FetchItem *Itm) APT_OVERRIDE;
public:
-
GPGVMethod() : aptMethod("gpgv","1.0",SingleInstance | SendConfig) {};
};
// Loop over the output of apt-key (which really is gnupg), and check the signatures.
std::vector<std::string> ValidSigners;
+ std::vector<std::string> ErrSigners;
size_t buffersize = 0;
char *buffer = NULL;
while (1)
std::clog << "Got BADSIG! " << std::endl;
BadSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
}
+ else if (strncmp(buffer, GNUPGERRSIG, sizeof(GNUPGERRSIG)-1) == 0)
+ {
+ if (Debug == true)
+ std::clog << "Got ERRSIG " << std::endl;
+ ErrSigners.push_back(string(buffer, strlen(GNUPGPREFIX), strlen("ERRSIG ") + 16));
+ }
else if (strncmp(buffer, GNUPGNOPUBKEY, sizeof(GNUPGNOPUBKEY)-1) == 0)
{
if (Debug == true)
std::clog << "Got NO_PUBKEY " << std::endl;
NoPubKeySigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
+ ErrSigners.erase(std::remove_if(ErrSigners.begin(), ErrSigners.end(), [&](std::string const &errsig) {
+ return errsig.compare(strlen("ERRSIG "), 16, buffer, strlen(GNUPGNOPUBKEY), 16) == 0; }), ErrSigners.end());
}
else if (strncmp(buffer, GNUPGNODATA, sizeof(GNUPGBADSIG)-1) == 0)
{
*p = 0;
// Reject weak digest algorithms
Digest digest = FindDigest(tokens[7]);
- switch (digest.state) {
+ switch (digest.getState()) {
case Digest::State::Weak:
// Treat them like an expired key: For that a message about expiry
// is emitted, a VALIDSIG, but no GOODSIG.
// Treat them like an expired key: For that a message about expiry
// is emitted, a VALIDSIG, but no GOODSIG.
WorthlessSigners.push_back(string(sig));
- GoodSigners.erase(std::remove(GoodSigners.begin(), GoodSigners.end(), string(sig)));
+ GoodSigners.erase(std::remove_if(GoodSigners.begin(), GoodSigners.end(), [&](std::string const &goodsig) {
+ return IsTheSameKey(string(sig), goodsig); }), GoodSigners.end());
if (Debug == true)
std::clog << "Got untrusted VALIDSIG, key ID: " << sig << std::endl;
break;
+ case Digest::State::Configureable:
case Digest::State::Trusted:
if (Debug == true)
std::clog << "Got trusted VALIDSIG, key ID: " << sig << std::endl;
}
fclose(pipein);
free(buffer);
+ std::move(ErrSigners.begin(), ErrSigners.end(), std::back_inserter(WorthlessSigners));
// 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
{
ioprintf(std::clog, "gpgv exited with status %i\n", WEXITSTATUS(status));
}
-
+
+ if (Debug)
+ {
+ std::cerr << "Summary:" << std::endl << " Good: ";
+ std::copy(GoodSigners.begin(), GoodSigners.end(), std::ostream_iterator<std::string>(std::cerr, ", "));
+ std::cerr << std::endl << " Bad: ";
+ std::copy(BadSigners.begin(), BadSigners.end(), std::ostream_iterator<std::string>(std::cerr, ", "));
+ std::cerr << std::endl << " Worthless: ";
+ std::copy(WorthlessSigners.begin(), WorthlessSigners.end(), std::ostream_iterator<std::string>(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::string>(std::cerr, ", "));
+ std::cerr << std::endl;
+ }
+
if (WEXITSTATUS(status) == 0)
{
if (keyIsID)
// 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) {
- // VALIDSIG reports a keyid (40 = 24 + 16), GOODSIG is a longid (16) only
- return weak.key.compare(24, 16, good, strlen("GOODSIG "), 16) == 0;
+ return IsTheSameKey(weak.key, good);
});
}))
{
}
runtest() {
+ local DELETEFILE="$1"
msgmsg 'Cold archive signed by' 'Joe Sixpack'
prepare "${PKGFILE}"
rm -rf rootdir/var/lib/apt/lists
}
runtest3() {
- export APT_TESTS_DIGEST_ALGO="$1"
- msgmsg "Running base test with digest $1"
+ echo "Debug::Acquire::gpgv::configdigest::truststate \"$1\";" > rootdir/etc/apt/apt.conf.d/truststate
+ msgmsg "Running base test with $1 digest"
runtest2
- DELETEFILE="InRelease"
- msgmsg "Running test with deletion of $DELETEFILE and digest $1"
- runtest
-
- DELETEFILE="Release.gpg"
- msgmsg "Running test with deletion of $DELETEFILE and digest $1"
- runtest
-
- unset APT_TESTS_DIGEST_ALGO
+ for DELETEFILE in 'InRelease' 'Release.gpg'; do
+ msgmsg "Running test with deletion of $DELETEFILE and $1 digest"
+ runtest "$DELETEFILE"
+ done
}
# diable some protection by default and ensure we still do the verification
Acquire::AllowInsecureRepositories "1";
Acquire::AllowDowngradeToInsecureRepositories "1";
EOF
+# the hash marked as configureable in our gpgv method
+export APT_TESTS_DIGEST_ALGO='SHA224'
-# an all-round good hash
successfulaptgetupdate() {
testsuccess aptget update -o Debug::pkgAcquire::Worker=1 -o Debug::Acquire::gpgv=1
}
-runtest3 'SHA512'
+runtest3 'trusted'
-# a hash we consider weak and therefore warn about
-rm -f rootdir/etc/apt/apt.conf.d/no-sha1
successfulaptgetupdate() {
testwarning aptget update -o Debug::pkgAcquire::Worker=1 -o Debug::Acquire::gpgv=1
testsuccess grep 'uses weak digest algorithm' rootdir/tmp/testwarning.output
}
-runtest3 'SHA1'
+runtest3 'weak'
+
+msgmsg "Running test with apt-untrusted digest"
+echo "Debug::Acquire::gpgv::configdigest::truststate \"untrusted\";" > rootdir/etc/apt/apt.conf.d/truststate
+runfailure() {
+ for DELETEFILE in 'InRelease' 'Release.gpg'; do
+ msgmsg 'Cold archive signed by' 'Joe Sixpack'
+ prepare "${PKGFILE}"
+ rm -rf rootdir/var/lib/apt/lists
+ signreleasefiles 'Joe Sixpack'
+ find aptarchive/ -name "$DELETEFILE" -delete
+ testfailure aptget update --no-allow-insecure-repositories -o Debug::pkgAcquire::Worker=1 -o Debug::Acquire::gpgv=1
+ testsuccess grep 'The following signatures were invalid' rootdir/tmp/testfailure.output
+ testnopackage 'apt'
+ testwarning aptget update --allow-insecure-repositories -o Debug::pkgAcquire::Worker=1 -o Debug::Acquire::gpgv=1
+ failaptold
+
+ msgmsg 'Cold archive signed by' 'Marvin Paranoid'
+ prepare "${PKGFILE}"
+ rm -rf rootdir/var/lib/apt/lists
+ signreleasefiles 'Marvin Paranoid'
+ find aptarchive/ -name "$DELETEFILE" -delete
+ testfailure aptget update --no-allow-insecure-repositories -o Debug::pkgAcquire::Worker=1 -o Debug::Acquire::gpgv=1
+ testnopackage 'apt'
+ updatewithwarnings '^W: .* NO_PUBKEY'
+ testsuccessequal "$(cat "${PKGFILE}")
+" aptcache show apt
+ failaptold
+ done
+}
+runfailure
+
+msgmsg "Running test with gpgv-untrusted digest"
+export APT_TESTS_DIGEST_ALGO='MD5'
+runfailure