aws / aws-sdk-cpp

AWS SDK for C++
Apache License 2.0
1.98k stars 1.06k forks source link

s2n links static version libcrypto #2735

Open ghost opened 1 year ago

ghost commented 1 year ago

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:

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

jmklix commented 1 year ago

We have make some cmake changes from v1.9 to v1.11 so that might be what is causing this error that you are seeing. Looking into this, but you can follow the tracking issue here: https://github.com/aws/aws-sdk-cpp/issues/1888

ghost commented 1 year ago

Thanks for looking into this!

BTW: I notices that with aws-sdk-cpp 1.9.212 there is also something off around FindCrypto ... Maybe 1.9.212 was also linking the static version of libcrypto but (due to another bug?) always found the dynamic (.so) and not the static (.a) library? (see LibCrypto Static Lib: /usr/local/lib64/libcrypto.so below)

-- Found AWS SDK for C++, Version: 1.9.212, Install Root:/usr/local, Platform Prefix:, Platform Dependent Libraries: pthread;crypto;ssl;z;curl
-- Components specified for AWSSDK: s3, application will be depending on libs: aws-cpp-sdk-s3;aws-cpp-sdk-core
-- Try finding aws-cpp-sdk-core
-- LibCrypto Include Dir: /usr/local/include
-- LibCrypto Shared Lib:  /usr/local/lib64/libcrypto.so
-- LibCrypto Static Lib:  /usr/local/lib64/libcrypto.so
sbiscigl commented 5 months ago

I notices that with aws-sdk-cpp 1.9.212 there is also something off around FindCrypto ...

We removed finding libcryto all together in a recent release, can you please confirm if this is still a problem? I tried building off of main using the implicit BUILD_SHARED_LIBS=ON by default and im seeing it working as expected

cmake build commands:

cmake3 -DCMAKE_BUILD_TYPE=Debug -DBUILD_ONLY="s3" -DCMAKE_INSTALL_PREFIX="~/test-install" ..
cmake --build .
cmake3 --install .

looking at the installed binary i see it linking against a shared libcrypto:

$ldd ~/test-install/lib64/libs2n.so
    linux-vdso.so.1 (0x00007ffe2793b000)
    libcrypto.so.10 => /lib64/libcrypto.so.10 (0x00007ff343e4e000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00007ff343c4a000)
    librt.so.1 => /lib64/librt.so.1 (0x00007ff343a42000)
    libm.so.6 => /lib64/libm.so.6 (0x00007ff343702000)
    libpthread.so.0 => /lib64/libpthread.so.0 (0x00007ff3434e4000)
    libc.so.6 => /lib64/libc.so.6 (0x00007ff343137000)
    /lib64/ld-linux-x86-64.so.2 (0x00007ff34465d000)
    libz.so.1 => /lib64/libz.so.1 (0x00007ff342f22000)

so interested if you are still seeing this

ghost commented 5 months ago

Thanks for looking into this!

Unfortunately the project i ran into this issue with, is no longer active and so i can not check with a recent release! Sorry!