
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:
- The issue happens within a OpenSSL function (
SSL_CTX_use_certificate_chain_file()
) - 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) - The exact location of the problem is deep within the call to
PEM_read_bio_X509()
fromuse_certificate_chain_file()
in ssl_rsa.c:634. The functionget_name()
in pem_lib.c:745 sets aPEM_R_NO_START_LINE
error to signal "end-of-file", but whenuse_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. - This is apparently cause by linking both libcrypto.a and libcrypto.so
- 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