diff --git a/contrib/openssl-cmake/CMakeLists.txt b/contrib/openssl-cmake/CMakeLists.txt index 0527936648de..5837c241df0a 100644 --- a/contrib/openssl-cmake/CMakeLists.txt +++ b/contrib/openssl-cmake/CMakeLists.txt @@ -42,6 +42,11 @@ docker cp $id:$lib_dir/build/ssl/libssl.a $OUTPUT_DIR docker cp $id:$lib_dir/build/crypto/libcrypto.a $OUTPUT_DIR docker cp $id:$lib_dir/include $OUTPUT_DIR +# Extract pre-built test harness libraries (symbol-localized, with libstdc++ baked in) +docker cp $id:/harness-output/libawslc_shim.a $OUTPUT_DIR +docker cp $id:/harness-output/libawslc_handshaker.a $OUTPUT_DIR +docker cp $id:/harness-output/libawslc_acvp_server.a $OUTPUT_DIR + docker rm $id" ) @@ -70,7 +75,12 @@ execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory "${AWSLC_BINARIES_DIR add_custom_target(build-awslc COMMENT "Build AWS-LC in FIPS mode with docker" - DEPENDS ${AWSLC_BINARIES_DIR}/libssl.a ${AWSLC_BINARIES_DIR}/libcrypto.a + DEPENDS + ${AWSLC_BINARIES_DIR}/libssl.a + ${AWSLC_BINARIES_DIR}/libcrypto.a + ${AWSLC_BINARIES_DIR}/libawslc_shim.a + ${AWSLC_BINARIES_DIR}/libawslc_handshaker.a + ${AWSLC_BINARIES_DIR}/libawslc_acvp_server.a ) if(ARCH_AARCH64) @@ -83,7 +93,20 @@ add_custom_command( OUTPUT "${AWSLC_BUILD_DIR}/output/libssl.a" "${AWSLC_BUILD_DIR}/output/libcrypto.a" + "${AWSLC_BUILD_DIR}/output/libawslc_shim.a" + "${AWSLC_BUILD_DIR}/output/libawslc_handshaker.a" + "${AWSLC_BUILD_DIR}/output/libawslc_acvp_server.a" COMMENT "Building AWS-LC in FIPS mode using docker" + # Copy test harness build inputs into the Docker context directory + COMMAND ${CMAKE_COMMAND} -E copy + ${ClickHouse_SOURCE_DIR}/programs/ssl-common/posix_spawn_2.c + ${AWSLC_BUILD_DIR}/posix_spawn_2.c + COMMAND ${CMAKE_COMMAND} -E copy + ${ClickHouse_SOURCE_DIR}/programs/ssl-common/glibc_compat.c + ${AWSLC_BUILD_DIR}/glibc_compat.c + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/build_test_harness.sh + ${AWSLC_BUILD_DIR}/build_test_harness.sh COMMAND bash -c "chmod +x ${AWSLC_BUILD_DIR}/build_awclc_fips.sh" COMMAND bash -c "${AWSLC_BUILD_DIR}/build_awclc_fips.sh ${AWSLC_BINARIES_DIR} ${DOCKERFILE_PATH}" WORKING_DIRECTORY ${AWSLC_BUILD_DIR} @@ -92,6 +115,9 @@ add_custom_command( ${AWSLC_BUILD_DIR}/build_awclc_fips.sh ${AWSLC_BUILD_DIR}/check_version.c ${DOCKERFILE_PATH} + ${ClickHouse_SOURCE_DIR}/programs/ssl-common/posix_spawn_2.c + ${ClickHouse_SOURCE_DIR}/programs/ssl-common/glibc_compat.c + ${CMAKE_CURRENT_SOURCE_DIR}/build_test_harness.sh ) add_library(crypto UNKNOWN IMPORTED GLOBAL) @@ -130,6 +156,45 @@ target_compile_options(ssl INTERFACE target_compile_options(global-group INTERFACE "-Wno-deprecated-declarations") target_compile_options(global-group INTERFACE "-Wno-poison-system-directories") +# ── Test harness IMPORTED libraries ────────────────────────────────────────── +# These archives are built inside Docker (build_test_harness.sh), partially +# linked with libstdc++ via `ld -r`, and symbol-localized so only the entry +# point is globally visible. No --allow-multiple-definition needed. + +add_library(awslc_shim STATIC IMPORTED GLOBAL) +add_dependencies(awslc_shim build-awslc) +set_target_properties(awslc_shim PROPERTIES + IMPORTED_LOCATION "${AWSLC_BINARIES_DIR}/libawslc_shim.a") + +add_library(awslc_handshaker STATIC IMPORTED GLOBAL) +add_dependencies(awslc_handshaker build-awslc) +set_target_properties(awslc_handshaker PROPERTIES + IMPORTED_LOCATION "${AWSLC_BINARIES_DIR}/libawslc_handshaker.a") + +add_library(awslc_acvp_server STATIC IMPORTED GLOBAL) +add_dependencies(awslc_acvp_server build-awslc) +set_target_properties(awslc_acvp_server PROPERTIES + IMPORTED_LOCATION "${AWSLC_BINARIES_DIR}/libawslc_acvp_server.a") + +# ── Test harness program libraries ─────────────────────────────────────────── +# Linked into the main clickhouse binary via clickhouse_program_install() +# in programs/CMakeLists.txt. The entry-point .cpp files stay in programs/. + +add_library(clickhouse-ssl-shim-lib + ${ClickHouse_SOURCE_DIR}/programs/ssl-shim/SslShim.cpp) +target_link_libraries(clickhouse-ssl-shim-lib PRIVATE awslc_shim ssl crypto) +add_dependencies(clickhouse-ssl-shim-lib build-awslc) + +add_library(clickhouse-ssl-handshaker-lib + ${ClickHouse_SOURCE_DIR}/programs/ssl-handshaker/SslHandshaker.cpp) +target_link_libraries(clickhouse-ssl-handshaker-lib PRIVATE awslc_handshaker ssl crypto) +add_dependencies(clickhouse-ssl-handshaker-lib build-awslc) + +add_library(clickhouse-acvp-server-lib + ${ClickHouse_SOURCE_DIR}/programs/acvp-server/AcvpServer.cpp) +target_link_libraries(clickhouse-acvp-server-lib PRIVATE awslc_acvp_server crypto) +add_dependencies(clickhouse-acvp-server-lib build-awslc) + else() # FIPS_CLICKHOUSE diff --git a/contrib/openssl-cmake/Dockerfile b/contrib/openssl-cmake/Dockerfile index de3ccff2d66f..c6d1ffb5741d 100644 --- a/contrib/openssl-cmake/Dockerfile +++ b/contrib/openssl-cmake/Dockerfile @@ -30,3 +30,9 @@ RUN test $(/aws-lc-AWS-LC-FIPS-2.0.0/build/tool/bssl isfips) = 1 # execute all test RUN find /aws-lc-AWS-LC-FIPS-2.0.0/build -iname '*test*' -type f -executable -print -exec {} \; + +# Build test harness libraries for ClickHouse FIPS testing integration +COPY posix_spawn_2.c glibc_compat.c /tmp/ +COPY build_test_harness.sh /tmp/ +RUN chmod +x /tmp/build_test_harness.sh && \ + /tmp/build_test_harness.sh /aws-lc-AWS-LC-FIPS-2.0.0 /harness-output diff --git a/contrib/openssl-cmake/Dockerfile.aarch64 b/contrib/openssl-cmake/Dockerfile.aarch64 index 818e8e4d82a6..5ce69568b704 100644 --- a/contrib/openssl-cmake/Dockerfile.aarch64 +++ b/contrib/openssl-cmake/Dockerfile.aarch64 @@ -30,3 +30,9 @@ RUN test $(/aws-lc-AWS-LC-FIPS-2.0.0/build/tool/bssl isfips) = 1 # execute all test RUN find /aws-lc-AWS-LC-FIPS-2.0.0/build -iname '*test*' -type f -executable -print -exec {} \; + +# Build test harness libraries for ClickHouse FIPS testing integration +COPY posix_spawn_2.c glibc_compat.c /tmp/ +COPY build_test_harness.sh /tmp/ +RUN chmod +x /tmp/build_test_harness.sh && \ + /tmp/build_test_harness.sh /aws-lc-AWS-LC-FIPS-2.0.0 /harness-output diff --git a/contrib/openssl-cmake/build_test_harness.sh b/contrib/openssl-cmake/build_test_harness.sh new file mode 100755 index 000000000000..3b38226fe549 --- /dev/null +++ b/contrib/openssl-cmake/build_test_harness.sh @@ -0,0 +1,105 @@ +#!/bin/bash +# Build test harness static libraries for ClickHouse FIPS testing integration. +# +# Each library is partially linked with libstdc++ via `ld -r` and then all +# symbols are prefixed with __awslc_ using `objcopy --prefix-symbols`. +# Undefined symbols (libssl/libcrypto/libc refs) and the entry point are +# restored to their original names via `--redefine-syms`. +# +# This prevents libstdc++ symbols from colliding with ClickHouse's libc++ +# at final link time, while keeping COMDAT groups intact (localizing COMDAT +# key symbols would cause lld to discard their sections). +# +# Usage: build_test_harness.sh +set -ex + +SRC=${1:?usage: build_test_harness.sh } +OUTDIR=${2:?usage: build_test_harness.sh } +mkdir -p "$OUTDIR" + +CXXFLAGS="-std=c++17 -fPIC -g -O2 -fno-exceptions -w" +CFLAGS="-fPIC -O2 -w" +INC="-I$SRC/include -I$SRC -I$SRC/ssl/test" +STDCXX=$(c++ -print-file-name=libstdc++.a) +LIBGCC=$(cc -print-libgcc-file-name) +PREFIX="__awslc_" + +# build_target +# Partial-links .o files in with libstdc++, prefixes all symbols, +# then restores undefined refs and entry point to original names. +build_target() { + local name=$1 entry_grep=$2 obj_dir=$3 + ld -r -o "$obj_dir/combined.o" "$obj_dir"/*.o "$STDCXX" "$LIBGCC" + + # Collect undefined symbols — these reference external libraries + # (libssl, libcrypto, libc, pthreads) and must keep their original names. + nm -u "$obj_dir/combined.o" | awk 'NF>=2{print $NF}' | sort -u > "$obj_dir/undef.txt" + + # Find entry point symbol + nm "$obj_dir/combined.o" | awk "/T.*${entry_grep}/{print \$3}" > "$obj_dir/entry.txt" + + # Build redefine map: after --prefix-symbols adds the prefix, + # restore undefined symbols and entry point to original names. + awk -v p="$PREFIX" '{print p $0 " " $0}' "$obj_dir/undef.txt" > "$obj_dir/redefine.txt" + awk -v p="$PREFIX" '{print p $0 " " $0}' "$obj_dir/entry.txt" >> "$obj_dir/redefine.txt" + + # Collect defined symbols — only these need weakening (to avoid + # duplicate-definition errors when multiple archives pull the same + # libstdc++ members). Undefined symbols must stay strong so the + # linker errors out if they cannot be resolved, instead of silently + # binding them to NULL. + # Names are prefixed to match post-pass-1 state. + nm --defined-only "$obj_dir/combined.o" | awk -v p="$PREFIX" \ + 'NF>=3{print p $NF}' | sort -u > "$obj_dir/weaken.txt" + + # Two passes: objcopy applies --redefine-syms BEFORE --prefix-symbols + # in a single invocation, so we must split them. + # Pass 1: prefix every symbol. + objcopy --prefix-symbols="$PREFIX" "$obj_dir/combined.o" + # Pass 2: restore undefined refs + entry point to original names, + # and weaken only defined symbols. + objcopy --redefine-syms="$obj_dir/redefine.txt" \ + --weaken-symbols="$obj_dir/weaken.txt" "$obj_dir/combined.o" + + ar rcs "$OUTDIR/lib${name}.a" "$obj_dir/combined.o" + rm -rf "$obj_dir" +} + +# --- ssl-shim --- +OBJ=$(mktemp -d) +for f in async_bio bssl_shim handshake_util mock_quic_transport \ + packeted_bio settings_writer ssl_transfer test_config test_state; do + EXTRA="-Dposix_spawn=__ssl_posix_spawn" + [ "$f" = "bssl_shim" ] && EXTRA="$EXTRA -Dmain=bssl_shim_main" + c++ $CXXFLAGS $INC $EXTRA -c "$SRC/ssl/test/$f.cc" -o "$OBJ/$f.o" +done +c++ $CXXFLAGS $INC -Dposix_spawn=__ssl_posix_spawn \ + -c "$SRC/crypto/test/test_util.cc" -o "$OBJ/test_util.o" +cc $CFLAGS -c /tmp/posix_spawn_2.c -o "$OBJ/posix_spawn_2.o" +cc $CFLAGS -c /tmp/glibc_compat.c -o "$OBJ/glibc_compat.o" +build_target awslc_shim bssl_shim_main "$OBJ" + +# --- ssl-handshaker --- +OBJ=$(mktemp -d) +for f in async_bio handshake_util handshaker mock_quic_transport \ + packeted_bio settings_writer test_config test_state; do + EXTRA="-Dposix_spawn=__ssl_posix_spawn" + [ "$f" = "handshaker" ] && EXTRA="$EXTRA -Dmain=handshaker_main" + c++ $CXXFLAGS $INC $EXTRA -c "$SRC/ssl/test/$f.cc" -o "$OBJ/$f.o" +done +c++ $CXXFLAGS $INC -Dposix_spawn=__ssl_posix_spawn \ + -c "$SRC/crypto/test/test_util.cc" -o "$OBJ/test_util.o" +cc $CFLAGS -c /tmp/posix_spawn_2.c -o "$OBJ/posix_spawn_2.o" +cc $CFLAGS -c /tmp/glibc_compat.c -o "$OBJ/glibc_compat.o" +build_target awslc_handshaker handshaker_main "$OBJ" + +# --- acvp-server --- +OBJ=$(mktemp -d) +ACVP_DIR="$SRC/util/fipstools/acvp/modulewrapper" +c++ $CXXFLAGS -I"$ACVP_DIR" -I"$SRC/include" -I"$SRC" \ + -Dmain=acvp_modulewrapper_main \ + -c "$ACVP_DIR/main.cc" -o "$OBJ/main.o" +c++ $CXXFLAGS -I"$ACVP_DIR" -I"$SRC/include" -I"$SRC" \ + -c "$ACVP_DIR/modulewrapper.cc" -o "$OBJ/modulewrapper.o" +cc $CFLAGS -c /tmp/glibc_compat.c -o "$OBJ/glibc_compat.o" +build_target awslc_acvp_server acvp_modulewrapper_main "$OBJ" diff --git a/programs/CMakeLists.txt b/programs/CMakeLists.txt index 59cf435fdf82..04dfaef008d7 100644 --- a/programs/CMakeLists.txt +++ b/programs/CMakeLists.txt @@ -62,6 +62,12 @@ else() message(STATUS "ClickHouse keeper-client mode: OFF") endif() +if (FIPS_CLICKHOUSE AND CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(ENABLE_CLICKHOUSE_SSL_SHIM 1) + set(ENABLE_CLICKHOUSE_SSL_HANDSHAKER 1) + set(ENABLE_CLICKHOUSE_ACVP_SERVER 1) +endif() + configure_file (config_tools.h.in ${CONFIG_INCLUDE_PATH}/config_tools.h) macro(clickhouse_target_link_split_lib target name) @@ -225,6 +231,16 @@ if (ENABLE_CLICKHOUSE_KEEPER_CLIENT) list(APPEND CLICKHOUSE_BUNDLE clickhouse-keeper-client) endif () +if (ENABLE_CLICKHOUSE_SSL_SHIM) + clickhouse_program_install(clickhouse-ssl-shim ssl-shim) +endif() +if (ENABLE_CLICKHOUSE_SSL_HANDSHAKER) + clickhouse_program_install(clickhouse-ssl-handshaker ssl-handshaker) +endif() +if (ENABLE_CLICKHOUSE_ACVP_SERVER) + clickhouse_program_install(clickhouse-acvp-server acvp-server) +endif() + add_custom_target (clickhouse-bundle ALL DEPENDS ${CLICKHOUSE_BUNDLE}) if (USE_BINARY_HASH) diff --git a/programs/acvp-server/AcvpServer.cpp b/programs/acvp-server/AcvpServer.cpp new file mode 100644 index 000000000000..95209e62a0fc --- /dev/null +++ b/programs/acvp-server/AcvpServer.cpp @@ -0,0 +1,6 @@ +extern int acvp_modulewrapper_main(int argc, char ** argv); + +int mainEntryClickHouseAcvpServer(int argc, char ** argv) +{ + return acvp_modulewrapper_main(argc, argv); +} diff --git a/programs/config_tools.h.in b/programs/config_tools.h.in index 11689cf346f9..c9e87a938f3c 100644 --- a/programs/config_tools.h.in +++ b/programs/config_tools.h.in @@ -5,3 +5,6 @@ #cmakedefine01 ENABLE_CLICKHOUSE_KEEPER #cmakedefine01 ENABLE_CLICKHOUSE_KEEPER_CLIENT #cmakedefine01 ENABLE_CLICKHOUSE_KEEPER_CONVERTER +#cmakedefine01 ENABLE_CLICKHOUSE_SSL_SHIM +#cmakedefine01 ENABLE_CLICKHOUSE_SSL_HANDSHAKER +#cmakedefine01 ENABLE_CLICKHOUSE_ACVP_SERVER diff --git a/programs/main.cpp b/programs/main.cpp index 4240778f768f..8db9ca3c50cf 100644 --- a/programs/main.cpp +++ b/programs/main.cpp @@ -64,6 +64,16 @@ int mainEntryClickHouseKeeperBench(int argc, char ** argv); int mainEntryClickHouseKeeperDataDumper(int argc, char ** argv); #endif +#if ENABLE_CLICKHOUSE_SSL_SHIM +int mainEntryClickHouseSslShim(int argc, char ** argv); +#endif +#if ENABLE_CLICKHOUSE_SSL_HANDSHAKER +int mainEntryClickHouseSslHandshaker(int argc, char ** argv); +#endif +#if ENABLE_CLICKHOUSE_ACVP_SERVER +int mainEntryClickHouseAcvpServer(int argc, char ** argv); +#endif + // install int mainEntryClickHouseInstall(int argc, char ** argv); int mainEntryClickHouseStart(int argc, char ** argv); @@ -115,6 +125,15 @@ std::pair clickhouse_applications[] = #endif #if USE_NURAFT {"keeper-data-dumper", mainEntryClickHouseKeeperDataDumper}, +#endif +#if ENABLE_CLICKHOUSE_SSL_SHIM + {"ssl-shim", mainEntryClickHouseSslShim}, +#endif +#if ENABLE_CLICKHOUSE_SSL_HANDSHAKER + {"ssl-handshaker", mainEntryClickHouseSslHandshaker}, +#endif +#if ENABLE_CLICKHOUSE_ACVP_SERVER + {"acvp-server", mainEntryClickHouseAcvpServer}, #endif // install {"install", mainEntryClickHouseInstall}, diff --git a/programs/ssl-common/README.md b/programs/ssl-common/README.md new file mode 100644 index 000000000000..804e51ecac6e --- /dev/null +++ b/programs/ssl-common/README.md @@ -0,0 +1,157 @@ +# Running SSL and ACVP Tests Manually for ClickHouse FIPS Build + +This guide explains how to run the AWS-LC SSL conformance tests and ACVP +cryptographic algorithm validation tests against the ClickHouse FIPS binary. + +## Prerequisites + +- ClickHouse built with `-DFIPS_CLICKHOUSE=1` and `-DAWSLC_SRC_DIR=` + (or the auto-populated source from the Docker build) +- The resulting binary must contain the `ssl-shim`, `ssl-handshaker`, and + `acvp-server` modes (verify with `./clickhouse ssl-shim --help`) +- **Go >= 1.13** (the AWS-LC test harness is written in Go) +- A checkout of the AWS-LC source matching the FIPS build version + (`AWS-LC-FIPS-2.0.0`), which is the same tree referenced by `AWSLC_SRC_DIR` + +Throughout this document, the following paths are used as examples: + +| Variable | Example Path | +|---|---| +| `CLICKHOUSE_BUILD` | `/path/to/ClickHouse/build` | +| `AWSLC_SRC` | `/path/to/aws-lc` (the AWS-LC source root) | + +## SSL Tests (8,037 tests) + +The SSL test suite exercises the full TLS stack: handshake flows, cipher +negotiation, session resumption, certificate handling, QUIC transport, and +more. It uses a Go test runner that drives the ClickHouse `ssl-shim` and +`ssl-handshaker` binaries. + +### Create symlinks + +The Go test runner invokes `bssl_shim` and `handshaker` by path. Create +symlinks from the ClickHouse binary: + +```bash +ln -sf "$CLICKHOUSE_BUILD/programs/clickhouse" "$CLICKHOUSE_BUILD/programs/clickhouse-ssl-shim" +ln -sf "$CLICKHOUSE_BUILD/programs/clickhouse" "$CLICKHOUSE_BUILD/programs/clickhouse-ssl-handshaker" +``` + +### Run the tests + +```bash +cd "$AWSLC_SRC/ssl/test/runner" + +go test -v . \ + -shim-path "$CLICKHOUSE_BUILD/programs/clickhouse-ssl-shim" \ + -handshaker-path "$CLICKHOUSE_BUILD/programs/clickhouse-ssl-handshaker" \ + -num-workers 16 +``` + +`-num-workers` defaults to the number of CPU cores. Adjust as needed. + +### Expected results + +All 8,037 tests should pass. A passing run ends with output similar to: + +``` +PASS +ok boringssl.googlesource.com/boringssl/ssl/test/runner 142.538s +``` + +If any tests fail, the runner prints the failing test name and a diff of +the expected vs. actual TLS behavior. + +## ACVP Tests (31 algorithm suites) + +The ACVP (Automated Cryptographic Validation Protocol) tests validate that +the FIPS cryptographic module produces correct outputs for known-answer test +vectors. AWS-LC bundles 31 algorithm suites with pre-computed expected +results. + +### Build the test tools + +```bash +cd "$AWSLC_SRC/util/fipstools/acvp/acvptool" + +# Build acvptool (the ACVP JSON ↔ modulewrapper translator) +go build -o /tmp/acvptool . + +# Build testmodulewrapper (needed by 2 of the 31 test suites) +cd "$AWSLC_SRC/util/fipstools/acvp/acvptool/testmodulewrapper" +go build -o /tmp/testmodulewrapper . +``` + +### Create the acvp-server symlink + +```bash +ln -sf "$CLICKHOUSE_BUILD/programs/clickhouse" "$CLICKHOUSE_BUILD/programs/clickhouse-acvp-server" +``` + +### Run the tests + +```bash +cd "$AWSLC_SRC/util/fipstools/acvp/acvptool/test" + +go run check_expected.go \ + -tool /tmp/acvptool \ + -module-wrappers "modulewrapper:$CLICKHOUSE_BUILD/programs/clickhouse-acvp-server,testmodulewrapper:/tmp/testmodulewrapper" \ + -tests tests.json +``` + +### Expected results + +All 31 test suites should pass (some have no expected output and only verify +that the module does not crash): + +``` +All tests passed +``` + +The 31 validated algorithm suites are: + +| Category | Algorithms | +|---|---| +| AES | CBC, CCM, CTR, ECB, GCM, GMAC, KW, KWP, XTS, CBC-CS3 (via testmodulewrapper) | +| DRBG | ctrDRBG, hmacDRBG (via testmodulewrapper) | +| ECDSA | KeyGen, SigGen, SigVer | +| HMAC | SHA-1, SHA2-224, SHA2-256, SHA2-384, SHA2-512, SHA2-512/256 | +| KAS | ECC-SSC, FFC-SSC | +| KDF | SP800-108, kdf-components (TLS, SSH), HKDF | +| RSA | KeyGen, SigGen, SigVer | +| TLS | TLS-1.2-KDF | +| Other | PBKDF, AES-GCM-internal-IV | + +## Troubleshooting + +### `clickhouse ssl-shim` says "unknown mode" + +The binary was built without the FIPS test targets. Re-run CMake with: + +```bash +cmake .. -DFIPS_CLICKHOUSE=1 -DAWSLC_SRC_DIR=/path/to/aws-lc +``` + +and rebuild. If `AWSLC_SRC_DIR` was auto-populated from the Docker build, +ensure the Docker build completed successfully (check that +`build/awslc-build/awslc-src/ssl/test/bssl_shim.cc` exists). + +### Go test runner shows fewer than 8,037 tests + +Make sure you are running from the `ssl/test/runner/` directory inside the +AWS-LC source tree, not from a different BoringSSL checkout. + +### ACVP `check_expected.go` fails with "wrapper returned error" + +Verify that `clickhouse-acvp-server` symlink exists and points to the +ClickHouse binary. Also verify it is executable: + +```bash +"$CLICKHOUSE_BUILD/programs/clickhouse-acvp-server" --help +``` + +### Permission denied on Docker socket during build + +The AWS-LC FIPS libraries are built inside a Docker container per the FIPS +140-3 security policy. Ensure the build user has access to the Docker +daemon (is in the `docker` group or has rootless Docker configured). diff --git a/programs/ssl-common/glibc_compat.c b/programs/ssl-common/glibc_compat.c new file mode 100644 index 000000000000..25348621262f --- /dev/null +++ b/programs/ssl-common/glibc_compat.c @@ -0,0 +1,15 @@ +#include + +/* glibc 2.32+ symbol used by libstdc++; sysroot may have older libc. */ +__attribute__((weak)) +char __libc_single_threaded = 0; + +__attribute__((weak)) +unsigned long long __isoc23_strtoull(const char *nptr, char **endptr, int base) { + return strtoull(nptr, endptr, base); +} + +__attribute__((weak)) +long long __isoc23_strtoll(const char *nptr, char **endptr, int base) { + return strtoll(nptr, endptr, base); +} diff --git a/programs/ssl-common/posix_spawn_2.c b/programs/ssl-common/posix_spawn_2.c new file mode 100644 index 000000000000..e06d36fc5b4f --- /dev/null +++ b/programs/ssl-common/posix_spawn_2.c @@ -0,0 +1,324 @@ +/// Based on musl's posix_spawn.c: https://git.musl-libc.org/cgit/musl/tree/src/process/posix_spawn.c +/// +/// Compiled with the system compiler and linked only into the ssl-shim +/// and ssl-handshaker archives. The AWS-LC sources are compiled with +/// -Dposix_spawn=__ssl_posix_spawn so only their posix_spawn calls are +/// redirected here; the rest of ClickHouse continues to use the original +/// limited stub in glibc-compatibility, completely unaffected. +/// +/// The child function uses raw inline-asm syscalls (not glibc wrappers) +/// because CLONE_VM shares the parent's TLS, and glibc's syscall() +/// would clobber the parent's errno. + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* ── raw syscall wrappers (no errno, no TLS) ────────────────────── */ + +#if defined(__x86_64__) + +static inline long raw_sc0(long n) +{ + long ret; + __asm__ __volatile__ ("syscall" : "=a"(ret) + : "a"(n) : "rcx", "r11", "memory"); + return ret; +} + +static inline long raw_sc1(long n, long a1) +{ + long ret; + __asm__ __volatile__ ("syscall" : "=a"(ret) + : "a"(n), "D"(a1) : "rcx", "r11", "memory"); + return ret; +} + +static inline long raw_sc2(long n, long a1, long a2) +{ + long ret; + __asm__ __volatile__ ("syscall" : "=a"(ret) + : "a"(n), "D"(a1), "S"(a2) : "rcx", "r11", "memory"); + return ret; +} + +static inline long raw_sc3(long n, long a1, long a2, long a3) +{ + long ret; + __asm__ __volatile__ ("syscall" : "=a"(ret) + : "a"(n), "D"(a1), "S"(a2), "d"(a3) : "rcx", "r11", "memory"); + return ret; +} + +static inline long raw_sc4(long n, long a1, long a2, long a3, long a4) +{ + register long r10 __asm__("r10") = a4; + long ret; + __asm__ __volatile__ ("syscall" : "=a"(ret) + : "a"(n), "D"(a1), "S"(a2), "d"(a3), "r"(r10) + : "rcx", "r11", "memory"); + return ret; +} + +#elif defined(__aarch64__) + +static inline long raw_sc0(long n) +{ + register long x8 __asm__("x8") = n; + register long x0 __asm__("x0"); + __asm__ __volatile__ ("svc 0" : "=r"(x0) + : "r"(x8) : "memory"); + return x0; +} + +static inline long raw_sc1(long n, long a1) +{ + register long x8 __asm__("x8") = n; + register long x0 __asm__("x0") = a1; + __asm__ __volatile__ ("svc 0" : "=r"(x0) + : "r"(x8), "0"(x0) : "memory"); + return x0; +} + +static inline long raw_sc2(long n, long a1, long a2) +{ + register long x8 __asm__("x8") = n; + register long x0 __asm__("x0") = a1; + register long x1 __asm__("x1") = a2; + __asm__ __volatile__ ("svc 0" : "=r"(x0) + : "r"(x8), "0"(x0), "r"(x1) : "memory"); + return x0; +} + +static inline long raw_sc3(long n, long a1, long a2, long a3) +{ + register long x8 __asm__("x8") = n; + register long x0 __asm__("x0") = a1; + register long x1 __asm__("x1") = a2; + register long x2 __asm__("x2") = a3; + __asm__ __volatile__ ("svc 0" : "=r"(x0) + : "r"(x8), "0"(x0), "r"(x1), "r"(x2) : "memory"); + return x0; +} + +static inline long raw_sc4(long n, long a1, long a2, long a3, long a4) +{ + register long x8 __asm__("x8") = n; + register long x0 __asm__("x0") = a1; + register long x1 __asm__("x1") = a2; + register long x2 __asm__("x2") = a3; + register long x3 __asm__("x3") = a4; + __asm__ __volatile__ ("svc 0" : "=r"(x0) + : "r"(x8), "0"(x0), "r"(x1), "r"(x2), "r"(x3) : "memory"); + return x0; +} + +#else +#error "posix_spawn_2.c: unsupported architecture" +#endif + +/* ── file-action constants & struct (must match glibc-compatibility.c) ── */ + +#define FDOP_CLOSE 1 +#define FDOP_DUP2 2 +#define FDOP_OPEN 3 +#define FDOP_CHDIR 4 +#define FDOP_FCHDIR 5 + +struct fdop { + struct fdop *next, *prev; + int cmd, fd, srcfd, oflag; + mode_t mode; + char path[]; +}; + +struct spawn_args { + int p[2]; + sigset_t oldmask; + const char *path; + int (*exec)(const char *, char *const *, char *const *); + const posix_spawn_file_actions_t *fa; + const posix_spawnattr_t *restrict attr; + char *const *argv, *const *envp; +}; + +static int spawn_child(void *args_vp) +{ + int i; + long ret; + struct spawn_args *args = args_vp; + int p = args->p[1]; + const posix_spawn_file_actions_t *fa = args->fa; + const posix_spawnattr_t *restrict attr = args->attr; + + raw_sc1(SYS_close, args->p[0]); + + if (attr->__flags & POSIX_SPAWN_SETSIGDEF) { + struct sigaction sa = {0}; + sa.sa_handler = SIG_DFL; + for (i = 1; i < _NSIG; i++) + if (sigismember(&attr->__sd, i)) + raw_sc4(SYS_rt_sigaction, i, + (long)&sa, 0, _NSIG/8); + } + + if (attr->__flags & POSIX_SPAWN_SETSID) + if ((ret = raw_sc0(SYS_setsid)) < 0) + goto fail; + + if (attr->__flags & POSIX_SPAWN_SETPGROUP) + if ((ret = raw_sc2(SYS_setpgid, 0, attr->__pgrp))) + goto fail; + + if (attr->__flags & POSIX_SPAWN_RESETIDS) + if ((ret = raw_sc1(SYS_setgid, raw_sc0(SYS_getgid))) || + (ret = raw_sc1(SYS_setuid, raw_sc0(SYS_getuid)))) + goto fail; + + if (fa && fa->__actions) { + struct fdop *op; + int fd; + for (op = fa->__actions; op->next; op = op->next); + for (; op; op = op->prev) { + if (op->fd == p) { + ret = raw_sc1(SYS_dup, p); + if (ret < 0) goto fail; + raw_sc1(SYS_close, p); + p = ret; + } + switch (op->cmd) { + case FDOP_CLOSE: + raw_sc1(SYS_close, op->fd); + break; + case FDOP_DUP2: + fd = op->srcfd; + if (fd == p) { + ret = -EBADF; + goto fail; + } + if (fd != op->fd) { + if ((ret = raw_sc3(SYS_dup3, + fd, op->fd, 0)) < 0) + goto fail; + } else { + ret = raw_sc3(SYS_fcntl, fd, + F_GETFD, 0); + if (ret < 0) goto fail; + ret = raw_sc3(SYS_fcntl, fd, + F_SETFD, ret & ~FD_CLOEXEC); + if (ret < 0) goto fail; + } + break; + case FDOP_OPEN: + fd = raw_sc4(SYS_openat, AT_FDCWD, + (long)op->path, op->oflag, op->mode); + if ((ret = fd) < 0) goto fail; + if (fd != op->fd) { + if ((ret = raw_sc3(SYS_dup3, + fd, op->fd, 0)) < 0) + goto fail; + raw_sc1(SYS_close, fd); + } + break; + case FDOP_CHDIR: + ret = raw_sc1(SYS_chdir, (long)op->path); + if (ret < 0) goto fail; + break; + case FDOP_FCHDIR: + ret = raw_sc1(SYS_fchdir, op->fd); + if (ret < 0) goto fail; + break; + } + } + } + + raw_sc3(SYS_fcntl, p, F_SETFD, FD_CLOEXEC); + + raw_sc4(SYS_rt_sigprocmask, SIG_SETMASK, + (long)((attr->__flags & POSIX_SPAWN_SETSIGMASK) + ? &attr->__ss : &args->oldmask), + 0, _NSIG/8); + + args->exec(args->path, args->argv, args->envp); + ret = -errno; + +fail: + ret = -ret; + if (ret) { + long r; + do r = raw_sc3(SYS_write, p, (long)&ret, sizeof ret); + while (r < 0 && r != -EPIPE); + } + _exit(127); +} + + +static int spawnx_impl(pid_t *restrict res, const char *restrict path, + int (*exec)(const char *, char *const *, char *const *), + const posix_spawn_file_actions_t *fa, + const posix_spawnattr_t *restrict attr, + char *const argv[restrict], char *const envp[restrict]) +{ + pid_t pid; + char stack[1024+PATH_MAX]; + int ec = 0, cs; + struct spawn_args args; + + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); + + args.path = path; + args.exec = exec; + args.fa = fa; + args.attr = attr ? attr : &(const posix_spawnattr_t){0}; + args.argv = argv; + args.envp = envp; + + sigset_t allsigs; + sigfillset(&allsigs); + pthread_sigmask(SIG_BLOCK, &allsigs, &args.oldmask); + + if (pipe2(args.p, O_CLOEXEC)) { + ec = errno; + goto out; + } + + pid = clone(spawn_child, stack + sizeof stack, + CLONE_VM | CLONE_VFORK | SIGCHLD, &args); + close(args.p[1]); + + if (pid > 0) { + if (read(args.p[0], &ec, sizeof ec) != sizeof ec) ec = 0; + else waitpid(pid, &(int){0}, 0); + } else { + ec = -pid; + } + + close(args.p[0]); + + if (!ec && res) *res = pid; + +out: + pthread_sigmask(SIG_SETMASK, &args.oldmask, 0); + pthread_setcancelstate(cs, 0); + + return ec; +} + +/* Called by AWS-LC objects compiled with -Dposix_spawn=__ssl_posix_spawn. + * Only those translation units are redirected; all other CH code uses + * the original posix_spawn from glibc-compatibility. */ +int __ssl_posix_spawn(pid_t *restrict res, const char *restrict path, + const posix_spawn_file_actions_t *fa, + const posix_spawnattr_t *restrict attr, + char *const argv[restrict], char *const envp[restrict]) +{ + return spawnx_impl(res, path, execve, fa, attr, argv, envp); +} diff --git a/programs/ssl-handshaker/SslHandshaker.cpp b/programs/ssl-handshaker/SslHandshaker.cpp new file mode 100644 index 000000000000..a86e927a7d47 --- /dev/null +++ b/programs/ssl-handshaker/SslHandshaker.cpp @@ -0,0 +1,6 @@ +extern int handshaker_main(int argc, char ** argv); + +int mainEntryClickHouseSslHandshaker(int argc, char ** argv) +{ + return handshaker_main(argc, argv); +} diff --git a/programs/ssl-shim/SslShim.cpp b/programs/ssl-shim/SslShim.cpp new file mode 100644 index 000000000000..a96e97c3a8ad --- /dev/null +++ b/programs/ssl-shim/SslShim.cpp @@ -0,0 +1,6 @@ +extern int bssl_shim_main(int argc, char ** argv); + +int mainEntryClickHouseSslShim(int argc, char ** argv) +{ + return bssl_shim_main(argc, argv); +}