From becf3fed0f5f3da225eb1df2cd306add1fdf5ab8 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Fri, 20 Dec 2024 08:59:48 +0100 Subject: Remove gnupg and versioned gpgv test depends --- apt-pkg/contrib/gpgv.cc | 4 ---- 1 file changed, 4 deletions(-) (limited to 'apt-pkg') 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> 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)) -- cgit v1.2.3-70-g09d2 From ef435a3ed9c3e835c799df651f160b658eeee56a Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Wed, 18 Dec 2024 18:44:05 +0100 Subject: hashes: Refactor backend into PrivateHashes Everything is abstracted away now. --- apt-pkg/contrib/hashes.cc | 137 +++++++++++++++++++++++++++------------------- 1 file changed, 82 insertions(+), 55 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/contrib/hashes.cc b/apt-pkg/contrib/hashes.cc index 06bfd003e..a4d5026b4 100644 --- a/apt-pkg/contrib/hashes.cc +++ b/apt-pkg/contrib/hashes.cc @@ -32,18 +32,6 @@ #include /*}}}*/ -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,11 +299,31 @@ bool HashStringList::operator!=(HashStringList const &other) const return !(*this == other); } /*}}}*/ +static APT_PURE std::string HexDigest(std::basic_string_view 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; +class PrivateHashes +{ + public: + unsigned long long FileSize{0}; + + private: gcry_md_hd_t hd; void maybeInit() @@ -339,30 +347,72 @@ public: } } - explicit PrivateHashes(unsigned int const CalcHashes) : FileSize(0) + public: + struct HashAlgo + { + const char *name; + int gcryAlgo; + Hashes::SupportedHashes ourAlgo; + }; + + static constexpr std::array Algorithms{ + HashAlgo{"MD5Sum", GCRY_MD_MD5, Hashes::MD5SUM}, + HashAlgo{"SHA1", GCRY_MD_SHA1, Hashes::SHA1SUM}, + HashAlgo{"SHA256", GCRY_MD_SHA256, Hashes::SHA256SUM}, + HashAlgo{"SHA512", GCRY_MD_SHA512, Hashes::SHA512SUM}, + }; + + bool Write(unsigned char const *Data, size_t Size) + { + gcry_md_write(hd, Data, Size); + return true; + } + + std::string HexDigest(HashAlgo const &algo) + { + auto Size = gcry_md_get_algo_dlen(algo.gcryAlgo); + auto Sum = gcry_md_read(hd, algo.gcryAlgo); + return ::HexDigest(std::basic_string_view(Sum, Size)); + } + + bool Enable(HashAlgo const &Algo) + { + gcry_md_enable(hd, Algo.gcryAlgo); + return true; + } + bool IsEnabled(HashAlgo const &algo) + { + return gcry_md_is_enabled(hd, algo.gcryAlgo); + } + + explicit PrivateHashes() { maybeInit(); gcry_md_open(&hd, 0, 0); + } + + ~PrivateHashes() + { + gcry_md_close(hd); + } + + explicit PrivateHashes(unsigned int const CalcHashes) : PrivateHashes() + { 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 +420,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 +471,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 +484,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(); } -- cgit v1.2.3-70-g09d2 From 470f5cf449ac20c7d7bf50ad805c72a5f52d256f Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Wed, 18 Dec 2024 19:11:26 +0100 Subject: hashes: Add GnuTLS backend, make it default The GnuTLS backend avoids the need to link in libgcrypt, and gets us another certified library people will be happy with. This should be an intermediate step on the road to OpenSSL. --- CMakeLists.txt | 4 ++- apt-pkg/CMakeLists.txt | 2 ++ apt-pkg/contrib/hashes.cc | 66 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) (limited to 'apt-pkg') diff --git a/CMakeLists.txt b/CMakeLists.txt index c6977675e..4d2a5414d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -141,7 +141,9 @@ if (SECCOMP_FOUND) set(HAVE_SECCOMP 1) endif() -find_package(GCRYPT REQUIRED) +if (NOT HAVE_GNUTLS) + find_package(GCRYPT REQUIRED) +endif() find_package(XXHASH REQUIRED) # Mount()ing and stat()ing and friends diff --git a/apt-pkg/CMakeLists.txt b/apt-pkg/CMakeLists.txt index 63052faad..c68b7bddc 100644 --- a/apt-pkg/CMakeLists.txt +++ b/apt-pkg/CMakeLists.txt @@ -56,6 +56,7 @@ target_include_directories(apt-pkg $<$:${UDEV_INCLUDE_DIRS}> $<$:${SYSTEMD_INCLUDE_DIRS}> ${ICONV_INCLUDE_DIRS} + $<$:${GNUTLS_INCLUDE_DIRS}> $<$:${GCRYPT_INCLUDE_DIRS}> $<$:${XXHASH_INCLUDE_DIRS}> ) @@ -71,6 +72,7 @@ target_link_libraries(apt-pkg $<$:${UDEV_LIBRARIES}> $<$:${SYSTEMD_LIBRARIES}> ${ICONV_LIBRARIES} + $<$:${GNUTLS_LIBRARIES}> $<$:${GCRYPT_LIBRARIES}> $<$:${XXHASH_LIBRARIES}> ) diff --git a/apt-pkg/contrib/hashes.cc b/apt-pkg/contrib/hashes.cc index a4d5026b4..033cd0845 100644 --- a/apt-pkg/contrib/hashes.cc +++ b/apt-pkg/contrib/hashes.cc @@ -26,10 +26,15 @@ #include #include #include +#include #include #include +#ifdef HAVE_GNUTLS +#include +#else #include +#endif /*}}}*/ const char * HashString::_SupportedHashes[] = @@ -324,6 +329,66 @@ class PrivateHashes unsigned long long FileSize{0}; private: +#ifdef HAVE_GNUTLS + std::array, 4> digs{}; + + public: + struct HashAlgo + { + size_t index; + const char *name; + gnutls_digest_algorithm_t gnuTlsAlgo; + Hashes::SupportedHashes ourAlgo; + }; + + static constexpr std::array Algorithms{ + HashAlgo{0, "MD5Sum", GNUTLS_DIG_MD5, Hashes::MD5SUM}, + HashAlgo{1, "SHA1", GNUTLS_DIG_SHA1, Hashes::SHA1SUM}, + HashAlgo{2, "SHA256", GNUTLS_DIG_SHA256, Hashes::SHA256SUM}, + HashAlgo{3, "SHA512", GNUTLS_DIG_SHA512, Hashes::SHA512SUM}, + }; + + bool Write(unsigned char const *Data, size_t Size) + { + for (auto &dig : digs) + { + if (dig) + gnutls_hash(*dig, Data, Size); + } + return true; + } + + std::string HexDigest(HashAlgo const &algo) + { + auto Size = gnutls_hash_get_len(algo.gnuTlsAlgo); + unsigned char Sum[Size]; + if (auto copy = gnutls_hash_copy(*digs[algo.index])) + gnutls_hash_deinit(copy, &Sum); + return ::HexDigest(std::basic_string_view(Sum, Size)); + } + bool Enable(HashAlgo const &algo) + { + digs[algo.index].emplace(); + if (gnutls_hash_init(&*digs[algo.index], algo.gnuTlsAlgo) == 0) + return true; + digs[algo.index] = std::nullopt; + return false; + } + bool IsEnabled(HashAlgo const &algo) + { + return bool{digs[algo.index]}; + } + + explicit PrivateHashes() {} + ~PrivateHashes() + { + for (auto &dig : digs) + { + if (dig) + gnutls_hash_deinit(*dig, nullptr); + } + } +#else gcry_md_hd_t hd; void maybeInit() @@ -395,6 +460,7 @@ class PrivateHashes { gcry_md_close(hd); } +#endif explicit PrivateHashes(unsigned int const CalcHashes) : PrivateHashes() { -- cgit v1.2.3-70-g09d2 From 90270f0959d490d56db891809d83c91b3d4b9bf0 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Wed, 18 Dec 2024 19:37:40 +0100 Subject: hashes, methods: Add OpenSSL backends Introduce an OpenSSL::Crypto backend for the hashes library and an OpenSSL::SSL backend for the TLS support in our https method. Many thanks to curl for showing the way with how to handle a CRL file. There are some memory leaks here with the TlsFd itself as well as the proxy support; and we should reorganize the code to generate the ssl object as late as possible. A peculiar aspect of OpenSSL is that SSL_has_pending() returns 1 even if SSL_read() will fail to read anything and return the equivalent of EAGAIN. We work around this here by also peeking ahead 1 byte. I was running a very high RTT connection from Germany to Australia for testing, and with the peeking it's using negligible amounts of CPU; before that, it was busy looping at 100%. Bad OpenSSL! --- CMake/config.h.in | 3 + CMakeLists.txt | 13 +- COPYING | 21 ++++ apt-pkg/CMakeLists.txt | 2 + apt-pkg/contrib/hashes.cc | 73 +++++++++++- debian/control | 3 +- methods/CMakeLists.txt | 7 +- methods/connect.cc | 295 +++++++++++++++++++++++++++++++++++++++++++++- methods/http.cc | 12 +- 9 files changed, 409 insertions(+), 20 deletions(-) (limited to 'apt-pkg') diff --git a/CMake/config.h.in b/CMake/config.h.in index 8346a5e9e..db22ac0a2 100644 --- a/CMake/config.h.in +++ b/CMake/config.h.in @@ -32,6 +32,9 @@ /* Define if we have the seccomp library */ #cmakedefine HAVE_SECCOMP +/* Define if we want to use the openssl libraries */ +#cmakedefine WITH_OPENSSL + /* These two are used by the statvfs shim for glibc2.0 and bsd */ /* Define if we have sys/vfs.h */ #cmakedefine HAVE_VFS_H diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d2a5414d..f2433c6c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,7 @@ enable_testing() option(REQUIRE_MERGED_USR "Require merged-usr." ON) option(WITH_DOC "Build all documentation." ON) +option(WITH_OPENSSL "Build all documentation." ON) include(CMakeDependentOption) cmake_dependent_option(WITH_DOC_MANPAGES "Force building manpages." OFF "NOT WITH_DOC" OFF) cmake_dependent_option(WITH_DOC_GUIDES "Force building guides." OFF "NOT WITH_DOC" OFF) @@ -92,9 +93,13 @@ if (BERKELEY_FOUND) set(HAVE_BDB 1) endif() -find_package(GnuTLS) -if (GNUTLS_FOUND) - set(HAVE_GNUTLS 1) +if (WITH_OPENSSL) + find_package(OpenSSL REQUIRED) +else() + find_package(GnuTLS) + if (GNUTLS_FOUND) + set(HAVE_GNUTLS 1) + endif() endif() # (De)Compressor libraries @@ -141,7 +146,7 @@ if (SECCOMP_FOUND) set(HAVE_SECCOMP 1) endif() -if (NOT HAVE_GNUTLS) +if (NOT HAVE_GNUTLS AND NOT WITH_OPENSSL) find_package(GCRYPT REQUIRED) endif() find_package(XXHASH REQUIRED) 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, , and many contributors +License: GPL-2+ and curl + Files: methods/rsh.cc Copyright: 2000 Ben Collins 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 c68b7bddc..88f07fb79 100644 --- a/apt-pkg/CMakeLists.txt +++ b/apt-pkg/CMakeLists.txt @@ -56,6 +56,7 @@ target_include_directories(apt-pkg $<$:${UDEV_INCLUDE_DIRS}> $<$:${SYSTEMD_INCLUDE_DIRS}> ${ICONV_INCLUDE_DIRS} + $<$:${OPENSSL_INCLUDE_DIRS}> $<$:${GNUTLS_INCLUDE_DIRS}> $<$:${GCRYPT_INCLUDE_DIRS}> $<$:${XXHASH_INCLUDE_DIRS}> @@ -72,6 +73,7 @@ target_link_libraries(apt-pkg $<$:${UDEV_LIBRARIES}> $<$:${SYSTEMD_LIBRARIES}> ${ICONV_LIBRARIES} + $<$:OpenSSL::Crypto> $<$:${GNUTLS_LIBRARIES}> $<$:${GCRYPT_LIBRARIES}> $<$:${XXHASH_LIBRARIES}> diff --git a/apt-pkg/contrib/hashes.cc b/apt-pkg/contrib/hashes.cc index 033cd0845..664ad0ac7 100644 --- a/apt-pkg/contrib/hashes.cc +++ b/apt-pkg/contrib/hashes.cc @@ -30,7 +30,9 @@ #include #include -#ifdef HAVE_GNUTLS +#if defined(WITH_OPENSSL) +#include +#elif defined(HAVE_GNUTLS) #include #else #include @@ -329,7 +331,74 @@ class PrivateHashes unsigned long long FileSize{0}; private: -#ifdef HAVE_GNUTLS +#if defined(WITH_OPENSSL) + std::array contexts{}; + + public: + struct HashAlgo + { + size_t index; + const char *name; + const EVP_MD *(*evpLink)(void); + Hashes::SupportedHashes ourAlgo; + }; + + static constexpr std::array 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 (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(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); + } + +#elif defined(HAVE_GNUTLS) std::array, 4> digs{}; public: 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) , googletest | libgtest-dev , 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/methods/CMakeLists.txt b/methods/CMakeLists.txt index 548d867dc..c27b1438b 100644 --- a/methods/CMakeLists.txt +++ b/methods/CMakeLists.txt @@ -13,14 +13,17 @@ add_executable(http http.cc basehttp.cc $) add_executable(mirror mirror.cc) add_executable(rred rred.cc) -if (HAVE_GNUTLS) +if (WITH_OPENSSL) + target_compile_definitions(connectlib PRIVATE ${OPENSSL_DEFINITIONS}) + target_include_directories(connectlib PRIVATE ${OPENSSL_INCLUDE_DIR}) +elseif (HAVE_GNUTLS) target_compile_definitions(connectlib PRIVATE ${GNUTLS_DEFINITIONS}) target_include_directories(connectlib PRIVATE ${GNUTLS_INCLUDE_DIR}) endif() target_include_directories(http PRIVATE $<$:${SYSTEMD_INCLUDE_DIRS}>) # Additional libraries to link against for networked stuff -target_link_libraries(http $<$:${GNUTLS_LIBRARIES}> $<$:${SYSTEMD_LIBRARIES}>) +target_link_libraries(http $<$:OpenSSL::SSL> $<$:${GNUTLS_LIBRARIES}> $<$:${SYSTEMD_LIBRARIES}>) target_link_libraries(rred apt-private) diff --git a/methods/connect.cc b/methods/connect.cc index 12847c594..fc1bd132c 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 - 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,15 @@ #include #include -#ifdef HAVE_GNUTLS +#ifdef WITH_OPENSSL +#include +#include +#elif defined(HAVE_GNUTLS) #include #include #endif +#include #include #include #include @@ -801,7 +807,288 @@ ResultState UnwrapSocks(std::string Host, int Port, URI Proxy, std::unique_ptrError(__VA_ARGS__), nullptr) +#define ssl_strerr() ERR_error_string(ERR_get_error(), nullptr) +struct TlsFd final : public MethodFd +{ + std::unique_ptr UnderlyingFd{}; + + SSL *ssl{}; + + std::string hostname{}; + unsigned long Timeout{}; + bool broken{false}; + + int Fd() override { return UnderlyingFd ? UnderlyingFd->Fd() : -1; } + + ssize_t Read(void *buf, size_t count) override + { + assert(ssl); + return HandleError(SSL_read(ssl, buf, count)); + } + ssize_t Write(void *buf, size_t count) override + { + assert(ssl); + return HandleError(SSL_write(ssl, buf, count)); + } + + ssize_t HandleError(ssize_t r) + { + assert(ssl); + if (r > 0) + return r; + + auto err = SSL_get_error(ssl, r); + switch (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 r; + } + + int Close() override + { + int res = 0; // 0 or 1 are success + int lower = 0; // 0 is success + + if (ssl) + { + if (not broken && res) + res = SSL_shutdown(ssl); + if (res < 0) + HandleError(res); + SSL_free(ssl); + } + ssl = nullptr; + + if (UnderlyingFd) + lower = UnderlyingFd->Close(); + UnderlyingFd = nullptr; + + // Return -1 on failure, 0 on success + return res < 0 || lower < 0 ? -1 : 0; + } + + bool HasPending() override + { + 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; + } +}; + +static BIO_METHOD *NewBioMethod() +{ + BIO_METHOD *m = BIO_meth_new(BIO_TYPE_MEM, "OpenSSL APT BIO method"); + if (not m) + { + _error->Error("SSL connection failed: %s - %s", ssl_strerr(), strerror(errno)); + return nullptr; + } + + 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(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(p)->Read(buf, size); + if (errno == EAGAIN) + BIO_set_retry_read(bio); + return res; }); + + return m; +} + +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 (lastHost == host) + { + if (Debug) + std::clog << "Reusing context for " << host << std::endl; + return ctx; + } + if (Debug) + std::clog << "Creating context for " << host << std::endl; + + // 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"); + + // 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()); + + // Load the certificate authorities, either custom or default ones + if (auto const fileinfo = OwnerConf->ConfigFind("CaInfo", ""); not fileinfo.empty()) + { + 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 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()) + { + // tell OpenSSL where to find CRL file that is used to check certificate revocation. + // lifted from curl: + // Copyright (c) 1996 - 2023, Daniel Stenberg, , 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); + } + + lastHost = host; + return ctx; +} + +ResultState UnwrapTLS(std::string const &Host, std::unique_ptr &Fd, + unsigned long const Timeout, aptMethod *const Owner, + aptConfigWrapperForMethods const *const OwnerConf) +{ + if (_config->FindB("Acquire::AllowTLS", true) == false) + { + _error->Error("TLS support has been disabled: Acquire::AllowTLS is false."); + return ResultState::FATAL_ERROR; + } + + 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; + + FdFd *fdfd = dynamic_cast(Fd.get()); + if (fdfd != nullptr) + { + SSL_set_fd(tlsFd->ssl, fdfd->fd); + } + else + { + static auto m = NewBioMethod(); + if (m == nullptr) + return ResultState::FATAL_ERROR; + + auto bio = BIO_new(m); + if (bio == nullptr) + 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)) + { + 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 + { + struct in_addr addr4; + struct in6_addr addr6; + + 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 (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(), 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); + + return ResultState::SUCCESSFUL; +} +#elif defined(HAVE_GNUTLS /*}}}*/ // UnwrapTLS - Handle TLS connections /*{{{*/ // --------------------------------------------------------------------- /* Performs a TLS handshake on the socket */ diff --git a/methods/http.cc b/methods/http.cc index 1e76a1f57..48c3d540d 100644 --- a/methods/http.cc +++ b/methods/http.cc @@ -428,7 +428,7 @@ ResultState HttpServerState::Open() Out.Reset(); Persistent = true; -#ifdef HAVE_GNUTLS +#if defined(HAVE_GNUTLS) || defined(WITH_OPENSSL) bool tls = (ServerName.Access == "https" || APT::String::Endswith(ServerName.Access, "+https")); #endif @@ -455,7 +455,7 @@ ResultState HttpServerState::Open() { char *result = getenv("http_proxy"); Proxy = result ? result : ""; -#ifdef HAVE_GNUTLS +#if defined(HAVE_GNUTLS) || defined(WITH_OPENSSL) if (tls == true) { char *result = getenv("https_proxy"); @@ -478,7 +478,7 @@ ResultState HttpServerState::Open() if (Proxy.empty() == false) Owner->AddProxyAuth(Proxy, ServerName); -#ifdef HAVE_GNUTLS +#if defined(HAVE_GNUTLS) || defined(WITH_OPENSSL) auto const DefaultService = tls ? "https" : "http"; auto const DefaultPort = tls ? 443 : 80; #else @@ -518,7 +518,7 @@ ResultState HttpServerState::Open() Port = Proxy.Port; Host = Proxy.Host; -#ifdef HAVE_GNUTLS +#if defined(HAVE_GNUTLS) || defined(WITH_OPENSSL) if (Proxy.Access == "https" && Port == 0) Port = 443; #endif @@ -526,7 +526,7 @@ ResultState HttpServerState::Open() auto result = Connect(Host, Port, DefaultService, DefaultPort, ServerFd, TimeOut, Owner); if (result != ResultState::SUCCESSFUL) return result; -#ifdef HAVE_GNUTLS +#if defined(HAVE_GNUTLS) || defined(WITH_OPENSSL) if (Host == Proxy.Host && Proxy.Access == "https") { aptConfigWrapperForMethods ProxyConf{std::vector{"http", "https"}}; @@ -544,7 +544,7 @@ ResultState HttpServerState::Open() #endif } -#ifdef HAVE_GNUTLS +#if defined(HAVE_GNUTLS) || defined(WITH_OPENSSL) if (tls) return UnwrapTLS(ServerName.Host, ServerFd, TimeOut, Owner, Owner); #endif -- cgit v1.2.3-70-g09d2 From 7b1fb90b93ced7c0772d726e512da01fad456852 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Sat, 21 Dec 2024 15:26:34 +0100 Subject: Remove GnuTLS and gcrypt support OpenSSL is mandatory now, it is no longer possible to build without https support either. --- CMake/FindGCRYPT.cmake | 25 ----- CMake/config.h.in | 6 -- CMakeLists.txt | 13 +-- apt-pkg/CMakeLists.txt | 8 +- apt-pkg/contrib/hashes.cc | 140 ------------------------- methods/CMakeLists.txt | 11 +- methods/connect.cc | 259 ---------------------------------------------- methods/http.cc | 15 --- 8 files changed, 6 insertions(+), 471 deletions(-) delete mode 100644 CMake/FindGCRYPT.cmake (limited to 'apt-pkg') 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 db22ac0a2..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 @@ -32,9 +29,6 @@ /* Define if we have the seccomp library */ #cmakedefine HAVE_SECCOMP -/* Define if we want to use the openssl libraries */ -#cmakedefine WITH_OPENSSL - /* These two are used by the statvfs shim for glibc2.0 and bsd */ /* Define if we have sys/vfs.h */ #cmakedefine HAVE_VFS_H diff --git a/CMakeLists.txt b/CMakeLists.txt index f2433c6c8..af2ec3a86 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,6 @@ enable_testing() option(REQUIRE_MERGED_USR "Require merged-usr." ON) option(WITH_DOC "Build all documentation." ON) -option(WITH_OPENSSL "Build all documentation." ON) include(CMakeDependentOption) cmake_dependent_option(WITH_DOC_MANPAGES "Force building manpages." OFF "NOT WITH_DOC" OFF) cmake_dependent_option(WITH_DOC_GUIDES "Force building guides." OFF "NOT WITH_DOC" OFF) @@ -93,14 +92,7 @@ if (BERKELEY_FOUND) set(HAVE_BDB 1) endif() -if (WITH_OPENSSL) - find_package(OpenSSL REQUIRED) -else() - find_package(GnuTLS) - if (GNUTLS_FOUND) - set(HAVE_GNUTLS 1) - endif() -endif() +find_package(OpenSSL REQUIRED) # (De)Compressor libraries find_package(ZLIB REQUIRED) @@ -146,9 +138,6 @@ if (SECCOMP_FOUND) set(HAVE_SECCOMP 1) endif() -if (NOT HAVE_GNUTLS AND NOT WITH_OPENSSL) - find_package(GCRYPT REQUIRED) -endif() find_package(XXHASH REQUIRED) # Mount()ing and stat()ing and friends diff --git a/apt-pkg/CMakeLists.txt b/apt-pkg/CMakeLists.txt index 88f07fb79..4c036cc67 100644 --- a/apt-pkg/CMakeLists.txt +++ b/apt-pkg/CMakeLists.txt @@ -56,9 +56,7 @@ target_include_directories(apt-pkg $<$:${UDEV_INCLUDE_DIRS}> $<$:${SYSTEMD_INCLUDE_DIRS}> ${ICONV_INCLUDE_DIRS} - $<$:${OPENSSL_INCLUDE_DIRS}> - $<$:${GNUTLS_INCLUDE_DIRS}> - $<$:${GCRYPT_INCLUDE_DIRS}> + ${OPENSSL_INCLUDE_DIRS} $<$:${XXHASH_INCLUDE_DIRS}> ) @@ -73,9 +71,7 @@ target_link_libraries(apt-pkg $<$:${UDEV_LIBRARIES}> $<$:${SYSTEMD_LIBRARIES}> ${ICONV_LIBRARIES} - $<$:OpenSSL::Crypto> - $<$:${GNUTLS_LIBRARIES}> - $<$:${GCRYPT_LIBRARIES}> + OpenSSL::Crypto $<$:${XXHASH_LIBRARIES}> ) set_target_properties(apt-pkg PROPERTIES VERSION ${MAJOR}.${MINOR}) diff --git a/apt-pkg/contrib/hashes.cc b/apt-pkg/contrib/hashes.cc index 664ad0ac7..c7a4ab0e7 100644 --- a/apt-pkg/contrib/hashes.cc +++ b/apt-pkg/contrib/hashes.cc @@ -30,13 +30,7 @@ #include #include -#if defined(WITH_OPENSSL) #include -#elif defined(HAVE_GNUTLS) -#include -#else -#include -#endif /*}}}*/ const char * HashString::_SupportedHashes[] = @@ -331,7 +325,6 @@ class PrivateHashes unsigned long long FileSize{0}; private: -#if defined(WITH_OPENSSL) std::array contexts{}; public: @@ -398,139 +391,6 @@ class PrivateHashes EVP_MD_CTX_free(ctx); } -#elif defined(HAVE_GNUTLS) - std::array, 4> digs{}; - - public: - struct HashAlgo - { - size_t index; - const char *name; - gnutls_digest_algorithm_t gnuTlsAlgo; - Hashes::SupportedHashes ourAlgo; - }; - - static constexpr std::array Algorithms{ - HashAlgo{0, "MD5Sum", GNUTLS_DIG_MD5, Hashes::MD5SUM}, - HashAlgo{1, "SHA1", GNUTLS_DIG_SHA1, Hashes::SHA1SUM}, - HashAlgo{2, "SHA256", GNUTLS_DIG_SHA256, Hashes::SHA256SUM}, - HashAlgo{3, "SHA512", GNUTLS_DIG_SHA512, Hashes::SHA512SUM}, - }; - - bool Write(unsigned char const *Data, size_t Size) - { - for (auto &dig : digs) - { - if (dig) - gnutls_hash(*dig, Data, Size); - } - return true; - } - - std::string HexDigest(HashAlgo const &algo) - { - auto Size = gnutls_hash_get_len(algo.gnuTlsAlgo); - unsigned char Sum[Size]; - if (auto copy = gnutls_hash_copy(*digs[algo.index])) - gnutls_hash_deinit(copy, &Sum); - return ::HexDigest(std::basic_string_view(Sum, Size)); - } - bool Enable(HashAlgo const &algo) - { - digs[algo.index].emplace(); - if (gnutls_hash_init(&*digs[algo.index], algo.gnuTlsAlgo) == 0) - return true; - digs[algo.index] = std::nullopt; - return false; - } - bool IsEnabled(HashAlgo const &algo) - { - return bool{digs[algo.index]}; - } - - explicit PrivateHashes() {} - ~PrivateHashes() - { - for (auto &dig : digs) - { - if (dig) - gnutls_hash_deinit(*dig, nullptr); - } - } -#else - gcry_md_hd_t hd; - - void maybeInit() - { - - // 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)) - { - 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); - } - } - - public: - struct HashAlgo - { - const char *name; - int gcryAlgo; - Hashes::SupportedHashes ourAlgo; - }; - - static constexpr std::array Algorithms{ - HashAlgo{"MD5Sum", GCRY_MD_MD5, Hashes::MD5SUM}, - HashAlgo{"SHA1", GCRY_MD_SHA1, Hashes::SHA1SUM}, - HashAlgo{"SHA256", GCRY_MD_SHA256, Hashes::SHA256SUM}, - HashAlgo{"SHA512", GCRY_MD_SHA512, Hashes::SHA512SUM}, - }; - - bool Write(unsigned char const *Data, size_t Size) - { - gcry_md_write(hd, Data, Size); - return true; - } - - std::string HexDigest(HashAlgo const &algo) - { - auto Size = gcry_md_get_algo_dlen(algo.gcryAlgo); - auto Sum = gcry_md_read(hd, algo.gcryAlgo); - return ::HexDigest(std::basic_string_view(Sum, Size)); - } - - bool Enable(HashAlgo const &Algo) - { - gcry_md_enable(hd, Algo.gcryAlgo); - return true; - } - bool IsEnabled(HashAlgo const &algo) - { - return gcry_md_is_enabled(hd, algo.gcryAlgo); - } - - explicit PrivateHashes() - { - maybeInit(); - gcry_md_open(&hd, 0, 0); - } - - ~PrivateHashes() - { - gcry_md_close(hd); - } -#endif - explicit PrivateHashes(unsigned int const CalcHashes) : PrivateHashes() { for (auto & Algo : Algorithms) diff --git a/methods/CMakeLists.txt b/methods/CMakeLists.txt index c27b1438b..beb08a81d 100644 --- a/methods/CMakeLists.txt +++ b/methods/CMakeLists.txt @@ -13,17 +13,12 @@ add_executable(http http.cc basehttp.cc $) add_executable(mirror mirror.cc) add_executable(rred rred.cc) -if (WITH_OPENSSL) - target_compile_definitions(connectlib PRIVATE ${OPENSSL_DEFINITIONS}) - target_include_directories(connectlib PRIVATE ${OPENSSL_INCLUDE_DIR}) -elseif (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 $<$:${SYSTEMD_INCLUDE_DIRS}>) # Additional libraries to link against for networked stuff -target_link_libraries(http $<$:OpenSSL::SSL> $<$:${GNUTLS_LIBRARIES}> $<$:${SYSTEMD_LIBRARIES}>) +target_link_libraries(http OpenSSL::SSL $<$:${SYSTEMD_LIBRARIES}>) target_link_libraries(rred apt-private) diff --git a/methods/connect.cc b/methods/connect.cc index fc1bd132c..b00e73eb7 100644 --- a/methods/connect.cc +++ b/methods/connect.cc @@ -21,13 +21,8 @@ #include #include -#ifdef WITH_OPENSSL #include #include -#elif defined(HAVE_GNUTLS) -#include -#include -#endif #include #include @@ -807,7 +802,6 @@ ResultState UnwrapSocks(std::string Host, int Port, URI Proxy, std::unique_ptr &Fd, return ResultState::SUCCESSFUL; } -#elif defined(HAVE_GNUTLS /*}}}*/ -// UnwrapTLS - Handle TLS connections /*{{{*/ -// --------------------------------------------------------------------- -/* Performs a TLS handshake on the socket */ -struct TlsFd final : public MethodFd -{ - std::unique_ptr UnderlyingFd; - gnutls_session_t session; - gnutls_certificate_credentials_t credentials; - std::string hostname; - unsigned long Timeout; - - int Fd() APT_OVERRIDE { return UnderlyingFd->Fd(); } - - ssize_t Read(void *buf, size_t count) APT_OVERRIDE - { - return HandleError(gnutls_record_recv(session, buf, count)); - } - ssize_t Write(void *buf, size_t count) APT_OVERRIDE - { - return HandleError(gnutls_record_send(session, buf, count)); - } - - ssize_t DoTLSHandshake() - { - 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) - { - // 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)); - } - return err; - } - - template - T HandleError(T err) - { - // Server may request re-handshake if client certificates need to be provided - // based on resource requested - if (err == GNUTLS_E_REHANDSHAKE) - { - 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 (err < 0 && gnutls_error_is_fatal(err)) - errno = EIO; - else if (err < 0) - errno = EAGAIN; - else - errno = 0; - return err; - } - - int Close() APT_OVERRIDE - { - auto err = HandleError(gnutls_bye(session, GNUTLS_SHUT_RDWR)); - auto lower = UnderlyingFd->Close(); - return err < 0 ? HandleError(err) : lower; - } - - bool HasPending() APT_OVERRIDE - { - return gnutls_record_check_pending(session) > 0; - } -}; - -ResultState UnwrapTLS(std::string const &Host, std::unique_ptr &Fd, - unsigned long const Timeout, aptMethod * const Owner, - aptConfigWrapperForMethods const * const OwnerConf) -{ - if (_config->FindB("Acquire::AllowTLS", true) == false) - { - _error->Error("TLS support has been disabled: Acquire::AllowTLS is false."); - return ResultState::FATAL_ERROR; - } - - int err; - TlsFd *tlsFd = new TlsFd(); - - tlsFd->hostname = Host; - tlsFd->UnderlyingFd = MethodFd::FromFd(-1); // For now - tlsFd->Timeout = Timeout; - - if ((err = gnutls_init(&tlsFd->session, GNUTLS_CLIENT | GNUTLS_NONBLOCK)) < 0) - { - _error->Error("Internal error: could not allocate credentials: %s", gnutls_strerror(err)); - return ResultState::FATAL_ERROR; - } - - FdFd *fdfd = dynamic_cast(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(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(p)->Write((void *)buf, size); - }); - } - - 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; - } - - // Credential setup - std::string fileinfo = OwnerConf->ConfigFind("CaInfo", ""); - if (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; - } - } - else - { - // 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; - } - } - - if (not OwnerConf->ConfigFind("IssuerCert", "").empty()) - { - _error->Error("The option '%s' is not supported anymore", "IssuerCert"); - return ResultState::FATAL_ERROR; - } - if (not OwnerConf->ConfigFind("SslForceVersion", "").empty()) - { - _error->Error("The option '%s' is not supported anymore", "SslForceVersion"); - 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) - { - 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; - } - } - - // CRL file - std::string const crlfile = OwnerConf->ConfigFind("CrlFile", ""); - if (crlfile.empty() == false) - { - 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)); - 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; - } - - 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; - } - - if (OwnerConf->ConfigFindB("Verify-Peer", true)) - { - gnutls_session_set_verify_cert(tlsFd->session, OwnerConf->ConfigFindB("Verify-Host", true) ? tlsFd->hostname.c_str() : nullptr, 0); - } - - // set SNI only if the hostname is really a name and not an address - { - struct in_addr addr4; - struct in6_addr addr6; - - 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) - { - _error->Error("Could not set host name %s to indicate to server: %s", tlsFd->hostname.c_str(), gnutls_strerror(err)); - return ResultState::FATAL_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 48c3d540d..678ce3952 100644 --- a/methods/http.cc +++ b/methods/http.cc @@ -428,9 +428,7 @@ ResultState HttpServerState::Open() Out.Reset(); Persistent = true; -#if defined(HAVE_GNUTLS) || defined(WITH_OPENSSL) 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 : ""; -#if defined(HAVE_GNUTLS) || defined(WITH_OPENSSL) 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); -#if defined(HAVE_GNUTLS) || defined(WITH_OPENSSL) 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; -#if defined(HAVE_GNUTLS) || defined(WITH_OPENSSL) 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; -#if defined(HAVE_GNUTLS) || defined(WITH_OPENSSL) if (Host == Proxy.Host && Proxy.Access == "https") { aptConfigWrapperForMethods ProxyConf{std::vector{"http", "https"}}; @@ -541,13 +529,10 @@ ResultState HttpServerState::Open() if (result != ResultState::SUCCESSFUL) return result; } -#endif } -#if defined(HAVE_GNUTLS) || defined(WITH_OPENSSL) if (tls) return UnwrapTLS(ServerName.Host, ServerFd, TimeOut, Owner, Owner); -#endif return ResultState::SUCCESSFUL; } -- cgit v1.2.3-70-g09d2