summaryrefslogtreecommitdiff
path: root/apt-pkg
diff options
context:
space:
mode:
authorJulian Andres Klode <jak@debian.org>2024-11-28 18:25:02 +0000
committerJulian Andres Klode <jak@debian.org>2024-11-28 18:25:02 +0000
commit46e45bf1f4658075f87985d543414e45186e72d2 (patch)
tree7259447c78be773163b348c196f9a59577f40427 /apt-pkg
parent8d66ab29191c59baac04ef89fac30630cf9535e9 (diff)
parent86f8353ce7a343db7b19422db0d37379ac45ff4e (diff)
Merge branch 'prep-bye-apt-key' into 'main'
Stop using apt-key See merge request apt-team/apt!407
Diffstat (limited to 'apt-pkg')
-rw-r--r--apt-pkg/contrib/fileutl.cc2
-rw-r--r--apt-pkg/contrib/gpgv.cc254
-rw-r--r--apt-pkg/contrib/gpgv.h10
-rw-r--r--apt-pkg/contrib/strutl.cc67
-rw-r--r--apt-pkg/contrib/strutl.h1
5 files changed, 269 insertions, 65 deletions
diff --git a/apt-pkg/contrib/fileutl.cc b/apt-pkg/contrib/fileutl.cc
index a45069ee9..67c5acee2 100644
--- a/apt-pkg/contrib/fileutl.cc
+++ b/apt-pkg/contrib/fileutl.cc
@@ -3232,7 +3232,7 @@ bool Popen(const char *Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode,
} else if(Mode == FileFd::WriteOnly)
dup2(fd, 0);
- execv(Args[0], (char**)Args);
+ execvp(Args[0], (char **)Args);
_exit(100);
}
if(Mode == FileFd::ReadOnly)
diff --git a/apt-pkg/contrib/gpgv.cc b/apt-pkg/contrib/gpgv.cc
index 147f15d9d..43c634dde 100644
--- a/apt-pkg/contrib/gpgv.cc
+++ b/apt-pkg/contrib/gpgv.cc
@@ -18,11 +18,13 @@
#include <unistd.h>
#include <algorithm>
+#include <forward_list>
#include <fstream>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
+#include <unordered_map>
#include <vector>
#include <apti18n.h>
@@ -112,7 +114,9 @@ static bool operator!=(LineBuffer const &buf, std::string_view const exp) noexce
And as a cherry on the cake, we use our apt-key wrapper to do part
of the lifting in regards to merging keyrings. Fun for the whole family.
*/
-static void APT_PRINTF(4) apt_error(std::ostream &outterm, int const statusfd, int fd[2], const char *format, ...)
+#define apt_error(...) apt_msg("ERROR", __VA_ARGS__)
+#define apt_warning(...) apt_msg("WARNING", __VA_ARGS__)
+static void APT_PRINTF(5) apt_msg(std::string const &tag, std::ostream &outterm, int const statusfd, int fd[2], const char *format, ...)
{
std::ostringstream outstr;
std::ostream &out = (statusfd == -1) ? outterm : outstr;
@@ -128,53 +132,197 @@ static void APT_PRINTF(4) apt_error(std::ostream &outterm, int const statusfd, i
}
if (statusfd != -1)
{
- auto const errtag = "[APTKEY:] ERROR ";
+ auto const errtag = "[APTKEY:] " + tag + " ";
outstr << '\n';
auto const errtext = outstr.str();
- if (not FileFd::Write(fd[1], errtag, strlen(errtag)) ||
+ if (not FileFd::Write(fd[1], errtag.data(), errtag.size()) ||
not FileFd::Write(fd[1], errtext.data(), errtext.size()))
outterm << errtext << std::flush;
}
}
+
+static bool CheckGPGV(std::unordered_map<std::string, std::forward_list<std::string>> &checkedCommands, std::string gpgv, bool Debug)
+{
+ if (checkedCommands.find(gpgv) == checkedCommands.end())
+ {
+ // Create entry
+ checkedCommands[gpgv];
+ FileFd dumpOptions;
+ pid_t child;
+ const char *argv[] = {gpgv.c_str(), "--dump-options", nullptr};
+ if (unlikely(Debug))
+ std::clog << "Executing " << gpgv << " --dump-options" << std::endl;
+ if (not Popen(argv, dumpOptions, child, FileFd::ReadOnly) && Debug)
+ return false;
+
+ for (std::string line; dumpOptions.ReadLine(line);)
+ {
+ if (unlikely(Debug))
+ std::clog << "Read line: " << line << std::endl;
+ checkedCommands[gpgv].push_front(APT::String::Strip(line));
+ }
+ dumpOptions.Close();
+ waitpid(child, NULL, 0);
+ }
+ return not checkedCommands[gpgv].empty();
+}
+
+std::pair<std::string, std::forward_list<std::string>> APT::Internal::FindGPGV(bool Debug)
+{
+ static thread_local std::unordered_map<std::string, std::forward_list<std::string>> checkedCommands;
+ const std::string gpgvVariants[] = {
+ _config->Find("Apt::Key::gpgvcommand"),
+ // Prefer absolute path
+ "/usr/bin/gpgv-sq",
+ "/usr/bin/gpgv",
+ "/usr/bin/gpgv2",
+ "/usr/bin/gpgv1",
+ "gpgv-sq",
+ "gpgv",
+ "gpgv2",
+ "gpgv1",
+ };
+ for (auto gpgv : gpgvVariants)
+ if (CheckGPGV(checkedCommands, gpgv, Debug))
+ return std::make_pair(gpgv, checkedCommands[gpgv]);
+ return {};
+}
+
void ExecGPGV(std::string const &File, std::string const &FileGPG,
int const &statusfd, int fd[2], std::string const &key)
{
- #define EINTERNAL 111
- std::string const aptkey = _config->Find("Dir::Bin::apt-key", CMAKE_INSTALL_FULL_BINDIR "/apt-key");
+ auto const keyFiles = VectorizeString(key, ',');
+ ExecGPGV(File, FileGPG, statusfd, fd, keyFiles);
+}
+void ExecGPGV(std::string const &File, std::string const &FileGPG,
+ int const &statusfd, int fd[2], std::vector<std::string> const &KeyFiles)
+{
+#define EINTERNAL 111
bool const Debug = _config->FindB("Debug::Acquire::gpgv", false);
struct exiter {
- std::vector<const char *> files;
+ std::vector<std::string> files;
void operator ()(int code) APT_NORETURN {
- std::for_each(files.begin(), files.end(), unlink);
+ std::for_each(files.begin(), files.end(), [](auto f)
+ { unlink(f.c_str()); });
exit(code);
}
} local_exit;
+ auto [gpgv, supportedOptions] = APT::Internal::FindGPGV(Debug);
+ if (gpgv.empty())
+ {
+ apt_error(std::cerr, statusfd, fd, "Couldn't find a gpgv binary");
+ local_exit(EINTERNAL);
+ }
- std::vector<const char *> Args;
+ std::vector<std::string> Args;
Args.reserve(10);
- Args.push_back(aptkey.c_str());
- Args.push_back("--quiet");
- Args.push_back("--readonly");
- auto const keysFileFpr = VectorizeString(key, ',');
- for (auto const &k: keysFileFpr)
+ Args.push_back(gpgv);
+ Args.push_back("--ignore-time-conflict");
+
+ auto dearmorKeyOrCheckFormat = [&](std::string const &k) -> std::string
+ {
+ FileFd keyFd(k, FileFd::ReadOnly);
+ if (not keyFd.IsOpen())
+ {
+ apt_warning(std::cerr, statusfd, fd, "The key(s) in the keyring %s are ignored as the file is not readable by user executing apt-key.\n", k.c_str());
+ return "";
+ }
+ if (APT::String::Endswith(k, ".gpg") || APT::String::Endswith(k, ".pub"))
+ {
+ unsigned char c;
+ if (not keyFd.Read(&c, sizeof(c)))
+ goto err;
+ // Identify the leading byte of an OpenPGP public key packet
+ // 0x98 -- old-format OpenPGP public key packet, up to 255 octets
+ // 0x99 -- old-format OpenPGP public key packet, 256-65535 octets
+ // 0xc6 -- new-format OpenPGP public key packet, any length
+ if (c == 0x98 || c == 0x99 || c == 0xc6)
+ return k;
+ }
+ else if (APT::String::Endswith(k, ".asc"))
+ {
+ std::string b64msg;
+ int state = 0;
+ for (std::string line; keyFd.ReadLine(line);)
+ {
+ line = APT::String::Strip(line);
+ if (APT::String::Startswith(line, "-----BEGIN PGP PUBLIC KEY BLOCK-----"))
+ state = 1;
+ else if (state == 1 && line == "")
+ state = 2;
+ else if (state == 2 && line != "" && line[0] != '=' && line[0] != '-')
+ b64msg += line;
+ else if (APT::String::Startswith(line, "-----END"))
+ state = 3;
+ }
+ if (state != 3)
+ goto err;
+
+ FileFd dearmoredFd;
+ if (GetTempFile("apt.XXXXXX.gpg", false, &dearmoredFd) == nullptr)
+ local_exit(EINTERNAL);
+ if (auto decoded = Base64Decode(b64msg); not decoded.empty())
+ dearmoredFd.Write(decoded.data(), decoded.size());
+ local_exit.files.push_back(dearmoredFd.Name());
+ return dearmoredFd.Name();
+ }
+ err:
+ apt_warning(std::cerr, statusfd, fd, "The key(s) in the keyring %s are ignored as the file has an unsupported filetype.", k.c_str());
+ return "";
+ };
+ auto maybeAddKeyring = [&](std::string const &k)
+ {
+ if (struct stat st; stat(k.c_str(), &st) != 0 || st.st_size == 0)
+ return;
+ if (auto cleanKey = dearmorKeyOrCheckFormat(k); not cleanKey.empty())
+ {
+ Args.push_back("--keyring");
+ Args.push_back(cleanKey);
+ }
+ return;
+ };
+
+ for (auto const &k : KeyFiles)
{
if (unlikely(k.empty()))
continue;
if (k[0] == '/')
{
- Args.push_back("--keyring");
- Args.push_back(k.c_str());
+ if (Debug)
+ std::clog << "Trying Signed-By: " << k << std::endl;
+
+ maybeAddKeyring(k);
}
else
{
Args.push_back("--keyid");
- Args.push_back(k.c_str());
+ Args.push_back(k);
}
}
- Args.push_back("verify");
+
+ std::vector<std::string> Parts;
+ if (std::find(Args.begin(), Args.end(), "--keyring") == Args.end())
+ {
+ Parts = GetListOfFilesInDir(_config->FindDir("Dir::Etc::TrustedParts"), std::vector<std::string>{"gpg", "asc"}, true);
+ if (char *env = getenv("APT_KEY_NO_LEGACY_KEYRING"); env == nullptr || not StringToBool(env, false))
+ Parts.insert(Parts.begin(), _config->FindFile("Dir::Etc::Trusted"));
+ for (auto &Part : Parts)
+ {
+ if (Debug)
+ std::clog << "Trying TrustedPart: " << Part << std::endl;
+ maybeAddKeyring(Part);
+ }
+ }
+
+ // If we do not give it any keyring, gpgv shouts keydb errors at us
+ if (std::find(Args.begin(), Args.end(), "--keyring") == Args.end())
+ {
+ Args.push_back("--keyring");
+ Args.push_back("/dev/null");
+ }
char statusfdstr[10];
if (statusfd != -1)
@@ -184,6 +332,12 @@ void ExecGPGV(std::string const &File, std::string const &FileGPG,
Args.push_back(statusfdstr);
}
+ if (auto assertPubkeyAlgo = _config->Find("Apt::Key::assert-pubkey-algo"); not assertPubkeyAlgo.empty())
+ {
+ if (std::find(supportedOptions.begin(), supportedOptions.end(), "--assert-pubkey-algo") != supportedOptions.end())
+ Args.push_back("--assert-pubkey-algo=" + assertPubkeyAlgo);
+ }
+
Configuration::Item const *Opts;
Opts = _config->Tree("Acquire::gpgv::Options");
if (Opts != 0)
@@ -193,42 +347,11 @@ void ExecGPGV(std::string const &File, std::string const &FileGPG,
{
if (Opts->Value.empty())
continue;
- Args.push_back(Opts->Value.c_str());
+ Args.push_back(Opts->Value);
}
}
enum { DETACHED, CLEARSIGNED } releaseSignature = (FileGPG != File) ? DETACHED : CLEARSIGNED;
- std::unique_ptr<char, FreeDeleter> sig;
- std::unique_ptr<char, FreeDeleter> data;
- std::unique_ptr<char, FreeDeleter> conf;
-
- // Dump the configuration so apt-key picks up the correct Dir values
- {
- {
- std::string tmpfile;
- strprintf(tmpfile, "%s/apt.conf.XXXXXX", GetTempDir().c_str());
- conf.reset(strdup(tmpfile.c_str()));
- }
- if (conf == nullptr) {
- apt_error(std::cerr, statusfd, fd, "Couldn't create tempfile names for passing config to apt-key");
- local_exit(EINTERNAL);
- }
- int confFd = mkstemp(conf.get());
- if (confFd == -1) {
- apt_error(std::cerr, statusfd, fd, "Couldn't create temporary file %s for passing config to apt-key", conf.get());
- local_exit(EINTERNAL);
- }
- local_exit.files.push_back(conf.get());
-
- std::ofstream confStream(conf.get());
- close(confFd);
- _config->Dump(confStream);
- confStream.close();
- setenv("APT_CONFIG", conf.get(), 1);
- }
-
- // Tell apt-key not to emit warnings
- setenv("APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE", "1", 1);
if (releaseSignature == DETACHED)
{
@@ -307,21 +430,19 @@ void ExecGPGV(std::string const &File, std::string const &FileGPG,
local_exit(112);
}
- Args.push_back(FileGPG.c_str());
- Args.push_back(File.c_str());
+ Args.push_back(FileGPG);
+ Args.push_back(File);
}
else // clear-signed file
{
FileFd signature;
if (GetTempFile("apt.sig", false, &signature) == nullptr)
local_exit(EINTERNAL);
- sig.reset(strdup(signature.Name().c_str()));
- local_exit.files.push_back(sig.get());
+ local_exit.files.push_back(signature.Name());
FileFd message;
if (GetTempFile("apt.data", false, &message) == nullptr)
local_exit(EINTERNAL);
- data.reset(strdup(message.Name().c_str()));
- local_exit.files.push_back(data.get());
+ local_exit.files.push_back(message.Name());
if (signature.Failed() || message.Failed() ||
not SplitClearSignedFile(File, &message, nullptr, &signature))
@@ -329,17 +450,15 @@ void ExecGPGV(std::string const &File, std::string const &FileGPG,
apt_error(std::cerr, statusfd, fd, "Splitting up %s into data and signature failed", File.c_str());
local_exit(112);
}
- Args.push_back(sig.get());
- Args.push_back(data.get());
+ Args.push_back(signature.Name());
+ Args.push_back(message.Name());
}
- Args.push_back(NULL);
-
if (Debug)
{
std::clog << "Preparing to exec: ";
- for (std::vector<const char *>::const_iterator a = Args.begin(); *a != NULL; ++a)
- std::clog << " " << *a;
+ for (auto const &a : Args)
+ std::clog << " " << a;
std::clog << std::endl;
}
@@ -360,20 +479,27 @@ void ExecGPGV(std::string const &File, std::string const &FileGPG,
putenv((char *)"LC_MESSAGES=");
}
+ // Translate the argument list to a C array. This should happen before
+ // the fork so we don't allocate money between fork() and execvp().
+ std::vector<const char *> cArgs;
+ cArgs.reserve(Args.size() + 1);
+ for (auto const &arg : Args)
+ cArgs.push_back(arg.c_str());
+ cArgs.push_back(nullptr);
// We have created tempfiles we have to clean up
// and we do an additional check, so fork yet another time …
pid_t pid = ExecFork();
if(pid < 0) {
- apt_error(std::cerr, statusfd, fd, "Fork failed for %s to check %s", Args[0], File.c_str());
+ apt_error(std::cerr, statusfd, fd, "Fork failed for %s to check %s", Args[0].c_str(), File.c_str());
local_exit(EINTERNAL);
}
if(pid == 0)
{
if (statusfd != -1)
dup2(fd[1], statusfd);
- execvp(Args[0], (char **) &Args[0]);
- apt_error(std::cerr, statusfd, fd, "Couldn't execute %s to check %s", Args[0], File.c_str());
+ execvp(cArgs[0], (char **) &cArgs[0]);
+ apt_error(std::cerr, statusfd, fd, "Couldn't execute %s to check %s", Args[0].c_str(), File.c_str());
local_exit(EINTERNAL);
}
diff --git a/apt-pkg/contrib/gpgv.h b/apt-pkg/contrib/gpgv.h
index 1f3ef26f9..d4520f3a2 100644
--- a/apt-pkg/contrib/gpgv.h
+++ b/apt-pkg/contrib/gpgv.h
@@ -11,12 +11,20 @@
#include <apt-pkg/macros.h>
+#include <forward_list>
#include <string>
#include <vector>
class FileFd;
+#ifdef APT_COMPILING_APT
+namespace APT::Internal
+{
+APT_PUBLIC std::pair<std::string, std::forward_list<std::string>> FindGPGV(bool Debug);
+}
+#endif
+
/** \brief generates and run the command to verify a file with gpgv
*
* If File and FileSig specify the same file it is assumed that we
@@ -40,6 +48,8 @@ class FileFd;
* @param key is the specific one to be used instead of using all
*/
APT_PUBLIC void ExecGPGV(std::string const &File, std::string const &FileSig,
+ int const &statusfd, int fd[2], std::vector<std::string> const &KeyFiles) APT_NORETURN;
+APT_PUBLIC void ExecGPGV(std::string const &File, std::string const &FileSig,
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) {
diff --git a/apt-pkg/contrib/strutl.cc b/apt-pkg/contrib/strutl.cc
index ec61c7e2b..69a67188e 100644
--- a/apt-pkg/contrib/strutl.cc
+++ b/apt-pkg/contrib/strutl.cc
@@ -625,6 +625,73 @@ string Base64Encode(const string &S)
return Final;
}
/*}}}*/
+// Base64Decode - Base64 decoding routine for short strings /*{{{*/
+// ---------------------------------------------------------------------
+// Taken from mjg59's dpkg code: https://lists.debian.org/debian-dpkg/2018/05/msg00002.html
+std::string Base64Decode(const std::string_view in)
+{
+ constexpr std::array<unsigned char, 256> mime_base64_rank{
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 0, 255, 255,
+ 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
+ 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
+ std::string out;
+ unsigned char rank;
+ unsigned char last[2];
+ unsigned int v;
+ int i;
+
+ /* convert 4 base64 bytes to 3 normal bytes */
+ v = 0;
+ i = 0;
+
+ last[0] = last[1] = 0;
+
+ /* we use the sign in the state to determine if we got a padding character
+ in the previous sequence */
+ if (i < 0)
+ {
+ i = -i;
+ last[0] = '=';
+ }
+
+ for (const unsigned char c : in)
+ {
+ rank = mime_base64_rank[c];
+ if (rank != 0xff)
+ {
+ last[1] = last[0];
+ last[0] = c;
+ v = (v << 6) | rank;
+ i++;
+ if (i == 4)
+ {
+ out += v >> 16;
+ if (last[1] != '=')
+ out += v >> 8;
+ if (last[0] != '=')
+ out += v;
+ i = 0;
+ }
+ }
+ }
+
+ return out;
+}
+
+ /*}}}*/
// stringcmp - Arbitrary string compare /*{{{*/
// ---------------------------------------------------------------------
/* This safely compares two non-null terminated strings of arbitrary
diff --git a/apt-pkg/contrib/strutl.h b/apt-pkg/contrib/strutl.h
index 90d9674fd..91f5fe44b 100644
--- a/apt-pkg/contrib/strutl.h
+++ b/apt-pkg/contrib/strutl.h
@@ -69,6 +69,7 @@ APT_PUBLIC std::string DeEscapeString(const std::string &input);
APT_PUBLIC std::string SizeToStr(double Bytes);
APT_PUBLIC std::string TimeToStr(unsigned long Sec);
APT_PUBLIC std::string Base64Encode(const std::string &Str);
+APT_PUBLIC std::string Base64Decode(const std::string_view in);
APT_PUBLIC std::string OutputInDepth(const unsigned long Depth, const char* Separator=" ");
APT_PUBLIC std::string URItoFileName(const std::string &URI);
/** returns a datetime string as needed by HTTP/1.1 and Debian files.