summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Andres Klode <julian.klode@canonical.com>2024-12-18 19:37:40 +0100
committerJulian Andres Klode <julian.klode@canonical.com>2024-12-22 22:55:39 +0100
commit90270f0959d490d56db891809d83c91b3d4b9bf0 (patch)
tree80589894ae2a0eda080b6c6cf416882b52eaef7d
parent470f5cf449ac20c7d7bf50ad805c72a5f52d256f (diff)
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!
-rw-r--r--CMake/config.h.in3
-rw-r--r--CMakeLists.txt13
-rw-r--r--COPYING21
-rw-r--r--apt-pkg/CMakeLists.txt2
-rw-r--r--apt-pkg/contrib/hashes.cc73
-rw-r--r--debian/control3
-rw-r--r--methods/CMakeLists.txt7
-rw-r--r--methods/connect.cc295
-rw-r--r--methods/http.cc12
9 files changed, 409 insertions, 20 deletions
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, <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 c68b7bddc..88f07fb79 100644
--- a/apt-pkg/CMakeLists.txt
+++ b/apt-pkg/CMakeLists.txt
@@ -56,6 +56,7 @@ target_include_directories(apt-pkg
$<$<BOOL:${UDEV_FOUND}>:${UDEV_INCLUDE_DIRS}>
$<$<BOOL:${SYSTEMD_FOUND}>:${SYSTEMD_INCLUDE_DIRS}>
${ICONV_INCLUDE_DIRS}
+ $<$<BOOL:${WITH_OPENSSL}>:${OPENSSL_INCLUDE_DIRS}>
$<$<BOOL:${GNUTLS_FOUND}>:${GNUTLS_INCLUDE_DIRS}>
$<$<BOOL:${GCRYPT_FOUND}>:${GCRYPT_INCLUDE_DIRS}>
$<$<BOOL:${XXHASH_FOUND}>:${XXHASH_INCLUDE_DIRS}>
@@ -72,6 +73,7 @@ target_link_libraries(apt-pkg
$<$<BOOL:${UDEV_FOUND}>:${UDEV_LIBRARIES}>
$<$<BOOL:${SYSTEMD_FOUND}>:${SYSTEMD_LIBRARIES}>
${ICONV_LIBRARIES}
+ $<$<BOOL:${WITH_OPENSSL}>:OpenSSL::Crypto>
$<$<BOOL:${GNUTLS_FOUND}>:${GNUTLS_LIBRARIES}>
$<$<BOOL:${GCRYPT_FOUND}>:${GCRYPT_LIBRARIES}>
$<$<BOOL:${XXHASH_FOUND}>:${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 <string>
#include <unistd.h>
-#ifdef HAVE_GNUTLS
+#if defined(WITH_OPENSSL)
+#include <openssl/evp.h>
+#elif defined(HAVE_GNUTLS)
#include <gnutls/crypto.h>
#else
#include <gcrypt.h>
@@ -329,7 +331,74 @@ class PrivateHashes
unsigned long long FileSize{0};
private:
-#ifdef HAVE_GNUTLS
+#if defined(WITH_OPENSSL)
+ std::array<EVP_MD_CTX *, 4> contexts{};
+
+ 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 (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);
+ }
+
+#elif defined(HAVE_GNUTLS)
std::array<std::optional<gnutls_hash_hd_t>, 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) <!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/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 $<TARGET_OBJECTS:connectlib>)
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 $<$<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 $<$<BOOL:${WITH_OPENSSL}>:OpenSSL::SSL> $<$<BOOL:${GNUTLS_FOUND}>:${GNUTLS_LIBRARIES}> $<$<BOOL:${SYSTEMD_FOUND}>:${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 <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,15 @@
#include <apt-pkg/srvrec.h>
#include <apt-pkg/strutl.h>
-#ifdef HAVE_GNUTLS
+#ifdef WITH_OPENSSL
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+#elif defined(HAVE_GNUTLS)
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
#endif
+#include <cassert>
#include <cerrno>
#include <cstdio>
#include <cstring>
@@ -801,7 +807,288 @@ ResultState UnwrapSocks(std::string Host, int Port, URI Proxy, std::unique_ptr<M
return ResultState::SUCCESSFUL;
}
-#ifdef HAVE_GNUTLS /*}}}*/
+#if defined(WITH_OPENSSL)
+// 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{};
+
+ 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<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;
+}
+
+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, <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);
+ }
+
+ 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("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<FdFd *>(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<std::string>{"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