summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMake/FindGCRYPT.cmake25
-rw-r--r--CMake/config.h.in3
-rw-r--r--CMakeLists.txt6
-rw-r--r--COPYING21
-rw-r--r--apt-pkg/CMakeLists.txt4
-rw-r--r--apt-pkg/contrib/gpgv.cc4
-rw-r--r--apt-pkg/contrib/hashes.cc172
-rw-r--r--cmdline/apt-helper.cc51
-rw-r--r--debian/control3
-rw-r--r--debian/tests/control3
-rw-r--r--methods/CMakeLists.txt8
-rw-r--r--methods/connect.cc386
-rw-r--r--methods/http.cc15
13 files changed, 374 insertions, 327 deletions
diff --git a/CMake/FindGCRYPT.cmake b/CMake/FindGCRYPT.cmake
deleted file mode 100644
index 56bfc9fef..000000000
--- a/CMake/FindGCRYPT.cmake
+++ /dev/null
@@ -1,25 +0,0 @@
-# - Try to find GCRYPT
-# Once done, this will define
-#
-# GCRYPT_FOUND - system has GCRYPT
-# GCRYPT_INCLUDE_DIRS - the GCRYPT include directories
-# GCRYPT_LIBRARIES - the GCRYPT library
-find_package(PkgConfig)
-
-pkg_check_modules(GCRYPT_PKGCONF libgcrypt)
-
-find_path(GCRYPT_INCLUDE_DIRS
- NAMES gcrypt.h
- PATHS ${GCRYPT_PKGCONF_INCLUDE_DIRS}
-)
-
-
-find_library(GCRYPT_LIBRARIES
- NAMES gcrypt
- PATHS ${GCRYPT_PKGCONF_LIBRARY_DIRS}
-)
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(GCRYPT DEFAULT_MSG GCRYPT_INCLUDE_DIRS GCRYPT_LIBRARIES)
-
-mark_as_advanced(GCRYPT_INCLUDE_DIRS GCRYPT_LIBRARIES)
diff --git a/CMake/config.h.in b/CMake/config.h.in
index 8346a5e9e..ced74d8d8 100644
--- a/CMake/config.h.in
+++ b/CMake/config.h.in
@@ -5,9 +5,6 @@
/* Define if we have the timegm() function */
#cmakedefine HAVE_TIMEGM
-/* Define if we have the gnutls library for TLS */
-#cmakedefine HAVE_GNUTLS
-
/* Define if we have the zlib library for gzip */
#cmakedefine HAVE_ZLIB
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c6977675e..af2ec3a86 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -92,10 +92,7 @@ if (BERKELEY_FOUND)
set(HAVE_BDB 1)
endif()
-find_package(GnuTLS)
-if (GNUTLS_FOUND)
- set(HAVE_GNUTLS 1)
-endif()
+find_package(OpenSSL REQUIRED)
# (De)Compressor libraries
find_package(ZLIB REQUIRED)
@@ -141,7 +138,6 @@ if (SECCOMP_FOUND)
set(HAVE_SECCOMP 1)
endif()
-find_package(GCRYPT REQUIRED)
find_package(XXHASH REQUIRED)
# Mount()ing and stat()ing and friends
diff --git a/COPYING b/COPYING
index 21b874fa7..670c30d5d 100644
--- a/COPYING
+++ b/COPYING
@@ -52,6 +52,10 @@ Copyright: 1997-1999 Jason Gunthorpe and others
2014 Anthony Towns
License: GPL-2+
+Files: methods/connect.c
+Copyright: Copyright (c) 1996 - 2023, Daniel Stenberg, <daniel@haxx.se>, and many contributors
+License: GPL-2+ and curl
+
Files: methods/rsh.cc
Copyright: 2000 Ben Collins <bcollins@debian.org>
License: GPL-2
@@ -153,3 +157,20 @@ License: GPL-2+
Comment:
On Debian systems, the complete text of the GNU General
Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
+
+License: curl
+ Permission to use, copy, modify, and distribute this software for any purpose
+ with or without fee is hereby granted, provided that the above copyright
+ notice and this permission notice appear in all copies.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+ OR OTHER DEALINGS IN THE SOFTWARE.
+ .
+ Except as contained in this notice, the name of a copyright holder shall not
+ be used in advertising or otherwise to promote the sale, use or other dealings
+ in this Software without prior written authorization of the copyright holder.
diff --git a/apt-pkg/CMakeLists.txt b/apt-pkg/CMakeLists.txt
index 63052faad..4c036cc67 100644
--- a/apt-pkg/CMakeLists.txt
+++ b/apt-pkg/CMakeLists.txt
@@ -56,7 +56,7 @@ target_include_directories(apt-pkg
$<$<BOOL:${UDEV_FOUND}>:${UDEV_INCLUDE_DIRS}>
$<$<BOOL:${SYSTEMD_FOUND}>:${SYSTEMD_INCLUDE_DIRS}>
${ICONV_INCLUDE_DIRS}
- $<$<BOOL:${GCRYPT_FOUND}>:${GCRYPT_INCLUDE_DIRS}>
+ ${OPENSSL_INCLUDE_DIRS}
$<$<BOOL:${XXHASH_FOUND}>:${XXHASH_INCLUDE_DIRS}>
)
@@ -71,7 +71,7 @@ target_link_libraries(apt-pkg
$<$<BOOL:${UDEV_FOUND}>:${UDEV_LIBRARIES}>
$<$<BOOL:${SYSTEMD_FOUND}>:${SYSTEMD_LIBRARIES}>
${ICONV_LIBRARIES}
- $<$<BOOL:${GCRYPT_FOUND}>:${GCRYPT_LIBRARIES}>
+ OpenSSL::Crypto
$<$<BOOL:${XXHASH_FOUND}>:${XXHASH_LIBRARIES}>
)
set_target_properties(apt-pkg PROPERTIES VERSION ${MAJOR}.${MINOR})
diff --git a/apt-pkg/contrib/gpgv.cc b/apt-pkg/contrib/gpgv.cc
index 565fb7de5..7b0a0d240 100644
--- a/apt-pkg/contrib/gpgv.cc
+++ b/apt-pkg/contrib/gpgv.cc
@@ -255,12 +255,8 @@ std::pair<std::string, std::forward_list<std::string>> APT::Internal::FindGPGV(b
// 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))
diff --git a/apt-pkg/contrib/hashes.cc b/apt-pkg/contrib/hashes.cc
index 06bfd003e..c7a4ab0e7 100644
--- a/apt-pkg/contrib/hashes.cc
+++ b/apt-pkg/contrib/hashes.cc
@@ -26,24 +26,13 @@
#include <cstddef>
#include <cstdlib>
#include <iostream>
+#include <optional>
#include <string>
#include <unistd.h>
-#include <gcrypt.h>
+#include <openssl/evp.h>
/*}}}*/
-static const constexpr struct HashAlgo
-{
- const char *name;
- int gcryAlgo;
- Hashes::SupportedHashes ourAlgo;
-} Algorithms[] = {
- {"MD5Sum", GCRY_MD_MD5, Hashes::MD5SUM},
- {"SHA1", GCRY_MD_SHA1, Hashes::SHA1SUM},
- {"SHA256", GCRY_MD_SHA256, Hashes::SHA256SUM},
- {"SHA512", GCRY_MD_SHA512, Hashes::SHA512SUM},
-};
-
const char * HashString::_SupportedHashes[] =
{
"SHA512", "SHA256", "SHA1", "MD5Sum", "Checksum-FileSize", NULL
@@ -311,58 +300,114 @@ bool HashStringList::operator!=(HashStringList const &other) const
return !(*this == other);
}
/*}}}*/
+static APT_PURE std::string HexDigest(std::basic_string_view<unsigned char> const &Sum)
+{
+ char Conv[16] =
+ {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b',
+ 'c', 'd', 'e', 'f'};
+ std::string Result(Sum.size() * 2, 0);
+
+ // Convert each char into two letters
+ size_t J = 0;
+ size_t I = 0;
+ for (; I != (Sum.size()) * 2; J++, I += 2)
+ {
+ Result[I] = Conv[Sum[J] >> 4];
+ Result[I + 1] = Conv[Sum[J] & 0xF];
+ }
+ return Result;
+};
// PrivateHashes /*{{{*/
-class PrivateHashes {
-public:
- unsigned long long FileSize;
- gcry_md_hd_t hd;
+class PrivateHashes
+{
+ public:
+ unsigned long long FileSize{0};
- void maybeInit()
- {
+ private:
+ std::array<EVP_MD_CTX *, 4> contexts{};
- // Yikes, we got to initialize libgcrypt, or we get warnings. But we
- // abstract away libgcrypt in Hashes from our users - they are not
- // supposed to know what the hashing backend is, so we can't force
- // them to init themselves as libgcrypt folks want us to. So this
- // only leaves us with this option...
- if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
+ public:
+ struct HashAlgo
+ {
+ size_t index;
+ const char *name;
+ const EVP_MD *(*evpLink)(void);
+ Hashes::SupportedHashes ourAlgo;
+ };
+
+ static constexpr std::array<HashAlgo, 4> Algorithms{
+ HashAlgo{0, "MD5Sum", EVP_md5, Hashes::MD5SUM},
+ HashAlgo{1, "SHA1", EVP_sha1, Hashes::SHA1SUM},
+ HashAlgo{2, "SHA256", EVP_sha256, Hashes::SHA256SUM},
+ HashAlgo{3, "SHA512", EVP_sha512, Hashes::SHA512SUM},
+ };
+
+ bool Write(unsigned char const *Data, size_t Size)
+ {
+ for (auto &context : contexts)
{
- if (!gcry_check_version(nullptr))
- {
- fprintf(stderr, "libgcrypt is too old (need %s, have %s)\n",
- "nullptr", gcry_check_version(NULL));
- exit(2);
- }
-
- gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
+ if (context)
+ EVP_DigestUpdate(context, Data, Size);
}
+ return true;
+ }
+
+ std::string HexDigest(HashAlgo const &algo)
+ {
+ auto Size = EVP_MD_size(algo.evpLink());
+ unsigned char Sum[Size];
+
+ // We need to work on a copy, as we update the hash after creating a digest...
+ auto tmpContext = EVP_MD_CTX_create();
+ EVP_MD_CTX_copy(tmpContext, contexts[algo.index]);
+ EVP_DigestFinal_ex(tmpContext, Sum, nullptr);
+ EVP_MD_CTX_destroy(tmpContext);
+
+ return ::HexDigest(std::basic_string_view<unsigned char>(Sum, Size));
+ }
+
+ bool Enable(HashAlgo const &algo)
+ {
+ contexts[algo.index] = EVP_MD_CTX_new();
+ if (contexts[algo.index] == nullptr)
+ return false;
+ if (EVP_DigestInit_ex(contexts[algo.index], algo.evpLink(), NULL))
+ return true;
+ EVP_MD_CTX_destroy(contexts[algo.index]);
+ contexts[algo.index] = nullptr;
+ return false;
+ }
+ bool IsEnabled(HashAlgo const &algo)
+ {
+ return contexts[algo.index] != nullptr;
+ }
+
+ explicit PrivateHashes() {}
+ ~PrivateHashes()
+ {
+ for (auto ctx : contexts)
+ if (ctx != nullptr)
+ EVP_MD_CTX_free(ctx);
}
- explicit PrivateHashes(unsigned int const CalcHashes) : FileSize(0)
+ explicit PrivateHashes(unsigned int const CalcHashes) : PrivateHashes()
{
- maybeInit();
- gcry_md_open(&hd, 0, 0);
for (auto & Algo : Algorithms)
{
if ((CalcHashes & Algo.ourAlgo) == Algo.ourAlgo)
- gcry_md_enable(hd, Algo.gcryAlgo);
+ Enable(Algo);
}
}
- explicit PrivateHashes(HashStringList const &Hashes) : FileSize(0) {
- maybeInit();
- gcry_md_open(&hd, 0, 0);
+ explicit PrivateHashes(HashStringList const &Hashes) : PrivateHashes()
+ {
for (auto & Algo : Algorithms)
{
if (not Hashes.usable() || Hashes.find(Algo.name) != NULL)
- gcry_md_enable(hd, Algo.gcryAlgo);
+ Enable(Algo);
}
}
- ~PrivateHashes()
- {
- gcry_md_close(hd);
- }
};
/*}}}*/
// Hashes::Add* - Add the contents of data or FD /*{{{*/
@@ -370,7 +415,8 @@ bool Hashes::Add(const unsigned char * const Data, unsigned long long const Size
{
if (Size != 0)
{
- gcry_md_write(d->hd, Data, Size);
+ if (not d->Write(Data, Size))
+ return false;
d->FileSize += Size;
}
return true;
@@ -420,36 +466,12 @@ bool Hashes::AddFD(FileFd &Fd,unsigned long long Size)
}
/*}}}*/
-static APT_PURE std::string HexDigest(gcry_md_hd_t hd, int algo)
-{
- char Conv[16] =
- {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b',
- 'c', 'd', 'e', 'f'};
-
- auto Size = gcry_md_get_algo_dlen(algo);
- assert(Size <= 512/8);
- char Result[((Size)*2) + 1];
- Result[(Size)*2] = 0;
-
- auto Sum = gcry_md_read(hd, algo);
-
- // Convert each char into two letters
- size_t J = 0;
- size_t I = 0;
- for (; I != (Size)*2; J++, I += 2)
- {
- Result[I] = Conv[Sum[J] >> 4];
- Result[I + 1] = Conv[Sum[J] & 0xF];
- }
- return std::string(Result);
-};
-
HashStringList Hashes::GetHashStringList()
{
HashStringList hashes;
- for (auto & Algo : Algorithms)
- if (gcry_md_is_enabled(d->hd, Algo.gcryAlgo))
- hashes.push_back(HashString(Algo.name, HexDigest(d->hd, Algo.gcryAlgo)));
+ for (auto &Algo : d->Algorithms)
+ if (d->IsEnabled(Algo))
+ hashes.push_back(HashString(Algo.name, d->HexDigest(Algo)));
hashes.FileSize(d->FileSize);
return hashes;
@@ -457,9 +479,9 @@ HashStringList Hashes::GetHashStringList()
HashString Hashes::GetHashString(SupportedHashes hash)
{
- for (auto & Algo : Algorithms)
+ for (auto &Algo : d->Algorithms)
if (hash == Algo.ourAlgo)
- return HashString(Algo.name, HexDigest(d->hd, Algo.gcryAlgo));
+ return HashString(Algo.name, d->HexDigest(Algo));
abort();
}
diff --git a/cmdline/apt-helper.cc b/cmdline/apt-helper.cc
index f48cb293f..4404259d8 100644
--- a/cmdline/apt-helper.cc
+++ b/cmdline/apt-helper.cc
@@ -201,7 +201,37 @@ static bool DoCatFile(CommandLine &CmdL) /*{{{*/
return true;
}
/*}}}*/
+static bool DoHashFile(CommandLine &CmdL) /*{{{*/
+{
+ FileFd fd;
+
+ if (fd.OpenDescriptor(STDIN_FILENO, FileFd::ReadOnly) == false)
+ return false;
+ for (size_t i = 1; CmdL.FileList[i] != NULL; ++i)
+ {
+ std::string const name = CmdL.FileList[i];
+ Hashes hashes;
+
+ if (name != "-")
+ {
+ if (fd.Open(name, FileFd::ReadOnly, FileFd::Extension) == false)
+ return false;
+ }
+ else
+ {
+ if (fd.OpenDescriptor(STDIN_FILENO, FileFd::ReadOnly) == false)
+ return false;
+ }
+ if (hashes.AddFD(fd) == false)
+ return false;
+ for (auto hs : hashes.GetHashStringList())
+ std::cout << hs.toStr() << std::endl;
+ std::cout << std::endl;
+ }
+ return true;
+}
+ /*}}}*/
static pid_t ExecuteProcess(const char *Args[]) /*{{{*/
{
pid_t pid = ExecFork();
@@ -310,16 +340,17 @@ static bool ShowHelp(CommandLine &) /*{{{*/
static std::vector<aptDispatchWithHelp> GetCommands() /*{{{*/
{
return {
- {"download-file", &DoDownloadFile, _("download the given uri to the target-path")},
- {"srv-lookup", &DoSrvLookup, _("lookup a SRV record (e.g. _http._tcp.ftp.debian.org)")},
- {"cat-file", &DoCatFile, _("concatenate files, with automatic decompression")},
- {"auto-detect-proxy", &DoAutoDetectProxy, _("detect proxy using apt.conf")},
- {"wait-online", &DoWaitOnline, _("wait for system to be online")},
- {"drop-privs", &DropPrivsAndRun, _("drop privileges before running given command")},
- {"analyze-pattern", &AnalyzePattern, _("analyse a pattern")},
- {"analyse-pattern", &AnalyzePattern, nullptr},
- {"quote-string", &DoQuoteString, nullptr},
- {nullptr, nullptr, nullptr}};
+ {"download-file", &DoDownloadFile, _("download the given uri to the target-path")},
+ {"srv-lookup", &DoSrvLookup, _("lookup a SRV record (e.g. _http._tcp.ftp.debian.org)")},
+ {"cat-file", &DoCatFile, _("concatenate files, with automatic decompression")},
+ {"hash-file", &DoHashFile, _("hash file")},
+ {"auto-detect-proxy", &DoAutoDetectProxy, _("detect proxy using apt.conf")},
+ {"wait-online", &DoWaitOnline, _("wait for system to be online")},
+ {"drop-privs", &DropPrivsAndRun, _("drop privileges before running given command")},
+ {"analyze-pattern", &AnalyzePattern, _("analyse a pattern")},
+ {"analyse-pattern", &AnalyzePattern, nullptr},
+ {"quote-string", &DoQuoteString, nullptr},
+ {nullptr, nullptr, nullptr}};
}
/*}}}*/
int main(int argc,const char *argv[]) /*{{{*/
diff --git a/debian/control b/debian/control
index 588539a02..50297362e 100644
--- a/debian/control
+++ b/debian/control
@@ -17,8 +17,7 @@ Build-Depends: dpkg-dev (>= 1.22.5) <!pkg.apt.ci>,
googletest <!nocheck> | libgtest-dev <!nocheck>,
libbz2-dev,
libdb-dev,
- libgnutls28-dev (>= 3.4.6),
- libgcrypt20-dev,
+ libssl-dev,
liblz4-dev (>= 0.0~r126),
liblzma-dev,
libseccomp-dev (>= 2.4.2) [amd64 arm64 armel armhf i386 mips mips64el mipsel ppc64el s390x hppa powerpc powerpcspe ppc64 x32],
diff --git a/debian/tests/control b/debian/tests/control
index 3aef98710..428bed456 100644
--- a/debian/tests/control
+++ b/debian/tests/control
@@ -5,8 +5,7 @@ Depends: libapt-pkg-dev, pkg-config, g++
Tests: run-tests
Restrictions: allow-stderr
Depends: @, @builddeps@, dpkg (>= 1.20.8), expect, fakeroot, wget, stunnel4, lsof, db-util,
- gnupg (>= 2) | gnupg2, gnupg1 | gnupg (<< 2),
- gpgv (>= 2) | gpgv2, gpgv1 | gpgv (<< 2),
+ gpgv,
gpgv-sq,
sq (>= 0.40),
moreutils,
diff --git a/methods/CMakeLists.txt b/methods/CMakeLists.txt
index 548d867dc..beb08a81d 100644
--- a/methods/CMakeLists.txt
+++ b/methods/CMakeLists.txt
@@ -13,14 +13,12 @@ add_executable(http http.cc basehttp.cc $<TARGET_OBJECTS:connectlib>)
add_executable(mirror mirror.cc)
add_executable(rred rred.cc)
-if (HAVE_GNUTLS)
- target_compile_definitions(connectlib PRIVATE ${GNUTLS_DEFINITIONS})
- target_include_directories(connectlib PRIVATE ${GNUTLS_INCLUDE_DIR})
-endif()
+target_compile_definitions(connectlib PRIVATE ${OPENSSL_DEFINITIONS})
+target_include_directories(connectlib PRIVATE ${OPENSSL_INCLUDE_DIR})
target_include_directories(http PRIVATE $<$<BOOL:${SYSTEMD_FOUND}>:${SYSTEMD_INCLUDE_DIRS}>)
# Additional libraries to link against for networked stuff
-target_link_libraries(http $<$<BOOL:${GNUTLS_FOUND}>:${GNUTLS_LIBRARIES}> $<$<BOOL:${SYSTEMD_FOUND}>:${SYSTEMD_LIBRARIES}>)
+target_link_libraries(http OpenSSL::SSL $<$<BOOL:${SYSTEMD_FOUND}>:${SYSTEMD_LIBRARIES}>)
target_link_libraries(rred apt-private)
diff --git a/methods/connect.cc b/methods/connect.cc
index 12847c594..b00e73eb7 100644
--- a/methods/connect.cc
+++ b/methods/connect.cc
@@ -1,12 +1,14 @@
// -*- mode: cpp; mode: fold -*-
+// SPDX-License-Identifier: GPL-2.0+ and curl
// Description /*{{{*/
/* ######################################################################
Connect - Replacement connect call
This was originally authored by Jason Gunthorpe <jgg@debian.org>
- and is placed in the Public Domain, do with it what you will.
-
+ and is placed in the Public Domain, do with it what you will. It
+ is now GPL-2.0+. See COPYING for details.
+
##################################################################### */
/*}}}*/
// Include Files /*{{{*/
@@ -19,11 +21,10 @@
#include <apt-pkg/srvrec.h>
#include <apt-pkg/strutl.h>
-#ifdef HAVE_GNUTLS
-#include <gnutls/gnutls.h>
-#include <gnutls/x509.h>
-#endif
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+#include <cassert>
#include <cerrno>
#include <cstdio>
#include <cstring>
@@ -801,229 +802,242 @@ ResultState UnwrapSocks(std::string Host, int Port, URI Proxy, std::unique_ptr<M
return ResultState::SUCCESSFUL;
}
-#ifdef HAVE_GNUTLS /*}}}*/
// UnwrapTLS - Handle TLS connections /*{{{*/
// ---------------------------------------------------------------------
/* Performs a TLS handshake on the socket */
+#define null_error(...) (_error->Error(__VA_ARGS__), nullptr)
+#define ssl_strerr() ERR_error_string(ERR_get_error(), nullptr)
struct TlsFd final : public MethodFd
{
- std::unique_ptr<MethodFd> UnderlyingFd;
- gnutls_session_t session;
- gnutls_certificate_credentials_t credentials;
- std::string hostname;
- unsigned long Timeout;
+ std::unique_ptr<MethodFd> UnderlyingFd{};
+
+ SSL *ssl{};
- int Fd() APT_OVERRIDE { return UnderlyingFd->Fd(); }
+ std::string hostname{};
+ unsigned long Timeout{};
+ bool broken{false};
- ssize_t Read(void *buf, size_t count) APT_OVERRIDE
+ int Fd() override { return UnderlyingFd ? UnderlyingFd->Fd() : -1; }
+
+ ssize_t Read(void *buf, size_t count) override
{
- return HandleError(gnutls_record_recv(session, buf, count));
+ assert(ssl);
+ return HandleError(SSL_read(ssl, buf, count));
}
- ssize_t Write(void *buf, size_t count) APT_OVERRIDE
+ ssize_t Write(void *buf, size_t count) override
{
- return HandleError(gnutls_record_send(session, buf, count));
+ assert(ssl);
+ return HandleError(SSL_write(ssl, buf, count));
}
- ssize_t DoTLSHandshake()
+ ssize_t HandleError(ssize_t r)
{
- int err;
- // Do the handshake. Our socket is non-blocking, so we need to call WaitFd()
- // accordingly.
- do
- {
- err = gnutls_handshake(session);
- if ((err == GNUTLS_E_INTERRUPTED || err == GNUTLS_E_AGAIN) &&
- WaitFd(this->Fd(), gnutls_record_get_direction(session) == 1, Timeout) == false)
- {
- _error->Errno("select", "Could not wait for server fd");
- return err;
- }
- } while (err < 0 && gnutls_error_is_fatal(err) == 0);
-
- if (err < 0)
+ assert(ssl);
+ if (r > 0)
+ return r;
+
+ auto err = SSL_get_error(ssl, r);
+ switch (err)
{
- // Print reason why validation failed.
- if (err == GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR)
- {
- gnutls_datum_t txt;
- auto type = gnutls_certificate_type_get(session);
- auto status = gnutls_session_get_verify_cert_status(session);
- if (gnutls_certificate_verification_status_print(status, type, &txt, 0) == 0)
- {
- _error->Error("Certificate verification failed: %s", txt.data);
- }
- gnutls_free(txt.data);
- }
- _error->Error("Could not handshake: %s", gnutls_strerror(err));
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ errno = EAGAIN;
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ // Remote host has closed connection
+ errno = 0;
+ break;
+ case SSL_ERROR_SYSCALL:
+ broken = true;
+ break;
+ case SSL_ERROR_SSL:
+ broken = true;
+ errno = EIO;
+ _error->Error("OpenSSL error: %s\n", ssl_strerr());
+ break;
}
- return err;
+ return r;
}
- template <typename T>
- T HandleError(T err)
+ int Close() override
{
- // Server may request re-handshake if client certificates need to be provided
- // based on resource requested
- if (err == GNUTLS_E_REHANDSHAKE)
+ int res = 0; // 0 or 1 are success
+ int lower = 0; // 0 is success
+
+ if (ssl)
{
- int rc = DoTLSHandshake();
- // Only reset err if DoTLSHandshake() fails.
- // Otherwise, we want to follow the original error path and set errno to EAGAIN
- // so that the request is retried.
- if (rc < 0)
- err = rc;
+ if (not broken && res)
+ res = SSL_shutdown(ssl);
+ if (res < 0)
+ HandleError(res);
+ SSL_free(ssl);
}
+ ssl = nullptr;
- if (err < 0 && gnutls_error_is_fatal(err))
- errno = EIO;
- else if (err < 0)
- errno = EAGAIN;
- else
- errno = 0;
- return err;
- }
+ if (UnderlyingFd)
+ lower = UnderlyingFd->Close();
+ UnderlyingFd = nullptr;
- int Close() APT_OVERRIDE
- {
- auto err = HandleError(gnutls_bye(session, GNUTLS_SHUT_RDWR));
- auto lower = UnderlyingFd->Close();
- return err < 0 ? HandleError(err) : lower;
+ // Return -1 on failure, 0 on success
+ return res < 0 || lower < 0 ? -1 : 0;
}
- bool HasPending() APT_OVERRIDE
+ bool HasPending() override
{
- return gnutls_record_check_pending(session) > 0;
+ assert(ssl);
+ // SSL_has_pending() can return 1 even if there are no actual bytes to read
+ // post decoding, so we need to SSL_peek() too to see if there are actual
+ // bytes as otherwise we end up busy looping (tested by DE -> AU https connection)
+ char buf;
+ return SSL_has_pending(ssl) && SSL_peek(ssl, &buf, 1) > 0;
}
};
-ResultState UnwrapTLS(std::string const &Host, std::unique_ptr<MethodFd> &Fd,
- unsigned long const Timeout, aptMethod * const Owner,
- aptConfigWrapperForMethods const * const OwnerConf)
+static BIO_METHOD *NewBioMethod()
{
- if (_config->FindB("Acquire::AllowTLS", true) == false)
+ BIO_METHOD *m = BIO_meth_new(BIO_TYPE_MEM, "OpenSSL APT BIO method");
+ if (not m)
{
- _error->Error("TLS support has been disabled: Acquire::AllowTLS is false.");
- return ResultState::FATAL_ERROR;
+ _error->Error("SSL connection failed: %s - %s", ssl_strerr(), strerror(errno));
+ return nullptr;
}
- int err;
- TlsFd *tlsFd = new TlsFd();
+ BIO_meth_set_ctrl(m, [](BIO *, int, long, void *) -> long
+ { return 1; });
+ BIO_meth_set_write(m, [](BIO *bio, const char *buf, int size) -> int
+ {
+ auto p = BIO_get_data(bio);
+ auto res = reinterpret_cast<MethodFd *>(p)->Write((void*)buf, size);
+ if (errno == EAGAIN)
+ BIO_set_retry_write(bio);
+ return res; });
+ BIO_meth_set_read(m, [](BIO *bio, char *buf, int size) -> int
+ {
+ auto p = BIO_get_data(bio);
+ auto res = reinterpret_cast<MethodFd *>(p)->Read(buf, size);
+ if (errno == EAGAIN)
+ BIO_set_retry_read(bio);
+ return res; });
+
+ return m;
+}
- tlsFd->hostname = Host;
- tlsFd->UnderlyingFd = MethodFd::FromFd(-1); // For now
- tlsFd->Timeout = Timeout;
+static SSL_CTX *GetContextForHost(std::string const &host, aptConfigWrapperForMethods const *const OwnerConf)
+{
+ static std::string lastHost;
+ static SSL_CTX *ctx;
+ auto Debug = OwnerConf->DebugEnabled();
- if ((err = gnutls_init(&tlsFd->session, GNUTLS_CLIENT | GNUTLS_NONBLOCK)) < 0)
+ if (lastHost == host)
{
- _error->Error("Internal error: could not allocate credentials: %s", gnutls_strerror(err));
- return ResultState::FATAL_ERROR;
+ if (Debug)
+ std::clog << "Reusing context for " << host << std::endl;
+ return ctx;
}
+ if (Debug)
+ std::clog << "Creating context for " << host << std::endl;
- FdFd *fdfd = dynamic_cast<FdFd *>(Fd.get());
- if (fdfd != nullptr)
- {
- gnutls_transport_set_int(tlsFd->session, fdfd->fd);
- }
- else
- {
- gnutls_transport_set_ptr(tlsFd->session, Fd.get());
- gnutls_transport_set_pull_function(tlsFd->session,
- [](gnutls_transport_ptr_t p, void *buf, size_t size) -> ssize_t {
- return reinterpret_cast<MethodFd *>(p)->Read(buf, size);
- });
- gnutls_transport_set_push_function(tlsFd->session,
- [](gnutls_transport_ptr_t p, const void *buf, size_t size) -> ssize_t {
- return reinterpret_cast<MethodFd *>(p)->Write((void *)buf, size);
- });
- }
+ // Check unsupported options first, maybe we reuse the host context later, who knows
+ if (not OwnerConf->ConfigFind("IssuerCert", "").empty())
+ return null_error("The option '%s' is not supported anymore", "IssuerCert");
+ if (not OwnerConf->ConfigFind("SslForceVersion", "").empty())
+ return null_error("The option '%s' is not supported anymore", "SslForceVersion");
- if ((err = gnutls_certificate_allocate_credentials(&tlsFd->credentials)) < 0)
- {
- _error->Error("Internal error: could not allocate credentials: %s", gnutls_strerror(err));
- return ResultState::FATAL_ERROR;
- }
+ // Delete the existing context and render it unusable
+ lastHost = "";
+ SSL_CTX_free(ctx);
+
+ // We set the context here, but lastHost at the end to only allow reuse of fully initialized contexts
+ ctx = SSL_CTX_new(TLS_client_method());
+ if (ctx == nullptr)
+ return null_error("Could not create new SSL context: %s", ssl_strerr());
- // Credential setup
- std::string fileinfo = OwnerConf->ConfigFind("CaInfo", "");
- if (fileinfo.empty())
+ // Load the certificate authorities, either custom or default ones
+ if (auto const fileinfo = OwnerConf->ConfigFind("CaInfo", ""); not fileinfo.empty())
{
- // No CaInfo specified, use system trust store.
- err = gnutls_certificate_set_x509_system_trust(tlsFd->credentials);
- if (err == 0)
- Owner->Warning("No system certificates available. Try installing ca-certificates.");
- else if (err < 0)
- {
- _error->Error("Could not load system TLS certificates: %s", gnutls_strerror(err));
- return ResultState::FATAL_ERROR;
- }
+ if (auto res = SSL_CTX_load_verify_file(ctx, fileinfo.c_str()); res != 1)
+ return null_error("Could not load certificates from %s (CaInfo option): %s", fileinfo.c_str(), ssl_strerr());
}
- else
+ else if (auto err = SSL_CTX_set_default_verify_paths(ctx); err != 1)
+ return null_error("Could not load certificates: %s", ssl_strerr());
+
+ // Client certificate setup, such that clients can authenticate to the server
+ if (auto const cert = OwnerConf->ConfigFind("SslCert", ""); not cert.empty())
+ if (auto res = SSL_CTX_use_certificate_file(ctx, cert.c_str(), SSL_FILETYPE_PEM); res != 1)
+ return null_error("Could not load client certificate (%s, SslCert option): %s", cert.c_str(), ssl_strerr());
+ if (auto const key = OwnerConf->ConfigFind("SslKey", ""); not key.empty())
+ if (auto res = SSL_CTX_use_PrivateKey_file(ctx, key.c_str(), SSL_FILETYPE_PEM); res != 1)
+ return null_error("Could not load client or key (%s, SslKey option): %s", key.c_str(), ssl_strerr()), nullptr;
+
+ // Custom certificate revocation lists. We surely support all the niche cases.
+ if (auto const crlfile = OwnerConf->ConfigFind("CrlFile", ""); not crlfile.empty())
{
- // CA location has been set, use the specified one instead
- gnutls_certificate_set_verify_flags(tlsFd->credentials, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
- err = gnutls_certificate_set_x509_trust_file(tlsFd->credentials, fileinfo.c_str(), GNUTLS_X509_FMT_PEM);
- if (err < 0)
- {
- _error->Error("Could not load certificates from %s (CaInfo option): %s", fileinfo.c_str(), gnutls_strerror(err));
- return ResultState::FATAL_ERROR;
- }
+ // tell OpenSSL where to find CRL file that is used to check certificate revocation.
+ // lifted from curl:
+ // Copyright (c) 1996 - 2023, Daniel Stenberg, <daniel@haxx.se>, and many
+ // contributors, see the THANKS file.
+ auto lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(ctx),
+ X509_LOOKUP_file());
+ if (not lookup || not X509_load_crl_file(lookup, crlfile.c_str(), X509_FILETYPE_PEM))
+ return null_error("Could not load custom certificate revocation list %s (CrlFile option): %s", crlfile.c_str(), ssl_strerr());
+ /* Everything is fine. */
+ X509_STORE_set_flags(SSL_CTX_get_cert_store(ctx),
+ X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
}
- if (not OwnerConf->ConfigFind("IssuerCert", "").empty())
+ lastHost = host;
+ return ctx;
+}
+
+ResultState UnwrapTLS(std::string const &Host, std::unique_ptr<MethodFd> &Fd,
+ unsigned long const Timeout, aptMethod *const Owner,
+ aptConfigWrapperForMethods const *const OwnerConf)
+{
+ if (_config->FindB("Acquire::AllowTLS", true) == false)
{
- _error->Error("The option '%s' is not supported anymore", "IssuerCert");
+ _error->Error("TLS support has been disabled: Acquire::AllowTLS is false.");
return ResultState::FATAL_ERROR;
}
- if (not OwnerConf->ConfigFind("SslForceVersion", "").empty())
- {
- _error->Error("The option '%s' is not supported anymore", "SslForceVersion");
+
+ TlsFd *tlsFd = new TlsFd();
+
+ tlsFd->hostname = Host;
+ tlsFd->Timeout = Timeout;
+
+ if (auto ctx = GetContextForHost(Host, OwnerConf))
+ tlsFd->ssl = SSL_new(ctx);
+ else
return ResultState::FATAL_ERROR;
- }
- // For client authentication, certificate file ...
- std::string const cert = OwnerConf->ConfigFind("SslCert", "");
- std::string const key = OwnerConf->ConfigFind("SslKey", "");
- if (cert.empty() == false)
+ FdFd *fdfd = dynamic_cast<FdFd *>(Fd.get());
+ if (fdfd != nullptr)
{
- if ((err = gnutls_certificate_set_x509_key_file(
- tlsFd->credentials,
- cert.c_str(),
- key.empty() ? cert.c_str() : key.c_str(),
- GNUTLS_X509_FMT_PEM)) < 0)
- {
- _error->Error("Could not load client certificate (%s, SslCert option) or key (%s, SslKey option): %s", cert.c_str(), key.c_str(), gnutls_strerror(err));
- return ResultState::FATAL_ERROR;
- }
+ SSL_set_fd(tlsFd->ssl, fdfd->fd);
}
-
- // CRL file
- std::string const crlfile = OwnerConf->ConfigFind("CrlFile", "");
- if (crlfile.empty() == false)
+ else
{
- if ((err = gnutls_certificate_set_x509_crl_file(tlsFd->credentials,
- crlfile.c_str(),
- GNUTLS_X509_FMT_PEM)) < 0)
- {
- _error->Error("Could not load custom certificate revocation list %s (CrlFile option): %s", crlfile.c_str(), gnutls_strerror(err));
+ static auto m = NewBioMethod();
+ if (m == nullptr)
return ResultState::FATAL_ERROR;
- }
- }
- if ((err = gnutls_credentials_set(tlsFd->session, GNUTLS_CRD_CERTIFICATE, tlsFd->credentials)) < 0)
- {
- _error->Error("Internal error: Could not add certificates to session: %s", gnutls_strerror(err));
- return ResultState::FATAL_ERROR;
- }
+ auto bio = BIO_new(m);
+ if (bio == nullptr)
+ return ResultState::FATAL_ERROR;
- if ((err = gnutls_set_default_priority(tlsFd->session)) < 0)
- {
- _error->Error("Internal error: Could not set algorithm preferences: %s", gnutls_strerror(err));
- return ResultState::FATAL_ERROR;
+ BIO_set_data(bio, Fd.get());
+ BIO_up_ref(bio); // the following take one reference each
+ SSL_set0_rbio(tlsFd->ssl, bio);
+ SSL_set0_wbio(tlsFd->ssl, bio);
}
if (OwnerConf->ConfigFindB("Verify-Peer", true))
{
- gnutls_session_set_verify_cert(tlsFd->session, OwnerConf->ConfigFindB("Verify-Host", true) ? tlsFd->hostname.c_str() : nullptr, 0);
+ SSL_set_verify(tlsFd->ssl, SSL_VERIFY_PEER, nullptr);
+ if (auto res = SSL_set1_host(tlsFd->ssl, OwnerConf->ConfigFindB("Verify-Host", true) ? tlsFd->hostname.c_str() : nullptr); res != 1)
+ {
+ _error->Error("Could not set hostname: %s", ssl_strerr());
+ return ResultState::FATAL_ERROR;
+ }
}
// set SNI only if the hostname is really a name and not an address
@@ -1034,23 +1048,37 @@ ResultState UnwrapTLS(std::string const &Host, std::unique_ptr<MethodFd> &Fd,
if (inet_pton(AF_INET, tlsFd->hostname.c_str(), &addr4) == 1 ||
inet_pton(AF_INET6, tlsFd->hostname.c_str(), &addr6) == 1)
/* not a host name */;
- else if ((err = gnutls_server_name_set(tlsFd->session, GNUTLS_NAME_DNS, tlsFd->hostname.c_str(), tlsFd->hostname.length())) < 0)
+ else if (auto res = SSL_set_tlsext_host_name(tlsFd->ssl, tlsFd->hostname.c_str()); res != 1)
{
- _error->Error("Could not set host name %s to indicate to server: %s", tlsFd->hostname.c_str(), gnutls_strerror(err));
+ _error->Error("Could not set host name %s to indicate to server: %s", tlsFd->hostname.c_str(), ssl_strerr());
return ResultState::FATAL_ERROR;
}
}
+ while (true)
+ {
+ auto res = SSL_connect(tlsFd->ssl);
+ if (res == 1)
+ break;
+ switch (auto error = SSL_get_error(tlsFd->ssl, res))
+ {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ if (not WaitFd(Fd->Fd(), error == SSL_ERROR_WANT_WRITE, Timeout))
+ {
+ _error->Errno("select", "Could not wait for server fd");
+ return ResultState::TRANSIENT_ERROR;
+ }
+ break;
+ default:
+ _error->Error("SSL connection failed: %s / %s", ssl_strerr(), strerror(errno));
+ return ResultState::TRANSIENT_ERROR;
+ }
+ }
+
// Set the FD now, so closing it works reliably.
tlsFd->UnderlyingFd = std::move(Fd);
Fd.reset(tlsFd);
- // Do the handshake.
- err = tlsFd->DoTLSHandshake();
-
- if (err < 0)
- return ResultState::TRANSIENT_ERROR;
-
return ResultState::SUCCESSFUL;
}
-#endif /*}}}*/
diff --git a/methods/http.cc b/methods/http.cc
index 1e76a1f57..678ce3952 100644
--- a/methods/http.cc
+++ b/methods/http.cc
@@ -428,9 +428,7 @@ ResultState HttpServerState::Open()
Out.Reset();
Persistent = true;
-#ifdef HAVE_GNUTLS
bool tls = (ServerName.Access == "https" || APT::String::Endswith(ServerName.Access, "+https"));
-#endif
// Determine the proxy setting
// Used to run AutoDetectProxy(ServerName) here, but we now send a Proxy
@@ -455,7 +453,6 @@ ResultState HttpServerState::Open()
{
char *result = getenv("http_proxy");
Proxy = result ? result : "";
-#ifdef HAVE_GNUTLS
if (tls == true)
{
char *result = getenv("https_proxy");
@@ -464,7 +461,6 @@ ResultState HttpServerState::Open()
Proxy = result;
}
}
-#endif
}
}
@@ -478,13 +474,8 @@ ResultState HttpServerState::Open()
if (Proxy.empty() == false)
Owner->AddProxyAuth(Proxy, ServerName);
-#ifdef HAVE_GNUTLS
auto const DefaultService = tls ? "https" : "http";
auto const DefaultPort = tls ? 443 : 80;
-#else
- auto const DefaultService = "http";
- auto const DefaultPort = 80;
-#endif
if (Proxy.Access == "socks5h")
{
auto result = Connect(Proxy.Host, Proxy.Port, "socks", 1080, ServerFd, TimeOut, Owner);
@@ -518,15 +509,12 @@ ResultState HttpServerState::Open()
Port = Proxy.Port;
Host = Proxy.Host;
-#ifdef HAVE_GNUTLS
if (Proxy.Access == "https" && Port == 0)
Port = 443;
-#endif
}
auto result = Connect(Host, Port, DefaultService, DefaultPort, ServerFd, TimeOut, Owner);
if (result != ResultState::SUCCESSFUL)
return result;
-#ifdef HAVE_GNUTLS
if (Host == Proxy.Host && Proxy.Access == "https")
{
aptConfigWrapperForMethods ProxyConf{std::vector<std::string>{"http", "https"}};
@@ -541,13 +529,10 @@ ResultState HttpServerState::Open()
if (result != ResultState::SUCCESSFUL)
return result;
}
-#endif
}
-#ifdef HAVE_GNUTLS
if (tls)
return UnwrapTLS(ServerName.Host, ServerFd, TimeOut, Owner, Owner);
-#endif
return ResultState::SUCCESSFUL;
}