Header += MaximumSize;
string const FinalFile = GetFinalFilename();
-
struct stat Buf;
if (stat(FinalFile.c_str(),&Buf) == 0)
Header += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
{
string Header = pkgAcqMetaBase::Custom600Headers();
Header += "\nFail-Ignore: true";
+ std::string const key = TransactionManager->MetaIndexParser->GetSignedBy();
+ if (key.empty() == false)
+ Header += "\nSigned-By: " + key;
+
return Header;
}
/*}}}*/
/*}}}*/
pkgAcqMetaSig::~pkgAcqMetaSig() /*{{{*/
{
+}
+ /*}}}*/
+// pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
+std::string pkgAcqMetaSig::Custom600Headers() const
+{
+ std::string Header = pkgAcqTransactionItem::Custom600Headers();
+ std::string const key = TransactionManager->MetaIndexParser->GetSignedBy();
+ if (key.empty() == false)
+ Header += "\nSigned-By: " + key;
+ return Header;
}
/*}}}*/
// AcqMetaSig::Done - The signature was downloaded/verified /*{{{*/
virtual void Failed(std::string const &Message,pkgAcquire::MethodConfig const * const Cnf);
virtual void Done(std::string const &Message, HashStringList const &Hashes,
pkgAcquire::MethodConfig const * const Cnf);
+ virtual std::string Custom600Headers() const;
/** \brief Create a new pkgAcqMetaSig. */
pkgAcqMetaSig(pkgAcquire * const Owner, pkgAcqMetaClearSig * const TransactionManager,
#include <sys/wait.h>
#include <unistd.h>
#include <stddef.h>
+
+#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
of the lifting in regards to merging keyrings. Fun for the whole family.
*/
void ExecGPGV(std::string const &File, std::string const &FileGPG,
- int const &statusfd, int fd[2])
+ int const &statusfd, int fd[2], std::string const &key)
{
#define EINTERNAL 111
std::string const aptkey = _config->FindFile("Dir::Bin::apt-key", "/usr/bin/apt-key");
Args.push_back(aptkey.c_str());
Args.push_back("--quiet");
Args.push_back("--readonly");
+ if (key.empty() == false)
+ {
+ if (key[0] == '/')
+ {
+ Args.push_back("--keyring");
+ Args.push_back(key.c_str());
+ }
+ else
+ {
+ Args.push_back("--keyid");
+ Args.push_back(key.c_str());
+ }
+ }
Args.push_back("verify");
char statusfdstr[10];
*
* @param File is the message (unsigned or clear-signed)
* @param FileSig is the signature (detached or clear-signed)
+ * @param statusfd is the fd given to gpgv as --status-fd
+ * @param fd is used as a pipe for the standard output of gpgv
+ * @param key is the specific one to be used instead of using all
*/
void ExecGPGV(std::string const &File, std::string const &FileSig,
- int const &statusfd, int fd[2]) APT_NORETURN;
+ int const &statusfd, int fd[2], std::string const &Key = "") APT_NORETURN;
inline APT_NORETURN void ExecGPGV(std::string const &File, std::string const &FileSig,
int const &statusfd = -1) {
int fd[2];
else if (d->ValidUntilMax != Valid)
return _error->Error(_("Conflicting values set for option %s concerning source %s %s"), "Max-ValidTime", URI.c_str(), Dist.c_str());
return true;
+}
+bool debReleaseIndex::SetSignedBy(std::string const &pSignedBy)
+{
+ if (SignedBy.empty() == true && pSignedBy.empty() == false)
+ {
+ if (pSignedBy[0] == '/') // no check for existence as we could be chrooting later or such things
+ ; // absolute path to a keyring file
+ else
+ {
+ // we could go all fancy and allow short/long/string matches as gpgv/apt-key does,
+ // but fingerprints are harder to fake than the others and this option is set once,
+ // not interactively all the time so easy to type is not really a concern.
+ std::string finger = pSignedBy;
+ finger.erase(std::remove(finger.begin(), finger.end(), ' '), finger.end());
+ std::transform(finger.begin(), finger.end(), finger.begin(), ::toupper);
+ if (finger.length() != 40 || finger.find_first_not_of("0123456789ABCDEF") != std::string::npos)
+ return _error->Error(_("Invalid value set for option %s concerning source %s %s (%s)"), "Signed-By", URI.c_str(), Dist.c_str(), "not a fingerprint");
+ }
+ SignedBy = pSignedBy;
+ }
+ else if (SignedBy != pSignedBy)
+ return _error->Error(_("Conflicting values set for option %s concerning source %s %s"), "Signed-By", URI.c_str(), Dist.c_str());
+ return true;
}
/*}}}*/
// ReleaseIndex::IsTrusted /*{{{*/
Deb->SetValidUntilMin(GetTimeOption(Options, "valid-until-min")) == false)
return false;
+ std::map<std::string, std::string>::const_iterator const signedby = Options.find("signed-by");
+ if (signedby == Options.end())
+ {
+ if (Deb->SetSignedBy("") == false)
+ return false;
+ }
+ else
+ {
+ if (Deb->SetSignedBy(signedby->second) == false)
+ return false;
+ }
+
return true;
}
bool SetCheckValidUntil(TriState const Trusted);
bool SetValidUntilMin(time_t const Valid);
bool SetValidUntilMax(time_t const Valid);
+ bool SetSignedBy(std::string const &SignedBy);
virtual bool IsTrusted() const;
metaIndex::metaIndex(std::string const &URI, std::string const &Dist,
char const * const Type)
: d(NULL), Indexes(NULL), Type(Type), URI(URI), Dist(Dist), Trusted(TRI_UNSET),
- LoadedSuccessfully(TRI_UNSET),
- Date(0), ValidUntil(0), SupportsAcquireByHash(false)
+ Date(0), ValidUntil(0), SupportsAcquireByHash(false), LoadedSuccessfully(TRI_UNSET)
{
/* nothing */
}
APT_PURE std::string metaIndex::GetDist() const { return Dist; }
APT_PURE const char* metaIndex::GetType() const { return Type; }
APT_PURE metaIndex::TriState metaIndex::GetTrusted() const { return Trusted; }
+APT_PURE std::string metaIndex::GetSignedBy() const { return SignedBy; }
APT_PURE std::string metaIndex::GetCodename() const { return Codename; }
APT_PURE std::string metaIndex::GetSuite() const { return Suite; }
APT_PURE bool metaIndex::GetSupportsAcquireByHash() const { return SupportsAcquireByHash; }
std::string URI;
std::string Dist;
TriState Trusted;
- TriState LoadedSuccessfully;
+ std::string SignedBy;
// parsed from a file
std::string Suite;
time_t ValidUntil;
bool SupportsAcquireByHash;
std::map<std::string, checkSum *> Entries;
+ TriState LoadedSuccessfully;
public:
// Various accessors
std::string GetDist() const;
const char* GetType() const;
TriState GetTrusted() const;
+ std::string GetSignedBy() const;
std::string GetCodename() const;
std::string GetSuite() const;
if (Enabled.empty() == false && StringToBool(Enabled) == false)
return true;
- std::map<char const * const, char const * const> mapping;
+ std::map<char const * const, std::pair<char const * const, bool> > mapping;
#define APT_PLUSMINUS(X, Y) \
- mapping.insert(std::make_pair(X, Y)); \
- mapping.insert(std::make_pair(X "Add", Y "+")); \
- mapping.insert(std::make_pair(X "Remove", Y "-"))
+ mapping.insert(std::make_pair(X, std::make_pair(Y, true))); \
+ mapping.insert(std::make_pair(X "Add", std::make_pair(Y "+", true))); \
+ mapping.insert(std::make_pair(X "Remove", std::make_pair(Y "-", true)))
APT_PLUSMINUS("Architectures", "arch");
APT_PLUSMINUS("Languages", "lang");
APT_PLUSMINUS("Targets", "target");
#undef APT_PLUSMINUS
- mapping.insert(std::make_pair("Trusted", "trusted"));
- mapping.insert(std::make_pair("Check-Valid-Until", "check-valid-until"));
- mapping.insert(std::make_pair("Valid-Until-Min", "valid-until-min"));
- mapping.insert(std::make_pair("Valid-Until-Max", "valid-until-max"));
+ mapping.insert(std::make_pair("Trusted", std::make_pair("trusted", false)));
+ mapping.insert(std::make_pair("Check-Valid-Until", std::make_pair("check-valid-until", false)));
+ mapping.insert(std::make_pair("Valid-Until-Min", std::make_pair("valid-until-min", false)));
+ mapping.insert(std::make_pair("Valid-Until-Max", std::make_pair("valid-until-max", false)));
+ mapping.insert(std::make_pair("Signed-By", std::make_pair("signed-by", false)));
- for (std::map<char const * const, char const * const>::const_iterator m = mapping.begin(); m != mapping.end(); ++m)
+ for (std::map<char const * const, std::pair<char const * const, bool> >::const_iterator m = mapping.begin(); m != mapping.end(); ++m)
if (Tags.Exists(m->first))
{
- // for deb822 the " " is the delimiter, but the backend expects ","
- std::string option = Tags.FindS(m->first);
- std::replace(option.begin(), option.end(), ' ', ',');
- Options[m->second] = option;
+ std::string option = Tags.FindS(m->first);
+ // for deb822 the " " is the delimiter, but the backend expects ","
+ if (m->second.second == true)
+ std::replace(option.begin(), option.end(), ' ', ',');
+ Options[m->second.first] = option;
}
// now create one item per suite/section
foreach_keyring_do() {
local ACTION="$1"
shift
- # if a --keyring was given, just remove from there
+ # if a --keyring was given, just work on this one
if [ -n "$FORCED_KEYRING" ]; then
$ACTION "$FORCED_KEYRING" "$@"
else
}
setup_merged_keyring() {
- if [ -z "$FORCED_KEYRING" ]; then
+ if [ -n "$FORCED_KEYID" ]; then
+ foreach_keyring_do 'import_keys_from_keyring' "${GPGHOMEDIR}/allrings.gpg"
+ FORCED_KEYRING="${GPGHOMEDIR}/forcedkeyid.gpg"
+ TRUSTEDFILE="${FORCED_KEYRING}"
+ GPG="$GPG --keyring $TRUSTEDFILE"
+ # ignore error as this "just" means we haven't found the forced keyid and the keyring will be empty
+ $GPG_CMD --batch --yes --keyring "${GPGHOMEDIR}/allrings.gpg" --export "$FORCED_KEYID" | $GPG --batch --yes --import || true
+ elif [ -z "$FORCED_KEYRING" ]; then
foreach_keyring_do 'import_keys_from_keyring' "${GPGHOMEDIR}/pubring.gpg"
if [ -r "${GPGHOMEDIR}/pubring.gpg" ]; then
cp -a "${GPGHOMEDIR}/pubring.gpg" "${GPGHOMEDIR}/pubring.orig.gpg"
TRUSTEDFILE="$1"
FORCED_KEYRING="$1"
;;
+ --keyid)
+ shift
+ FORCED_KEYID="$1"
+ ;;
--secret-keyring)
shift
FORCED_SECRET_KEYRING="$1"
;;
--readonly)
merge_back_changes() { true; }
+ create_new_keyring() { true; }
;;
--fakeroot)
requires_root() { true; }
verify)
setup_merged_keyring
if which gpgv >/dev/null 2>&1; then
- gpgv --homedir "${GPGHOMEDIR}" --keyring "${GPGHOMEDIR}/pubring.gpg" --ignore-time-conflict "$@"
+ if [ -n "$FORCED_KEYRING" ]; then
+ gpgv --homedir "${GPGHOMEDIR}" --keyring "${FORCED_KEYRING}" --ignore-time-conflict "$@"
+ else
+ gpgv --homedir "${GPGHOMEDIR}" --keyring "${GPGHOMEDIR}/pubring.gpg" --ignore-time-conflict "$@"
+ fi
else
$GPG --verify "$@"
fi
anomalies.
<itemizedlist>
- <listitem><para><option>Trusted</option> (<option>trusted</option>)
- is a tri-state value which defaults to APT deciding if a source
- is considered trusted or if warnings should be raised before e.g.
- packages are installed from this source. This option can be used
- to override this decision either with the value <literal>yes</literal>,
- which lets APT consider this source always as a trusted source
- even if it has no or fails authentication checks by disabling parts
- of &apt-secure; and should therefore only be used in a local and trusted
- context (if at all) as otherwise security is breached. The opposite
- can be achieved with the value no, which causes the source to be handled
- as untrusted even if the authentication checks passed successfully.
- The default value can't be set explicitly.
+ <listitem><para><option>Signed-By</option> (<option>signed-by</option>)
+ is either an absolute path to a keyring file (has to be
+ accessible and readable for the <literal>_apt</literal> user,
+ so ensure everyone has read-permissions on the file) or a
+ fingerprint of a key in either the
+ <filename>trusted.gpg</filename> keyring or in one of the
+ keyrings in the <filename>trusted.gpg.d/</filename> directory
+ (see <command>apt-key fingerprint</command>). If the option is
+ set only the key(s) in this keyring or only the key with this
+ fingerprint is used for the &apt-secure; verification of this
+ repository. Otherwise all keys in the trusted keyrings are
+ considered valid signers for this repository.
</para></listitem>
<listitem><para><option>Check-Valid-Until</option> (<option>check-valid-until</option>)
{
private:
string VerifyGetSigners(const char *file, const char *outfile,
- vector<string> &GoodSigners,
+ std::string const &key,
+ vector<string> &GoodSigners,
vector<string> &BadSigners,
vector<string> &WorthlessSigners,
vector<string> &NoPubKeySigners);
protected:
- virtual bool Fetch(FetchItem *Itm);
+ virtual bool URIAcquire(std::string const &Message, FetchItem *Itm);
virtual bool Configuration(string Message);
public:
}
string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile,
+ std::string const &key,
vector<string> &GoodSigners,
vector<string> &BadSigners,
vector<string> &WorthlessSigners,
if (pid < 0)
return string("Couldn't spawn new process") + strerror(errno);
else if (pid == 0)
- ExecGPGV(outfile, file, 3, fd);
+ ExecGPGV(outfile, file, 3, fd, key);
close(fd[1]);
FILE *pipein = fdopen(fd[0], "r");
return _("Unknown error executing apt-key");
}
-bool GPGVMethod::Fetch(FetchItem *Itm)
+bool GPGVMethod::URIAcquire(std::string const &Message, FetchItem *Itm)
{
- URI Get = Itm->Uri;
- string Path = Get.Host + Get.Path; // To account for relative paths
- string keyID;
+ URI const Get = Itm->Uri;
+ string const Path = Get.Host + Get.Path; // To account for relative paths
+ std::string const key = LookupTag(Message, "Signed-By");
vector<string> GoodSigners;
vector<string> BadSigners;
// a worthless signature is a expired or revoked one
URIStart(Res);
// Run apt-key on file, extract contents and get the key ID of the signer
- string msg = VerifyGetSigners(Path.c_str(), Itm->DestFile.c_str(),
+ string msg = VerifyGetSigners(Path.c_str(), Itm->DestFile.c_str(), key,
GoodSigners, BadSigners, WorthlessSigners,
NoPubKeySigners);
if (GoodSigners.empty() || !BadSigners.empty() || !NoPubKeySigners.empty())
else
local EXITCODE=$?
if expr match "$1" '^apt.*' >/dev/null; then
- if grep -q -E ' runtime error: ' "$OUTPUT"; then
- msgfailoutput 'compiler detected undefined behavior' "$OUTPUT" "$@"
- elif grep -q -E '==ERROR' "$OUTPUT"; then
- msgfailoutput 'compiler sanitizers reported errors' "$OUTPUT" "$@"
- elif ! grep -q -E '^E: ' "$OUTPUT"; then
- msgfailoutput "run failed with exitcode ${EXITCODE}, but with no errors" "$OUTPUT" "$@"
+ if [ "$1" = 'aptkey' ]; then
+ if grep -q -E " Can't check signature: " "$OUTPUT" || \
+ grep -q -E " BAD signature from " "$OUTPUT"; then
+ msgpass
+ else
+ msgfailoutput "run failed with exitcode ${EXITCODE}, but no signature error" "$OUTPUT" "$@"
+ fi
else
- msgpass
+ if grep -q -E ' runtime error: ' "$OUTPUT"; then
+ msgfailoutput 'compiler detected undefined behavior' "$OUTPUT" "$@"
+ elif grep -q -E '==ERROR' "$OUTPUT"; then
+ msgfailoutput 'compiler sanitizers reported errors' "$OUTPUT" "$@"
+ elif ! grep -q -E '^E: ' "$OUTPUT"; then
+ msgfailoutput "run failed with exitcode ${EXITCODE}, but with no errors" "$OUTPUT" "$@"
+ else
+ msgpass
+ fi
fi
else
msgpass
testsuccess aptkey --fakeroot del DBAC8DAE
testempty aptkey list
- msgtest 'Test key removal with' 'lowercase key ID' #keylength somewher between 8byte and short
+ msgtest 'Test key removal with' 'lowercase key ID' #keylength somewhere between 8byte and short
cleanplate
cp -a keys/joesixpack.pub rootdir/etc/apt/trusted.gpg.d/joesixpack.gpg
testsuccess --nomsg aptkey --fakeroot del d141dbac8dae
msgtest 'Test merge-back of' 'removed duplicate keys'
testsuccess --nomsg aptkey adv --batch --yes --delete-keys DBAC8DAE
testaptkeys 'pub 2048R/528144E2 2011-01-16'
+
+ cleanplate
+ cp -a keys/joesixpack.pub rootdir/etc/apt/trusted.gpg.d/joesixpack.gpg
+ cp -a keys/testcase-multikey.pub rootdir/etc/apt/trusted.gpg.d/multikey.gpg
+ msgtest 'Test signing a file' 'with a key'
+ echo 'Verify me. This is my signature.' > signature
+ testsuccess --nomsg aptkey --quiet --keyring keys/marvinparanoid.pub --secret-keyring keys/marvinparanoid.sec --readonly \
+ adv --batch --yes --default-key 'Marvin' --armor --detach-sign --sign --output signature.gpg signature
+
+ msgtest 'Test verify a file' 'with all keys'
+ testsuccess --nomsg aptkey --quiet --readonly verify signature.gpg signature
+
+ msgtest 'Test verify a file' 'with good keyring'
+ testsuccess --nomsg aptkey --quiet --readonly --keyring keys/testcase-multikey.pub verify signature.gpg signature
+
+ msgtest 'Test fail verify a file' 'with bad keyring'
+ testfailure --nomsg aptkey --quiet --readonly --keyring keys/joesixpack.pub verify signature.gpg signature
+
+ msgtest 'Test fail verify a file' 'with non-existing keyring'
+ testfailure --nomsg aptkey --quiet --readonly --keyring keys/does-not-exist.pub verify signature.gpg signature
+ testfailure test -e keys/does-not-exist.pub
+
+ msgtest 'Test verify a file' 'with good keyid'
+ testsuccess --nomsg aptkey --quiet --readonly --keyid 'Paranoid' verify signature.gpg signature
+
+ msgtest 'Test fail verify a file' 'with bad keyid'
+ testfailure --nomsg aptkey --quiet --readonly --keyid 'Sixpack' verify signature.gpg signature
+
+ msgtest 'Test fail verify a file' 'with non-existing keyid'
+ testfailure --nomsg aptkey --quiet --readonly --keyid 'Kalnischkies' verify signature.gpg signature
+
+ msgtest 'Test verify fails on' 'bad file'
+ echo 'lalalalala' > signature
+ testfailure --nomsg aptkey --quiet --readonly verify signature.gpg signature
}
setupgpgcommand() {
testrun
setupgpgcommand 'gpg2'
testrun
-
failaptold
prepare ${PKGFILE}-new
- # weborf doesn't support If-Range
- for release in $(find rootdir/var/lib/apt/lists/partial/ -name '*Release'); do
- rm $release
- touch $release
- done
signreleasefiles 'Joe Sixpack'
find aptarchive/ -name "$DELETEFILE" -delete
msgmsg 'Bad warm archive signed by' 'Joe Sixpack'
testsuccessequal "$(cat ${PKGFILE}-new)
" aptcache show apt
installaptnew
+
+ prepare ${PKGFILE}
+ rm -rf rootdir/var/lib/apt/lists
+ signreleasefiles 'Marvin Paranoid'
+ find aptarchive/ -name "$DELETEFILE" -delete
+ msgmsg 'Cold archive signed by good keyring' 'Marvin Paranoid'
+ local MARVIN="$(readlink -f keys/marvinparanoid.pub)"
+ sed -i "s#^\(deb\(-src\)\?\) #\1 [signed-by=$MARVIN] #" rootdir/etc/apt/sources.list.d/*
+ testsuccess aptget update -o Debug::pkgAcquire::Worker=1
+ testsuccessequal "$(cat ${PKGFILE})
+" aptcache show apt
+ installaptold
+
+ rm -rf rootdir/var/lib/apt/lists
+ signreleasefiles 'Joe Sixpack'
+ find aptarchive/ -name "$DELETEFILE" -delete
+ msgmsg 'Cold archive signed by bad keyring' 'Joe Sixpack'
+ updatewithwarnings '^W: .* NO_PUBKEY'
+
+ sed -i "s#^\(deb\(-src\)\?\) \[signed-by=$MARVIN\] #\1 #" rootdir/etc/apt/sources.list.d/*
+ local MARVIN="$(aptkey --keyring $MARVIN finger | grep 'Key fingerprint' | cut -d'=' -f 2 | tr -d ' ')"
+
+ prepare ${PKGFILE}
+ rm -rf rootdir/var/lib/apt/lists
+ signreleasefiles 'Marvin Paranoid'
+ find aptarchive/ -name "$DELETEFILE" -delete
+ msgmsg 'Cold archive signed by good keyid' 'Marvin Paranoid'
+ sed -i "s#^\(deb\(-src\)\?\) #\1 [signed-by=$MARVIN] #" rootdir/etc/apt/sources.list.d/*
+ cp keys/marvinparanoid.pub rootdir/etc/apt/trusted.gpg.d/marvinparanoid.gpg
+ testsuccess aptget update -o Debug::pkgAcquire::Worker=1 -o Debug::Acquire::gpgv=1
+ testsuccessequal "$(cat ${PKGFILE})
+" aptcache show apt
+ installaptold
+ rm -f rootdir/etc/apt/trusted.gpg.d/marvinparanoid.gpg
+
+ rm -rf rootdir/var/lib/apt/lists
+ signreleasefiles 'Joe Sixpack'
+ find aptarchive/ -name "$DELETEFILE" -delete
+ msgmsg 'Cold archive signed by bad keyid' 'Joe Sixpack'
+ updatewithwarnings '^W: .* NO_PUBKEY'
+
+ sed -i "s#^\(deb\(-src\)\?\) \[signed-by=$MARVIN\] #\1 #" rootdir/etc/apt/sources.list.d/*
}
runtest2() {