From 41c106dcae4c09139c26100c0d07175253e4e5fb Mon Sep 17 00:00:00 2001 From: Will Childs-Klein Date: Thu, 20 Jun 2024 20:17:28 -0400 Subject: [PATCH] (FIPS Backport) Add EVP_md_null and SSL_set_ciphersuites (#1637) (#1653) --- .github/workflows/integrations.yml | 25 ++++ crypto/digest_extra/digest_extra.c | 19 +++ crypto/digest_extra/digest_test.cc | 5 + include/openssl/digest.h | 4 + include/openssl/ssl.h | 8 ++ ssl/ssl_lib.cc | 9 ++ ssl/ssl_test.cc | 15 ++- .../print-libcrypto-info.patch | 20 +++ .../master/print-libcrypto-info.patch | 20 +++ tests/ci/run_openldap_integration.sh | 119 ++++++++++++++++++ 10 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/integrations.yml create mode 100644 tests/ci/openldap_patch/OPENLDAP_REL_ENG_2_5/print-libcrypto-info.patch create mode 100644 tests/ci/openldap_patch/master/print-libcrypto-info.patch create mode 100755 tests/ci/run_openldap_integration.sh diff --git a/.github/workflows/integrations.yml b/.github/workflows/integrations.yml new file mode 100644 index 0000000000..307959e619 --- /dev/null +++ b/.github/workflows/integrations.yml @@ -0,0 +1,25 @@ +name: Integration tests +on: + push: + branches: [ '*' ] + pull_request: + branches: [ '*' ] +concurrency: + group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: true +env: + CC: gcc +jobs: + openldap: + if: github.repository_owner == 'aws' + runs-on: ubuntu-latest + name: OpenLDAP + steps: + - name: Install OS Dependencies + run: | + sudo apt-get update + sudo apt-get -y --no-install-recommends install cmake gcc ninja-build golang make + - uses: actions/checkout@v3 + - name: Build AWS-LC, build openldap, run tests + run: | + ./tests/ci/run_openldap_integration.sh master OPENLDAP_REL_ENG_2_5 diff --git a/crypto/digest_extra/digest_extra.c b/crypto/digest_extra/digest_extra.c index e0e5ee5092..a150a5b5ab 100644 --- a/crypto/digest_extra/digest_extra.c +++ b/crypto/digest_extra/digest_extra.c @@ -269,3 +269,22 @@ static const EVP_MD evp_md_blake2b256 = { }; const EVP_MD *EVP_blake2b256(void) { return &evp_md_blake2b256; } + +static void null_init(EVP_MD_CTX *ctx) {} + +static void null_update(EVP_MD_CTX *ctx, const void *data, size_t count) {} + +static void null_final(EVP_MD_CTX *ctx, unsigned char *md) {} + +static const EVP_MD evp_md_null = { + NID_undef, + 0, + 0, + null_init, + null_update, + null_final, + 0, + sizeof(EVP_MD_CTX), +}; + +const EVP_MD *EVP_md_null(void) { return &evp_md_null; } diff --git a/crypto/digest_extra/digest_test.cc b/crypto/digest_extra/digest_test.cc index 7eb95c0722..7cba7c15a2 100644 --- a/crypto/digest_extra/digest_test.cc +++ b/crypto/digest_extra/digest_test.cc @@ -63,6 +63,7 @@ static const MD sha3_384 = { "SHA3-384", &EVP_sha3_384, &SHA3_384 }; static const MD sha3_512 = { "SHA3-512", &EVP_sha3_512, &SHA3_512 }; static const MD md5_sha1 = { "MD5-SHA1", &EVP_md5_sha1, nullptr }; static const MD blake2b256 = { "BLAKE2b-256", &EVP_blake2b256, nullptr }; +static const MD md_null = { "NULL", &EVP_md_null, nullptr }; struct DigestTestVector { // md is the digest to test. @@ -224,6 +225,10 @@ static const DigestTestVector kTestVectors[] = { // BLAKE2b-256 tests. {blake2b256, "abc", 1, "bddd813c634239723171ef3fee98579b94964e3bb1cb3e427262c8c068d52319"}, + + // NULL tests. Empty output for any input + {md_null, "abc", 1, ""}, + {md_null, "", 1, ""}, }; static void CompareDigest(const DigestTestVector *test, diff --git a/include/openssl/digest.h b/include/openssl/digest.h index 1b43f46cf8..fbf4bb0260 100644 --- a/include/openssl/digest.h +++ b/include/openssl/digest.h @@ -353,6 +353,10 @@ struct env_md_ctx_st { } /* EVP_MD_CTX */; +// EVP_md_null is a "null" message digest that does nothing: i.e. the hash it +// returns is of zero length. Included for OpenSSL compatibility +OPENSSL_EXPORT const EVP_MD *EVP_md_null(void); + #if defined(__cplusplus) } // extern C diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 5ddd2327ee..d1aa1d20ad 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -1546,6 +1546,14 @@ OPENSSL_EXPORT int SSL_set_strict_cipher_list(SSL *ssl, const char *str); // evaluating |str| as a cipher string. It returns one on success and zero on failure. OPENSSL_EXPORT int SSL_CTX_set_ciphersuites(SSL_CTX *ctx, const char *str); +// SSL_set_ciphersuites sets the available TLSv1.3 ciphersuites on an |ssl|, +// returning one on success and zero on failure. In OpenSSL, the only +// difference between |SSL_CTX_set_ciphersuites| and |SSL_set_ciphersuites| is +// that the latter copies the |SSL|'s |cipher_list| to its associated +// |SSL_CONNECTION|. In AWS-LC, we track everything on the |ssl|'s |config| so +// duplication is not necessary. +OPENSSL_EXPORT int SSL_set_ciphersuites(SSL *ssl, const char *str); + // SSL_set_cipher_list configures the cipher list for |ssl|, evaluating |str| as // a cipher string. It returns one on success and zero on failure. // diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc index fe32402c99..c88e6a54c6 100644 --- a/ssl/ssl_lib.cc +++ b/ssl/ssl_lib.cc @@ -2057,6 +2057,15 @@ int SSL_CTX_set_ciphersuites(SSL_CTX *ctx, const char *str) { true /* only configure TLSv1.3 ciphers */); } +int SSL_set_ciphersuites(SSL *ssl, const char *str) { + if (!ssl->config) { + return 0; + } + return ssl_create_cipher_list(&ssl->config->cipher_list, str, + false /* not strict */, + true /* configure TLSv1.3 ciphers */); +} + int SSL_set_strict_cipher_list(SSL *ssl, const char *str) { if (!ssl->config) { return 0; diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc index 9ca45fe071..b3c7e05036 100644 --- a/ssl/ssl_test.cc +++ b/ssl/ssl_test.cc @@ -876,10 +876,14 @@ TEST(SSLTest, TLSv13CipherRules) { SCOPED_TRACE(t.rule); bssl::UniquePtr ctx(SSL_CTX_new(TLS_method())); ASSERT_TRUE(ctx); + bssl::UniquePtr ssl(SSL_new(ctx.get())); + ASSERT_TRUE(ssl); // Test lax mode. ASSERT_TRUE(SSL_CTX_set_ciphersuites(ctx.get(), t.rule)); - EXPECT_TRUE(CipherListsEqual(ctx.get(), t.expected, true /* TLSv1.3 only */)) + ASSERT_TRUE(SSL_set_ciphersuites(ssl.get(), t.rule)); + EXPECT_TRUE( + CipherListsEqual(ctx.get(), t.expected, true /* TLSv1.3 only */)) << "Cipher rule evaluated to:\n" << CipherListToString(ctx.get(), true /* TLSv1.3 only */); @@ -890,8 +894,11 @@ TEST(SSLTest, TLSv13CipherRules) { SCOPED_TRACE(rule); bssl::UniquePtr ctx(SSL_CTX_new(TLS_method())); ASSERT_TRUE(ctx); + bssl::UniquePtr ssl(SSL_new(ctx.get())); + ASSERT_TRUE(ssl); EXPECT_FALSE(SSL_CTX_set_ciphersuites(ctx.get(), rule)); + EXPECT_FALSE(SSL_set_ciphersuites(ssl.get(), rule)); ERR_clear_error(); } @@ -899,8 +906,11 @@ TEST(SSLTest, TLSv13CipherRules) { SCOPED_TRACE(rule); bssl::UniquePtr ctx(SSL_CTX_new(TLS_method())); ASSERT_TRUE(ctx); + bssl::UniquePtr ssl(SSL_new(ctx.get())); + ASSERT_TRUE(ssl); ASSERT_TRUE(SSL_CTX_set_ciphersuites(ctx.get(), rule)); + ASSERT_TRUE(SSL_set_ciphersuites(ssl.get(), rule)); // Currenly, only three TLSv1.3 ciphers are supported. EXPECT_EQ(3u, sk_SSL_CIPHER_num(tls13_ciphers(ctx.get()))); for (const SSL_CIPHER *cipher : tls13_ciphers(ctx.get())) { @@ -918,8 +928,11 @@ TEST(SSLTest, TLSv13CipherRules) { SCOPED_TRACE(t.rule); bssl::UniquePtr ctx(SSL_CTX_new(TLS_method())); ASSERT_TRUE(ctx); + bssl::UniquePtr ssl(SSL_new(ctx.get())); + ASSERT_TRUE(ssl); EXPECT_FALSE(SSL_CTX_set_ciphersuites(ctx.get(), t.rule)); + EXPECT_FALSE(SSL_set_ciphersuites(ssl.get(), t.rule)); ASSERT_EQ(ERR_GET_REASON(ERR_get_error()), SSL_R_NO_CIPHER_MATCH); ERR_clear_error(); } diff --git a/tests/ci/openldap_patch/OPENLDAP_REL_ENG_2_5/print-libcrypto-info.patch b/tests/ci/openldap_patch/OPENLDAP_REL_ENG_2_5/print-libcrypto-info.patch new file mode 100644 index 0000000000..b35971422e --- /dev/null +++ b/tests/ci/openldap_patch/OPENLDAP_REL_ENG_2_5/print-libcrypto-info.patch @@ -0,0 +1,20 @@ +--- ./servers/slapd/main.c 2024-01-29 18:53:15.000000000 +0000 ++++ ./servers/slapd/main.c 2024-01-29 18:22:49.300948791 +0000 +@@ -43,6 +43,8 @@ + #include "slapi/slapi.h" + #endif + ++#include ++ + #ifdef LDAP_SIGCHLD + static RETSIGTYPE wait4child( int sig ); + #endif +@@ -764,6 +766,8 @@ + + if ( version ) { + fprintf( stderr, "%s\n", Versionstr ); ++ fprintf( stderr, "COMPILE OPENSSL VERSION: %s\n", OPENSSL_VERSION_TEXT); ++ fprintf( stderr, "RUNTIME OPENSSL VERSION: %s\n", OpenSSL_version(OPENSSL_VERSION)); + if ( version > 2 ) { + if ( slap_oinfo[0].ov_type ) { + fprintf( stderr, "Included static overlays:\n"); diff --git a/tests/ci/openldap_patch/master/print-libcrypto-info.patch b/tests/ci/openldap_patch/master/print-libcrypto-info.patch new file mode 100644 index 0000000000..b35971422e --- /dev/null +++ b/tests/ci/openldap_patch/master/print-libcrypto-info.patch @@ -0,0 +1,20 @@ +--- ./servers/slapd/main.c 2024-01-29 18:53:15.000000000 +0000 ++++ ./servers/slapd/main.c 2024-01-29 18:22:49.300948791 +0000 +@@ -43,6 +43,8 @@ + #include "slapi/slapi.h" + #endif + ++#include ++ + #ifdef LDAP_SIGCHLD + static RETSIGTYPE wait4child( int sig ); + #endif +@@ -764,6 +766,8 @@ + + if ( version ) { + fprintf( stderr, "%s\n", Versionstr ); ++ fprintf( stderr, "COMPILE OPENSSL VERSION: %s\n", OPENSSL_VERSION_TEXT); ++ fprintf( stderr, "RUNTIME OPENSSL VERSION: %s\n", OpenSSL_version(OPENSSL_VERSION)); + if ( version > 2 ) { + if ( slap_oinfo[0].ov_type ) { + fprintf( stderr, "Included static overlays:\n"); diff --git a/tests/ci/run_openldap_integration.sh b/tests/ci/run_openldap_integration.sh new file mode 100755 index 0000000000..447c8d6da1 --- /dev/null +++ b/tests/ci/run_openldap_integration.sh @@ -0,0 +1,119 @@ +#!/usr/bin/env bash +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 OR ISC + +set -exu + +source tests/ci/common_posix_setup.sh + +set -exuo pipefail + +# Set up environment. + +# SYS_ROOT +# - SRC_ROOT(aws-lc) +# - SCRATCH_FOLDER +# - OPENLDAP_SRC_FOLDER +# - main +# ... +# - OPENLDAP_PATCH_FOLDER +# - main +# ... +# - AWS_LC_BUILD_FOLDER +# - AWS_LC_INSTALL_FOLDER + +# Assumes script is executed from the root of aws-lc directory +SCRATCH_FOLDER="${SRC_ROOT}/OPENLDAP_BUILD_ROOT" +OPENLDAP_SRC_FOLDER="${SCRATCH_FOLDER}/openldap-src" +OPENLDAP_PATCH_FOLDER="${SRC_ROOT}/tests/ci/openldap_patch" +AWS_LC_BUILD_FOLDER="${SCRATCH_FOLDER}/aws-lc-build" +AWS_LC_INSTALL_FOLDER="${SCRATCH_FOLDER}/aws-lc-install" + +AWS_LC_DIR=$(pwd) +function aws_lc_build() { + ${CMAKE_COMMAND} ${AWS_LC_DIR} -GNinja "-B${AWS_LC_BUILD_FOLDER}" "-DCMAKE_INSTALL_PREFIX=${AWS_LC_INSTALL_FOLDER}" + ninja -C ${AWS_LC_BUILD_FOLDER} install + ls -R ${AWS_LC_INSTALL_FOLDER} + rm -rf ${AWS_LC_BUILD_FOLDER}/* +} + +function openldap_build() { + local branch=${1} + pushd ${branch} + # Modify CFLAGS and LDFLAGS so compiler and linker can find AWS-LC's artifacts + export STRICT_C_COMPILER="gcc" + export CPPFLAGS="-I$AWS_LC_INSTALL_FOLDER/include" + export LDFLAGS="$AWS_LC_INSTALL_FOLDER/lib/libcrypto.a $AWS_LC_INSTALL_FOLDER/lib/libssl.a" + export LDFLAGS="$LDFLAGS -L$AWS_LC_INSTALL_FOLDER/lib" + ./configure \ + --prefix=$AWS_LC_INSTALL_FOLDER \ + --enable-debug \ + --enable-static \ + --enable-slapd \ + --disable-syslog \ + --with-tls \ + --without-systemd + make -j ${NUM_CPU_THREADS} + # assert that neither libcrypto nor libssl are linked dynamically + ldd ./servers/slapd/slapd | grep libcrypto || true | wc -l | xargs test 0 -eq + ldd ./servers/slapd/slapd | grep libssl || true | wc -l | xargs test 0 -eq + # assert that patched slapd binary is compiled against and linked to AWS-LC + # for some reason, -V exits non-zero so use "true" to guard against pipefail + ( ./servers/slapd/slapd -V || true ) |& grep AWS-LC | wc -l | xargs test 2 -eq + popd +} + +function openldap_run_tests() { + local branch=${1} + pushd ${branch} + make -j ${NUM_CPU_THREADS} test + popd +} + +function openldap_patch() { + local branch=${1} + local src_dir="${OPENLDAP_SRC_FOLDER}/${branch}" + local patch_dir="${OPENLDAP_PATCH_FOLDER}/${branch}" + if [[ ! $(find -L ${patch_dir} -type f -name '*.patch') ]]; then + echo "No patch for ${branch}!" + exit 1 + fi + git clone https://github.com/openldap/openldap.git ${src_dir} \ + --depth 1 \ + --branch ${branch} + for patchfile in $(find -L ${patch_dir} -type f -name '*.patch'); do + echo "Apply patch ${patchfile}..." + cat ${patchfile} \ + | patch -p1 --quiet -d ${src_dir} + done +} + +if [[ "$#" -eq "0" ]]; then + echo "No openldap branches provided for testing" + exit 1 +fi + +mkdir -p ${SCRATCH_FOLDER} +rm -rf ${SCRATCH_FOLDER}/* +cd ${SCRATCH_FOLDER} + +mkdir -p ${AWS_LC_BUILD_FOLDER} ${AWS_LC_INSTALL_FOLDER} + +aws_lc_build ${SRC_ROOT} ${AWS_LC_BUILD_FOLDER} ${AWS_LC_INSTALL_FOLDER} \ + -DBUILD_TESTING=OFF \ + -DBUILD_SHARED_LIBS=0 + +# Some systems install under "lib64" instead of "lib" +ln -s ${AWS_LC_INSTALL_FOLDER}/lib64 ${AWS_LC_INSTALL_FOLDER}/lib + +mkdir -p ${OPENLDAP_SRC_FOLDER} +pushd ${OPENLDAP_SRC_FOLDER} + +# NOTE: As we add more versions to support, we may want to parallelize here +for branch in "$@"; do + openldap_patch ${branch} + openldap_build ${branch} + openldap_run_tests ${branch} +done + +popd