Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement runtime check on libcrypto linkage #186

Merged
merged 10 commits into from
Jun 14, 2024
37 changes: 37 additions & 0 deletions source/unix/openssl_platform_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
#define OPENSSL_SUPPRESS_DEPRECATED
#include <openssl/crypto.h>

#if defined(OPENSSL_IS_AWSLC)
# include <openssl/service_indicator.h>
#endif

static struct openssl_hmac_ctx_table hmac_ctx_table;
static struct openssl_evp_md_ctx_table evp_md_ctx_table;

Expand Down Expand Up @@ -555,6 +559,37 @@ static enum aws_libcrypto_version s_resolve_libcrypto_lib(void) {
return AWS_LIBCRYPTO_NONE;
}

/* Validate at runtime that we're linked against the same libcrypto we compiled against. */
static void s_validate_libcrypto_linkage(void) {
WillChilds-Klein marked this conversation as resolved.
Show resolved Hide resolved
/* NOTE: the choice of stack buffer size is somewhat arbitrary. it's
* possible, but unlikely, that libcrypto version strings may exceed this in
* the future. we guard against buffer overflow by limiting write size in
* snprintf with the size of the buffer itself. if libcrypto version strings
* do eventually exceed the chosen size, this runtime check will fail and
* will need to be addressed by increasing buffer size.*/
char expected_version[64] = {0};
#if defined(OPENSSL_IS_AWSLC)
/* get FIPS mode at runtime becuase headers don't give any indication of
WillChilds-Klein marked this conversation as resolved.
Show resolved Hide resolved
* AWS-LC's FIPSness at aws-c-cal compile time. version number can still be
* captured at preprocess/compile time from AWSLC_VERSION_NUMBER_STRING.*/
const char *mode = FIPS_mode() ? "AWS-LC FIPS" : "AWS-LC";
snprintf(expected_version, sizeof(expected_version), "%s %s", mode, AWSLC_VERSION_NUMBER_STRING);
#elif defined(OPENSSL_IS_BORINGSSL)
snprintf(expected_version, sizeof(expected_version), "BoringSSL");
#elif defined(OPENSSL_IS_OPENSSL)
snprintf(expected_version, sizeof(expected_version), OPENSSL_VERSION_TEXT);
#elif !defined(BYO_CRYPTO)
# error Unsupported libcrypto!
#endif
const char *runtime_version = SSLeay_version(SSLEAY_VERSION);
AWS_LOGF_DEBUG(
AWS_LS_CAL_LIBCRYPTO_RESOLVE,
"Compiled with libcrypto %s, linked to libcrypto %s",
expected_version,
runtime_version);
AWS_FATAL_ASSERT(strcmp(expected_version, runtime_version) == 0 && "libcrypto mislink");
Copy link
Contributor Author

@WillChilds-Klein WillChilds-Klein May 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An important call-out is that while the current implementation is very simple, it is quite strict. Any deviation between compile/link version (including patch version and build date for OpenSSL) will cause this check to fail (except for BoringSSL as its version string is constant). For instance, if a user compiles an application on a system with slightly older OpenSSL installation with -DUSE_OPENSSL=ON then deploys the application to a system with a slightly newer OpenSSL installation (i.e. higher patch version, same minor version), we'd expect this check to fail.

The s2n-tls initialization check that inspired this PR is more relaxed. That check only validates libcrypto "family" (AWS-LC, BoringSSL, OpenSSL, etc.) and "major version" (this concept differs by family), allowing OpenSSL minor version to differ.

If we're not OK with the strictness of the current implementation, we could do a more complicated (but more flexible) validation that tests the following:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To better illustrate this, I've implemented the above suggestion here in this PR. Let me know what the team thinks, we can always revert to the stricter validation if that's preferred.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed offline with @DmitriyMusatkin. We may want to relax this check to only validate the libcrypto "family" matches between compile and link, and allow flexibility for library versions as OpenSSL has relatively stable ABI across minor (and major?) versions.

}

static enum aws_libcrypto_version s_resolve_libcrypto(void) {
/* Try to auto-resolve against what's linked in/process space */
AWS_LOGF_DEBUG(AWS_LS_CAL_LIBCRYPTO_RESOLVE, "searching process and loaded modules");
Expand Down Expand Up @@ -583,6 +618,8 @@ static enum aws_libcrypto_version s_resolve_libcrypto(void) {
result = s_resolve_libcrypto_lib();
}

s_validate_libcrypto_linkage();
WillChilds-Klein marked this conversation as resolved.
Show resolved Hide resolved

return result;
}

Expand Down