Skip to content

s2n links static version libcrypto #2735

Closed
@ghost

Description

Describe the bug

After updating aws-sdk-cpp from version 1.9.212 to 1.11.189 we experienced very strange failures during initialization of an TLS capable HTTP server (httplib::SSLServer, https://github.com/yhirose/cpp-httplib).

What we found it that:

  1. The issue happens within a OpenSSL function (SSL_CTX_use_certificate_chain_file())
  2. It is cause by multiple copies of global data within OpenSSL (err_thread_local used as "key" to get the thread local instance of the struct OpenSSL uses to store errors)
  3. The exact location of the problem is deep within the call to PEM_read_bio_X509() from use_certificate_chain_file() in ssl_rsa.c:634. The function get_name() in pem_lib.c:745 sets a PEM_R_NO_START_LINE error to signal "end-of-file", but when use_certificate_chain_file() in ssl_rsa.c:653 checks for it, no error code is set and a "real error" is assumed (see ssl_rsa.c:658).
    This all is within one thread and in a single OpenSSL function!
    => use_certificate_chain_file() returns with error.
  4. This is apparently cause by linking both libcrypto.a and libcrypto.so
  5. Which is cause by the cmake aws-sdk-cpp targets that have PUBLIC or INTERFACE dependencies set to libcrypto.a

Workaround we currently use is to rename/delete libcrypto.a (and libssl.a) after we built OpenSSL and before building aws-sdk-cpp.
IMHO there is no (documented) build option in OpenSSL to not build the static libs ...

Callstack where PEM_R_NO_START_LINE error is set:

ERR_put_error err.c:454
get_name pem_lib.c:745
PEM_read_bio_ex pem_lib.c:926
pem_bytes_read_bio_flags pem_lib.c:247
PEM_bytes_read_bio pem_lib.c:278
PEM_ASN1_read_bio pem_oth.c:28
PEM_read_bio_X509 pem_x509.c:18
use_certificate_chain_file ssl_rsa.c:634
SSL_CTX_use_certificate_chain_file ssl_rsa.c:669
httplib::SSLServer::SSLServer httplib.cc:6084
std::make_unique<httplib::SSLServer, const char *, const char *> unique_ptr.h:1065
web_api::server::server server.cpp:175
std::make_unique<web_api::server, vau::vau_system &, const config::vau::web_api_config &> unique_ptr.h:1065
web_api_server::set_up server_test.cpp:135
...

Callstack with check for PEM_R_NO_START_LINE:

use_certificate_chain_file ssl_rsa.c:653
SSL_CTX_use_certificate_chain_file ssl_rsa.c:669
httplib::SSLServer::SSLServer httplib.cc:6084
std::make_unique<httplib::SSLServer, const char *, const char *> unique_ptr.h:1065
web_api::server::server server.cpp:175
std::make_unique<web_api::server, vau::vau_system &, const config::vau::web_api_config &> unique_ptr.h:1065
web_api_server::set_up server_test.cpp:135
...

Expected Behavior

When building aws-sdk-cpp resp. S3 and OpenSSL is used, s2n should always link libcrypto.so (if present).

Current Behavior

When building aws-sdk-cpp (resp. S3) and OpenSSL is used, s2n always links libcrypto.a (static link library) even if libcrypto.so is available on the same path and was found by FindCrypto!

Reproduction Steps

Build openssl 1.1.1w:

./config
make -j$(nproc)
make install

Build libcurl 8.4:

./configure --with-openssl=/usr/local --without-zstd
make -j$(nproc)
make install

Build S3 part of aws-sdk-cpp:

cmake .. -DBUILD_ONLY="s3" -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DOPENSSL_ROOT_DIR=/usr/local/ -DCPP_STANDARD=20 -DENABLE_TESTING=OFF -GNinja -DCMAKE_PREFIX_PATH=/usr/local/
ninja -j$(nproc)
ninja install

We include the aws-sdk-cpp in our cmake projekt using this:

set(CMAKE_PREFIX_PATH /usr/local/;${CMAKE_PREFIX_PATH})
find_package(CURL REQUIRED)
find_package(AWSSDK REQUIRED COMPONENTS s3)

set(AWS_INCLUDE_DIRS "")

foreach (LIB_TARGET ${AWSSDK_LINK_LIBRARIES})
  # get the correct name of the library target
  get_target_property(LIB_LOCATION ${LIB_TARGET} LOCATION)
  set(LIB_INCLUDE_DIRS "$<TARGET_PROPERTY:${LIB_TARGET},INCLUDE_DIRECTORIES>")
  set(AWS_INCLUDE_DIRS
      "${AWS_INCLUDE_DIRS};$<$<BOOL:${LIB_INCLUDE_DIRS}>:-I$<JOIN:${LIB_INCLUDE_DIRS}, -I>>"
  )

  file(GLOB LIB_NAME CONFIGURE_DEPENDS "${LIB_LOCATION}*")
  # install all versions from this library
  install(
    PROGRAMS ${LIB_NAME}
    DESTINATION lib
    COMPONENT vau
  )
endforeach ()

# add an interface for all awssdk include dirs
add_library(awssdk_dirs INTERFACE)
target_include_directories(awssdk_dirs SYSTEM INTERFACE ${AWS_INCLUDE_DIRS})

# and an interface for the libraries (the targets)
add_library(awssdk_libs INTERFACE)
target_link_libraries(awssdk_libs INTERFACE ${AWSSDK_LINK_LIBRARIES})

# aws depends on curl
find_package(CURL REQUIRED)
file(GLOB LIB_NAME CONFIGURE_DEPENDS "${CURL_LIBRARIES}*")
# install all versions from this library
install(
  PROGRAMS ${LIB_NAME}
  DESTINATION lib
  COMPONENT vau
)

So our binaries (shared libs and executables) link "awssdk_libs".
When inspecting the compiler/linker command lines both libcrypto.a and libcrypto.so are linked!

Possible Solution

Always link shared OpenSSL libraries if found and not explicitely configured to link static versions.

Additional Information/Context

openssl 1.1.1w
libcurl 8.4
aws-sdk-cpp 1.11.189
cpp-httplib 0.14.1
boost 1.80
fmt 9.1.0
nlohmann json 3.11.2
range-v3 0.12.0
spdlog 1.11.0
and some more libs

cmake 3.24.3

So libcrypto is at least used in our software via:

  • aws-sdk-cpp: (IMHO via s2n and libcurl)
  • cpp-httplib: HTTPS server and HTTPS client
    We also have code that uses functions from libcrypto directly.

We build all our code with visibility=hidden, "position independent code" on, C++20

AWS CPP SDK version used

1.11.189

Compiler and Version used

gcc 12.2.1 (Red Hat 12.2.1-7)

Operating System and version

RHEL 8.8

Metadata

Metadata

Assignees

No one assigned

    Labels

    CmakeCmake related submissionsbugThis issue is a bug.p2This is a standard priority issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions